Skip to content

Commit 061caa6

Browse files
authored
fix: Forward slash shown in rules list in multi-root workspaces on windows (aws#1855)
* fix: forward slash shown in rules list windows muti root workspace * fix: switch map to some * test: add unit tests * fix: send "thinking" in assistant response for pinned context
1 parent ee215c4 commit 061caa6

File tree

3 files changed

+89
-7
lines changed

3 files changed

+89
-7
lines changed

server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -715,9 +715,12 @@ export class AgenticChatController implements ChatHandlers {
715715
params.tabId,
716716
params.context
717717
)
718-
// Add active file to context list if it exists
718+
// Add active file to context list if it's not already there
719719
const activeFile =
720-
triggerContext.text && triggerContext.relativeFilePath && triggerContext.activeFilePath
720+
triggerContext.text &&
721+
triggerContext.relativeFilePath &&
722+
triggerContext.activeFilePath &&
723+
!additionalContext.some(item => item.path === triggerContext.activeFilePath)
721724
? [
722725
{
723726
name: path.basename(triggerContext.relativeFilePath),

server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/additionalContextProvider.test.ts

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -531,7 +531,7 @@ describe('AdditionalContextProvider', () => {
531531
assert.strictEqual(result.length, 2)
532532
assert.strictEqual(result[0].userInputMessage?.content?.includes('<promptInstruction>'), true)
533533
assert.strictEqual(result[0].userInputMessage?.content?.includes('Follow this rule'), true)
534-
assert.strictEqual(result[1].assistantResponseMessage?.content, '')
534+
assert.strictEqual(result[1].assistantResponseMessage?.content, 'Thinking...')
535535
})
536536

537537
it('should convert file context to fileContext XML', async () => {
@@ -615,4 +615,78 @@ describe('AdditionalContextProvider', () => {
615615
})
616616
})
617617
})
618+
619+
describe('convertRulesToRulesFolders', () => {
620+
it('should convert workspace rules to folders structure', () => {
621+
sinon.stub(workspaceUtils, 'getWorkspaceFolderPaths').returns(['/workspace'])
622+
623+
// Configure the getRules stub to return a specific value
624+
;(chatHistoryDb.getRules as sinon.SinonStub).returns({
625+
folders: {}, // Empty folders state (default all active)
626+
rules: {}, // Empty rules state (default all active)
627+
})
628+
629+
const workspaceRules = [
630+
{
631+
workspaceFolder: '/workspace',
632+
type: 'file' as any,
633+
relativePath: '.amazonq/rules/rule1.md',
634+
id: '/workspace/.amazonq/rules/rule1.md',
635+
},
636+
{
637+
workspaceFolder: '/workspace',
638+
type: 'file' as any,
639+
relativePath: '.amazonq/rules/rule2.md',
640+
id: '/workspace/.amazonq/rules/rule2.md',
641+
},
642+
]
643+
644+
const result = provider.convertRulesToRulesFolders(workspaceRules, 'tab1')
645+
646+
assert.strictEqual(result.length, 1)
647+
assert.strictEqual(result[0].folderName, '.amazonq/rules')
648+
assert.strictEqual(result[0].active, true)
649+
assert.strictEqual(result[0].rules.length, 2)
650+
assert.strictEqual(result[0].rules[0].name, 'rule1')
651+
assert.strictEqual(result[0].rules[1].name, 'rule2')
652+
})
653+
654+
it('should handle rules with explicit active/inactive states', () => {
655+
sinon.stub(workspaceUtils, 'getWorkspaceFolderPaths').returns(['/workspace'])
656+
657+
// Configure the getRules stub to return specific active/inactive states
658+
;(chatHistoryDb.getRules as sinon.SinonStub).returns({
659+
folders: {
660+
'.amazonq/rules': false, // This folder is explicitly inactive
661+
},
662+
rules: {
663+
'/workspace/.amazonq/rules/rule1.md': true, // This rule is explicitly active
664+
'/workspace/.amazonq/rules/rule2.md': false, // This rule is explicitly inactive
665+
},
666+
})
667+
668+
const workspaceRules = [
669+
{
670+
workspaceFolder: '/workspace',
671+
type: 'file' as any,
672+
relativePath: '.amazonq/rules/rule1.md',
673+
id: '/workspace/.amazonq/rules/rule1.md',
674+
},
675+
{
676+
workspaceFolder: '/workspace',
677+
type: 'file' as any,
678+
relativePath: '.amazonq/rules/rule2.md',
679+
id: '/workspace/.amazonq/rules/rule2.md',
680+
},
681+
]
682+
683+
const result = provider.convertRulesToRulesFolders(workspaceRules, 'tab1')
684+
685+
assert.strictEqual(result.length, 1)
686+
assert.strictEqual(result[0].folderName, '.amazonq/rules')
687+
assert.strictEqual(result[0].active, 'indeterminate') // Should be indeterminate since rules have mixed states
688+
assert.strictEqual(result[0].rules[0].active, true) // Explicitly set to true
689+
assert.strictEqual(result[0].rules[1].active, false) // Explicitly set to false
690+
})
691+
})
618692
})

server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/additionalContextProvider.ts

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ export class AdditionalContextProvider {
177177
if (prompt.filePath.endsWith(promptFileExtension)) {
178178
if (
179179
pathUtils.isInDirectory(path.join('.amazonq', 'rules'), prompt.relativePath) ||
180-
path.basename(prompt.relativePath) === 'AmazonQ.md'
180+
path.basename(prompt.relativePath) === 'AmazonQ.md' ||
181+
path.basename(prompt.relativePath) === 'README.md'
181182
) {
182183
return 'rule'
183184
} else if (pathUtils.isInDirectory(getUserPromptsDirectory(), prompt.filePath)) {
@@ -589,7 +590,7 @@ export class AdditionalContextProvider {
589590
this.sendPinnedContext(params.tabId)
590591
}
591592

592-
private convertRulesToRulesFolders(workspaceRules: ContextCommandItem[], tabId: string): RulesFolder[] {
593+
convertRulesToRulesFolders(workspaceRules: ContextCommandItem[], tabId: string): RulesFolder[] {
593594
// Check if there's only one workspace folder
594595
const workspaceFolders = workspaceUtils.getWorkspaceFolderPaths(this.features.workspace)
595596
const isSingleWorkspace = workspaceFolders.length <= 1
@@ -616,7 +617,11 @@ export class AdditionalContextProvider {
616617
// Root files will use the workspace folder name
617618
// Subdir files will use workspace folder name + subdir
618619
const workspaceFolderName = path.basename(rule.workspaceFolder)
619-
folderName = dirPath === '.' ? workspaceFolderName : `${workspaceFolderName}/${dirPath}`
620+
folderName =
621+
dirPath === '.'
622+
? workspaceFolderName
623+
: // Escape backslashes since folderName is displayed in innerHTML
624+
path.join(workspaceFolderName, dirPath).replace(/\\/g, '\\\\')
620625
}
621626

622627
// Get or create the folder's rule list
@@ -760,7 +765,7 @@ export class AdditionalContextProvider {
760765
// Create fake assistant response
761766
const assistantMessage: ChatMessage = {
762767
assistantResponseMessage: {
763-
content: '',
768+
content: 'Thinking...',
764769
},
765770
}
766771

0 commit comments

Comments
 (0)