Skip to content

Add Prompt Library support #1168

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
100 changes: 100 additions & 0 deletions cmd/src/prompts.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
package main

import (
"flag"
"fmt"
)

var promptsCommands commander

func init() {
usage := `'src prompts' is a tool that manages prompt library prompts and tags in a Sourcegraph instance.

Usage:

src prompts command [command options]

The commands are:

list lists prompts
get get a prompt by ID
create create a prompt
update update a prompt
delete delete a prompt
export export prompts to a JSON file
import import prompts from a JSON file
tags manage prompt tags (use "src prompts tags [command] -h" for more info)

Use "src prompts [command] -h" for more information about a command.
`

flagSet := flag.NewFlagSet("prompts", flag.ExitOnError)
handler := func(args []string) error {
promptsCommands.run(flagSet, "src prompts", usage, args)
return nil
}

// Register the command.
commands = append(commands, &command{
flagSet: flagSet,
handler: handler,
usageFunc: func() {
fmt.Println(usage)
},
})
}

const promptFragment = `
fragment PromptFields on Prompt {
id
name
description
definition {
text
}
draft
visibility
autoSubmit
mode
recommended
tags(first: 100) {
nodes {
id
name
}
}
}
`

const promptTagFragment = `
fragment PromptTagFields on PromptTag {
id
name
}
`

type Prompt struct {
ID string `json:"id"`
Name string `json:"name"`
Description string `json:"description"`
Definition Definition `json:"definition"`
Draft bool `json:"draft"`
Visibility string `json:"visibility"`
AutoSubmit bool `json:"autoSubmit"`
Mode string `json:"mode"`
Recommended bool `json:"recommended"`
Tags PromptTags `json:"tags"`
}

type Definition struct {
Text string `json:"text"`
}

type PromptTags struct {
Nodes []PromptTag `json:"nodes"`
}

type PromptTag struct {
ID string `json:"id"`
Name string `json:"name"`
}
136 changes: 136 additions & 0 deletions cmd/src/prompts_create.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package main

import (
"context"
"flag"
"fmt"
"strings"

"github.com/sourcegraph/sourcegraph/lib/errors"

"github.com/sourcegraph/src-cli/internal/api"
)

func init() {
usage := `
Examples:

Create a prompt "Go Error Handling":

$ src prompts create -name="Go Error Handling" \
-description="Best practices for Go error handling" \
-content="When handling errors in Go..." \
-owner=<owner-id>
`

flagSet := flag.NewFlagSet("create", flag.ExitOnError)
usageFunc := func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage of 'src prompts %s':\n", flagSet.Name())
flagSet.PrintDefaults()
fmt.Println(usage)
}
var (
nameFlag = flagSet.String("name", "", "The prompt name")
descriptionFlag = flagSet.String("description", "", "Description of the prompt")
contentFlag = flagSet.String("content", "", "The prompt template text content")
ownerFlag = flagSet.String("owner", "", "The ID of the owner (user or organization). Defaults to current user if not specified.")
tagsFlag = flagSet.String("tags", "", "Comma-separated list of tag IDs")
draftFlag = flagSet.Bool("draft", false, "Whether the prompt is a draft")
visibilityFlag = flagSet.String("visibility", "PUBLIC", "Visibility of the prompt (PUBLIC or SECRET)")
autoSubmitFlag = flagSet.Bool("auto-submit", false, "Whether the prompt should be automatically executed in one click")
modeFlag = flagSet.String("mode", "CHAT", "Mode to execute prompt (CHAT, EDIT, or INSERT)")
recommendedFlag = flagSet.Bool("recommended", false, "Whether the prompt is recommended")
apiFlags = api.NewFlags(flagSet)
)

handler := func(args []string) error {
if err := flagSet.Parse(args); err != nil {
return err
}

if *nameFlag == "" {
return errors.New("provide a name for the prompt")
}
if *descriptionFlag == "" {
return errors.New("provide a description for the prompt")
}
if *contentFlag == "" {
return errors.New("provide content for the prompt")
}
client := cfg.apiClient(apiFlags, flagSet.Output())

// Use current user as default owner if not specified
ownerID := *ownerFlag
if ownerID == "" {
var err error
ownerID, err = getViewerUserID(context.Background(), client)
if err != nil {
return errors.Wrap(err, "failed to get current user ID")
}
}

// Validate mode
validModes := map[string]bool{"CHAT": true, "EDIT": true, "INSERT": true}
mode := strings.ToUpper(*modeFlag)
if !validModes[mode] {
return errors.New("mode must be one of: CHAT, EDIT, or INSERT")
}

// Validate visibility
validVisibility := map[string]bool{"PUBLIC": true, "SECRET": true}
visibility := strings.ToUpper(*visibilityFlag)
if !validVisibility[visibility] {
return errors.New("visibility must be either PUBLIC or SECRET")
}

// Parse tags into array
var tagIDs []string
if *tagsFlag != "" {
tagIDs = strings.Split(*tagsFlag, ",")
}

query := `mutation CreatePrompt(
$input: PromptInput!
) {
createPrompt(input: $input) {
...PromptFields
}
}
` + promptFragment

input := map[string]interface{}{
"name": *nameFlag,
"description": *descriptionFlag,
"definitionText": *contentFlag,
"owner": ownerID,
"draft": *draftFlag,
"visibility": visibility,
"autoSubmit": *autoSubmitFlag,
"mode": mode,
"recommended": *recommendedFlag,
}

if len(tagIDs) > 0 {
input["tags"] = tagIDs
}

var result struct {
CreatePrompt Prompt
}
if ok, err := client.NewRequest(query, map[string]interface{}{
"input": input,
}).Do(context.Background(), &result); err != nil || !ok {
return err
}

fmt.Printf("Prompt created: %s\n", result.CreatePrompt.ID)
return nil
}

// Register the command.
promptsCommands = append(promptsCommands, &command{
flagSet: flagSet,
handler: handler,
usageFunc: usageFunc,
})
}
75 changes: 75 additions & 0 deletions cmd/src/prompts_delete.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package main

import (
"context"
"flag"
"fmt"

"github.com/sourcegraph/sourcegraph/lib/errors"

"github.com/sourcegraph/src-cli/internal/api"
)

func init() {
usage := `
Examples:

Delete a prompt by ID:

$ src prompts delete <prompt-id>

`

flagSet := flag.NewFlagSet("delete", flag.ExitOnError)
usageFunc := func() {
fmt.Fprintf(flag.CommandLine.Output(), "Usage of 'src prompts %s':\n", flagSet.Name())
flagSet.PrintDefaults()
fmt.Println(usage)
}
var (
apiFlags = api.NewFlags(flagSet)
)

handler := func(args []string) error {
if err := flagSet.Parse(args); err != nil {
return err
}

// Check for prompt ID as positional argument
if len(flagSet.Args()) != 1 {
return errors.New("provide exactly one prompt ID as an argument")
}
promptID := flagSet.Arg(0)

client := cfg.apiClient(apiFlags, flagSet.Output())

query := `mutation DeletePrompt($id: ID!) {
deletePrompt(id: $id) {
alwaysNil
}
}
`

var result struct {
DeletePrompt struct {
AlwaysNil interface{} `json:"alwaysNil"`
}
}

if ok, err := client.NewRequest(query, map[string]interface{}{
"id": promptID,
}).Do(context.Background(), &result); err != nil || !ok {
return err
}

fmt.Println("Prompt deleted successfully.")
return nil
}

// Register the command.
promptsCommands = append(promptsCommands, &command{
flagSet: flagSet,
handler: handler,
usageFunc: usageFunc,
})
}
Loading
Loading