Skip to content

Commit c28123a

Browse files
committed
Adding execute bash tool setup to the Q Agentic chat
1 parent ef4e872 commit c28123a

File tree

6 files changed

+105
-72
lines changed

6 files changed

+105
-72
lines changed

packages/core/src/codewhispererChat/controllers/chat/controller.ts

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ import { amazonQTabSuffix } from '../../../shared/constants'
8787
import { FsRead, FsReadParams } from '../../tools/fsRead'
8888
import { InvokeOutput, OutputKind } from '../../tools/toolShared'
8989
import { FsWrite, FsWriteCommand } from '../../tools/fsWrite'
90+
import { ExecuteBash, ExecuteBashParams } from '../../tools/executeBash'
9091

9192
export interface ChatControllerMessagePublishers {
9293
readonly processPromptChatMessage: MessagePublisher<PromptMessage>
@@ -947,12 +948,12 @@ export class ChatController {
947948
const toolResults: ToolResult[] = []
948949
try {
949950
switch (toolUse.name) {
950-
// case 'execute_bash': {
951-
// const executeBash = new ExecuteBash(toolUse.input as unknown as ExecuteBashParams)
952-
// await executeBash.validate()
953-
// result = await executeBash.invoke(process.stdout)
954-
// break
955-
// }
951+
case 'executeBash': {
952+
const executeBash = new ExecuteBash(toolUse.input as unknown as ExecuteBashParams)
953+
await executeBash.validate()
954+
result = await executeBash.invoke(process.stdout)
955+
break
956+
}
956957
case 'fsRead': {
957958
const fsRead = new FsRead(toolUse.input as unknown as FsReadParams)
958959
await fsRead.validate()
@@ -966,12 +967,12 @@ export class ChatController {
966967
break
967968
}
968969
default:
969-
getLogger().warn('Received invalid tool: %s', toolUse.name)
970-
break
970+
throw new ToolkitError(`Unsupported tool: ${toolUse.name}`)
971971
}
972972
if (!result) {
973973
throw new ToolkitError('Failed to execute tool and get results')
974974
}
975+
975976
toolResults.push({
976977
content: [
977978
result.output.kind === OutputKind.Text
@@ -982,7 +983,14 @@ export class ChatController {
982983
status: 'success',
983984
})
984985
} catch (e: any) {
985-
toolResults.push({ content: [{ text: e.message }], toolUseId: toolUse.toolUseId, status: 'error' })
986+
const errorMessage = e instanceof Error ? e.message : 'Unknown error occurred during tool execution'
987+
getLogger().debug(`Tool execution failed: ${toolUse.name} - ${errorMessage}`)
988+
989+
toolResults.push({
990+
content: [{ text: errorMessage }],
991+
toolUseId: toolUse.toolUseId,
992+
status: 'error',
993+
})
986994
}
987995

988996
await this.generateResponse(

packages/core/src/codewhispererChat/controllers/chat/messenger/messenger.ts

Lines changed: 53 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ import { extractAuthFollowUp } from '../../../../amazonq/util/authUtils'
4141
import { helpMessage } from '../../../../amazonq/webview/ui/texts/constants'
4242
import { ChatItemButton, ChatItemFormItem, MynahUIDataModel } from '@aws/mynah-ui'
4343
import { ChatHistoryManager } from '../../../storages/chatHistory'
44+
import { ExecuteBashParams } from '../../../tools/executeBash'
45+
import { FsWriteCommand } from '../../../tools/fsWrite'
4446

4547
export type StaticTextResponseType = 'quick-action-help' | 'onboarding-help' | 'transform' | 'help'
4648

@@ -604,57 +606,57 @@ export class Messenger {
604606
// return toolUse.name === 'fs_write'
605607
// }
606608
private getToolUseMessage(toolUse: ToolUse) {
607-
if (toolUse.name === 'fs_read') {
608-
return `Reading the file at \`${(toolUse.input as any)?.path}\` using the \`fs_read\` tool.`
609+
if (toolUse.name === 'fsRead') {
610+
return `Reading the file at \`${(toolUse.input as any)?.path}\` using the \`fsRead\` tool.`
611+
}
612+
if (toolUse.name === 'executeBash') {
613+
const input = toolUse.input as unknown as ExecuteBashParams
614+
return `Executing the bash command
615+
\`\`\`bash
616+
${input.command}
617+
\`\`\`
618+
using the \`executeBash\` tool.`
619+
}
620+
if (toolUse.name === 'fsWrite') {
621+
const input = toolUse.input as unknown as FsWriteCommand
622+
switch (input.command) {
623+
case 'create': {
624+
return `Writing
625+
\`\`\`
626+
${input.fileText}
627+
\`\`\`
628+
into the file at \`${input.path}\` using the \`fsWrite\` tool.`
629+
}
630+
case 'strReplace': {
631+
return `Replacing
632+
\`\`\`
633+
${input.oldStr}
634+
\`\`\`
635+
with
636+
\`\`\`
637+
${input.newStr}
638+
\`\`\`
639+
at \`${input.path}\` using the \`fsWrite\` tool.`
640+
}
641+
case 'insert': {
642+
return `Inserting
643+
\`\`\`
644+
${input.newStr}
645+
\`\`\`
646+
at line
647+
\`\`\`
648+
${input.insertLine}
649+
\`\`\`
650+
at \`${input.path}\` using the \`fsWrite\` tool.`
651+
}
652+
case 'append': {
653+
return `Appending
654+
\`\`\`
655+
${input.newStr}
656+
\`\`\`
657+
at \`${input.path}\` using the \`fsWrite\` tool.`
658+
}
659+
}
609660
}
610-
// if (toolUse.name === 'execute_bash') {
611-
// const input = toolUse.input as unknown as ExecuteBashParams
612-
// return `Executing the bash command
613-
// \`\`\`bash
614-
// ${input.command}
615-
// \`\`\`
616-
// using the \`execute_bash\` tool.`
617-
// }
618-
// if (toolUse.name === 'fs_write') {
619-
// const input = toolUse.input as unknown as FsWriteParams
620-
// switch (input.command) {
621-
// case 'create': {
622-
// return `Writing
623-
// \`\`\`
624-
// ${input.file_text}
625-
// \`\`\`
626-
// into the file at \`${input.path}\` using the \`fs_write\` tool.`
627-
// }
628-
// case 'str_replace': {
629-
// return `Replacing
630-
// \`\`\`
631-
// ${input.old_str}
632-
// \`\`\`
633-
// with
634-
// \`\`\`
635-
// ${input.new_str}
636-
// \`\`\`
637-
// at \`${input.path}\` using the \`fs_write\` tool.`
638-
// }
639-
// case 'insert': {
640-
// return `Inserting
641-
// \`\`\`
642-
// ${input.new_str}
643-
// \`\`\`
644-
// at line
645-
// \`\`\`
646-
// ${input.insert_line}
647-
// \`\`\`
648-
// at \`${input.path}\` using the \`fs_write\` tool.`
649-
// }
650-
// case 'append': {
651-
// return `Appending
652-
// \`\`\`
653-
// ${input.new_str}
654-
// \`\`\`
655-
// at \`${input.path}\` using the \`fs_write\` tool.`
656-
// }
657-
// }
658-
// }
659661
}
660662
}

packages/core/src/codewhispererChat/tools/executeBash.ts

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -111,27 +111,32 @@ export class ExecuteBash {
111111
stderr: stderrTrunc + (stderrSuffix ? ' ... truncated' : ''),
112112
}
113113

114-
return {
114+
resolve({
115115
output: {
116116
kind: OutputKind.Json,
117117
content: outputJson,
118118
},
119-
}
119+
})
120120
} catch (err: any) {
121121
this.logger.error(`Failed to execute bash command '${this.command}': ${err.message}`)
122-
throw new Error(`Failed to execute command: ${err.message}`)
122+
reject(new Error(`Failed to execute command: ${err.message}`))
123123
}
124124
})
125125
}
126126

127127
private static handleChunk(chunk: string, buffer: string[], updates: Writable) {
128-
const lines = chunk.split(/\r?\n/)
129-
for (const line of lines) {
130-
updates.write(`${line}\n`)
131-
buffer.push(line)
132-
if (buffer.length > lineCount) {
133-
buffer.shift()
128+
try {
129+
const lines = chunk.split(/\r?\n/)
130+
for (const line of lines) {
131+
updates.write(`${line}\n`)
132+
buffer.push(line)
133+
if (buffer.length > lineCount) {
134+
buffer.shift()
135+
}
134136
}
137+
} catch (error) {
138+
// Log the error but don't let it crash the process
139+
throw new Error('Error handling output chunk')
135140
}
136141
}
137142

packages/core/src/codewhispererChat/tools/toolShared.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export enum OutputKind {
1616
export interface InvokeOutput {
1717
output: {
1818
kind: OutputKind
19-
content: string
19+
content: string | any
2020
}
2121
}
2222

packages/core/src/codewhispererChat/tools/tool_index.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,5 +54,23 @@
5454
},
5555
"required": ["command", "path"]
5656
}
57+
},
58+
"executeBash": {
59+
"name": "executeBash",
60+
"description": "Execute the specified bash command.",
61+
"inputSchema": {
62+
"type": "object",
63+
"properties": {
64+
"command": {
65+
"type": "string",
66+
"description": "Bash command to execute"
67+
},
68+
"cwd": {
69+
"type": "string",
70+
"description": "Parameter to set the current working directory for the bash command."
71+
}
72+
},
73+
"required": ["command", "cwd"]
74+
}
5775
}
5876
}

packages/core/src/test/codewhispererChat/tools/fsRead.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ describe('FsRead Tool', () => {
5353
const result = await fsRead.invoke()
5454

5555
const lines = result.output.content.split('\n')
56-
const hasFileA = lines.some((line) => line.includes('- ') && line.includes('fileA.txt'))
57-
const hasSubfolder = lines.some((line) => line.includes('d ') && line.includes('subfolder'))
56+
const hasFileA = lines.some((line: string | string[]) => line.includes('- ') && line.includes('fileA.txt'))
57+
const hasSubfolder = lines.some((line: string | string[]) => line.includes('d ') && line.includes('subfolder'))
5858

5959
assert.ok(hasFileA, 'Should list fileA.txt in the directory output')
6060
assert.ok(hasSubfolder, 'Should list the subfolder in the directory output')

0 commit comments

Comments
 (0)