Skip to content
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

feat: pass additional input to custom command #1731

Merged
merged 6 commits into from
Nov 15, 2023
Merged
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
1 change: 1 addition & 0 deletions lib/shared/src/chat/prompts/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export interface CodyPrompt {

// internal properties
contextFiles?: ContextFile[]
additionalInput?: string
}

/**
Expand Down
8 changes: 6 additions & 2 deletions lib/shared/src/chat/recipes/custom-prompt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,8 @@ export class CustomPrompt implements Recipe {
: context.editor.getActiveTextEditorSelectionOrVisibleContent()

// Get prompt text from the editor command or from the human input
const promptText = command.prompt
const commandAdditionalInput = command.additionalInput
const promptText = commandAdditionalInput ? `${command.prompt}\n${commandAdditionalInput}` : command.prompt
const commandName = isChatQuestion ? promptText : command.slashCommand || promptText

// Log all custom commands under 'custom'
Expand All @@ -84,7 +85,10 @@ export class CustomPrompt implements Recipe {
// Add selection file name as display when available
const displayText = contextFiles?.length
? createDisplayTextWithFileLinks(contextFiles, promptText)
: createDisplayTextWithFileSelection(commandName, selection)
: createDisplayTextWithFileSelection(
commandAdditionalInput ? `${commandName} ${commandAdditionalInput}` : commandName,
selection
)

const truncatedText = truncateText(text, MAX_HUMAN_INPUT_TOKENS)

Expand Down
9 changes: 9 additions & 0 deletions lib/ui/src/Chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,15 @@ export const Chat: React.FunctionComponent<ChatProps> = ({
if (!chatCommands || !ChatCommandsComponent) {
return
}
const splittedValue = inputValue.split(' ')
if (splittedValue.length > 1) {
const matchedCommand = chatCommands.filter(([name]) => name === splittedValue[0])
if (matchedCommand.length === 1) {
setDisplayCommands(matchedCommand)
setSelectedChatCommand(0)
}
return
}
if (inputValue === '/') {
setDisplayCommands(chatCommands)
setSelectedChatCommand(chatCommands.length)
Expand Down
1 change: 1 addition & 0 deletions vscode/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Starting from `0.2.0`, Cody is using `major.EVEN_NUMBER.patch` for release versi
- Edit: Added a specific, faster, response flow for fixes when triggered directly from code actions. [pull/1639](https://github.com/sourcegraph/cody/pull/1639)
- Edit: Improved context fetching for quick fixes to better include code related to the problem. [pull/1723](https://github.com/sourcegraph/cody/pull/1723)
- Autocomplete: Added new retrieval and mixing strategies to improve Autocomplete context. [pull/1752](https://github.com/sourcegraph/cody/pull/1752)
- Commands: Supports passing additional input text to commands via the chat input box. For example, adds additional instruction after the command key: `/explain response in Spanish`. [pull/1731](https://github.com/sourcegraph/cody/pull/1731)

### Fixed

Expand Down
4 changes: 1 addition & 3 deletions vscode/src/chat/MessageProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { IntentDetector } from '@sourcegraph/cody-shared/src/intent-detector'
import { ANSWER_TOKENS, DEFAULT_MAX_TOKENS } from '@sourcegraph/cody-shared/src/prompt/constants'
import { Message } from '@sourcegraph/cody-shared/src/sourcegraph-api'

import { showAskQuestionQuickPick } from '../custom-prompts/utils/menu'
import { showAskQuestionQuickPick } from '../commands/utils/menu'
import { VSCodeEditor } from '../editor/vscode-editor'
import { PlatformContext } from '../extension.common'
import { logDebug, logError } from '../log'
Expand Down Expand Up @@ -642,10 +642,8 @@ export abstract class MessageProvider extends MessageHandler implements vscode.D
const assistantResponse = 'Command failed. Please open a file and try again.'
return this.addCustomInteraction({ assistantResponse, text, source })
}

const commandRunnerID = await this.editor.controllers.command?.addCommand(
text,
'',
eventTrace?.requestID,
this.userContextFiles
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,9 @@ import { CommandRunner } from './CommandRunner'
import { CustomPromptsStore } from './CustomPromptsStore'
import { showCommandConfigMenu, showCommandMenu, showCustomCommandMenu, showNewCustomCommandMenu } from './menus'
import { PromptsProvider } from './PromptsProvider'
import { ToolsProvider } from './ToolsProvider'
import { constructFileUri, createFileWatchers, createQuickPickItem, openCustomCommandDocsLink } from './utils/helpers'
import {
menu_options,
menu_separators,
showAskQuestionQuickPick,
showcommandTypeQuickPick,
showRemoveConfirmationInput,
} from './utils/menu'
import { menu_options, menu_separators, showAskQuestionQuickPick, showRemoveConfirmationInput } from './utils/menu'
import { ToolsProvider } from './utils/ToolsProvider'

/**
* Manage commands built with prompts from CustomPromptsStore and PromptsProvider
Expand Down Expand Up @@ -75,36 +69,40 @@ export class CommandsController implements VsCodeCommandsController, vscode.Disp

/**
* Adds a new command to the commands map.
* @param key - The unique key for the command. e.g. /test
* or 'invalid' if the command is not found.
*
* Looks up the command prompt using the given key in the default prompts map.
* If found, creates a new Cody command runner instance for that prompt and input.
* Returns the ID of the created runner, or 'invalid' if not found.
*/
public async addCommand(
key: string,
input = '',
text: string,
requestID?: string,
contextFiles?: ContextFile[],
addEnhancedContext?: boolean
): Promise<string> {
const command = this.default.get(key)
const commandSplit = text.split(' ')
// The unique key for the command. e.g. /test
const commandKey = commandSplit.shift() || text
// Additional instruction that will be added to end of prompt in the custom-prompt recipe
const commandInput = commandKey === text ? '' : commandSplit.join(' ')

const command = this.default.get(commandKey)
if (!command) {
return 'invalid'
}

if (command.slashCommand === '/ask') {
command.prompt = input
command.prompt = text
}

if (!command.context && addEnhancedContext) {
command.context = { codebase: addEnhancedContext }
}

command.additionalInput = commandInput
command.requestID = requestID
command.contextFiles = contextFiles
return this.createCodyCommandRunner(command, input)
return this.createCodyCommandRunner(command, commandInput)
}

/**
Expand Down Expand Up @@ -329,19 +327,19 @@ export class CommandsController implements VsCodeCommandsController, vscode.Disp
}

logDebug('CommandsController:customPrompts:menu', action)
await this.configFileAction(action, selected.type, selected.type)

return this.refresh()
}

/**
* Config file controller
* handles operations on config files for user and workspace commands
*/
public async configFileAction(action: string, fileType?: CustomCommandType, filePath?: string): Promise<void> {
switch (action) {
case 'delete':
case 'file':
case 'open': {
await this.configFileAction(
action,
selected.type || (await showcommandTypeQuickPick(action, this.custom.promptSize))
)
break
}
case 'add': {
await this.configFileAction(action)
await this.addNewUserCommandQuick()
break
}
case 'list':
Expand All @@ -350,37 +348,22 @@ export class CommandsController implements VsCodeCommandsController, vscode.Disp
case 'docs':
await openCustomCommandDocsLink()
break
}

return this.refresh()
}

/**
* Config file controller
* handles operations on config files for user and workspace commands
*/
public async configFileAction(action: string, fileType?: CustomCommandType): Promise<void> {
switch (action) {
case 'delete':
case 'delete': {
if ((await showRemoveConfirmationInput()) !== 'Yes') {
return
}
await this.custom.deleteConfig(fileType)
await this.refresh()
break
}
case 'file':
await this.custom.createConfig(fileType)
break
case 'open':
if (fileType) {
await this.open(fileType)
if (filePath) {
await this.open(filePath)
}
break
case 'add':
await this.addNewUserCommandQuick()
break
default:
break
}
}

Expand Down Expand Up @@ -420,7 +403,7 @@ export class CommandsController implements VsCodeCommandsController, vscode.Disp
const uri = this.custom.jsonFileUris[filePath]
const doesExist = await this.tools.doesUriExist(uri)
// create file if it doesn't exist
return doesExist ? this.tools.openFile(uri) : this.configFileAction('file', filePath)
return doesExist ? this.tools.openFile(uri) : this.open(filePath)
}
const fileUri = constructFileUri(filePath, this.tools.getUserInfo()?.workspaceRoot)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ import { promisify } from 'util'

import * as vscode from 'vscode'

import { getActiveEditor } from '../editor/active-editor'
import { logDebug, logError } from '../log'
import { getActiveEditor } from '../../editor/active-editor'
import { logDebug, logError } from '../../log'

import { UserWorkspaceInfo } from './utils'
import { outputWrapper } from './utils/helpers'
import { UserWorkspaceInfo } from '.'
import { outputWrapper } from './helpers'

const rootPath: () => string | undefined = () => vscode.workspace.workspaceFolders?.[0]?.uri?.fsPath
const currentFilePath: () => string | undefined = () => getActiveEditor()?.document.uri.fsPath
Expand Down
54 changes: 54 additions & 0 deletions vscode/src/commands/utils/process-command-input.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import { describe, expect, it } from 'vitest'

import { parseInputToCommands } from './process-command-input'

describe('parseInputToCommands', () => {
it('parses a command key', () => {
const result = parseInputToCommands('/test')
expect(result).toEqual({ key: '/test' })
})

it('parses a command key and additional instruction', () => {
const result = parseInputToCommands('/test hello world')
expect(result).toEqual({ key: '/test', request: 'hello world' })
})

it('returns key as userInput if no space', () => {
const result = parseInputToCommands('/test')
expect(result).toEqual({ key: '/test' })
})

it('parses input into key and additionalInstruction', () => {
const { key, request } = parseInputToCommands('/test hello world')

expect(key).toBe('/test')
expect(request).toBe('hello world')
})

it('returns only key if no additional input', () => {
const { key, request } = parseInputToCommands('/test')

expect(key).toBe('/test')
expect(request).toBe(undefined)
})

it('returns original input as an ask command if not a command', () => {
const { key, request } = parseInputToCommands('hello world')

expect(key).toBe('/ask')
expect(request).toBe('hello world')
})

it('returns key and input with special characters', () => {
const { key, request } = parseInputToCommands(`/example Explain the following code:
\`\`\`
console.log('hello world')
\`\`\``)

expect(key).toBe('/example')
expect(request).toBe(`Explain the following code:
\`\`\`
console.log('hello world')
\`\`\``)
})
})
16 changes: 16 additions & 0 deletions vscode/src/commands/utils/process-command-input.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export function parseInputToCommands(userInput: string): { key: string; request?: string } {
if (!userInput.startsWith('/')) {
return { key: '/ask', request: userInput }
}

const inputParts = userInput.split(' ')

// The unique key for the command. e.g. /test
const key = inputParts.shift() || userInput

// Additional instruction that will be added to end of prompt in the custom-prompt recipe
const instruction = key === userInput ? '' : inputParts.join(' ')
const request = instruction.trim() || undefined

return { key, request }
}
2 changes: 1 addition & 1 deletion vscode/src/editor/vscode-editor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {
} from '@sourcegraph/cody-shared/src/editor'
import { SURROUNDING_LINES } from '@sourcegraph/cody-shared/src/prompt/constants'

import { CommandsController } from '../custom-prompts/CommandsController'
import { CommandsController } from '../commands/CommandsController'
import { FixupController } from '../non-stop/FixupController'
import { InlineController } from '../services/InlineController'

Expand Down
2 changes: 1 addition & 1 deletion vscode/src/extension.common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import { Configuration } from '@sourcegraph/cody-shared/src/configuration'
import type { SourcegraphBrowserCompletionsClient } from '@sourcegraph/cody-shared/src/sourcegraph-api/completions/browserClient'
import type { SourcegraphNodeCompletionsClient } from '@sourcegraph/cody-shared/src/sourcegraph-api/completions/nodeClient'

import { CommandsController } from './commands/CommandsController'
import { BfgRetriever } from './completions/context/retrievers/bfg/bfg-retriever'
import { CommandsController } from './custom-prompts/CommandsController'
import { onActivationDevelopmentHelpers } from './dev/helpers'
import { ExtensionApi } from './extension-api'
import type { FilenameContextFetcher } from './local-context/filename-context-fetcher'
Expand Down
2 changes: 1 addition & 1 deletion vscode/src/extension.node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { GitHistory } from '@sourcegraph/cody-shared/src/chat/recipes/git-log'
import { SourcegraphNodeCompletionsClient } from '@sourcegraph/cody-shared/src/sourcegraph-api/completions/nodeClient'

import { LocalIndexedKeywordSearch } from './chat/local-code-search'
import { CommandsController } from './commands/CommandsController'
import { BfgRetriever } from './completions/context/retrievers/bfg/bfg-retriever'
import { CommandsController } from './custom-prompts/CommandsController'
import { ExtensionApi } from './extension-api'
import { activate as activateCommon } from './extension.common'
import { VSCODE_WEB_RECIPES } from './extension.web'
Expand Down
2 changes: 1 addition & 1 deletion vscode/src/non-stop/FixupTypingUI.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import * as vscode from 'vscode'

import { EDIT_COMMAND, menu_buttons } from '../custom-prompts/utils/menu'
import { EDIT_COMMAND, menu_buttons } from '../commands/utils/menu'
import { getActiveEditor } from '../editor/active-editor'

import { FixupTask } from './FixupTask'
Expand Down
2 changes: 1 addition & 1 deletion vscode/src/services/LocalAppDetector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { LOCAL_APP_URL } from '@sourcegraph/cody-shared/src/sourcegraph-api/envi

import { version } from '../../package.json'
import { isOsSupportedByApp, LocalEnv } from '../chat/protocol'
import { constructFileUri } from '../custom-prompts/utils/helpers'
import { constructFileUri } from '../commands/utils/helpers'
import { fetch } from '../fetch'
import { logDebug, logError } from '../log'

Expand Down