From 0d4c188eb8438cc902ed225a8f7591653407afbf Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Thu, 19 Jun 2025 15:08:20 -0700 Subject: [PATCH 01/19] feat(amazonq): parse message from agentic reviewer --- packages/amazonq/src/lsp/chat/messages.ts | 61 +++++++++++++++++-- .../commands/startSecurityScan.ts | 4 +- .../src/codewhisperer/models/constants.ts | 2 + .../service/diagnosticsProvider.ts | 1 - 4 files changed, 60 insertions(+), 8 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index d78d33f55e7..1562e544bb3 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -65,7 +65,16 @@ import { v4 as uuidv4 } from 'uuid' import * as vscode from 'vscode' import { Disposable, LanguageClient, Position, TextDocumentIdentifier } from 'vscode-languageclient' import { AmazonQChatViewProvider } from './webviewProvider' -import { AuthUtil, ReferenceLogViewProvider } from 'aws-core-vscode/codewhisperer' +import { + AggregatedCodeScanIssue, + AuthUtil, + CodeAnalysisScope, + CodeWhispererSettings, + initSecurityScanRender, + ReferenceLogViewProvider, + SecurityIssueTreeViewProvider, + CodeWhispererConstants, +} from 'aws-core-vscode/codewhisperer' import { amazonQDiffScheme, AmazonQPromptSettings, messages, openUrl, isTextEditor } from 'aws-core-vscode/shared' import { DefaultAmazonQAppInitContext, @@ -79,6 +88,7 @@ import { isValidResponseError } from './error' import { decryptResponse, encryptRequest } from '../encryption' import { getCursorState } from '../utils' import { focusAmazonQPanel } from './commands' +import { ChatMessage } from '@aws/language-server-runtimes/server-interface' export function registerActiveEditorChangeListener(languageClient: LanguageClient) { let debounceTimer: NodeJS.Timeout | undefined @@ -292,7 +302,8 @@ export function registerMessageListeners( encryptionKey, provider, chatParams.tabId, - chatDisposable + chatDisposable, + languageClient ) } catch (e) { const errorMsg = `Error occurred during chat request: ${e}` @@ -308,7 +319,8 @@ export function registerMessageListeners( encryptionKey, provider, chatParams.tabId, - chatDisposable + chatDisposable, + languageClient ) } finally { chatStreamTokens.delete(chatParams.tabId) @@ -339,7 +351,8 @@ export function registerMessageListeners( encryptionKey, provider, message.params.tabId, - quickActionDisposable + quickActionDisposable, + languageClient ) break } @@ -573,6 +586,11 @@ async function handlePartialResult( ) { const decryptedMessage = await decryptResponse(partialResult, encryptionKey) + decryptedMessage.additionalMessages = decryptedMessage.additionalMessages?.filter( + (message) => + !(message.messageId !== undefined && message.messageId.endsWith(CodeWhispererConstants.findingsSuffix)) + ) + if (decryptedMessage.body !== undefined) { void provider.webview?.postMessage({ command: chatRequestType.method, @@ -593,10 +611,13 @@ async function handleCompleteResult( encryptionKey: Buffer | undefined, provider: AmazonQChatViewProvider, tabId: string, - disposable: Disposable + disposable: Disposable, + languageClient: LanguageClient ) { const decryptedMessage = await decryptResponse(result, encryptionKey) + handleSecurityFindings(decryptedMessage, languageClient) + void provider.webview?.postMessage({ command: chatRequestType.method, params: decryptedMessage, @@ -610,6 +631,36 @@ async function handleCompleteResult( disposable.dispose() } +function handleSecurityFindings( + decryptedMessage: { additionalMessages?: ChatMessage[] }, + languageClient: LanguageClient +): void { + if (decryptedMessage.additionalMessages == undefined || decryptedMessage.additionalMessages.length == 0) { + return + } + for (let i = decryptedMessage.additionalMessages.length; i >= 0; i--) { + const message = decryptedMessage.additionalMessages[i] + if (message.messageId !== undefined && message.messageId.endsWith(CodeWhispererConstants.findingsSuffix)) { + if (message.body != null) { + try { + const aggregatedCodeScanIssue: AggregatedCodeScanIssue = JSON.parse(message.body) + for (const issue of aggregatedCodeScanIssue.issues) { + issue.visible = !CodeWhispererSettings.instance.getIgnoredSecurityIssues().includes(issue.title) + if (issue.suggestedFixes == undefined) { + issue.suggestedFixes = [] + } + } + initSecurityScanRender([aggregatedCodeScanIssue], undefined, CodeAnalysisScope.PROJECT) + SecurityIssueTreeViewProvider.focus() + } catch (e) { + languageClient.info('Failed to parse findings') + } + } + decryptedMessage.additionalMessages.splice(i, 1) + } + } +} + async function resolveChatResponse( requestMethod: string, params: any, diff --git a/packages/core/src/codewhisperer/commands/startSecurityScan.ts b/packages/core/src/codewhisperer/commands/startSecurityScan.ts index d04fe6effc3..5ff6d13bd91 100644 --- a/packages/core/src/codewhisperer/commands/startSecurityScan.ts +++ b/packages/core/src/codewhisperer/commands/startSecurityScan.ts @@ -401,7 +401,7 @@ export function showSecurityScanResults( zipMetadata: ZipMetadata, totalIssues: number ) { - initSecurityScanRender(securityRecommendationCollection, context, editor, scope) + initSecurityScanRender(securityRecommendationCollection, editor, scope) if (scope === CodeWhispererConstants.CodeAnalysisScope.PROJECT) { populateCodeScanLogStream(zipMetadata.scannedFiles) @@ -439,7 +439,7 @@ export function showScanResultsInChat( break } - initSecurityScanRender(securityRecommendationCollection, context, editor, scope) + initSecurityScanRender(securityRecommendationCollection, editor, scope) if (totalIssues > 0) { SecurityIssueTreeViewProvider.focus() } diff --git a/packages/core/src/codewhisperer/models/constants.ts b/packages/core/src/codewhisperer/models/constants.ts index 319127cba20..84e99699d79 100644 --- a/packages/core/src/codewhisperer/models/constants.ts +++ b/packages/core/src/codewhisperer/models/constants.ts @@ -900,3 +900,5 @@ export const predictionTrackerDefaultConfig = { maxAgeMs: 30000, maxSupplementalContext: 15, } + +export const findingsSuffix = '_findings' diff --git a/packages/core/src/codewhisperer/service/diagnosticsProvider.ts b/packages/core/src/codewhisperer/service/diagnosticsProvider.ts index 72407cd80f5..f181bdb146d 100644 --- a/packages/core/src/codewhisperer/service/diagnosticsProvider.ts +++ b/packages/core/src/codewhisperer/service/diagnosticsProvider.ts @@ -25,7 +25,6 @@ export const securityScanRender: SecurityScanRender = { export function initSecurityScanRender( securityRecommendationList: AggregatedCodeScanIssue[], - context: vscode.ExtensionContext, editor: vscode.TextEditor | undefined, scope: CodeAnalysisScope ) { From 983a87d09495d364ddbe864034408b7960f0a526 Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Fri, 20 Jun 2025 14:44:28 -0700 Subject: [PATCH 02/19] feat(amazonq): support array of AggregatedCodeScanIssue objects from flare --- packages/amazonq/src/lsp/chat/messages.ts | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index 1562e544bb3..0ff9f1785fc 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -635,22 +635,23 @@ function handleSecurityFindings( decryptedMessage: { additionalMessages?: ChatMessage[] }, languageClient: LanguageClient ): void { - if (decryptedMessage.additionalMessages == undefined || decryptedMessage.additionalMessages.length == 0) { + if (decryptedMessage.additionalMessages === undefined || decryptedMessage.additionalMessages.length === 0) { return } - for (let i = decryptedMessage.additionalMessages.length; i >= 0; i--) { + for (let i = decryptedMessage.additionalMessages.length - 1; i >= 0; i--) { const message = decryptedMessage.additionalMessages[i] if (message.messageId !== undefined && message.messageId.endsWith(CodeWhispererConstants.findingsSuffix)) { - if (message.body != null) { + if (message.body !== undefined) { try { - const aggregatedCodeScanIssue: AggregatedCodeScanIssue = JSON.parse(message.body) - for (const issue of aggregatedCodeScanIssue.issues) { - issue.visible = !CodeWhispererSettings.instance.getIgnoredSecurityIssues().includes(issue.title) - if (issue.suggestedFixes == undefined) { - issue.suggestedFixes = [] + const aggregatedCodeScanIssues: AggregatedCodeScanIssue[] = JSON.parse(message.body) + for (const aggregatedCodeScanIssue of aggregatedCodeScanIssues) { + for (const issue of aggregatedCodeScanIssue.issues) { + issue.visible = !CodeWhispererSettings.instance + .getIgnoredSecurityIssues() + .includes(issue.title) } } - initSecurityScanRender([aggregatedCodeScanIssue], undefined, CodeAnalysisScope.PROJECT) + initSecurityScanRender(aggregatedCodeScanIssues, undefined, CodeAnalysisScope.PROJECT) SecurityIssueTreeViewProvider.focus() } catch (e) { languageClient.info('Failed to parse findings') From 51f72b099def14681f8540345d4f6bb8f8f0c202 Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Fri, 20 Jun 2025 15:48:19 -0700 Subject: [PATCH 03/19] fix(amazonq): change findings suffix to make it more precise --- packages/core/src/codewhisperer/models/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/codewhisperer/models/constants.ts b/packages/core/src/codewhisperer/models/constants.ts index 84e99699d79..c02ba6ff8bc 100644 --- a/packages/core/src/codewhisperer/models/constants.ts +++ b/packages/core/src/codewhisperer/models/constants.ts @@ -901,4 +901,4 @@ export const predictionTrackerDefaultConfig = { maxSupplementalContext: 15, } -export const findingsSuffix = '_findings' +export const findingsSuffix = '_qCodeScanFindings' From 9e58c93cf13f965b6d1c25b8c58a6054f887cab0 Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Fri, 20 Jun 2025 20:00:42 -0700 Subject: [PATCH 04/19] feat(amazonq): store the findings locally and include a path to them in ChatParams --- packages/amazonq/src/lsp/chat/messages.ts | 13 ++++++++ .../service/diagnosticsProvider.ts | 31 +++++++++++++++++++ .../service/securityIssueProvider.ts | 10 ++++++ .../core/src/shared/settings-toolkit.gen.ts | 4 +-- 4 files changed, 56 insertions(+), 2 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index 0ff9f1785fc..b6f26f3a715 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -72,6 +72,7 @@ import { CodeWhispererSettings, initSecurityScanRender, ReferenceLogViewProvider, + SecurityIssueProvider, SecurityIssueTreeViewProvider, CodeWhispererConstants, } from 'aws-core-vscode/codewhisperer' @@ -89,6 +90,7 @@ import { decryptResponse, encryptRequest } from '../encryption' import { getCursorState } from '../utils' import { focusAmazonQPanel } from './commands' import { ChatMessage } from '@aws/language-server-runtimes/server-interface' +import path from 'path' export function registerActiveEditorChangeListener(languageClient: LanguageClient) { let debounceTimer: NodeJS.Timeout | undefined @@ -285,6 +287,17 @@ export function registerMessageListeners( if (editor) { chatParams.cursorState = getCursorState(editor.selections) chatParams.textDocument = { uri: editor.document.uri.toString() } + chatParams.findingsPath = path.join( + __dirname, + '..', + '..', + '..', + '..', + '..', + '..', + 'findings', + `SecurityIssues-${SecurityIssueProvider.instance.id}.json` + ) } const chatRequest = await encryptRequest(chatParams, encryptionKey) diff --git a/packages/core/src/codewhisperer/service/diagnosticsProvider.ts b/packages/core/src/codewhisperer/service/diagnosticsProvider.ts index f181bdb146d..1877d2fd59d 100644 --- a/packages/core/src/codewhisperer/service/diagnosticsProvider.ts +++ b/packages/core/src/codewhisperer/service/diagnosticsProvider.ts @@ -8,6 +8,8 @@ import { CodeScanIssue, AggregatedCodeScanIssue } from '../models/model' import { CodeAnalysisScope, codewhispererDiagnosticSourceLabel } from '../models/constants' import { SecurityIssueTreeViewProvider } from './securityIssueTreeViewProvider' import { SecurityIssueProvider } from './securityIssueProvider' +import fs = require('fs') +import path from 'path' export interface SecurityDiagnostic extends vscode.Diagnostic { findingId?: string @@ -39,6 +41,15 @@ export function initSecurityScanRender( updateSecurityIssuesForProviders(securityRecommendation, scope === CodeAnalysisScope.FILE_AUTO) } securityScanRender.initialized = true + const issuesJson = JSON.stringify(SecurityIssueProvider.instance.issues) + const filePath = path.join(__dirname, '..', '..', '..', '..', '..', '..', 'findings') + fs.existsSync(filePath) || fs.mkdirSync(filePath) + fs.writeFileSync( + path.join(filePath, `SecurityIssues-${SecurityIssueProvider.instance.id}.json`), + issuesJson, + 'utf8' + ) + cleanOldFiles(filePath) } function updateSecurityIssuesForProviders(securityRecommendation: AggregatedCodeScanIssue, isAutoScope?: boolean) { @@ -56,6 +67,26 @@ function updateSecurityIssuesForProviders(securityRecommendation: AggregatedCode SecurityIssueTreeViewProvider.instance.refresh() } +function cleanOldFiles(dirPath: string, maxfiles = 100) { + const files = fs.readdirSync(dirPath) + if (files.length > maxfiles) { + type Stat = { fileName: string; mtime: number } + const stats: Stat[] = [] + for (const file of files) { + const stat = fs.statSync(path.join(dirPath, file[0])) + stats.push({ + fileName: file[0], + mtime: stat.mtime.getTime(), + }) + } + const sortedStats = stats.sort((a: Stat, b: Stat) => a.mtime - b.mtime) + const numberToDelete = files.length - maxfiles + for (let i = 0; i < numberToDelete; i++) { + fs.rmSync(path.join(dirPath, sortedStats[i].fileName)) + } + } +} + export function updateSecurityDiagnosticCollection(securityRecommendation: AggregatedCodeScanIssue) { const filePath = securityRecommendation.filePath const uri = vscode.Uri.file(filePath) diff --git a/packages/core/src/codewhisperer/service/securityIssueProvider.ts b/packages/core/src/codewhisperer/service/securityIssueProvider.ts index 61957e6eca5..8a1a5b6f688 100644 --- a/packages/core/src/codewhisperer/service/securityIssueProvider.ts +++ b/packages/core/src/codewhisperer/service/securityIssueProvider.ts @@ -5,6 +5,7 @@ import * as vscode from 'vscode' import { AggregatedCodeScanIssue, CodeScanIssue, SuggestedFix } from '../models/model' +import { randomUUID } from '../../shared/crypto' export class SecurityIssueProvider { static #instance: SecurityIssueProvider public static get instance() { @@ -20,6 +21,15 @@ export class SecurityIssueProvider { this._issues = issues } + private _id: string = randomUUID() + public get id() { + return this._id + } + + public set id(id: string) { + this._id = id + } + public handleDocumentChange(event: vscode.TextDocumentChangeEvent) { // handleDocumentChange function may be triggered while testing by our own code generation. if (!event.contentChanges || event.contentChanges.length === 0) { diff --git a/packages/core/src/shared/settings-toolkit.gen.ts b/packages/core/src/shared/settings-toolkit.gen.ts index 10020cf51f9..819d6741f36 100644 --- a/packages/core/src/shared/settings-toolkit.gen.ts +++ b/packages/core/src/shared/settings-toolkit.gen.ts @@ -44,8 +44,8 @@ export const toolkitSettings = { "jsonResourceModification": {}, "amazonqLSP": {}, "amazonqLSPInline": {}, - "amazonqLSPInlineChat": {}, - "amazonqChatLSP": {} + "amazonqChatLSP": {}, + "amazonqLSPInlineChat": {} }, "aws.resources.enabledResources": {}, "aws.lambda.recentlyUploaded": {}, From 1f4cfc4aeff5f8f760f373d97aca29ed1a318b66 Mon Sep 17 00:00:00 2001 From: Nitish Kumar Singh Date: Sat, 21 Jun 2025 19:36:22 -0700 Subject: [PATCH 05/19] fix: update q code review finding message suffix --- packages/core/src/codewhisperer/models/constants.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/codewhisperer/models/constants.ts b/packages/core/src/codewhisperer/models/constants.ts index c02ba6ff8bc..04ee34a6b50 100644 --- a/packages/core/src/codewhisperer/models/constants.ts +++ b/packages/core/src/codewhisperer/models/constants.ts @@ -901,4 +901,4 @@ export const predictionTrackerDefaultConfig = { maxSupplementalContext: 15, } -export const findingsSuffix = '_qCodeScanFindings' +export const findingsSuffix = '_qCodeReviewFindings' From 2fc9f36b80ec6b05011127f25c78ecaa3a866edd Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Tue, 24 Jun 2025 09:59:38 -0700 Subject: [PATCH 06/19] fix(amazonq): change the findings suffix, adjust the stored findings when lines are modified. --- packages/core/src/codewhisperer/activation.ts | 1 + .../src/codewhisperer/models/constants.ts | 2 +- .../service/diagnosticsProvider.ts | 32 +------------------ .../service/securityIssueProvider.ts | 26 +++++++++++++++ 4 files changed, 29 insertions(+), 32 deletions(-) diff --git a/packages/core/src/codewhisperer/activation.ts b/packages/core/src/codewhisperer/activation.ts index d6dd7fdc61d..c1fcfb2524b 100644 --- a/packages/core/src/codewhisperer/activation.ts +++ b/packages/core/src/codewhisperer/activation.ts @@ -476,6 +476,7 @@ export async function activate(context: ExtContext): Promise { CodeWhispererConstants.amazonqIgnoreNextLine ) ) + SecurityIssueProvider.instance.cleanOldFiles() }), vscode.window.createTreeView(SecurityIssueTreeViewProvider.viewType, { treeDataProvider: SecurityIssueTreeViewProvider.instance, diff --git a/packages/core/src/codewhisperer/models/constants.ts b/packages/core/src/codewhisperer/models/constants.ts index c02ba6ff8bc..04ee34a6b50 100644 --- a/packages/core/src/codewhisperer/models/constants.ts +++ b/packages/core/src/codewhisperer/models/constants.ts @@ -901,4 +901,4 @@ export const predictionTrackerDefaultConfig = { maxSupplementalContext: 15, } -export const findingsSuffix = '_qCodeScanFindings' +export const findingsSuffix = '_qCodeReviewFindings' diff --git a/packages/core/src/codewhisperer/service/diagnosticsProvider.ts b/packages/core/src/codewhisperer/service/diagnosticsProvider.ts index 1877d2fd59d..b88899c4f37 100644 --- a/packages/core/src/codewhisperer/service/diagnosticsProvider.ts +++ b/packages/core/src/codewhisperer/service/diagnosticsProvider.ts @@ -8,8 +8,6 @@ import { CodeScanIssue, AggregatedCodeScanIssue } from '../models/model' import { CodeAnalysisScope, codewhispererDiagnosticSourceLabel } from '../models/constants' import { SecurityIssueTreeViewProvider } from './securityIssueTreeViewProvider' import { SecurityIssueProvider } from './securityIssueProvider' -import fs = require('fs') -import path from 'path' export interface SecurityDiagnostic extends vscode.Diagnostic { findingId?: string @@ -41,15 +39,7 @@ export function initSecurityScanRender( updateSecurityIssuesForProviders(securityRecommendation, scope === CodeAnalysisScope.FILE_AUTO) } securityScanRender.initialized = true - const issuesJson = JSON.stringify(SecurityIssueProvider.instance.issues) - const filePath = path.join(__dirname, '..', '..', '..', '..', '..', '..', 'findings') - fs.existsSync(filePath) || fs.mkdirSync(filePath) - fs.writeFileSync( - path.join(filePath, `SecurityIssues-${SecurityIssueProvider.instance.id}.json`), - issuesJson, - 'utf8' - ) - cleanOldFiles(filePath) + SecurityIssueProvider.instance.cleanOldFiles() } function updateSecurityIssuesForProviders(securityRecommendation: AggregatedCodeScanIssue, isAutoScope?: boolean) { @@ -67,26 +57,6 @@ function updateSecurityIssuesForProviders(securityRecommendation: AggregatedCode SecurityIssueTreeViewProvider.instance.refresh() } -function cleanOldFiles(dirPath: string, maxfiles = 100) { - const files = fs.readdirSync(dirPath) - if (files.length > maxfiles) { - type Stat = { fileName: string; mtime: number } - const stats: Stat[] = [] - for (const file of files) { - const stat = fs.statSync(path.join(dirPath, file[0])) - stats.push({ - fileName: file[0], - mtime: stat.mtime.getTime(), - }) - } - const sortedStats = stats.sort((a: Stat, b: Stat) => a.mtime - b.mtime) - const numberToDelete = files.length - maxfiles - for (let i = 0; i < numberToDelete; i++) { - fs.rmSync(path.join(dirPath, sortedStats[i].fileName)) - } - } -} - export function updateSecurityDiagnosticCollection(securityRecommendation: AggregatedCodeScanIssue) { const filePath = securityRecommendation.filePath const uri = vscode.Uri.file(filePath) diff --git a/packages/core/src/codewhisperer/service/securityIssueProvider.ts b/packages/core/src/codewhisperer/service/securityIssueProvider.ts index 8a1a5b6f688..4727aa43caa 100644 --- a/packages/core/src/codewhisperer/service/securityIssueProvider.ts +++ b/packages/core/src/codewhisperer/service/securityIssueProvider.ts @@ -6,6 +6,8 @@ import * as vscode from 'vscode' import { AggregatedCodeScanIssue, CodeScanIssue, SuggestedFix } from '../models/model' import { randomUUID } from '../../shared/crypto' +import fs = require('fs') +import path from 'path' export class SecurityIssueProvider { static #instance: SecurityIssueProvider public static get instance() { @@ -167,4 +169,28 @@ export class SecurityIssueProvider { (i) => i.title === issue.title && i.startLine === issue.startLine && i.endLine === issue.endLine ) } + + public cleanOldFiles(maxfiles = 100): void { + const issuesJson = JSON.stringify(this._issues) + const dirPath = path.join(__dirname, '..', '..', '..', '..', '..', '..', 'findings') + fs.existsSync(dirPath) || fs.mkdirSync(dirPath) + fs.writeFileSync(path.join(dirPath, `SecurityIssues-${this._id}.json`), issuesJson, 'utf8') + const files = fs.readdirSync(dirPath) + if (files.length > maxfiles) { + type Stat = { fileName: string; mtime: number } + const stats: Stat[] = [] + for (const file of files) { + const stat = fs.statSync(path.join(dirPath, file[0])) + stats.push({ + fileName: file[0], + mtime: stat.mtime.getTime(), + }) + } + const sortedStats = stats.sort((a: Stat, b: Stat) => a.mtime - b.mtime) + const numberToDelete = files.length - maxfiles + for (let i = 0; i < numberToDelete; i++) { + fs.rmSync(path.join(dirPath, sortedStats[i].fileName)) + } + } + } } From e6d2f06941576f396c666068ca64da50f3970039 Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Wed, 25 Jun 2025 09:22:19 -0700 Subject: [PATCH 07/19] feat(amazonq): improve explain with q option for code issue --- packages/amazonq/src/lsp/chat/commands.ts | 59 ++++++++++++------- packages/amazonq/src/lsp/chat/messages.ts | 22 +++---- packages/core/src/codewhisperer/activation.ts | 2 +- .../codewhisperer/commands/basicCommands.ts | 2 +- .../service/diagnosticsProvider.ts | 2 +- .../securityIssueCodeActionProvider.ts | 2 +- .../service/securityIssueHoverProvider.ts | 52 ++++++++-------- 7 files changed, 81 insertions(+), 60 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/commands.ts b/packages/amazonq/src/lsp/chat/commands.ts index 115118a4ad2..f1ecdd88918 100644 --- a/packages/amazonq/src/lsp/chat/commands.ts +++ b/packages/amazonq/src/lsp/chat/commands.ts @@ -7,8 +7,9 @@ import { Commands, globals } from 'aws-core-vscode/shared' import { window } from 'vscode' import { AmazonQChatViewProvider } from './webviewProvider' import { CodeScanIssue } from 'aws-core-vscode/codewhisperer' -import { EditorContextExtractor } from 'aws-core-vscode/codewhispererChat' import { DefaultAmazonQAppInitContext } from 'aws-core-vscode/amazonq' +import * as vscode from 'vscode' +import * as path from 'path' /** * TODO: Re-enable these once we can figure out which path they're going to live in @@ -27,25 +28,35 @@ export function registerCommands(provider: AmazonQChatViewProvider) { type: 'chatMessage', }) }), - Commands.register('aws.amazonq.explainIssue', async (issue: CodeScanIssue) => { + Commands.register('aws.amazonq.explainIssue', async (issue: CodeScanIssue, filePath: string) => { void focusAmazonQPanel().then(async () => { - const editorContextExtractor = new EditorContextExtractor() - const extractedContext = await editorContextExtractor.extractContextForTrigger('ContextMenu') - const selectedCode = - extractedContext?.activeFileContext?.fileText - ?.split('\n') - .slice(issue.startLine, issue.endLine) - .join('\n') ?? '' - - // The message that gets sent to the UI - const uiMessage = [ - 'Explain the ', - issue.title, - ' issue in the following code:', - '\n```\n', - selectedCode, - '\n```', - ].join('') + let selectedCode: string | undefined = undefined + if (issue && filePath) { + const range = new vscode.Range(issue.startLine, 0, issue.endLine, 0) + await vscode.workspace.openTextDocument(filePath).then((doc) => { + void vscode.window.showTextDocument(doc, { + selection: range, + viewColumn: vscode.ViewColumn.One, + preview: true, + }) + selectedCode = stripIndent(doc.getText(range)) + }) + } + const uiMessageComponents = [`## ${issue.title}`] + if (filePath) { + uiMessageComponents.push(`File: ${path.basename(filePath)}`) + } + if (issue.relatedVulnerabilities !== undefined && issue.relatedVulnerabilities.length > 0) { + uiMessageComponents.push( + `Common Weakness Enumeration (CWE): ${issue.relatedVulnerabilities.map((cwe) => `[${cwe}](https://cwe.mitre.org/data/definitions/${cwe}.html)`).join(', ')}` + ) + } + if (issue.detectorName !== undefined) { + uiMessageComponents.push(`Detector Library: [${issue.detectorName}](${issue.recommendation.url})`) + } + if (selectedCode) { + uiMessageComponents.push(`\`\`\`\n${selectedCode}\n\`\`\``) + } // The message that gets sent to the backend const contextMessage = `Explain the issue "${issue.title}" (${JSON.stringify( @@ -58,7 +69,7 @@ export function registerCommands(provider: AmazonQChatViewProvider) { selection: '', triggerType: 'contextMenu', prompt: { - prompt: uiMessage, // what gets sent to the user + prompt: uiMessageComponents.join('\n'), // what gets sent to the user escapedPrompt: contextMessage, // what gets sent to the backend }, autoSubmit: true, @@ -119,6 +130,14 @@ function registerGenericCommand(commandName: string, genericCommand: string, pro }) } +function stripIndent(text: string): string { + const lines = text.split('\n') + const minIndent = Math.min( + ...lines.filter((line) => line.trim()).map((line) => line.length - line.trimStart().length) + ) + return lines.map((line) => line.slice(minIndent)).join('\n') +} + /** * Importing focusAmazonQPanel from aws-core-vscode/amazonq leads to several dependencies down the chain not resolving since AmazonQ chat * is currently only activated on node, but the language server is activated on both web and node. diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index b6f26f3a715..75a1bcda7ef 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -287,17 +287,17 @@ export function registerMessageListeners( if (editor) { chatParams.cursorState = getCursorState(editor.selections) chatParams.textDocument = { uri: editor.document.uri.toString() } - chatParams.findingsPath = path.join( - __dirname, - '..', - '..', - '..', - '..', - '..', - '..', - 'findings', - `SecurityIssues-${SecurityIssueProvider.instance.id}.json` - ) + // chatParams.findingsPath = path.join( + // __dirname, + // '..', + // '..', + // '..', + // '..', + // '..', + // '..', + // 'findings', + // `SecurityIssues-${SecurityIssueProvider.instance.id}.json` + // ) } const chatRequest = await encryptRequest(chatParams, encryptionKey) diff --git a/packages/core/src/codewhisperer/activation.ts b/packages/core/src/codewhisperer/activation.ts index c1fcfb2524b..11bbce5a817 100644 --- a/packages/core/src/codewhisperer/activation.ts +++ b/packages/core/src/codewhisperer/activation.ts @@ -476,7 +476,7 @@ export async function activate(context: ExtContext): Promise { CodeWhispererConstants.amazonqIgnoreNextLine ) ) - SecurityIssueProvider.instance.cleanOldFiles() + // SecurityIssueProvider.instance.cleanOldFiles() }), vscode.window.createTreeView(SecurityIssueTreeViewProvider.viewType, { treeDataProvider: SecurityIssueTreeViewProvider.instance, diff --git a/packages/core/src/codewhisperer/commands/basicCommands.ts b/packages/core/src/codewhisperer/commands/basicCommands.ts index f2b67c49593..2bfbfb05299 100644 --- a/packages/core/src/codewhisperer/commands/basicCommands.ts +++ b/packages/core/src/codewhisperer/commands/basicCommands.ts @@ -836,7 +836,7 @@ export const regenerateFix = Commands.declare( export const explainIssue = Commands.declare( { id: 'aws.amazonq.security.explain' }, () => async (issueItem: IssueItem) => { - await vscode.commands.executeCommand('aws.amazonq.explainIssue', issueItem.issue) + await vscode.commands.executeCommand('aws.amazonq.explainIssue', issueItem.issue, issueItem.filePath) } ) diff --git a/packages/core/src/codewhisperer/service/diagnosticsProvider.ts b/packages/core/src/codewhisperer/service/diagnosticsProvider.ts index b88899c4f37..e110f37c121 100644 --- a/packages/core/src/codewhisperer/service/diagnosticsProvider.ts +++ b/packages/core/src/codewhisperer/service/diagnosticsProvider.ts @@ -39,7 +39,7 @@ export function initSecurityScanRender( updateSecurityIssuesForProviders(securityRecommendation, scope === CodeAnalysisScope.FILE_AUTO) } securityScanRender.initialized = true - SecurityIssueProvider.instance.cleanOldFiles() + // SecurityIssueProvider.instance.cleanOldFiles() } function updateSecurityIssuesForProviders(securityRecommendation: AggregatedCodeScanIssue, isAutoScope?: boolean) { diff --git a/packages/core/src/codewhisperer/service/securityIssueCodeActionProvider.ts b/packages/core/src/codewhisperer/service/securityIssueCodeActionProvider.ts index f1d01494d54..4dc7bceebe7 100644 --- a/packages/core/src/codewhisperer/service/securityIssueCodeActionProvider.ts +++ b/packages/core/src/codewhisperer/service/securityIssueCodeActionProvider.ts @@ -66,7 +66,7 @@ export class SecurityIssueCodeActionProvider implements vscode.CodeActionProvide `Amazon Q: Explain "${issue.title}"`, vscode.CodeActionKind.QuickFix ) - const explainWithQArgs = [issue] + const explainWithQArgs = [issue, group.filePath] explainWithQ.command = { title: 'Explain with Amazon Q', command: 'aws.amazonq.explainIssue', diff --git a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts index b82c10063e6..7d350965673 100644 --- a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts +++ b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts @@ -79,23 +79,24 @@ export class SecurityIssueHoverProvider implements vscode.HoverProvider { `${suggestedFix?.code && suggestedFix.description !== '' ? suggestedFix.description : issue.recommendation.text}\n\n` ) - const viewDetailsCommand = this._getCommandMarkdown( - 'aws.amazonq.openSecurityIssuePanel', - [issue, filePath], - 'eye', - 'View Details', - `Open "${amazonqCodeIssueDetailsTabTitle}"` - ) - markdownString.appendMarkdown(viewDetailsCommand) + // Commenting out view details button as we are deprecating the Code Issue Details page + // const viewDetailsCommand = this._getCommandMarkdown( + // 'aws.amazonq.openSecurityIssuePanel', + // [issue, filePath], + // 'eye', + // 'View Details', + // `Open "${amazonqCodeIssueDetailsTabTitle}"` + // ) + // markdownString.appendMarkdown(viewDetailsCommand) const explainWithQCommand = this._getCommandMarkdown( 'aws.amazonq.explainIssue', - [issue], + [issue, filePath], 'comment', 'Explain', 'Explain with Amazon Q' ) - markdownString.appendMarkdown(' | ' + explainWithQCommand) + markdownString.appendMarkdown(explainWithQCommand) const ignoreIssueCommand = this._getCommandMarkdown( 'aws.amazonq.security.ignore', @@ -115,21 +116,22 @@ export class SecurityIssueHoverProvider implements vscode.HoverProvider { ) markdownString.appendMarkdown(' | ' + ignoreSimilarIssuesCommand) - if (suggestedFix && suggestedFix.code) { - const applyFixCommand = this._getCommandMarkdown( - 'aws.amazonq.applySecurityFix', - [issue, filePath, 'hover'], - 'wrench', - 'Fix', - 'Fix with Amazon Q' - ) - markdownString.appendMarkdown(' | ' + applyFixCommand) - - markdownString.appendMarkdown('### Suggested Fix Preview\n') - markdownString.appendMarkdown( - `${this._makeCodeBlock(suggestedFix.code, issue.detectorId.split('/').shift())}\n` - ) - } + // Commenting out fix related options as we are deprecating Code Issue Details page + // if (suggestedFix && suggestedFix.code) { + // const applyFixCommand = this._getCommandMarkdown( + // 'aws.amazonq.applySecurityFix', + // [issue, filePath, 'hover'], + // 'wrench', + // 'Fix', + // 'Fix with Amazon Q' + // ) + // markdownString.appendMarkdown(' | ' + applyFixCommand) + + // markdownString.appendMarkdown('### Suggested Fix Preview\n') + // markdownString.appendMarkdown( + // `${this._makeCodeBlock(suggestedFix.code, issue.detectorId.split('/').shift())}\n` + // ) + // } return markdownString } From 865b0e3ecbf3e608dab79a243568db0213a56f16 Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Wed, 25 Jun 2025 09:49:59 -0700 Subject: [PATCH 08/19] feat(amazonq): update findings bar --- packages/amazonq/package.json | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index 8f398613ffb..2f225ec1a86 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -442,6 +442,11 @@ }, { "command": "aws.amazonq.openSecurityIssuePanel", + "when": "false && view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)", + "group": "inline@4" + }, + { + "command": "aws.amazonq.security.explain", "when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)", "group": "inline@4" }, @@ -452,7 +457,7 @@ }, { "command": "aws.amazonq.security.generateFix", - "when": "view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithoutFix", + "when": "false && view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithoutFix", "group": "inline@6" }, { @@ -535,16 +540,17 @@ "aws.amazonq.submenu.securityIssueMoreActions": [ { "command": "aws.amazonq.security.explain", + "when": "false", "group": "1_more@1" }, { "command": "aws.amazonq.applySecurityFix", - "when": "view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithFix", + "when": "false && view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithFix", "group": "1_more@3" }, { "command": "aws.amazonq.security.regenerateFix", - "when": "view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithFix", + "when": "false && view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithFix", "group": "1_more@4" }, { @@ -778,6 +784,7 @@ { "command": "aws.amazonq.security.explain", "title": "%AWS.command.amazonq.explainIssue%", + "icon": "$(search)", "enablement": "view == aws.amazonq.SecurityIssuesTree" }, { From 5b7df1683c895c153cf09f188bb2ed54aafcf0e4 Mon Sep 17 00:00:00 2001 From: Nitish Kumar Singh Date: Wed, 25 Jun 2025 10:52:18 -0700 Subject: [PATCH 09/19] fix: remove unused code --- packages/amazonq/src/lsp/chat/messages.ts | 2 - .../service/securityIssueHoverProvider.ts | 84 ------------------- 2 files changed, 86 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index 6b3da4a86d9..05c77ab812a 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -72,7 +72,6 @@ import { CodeWhispererSettings, initSecurityScanRender, ReferenceLogViewProvider, - SecurityIssueProvider, SecurityIssueTreeViewProvider, CodeWhispererConstants, } from 'aws-core-vscode/codewhisperer' @@ -90,7 +89,6 @@ import { decryptResponse, encryptRequest } from '../encryption' import { getCursorState } from '../utils' import { focusAmazonQPanel } from './commands' import { ChatMessage } from '@aws/language-server-runtimes/server-interface' -import path from 'path' export function registerActiveEditorChangeListener(languageClient: LanguageClient) { let debounceTimer: NodeJS.Timeout | undefined diff --git a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts index 7d350965673..3acabeff695 100644 --- a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts +++ b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts @@ -10,7 +10,6 @@ import path from 'path' import { AuthUtil } from '../util/authUtil' import { TelemetryHelper } from '../util/telemetryHelper' import { SecurityIssueProvider } from './securityIssueProvider' -import { amazonqCodeIssueDetailsTabTitle } from '../models/constants' export class SecurityIssueHoverProvider implements vscode.HoverProvider { static #instance: SecurityIssueHoverProvider @@ -79,16 +78,6 @@ export class SecurityIssueHoverProvider implements vscode.HoverProvider { `${suggestedFix?.code && suggestedFix.description !== '' ? suggestedFix.description : issue.recommendation.text}\n\n` ) - // Commenting out view details button as we are deprecating the Code Issue Details page - // const viewDetailsCommand = this._getCommandMarkdown( - // 'aws.amazonq.openSecurityIssuePanel', - // [issue, filePath], - // 'eye', - // 'View Details', - // `Open "${amazonqCodeIssueDetailsTabTitle}"` - // ) - // markdownString.appendMarkdown(viewDetailsCommand) - const explainWithQCommand = this._getCommandMarkdown( 'aws.amazonq.explainIssue', [issue, filePath], @@ -116,23 +105,6 @@ export class SecurityIssueHoverProvider implements vscode.HoverProvider { ) markdownString.appendMarkdown(' | ' + ignoreSimilarIssuesCommand) - // Commenting out fix related options as we are deprecating Code Issue Details page - // if (suggestedFix && suggestedFix.code) { - // const applyFixCommand = this._getCommandMarkdown( - // 'aws.amazonq.applySecurityFix', - // [issue, filePath, 'hover'], - // 'wrench', - // 'Fix', - // 'Fix with Amazon Q' - // ) - // markdownString.appendMarkdown(' | ' + applyFixCommand) - - // markdownString.appendMarkdown('### Suggested Fix Preview\n') - // markdownString.appendMarkdown( - // `${this._makeCodeBlock(suggestedFix.code, issue.detectorId.split('/').shift())}\n` - // ) - // } - return markdownString } @@ -147,60 +119,4 @@ export class SecurityIssueHoverProvider implements vscode.HoverProvider { } return `![${severity}](severity-${severity.toLowerCase()}.svg)` } - - /** - * Creates a markdown string to render a code diff block for a given code block. Lines - * that are highlighted red indicate deletion while lines highlighted in green indicate - * addition. An optional language can be provided for syntax highlighting on lines which are - * not additions or deletions. - * - * @param code The code containing the diff - * @param language The language for syntax highlighting - * @returns The markdown string - */ - private _makeCodeBlock(code: string, language?: string) { - const lines = code - .replaceAll('\n\\ No newline at end of file', '') - .replaceAll('--- buggyCode\n', '') - .replaceAll('+++ fixCode\n', '') - .split('\n') - const maxLineChars = lines.reduce((acc, curr) => Math.max(acc, curr.length), 0) - const paddedLines = lines.map((line) => line.padEnd(maxLineChars + 2)) - - // Group the lines into sections so consecutive lines of the same type can be placed in - // the same span below - const sections = [paddedLines[0]] - let i = 1 - while (i < paddedLines.length) { - if (paddedLines[i][0] === sections[sections.length - 1][0]) { - sections[sections.length - 1] += '\n' + paddedLines[i] - } else { - sections.push(paddedLines[i]) - } - i++ - } - - // Return each section with the correct syntax highlighting and background color - return sections - .map( - (section) => ` - - -\`\`\`${section.startsWith('-') || section.startsWith('+') ? 'diff' : section.startsWith('@@') ? undefined : language} -${section} -\`\`\` - - -` - ) - .join('
') - } } From 98b12654382530c5e010e724cabd5ff5d0497b43 Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Fri, 27 Jun 2025 10:08:45 -0700 Subject: [PATCH 10/19] fix(amazonq): commenting out unncalled functions, unused imports --- packages/amazonq/src/lsp/chat/messages.ts | 4 ++-- .../src/codewhisperer/service/securityIssueHoverProvider.ts | 5 ++++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index 75a1bcda7ef..4aafd5cb771 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -72,7 +72,7 @@ import { CodeWhispererSettings, initSecurityScanRender, ReferenceLogViewProvider, - SecurityIssueProvider, + // SecurityIssueProvider, SecurityIssueTreeViewProvider, CodeWhispererConstants, } from 'aws-core-vscode/codewhisperer' @@ -90,7 +90,7 @@ import { decryptResponse, encryptRequest } from '../encryption' import { getCursorState } from '../utils' import { focusAmazonQPanel } from './commands' import { ChatMessage } from '@aws/language-server-runtimes/server-interface' -import path from 'path' +// import path from 'path' export function registerActiveEditorChangeListener(languageClient: LanguageClient) { let debounceTimer: NodeJS.Timeout | undefined diff --git a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts index 7d350965673..f61cea35fd3 100644 --- a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts +++ b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts @@ -10,7 +10,7 @@ import path from 'path' import { AuthUtil } from '../util/authUtil' import { TelemetryHelper } from '../util/telemetryHelper' import { SecurityIssueProvider } from './securityIssueProvider' -import { amazonqCodeIssueDetailsTabTitle } from '../models/constants' +// import { amazonqCodeIssueDetailsTabTitle } from '../models/constants' export class SecurityIssueHoverProvider implements vscode.HoverProvider { static #instance: SecurityIssueHoverProvider @@ -158,6 +158,8 @@ export class SecurityIssueHoverProvider implements vscode.HoverProvider { * @param language The language for syntax highlighting * @returns The markdown string */ + + /* private _makeCodeBlock(code: string, language?: string) { const lines = code .replaceAll('\n\\ No newline at end of file', '') @@ -203,4 +205,5 @@ ${section} ) .join('
') } + */ } From daa3ffe7bfa0b72c43544f0a92e4530fe390bffb Mon Sep 17 00:00:00 2001 From: Nitish Kumar Singh Date: Tue, 1 Jul 2025 22:11:25 -0700 Subject: [PATCH 11/19] Updated explain code issue prompt --- packages/amazonq/src/lsp/chat/commands.ts | 33 +++-------------------- packages/amazonq/src/lsp/chat/messages.ts | 11 -------- 2 files changed, 4 insertions(+), 40 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/commands.ts b/packages/amazonq/src/lsp/chat/commands.ts index f1ecdd88918..19e56c74356 100644 --- a/packages/amazonq/src/lsp/chat/commands.ts +++ b/packages/amazonq/src/lsp/chat/commands.ts @@ -30,7 +30,6 @@ export function registerCommands(provider: AmazonQChatViewProvider) { }), Commands.register('aws.amazonq.explainIssue', async (issue: CodeScanIssue, filePath: string) => { void focusAmazonQPanel().then(async () => { - let selectedCode: string | undefined = undefined if (issue && filePath) { const range = new vscode.Range(issue.startLine, 0, issue.endLine, 0) await vscode.workspace.openTextDocument(filePath).then((doc) => { @@ -39,29 +38,13 @@ export function registerCommands(provider: AmazonQChatViewProvider) { viewColumn: vscode.ViewColumn.One, preview: true, }) - selectedCode = stripIndent(doc.getText(range)) }) } - const uiMessageComponents = [`## ${issue.title}`] - if (filePath) { - uiMessageComponents.push(`File: ${path.basename(filePath)}`) - } - if (issue.relatedVulnerabilities !== undefined && issue.relatedVulnerabilities.length > 0) { - uiMessageComponents.push( - `Common Weakness Enumeration (CWE): ${issue.relatedVulnerabilities.map((cwe) => `[${cwe}](https://cwe.mitre.org/data/definitions/${cwe}.html)`).join(', ')}` - ) - } - if (issue.detectorName !== undefined) { - uiMessageComponents.push(`Detector Library: [${issue.detectorName}](${issue.recommendation.url})`) - } - if (selectedCode) { - uiMessageComponents.push(`\`\`\`\n${selectedCode}\n\`\`\``) - } + + const visibleMessageInChat = `_Explain **${issue.title}** issue in **${path.basename(filePath)}** at \`(${issue.startLine}, ${issue.endLine})\`_` // The message that gets sent to the backend - const contextMessage = `Explain the issue "${issue.title}" (${JSON.stringify( - issue - )}) and generate code demonstrating the fix` + const contextMessage = `Provide a small description of the issue followed by a small description of the recommended fix for it. Code issue - ${JSON.stringify(issue)}` void provider.webview?.postMessage({ command: 'sendToPrompt', @@ -69,7 +52,7 @@ export function registerCommands(provider: AmazonQChatViewProvider) { selection: '', triggerType: 'contextMenu', prompt: { - prompt: uiMessageComponents.join('\n'), // what gets sent to the user + prompt: visibleMessageInChat, // what gets sent to the user escapedPrompt: contextMessage, // what gets sent to the backend }, autoSubmit: true, @@ -130,14 +113,6 @@ function registerGenericCommand(commandName: string, genericCommand: string, pro }) } -function stripIndent(text: string): string { - const lines = text.split('\n') - const minIndent = Math.min( - ...lines.filter((line) => line.trim()).map((line) => line.length - line.trimStart().length) - ) - return lines.map((line) => line.slice(minIndent)).join('\n') -} - /** * Importing focusAmazonQPanel from aws-core-vscode/amazonq leads to several dependencies down the chain not resolving since AmazonQ chat * is currently only activated on node, but the language server is activated on both web and node. diff --git a/packages/amazonq/src/lsp/chat/messages.ts b/packages/amazonq/src/lsp/chat/messages.ts index 05c77ab812a..f0a49292d6f 100644 --- a/packages/amazonq/src/lsp/chat/messages.ts +++ b/packages/amazonq/src/lsp/chat/messages.ts @@ -286,17 +286,6 @@ export function registerMessageListeners( if (editor) { chatParams.cursorState = getCursorState(editor.selections) chatParams.textDocument = { uri: editor.document.uri.toString() } - // chatParams.findingsPath = path.join( - // __dirname, - // '..', - // '..', - // '..', - // '..', - // '..', - // '..', - // 'findings', - // `SecurityIssues-${SecurityIssueProvider.instance.id}.json` - // ) } const chatRequest = await encryptRequest(chatParams, encryptionKey) From 784ae63556c55f2066fb03447ed74dd363f0976e Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Thu, 3 Jul 2025 12:32:55 -0700 Subject: [PATCH 12/19] feat(amazonq): separate the Explain and Generate Fix commands --- packages/amazonq/package.json | 26 +- packages/amazonq/src/lsp/chat/commands.ts | 34 ++- .../codewhisperer/commands/basicCommands.ts | 231 +++++++++--------- .../service/securityIssueHoverProvider.ts | 9 + .../securityIssue/securityIssueWebview.ts | 5 +- 5 files changed, 181 insertions(+), 124 deletions(-) diff --git a/packages/amazonq/package.json b/packages/amazonq/package.json index 9ad47d29521..263e5465a34 100644 --- a/packages/amazonq/package.json +++ b/packages/amazonq/package.json @@ -451,13 +451,13 @@ "group": "inline@4" }, { - "command": "aws.amazonq.security.ignore", + "command": "aws.amazonq.security.generateFix", "when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)", "group": "inline@5" }, { - "command": "aws.amazonq.security.generateFix", - "when": "false && view == aws.amazonq.SecurityIssuesTree && viewItem == issueWithoutFix", + "command": "aws.amazonq.security.ignore", + "when": "view == aws.amazonq.SecurityIssuesTree && (viewItem == issueWithoutFix || viewItem == issueWithFix || viewItem == issueWithFixDisabled)", "group": "inline@6" }, { @@ -1332,26 +1332,40 @@ "fontCharacter": "\\f1de" } }, - "aws-schemas-registry": { + "aws-sagemaker-code-editor": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1df" } }, - "aws-schemas-schema": { + "aws-sagemaker-jupyter-lab": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1e0" } }, - "aws-stepfunctions-preview": { + "aws-schemas-registry": { "description": "AWS Contributed Icon", "default": { "fontPath": "./resources/fonts/aws-toolkit-icons.woff", "fontCharacter": "\\f1e1" } + }, + "aws-schemas-schema": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e2" + } + }, + "aws-stepfunctions-preview": { + "description": "AWS Contributed Icon", + "default": { + "fontPath": "./resources/fonts/aws-toolkit-icons.woff", + "fontCharacter": "\\f1e3" + } } }, "walkthroughs": [ diff --git a/packages/amazonq/src/lsp/chat/commands.ts b/packages/amazonq/src/lsp/chat/commands.ts index 19e56c74356..ffb0a634220 100644 --- a/packages/amazonq/src/lsp/chat/commands.ts +++ b/packages/amazonq/src/lsp/chat/commands.ts @@ -44,7 +44,39 @@ export function registerCommands(provider: AmazonQChatViewProvider) { const visibleMessageInChat = `_Explain **${issue.title}** issue in **${path.basename(filePath)}** at \`(${issue.startLine}, ${issue.endLine})\`_` // The message that gets sent to the backend - const contextMessage = `Provide a small description of the issue followed by a small description of the recommended fix for it. Code issue - ${JSON.stringify(issue)}` + const contextMessage = `Provide a small description of the issue. Do not attempt to fix the issue, only explain it. Code issue - ${JSON.stringify(issue)}` + + void provider.webview?.postMessage({ + command: 'sendToPrompt', + params: { + selection: '', + triggerType: 'contextMenu', + prompt: { + prompt: visibleMessageInChat, // what gets sent to the user + escapedPrompt: contextMessage, // what gets sent to the backend + }, + autoSubmit: true, + }, + }) + }) + }), + Commands.register('aws.amazonq.generateFix', async (issue: CodeScanIssue, filePath: string) => { + void focusAmazonQPanel().then(async () => { + if (issue && filePath) { + const range = new vscode.Range(issue.startLine, 0, issue.endLine, 0) + await vscode.workspace.openTextDocument(filePath).then((doc) => { + void vscode.window.showTextDocument(doc, { + selection: range, + viewColumn: vscode.ViewColumn.One, + preview: true, + }) + }) + } + + const visibleMessageInChat = `_Fix **${issue.title}** issue in **${path.basename(filePath)}** at \`(${issue.startLine}, ${issue.endLine})\`_` + + // The message that gets sent to the backend + const contextMessage = `Generate a fix for the following code issue. Do not explain the issue, just generate and explain the fix. The user should have the option to accept or reject the fix before any code is changed. Code issue - ${JSON.stringify(issue)}` void provider.webview?.postMessage({ command: 'sendToPrompt', diff --git a/packages/core/src/codewhisperer/commands/basicCommands.ts b/packages/core/src/codewhisperer/commands/basicCommands.ts index 2bfbfb05299..1bdd2454342 100644 --- a/packages/core/src/codewhisperer/commands/basicCommands.ts +++ b/packages/core/src/codewhisperer/commands/basicCommands.ts @@ -12,7 +12,6 @@ import { DefaultCodeWhispererClient } from '../client/codewhisperer' import { confirmStopSecurityScan, startSecurityScan } from './startSecurityScan' import { SecurityPanelViewProvider } from '../views/securityPanelViewProvider' import { - codeFixState, CodeScanIssue, CodeScansState, codeScanState, @@ -50,7 +49,7 @@ import { once } from '../../shared/utilities/functionUtils' import { focusAmazonQPanel } from '../../codewhispererChat/commands/registerCommands' import { removeDiagnostic } from '../service/diagnosticsProvider' import { SsoAccessTokenProvider } from '../../auth/sso/ssoAccessTokenProvider' -import { ToolkitError, getErrorMsg, getTelemetryReason, getTelemetryReasonDesc } from '../../shared/errors' +import { ToolkitError, getTelemetryReason, getTelemetryReasonDesc } from '../../shared/errors' import { isRemoteWorkspace } from '../../shared/vscode/env' import { isBuilderIdConnection } from '../../auth/connection' import globals from '../../shared/extensionGlobals' @@ -61,7 +60,6 @@ import { SecurityIssueProvider } from '../service/securityIssueProvider' import { CodeWhispererSettings } from '../util/codewhispererSettings' import { closeDiff, getPatchedCode } from '../../shared/utilities/diffUtils' import { insertCommentAboveLine } from '../../shared/utilities/commentUtils' -import { startCodeFixGeneration } from './startCodeFixGeneration' import { DefaultAmazonQAppInitContext } from '../../amazonq/apps/initContext' import path from 'path' import { UserWrittenCodeTracker } from '../tracker/userWrittenCodeTracker' @@ -369,9 +367,9 @@ export const openSecurityIssuePanel = Commands.declare( const targetFilePath: string = issue instanceof IssueItem ? issue.filePath : filePath await showSecurityIssueWebview(context.extensionContext, targetIssue, targetFilePath) - if (targetIssue.suggestedFixes.length === 0) { - await generateFix.execute(targetIssue, targetFilePath, 'webview', true, false) - } + // if (targetIssue.suggestedFixes.length === 0) { + // await generateFix.execute(targetIssue, targetFilePath, 'webview', true, false) + // } telemetry.codewhisperer_codeScanIssueViewDetails.emit({ findingId: targetIssue.findingId, detectorId: targetIssue.detectorId, @@ -685,116 +683,117 @@ export const generateFix = Commands.declare( { id: 'aws.amazonq.security.generateFix' }, (client: DefaultCodeWhispererClient, context: ExtContext) => async ( - issue: CodeScanIssue | IssueItem | undefined, + issueItem: IssueItem, filePath: string, source: Component, refresh: boolean = false, shouldOpenSecurityIssuePanel: boolean = true ) => { - const targetIssue: CodeScanIssue | undefined = issue instanceof IssueItem ? issue.issue : issue - const targetFilePath: string = issue instanceof IssueItem ? issue.filePath : filePath - const targetSource: Component = issue instanceof IssueItem ? 'tree' : source - if (!targetIssue) { - return - } - if (targetIssue.ruleId === CodeWhispererConstants.sasRuleId) { - getLogger().warn('GenerateFix is not available for SAS findings.') - return - } - await telemetry.codewhisperer_codeScanIssueGenerateFix.run(async () => { - try { - if (shouldOpenSecurityIssuePanel) { - await vscode.commands - .executeCommand('aws.amazonq.openSecurityIssuePanel', targetIssue, targetFilePath) - .then(undefined, (e) => { - getLogger().error('Failed to open security issue panel: %s', e.message) - }) - } - await updateSecurityIssueWebview({ - isGenerateFixLoading: true, - // eslint-disable-next-line unicorn/no-null - generateFixError: null, - context: context.extensionContext, - filePath: targetFilePath, - shouldRefreshView: false, - }) - - codeFixState.setToRunning() - let hasSuggestedFix = false - const { suggestedFix, jobId } = await startCodeFixGeneration( - client, - targetIssue, - targetFilePath, - targetIssue.findingId - ) - // redact the fix if the user disabled references and there is a reference - if ( - // TODO: enable references later for scans - // !CodeWhispererSettings.instance.isSuggestionsWithCodeReferencesEnabled() && - suggestedFix?.references && - suggestedFix?.references?.length > 0 - ) { - getLogger().debug( - `Received fix with reference and user settings disallow references. Job ID: ${jobId}` - ) - // TODO: re-enable notifications once references published - // void vscode.window.showInformationMessage( - // 'Your settings do not allow code generation with references.' - // ) - hasSuggestedFix = false - } else { - hasSuggestedFix = suggestedFix !== undefined - } - telemetry.record({ includesFix: hasSuggestedFix }) - const updatedIssue: CodeScanIssue = { - ...targetIssue, - fixJobId: jobId, - suggestedFixes: - hasSuggestedFix && suggestedFix - ? [ - { - code: suggestedFix.codeDiff, - description: suggestedFix.description ?? '', - references: suggestedFix.references, - }, - ] - : [], - } - await updateSecurityIssueWebview({ - issue: updatedIssue, - isGenerateFixLoading: false, - filePath: targetFilePath, - context: context.extensionContext, - shouldRefreshView: true, - }) - - SecurityIssueProvider.instance.updateIssue(updatedIssue, targetFilePath) - SecurityIssueTreeViewProvider.instance.refresh() - } catch (err) { - const error = err instanceof Error ? err : new TypeError('Unexpected error') - await updateSecurityIssueWebview({ - issue: targetIssue, - isGenerateFixLoading: false, - generateFixError: getErrorMsg(error, true), - filePath: targetFilePath, - context: context.extensionContext, - shouldRefreshView: false, - }) - SecurityIssueProvider.instance.updateIssue(targetIssue, targetFilePath) - SecurityIssueTreeViewProvider.instance.refresh() - throw err - } finally { - telemetry.record({ - component: targetSource, - detectorId: targetIssue.detectorId, - findingId: targetIssue.findingId, - ruleId: targetIssue.ruleId, - variant: refresh ? 'refresh' : undefined, - autoDetected: targetIssue.autoDetected, - codewhispererCodeScanJobId: targetIssue.scanJobId, - }) - } - }) + await vscode.commands.executeCommand('aws.amazonq.generateFix', issueItem.issue, issueItem.filePath) + // const targetIssue: CodeScanIssue | undefined = issue instanceof IssueItem ? issue.issue : issue + // const targetFilePath: string = issue instanceof IssueItem ? issue.filePath : filePath + // const targetSource: Component = issue instanceof IssueItem ? 'tree' : source + // if (!targetIssue) { + // return + // } + // if (targetIssue.ruleId === CodeWhispererConstants.sasRuleId) { + // getLogger().warn('GenerateFix is not available for SAS findings.') + // return + // } + // await telemetry.codewhisperer_codeScanIssueGenerateFix.run(async () => { + // try { + // if (shouldOpenSecurityIssuePanel) { + // await vscode.commands + // .executeCommand('aws.amazonq.openSecurityIssuePanel', targetIssue, targetFilePath) + // .then(undefined, (e) => { + // getLogger().error('Failed to open security issue panel: %s', e.message) + // }) + // } + // await updateSecurityIssueWebview({ + // isGenerateFixLoading: true, + // // eslint-disable-next-line unicorn/no-null + // generateFixError: null, + // context: context.extensionContext, + // filePath: targetFilePath, + // shouldRefreshView: false, + // }) + + // codeFixState.setToRunning() + // let hasSuggestedFix = false + // const { suggestedFix, jobId } = await startCodeFixGeneration( + // client, + // targetIssue, + // targetFilePath, + // targetIssue.findingId + // ) + // // redact the fix if the user disabled references and there is a reference + // if ( + // // TODO: enable references later for scans + // // !CodeWhispererSettings.instance.isSuggestionsWithCodeReferencesEnabled() && + // suggestedFix?.references && + // suggestedFix?.references?.length > 0 + // ) { + // getLogger().debug( + // `Received fix with reference and user settings disallow references. Job ID: ${jobId}` + // ) + // // TODO: re-enable notifications once references published + // // void vscode.window.showInformationMessage( + // // 'Your settings do not allow code generation with references.' + // // ) + // hasSuggestedFix = false + // } else { + // hasSuggestedFix = suggestedFix !== undefined + // } + // telemetry.record({ includesFix: hasSuggestedFix }) + // const updatedIssue: CodeScanIssue = { + // ...targetIssue, + // fixJobId: jobId, + // suggestedFixes: + // hasSuggestedFix && suggestedFix + // ? [ + // { + // code: suggestedFix.codeDiff, + // description: suggestedFix.description ?? '', + // references: suggestedFix.references, + // }, + // ] + // : [], + // } + // await updateSecurityIssueWebview({ + // issue: updatedIssue, + // isGenerateFixLoading: false, + // filePath: targetFilePath, + // context: context.extensionContext, + // shouldRefreshView: true, + // }) + + // SecurityIssueProvider.instance.updateIssue(updatedIssue, targetFilePath) + // SecurityIssueTreeViewProvider.instance.refresh() + // } catch (err) { + // const error = err instanceof Error ? err : new TypeError('Unexpected error') + // await updateSecurityIssueWebview({ + // issue: targetIssue, + // isGenerateFixLoading: false, + // generateFixError: getErrorMsg(error, true), + // filePath: targetFilePath, + // context: context.extensionContext, + // shouldRefreshView: false, + // }) + // SecurityIssueProvider.instance.updateIssue(targetIssue, targetFilePath) + // SecurityIssueTreeViewProvider.instance.refresh() + // throw err + // } finally { + // telemetry.record({ + // component: targetSource, + // detectorId: targetIssue.detectorId, + // findingId: targetIssue.findingId, + // ruleId: targetIssue.ruleId, + // variant: refresh ? 'refresh' : undefined, + // autoDetected: targetIssue.autoDetected, + // codewhispererCodeScanJobId: targetIssue.scanJobId, + // }) + // } + // }) } ) @@ -825,11 +824,11 @@ export const rejectFix = Commands.declare( export const regenerateFix = Commands.declare( { id: 'aws.amazonq.security.regenerateFix' }, () => async (issue: CodeScanIssue | IssueItem | undefined, filePath: string, source: Component) => { - const targetIssue: CodeScanIssue | undefined = issue instanceof IssueItem ? issue.issue : issue - const targetFilePath: string = issue instanceof IssueItem ? issue.filePath : filePath - const targetSource: Component = issue instanceof IssueItem ? 'tree' : source - const updatedIssue = await rejectFix.execute(targetIssue, targetFilePath) - await generateFix.execute(updatedIssue, targetFilePath, targetSource, true) + // const targetIssue: CodeScanIssue | undefined = issue instanceof IssueItem ? issue.issue : issue + // const targetFilePath: string = issue instanceof IssueItem ? issue.filePath : filePath + // const targetSource: Component = issue instanceof IssueItem ? 'tree' : source + // const updatedIssue = await rejectFix.execute(targetIssue, targetFilePath) + // await generateFix.execute(updatedIssue, targetFilePath, targetSource, true) } ) diff --git a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts index 3acabeff695..95ea3869cb0 100644 --- a/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts +++ b/packages/core/src/codewhisperer/service/securityIssueHoverProvider.ts @@ -87,6 +87,15 @@ export class SecurityIssueHoverProvider implements vscode.HoverProvider { ) markdownString.appendMarkdown(explainWithQCommand) + const generateFixCommand = this._getCommandMarkdown( + 'aws.amazonq.generateFix', + [issue, filePath], + 'comment', + 'Fix', + 'Generate Fix for Issue' + ) + markdownString.appendMarkdown(' | ' + generateFixCommand) + const ignoreIssueCommand = this._getCommandMarkdown( 'aws.amazonq.security.ignore', [issue, filePath, 'hover'], diff --git a/packages/core/src/codewhisperer/views/securityIssue/securityIssueWebview.ts b/packages/core/src/codewhisperer/views/securityIssue/securityIssueWebview.ts index d511bd9a5f6..632283215ab 100644 --- a/packages/core/src/codewhisperer/views/securityIssue/securityIssueWebview.ts +++ b/packages/core/src/codewhisperer/views/securityIssue/securityIssueWebview.ts @@ -109,7 +109,10 @@ export class SecurityIssueWebview extends VueWebview { } public generateFix() { - void vscode.commands.executeCommand('aws.amazonq.security.generateFix', this.issue, this.filePath, 'webview') + const args = [this.issue] + void this.navigateToFile()?.then(() => { + void vscode.commands.executeCommand('aws.amazonq.generateFix', ...args) + }) } public regenerateFix() { From ec7048ea805c10ac5e2f0400cba135f1e1fc8ef4 Mon Sep 17 00:00:00 2001 From: Nitish Kumar Singh Date: Fri, 4 Jul 2025 14:17:29 -0700 Subject: [PATCH 13/19] fix: remove commented code --- .../codewhisperer/commands/basicCommands.ts | 104 ------------------ 1 file changed, 104 deletions(-) diff --git a/packages/core/src/codewhisperer/commands/basicCommands.ts b/packages/core/src/codewhisperer/commands/basicCommands.ts index 1bdd2454342..c7df25bf824 100644 --- a/packages/core/src/codewhisperer/commands/basicCommands.ts +++ b/packages/core/src/codewhisperer/commands/basicCommands.ts @@ -690,110 +690,6 @@ export const generateFix = Commands.declare( shouldOpenSecurityIssuePanel: boolean = true ) => { await vscode.commands.executeCommand('aws.amazonq.generateFix', issueItem.issue, issueItem.filePath) - // const targetIssue: CodeScanIssue | undefined = issue instanceof IssueItem ? issue.issue : issue - // const targetFilePath: string = issue instanceof IssueItem ? issue.filePath : filePath - // const targetSource: Component = issue instanceof IssueItem ? 'tree' : source - // if (!targetIssue) { - // return - // } - // if (targetIssue.ruleId === CodeWhispererConstants.sasRuleId) { - // getLogger().warn('GenerateFix is not available for SAS findings.') - // return - // } - // await telemetry.codewhisperer_codeScanIssueGenerateFix.run(async () => { - // try { - // if (shouldOpenSecurityIssuePanel) { - // await vscode.commands - // .executeCommand('aws.amazonq.openSecurityIssuePanel', targetIssue, targetFilePath) - // .then(undefined, (e) => { - // getLogger().error('Failed to open security issue panel: %s', e.message) - // }) - // } - // await updateSecurityIssueWebview({ - // isGenerateFixLoading: true, - // // eslint-disable-next-line unicorn/no-null - // generateFixError: null, - // context: context.extensionContext, - // filePath: targetFilePath, - // shouldRefreshView: false, - // }) - - // codeFixState.setToRunning() - // let hasSuggestedFix = false - // const { suggestedFix, jobId } = await startCodeFixGeneration( - // client, - // targetIssue, - // targetFilePath, - // targetIssue.findingId - // ) - // // redact the fix if the user disabled references and there is a reference - // if ( - // // TODO: enable references later for scans - // // !CodeWhispererSettings.instance.isSuggestionsWithCodeReferencesEnabled() && - // suggestedFix?.references && - // suggestedFix?.references?.length > 0 - // ) { - // getLogger().debug( - // `Received fix with reference and user settings disallow references. Job ID: ${jobId}` - // ) - // // TODO: re-enable notifications once references published - // // void vscode.window.showInformationMessage( - // // 'Your settings do not allow code generation with references.' - // // ) - // hasSuggestedFix = false - // } else { - // hasSuggestedFix = suggestedFix !== undefined - // } - // telemetry.record({ includesFix: hasSuggestedFix }) - // const updatedIssue: CodeScanIssue = { - // ...targetIssue, - // fixJobId: jobId, - // suggestedFixes: - // hasSuggestedFix && suggestedFix - // ? [ - // { - // code: suggestedFix.codeDiff, - // description: suggestedFix.description ?? '', - // references: suggestedFix.references, - // }, - // ] - // : [], - // } - // await updateSecurityIssueWebview({ - // issue: updatedIssue, - // isGenerateFixLoading: false, - // filePath: targetFilePath, - // context: context.extensionContext, - // shouldRefreshView: true, - // }) - - // SecurityIssueProvider.instance.updateIssue(updatedIssue, targetFilePath) - // SecurityIssueTreeViewProvider.instance.refresh() - // } catch (err) { - // const error = err instanceof Error ? err : new TypeError('Unexpected error') - // await updateSecurityIssueWebview({ - // issue: targetIssue, - // isGenerateFixLoading: false, - // generateFixError: getErrorMsg(error, true), - // filePath: targetFilePath, - // context: context.extensionContext, - // shouldRefreshView: false, - // }) - // SecurityIssueProvider.instance.updateIssue(targetIssue, targetFilePath) - // SecurityIssueTreeViewProvider.instance.refresh() - // throw err - // } finally { - // telemetry.record({ - // component: targetSource, - // detectorId: targetIssue.detectorId, - // findingId: targetIssue.findingId, - // ruleId: targetIssue.ruleId, - // variant: refresh ? 'refresh' : undefined, - // autoDetected: targetIssue.autoDetected, - // codewhispererCodeScanJobId: targetIssue.scanJobId, - // }) - // } - // }) } ) From ccc19de7e9d8998092ba92e03b1d4927bc50465e Mon Sep 17 00:00:00 2001 From: Nitish Kumar Singh Date: Sun, 6 Jul 2025 13:11:48 -0700 Subject: [PATCH 14/19] fix: update explain issue prompt --- packages/amazonq/src/lsp/chat/commands.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/amazonq/src/lsp/chat/commands.ts b/packages/amazonq/src/lsp/chat/commands.ts index ffb0a634220..193770661e8 100644 --- a/packages/amazonq/src/lsp/chat/commands.ts +++ b/packages/amazonq/src/lsp/chat/commands.ts @@ -44,7 +44,7 @@ export function registerCommands(provider: AmazonQChatViewProvider) { const visibleMessageInChat = `_Explain **${issue.title}** issue in **${path.basename(filePath)}** at \`(${issue.startLine}, ${issue.endLine})\`_` // The message that gets sent to the backend - const contextMessage = `Provide a small description of the issue. Do not attempt to fix the issue, only explain it. Code issue - ${JSON.stringify(issue)}` + const contextMessage = `Provide a small description of the issue. Do not attempt to fix the issue, only give a small summary of it. Code issue - ${JSON.stringify(issue)}` void provider.webview?.postMessage({ command: 'sendToPrompt', From 11bcfc9b80a1d0a57588ad6a7f2d811bcf33ea12 Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Tue, 8 Jul 2025 10:56:05 -0700 Subject: [PATCH 15/19] fix(amazonq): change 'Generate Fix' to 'Fix' and remove form code issues --- packages/core/package.nls.json | 2 +- .../codewhisperer/service/securityIssueTreeViewProvider.ts | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/core/package.nls.json b/packages/core/package.nls.json index b3cf958c980..c8c091a609e 100644 --- a/packages/core/package.nls.json +++ b/packages/core/package.nls.json @@ -146,7 +146,7 @@ "AWS.command.amazonq.generateUnitTests": "Generate Tests", "AWS.command.amazonq.security.scan": "Run Project Review", "AWS.command.amazonq.security.fileScan": "Run File Review", - "AWS.command.amazonq.generateFix": "Generate Fix", + "AWS.command.amazonq.generateFix": "Fix", "AWS.command.amazonq.viewDetails": "View Details", "AWS.command.amazonq.explainIssue": "Explain", "AWS.command.amazonq.ignoreIssue": "Ignore Issue", diff --git a/packages/core/src/codewhisperer/service/securityIssueTreeViewProvider.ts b/packages/core/src/codewhisperer/service/securityIssueTreeViewProvider.ts index d7c93f70423..b1f7f73907b 100644 --- a/packages/core/src/codewhisperer/service/securityIssueTreeViewProvider.ts +++ b/packages/core/src/codewhisperer/service/securityIssueTreeViewProvider.ts @@ -189,11 +189,8 @@ export class IssueItem extends vscode.TreeItem { } private getDescription() { - const positionStr = `[Ln ${this.issue.startLine + 1}, Col 1]` const groupingStrategy = CodeIssueGroupingStrategyState.instance.getState() - return groupingStrategy !== CodeIssueGroupingStrategy.FileLocation - ? `${path.basename(this.filePath)} ${positionStr}` - : positionStr + return groupingStrategy !== CodeIssueGroupingStrategy.FileLocation ? `${path.basename(this.filePath)}` : '' } private getContextValue() { From f1b693633dd97e5bd276cb0f20943aa8260329b2 Mon Sep 17 00:00:00 2001 From: Nitish Kumar Singh Date: Tue, 8 Jul 2025 15:28:38 -0700 Subject: [PATCH 16/19] fix: remove unnecessary imports --- packages/amazonq/src/lsp/chat/commands.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/commands.ts b/packages/amazonq/src/lsp/chat/commands.ts index 9ad61e2d9c7..0e0cc996f0f 100644 --- a/packages/amazonq/src/lsp/chat/commands.ts +++ b/packages/amazonq/src/lsp/chat/commands.ts @@ -7,8 +7,6 @@ import { Commands, globals } from 'aws-core-vscode/shared' import { window } from 'vscode' import { AmazonQChatViewProvider } from './webviewProvider' import { CodeScanIssue } from 'aws-core-vscode/codewhisperer' -import { DefaultAmazonQAppInitContext } from 'aws-core-vscode/amazonq' -import { EditorContextExtractor } from 'aws-core-vscode/codewhispererChat' import * as vscode from 'vscode' import * as path from 'path' From a479a42c4f14c5f129cde11b2a744cb04806d105 Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Tue, 8 Jul 2025 16:25:43 -0700 Subject: [PATCH 17/19] fix(amazonq): improve line numbering in Explain and Fix commands --- packages/amazonq/src/lsp/chat/commands.ts | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/commands.ts b/packages/amazonq/src/lsp/chat/commands.ts index ffb0a634220..03739de4492 100644 --- a/packages/amazonq/src/lsp/chat/commands.ts +++ b/packages/amazonq/src/lsp/chat/commands.ts @@ -41,7 +41,11 @@ export function registerCommands(provider: AmazonQChatViewProvider) { }) } - const visibleMessageInChat = `_Explain **${issue.title}** issue in **${path.basename(filePath)}** at \`(${issue.startLine}, ${issue.endLine})\`_` + const lineRange = + issue.startLine === issue.endLine - 1 + ? `[${issue.startLine + 1}]` + : `[${issue.startLine + 1}, ${issue.endLine}]` + const visibleMessageInChat = `_Explain **${issue.title}** issue in **${path.basename(filePath)}** at \`${lineRange}\`_` // The message that gets sent to the backend const contextMessage = `Provide a small description of the issue. Do not attempt to fix the issue, only explain it. Code issue - ${JSON.stringify(issue)}` @@ -73,7 +77,11 @@ export function registerCommands(provider: AmazonQChatViewProvider) { }) } - const visibleMessageInChat = `_Fix **${issue.title}** issue in **${path.basename(filePath)}** at \`(${issue.startLine}, ${issue.endLine})\`_` + const lineRange = + issue.startLine === issue.endLine - 1 + ? `[${issue.startLine + 1}]` + : `[${issue.startLine + 1}, ${issue.endLine}]` + const visibleMessageInChat = `_Fix **${issue.title}** issue in **${path.basename(filePath)}** at \`${lineRange}\`_` // The message that gets sent to the backend const contextMessage = `Generate a fix for the following code issue. Do not explain the issue, just generate and explain the fix. The user should have the option to accept or reject the fix before any code is changed. Code issue - ${JSON.stringify(issue)}` From d512facec62747175d83744039f3abeb8ae42c6f Mon Sep 17 00:00:00 2001 From: Nitish Kumar Singh Date: Tue, 8 Jul 2025 17:16:03 -0700 Subject: [PATCH 18/19] fix: update explain and fix prompt --- packages/amazonq/src/lsp/chat/commands.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/amazonq/src/lsp/chat/commands.ts b/packages/amazonq/src/lsp/chat/commands.ts index 0ef12ba38bd..2876bf83b33 100644 --- a/packages/amazonq/src/lsp/chat/commands.ts +++ b/packages/amazonq/src/lsp/chat/commands.ts @@ -42,7 +42,7 @@ export function registerCommands(provider: AmazonQChatViewProvider) { const visibleMessageInChat = `_Explain **${issue.title}** issue in **${path.basename(filePath)}** at \`${lineRange}\`_` // The message that gets sent to the backend - const contextMessage = `Provide a small description of the issue. Do not attempt to fix the issue, only give a small summary of it. Code issue - ${JSON.stringify(issue)}` + const contextMessage = `Provide a small description of the issue. You must not attempt to fix the issue. You should only give a small summary of it to the user. Code issue - ${JSON.stringify(issue)}` void provider.webview?.postMessage({ command: 'sendToPrompt', @@ -78,7 +78,7 @@ export function registerCommands(provider: AmazonQChatViewProvider) { const visibleMessageInChat = `_Fix **${issue.title}** issue in **${path.basename(filePath)}** at \`${lineRange}\`_` // The message that gets sent to the backend - const contextMessage = `Generate a fix for the following code issue. Do not explain the issue, just generate and explain the fix. The user should have the option to accept or reject the fix before any code is changed. Code issue - ${JSON.stringify(issue)}` + const contextMessage = `Generate a fix for the following code issue. You must not explain the issue, just generate and explain the fix. The user should have the option to accept or reject the fix before any code is changed. Code issue - ${JSON.stringify(issue)}` void provider.webview?.postMessage({ command: 'sendToPrompt', From 170704067471354db7ca28a7d227771d0fa693cb Mon Sep 17 00:00:00 2001 From: Blake Lazarine Date: Wed, 9 Jul 2025 13:49:29 -0700 Subject: [PATCH 19/19] feat(amazonq): add agentic reviewer feature flag --- packages/amazonq/src/lsp/client.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/amazonq/src/lsp/client.ts b/packages/amazonq/src/lsp/client.ts index 67f21ab396a..5f38c72a16f 100644 --- a/packages/amazonq/src/lsp/client.ts +++ b/packages/amazonq/src/lsp/client.ts @@ -165,6 +165,7 @@ export async function startLanguageServer( pinnedContextEnabled: true, mcp: true, workspaceFilePath: vscode.workspace.workspaceFile?.fsPath, + agenticReviewer: true, }, window: { notifications: true,