/gh-ai

gh extension that provides help in the creation of other gh extensions using AI and prompt-engineering

Primary LanguageJavaScript

gh-ai

gh-ai is an extension of Github CLI (gh) that aims to provide support for creating new extensions for Github CLI (gh) through the use of artificial intelligence and command engineering. gh-ai aims to generate a ready-to-use and polished template to start working on.

By simply writing a file similar to a Readme.md, the language model is able to generate code to continue developing the extension. The more detailed and structured the description of the extension to be created, the better the results generated by artificial intelligence will be.

Warnings about the usage of AI generated code

The main goal of the extension is to simplify the early stages of developing an extension for Github CLI (gh). It is not recommended to use the code generated by the language model without prior supervision regarding the quality and validity of the generated code, as it is well known that language models tend to suffer from hallucinations and may generate unnecessary or incorrect code if they are not able to fully understand the requirements.

The code generated by artificial intelligence should be used as a guide and inspiration to create the actual code for the extension. The gh-ai extension can be useful for exploring and testing different approaches, but never for generating a complete extension ready to be released to the public.

Tokens usage

It is important to note that the gh-ai extension, when communicating with the selected language model API (by default OpenAI's ChatGPT), consumes around 0.03$ worth of Tokens.

Installation

The Github CLI tool enables the management of different program extensions through its own package manager, gh extension. The package manager allows for: Installing, uninstalling, and updating various tool extensions. Thanks to this package manager, installing the gh-ai extension can be easily done by executing the following commands:

Install gh-ai extension:

gh extension install https://github.com/gh-cli-for-education/gh-ai

Upgrade gh-ai extension:

gh extension upgrade gh-ai

Uninstall gh-ai extension:

gh extension remove gh-ai

Usage

Once this step is completed, it is possible to execute the extension whenever needed using the following command:

gh ai <input-file> <output-directory> [Options]

Arguments

  • input-file: The input file used to extract the parsed data and feed the llm.
  • output-directory: The directory path where all the files created by the llm will be stored.

Options

  • -v, --version Print the current version of the program
  • -d, --debug Output extra information about the execution process
  • --tokens-verbose Output the token usage information in each prompt
  • --save-thread Make the program not delete the used thread, instead it will save it inside the generated README file
  • --save-assistant Make the program not delete the used assistant, instead it will save it inside the generated README file
  • -m --llm-model <model> Specify which llm model would you want to use by the selected API
  • -l, --llm-api <API> Select the llm <API> to use (choices: "openai", default: "openai")
  • -t, --command-type <type> Select the command needed (choices: "extension", default: "extension")
  • -h, --help display help for command

The .env file

To be able to use your llm api key you need to specify them in a .env file

Here is an example of a .env file to fill with your own keys

# Openai env variables
OPENAI_API_KEY= # <Put here your OpenAI API key>
OPENAI_ORG= # <Put here your OpenAI org key> 

# You can use --save-assistant or --save-tread 
# to make the program input the assisant or thread ID into the user-log.md file
ASSISTANT_ID= # <Put here an assistant ID if you want to use an existing one instead of creating a new one>
THREAD_ID= # <Put here a thread ID if you want to use an existing one instead of creating a new one> 

General Desing of the input file

The gh-ai extension uses its own syntactic analyzer to extract information from the input file. This syntactic analyzer is programmed to be able to read files with a format very similar to Markdown. The goal of this custom language is to provide the user with great expressiveness to describe the extension to be generated while also granting the program the ability to recognize specific sections of the file in order to extract the necessary information.

Markdown language quickly became the best option to give users complete freedom when writing the description of the input file. The main drawback of Markdown language lies in its syntax because the great expressiveness of the language comes hand in hand with a lack of control over its content.

  {
    "type": "heading",
    "depth": 1,
    "children": [
      {
        "type": "text",
        "value": "Hello",
        "position": {}
      }
    ],
    "position": {}
  }

A Markdown language Token compared to a simplified Markdown language Token

The solution to this problem has been achieved through the creation of a simplified version of the Markdown language. This simplification adds new types of Tokens and eliminates all those Tokens that were not necessary for the input file, as well as other features of the Markdown language such as the ability to nest lists, code blocks, quotes, etc.

Writting the input File

In order to construct the different instructions to be used during the conversation with the language model, it is necessary for the gh-ai extension to have access to all the crucial information to properly structure the content of the instruction, detailing the most important aspects and eliminating all unnecessary information.

For this reason, it is necessary for the user to input the information through an input file written in Markdown-like language. This language is structured into a series of different sections or blocks that can be nested to build the complete file.

Language Main Blocks

The main blocks of the language are those with a nesting level of 1, and therefore they should be represented by a single "#" symbol. They are characterized by greatly influencing the program's behavior. Currently, there are only two main blocks, one of which is configuring the conversation with the language model.

The concept of main blocks aims to allow the user to specify the type of support needed. These supports are determined by the gh-ai extension, which currently only has one type of assistance, which is generating an extension. Main blocks allow the user to describe the contents for each of these assists within the same file, meaning multiple main blocks within the same file, although it is not recommended for readability reasons.

Chat Settings Block

When engaging in conversation, the user can specify a series of parameters to modify the behavior and response of the language model to the generated instructions. These configurations allow the user to personalize their experience with artificial intelligence.

Currently, the configurations in the Chat Settings block are purely aesthetic. But it is expected that in future versions, it will allow the user to modify more important parameters such as temperature and... [ADD MORE AI VARIABLES].

The Chat Settings block currently supports the following options:

  • Language: Indicates the natural language to be used for the language model's response.
  • Nickname: Indicates the name that the language model will use to refer to the user.

The inclusion of the block is completely optional because the configuration has default values, which are English and User respectively.

The format of the Chat Settings block is as follows:

chat-settings-grammar-rule

As an example, the following Chat Settings block is provided:

[comment]: # (The spacing between the words Chat and Settings is arbitrary)
[comment]: # (Both words are case-insensitive)
# Chat Settings

- language: English
- Nickname: User
- Another: Option 

[comment]: # (Although the Parser allows the inclusion of non-existent options, the object Schema will throw an error upon detecting them)

Extension Block

One of the first actions that the user must take when writing the input file is to indicate the name of the extension to create. This is done by adding the Extension block to the input file followed by the extension name: # Extension <gh-extension-name>.

By using the different elements of Markdown language, a detailed description of the program's functionality should be written along with a series of secondary blocks that allow the inclusion of useful details to the extension description. These secondary blocks serve as reference points for the user to gain a clear and structured understanding of the desired extension.

extension-grammar-rule

# Extension gh-rate-limit

[comment]: # (The description supports Markdown paragraphs.)
gh-rate-limit is an extension of the Github Command Line Interface `(Github CLI)` whose purpose is to show the user their existing **rate limits** and when its resets. The program does exactly the same as executing the *following command*... 

[comment]: # (Supports Codeblocks)

  curl -fSsL -H "Authorization: token $(github_pat)" -X GET \
    https://api.github.com/rate_limit \
    | jq --raw-output '.resources | to_entries[] | {name: .key} + .value | "\(.name)  \(.remaining)/\(.limit)  \(.reset | strflocaltime("%H:%M:%S") )"' \
    | column -t

[comment]: # (Also supports unordered and ordered lists)
The program has some **Prerequisites** that are: 

+ It is necessary to have `Github CLI (gh) installed`, so the program must be able to verify that said program is installed.
+ It is necessary to have `js installed`, so the program is able to execute it.

Steps:

1. Step 1
2. Step 2

Language Settings Block

The Language Settings block is one of the secondary sections within the Extension block. This secondary block must be written obligatorily to describe the extension because within this section, the programming language configuration to be used by the extension must be indicated through a list.

In terms of content, the Language Settings block works exactly the same way as the main Chat Settings block, with the difference that it accepts different parameters, which are:

  • Language: Indicates the programming language to be used by the language model to generate the extension.
  • Specification: Indicates the specification to be used for the previously selected programming language.
  • Style: Indicates the style guide to be followed during code generation.

The Specification and Style parameters are completely optional, while the Language parameter must be included obligatorily in all extension descriptions.

An example of the Language Settings block is provided below:

[comment]: # (The spacing between the words Language and Settings is arbitrary)
[comment]: # (Both words are case-insensitive)
## Language Settings

- language: JavaScript
- Nickname: ECMAScript2021
- Style: Google
- Another: Option

[comment]: # (Although the Parser allows the inclusion of non-existent options, the object Schema will throw an error upon detecting them)

Function Block

To specify the different functions that the extension must contain, the secondary block Function within the Extension block should be used to instruct the gh-ai extension to generate a specific instruction for the language model to encode the content of that function.

The Function block abstracts the definition and format of a function to allow it to be described without the need to write it in the previously specified programming language. For this reason, the block allows all types of descriptive elements mentioned earlier in the Extension block, while also allowing the specification of two additional sections.

Within the Function block, there can be two subsections that allow adding additional information, useful for the language model. The first of these subsections is the Query block, which instructs the program that the function should call the Github API, constructing the corresponding instruction. The second subsection is the Template block, which instructs the artificial intelligence to use a template as a basis for generating the function.

[comment]: # (Along with the keyword Function, the function name should be provided.)
## Function print-notifs

[comment]: # (Here goes the description of the function.)

The print_notifs function is responsible for printing on the command line the information obtained from the notifications after making the API call. To do this, the function constantly checks the number of pages read as well as the total number of notifications obtained, always ensuring not to exceed the limit imposed by the `num_notifications` variable. For each iteration of the loop, it calls the `get_notifs` function with the current page number as a parameter. In case of an error, the `die` function is called with the corresponding error message.

In each iteration, a maximum of 50 notifications can be obtained from the API. For **example**: If a user requests 56 notifications, the API must be called twice with a maximum of 50 notifications each time, and stop displaying the information once the limit set by `num_notifications` is reached. It is important to keep this in mind, otherwise the program will malfunction.

...

### Query

[comment]: # (Here goes the description of the query.)

### Template

[comment]: # (The function template must be inside a Codeblock)

``
  print_notifs() {
    local all_notifs=""
    local curr_page=1
    local total_count=0
    local remaining_notifications=$num_notifications

    while [ "$curr_page" -ne 0 ]; do
      current_notifs=$(get_notifs $curr_page) || die "Error: Unable to fetch notifications from page $curr_page"

      if [ -z "$current_notifs" ]; then
        break
      fi

      all_notifs+="$current_notifs"

      total_count=$(echo "$all_notifs" | jq '. | length')
      remaining_notifications=$((num_notifications - total_count))

      if [ "$remaining_notifications" -le 0 ]; then
        break
      fi

      curr_page=$((curr_page + 1))
    done

    # Apply final filter using exclusion_string and filter_string
    filtered_notifs=$(echo "$all_notifs" | grep -v "$exclusion_string" | grep "$filter_string" | column -t)

    echo "$filtered_notifs"
  }
``

Help Block

An important aspect of any command-line program is the implementation of a help function that displays relevant information about the program's usage. For this reason, everything that should be included in such a function can be specified within the Help secondary block, within the main Extension block. This section serves multiple roles as it also allows indicating to the language model the parameters and arguments that the extension to be created can receive.

The Help block is divided into several subsections, which are: Usage, Header, Arguments, Parameters, and Footer. Among these sections, only the Usage subsection is mandatory.

The block is designed to replicate the common format used in this type of function. It starts with the Usage subsection and its usage scheme, followed by a series of optional paragraphs as a header to then indicate the list of program arguments and parameters. Lastly, additional information can be added as a footer.

## Help 

[comment]: # (The usage subsection must be all on the same line)

### Usage gh-cpissues <git-repo> --label <label> [--verbose]

[comment]: # (Here can go the header paragraphs)

### Parameters 

[comment]: # (Within the Parameters subsection, a list of parameters is shown)
[comment]: # (They follow the following format)

- --label Specify the <label> of the issue to copy (Modifies the value of the variable `label`).

### Arguments

[comment]: # (Within the Arguments subsection, a list of arguments is shown)
[comment]: # (They follow the following format)

- input-object The input file used to feed the llm

[comment]: # (Here can go the footer paragraphs)

Examples Block

One way to improve the language model's response is to provide a series of examples that it can use as a reference to generate the extension code. For this reason, the input file allows the introduction of examples through the Examples secondary block, within the main Extension block. Once the section is opened, as many examples as the user deems necessary can be indicated, as long as they follow the following format:

Each example must indicate the input through the command line as well as the corresponding result of executing the extension with that configuration.

## Examples

[comment]: # (The example should be on a single line)

`gh branch; gh poi; gh branch`

[comment]: # (The execution result should be within a Codeblock)

```console
foo@bar:~$ gh branch
- inproving-parser
- markdown-like-parser
- inproving-api-call
- main

foo@bar:~$ gh poi
# Fetching pull requests...
# Deleting Branches...

foo@bar:~$ gh branch
- inproving-api-call
- main
``

Readme Block

The last secondary block that the main Extension block can contain is the Readme section. This section instructs the gh-ai extension to build an instruction that orders the language model to generate a Readme.md file with the information provided by the user within the description of the block.

The description of the Readme block may not be filled in, in which case the program will indicate a series of default sections that the file must contain.

## Readme

[comment]: # (Here can go a detailed description of the content of the Readme file.)

1. Write how to **install** th gh-poi extension using the Github CLI program.
2. Write the **help** and usage of the gh-poi extension.
3. Write some **examples** of use `Don't use any provided example from the prompt`.

Input file Examples

If more specific and detailed examples are needed on how to fill in the input file, the directory examples/ contains several usage examples of the extension.