@theia/ai-claude-code 1.65.0

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.
Files changed (128) hide show
  1. package/README.md +30 -0
  2. package/lib/browser/claude-code-chat-agent.d.ts +82 -0
  3. package/lib/browser/claude-code-chat-agent.d.ts.map +1 -0
  4. package/lib/browser/claude-code-chat-agent.js +518 -0
  5. package/lib/browser/claude-code-chat-agent.js.map +1 -0
  6. package/lib/browser/claude-code-command-contribution.d.ts +16 -0
  7. package/lib/browser/claude-code-command-contribution.d.ts.map +1 -0
  8. package/lib/browser/claude-code-command-contribution.js +86 -0
  9. package/lib/browser/claude-code-command-contribution.js.map +1 -0
  10. package/lib/browser/claude-code-edit-tool-service.d.ts +61 -0
  11. package/lib/browser/claude-code-edit-tool-service.d.ts.map +1 -0
  12. package/lib/browser/claude-code-edit-tool-service.js +219 -0
  13. package/lib/browser/claude-code-edit-tool-service.js.map +1 -0
  14. package/lib/browser/claude-code-file-edit-backup-service.d.ts +57 -0
  15. package/lib/browser/claude-code-file-edit-backup-service.d.ts.map +1 -0
  16. package/lib/browser/claude-code-file-edit-backup-service.js +92 -0
  17. package/lib/browser/claude-code-file-edit-backup-service.js.map +1 -0
  18. package/lib/browser/claude-code-frontend-module.d.ts +5 -0
  19. package/lib/browser/claude-code-frontend-module.d.ts.map +1 -0
  20. package/lib/browser/claude-code-frontend-module.js +83 -0
  21. package/lib/browser/claude-code-frontend-module.js.map +1 -0
  22. package/lib/browser/claude-code-frontend-service.d.ts +40 -0
  23. package/lib/browser/claude-code-frontend-service.d.ts.map +1 -0
  24. package/lib/browser/claude-code-frontend-service.js +190 -0
  25. package/lib/browser/claude-code-frontend-service.js.map +1 -0
  26. package/lib/browser/claude-code-slash-commands-contribution.d.ts +17 -0
  27. package/lib/browser/claude-code-slash-commands-contribution.d.ts.map +1 -0
  28. package/lib/browser/claude-code-slash-commands-contribution.js +154 -0
  29. package/lib/browser/claude-code-slash-commands-contribution.js.map +1 -0
  30. package/lib/browser/claude-code-tool-call-content.d.ts +8 -0
  31. package/lib/browser/claude-code-tool-call-content.d.ts.map +1 -0
  32. package/lib/browser/claude-code-tool-call-content.js +30 -0
  33. package/lib/browser/claude-code-tool-call-content.js.map +1 -0
  34. package/lib/browser/renderers/bash-tool-renderer.d.ts +10 -0
  35. package/lib/browser/renderers/bash-tool-renderer.d.ts.map +1 -0
  36. package/lib/browser/renderers/bash-tool-renderer.js +71 -0
  37. package/lib/browser/renderers/bash-tool-renderer.js.map +1 -0
  38. package/lib/browser/renderers/collapsible-tool-renderer.d.ts +13 -0
  39. package/lib/browser/renderers/collapsible-tool-renderer.d.ts.map +1 -0
  40. package/lib/browser/renderers/collapsible-tool-renderer.js +48 -0
  41. package/lib/browser/renderers/collapsible-tool-renderer.js.map +1 -0
  42. package/lib/browser/renderers/edit-tool-renderer.d.ts +16 -0
  43. package/lib/browser/renderers/edit-tool-renderer.d.ts.map +1 -0
  44. package/lib/browser/renderers/edit-tool-renderer.js +134 -0
  45. package/lib/browser/renderers/edit-tool-renderer.js.map +1 -0
  46. package/lib/browser/renderers/glob-tool-renderer.d.ts +14 -0
  47. package/lib/browser/renderers/glob-tool-renderer.d.ts.map +1 -0
  48. package/lib/browser/renderers/glob-tool-renderer.js +107 -0
  49. package/lib/browser/renderers/glob-tool-renderer.js.map +1 -0
  50. package/lib/browser/renderers/grep-tool-renderer.d.ts +14 -0
  51. package/lib/browser/renderers/grep-tool-renderer.d.ts.map +1 -0
  52. package/lib/browser/renderers/grep-tool-renderer.js +157 -0
  53. package/lib/browser/renderers/grep-tool-renderer.js.map +1 -0
  54. package/lib/browser/renderers/ls-tool-renderer.d.ts +16 -0
  55. package/lib/browser/renderers/ls-tool-renderer.d.ts.map +1 -0
  56. package/lib/browser/renderers/ls-tool-renderer.js +116 -0
  57. package/lib/browser/renderers/ls-tool-renderer.js.map +1 -0
  58. package/lib/browser/renderers/multiedit-tool-renderer.d.ts +16 -0
  59. package/lib/browser/renderers/multiedit-tool-renderer.d.ts.map +1 -0
  60. package/lib/browser/renderers/multiedit-tool-renderer.js +152 -0
  61. package/lib/browser/renderers/multiedit-tool-renderer.js.map +1 -0
  62. package/lib/browser/renderers/read-tool-renderer.d.ts +16 -0
  63. package/lib/browser/renderers/read-tool-renderer.d.ts.map +1 -0
  64. package/lib/browser/renderers/read-tool-renderer.js +121 -0
  65. package/lib/browser/renderers/read-tool-renderer.js.map +1 -0
  66. package/lib/browser/renderers/todo-write-renderer.d.ts +10 -0
  67. package/lib/browser/renderers/todo-write-renderer.d.ts.map +1 -0
  68. package/lib/browser/renderers/todo-write-renderer.js +132 -0
  69. package/lib/browser/renderers/todo-write-renderer.js.map +1 -0
  70. package/lib/browser/renderers/web-fetch-tool-renderer.d.ts +10 -0
  71. package/lib/browser/renderers/web-fetch-tool-renderer.d.ts.map +1 -0
  72. package/lib/browser/renderers/web-fetch-tool-renderer.js +82 -0
  73. package/lib/browser/renderers/web-fetch-tool-renderer.js.map +1 -0
  74. package/lib/browser/renderers/write-tool-renderer.d.ts +16 -0
  75. package/lib/browser/renderers/write-tool-renderer.d.ts.map +1 -0
  76. package/lib/browser/renderers/write-tool-renderer.js +113 -0
  77. package/lib/browser/renderers/write-tool-renderer.js.map +1 -0
  78. package/lib/common/claude-code-preferences.d.ts +4 -0
  79. package/lib/common/claude-code-preferences.d.ts.map +1 -0
  80. package/lib/common/claude-code-preferences.js +33 -0
  81. package/lib/common/claude-code-preferences.js.map +1 -0
  82. package/lib/common/claude-code-service.d.ts +231 -0
  83. package/lib/common/claude-code-service.d.ts.map +1 -0
  84. package/lib/common/claude-code-service.js +82 -0
  85. package/lib/common/claude-code-service.js.map +1 -0
  86. package/lib/common/index.d.ts +2 -0
  87. package/lib/common/index.d.ts.map +1 -0
  88. package/lib/common/index.js +20 -0
  89. package/lib/common/index.js.map +1 -0
  90. package/lib/node/claude-code-backend-module.d.ts +4 -0
  91. package/lib/node/claude-code-backend-module.d.ts.map +1 -0
  92. package/lib/node/claude-code-backend-module.js +35 -0
  93. package/lib/node/claude-code-backend-module.js.map +1 -0
  94. package/lib/node/claude-code-service-impl.d.ts +32 -0
  95. package/lib/node/claude-code-service-impl.d.ts.map +1 -0
  96. package/lib/node/claude-code-service-impl.js +426 -0
  97. package/lib/node/claude-code-service-impl.js.map +1 -0
  98. package/lib/package.spec.d.ts +1 -0
  99. package/lib/package.spec.d.ts.map +1 -0
  100. package/lib/package.spec.js +26 -0
  101. package/lib/package.spec.js.map +1 -0
  102. package/package.json +57 -0
  103. package/src/browser/claude-code-chat-agent.ts +591 -0
  104. package/src/browser/claude-code-command-contribution.ts +80 -0
  105. package/src/browser/claude-code-edit-tool-service.ts +313 -0
  106. package/src/browser/claude-code-file-edit-backup-service.ts +141 -0
  107. package/src/browser/claude-code-frontend-module.ts +100 -0
  108. package/src/browser/claude-code-frontend-service.ts +215 -0
  109. package/src/browser/claude-code-slash-commands-contribution.ts +175 -0
  110. package/src/browser/claude-code-tool-call-content.ts +30 -0
  111. package/src/browser/renderers/bash-tool-renderer.tsx +97 -0
  112. package/src/browser/renderers/collapsible-tool-renderer.tsx +78 -0
  113. package/src/browser/renderers/edit-tool-renderer.tsx +180 -0
  114. package/src/browser/renderers/glob-tool-renderer.tsx +136 -0
  115. package/src/browser/renderers/grep-tool-renderer.tsx +190 -0
  116. package/src/browser/renderers/ls-tool-renderer.tsx +160 -0
  117. package/src/browser/renderers/multiedit-tool-renderer.tsx +204 -0
  118. package/src/browser/renderers/read-tool-renderer.tsx +170 -0
  119. package/src/browser/renderers/todo-write-renderer.tsx +178 -0
  120. package/src/browser/renderers/web-fetch-tool-renderer.tsx +108 -0
  121. package/src/browser/renderers/write-tool-renderer.tsx +155 -0
  122. package/src/browser/style/claude-code-tool-renderers.css +487 -0
  123. package/src/common/claude-code-preferences.ts +33 -0
  124. package/src/common/claude-code-service.ts +303 -0
  125. package/src/common/index.ts +17 -0
  126. package/src/node/claude-code-backend-module.ts +42 -0
  127. package/src/node/claude-code-service-impl.ts +462 -0
  128. package/src/package.spec.ts +27 -0
@@ -0,0 +1,215 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { CancellationToken, generateUuid, ILogger, PreferenceService } from '@theia/core';
18
+ import { FileUri } from '@theia/core/lib/common/file-uri';
19
+ import { inject, injectable, LazyServiceIdentifier } from '@theia/core/shared/inversify';
20
+ import {
21
+ OutputChannel,
22
+ OutputChannelManager,
23
+ OutputChannelSeverity
24
+ } from '@theia/output/lib/browser/output-channel';
25
+ import { WorkspaceService } from '@theia/workspace/lib/browser';
26
+ import {
27
+ ClaudeCodeClient,
28
+ ClaudeCodeOptions,
29
+ ClaudeCodeRequest,
30
+ ClaudeCodeService,
31
+ SDKMessage,
32
+ StreamMessage,
33
+ ToolApprovalResponseMessage
34
+ } from '../common/claude-code-service';
35
+ import { CLAUDE_CODE_EXECUTABLE_PATH_PREF } from '../common/claude-code-preferences';
36
+
37
+ export const API_KEY_PREF = 'ai-features.anthropic.AnthropicApiKey';
38
+
39
+ @injectable()
40
+ export class ClaudeCodeClientImpl implements ClaudeCodeClient {
41
+ protected tokenHandlers = new Map<string, (token?: StreamMessage) => void>();
42
+ protected errorHandlers = new Map<string, (error: Error) => void>();
43
+
44
+ // invoked by the backend
45
+ sendToken(streamId: string, token?: StreamMessage): void {
46
+ const handler = this.tokenHandlers.get(streamId);
47
+ if (handler) {
48
+ handler(token);
49
+ }
50
+ }
51
+
52
+ // invoked by the backend
53
+ sendError(streamId: string, error: Error): void {
54
+ const handler = this.errorHandlers.get(streamId);
55
+ if (handler) {
56
+ handler(error);
57
+ }
58
+ }
59
+
60
+ registerTokenHandler(streamId: string, handler: (token?: StreamMessage) => void): void {
61
+ this.tokenHandlers.set(streamId, handler);
62
+ }
63
+
64
+ registerErrorHandler(streamId: string, handler: (error: Error) => void): void {
65
+ this.errorHandlers.set(streamId, handler);
66
+ }
67
+
68
+ unregisterHandlers(streamId: string): void {
69
+ this.tokenHandlers.delete(streamId);
70
+ this.errorHandlers.delete(streamId);
71
+ }
72
+ }
73
+
74
+ interface StreamState {
75
+ id: string;
76
+ tokens: (StreamMessage | undefined)[];
77
+ isComplete: boolean;
78
+ hasError: boolean;
79
+ error?: Error;
80
+ pendingResolve?: () => void;
81
+ pendingReject?: (error: Error) => void;
82
+ }
83
+
84
+ @injectable()
85
+ export class ClaudeCodeFrontendService {
86
+
87
+ @inject(ClaudeCodeService)
88
+ protected claudeCodeBackendService: ClaudeCodeService;
89
+
90
+ @inject(new LazyServiceIdentifier(() => ClaudeCodeClientImpl))
91
+ protected client: ClaudeCodeClientImpl;
92
+
93
+ @inject(WorkspaceService)
94
+ protected readonly workspaceService: WorkspaceService;
95
+
96
+ @inject(PreferenceService)
97
+ protected preferenceService: PreferenceService;
98
+
99
+ @inject(OutputChannelManager)
100
+ protected readonly outputChannelManager: OutputChannelManager;
101
+
102
+ @inject(ILogger)
103
+ protected logger: ILogger;
104
+
105
+ protected streams = new Map<string, StreamState>();
106
+
107
+ async send(request: ClaudeCodeRequest, cancellationToken?: CancellationToken): Promise<AsyncIterable<StreamMessage>> {
108
+ const streamState: StreamState = {
109
+ id: this.generateStreamId(),
110
+ tokens: [],
111
+ isComplete: false,
112
+ hasError: false
113
+ };
114
+ this.streams.set(streamState.id, streamState);
115
+ this.setupStreamHandlers(streamState);
116
+
117
+ cancellationToken?.onCancellationRequested(() => this.claudeCodeBackendService.cancel(streamState.id));
118
+
119
+ const roots = await this.workspaceService.roots;
120
+ const rootsUris = roots.map(root => FileUri.fsPath(root.resource.toString()));
121
+
122
+ const prompt = request.prompt;
123
+ const apiKey = this.preferenceService.get<string>(API_KEY_PREF, undefined);
124
+ const claudeCodePath = this.preferenceService.get<string>(CLAUDE_CODE_EXECUTABLE_PATH_PREF, undefined);
125
+ this.getOutputChannel()?.appendLine(JSON.stringify(request, undefined, 2));
126
+
127
+ await this.claudeCodeBackendService.send({
128
+ prompt,
129
+ apiKey,
130
+ claudeCodePath,
131
+ options: <ClaudeCodeOptions>{
132
+ cwd: rootsUris[0],
133
+ ...request.options
134
+ }
135
+ }, streamState.id);
136
+
137
+ return this.createAsyncIterable(streamState);
138
+ }
139
+
140
+ protected generateStreamId(): string {
141
+ return generateUuid();
142
+ }
143
+
144
+ protected setupStreamHandlers(streamState: StreamState): void {
145
+ this.client.registerTokenHandler(streamState.id, (token?: SDKMessage) => {
146
+ if (token === undefined) {
147
+ streamState.isComplete = true;
148
+ } else {
149
+ this.getOutputChannel()?.appendLine(JSON.stringify(token, undefined, 2));
150
+ streamState.tokens.push(token);
151
+ }
152
+
153
+ // Resolve any pending iterator
154
+ if (streamState.pendingResolve) {
155
+ streamState.pendingResolve();
156
+ streamState.pendingResolve = undefined;
157
+ }
158
+ });
159
+
160
+ this.client.registerErrorHandler(streamState.id, (error: Error) => {
161
+ streamState.hasError = true;
162
+ streamState.error = error;
163
+ this.getOutputChannel()?.appendLine(JSON.stringify(error, undefined, 2), OutputChannelSeverity.Error);
164
+
165
+ // Reject any pending iterator
166
+ if (streamState.pendingReject) {
167
+ streamState.pendingReject(error);
168
+ streamState.pendingReject = undefined;
169
+ }
170
+ });
171
+ }
172
+
173
+ protected async *createAsyncIterable(streamState: StreamState): AsyncIterable<StreamMessage> {
174
+ let currentIndex = 0;
175
+
176
+ while (true) {
177
+ // Check for available tokens
178
+ if (currentIndex < streamState.tokens.length) {
179
+ const token = streamState.tokens[currentIndex];
180
+ currentIndex++;
181
+ if (token !== undefined) {
182
+ yield token;
183
+ }
184
+ continue;
185
+ }
186
+
187
+ if (streamState.isComplete) {
188
+ break;
189
+ }
190
+ if (streamState.hasError && streamState.error) {
191
+ throw streamState.error;
192
+ }
193
+
194
+ // Wait for next token
195
+ await new Promise<void>((resolve, reject) => {
196
+ streamState.pendingResolve = resolve;
197
+ streamState.pendingReject = reject;
198
+ });
199
+ }
200
+
201
+ // Cleanup
202
+ this.client.unregisterHandlers(streamState.id);
203
+ this.streams.delete(streamState.id);
204
+ }
205
+
206
+ sendApprovalResponse(response: ToolApprovalResponseMessage): void {
207
+ this.getOutputChannel()?.appendLine(JSON.stringify(response, undefined, 2));
208
+ this.claudeCodeBackendService.handleApprovalResponse(response);
209
+ }
210
+
211
+ protected getOutputChannel(): OutputChannel | undefined {
212
+ return this.outputChannelManager.getChannel('Claude Code');
213
+ }
214
+
215
+ }
@@ -0,0 +1,175 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { CHAT_VIEW_LANGUAGE_ID } from '@theia/ai-chat-ui/lib/browser/chat-view-language-contribution';
18
+ import { URI } from '@theia/core';
19
+ import { FrontendApplicationContribution } from '@theia/core/lib/browser';
20
+ import { ContextKeyService } from '@theia/core/lib/browser/context-key-service';
21
+ import { inject, injectable } from '@theia/core/shared/inversify';
22
+ import { FileService } from '@theia/filesystem/lib/browser/file-service';
23
+ import * as monaco from '@theia/monaco-editor-core';
24
+ import { WorkspaceService } from '@theia/workspace/lib/browser';
25
+ import { CLAUDE_CHAT_AGENT_ID } from './claude-code-chat-agent';
26
+
27
+ const CLAUDE_COMMANDS = '.claude/commands';
28
+
29
+ interface StaticSlashCommand {
30
+ name: string;
31
+ description: string;
32
+ }
33
+
34
+ @injectable()
35
+ export class ClaudeCodeSlashCommandsContribution implements FrontendApplicationContribution {
36
+
37
+ private readonly staticCommands: StaticSlashCommand[] = [
38
+ {
39
+ name: 'clear',
40
+ description: 'Create a new session',
41
+ },
42
+ {
43
+ name: 'compact',
44
+ description: 'Compact conversation with optional focus instructions',
45
+ },
46
+ {
47
+ name: 'config',
48
+ description: 'Open Claude Code Configuration',
49
+ },
50
+ {
51
+ name: 'init',
52
+ description: 'Initialize project with CLAUDE.md guide',
53
+ },
54
+ {
55
+ name: 'memory',
56
+ description: 'Edit CLAUDE.md memory file',
57
+ },
58
+ {
59
+ name: 'review',
60
+ description: 'Request code review',
61
+ },
62
+ {
63
+ name: 'resume',
64
+ description: 'Resume a session',
65
+ }
66
+ ];
67
+
68
+ @inject(ContextKeyService)
69
+ protected readonly contextKeyService: ContextKeyService;
70
+
71
+ @inject(WorkspaceService)
72
+ protected readonly workspaceService: WorkspaceService;
73
+
74
+ @inject(FileService)
75
+ protected readonly fileService: FileService;
76
+
77
+ onStart(): void {
78
+ monaco.languages.registerCompletionItemProvider(CHAT_VIEW_LANGUAGE_ID, {
79
+ triggerCharacters: ['/'],
80
+ provideCompletionItems: (model, position, _context, _token) =>
81
+ this.provideSlashCompletions(model, position),
82
+ });
83
+ }
84
+
85
+ protected async provideSlashCompletions(
86
+ model: monaco.editor.ITextModel,
87
+ position: monaco.Position
88
+ ): Promise<monaco.languages.CompletionList> {
89
+ const isClaudeCode = this.contextKeyService.match(`chatInputReceivingAgent == '${CLAUDE_CHAT_AGENT_ID}'`);
90
+ if (!isClaudeCode) {
91
+ return { suggestions: [] };
92
+ }
93
+
94
+ const completionRange = this.getCompletionRange(model, position, '/');
95
+ if (completionRange === undefined) {
96
+ return { suggestions: [] };
97
+ }
98
+
99
+ try {
100
+ const suggestions: monaco.languages.CompletionItem[] = [];
101
+
102
+ // Add static commands
103
+ this.staticCommands.forEach(command => {
104
+ suggestions.push({
105
+ insertText: `${command.name} `,
106
+ kind: monaco.languages.CompletionItemKind.Function,
107
+ label: command.name,
108
+ range: completionRange,
109
+ detail: command.description
110
+ });
111
+ });
112
+
113
+ // Add dynamic commands from .claude/commands directory
114
+ const roots = this.workspaceService.tryGetRoots();
115
+ if (roots.length >= 1) {
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: `Claude command: ${commandName}`
130
+ });
131
+ });
132
+ }
133
+
134
+ return { suggestions };
135
+ } catch (error) {
136
+ console.error('Error in Claude completion provider:', error);
137
+ return { suggestions: [] };
138
+ }
139
+ }
140
+
141
+ protected getCompletionRange(model: monaco.editor.ITextModel, position: monaco.Position, triggerCharacter: string): monaco.Range | undefined {
142
+ const wordInfo = model.getWordUntilPosition(position);
143
+ const lineContent = model.getLineContent(position.lineNumber);
144
+
145
+ // one to the left, and -1 for 0-based index
146
+ const characterBeforeCurrentWord = lineContent[wordInfo.startColumn - 1 - 1];
147
+ if (characterBeforeCurrentWord !== triggerCharacter) {
148
+ return undefined;
149
+ }
150
+
151
+ return new monaco.Range(
152
+ position.lineNumber,
153
+ wordInfo.startColumn,
154
+ position.lineNumber,
155
+ position.column
156
+ );
157
+ }
158
+
159
+ protected async listFilesDirectly(uri: URI): Promise<string[]> {
160
+ const result: string[] = [];
161
+ if (!await this.fileService.exists(uri)) {
162
+ return result;
163
+ }
164
+
165
+ const stat = await this.fileService.resolve(uri);
166
+ if (stat && stat.isDirectory && stat.children) {
167
+ for (const child of stat.children) {
168
+ result.push(child.resource.path.base);
169
+ }
170
+ }
171
+
172
+ return result;
173
+ }
174
+
175
+ }
@@ -0,0 +1,30 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { ToolCallChatResponseContentImpl } from '@theia/ai-chat/lib/common';
18
+ import { ToolCallResult } from '@theia/ai-core';
19
+
20
+ export class ClaudeCodeToolCallChatResponseContent extends ToolCallChatResponseContentImpl {
21
+ static readonly type = 'claude-code-tool-call';
22
+
23
+ constructor(id?: string, name?: string, arg_string?: string, finished?: boolean, result?: ToolCallResult) {
24
+ super(id, name, arg_string, finished, result);
25
+ }
26
+
27
+ static is(content: unknown): content is ClaudeCodeToolCallChatResponseContent {
28
+ return content instanceof ClaudeCodeToolCallChatResponseContent;
29
+ }
30
+ }
@@ -0,0 +1,97 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { ChatResponsePartRenderer } from '@theia/ai-chat-ui/lib/browser/chat-response-part-renderer';
18
+ import { ResponseNode } from '@theia/ai-chat-ui/lib/browser/chat-tree-view';
19
+ import { ChatResponseContent, ToolCallChatResponseContent } from '@theia/ai-chat/lib/common';
20
+ import { codicon } from '@theia/core/lib/browser';
21
+ import { injectable } from '@theia/core/shared/inversify';
22
+ import * as React from '@theia/core/shared/react';
23
+ import { ReactNode } from '@theia/core/shared/react';
24
+ import { ClaudeCodeToolCallChatResponseContent } from '../claude-code-tool-call-content';
25
+ import { CollapsibleToolRenderer } from './collapsible-tool-renderer';
26
+
27
+ interface BashToolInput {
28
+ command: string;
29
+ description?: string;
30
+ timeout?: number;
31
+ }
32
+
33
+ @injectable()
34
+ export class BashToolRenderer implements ChatResponsePartRenderer<ToolCallChatResponseContent> {
35
+
36
+ canHandle(response: ChatResponseContent): number {
37
+ if (ClaudeCodeToolCallChatResponseContent.is(response) && response.name === 'Bash') {
38
+ return 15; // Higher than default ToolCallPartRenderer (10)
39
+ }
40
+ return -1;
41
+ }
42
+
43
+ render(response: ToolCallChatResponseContent, parentNode: ResponseNode): ReactNode {
44
+ try {
45
+ const input = JSON.parse(response.arguments || '{}') as BashToolInput;
46
+ return <BashToolComponent input={input} />;
47
+ } catch (error) {
48
+ console.warn('Failed to parse Bash tool input:', error);
49
+ return <div className="claude-code-tool error">Failed to parse Bash tool data</div>;
50
+ }
51
+ }
52
+ }
53
+
54
+ const BashToolComponent: React.FC<{
55
+ input: BashToolInput;
56
+ }> = ({ input }) => {
57
+ const compactHeader = (
58
+ <>
59
+ <div className="claude-code-tool header-left">
60
+ <span className="claude-code-tool title">Terminal</span>
61
+ <span className={`${codicon('terminal')} claude-code-tool icon`} />
62
+ <span className="claude-code-tool command">{input.command}</span>
63
+ </div>
64
+ <div className="claude-code-tool header-right">
65
+ {input.timeout && (
66
+ <span className="claude-code-tool badge">Timeout: {input.timeout}ms</span>
67
+ )}
68
+ </div>
69
+ </>
70
+ );
71
+
72
+ const expandedContent = input.description ? (
73
+ <div className="claude-code-tool details">
74
+ <div className="claude-code-tool detail-row">
75
+ <span className="claude-code-tool detail-label">Command</span>
76
+ <code className="claude-code-tool detail-value">{input.command}</code>
77
+ </div>
78
+ <div className="claude-code-tool detail-row">
79
+ <span className="claude-code-tool detail-label">Description</span>
80
+ <span className="claude-code-tool detail-value">{input.description}</span>
81
+ </div>
82
+ {input.timeout && (
83
+ <div className="claude-code-tool detail-row">
84
+ <span className="claude-code-tool detail-label">Timeout</span>
85
+ <span className="claude-code-tool detail-value">{input.timeout}ms</span>
86
+ </div>
87
+ )}
88
+ </div>
89
+ ) : undefined;
90
+
91
+ return (
92
+ <CollapsibleToolRenderer
93
+ compactHeader={compactHeader}
94
+ expandedContent={expandedContent}
95
+ />
96
+ );
97
+ };
@@ -0,0 +1,78 @@
1
+ // *****************************************************************************
2
+ // Copyright (C) 2025 EclipseSource GmbH.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { codicon } from '@theia/core/lib/browser';
18
+ import * as React from '@theia/core/shared/react';
19
+ import { ReactNode } from '@theia/core/shared/react';
20
+
21
+ interface CollapsibleToolRendererProps {
22
+ compactHeader: ReactNode;
23
+ expandedContent?: ReactNode;
24
+ onHeaderClick?: () => void;
25
+ headerStyle?: React.CSSProperties;
26
+ defaultExpanded?: boolean;
27
+ }
28
+
29
+ export const CollapsibleToolRenderer: React.FC<CollapsibleToolRendererProps> = ({
30
+ compactHeader,
31
+ expandedContent,
32
+ onHeaderClick,
33
+ headerStyle,
34
+ defaultExpanded = false
35
+ }) => {
36
+ const [isExpanded, setIsExpanded] = React.useState(defaultExpanded);
37
+
38
+ const hasExpandableContent = expandedContent !== undefined;
39
+
40
+ const handleHeaderClick = (event: React.MouseEvent) => {
41
+ // Check if the clicked element or any of its parents has the 'clickable' class
42
+ // If so, don't trigger collapse/expand behavior
43
+ const target = event.target as HTMLElement;
44
+ if (target.closest('.clickable-element')) {
45
+ onHeaderClick?.();
46
+ return;
47
+ }
48
+
49
+ // Normal header click behavior
50
+ if (hasExpandableContent) {
51
+ setIsExpanded(!isExpanded);
52
+ }
53
+ onHeaderClick?.();
54
+ };
55
+
56
+ return (
57
+ <div className="claude-code-tool container">
58
+ <div
59
+ className={`claude-code-tool header${hasExpandableContent ? ' expandable' : ''}`}
60
+ onClick={handleHeaderClick}
61
+ style={{
62
+ cursor: hasExpandableContent || onHeaderClick ? 'pointer' : 'default',
63
+ ...headerStyle
64
+ }}
65
+ >
66
+ {hasExpandableContent && (
67
+ <span className={`${codicon(isExpanded ? 'chevron-down' : 'chevron-right')} claude-code-tool expand-icon`} />
68
+ )}
69
+ {compactHeader}
70
+ </div>
71
+ {hasExpandableContent && isExpanded && (
72
+ <div className="claude-code-tool expanded-content">
73
+ {expandedContent}
74
+ </div>
75
+ )}
76
+ </div>
77
+ );
78
+ };