@theia/ai-claude-code 1.67.0-next.56 → 1.67.0-next.86
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/browser/claude-code-chat-agent.d.ts +84 -0
- package/lib/browser/claude-code-chat-agent.d.ts.map +1 -0
- package/lib/browser/claude-code-chat-agent.js +525 -0
- package/lib/browser/claude-code-chat-agent.js.map +1 -0
- package/lib/browser/claude-code-command-contribution.d.ts +16 -0
- package/lib/browser/claude-code-command-contribution.d.ts.map +1 -0
- package/lib/browser/claude-code-command-contribution.js +86 -0
- package/lib/browser/claude-code-command-contribution.js.map +1 -0
- package/lib/browser/claude-code-edit-tool-service.d.ts +63 -0
- package/lib/browser/claude-code-edit-tool-service.d.ts.map +1 -0
- package/lib/browser/claude-code-edit-tool-service.js +225 -0
- package/lib/browser/claude-code-edit-tool-service.js.map +1 -0
- package/lib/browser/claude-code-file-edit-backup-service.d.ts +59 -0
- package/lib/browser/claude-code-file-edit-backup-service.d.ts.map +1 -0
- package/lib/browser/claude-code-file-edit-backup-service.js +98 -0
- package/lib/browser/claude-code-file-edit-backup-service.js.map +1 -0
- package/lib/browser/claude-code-frontend-module.d.ts +5 -0
- package/lib/browser/claude-code-frontend-module.d.ts.map +1 -0
- package/lib/browser/claude-code-frontend-module.js +83 -0
- package/lib/browser/claude-code-frontend-module.js.map +1 -0
- package/lib/browser/claude-code-frontend-service.d.ts +39 -0
- package/lib/browser/claude-code-frontend-service.d.ts.map +1 -0
- package/lib/browser/claude-code-frontend-service.js +189 -0
- package/lib/browser/claude-code-frontend-service.js.map +1 -0
- package/lib/browser/claude-code-slash-commands-contribution.d.ts +34 -0
- package/lib/browser/claude-code-slash-commands-contribution.d.ts.map +1 -0
- package/lib/browser/claude-code-slash-commands-contribution.js +219 -0
- package/lib/browser/claude-code-slash-commands-contribution.js.map +1 -0
- package/lib/browser/claude-code-tool-call-content.d.ts +8 -0
- package/lib/browser/claude-code-tool-call-content.d.ts.map +1 -0
- package/lib/browser/claude-code-tool-call-content.js +30 -0
- package/lib/browser/claude-code-tool-call-content.js.map +1 -0
- package/lib/browser/renderers/bash-tool-renderer.d.ts +10 -0
- package/lib/browser/renderers/bash-tool-renderer.d.ts.map +1 -0
- package/lib/browser/renderers/bash-tool-renderer.js +67 -0
- package/lib/browser/renderers/bash-tool-renderer.js.map +1 -0
- package/lib/browser/renderers/collapsible-tool-renderer.d.ts +13 -0
- package/lib/browser/renderers/collapsible-tool-renderer.d.ts.map +1 -0
- package/lib/browser/renderers/collapsible-tool-renderer.js +48 -0
- package/lib/browser/renderers/collapsible-tool-renderer.js.map +1 -0
- package/lib/browser/renderers/edit-tool-renderer.d.ts +16 -0
- package/lib/browser/renderers/edit-tool-renderer.d.ts.map +1 -0
- package/lib/browser/renderers/edit-tool-renderer.js +135 -0
- package/lib/browser/renderers/edit-tool-renderer.js.map +1 -0
- package/lib/browser/renderers/glob-tool-renderer.d.ts +14 -0
- package/lib/browser/renderers/glob-tool-renderer.d.ts.map +1 -0
- package/lib/browser/renderers/glob-tool-renderer.js +104 -0
- package/lib/browser/renderers/glob-tool-renderer.js.map +1 -0
- package/lib/browser/renderers/grep-tool-renderer.d.ts +14 -0
- package/lib/browser/renderers/grep-tool-renderer.d.ts.map +1 -0
- package/lib/browser/renderers/grep-tool-renderer.js +160 -0
- package/lib/browser/renderers/grep-tool-renderer.js.map +1 -0
- package/lib/browser/renderers/ls-tool-renderer.d.ts +16 -0
- package/lib/browser/renderers/ls-tool-renderer.d.ts.map +1 -0
- package/lib/browser/renderers/ls-tool-renderer.js +116 -0
- package/lib/browser/renderers/ls-tool-renderer.js.map +1 -0
- package/lib/browser/renderers/multiedit-tool-renderer.d.ts +16 -0
- package/lib/browser/renderers/multiedit-tool-renderer.d.ts.map +1 -0
- package/lib/browser/renderers/multiedit-tool-renderer.js +148 -0
- package/lib/browser/renderers/multiedit-tool-renderer.js.map +1 -0
- package/lib/browser/renderers/read-tool-renderer.d.ts +16 -0
- package/lib/browser/renderers/read-tool-renderer.d.ts.map +1 -0
- package/lib/browser/renderers/read-tool-renderer.js +124 -0
- package/lib/browser/renderers/read-tool-renderer.js.map +1 -0
- package/lib/browser/renderers/todo-write-renderer.d.ts +10 -0
- package/lib/browser/renderers/todo-write-renderer.d.ts.map +1 -0
- package/lib/browser/renderers/todo-write-renderer.js +133 -0
- package/lib/browser/renderers/todo-write-renderer.js.map +1 -0
- package/lib/browser/renderers/web-fetch-tool-renderer.d.ts +10 -0
- package/lib/browser/renderers/web-fetch-tool-renderer.d.ts.map +1 -0
- package/lib/browser/renderers/web-fetch-tool-renderer.js +83 -0
- package/lib/browser/renderers/web-fetch-tool-renderer.js.map +1 -0
- package/lib/browser/renderers/write-tool-renderer.d.ts +16 -0
- package/lib/browser/renderers/write-tool-renderer.d.ts.map +1 -0
- package/lib/browser/renderers/write-tool-renderer.js +114 -0
- package/lib/browser/renderers/write-tool-renderer.js.map +1 -0
- package/lib/common/claude-code-preferences.d.ts +5 -0
- package/lib/common/claude-code-preferences.d.ts.map +1 -0
- package/lib/common/claude-code-preferences.js +40 -0
- package/lib/common/claude-code-preferences.js.map +1 -0
- package/lib/common/claude-code-service.d.ts +235 -0
- package/lib/common/claude-code-service.d.ts.map +1 -0
- package/lib/common/claude-code-service.js +82 -0
- package/lib/common/claude-code-service.js.map +1 -0
- package/lib/common/index.d.ts +2 -0
- package/lib/common/index.d.ts.map +1 -0
- package/lib/common/index.js +20 -0
- package/lib/common/index.js.map +1 -0
- package/lib/node/claude-code-backend-module.d.ts +4 -0
- package/lib/node/claude-code-backend-module.d.ts.map +1 -0
- package/lib/node/claude-code-backend-module.js +35 -0
- package/lib/node/claude-code-backend-module.js.map +1 -0
- package/lib/node/claude-code-service-impl.d.ts +33 -0
- package/lib/node/claude-code-service-impl.d.ts.map +1 -0
- package/lib/node/claude-code-service-impl.js +468 -0
- package/lib/node/claude-code-service-impl.js.map +1 -0
- package/lib/package.spec.d.ts +1 -0
- package/lib/package.spec.d.ts.map +1 -0
- package/lib/package.spec.js +26 -0
- package/lib/package.spec.js.map +1 -0
- package/package.json +10 -10
- package/src/browser/claude-code-chat-agent.ts +17 -10
- package/src/browser/claude-code-edit-tool-service.ts +10 -7
- package/src/browser/claude-code-file-edit-backup-service.ts +6 -2
- package/src/browser/claude-code-slash-commands-contribution.ts +152 -73
|
@@ -19,11 +19,11 @@ import { ChangeSetFileElement, ChangeSetFileElementFactory } from '@theia/ai-cha
|
|
|
19
19
|
import { ChangeSetElement } from '@theia/ai-chat/lib/common/change-set';
|
|
20
20
|
import { ContentReplacerV1Impl, Replacement } from '@theia/core/lib/common/content-replacer';
|
|
21
21
|
import { URI } from '@theia/core/lib/common/uri';
|
|
22
|
-
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
22
|
+
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
|
23
23
|
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|
24
24
|
import { WorkspaceService } from '@theia/workspace/lib/browser';
|
|
25
25
|
import { FileEditBackupService } from './claude-code-file-edit-backup-service';
|
|
26
|
-
import { nls } from '@theia/core';
|
|
26
|
+
import { ILogger, nls } from '@theia/core';
|
|
27
27
|
|
|
28
28
|
export interface EditToolInput {
|
|
29
29
|
file_path: string;
|
|
@@ -85,6 +85,9 @@ export class ClaudeCodeEditToolServiceImpl implements ClaudeCodeEditToolService
|
|
|
85
85
|
@inject(FileEditBackupService)
|
|
86
86
|
protected readonly backupService: FileEditBackupService;
|
|
87
87
|
|
|
88
|
+
@inject(ILogger) @named('claude-code')
|
|
89
|
+
protected readonly logger: ILogger;
|
|
90
|
+
|
|
88
91
|
private readonly contentReplacer = new ContentReplacerV1Impl();
|
|
89
92
|
|
|
90
93
|
async handleEditTool(toolUse: ToolUseBlock, request: MutableChatRequestModel, context: EditToolContext): Promise<void> {
|
|
@@ -103,7 +106,7 @@ export class ClaudeCodeEditToolServiceImpl implements ClaudeCodeEditToolService
|
|
|
103
106
|
break;
|
|
104
107
|
}
|
|
105
108
|
} catch (error) {
|
|
106
|
-
|
|
109
|
+
this.logger.error('Error handling edit tool:', error);
|
|
107
110
|
}
|
|
108
111
|
}
|
|
109
112
|
|
|
@@ -140,7 +143,7 @@ export class ClaudeCodeEditToolServiceImpl implements ClaudeCodeEditToolService
|
|
|
140
143
|
|
|
141
144
|
request.session.changeSet.setTitle(nls.localize('theia/ai/claude-code/changeSetTitle', 'Changes by Claude Code'));
|
|
142
145
|
} catch (error) {
|
|
143
|
-
|
|
146
|
+
this.logger.error('Error handling Edit tool:', error);
|
|
144
147
|
}
|
|
145
148
|
}
|
|
146
149
|
|
|
@@ -177,7 +180,7 @@ export class ClaudeCodeEditToolServiceImpl implements ClaudeCodeEditToolService
|
|
|
177
180
|
|
|
178
181
|
request.session.changeSet.setTitle(nls.localize('theia/ai/claude-code/changeSetTitle', 'Changes by Claude Code'));
|
|
179
182
|
} catch (error) {
|
|
180
|
-
|
|
183
|
+
this.logger.error('Error handling MultiEdit tool:', error);
|
|
181
184
|
}
|
|
182
185
|
}
|
|
183
186
|
|
|
@@ -237,7 +240,7 @@ export class ClaudeCodeEditToolServiceImpl implements ClaudeCodeEditToolService
|
|
|
237
240
|
|
|
238
241
|
request.session.changeSet.setTitle(nls.localize('theia/ai/claude-code/changeSetTitle', 'Changes by Claude Code'));
|
|
239
242
|
} catch (error) {
|
|
240
|
-
|
|
243
|
+
this.logger.error('Error handling Write tool:', error);
|
|
241
244
|
}
|
|
242
245
|
}
|
|
243
246
|
|
|
@@ -279,7 +282,7 @@ export class ClaudeCodeEditToolServiceImpl implements ClaudeCodeEditToolService
|
|
|
279
282
|
);
|
|
280
283
|
|
|
281
284
|
if (errors.length > 0) {
|
|
282
|
-
|
|
285
|
+
this.logger.error('Content replacement errors:', errors);
|
|
283
286
|
return;
|
|
284
287
|
}
|
|
285
288
|
|
|
@@ -17,10 +17,11 @@
|
|
|
17
17
|
import { MutableChatRequestModel } from '@theia/ai-chat';
|
|
18
18
|
import { ChangeSetFileElement } from '@theia/ai-chat/lib/browser/change-set-file-element';
|
|
19
19
|
import { URI } from '@theia/core/lib/common/uri';
|
|
20
|
-
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
20
|
+
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
|
21
21
|
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|
22
22
|
import { WorkspaceService } from '@theia/workspace/lib/browser';
|
|
23
23
|
import { CLAUDE_SESSION_ID_KEY } from './claude-code-chat-agent';
|
|
24
|
+
import { ILogger } from '@theia/core';
|
|
24
25
|
|
|
25
26
|
export const FileEditBackupService = Symbol('FileEditBackupService');
|
|
26
27
|
|
|
@@ -80,6 +81,9 @@ export class FileEditBackupServiceImpl implements FileEditBackupService {
|
|
|
80
81
|
@inject(WorkspaceService)
|
|
81
82
|
protected readonly workspaceService: WorkspaceService;
|
|
82
83
|
|
|
84
|
+
@inject(ILogger) @named('claude-code')
|
|
85
|
+
protected readonly logger: ILogger;
|
|
86
|
+
|
|
83
87
|
getLocation(workspaceRoot: URI): URI {
|
|
84
88
|
// This path structure must match the backup hooks in claude-code-service-impl.ts
|
|
85
89
|
// See ensureFileBackupHook() method which creates backups at:
|
|
@@ -107,7 +111,7 @@ export class FileEditBackupServiceImpl implements FileEditBackupService {
|
|
|
107
111
|
return backupContent.value.toString();
|
|
108
112
|
}
|
|
109
113
|
} catch (error) {
|
|
110
|
-
|
|
114
|
+
this.logger.error('Error reading backup file:', error);
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
return undefined;
|
|
@@ -14,17 +14,18 @@
|
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
|
|
17
|
-
import {
|
|
18
|
-
import { nls, URI } from '@theia/core';
|
|
17
|
+
import { PromptService } from '@theia/ai-core/lib/common/prompt-service';
|
|
18
|
+
import { DisposableCollection, ILogger, nls, URI } from '@theia/core';
|
|
19
19
|
import { FrontendApplicationContribution } from '@theia/core/lib/browser';
|
|
20
|
-
import {
|
|
21
|
-
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
20
|
+
import { inject, injectable, named } from '@theia/core/shared/inversify';
|
|
22
21
|
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|
23
|
-
import
|
|
22
|
+
import { FileChangeType } from '@theia/filesystem/lib/common/files';
|
|
24
23
|
import { WorkspaceService } from '@theia/workspace/lib/browser';
|
|
25
24
|
import { CLAUDE_CHAT_AGENT_ID } from './claude-code-chat-agent';
|
|
26
25
|
|
|
27
26
|
const CLAUDE_COMMANDS = '.claude/commands';
|
|
27
|
+
const COMMAND_FRAGMENT_PREFIX = 'claude-code-slash-';
|
|
28
|
+
const DYNAMIC_COMMAND_PREFIX = 'claude-code-dynamic-';
|
|
28
29
|
|
|
29
30
|
interface StaticSlashCommand {
|
|
30
31
|
name: string;
|
|
@@ -65,8 +66,11 @@ export class ClaudeCodeSlashCommandsContribution implements FrontendApplicationC
|
|
|
65
66
|
}
|
|
66
67
|
];
|
|
67
68
|
|
|
68
|
-
@inject(
|
|
69
|
-
protected readonly
|
|
69
|
+
@inject(ILogger) @named('claude-code')
|
|
70
|
+
protected readonly logger: ILogger;
|
|
71
|
+
|
|
72
|
+
@inject(PromptService)
|
|
73
|
+
protected readonly promptService: PromptService;
|
|
70
74
|
|
|
71
75
|
@inject(WorkspaceService)
|
|
72
76
|
protected readonly workspaceService: WorkspaceService;
|
|
@@ -74,86 +78,161 @@ export class ClaudeCodeSlashCommandsContribution implements FrontendApplicationC
|
|
|
74
78
|
@inject(FileService)
|
|
75
79
|
protected readonly fileService: FileService;
|
|
76
80
|
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
81
|
+
protected readonly toDispose = new DisposableCollection();
|
|
82
|
+
protected currentWorkspaceRoot: URI | undefined;
|
|
83
|
+
protected fileWatcherDisposable: DisposableCollection | undefined;
|
|
84
|
+
|
|
85
|
+
async onStart(): Promise<void> {
|
|
86
|
+
this.registerStaticCommands();
|
|
87
|
+
await this.initializeDynamicCommands();
|
|
88
|
+
|
|
89
|
+
this.toDispose.push(
|
|
90
|
+
this.workspaceService.onWorkspaceChanged(() => this.handleWorkspaceChange())
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
onStop(): void {
|
|
95
|
+
this.toDispose.dispose();
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
protected registerStaticCommands(): void {
|
|
99
|
+
for (const command of this.staticCommands) {
|
|
100
|
+
this.promptService.addBuiltInPromptFragment({
|
|
101
|
+
id: `${COMMAND_FRAGMENT_PREFIX}${command.name}`,
|
|
102
|
+
template: `/${command.name}`,
|
|
103
|
+
isCommand: true,
|
|
104
|
+
commandName: command.name,
|
|
105
|
+
commandDescription: command.description,
|
|
106
|
+
commandAgents: [CLAUDE_CHAT_AGENT_ID]
|
|
107
|
+
});
|
|
92
108
|
}
|
|
109
|
+
}
|
|
93
110
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
111
|
+
protected async initializeDynamicCommands(): Promise<void> {
|
|
112
|
+
const workspaceRoot = this.getWorkspaceRoot();
|
|
113
|
+
if (!workspaceRoot) {
|
|
114
|
+
return;
|
|
97
115
|
}
|
|
98
116
|
|
|
117
|
+
this.currentWorkspaceRoot = workspaceRoot;
|
|
118
|
+
await this.registerDynamicCommandsForWorkspace(workspaceRoot);
|
|
119
|
+
this.setupFileWatcher(workspaceRoot);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
protected async registerDynamicCommandsForWorkspace(workspaceRoot: URI): Promise<void> {
|
|
123
|
+
const commandsUri = this.getCommandsUri(workspaceRoot);
|
|
124
|
+
const files = await this.listMarkdownFiles(commandsUri);
|
|
125
|
+
|
|
126
|
+
for (const filename of files) {
|
|
127
|
+
await this.registerDynamicCommand(commandsUri, filename);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
protected async registerDynamicCommand(commandsDir: URI, filename: string): Promise<void> {
|
|
132
|
+
const commandName = this.getCommandNameFromFilename(filename);
|
|
133
|
+
const fileUri = commandsDir.resolve(filename);
|
|
134
|
+
|
|
99
135
|
try {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
label: command.name,
|
|
108
|
-
range: completionRange,
|
|
109
|
-
detail: command.description
|
|
110
|
-
});
|
|
136
|
+
const content = await this.fileService.read(fileUri);
|
|
137
|
+
this.promptService.addBuiltInPromptFragment({
|
|
138
|
+
id: this.getDynamicCommandId(commandName),
|
|
139
|
+
template: content.value,
|
|
140
|
+
isCommand: true,
|
|
141
|
+
commandName,
|
|
142
|
+
commandAgents: [CLAUDE_CHAT_AGENT_ID]
|
|
111
143
|
});
|
|
144
|
+
} catch (error) {
|
|
145
|
+
this.logger.error(`Failed to register Claude Code slash command '${commandName}' from ${fileUri}:`, error);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
112
148
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const uri = roots[0].resource;
|
|
117
|
-
const claudeCommandsUri = uri.resolve(CLAUDE_COMMANDS);
|
|
118
|
-
const files = await this.listFilesDirectly(claudeCommandsUri);
|
|
119
|
-
const commands = files
|
|
120
|
-
.filter(file => file.endsWith('.md'))
|
|
121
|
-
.map(file => file.replace(/\.md$/, ''));
|
|
122
|
-
|
|
123
|
-
commands.forEach(commandName => {
|
|
124
|
-
suggestions.push({
|
|
125
|
-
insertText: `${commandName} `,
|
|
126
|
-
kind: monaco.languages.CompletionItemKind.Function,
|
|
127
|
-
label: commandName,
|
|
128
|
-
range: completionRange,
|
|
129
|
-
detail: nls.localize('theia/ai/claude-code/commandDetail', 'Claude command: {0}', commandName)
|
|
130
|
-
});
|
|
131
|
-
});
|
|
132
|
-
}
|
|
149
|
+
protected setupFileWatcher(workspaceRoot: URI): void {
|
|
150
|
+
this.fileWatcherDisposable?.dispose();
|
|
151
|
+
this.fileWatcherDisposable = new DisposableCollection();
|
|
133
152
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
153
|
+
const commandsUri = this.getCommandsUri(workspaceRoot);
|
|
154
|
+
|
|
155
|
+
this.fileWatcherDisposable.push(
|
|
156
|
+
this.fileService.onDidFilesChange(async event => {
|
|
157
|
+
const relevantChanges = event.changes.filter(change =>
|
|
158
|
+
this.isCommandFile(change.resource, commandsUri)
|
|
159
|
+
);
|
|
160
|
+
|
|
161
|
+
if (relevantChanges.length === 0) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
for (const change of relevantChanges) {
|
|
166
|
+
await this.handleFileChange(change.resource, change.type, commandsUri);
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
this.toDispose.push(this.fileWatcherDisposable);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
protected async handleFileChange(resource: URI, changeType: FileChangeType, commandsUri: URI): Promise<void> {
|
|
175
|
+
const filename = resource.path.base;
|
|
176
|
+
const commandName = this.getCommandNameFromFilename(filename);
|
|
177
|
+
const fragmentId = this.getDynamicCommandId(commandName);
|
|
178
|
+
|
|
179
|
+
if (changeType === FileChangeType.DELETED) {
|
|
180
|
+
this.promptService.removePromptFragment(fragmentId);
|
|
181
|
+
} else if (changeType === FileChangeType.ADDED || changeType === FileChangeType.UPDATED) {
|
|
182
|
+
await this.registerDynamicCommand(commandsUri, filename);
|
|
138
183
|
}
|
|
139
184
|
}
|
|
140
185
|
|
|
141
|
-
protected
|
|
142
|
-
const
|
|
143
|
-
const lineContent = model.getLineContent(position.lineNumber);
|
|
186
|
+
protected async handleWorkspaceChange(): Promise<void> {
|
|
187
|
+
const newRoot = this.getWorkspaceRoot();
|
|
144
188
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (characterBeforeCurrentWord !== triggerCharacter) {
|
|
148
|
-
return undefined;
|
|
189
|
+
if (this.currentWorkspaceRoot?.toString() === newRoot?.toString()) {
|
|
190
|
+
return;
|
|
149
191
|
}
|
|
150
192
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
193
|
+
await this.clearDynamicCommands();
|
|
194
|
+
this.currentWorkspaceRoot = newRoot;
|
|
195
|
+
await this.initializeDynamicCommands();
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
protected async clearDynamicCommands(): Promise<void> {
|
|
199
|
+
if (!this.currentWorkspaceRoot) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const commandsUri = this.getCommandsUri(this.currentWorkspaceRoot);
|
|
204
|
+
const files = await this.listMarkdownFiles(commandsUri);
|
|
205
|
+
|
|
206
|
+
for (const filename of files) {
|
|
207
|
+
const commandName = this.getCommandNameFromFilename(filename);
|
|
208
|
+
this.promptService.removePromptFragment(this.getDynamicCommandId(commandName));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
protected getWorkspaceRoot(): URI | undefined {
|
|
213
|
+
const roots = this.workspaceService.tryGetRoots();
|
|
214
|
+
return roots.length > 0 ? roots[0].resource : undefined;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
protected getCommandsUri(workspaceRoot: URI): URI {
|
|
218
|
+
return workspaceRoot.resolve(CLAUDE_COMMANDS);
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
protected isCommandFile(resource: URI, commandsUri: URI): boolean {
|
|
222
|
+
return resource.toString().startsWith(commandsUri.toString()) && resource.path.ext === '.md';
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
protected getCommandNameFromFilename(filename: string): string {
|
|
226
|
+
return filename.replace(/\.md$/, '');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
protected getDynamicCommandId(commandName: string): string {
|
|
230
|
+
return `${DYNAMIC_COMMAND_PREFIX}${commandName}`;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
protected async listMarkdownFiles(uri: URI): Promise<string[]> {
|
|
234
|
+
const allFiles = await this.listFilesDirectly(uri);
|
|
235
|
+
return allFiles.filter(file => file.endsWith('.md'));
|
|
157
236
|
}
|
|
158
237
|
|
|
159
238
|
protected async listFilesDirectly(uri: URI): Promise<string[]> {
|