Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2cf1698
feat: context command pending state passed to ui, triggered when inde…
constewart9 Aug 1, 2025
ce32f5e
refactored pending flow
constewart9 Aug 5, 2025
b399f8f
Merge branch 'aws:main' into conorstw/context-pending
constewart9 Aug 5, 2025
c9d3922
fix: removed extra spaces
constewart9 Aug 5, 2025
832162c
fix: workspace pending turned back on when building index (when confi…
constewart9 Aug 6, 2025
fd23ee7
Merge branch 'main' into conorstw/context-pending
constewart9 Aug 6, 2025
eba7b90
fix: log errors if inital context commands fail to send
constewart9 Aug 6, 2025
477b80b
refactor: changed pending state from boolean to string to send to ui
constewart9 Aug 7, 2025
21c0c31
refactor: update from pending to disabledText
constewart9 Aug 7, 2025
b0a5d9c
Merge branch 'aws:main' into conorstw/context-pending
constewart9 Aug 11, 2025
e029335
fix: send intial pending state before any other action in contextComm…
constewart9 Aug 11, 2025
3c393aa
Merge branch 'main' into conorstw/context-pending
constewart9 Aug 12, 2025
9e8abcc
Merge branch 'main' into conorstw/context-pending
constewart9 Aug 13, 2025
10b68e0
fix: send initial pending context commands onReady, instead of before
constewart9 Aug 13, 2025
a3dc6cd
Merge branch 'main' into conorstw/context-pending
constewart9 Aug 14, 2025
137aea1
test: added unit test for onReady
constewart9 Aug 14, 2025
1edfe24
test: added unit test for when indexingInProgress is changed
constewart9 Aug 14, 2025
5b942a2
Merge branch 'main' into conorstw/context-pending
constewart9 Aug 14, 2025
bcce98d
Merge branch 'aws:main' into conorstw/context-pending
constewart9 Aug 18, 2025
9b4697f
Merge branch 'main' into conorstw/context-pending
constewart9 Aug 22, 2025
302a2e3
Merge pull request #1 from constewart9/conorstw/context-pending
manodnyab Aug 22, 2025
c26b7f9
Merge branch 'main' into conor/indexProj
XiaoxuanLu Sep 26, 2025
41d9e15
Merge branch 'aws:main' into conor/indexProj
manodnyab Sep 29, 2025
f33e44c
Merge branch 'main' into conor/indexProj
manodnyab Oct 16, 2025
f546bea
Merge branch 'conor/indexProj' of https://github.com/manodnyab/langua…
XiaoxuanLu Oct 16, 2025
d555c50
chore: fix the mynal ui test
XiaoxuanLu Oct 16, 2025
8d7e483
chore: fix the mynal test for lack of disabled field
XiaoxuanLu Oct 16, 2025
8c99f00
Merge branch 'main' into conor/indexProj
XiaoxuanLu Oct 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion chat-client/src/client/mynahUi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -634,6 +634,7 @@ describe('MynahUI', () => {
route: ['/workspace', 'src/file1.ts'],
icon: 'file',
children: undefined,
disabled: false,
},
],
promptTopBarTitle: '@',
Expand Down Expand Up @@ -690,6 +691,7 @@ describe('MynahUI', () => {
...activeEditorCommand,
description: 'file:///workspace/src/active.ts',
children: undefined,
disabled: false,
},
],
promptTopBarTitle: '@Pin Context',
Expand Down Expand Up @@ -729,7 +731,7 @@ describe('MynahUI', () => {
// Verify updateStore was called with empty context items
// Active editor should be removed since no textDocument was provided
sinon.assert.calledWith(updateStoreSpy, tabId, {
promptTopBarContextItems: [{ ...fileCommand, children: undefined }],
promptTopBarContextItems: [{ ...fileCommand, children: undefined, disabled: false }],
promptTopBarTitle: '@',
promptTopBarButton: null,
})
Expand Down
1 change: 1 addition & 0 deletions chat-client/src/client/mynahUi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1560,6 +1560,7 @@ ${params.message}`,
commands: toContextCommands(child.commands),
})),
icon: toMynahIcon(command.icon),
disabled: command.disabledText != null,
}))
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
* Will be deleted or merged.
*/

import * as crypto from 'crypto'

Check warning on line 6 in server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts

View workflow job for this annotation

GitHub Actions / Test (Windows)

Do not import Node.js builtin module "crypto"

Check warning on line 6 in server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts

View workflow job for this annotation

GitHub Actions / Test

Do not import Node.js builtin module "crypto"
import * as path from 'path'

Check warning on line 7 in server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts

View workflow job for this annotation

GitHub Actions / Test (Windows)

Do not import Node.js builtin module "path"

Check warning on line 7 in server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts

View workflow job for this annotation

GitHub Actions / Test

Do not import Node.js builtin module "path"
import * as os from 'os'

Check warning on line 8 in server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts

View workflow job for this annotation

GitHub Actions / Test (Windows)

Do not import Node.js builtin module "os"

Check warning on line 8 in server/aws-lsp-codewhisperer/src/language-server/agenticChat/agenticChatController.ts

View workflow job for this annotation

GitHub Actions / Test

Do not import Node.js builtin module "os"
import {
ChatTriggerType,
Origin,
Expand Down Expand Up @@ -3847,9 +3847,11 @@
*/
async onReady() {
await this.restorePreviousChats()
this.#contextCommandsProvider.onReady()
try {
const localProjectContextController = await LocalProjectContextController.getInstance()
const contextItems = await localProjectContextController.getContextCommandItems()
this.#contextCommandsProvider.setFilesAndFoldersPending(false)
await this.#contextCommandsProvider.processContextCommandUpdate(contextItems)
void this.#contextCommandsProvider.maybeUpdateCodeSymbols()
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as sinon from 'sinon'
import { TestFeatures } from '@aws/language-server-runtimes/testing'
import * as chokidar from 'chokidar'
import { ContextCommandItem } from 'local-indexing'
import { LocalProjectContextController } from '../../../shared/localProjectContextController'

describe('ContextCommandsProvider', () => {
let provider: ContextCommandsProvider
Expand All @@ -21,6 +22,12 @@ describe('ContextCommandsProvider', () => {

testFeatures.workspace.fs.exists = fsExistsStub
testFeatures.workspace.fs.readdir = fsReadDirStub

sinon.stub(LocalProjectContextController, 'getInstance').resolves({
onContextItemsUpdated: sinon.stub(),
onIndexingInProgressChanged: sinon.stub(),
} as any)

provider = new ContextCommandsProvider(
testFeatures.logging,
testFeatures.chat,
Expand Down Expand Up @@ -58,6 +65,26 @@ describe('ContextCommandsProvider', () => {
})
})

describe('onReady', () => {
it('should call processContextCommandUpdate with empty array on first call', async () => {
const processUpdateSpy = sinon.spy(provider, 'processContextCommandUpdate')

provider.onReady()

sinon.assert.calledOnce(processUpdateSpy)
sinon.assert.calledWith(processUpdateSpy, [])
})

it('should not call processContextCommandUpdate on subsequent calls', async () => {
const processUpdateSpy = sinon.spy(provider, 'processContextCommandUpdate')

provider.onReady()
provider.onReady()

sinon.assert.calledOnce(processUpdateSpy)
})
})

describe('onContextItemsUpdated', () => {
it('should call processContextCommandUpdate when controller raises event', async () => {
const mockContextItems: ContextCommandItem[] = [
Expand All @@ -78,4 +105,29 @@ describe('ContextCommandsProvider', () => {
sinon.assert.calledWith(processUpdateSpy, mockContextItems)
})
})

describe('onIndexingInProgressChanged', () => {
it('should update workspacePending and call processContextCommandUpdate when indexing status changes', async () => {
let capturedCallback: ((indexingInProgress: boolean) => void) | undefined

const mockController = {
onContextItemsUpdated: sinon.stub(),
set onIndexingInProgressChanged(callback: (indexingInProgress: boolean) => void) {
capturedCallback = callback
},
}

const processUpdateSpy = sinon.spy(provider, 'processContextCommandUpdate')
;(LocalProjectContextController.getInstance as sinon.SinonStub).resolves(mockController as any)

// Set initial state to false so condition is met
;(provider as any).workspacePending = false

await (provider as any).registerContextCommandHandler()

capturedCallback?.(true)

sinon.assert.calledWith(processUpdateSpy, [])
})
})
})
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import * as path from 'path'

Check warning on line 1 in server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts

View workflow job for this annotation

GitHub Actions / Test (Windows)

Do not import Node.js builtin module "path"

Check warning on line 1 in server/aws-lsp-codewhisperer/src/language-server/agenticChat/context/contextCommandsProvider.ts

View workflow job for this annotation

GitHub Actions / Test

Do not import Node.js builtin module "path"
import { FSWatcher, watch } from 'chokidar'
import { ContextCommand, ContextCommandGroup } from '@aws/language-server-runtimes/protocol'
import { Disposable } from 'vscode-languageclient/node'
Expand All @@ -12,6 +12,10 @@
export class ContextCommandsProvider implements Disposable {
private promptFileWatcher?: FSWatcher
private cachedContextCommands?: ContextCommandItem[]
private codeSymbolsPending = true
private filesAndFoldersPending = true
private workspacePending = true
private initialStateSent = false
constructor(
private readonly logging: Logging,
private readonly chat: Chat,
Expand All @@ -24,12 +28,27 @@
)
}

onReady() {
if (!this.initialStateSent) {
this.initialStateSent = true
void this.processContextCommandUpdate([]).catch(e =>
this.logging.error(`Failed to send initial context commands: ${e}`)
)
}
}

private async registerContextCommandHandler() {
try {
const controller = await LocalProjectContextController.getInstance()
controller.onContextItemsUpdated = async contextItems => {
await this.processContextCommandUpdate(contextItems)
}
controller.onIndexingInProgressChanged = (indexingInProgress: boolean) => {
if (this.workspacePending !== indexingInProgress) {
this.workspacePending = indexingInProgress
void this.processContextCommandUpdate(this.cachedContextCommands ?? [])
}
}
} catch (e) {
this.logging.warn(`Error processing context command update: ${e}`)
}
Expand Down Expand Up @@ -105,6 +124,7 @@
],
description: 'Add all files in a folder to context',
icon: 'folder',
disabledText: this.filesAndFoldersPending ? 'pending' : undefined,
}

const fileCmds: ContextCommand[] = [activeFileCmd]
Expand All @@ -118,6 +138,7 @@
],
description: 'Add a file to context',
icon: 'file',
disabledText: this.filesAndFoldersPending ? 'pending' : undefined,
}

const codeCmds: ContextCommand[] = []
Expand All @@ -131,6 +152,7 @@
],
description: 'Add code to context',
icon: 'code-block',
disabledText: this.codeSymbolsPending ? 'pending' : undefined,
}

const promptCmds: ContextCommand[] = []
Expand All @@ -152,10 +174,12 @@
icon: 'image',
placeholder: 'Select an image file',
}
const workspaceCmd = {

const workspaceCmd: ContextCommand = {
command: '@workspace',
id: '@workspace',
description: 'Reference all code in workspace',
disabledText: this.workspacePending ? 'pending' : undefined,
}
const commands = [workspaceCmd, folderCmdGroup, fileCmdGroup, codeCmdGroup, promptCmdGroup]

Expand Down Expand Up @@ -209,11 +233,16 @@
await LocalProjectContextController.getInstance()
).shouldUpdateContextCommandSymbolsOnce()
if (needUpdate) {
this.codeSymbolsPending = false
const items = await (await LocalProjectContextController.getInstance()).getContextCommandItems()
await this.processContextCommandUpdate(items)
}
}

setFilesAndFoldersPending(value: boolean) {
this.filesAndFoldersPending = value
}

dispose() {
void this.promptFileWatcher?.close()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,8 @@ export interface LocalProjectContextInitializationOptions {
export class LocalProjectContextController {
// Event handler for context items updated
public onContextItemsUpdated: ((contextItems: ContextCommandItem[]) => Promise<void>) | undefined
// Event handler for when index is being built
public onIndexingInProgressChanged: ((enabled: boolean) => void) | undefined
private static instance: LocalProjectContextController | undefined

private workspaceFolders: WorkspaceFolder[]
Expand Down Expand Up @@ -214,6 +216,7 @@ export class LocalProjectContextController {
}
try {
this._isIndexingInProgress = true
this.onIndexingInProgressChanged?.(this._isIndexingInProgress)
if (this._vecLib) {
if (!this.workspaceFolders.length) {
this.log.info('skip building index because no workspace folder found')
Expand All @@ -234,6 +237,7 @@ export class LocalProjectContextController {
this.log.error(`Error building index: ${error}`)
} finally {
this._isIndexingInProgress = false
this.onIndexingInProgressChanged?.(this._isIndexingInProgress)
}
}

Expand Down
Loading