@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.
- package/README.md +30 -0
- package/lib/browser/claude-code-chat-agent.d.ts +82 -0
- package/lib/browser/claude-code-chat-agent.d.ts.map +1 -0
- package/lib/browser/claude-code-chat-agent.js +518 -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 +61 -0
- package/lib/browser/claude-code-edit-tool-service.d.ts.map +1 -0
- package/lib/browser/claude-code-edit-tool-service.js +219 -0
- package/lib/browser/claude-code-edit-tool-service.js.map +1 -0
- package/lib/browser/claude-code-file-edit-backup-service.d.ts +57 -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 +92 -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 +40 -0
- package/lib/browser/claude-code-frontend-service.d.ts.map +1 -0
- package/lib/browser/claude-code-frontend-service.js +190 -0
- package/lib/browser/claude-code-frontend-service.js.map +1 -0
- package/lib/browser/claude-code-slash-commands-contribution.d.ts +17 -0
- package/lib/browser/claude-code-slash-commands-contribution.d.ts.map +1 -0
- package/lib/browser/claude-code-slash-commands-contribution.js +154 -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 +71 -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 +134 -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 +107 -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 +157 -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 +152 -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 +121 -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 +132 -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 +82 -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 +113 -0
- package/lib/browser/renderers/write-tool-renderer.js.map +1 -0
- package/lib/common/claude-code-preferences.d.ts +4 -0
- package/lib/common/claude-code-preferences.d.ts.map +1 -0
- package/lib/common/claude-code-preferences.js +33 -0
- package/lib/common/claude-code-preferences.js.map +1 -0
- package/lib/common/claude-code-service.d.ts +231 -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 +32 -0
- package/lib/node/claude-code-service-impl.d.ts.map +1 -0
- package/lib/node/claude-code-service-impl.js +426 -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 +57 -0
- package/src/browser/claude-code-chat-agent.ts +591 -0
- package/src/browser/claude-code-command-contribution.ts +80 -0
- package/src/browser/claude-code-edit-tool-service.ts +313 -0
- package/src/browser/claude-code-file-edit-backup-service.ts +141 -0
- package/src/browser/claude-code-frontend-module.ts +100 -0
- package/src/browser/claude-code-frontend-service.ts +215 -0
- package/src/browser/claude-code-slash-commands-contribution.ts +175 -0
- package/src/browser/claude-code-tool-call-content.ts +30 -0
- package/src/browser/renderers/bash-tool-renderer.tsx +97 -0
- package/src/browser/renderers/collapsible-tool-renderer.tsx +78 -0
- package/src/browser/renderers/edit-tool-renderer.tsx +180 -0
- package/src/browser/renderers/glob-tool-renderer.tsx +136 -0
- package/src/browser/renderers/grep-tool-renderer.tsx +190 -0
- package/src/browser/renderers/ls-tool-renderer.tsx +160 -0
- package/src/browser/renderers/multiedit-tool-renderer.tsx +204 -0
- package/src/browser/renderers/read-tool-renderer.tsx +170 -0
- package/src/browser/renderers/todo-write-renderer.tsx +178 -0
- package/src/browser/renderers/web-fetch-tool-renderer.tsx +108 -0
- package/src/browser/renderers/write-tool-renderer.tsx +155 -0
- package/src/browser/style/claude-code-tool-renderers.css +487 -0
- package/src/common/claude-code-preferences.ts +33 -0
- package/src/common/claude-code-service.ts +303 -0
- package/src/common/index.ts +17 -0
- package/src/node/claude-code-backend-module.ts +42 -0
- package/src/node/claude-code-service-impl.ts +462 -0
- package/src/package.spec.ts +27 -0
|
@@ -0,0 +1,180 @@
|
|
|
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 { LabelProvider } from '@theia/core/lib/browser';
|
|
21
|
+
import { URI } from '@theia/core/lib/common/uri';
|
|
22
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
23
|
+
import * as React from '@theia/core/shared/react';
|
|
24
|
+
import { ReactNode } from '@theia/core/shared/react';
|
|
25
|
+
import { EditorManager } from '@theia/editor/lib/browser';
|
|
26
|
+
import { WorkspaceService } from '@theia/workspace/lib/browser';
|
|
27
|
+
import { ClaudeCodeToolCallChatResponseContent } from '../claude-code-tool-call-content';
|
|
28
|
+
import { CollapsibleToolRenderer } from './collapsible-tool-renderer';
|
|
29
|
+
|
|
30
|
+
interface EditToolInput {
|
|
31
|
+
file_path: string;
|
|
32
|
+
old_string: string;
|
|
33
|
+
new_string: string;
|
|
34
|
+
replace_all?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@injectable()
|
|
38
|
+
export class EditToolRenderer implements ChatResponsePartRenderer<ToolCallChatResponseContent> {
|
|
39
|
+
|
|
40
|
+
@inject(WorkspaceService)
|
|
41
|
+
protected readonly workspaceService: WorkspaceService;
|
|
42
|
+
|
|
43
|
+
@inject(LabelProvider)
|
|
44
|
+
protected readonly labelProvider: LabelProvider;
|
|
45
|
+
|
|
46
|
+
@inject(EditorManager)
|
|
47
|
+
protected readonly editorManager: EditorManager;
|
|
48
|
+
|
|
49
|
+
canHandle(response: ChatResponseContent): number {
|
|
50
|
+
if (ClaudeCodeToolCallChatResponseContent.is(response) && response.name === 'Edit') {
|
|
51
|
+
return 15; // Higher than default ToolCallPartRenderer (10)
|
|
52
|
+
}
|
|
53
|
+
return -1;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
render(response: ToolCallChatResponseContent, parentNode: ResponseNode): ReactNode {
|
|
57
|
+
try {
|
|
58
|
+
const input = JSON.parse(response.arguments || '{}') as EditToolInput;
|
|
59
|
+
return <EditToolComponent
|
|
60
|
+
input={input}
|
|
61
|
+
workspaceService={this.workspaceService}
|
|
62
|
+
labelProvider={this.labelProvider}
|
|
63
|
+
editorManager={this.editorManager}
|
|
64
|
+
/>;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
console.warn('Failed to parse Edit tool input:', error);
|
|
67
|
+
return <div className="claude-code-tool error">Failed to parse Edit tool data</div>;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
const EditToolComponent: React.FC<{
|
|
73
|
+
input: EditToolInput;
|
|
74
|
+
workspaceService: WorkspaceService;
|
|
75
|
+
labelProvider: LabelProvider;
|
|
76
|
+
editorManager: EditorManager;
|
|
77
|
+
}> = ({ input, workspaceService, labelProvider, editorManager }) => {
|
|
78
|
+
const getFileName = (filePath: string): string => filePath.split('/').pop() || filePath;
|
|
79
|
+
const getWorkspaceRelativePath = async (filePath: string): Promise<string> => {
|
|
80
|
+
try {
|
|
81
|
+
const absoluteUri = new URI(filePath).parent;
|
|
82
|
+
const workspaceRelativePath = await workspaceService.getWorkspaceRelativePath(absoluteUri);
|
|
83
|
+
return workspaceRelativePath || '';
|
|
84
|
+
} catch {
|
|
85
|
+
return '';
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const getIcon = (filePath: string): string => {
|
|
90
|
+
try {
|
|
91
|
+
const uri = new URI(filePath);
|
|
92
|
+
return labelProvider.getIcon(uri) || 'codicon-file';
|
|
93
|
+
} catch {
|
|
94
|
+
return 'codicon-file';
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const handleOpenFile = async () => {
|
|
99
|
+
try {
|
|
100
|
+
const uri = new URI(input.file_path);
|
|
101
|
+
await editorManager.open(uri);
|
|
102
|
+
} catch (error) {
|
|
103
|
+
console.error('Failed to open file:', error);
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
const [relativePath, setRelativePath] = React.useState<string>('');
|
|
108
|
+
|
|
109
|
+
React.useEffect(() => {
|
|
110
|
+
getWorkspaceRelativePath(input.file_path).then(setRelativePath);
|
|
111
|
+
}, [input.file_path]);
|
|
112
|
+
|
|
113
|
+
const getChangeInfo = () => {
|
|
114
|
+
const oldLines = input.old_string.split('\n').length;
|
|
115
|
+
const newLines = input.new_string.split('\n').length;
|
|
116
|
+
return { oldLines, newLines };
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const compactHeader = (
|
|
120
|
+
<>
|
|
121
|
+
<div className="claude-code-tool header-left">
|
|
122
|
+
<span className="claude-code-tool title">Editing</span>
|
|
123
|
+
<span className={`${getIcon(input.file_path)} claude-code-tool icon`} />
|
|
124
|
+
<span
|
|
125
|
+
className="claude-code-tool file-name clickable-element"
|
|
126
|
+
onClick={handleOpenFile}
|
|
127
|
+
title="Click to open file in editor"
|
|
128
|
+
>
|
|
129
|
+
{getFileName(input.file_path)}
|
|
130
|
+
</span>
|
|
131
|
+
{relativePath && <span className="claude-code-tool relative-path" title={relativePath}>{relativePath}</span>}
|
|
132
|
+
</div>
|
|
133
|
+
<div className="claude-code-tool header-right">
|
|
134
|
+
<span className="claude-code-tool badge deleted">-{getChangeInfo().oldLines}</span>
|
|
135
|
+
<span className="claude-code-tool badge added">+{getChangeInfo().newLines}</span>
|
|
136
|
+
{input.replace_all && (
|
|
137
|
+
<span className="claude-code-tool badge">Replace All</span>
|
|
138
|
+
)}
|
|
139
|
+
</div>
|
|
140
|
+
</>
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const expandedContent = (
|
|
144
|
+
<div className="claude-code-tool details">
|
|
145
|
+
<div className="claude-code-tool detail-row">
|
|
146
|
+
<span className="claude-code-tool detail-label">File Path</span>
|
|
147
|
+
<code className="claude-code-tool detail-value">{input.file_path}</code>
|
|
148
|
+
</div>
|
|
149
|
+
<div className="claude-code-tool detail-row">
|
|
150
|
+
<span className="claude-code-tool detail-label">From</span>
|
|
151
|
+
<pre className="claude-code-tool detail-value code-preview">
|
|
152
|
+
{input.old_string.length > 200
|
|
153
|
+
? input.old_string.substring(0, 200) + '...'
|
|
154
|
+
: input.old_string}
|
|
155
|
+
</pre>
|
|
156
|
+
</div>
|
|
157
|
+
<div className="claude-code-tool detail-row">
|
|
158
|
+
<span className="claude-code-tool detail-label">To</span>
|
|
159
|
+
<pre className="claude-code-tool detail-value code-preview">
|
|
160
|
+
{input.new_string.length > 200
|
|
161
|
+
? input.new_string.substring(0, 200) + '...'
|
|
162
|
+
: input.new_string}
|
|
163
|
+
</pre>
|
|
164
|
+
</div>
|
|
165
|
+
{input.replace_all && (
|
|
166
|
+
<div className="claude-code-tool detail-row">
|
|
167
|
+
<span className="claude-code-tool detail-label">Mode</span>
|
|
168
|
+
<span className="claude-code-tool detail-value">Replace all occurrences</span>
|
|
169
|
+
</div>
|
|
170
|
+
)}
|
|
171
|
+
</div>
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
return (
|
|
175
|
+
<CollapsibleToolRenderer
|
|
176
|
+
compactHeader={compactHeader}
|
|
177
|
+
expandedContent={expandedContent}
|
|
178
|
+
/>
|
|
179
|
+
);
|
|
180
|
+
};
|
|
@@ -0,0 +1,136 @@
|
|
|
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, LabelProvider } from '@theia/core/lib/browser';
|
|
21
|
+
import { URI } from '@theia/core/lib/common/uri';
|
|
22
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
23
|
+
import * as React from '@theia/core/shared/react';
|
|
24
|
+
import { ReactNode } from '@theia/core/shared/react';
|
|
25
|
+
import { WorkspaceService } from '@theia/workspace/lib/browser';
|
|
26
|
+
import { ClaudeCodeToolCallChatResponseContent } from '../claude-code-tool-call-content';
|
|
27
|
+
import { CollapsibleToolRenderer } from './collapsible-tool-renderer';
|
|
28
|
+
|
|
29
|
+
interface GlobToolInput {
|
|
30
|
+
pattern: string;
|
|
31
|
+
path?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@injectable()
|
|
35
|
+
export class GlobToolRenderer implements ChatResponsePartRenderer<ToolCallChatResponseContent> {
|
|
36
|
+
|
|
37
|
+
@inject(WorkspaceService)
|
|
38
|
+
protected readonly workspaceService: WorkspaceService;
|
|
39
|
+
|
|
40
|
+
@inject(LabelProvider)
|
|
41
|
+
protected readonly labelProvider: LabelProvider;
|
|
42
|
+
|
|
43
|
+
canHandle(response: ChatResponseContent): number {
|
|
44
|
+
if (ClaudeCodeToolCallChatResponseContent.is(response) && response.name === 'Glob') {
|
|
45
|
+
return 15; // Higher than default ToolCallPartRenderer (10)
|
|
46
|
+
}
|
|
47
|
+
return -1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
render(response: ToolCallChatResponseContent, parentNode: ResponseNode): ReactNode {
|
|
51
|
+
try {
|
|
52
|
+
const input = JSON.parse(response.arguments || '{}') as GlobToolInput;
|
|
53
|
+
return <GlobToolComponent
|
|
54
|
+
input={input}
|
|
55
|
+
workspaceService={this.workspaceService}
|
|
56
|
+
labelProvider={this.labelProvider}
|
|
57
|
+
/>;
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.warn('Failed to parse Glob tool input:', error);
|
|
60
|
+
return <div className="claude-code-tool error">Failed to parse Glob tool data</div>;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const GlobToolComponent: React.FC<{
|
|
66
|
+
input: GlobToolInput;
|
|
67
|
+
workspaceService: WorkspaceService;
|
|
68
|
+
labelProvider: LabelProvider;
|
|
69
|
+
}> = ({ input, workspaceService, labelProvider }) => {
|
|
70
|
+
const getSearchScope = (): string => {
|
|
71
|
+
if (input.path) {
|
|
72
|
+
return input.path.split('/').pop() || input.path;
|
|
73
|
+
}
|
|
74
|
+
return 'project';
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
const getWorkspaceRelativePath = async (filePath: string): Promise<string> => {
|
|
78
|
+
try {
|
|
79
|
+
const absoluteUri = new URI(filePath);
|
|
80
|
+
const workspaceRelativePath = await workspaceService.getWorkspaceRelativePath(absoluteUri);
|
|
81
|
+
return workspaceRelativePath || '';
|
|
82
|
+
} catch {
|
|
83
|
+
return '';
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const [relativePath, setRelativePath] = React.useState<string>('');
|
|
88
|
+
|
|
89
|
+
React.useEffect(() => {
|
|
90
|
+
if (input.path) {
|
|
91
|
+
getWorkspaceRelativePath(input.path).then(setRelativePath);
|
|
92
|
+
}
|
|
93
|
+
}, [input.path]);
|
|
94
|
+
|
|
95
|
+
const compactHeader = (
|
|
96
|
+
<>
|
|
97
|
+
<div className="claude-code-tool header-left">
|
|
98
|
+
<span className="claude-code-tool title">Finding</span>
|
|
99
|
+
<span className={`${codicon('files')} claude-code-tool icon`} />
|
|
100
|
+
<span className="claude-code-tool glob-pattern">{input.pattern}</span>
|
|
101
|
+
<span className="claude-code-tool scope">in {getSearchScope()}</span>
|
|
102
|
+
{relativePath && <span className="claude-code-tool relative-path">{relativePath}</span>}
|
|
103
|
+
</div>
|
|
104
|
+
<div className="claude-code-tool header-right">
|
|
105
|
+
<span className="claude-code-tool badge">glob pattern</span>
|
|
106
|
+
</div>
|
|
107
|
+
</>
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const expandedContent = (
|
|
111
|
+
<div className="claude-code-tool details">
|
|
112
|
+
<div className="claude-code-tool detail-row">
|
|
113
|
+
<span className="claude-code-tool detail-label">Pattern</span>
|
|
114
|
+
<code className="claude-code-tool detail-value">{input.pattern}</code>
|
|
115
|
+
</div>
|
|
116
|
+
<div className="claude-code-tool detail-row">
|
|
117
|
+
<span className="claude-code-tool detail-label">Search Path</span>
|
|
118
|
+
<code className="claude-code-tool detail-value">{input.path || 'current directory'}</code>
|
|
119
|
+
</div>
|
|
120
|
+
<div className="claude-code-tool detail-row">
|
|
121
|
+
<span className="claude-code-tool detail-label">Description</span>
|
|
122
|
+
<span className="claude-code-tool detail-value">
|
|
123
|
+
Find files matching the glob pattern "{input.pattern}"
|
|
124
|
+
{input.path ? ` within ${input.path}` : ' in the current directory'}
|
|
125
|
+
</span>
|
|
126
|
+
</div>
|
|
127
|
+
</div>
|
|
128
|
+
);
|
|
129
|
+
|
|
130
|
+
return (
|
|
131
|
+
<CollapsibleToolRenderer
|
|
132
|
+
compactHeader={compactHeader}
|
|
133
|
+
expandedContent={expandedContent}
|
|
134
|
+
/>
|
|
135
|
+
);
|
|
136
|
+
};
|
|
@@ -0,0 +1,190 @@
|
|
|
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, LabelProvider } from '@theia/core/lib/browser';
|
|
21
|
+
import { URI } from '@theia/core/lib/common/uri';
|
|
22
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
23
|
+
import * as React from '@theia/core/shared/react';
|
|
24
|
+
import { ReactNode } from '@theia/core/shared/react';
|
|
25
|
+
import { WorkspaceService } from '@theia/workspace/lib/browser';
|
|
26
|
+
import { ClaudeCodeToolCallChatResponseContent } from '../claude-code-tool-call-content';
|
|
27
|
+
import { CollapsibleToolRenderer } from './collapsible-tool-renderer';
|
|
28
|
+
|
|
29
|
+
interface GrepToolInput {
|
|
30
|
+
pattern: string;
|
|
31
|
+
path?: string;
|
|
32
|
+
output_mode?: 'content' | 'files_with_matches' | 'count';
|
|
33
|
+
glob?: string;
|
|
34
|
+
type?: string;
|
|
35
|
+
'-i'?: boolean;
|
|
36
|
+
'-n'?: boolean;
|
|
37
|
+
'-A'?: number;
|
|
38
|
+
'-B'?: number;
|
|
39
|
+
'-C'?: number;
|
|
40
|
+
multiline?: boolean;
|
|
41
|
+
head_limit?: number;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@injectable()
|
|
45
|
+
export class GrepToolRenderer implements ChatResponsePartRenderer<ToolCallChatResponseContent> {
|
|
46
|
+
|
|
47
|
+
@inject(WorkspaceService)
|
|
48
|
+
protected readonly workspaceService: WorkspaceService;
|
|
49
|
+
|
|
50
|
+
@inject(LabelProvider)
|
|
51
|
+
protected readonly labelProvider: LabelProvider;
|
|
52
|
+
|
|
53
|
+
canHandle(response: ChatResponseContent): number {
|
|
54
|
+
if (ClaudeCodeToolCallChatResponseContent.is(response) && response.name === 'Grep') {
|
|
55
|
+
return 15; // Higher than default ToolCallPartRenderer (10)
|
|
56
|
+
}
|
|
57
|
+
return -1;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
render(response: ToolCallChatResponseContent, parentNode: ResponseNode): ReactNode {
|
|
61
|
+
try {
|
|
62
|
+
const input = JSON.parse(response.arguments || '{}') as GrepToolInput;
|
|
63
|
+
return <GrepToolComponent
|
|
64
|
+
input={input}
|
|
65
|
+
workspaceService={this.workspaceService}
|
|
66
|
+
labelProvider={this.labelProvider}
|
|
67
|
+
/>;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
console.warn('Failed to parse Grep tool input:', error);
|
|
70
|
+
return <div className="claude-code-tool error">Failed to parse Grep tool data</div>;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const GrepToolComponent: React.FC<{
|
|
76
|
+
input: GrepToolInput;
|
|
77
|
+
workspaceService: WorkspaceService;
|
|
78
|
+
labelProvider: LabelProvider;
|
|
79
|
+
}> = ({ input, workspaceService, labelProvider }) => {
|
|
80
|
+
const getSearchScope = (): string => {
|
|
81
|
+
if (input.path) {
|
|
82
|
+
return input.path.split('/').pop() || input.path;
|
|
83
|
+
}
|
|
84
|
+
return 'project';
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const getWorkspaceRelativePath = async (filePath: string): Promise<string> => {
|
|
88
|
+
try {
|
|
89
|
+
const absoluteUri = new URI(filePath);
|
|
90
|
+
const workspaceRelativePath = await workspaceService.getWorkspaceRelativePath(absoluteUri);
|
|
91
|
+
return workspaceRelativePath || '';
|
|
92
|
+
} catch {
|
|
93
|
+
return '';
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const [relativePath, setRelativePath] = React.useState<string>('');
|
|
98
|
+
|
|
99
|
+
React.useEffect(() => {
|
|
100
|
+
if (input.path) {
|
|
101
|
+
getWorkspaceRelativePath(input.path).then(setRelativePath);
|
|
102
|
+
}
|
|
103
|
+
}, [input.path]);
|
|
104
|
+
|
|
105
|
+
const getOptionsInfo = (): { label: string; count: number } => {
|
|
106
|
+
const options = [];
|
|
107
|
+
if (input['-i']) { options.push('case-insensitive'); }
|
|
108
|
+
if (input['-n']) { options.push('line numbers'); }
|
|
109
|
+
if (input['-A']) { options.push(`+${input['-A']} after`); }
|
|
110
|
+
if (input['-B']) { options.push(`+${input['-B']} before`); }
|
|
111
|
+
if (input['-C']) { options.push(`±${input['-C']} context`); }
|
|
112
|
+
if (input.multiline) { options.push('multiline'); }
|
|
113
|
+
if (input.glob) { options.push(`glob: ${input.glob}`); }
|
|
114
|
+
if (input.type) { options.push(`type: ${input.type}`); }
|
|
115
|
+
if (input.head_limit) { options.push(`limit: ${input.head_limit}`); }
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
label: options.length > 0 ? options.join(', ') : '',
|
|
119
|
+
count: options.length
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const optionsInfo = getOptionsInfo();
|
|
124
|
+
|
|
125
|
+
const compactHeader = (
|
|
126
|
+
<>
|
|
127
|
+
<div className="claude-code-tool header-left">
|
|
128
|
+
<span className="claude-code-tool title">Searching</span>
|
|
129
|
+
<span className={`${codicon('search')} claude-code-tool icon`} />
|
|
130
|
+
<span className="claude-code-tool pattern">"{input.pattern}"</span>
|
|
131
|
+
<span className="claude-code-tool scope">in {getSearchScope()}</span>
|
|
132
|
+
{relativePath && <span className="claude-code-tool relative-path">{relativePath}</span>}
|
|
133
|
+
</div>
|
|
134
|
+
<div className="claude-code-tool header-right">
|
|
135
|
+
{input.output_mode && input.output_mode !== 'files_with_matches' && (
|
|
136
|
+
<span className="claude-code-tool badge">{input.output_mode}</span>
|
|
137
|
+
)}
|
|
138
|
+
{optionsInfo.count > 0 && (
|
|
139
|
+
<span className="claude-code-tool badge" title={optionsInfo.label}>
|
|
140
|
+
{optionsInfo.count} option{optionsInfo.count > 1 ? 's' : ''}
|
|
141
|
+
</span>
|
|
142
|
+
)}
|
|
143
|
+
</div>
|
|
144
|
+
</>
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const expandedContent = (
|
|
148
|
+
<div className="claude-code-tool details">
|
|
149
|
+
<div className="claude-code-tool detail-row">
|
|
150
|
+
<span className="claude-code-tool detail-label">Pattern</span>
|
|
151
|
+
<code className="claude-code-tool detail-value">"{input.pattern}"</code>
|
|
152
|
+
</div>
|
|
153
|
+
<div className="claude-code-tool detail-row">
|
|
154
|
+
<span className="claude-code-tool detail-label">Search Path</span>
|
|
155
|
+
<code className="claude-code-tool detail-value">{input.path || 'project root'}</code>
|
|
156
|
+
</div>
|
|
157
|
+
{input.output_mode && (
|
|
158
|
+
<div className="claude-code-tool detail-row">
|
|
159
|
+
<span className="claude-code-tool detail-label">Mode</span>
|
|
160
|
+
<span className="claude-code-tool detail-value">{input.output_mode}</span>
|
|
161
|
+
</div>
|
|
162
|
+
)}
|
|
163
|
+
{input.glob && (
|
|
164
|
+
<div className="claude-code-tool detail-row">
|
|
165
|
+
<span className="claude-code-tool detail-label">File Filter</span>
|
|
166
|
+
<code className="claude-code-tool detail-value">{input.glob}</code>
|
|
167
|
+
</div>
|
|
168
|
+
)}
|
|
169
|
+
{input.type && (
|
|
170
|
+
<div className="claude-code-tool detail-row">
|
|
171
|
+
<span className="claude-code-tool detail-label">File Type</span>
|
|
172
|
+
<span className="claude-code-tool detail-value">{input.type}</span>
|
|
173
|
+
</div>
|
|
174
|
+
)}
|
|
175
|
+
{optionsInfo.label && (
|
|
176
|
+
<div className="claude-code-tool detail-row">
|
|
177
|
+
<span className="claude-code-tool detail-label">Options</span>
|
|
178
|
+
<span className="claude-code-tool detail-value">{optionsInfo.label}</span>
|
|
179
|
+
</div>
|
|
180
|
+
)}
|
|
181
|
+
</div>
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
return (
|
|
185
|
+
<CollapsibleToolRenderer
|
|
186
|
+
compactHeader={compactHeader}
|
|
187
|
+
expandedContent={expandedContent}
|
|
188
|
+
/>
|
|
189
|
+
);
|
|
190
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
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, LabelProvider } from '@theia/core/lib/browser';
|
|
21
|
+
import { URI } from '@theia/core/lib/common/uri';
|
|
22
|
+
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
23
|
+
import * as React from '@theia/core/shared/react';
|
|
24
|
+
import { ReactNode } from '@theia/core/shared/react';
|
|
25
|
+
import { EditorManager } from '@theia/editor/lib/browser';
|
|
26
|
+
import { WorkspaceService } from '@theia/workspace/lib/browser';
|
|
27
|
+
import { ClaudeCodeToolCallChatResponseContent } from '../claude-code-tool-call-content';
|
|
28
|
+
import { CollapsibleToolRenderer } from './collapsible-tool-renderer';
|
|
29
|
+
|
|
30
|
+
interface LSToolInput {
|
|
31
|
+
path: string;
|
|
32
|
+
ignore?: string[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@injectable()
|
|
36
|
+
export class LSToolRenderer implements ChatResponsePartRenderer<ToolCallChatResponseContent> {
|
|
37
|
+
|
|
38
|
+
@inject(WorkspaceService)
|
|
39
|
+
protected readonly workspaceService: WorkspaceService;
|
|
40
|
+
|
|
41
|
+
@inject(LabelProvider)
|
|
42
|
+
protected readonly labelProvider: LabelProvider;
|
|
43
|
+
|
|
44
|
+
@inject(EditorManager)
|
|
45
|
+
protected readonly editorManager: EditorManager;
|
|
46
|
+
|
|
47
|
+
canHandle(response: ChatResponseContent): number {
|
|
48
|
+
if (ClaudeCodeToolCallChatResponseContent.is(response) && response.name === 'LS') {
|
|
49
|
+
return 15; // Higher than default ToolCallPartRenderer (10)
|
|
50
|
+
}
|
|
51
|
+
return -1;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
render(response: ToolCallChatResponseContent, parentNode: ResponseNode): ReactNode {
|
|
55
|
+
try {
|
|
56
|
+
const input = JSON.parse(response.arguments || '{}') as LSToolInput;
|
|
57
|
+
return <LSToolComponent
|
|
58
|
+
input={input}
|
|
59
|
+
workspaceService={this.workspaceService}
|
|
60
|
+
labelProvider={this.labelProvider}
|
|
61
|
+
editorManager={this.editorManager}
|
|
62
|
+
/>;
|
|
63
|
+
} catch (error) {
|
|
64
|
+
console.warn('Failed to parse LS tool input:', error);
|
|
65
|
+
return <div className="claude-code-tool error">Failed to parse LS tool data</div>;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const LSToolComponent: React.FC<{
|
|
71
|
+
input: LSToolInput;
|
|
72
|
+
workspaceService: WorkspaceService;
|
|
73
|
+
labelProvider: LabelProvider;
|
|
74
|
+
editorManager: EditorManager;
|
|
75
|
+
}> = ({ input, workspaceService, labelProvider, editorManager }) => {
|
|
76
|
+
const getDirectoryName = (dirPath: string): string => dirPath.split('/').pop() || dirPath;
|
|
77
|
+
const getWorkspaceRelativePath = async (dirPath: string): Promise<string> => {
|
|
78
|
+
try {
|
|
79
|
+
const absoluteUri = new URI(dirPath);
|
|
80
|
+
const workspaceRelativePath = await workspaceService.getWorkspaceRelativePath(absoluteUri);
|
|
81
|
+
return workspaceRelativePath || '';
|
|
82
|
+
} catch {
|
|
83
|
+
return '';
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
const handleOpenDirectory = async () => {
|
|
88
|
+
try {
|
|
89
|
+
const uri = new URI(input.path);
|
|
90
|
+
// Note: This might need to be adjusted based on how directories are opened in Theia
|
|
91
|
+
await editorManager.open(uri);
|
|
92
|
+
} catch (error) {
|
|
93
|
+
console.error('Failed to open directory:', error);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
const [relativePath, setRelativePath] = React.useState<string>('');
|
|
98
|
+
|
|
99
|
+
React.useEffect(() => {
|
|
100
|
+
getWorkspaceRelativePath(input.path).then(setRelativePath);
|
|
101
|
+
}, [input.path]);
|
|
102
|
+
|
|
103
|
+
const compactHeader = (
|
|
104
|
+
<>
|
|
105
|
+
<div className="claude-code-tool header-left">
|
|
106
|
+
<span className="claude-code-tool title">Listing</span>
|
|
107
|
+
<span className={`${codicon('checklist')} claude-code-tool icon`} />
|
|
108
|
+
<span
|
|
109
|
+
className="claude-code-tool file-name clickable-element"
|
|
110
|
+
onClick={handleOpenDirectory}
|
|
111
|
+
title="Click to open directory"
|
|
112
|
+
>
|
|
113
|
+
{getDirectoryName(input.path)}
|
|
114
|
+
</span>
|
|
115
|
+
{relativePath && <span className="claude-code-tool relative-path">{relativePath}</span>}
|
|
116
|
+
</div>
|
|
117
|
+
<div className="claude-code-tool header-right">
|
|
118
|
+
{input.ignore && input.ignore.length > 0 && (
|
|
119
|
+
<span className="claude-code-tool badge">Ignoring {input.ignore.length} patterns</span>
|
|
120
|
+
)}
|
|
121
|
+
</div>
|
|
122
|
+
</>
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
const expandedContent = (
|
|
126
|
+
<div className="claude-code-tool details">
|
|
127
|
+
<div className="claude-code-tool detail-row">
|
|
128
|
+
<span className="claude-code-tool detail-label">Directory</span>
|
|
129
|
+
<code className="claude-code-tool detail-value">{input.path}</code>
|
|
130
|
+
</div>
|
|
131
|
+
{input.ignore && input.ignore.length > 0 && (
|
|
132
|
+
<div className="claude-code-tool detail-row">
|
|
133
|
+
<span className="claude-code-tool detail-label">Ignored Patterns</span>
|
|
134
|
+
<div className="claude-code-tool detail-value">
|
|
135
|
+
{input.ignore.map((pattern, index) => (
|
|
136
|
+
<code key={index} className="claude-code-tool ignore-pattern">
|
|
137
|
+
{pattern}{index < input.ignore!.length - 1 ? ', ' : ''}
|
|
138
|
+
</code>
|
|
139
|
+
))}
|
|
140
|
+
</div>
|
|
141
|
+
</div>
|
|
142
|
+
)}
|
|
143
|
+
<div className="claude-code-tool detail-row">
|
|
144
|
+
<span className="claude-code-tool detail-label">Description</span>
|
|
145
|
+
<span className="claude-code-tool detail-value">
|
|
146
|
+
List directory contents{input.ignore && input.ignore.length > 0
|
|
147
|
+
? ` (excluding ${input.ignore.length} pattern${input.ignore.length > 1 ? 's' : ''})`
|
|
148
|
+
: ''}
|
|
149
|
+
</span>
|
|
150
|
+
</div>
|
|
151
|
+
</div>
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
return (
|
|
155
|
+
<CollapsibleToolRenderer
|
|
156
|
+
compactHeader={compactHeader}
|
|
157
|
+
expandedContent={expandedContent}
|
|
158
|
+
/>
|
|
159
|
+
);
|
|
160
|
+
};
|