@theia/ai-ide 1.66.0-next.44 → 1.66.0-next.73
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/ai-configuration/agent-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/agent-configuration-widget.js +5 -3
- package/lib/browser/ai-configuration/agent-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/language-model-renderer.d.ts.map +1 -1
- package/lib/browser/ai-configuration/language-model-renderer.js +4 -3
- package/lib/browser/ai-configuration/language-model-renderer.js.map +1 -1
- package/lib/browser/ai-configuration/model-aliases-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/model-aliases-configuration-widget.js +4 -3
- package/lib/browser/ai-configuration/model-aliases-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/template-settings-renderer.js +1 -1
- package/lib/browser/ai-configuration/template-settings-renderer.js.map +1 -1
- package/lib/browser/ai-configuration/token-usage-configuration-widget.js +1 -1
- package/lib/browser/ai-configuration/token-usage-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/tools-configuration-widget.d.ts +1 -1
- package/lib/browser/ai-configuration/tools-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/tools-configuration-widget.js +12 -12
- package/lib/browser/ai-configuration/tools-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/variable-configuration-widget.d.ts +1 -1
- package/lib/browser/ai-configuration/variable-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/variable-configuration-widget.js +4 -3
- package/lib/browser/ai-configuration/variable-configuration-widget.js.map +1 -1
- package/lib/browser/app-tester-chat-agent.d.ts.map +1 -1
- package/lib/browser/app-tester-chat-agent.js +10 -7
- package/lib/browser/app-tester-chat-agent.js.map +1 -1
- package/lib/browser/architect-agent.d.ts.map +1 -1
- package/lib/browser/architect-agent.js +2 -2
- package/lib/browser/architect-agent.js.map +1 -1
- package/lib/browser/coder-agent.d.ts.map +1 -1
- package/lib/browser/coder-agent.js +6 -5
- package/lib/browser/coder-agent.js.map +1 -1
- package/lib/browser/file-changeset-functions.d.ts +10 -0
- package/lib/browser/file-changeset-functions.d.ts.map +1 -1
- package/lib/browser/file-changeset-functions.js +51 -3
- package/lib/browser/file-changeset-functions.js.map +1 -1
- package/lib/browser/file-changeset-functions.spec.js +18 -0
- package/lib/browser/file-changeset-functions.spec.js.map +1 -1
- package/lib/browser/frontend-module.d.ts.map +1 -1
- package/lib/browser/frontend-module.js +7 -0
- package/lib/browser/frontend-module.js.map +1 -1
- package/lib/browser/github-chat-agent.d.ts.map +1 -1
- package/lib/browser/github-chat-agent.js +18 -16
- package/lib/browser/github-chat-agent.js.map +1 -1
- package/lib/browser/github-repo-variable-contribution.d.ts +8 -10
- package/lib/browser/github-repo-variable-contribution.d.ts.map +1 -1
- package/lib/browser/github-repo-variable-contribution.js +22 -41
- package/lib/browser/github-repo-variable-contribution.js.map +1 -1
- package/lib/common/coder-replace-prompt-template.d.ts +2 -0
- package/lib/common/coder-replace-prompt-template.d.ts.map +1 -1
- package/lib/common/coder-replace-prompt-template.js +20 -8
- package/lib/common/coder-replace-prompt-template.js.map +1 -1
- package/lib/common/command-chat-agents.d.ts.map +1 -1
- package/lib/common/command-chat-agents.js +9 -9
- package/lib/common/command-chat-agents.js.map +1 -1
- package/lib/common/file-changeset-function-ids.d.ts +1 -0
- package/lib/common/file-changeset-function-ids.d.ts.map +1 -1
- package/lib/common/file-changeset-function-ids.js +2 -1
- package/lib/common/file-changeset-function-ids.js.map +1 -1
- package/lib/common/github-repo-protocol.d.ts +15 -0
- package/lib/common/github-repo-protocol.d.ts.map +1 -0
- package/lib/common/github-repo-protocol.js +21 -0
- package/lib/common/github-repo-protocol.js.map +1 -0
- package/lib/common/orchestrator-chat-agent.d.ts.map +1 -1
- package/lib/common/orchestrator-chat-agent.js +4 -4
- package/lib/common/orchestrator-chat-agent.js.map +1 -1
- package/lib/common/workspace-preferences.js +2 -2
- package/lib/common/workspace-preferences.js.map +1 -1
- package/lib/node/backend-module.d.ts.map +1 -1
- package/lib/node/backend-module.js +4 -0
- package/lib/node/backend-module.js.map +1 -1
- package/lib/node/github-repo-service-impl.d.ts +7 -0
- package/lib/node/github-repo-service-impl.d.ts.map +1 -0
- package/lib/node/github-repo-service-impl.js +86 -0
- package/lib/node/github-repo-service-impl.js.map +1 -0
- package/package.json +23 -23
- package/src/browser/ai-configuration/agent-configuration-widget.tsx +5 -3
- package/src/browser/ai-configuration/language-model-renderer.tsx +9 -4
- package/src/browser/ai-configuration/model-aliases-configuration-widget.tsx +8 -4
- package/src/browser/ai-configuration/template-settings-renderer.tsx +1 -1
- package/src/browser/ai-configuration/token-usage-configuration-widget.tsx +1 -1
- package/src/browser/ai-configuration/tools-configuration-widget.tsx +15 -14
- package/src/browser/ai-configuration/variable-configuration-widget.tsx +4 -3
- package/src/browser/app-tester-chat-agent.ts +15 -7
- package/src/browser/architect-agent.ts +4 -2
- package/src/browser/coder-agent.ts +10 -6
- package/src/browser/file-changeset-functions.spec.ts +28 -1
- package/src/browser/file-changeset-functions.ts +47 -3
- package/src/browser/frontend-module.ts +11 -1
- package/src/browser/github-chat-agent.ts +23 -17
- package/src/browser/github-repo-variable-contribution.ts +23 -50
- package/src/common/coder-replace-prompt-template.ts +23 -8
- package/src/common/command-chat-agents.ts +13 -9
- package/src/common/file-changeset-function-ids.ts +1 -0
- package/src/common/github-repo-protocol.ts +32 -0
- package/src/common/orchestrator-chat-agent.ts +5 -4
- package/src/common/workspace-preferences.ts +2 -2
- package/src/node/backend-module.ts +7 -0
- package/src/node/github-repo-service-impl.ts +98 -0
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// *****************************************************************************
|
|
3
|
+
// Copyright (C) 2025 EclipseSource GmbH.
|
|
4
|
+
//
|
|
5
|
+
// This program and the accompanying materials are made available under the
|
|
6
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
7
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
8
|
+
//
|
|
9
|
+
// This Source Code may also be made available under the following Secondary
|
|
10
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
11
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
12
|
+
// with the GNU Classpath Exception which is available at
|
|
13
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
14
|
+
//
|
|
15
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
16
|
+
// *****************************************************************************
|
|
17
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.GitHubRepoServiceImpl = void 0;
|
|
19
|
+
const tslib_1 = require("tslib");
|
|
20
|
+
const inversify_1 = require("@theia/core/shared/inversify");
|
|
21
|
+
const simple_git_1 = require("simple-git");
|
|
22
|
+
let GitHubRepoServiceImpl = class GitHubRepoServiceImpl {
|
|
23
|
+
async getGitHubRepoInfo(workspacePath) {
|
|
24
|
+
try {
|
|
25
|
+
// Initialize simple-git with the workspace path
|
|
26
|
+
const git = (0, simple_git_1.simpleGit)(workspacePath);
|
|
27
|
+
// Check if this is a git repository
|
|
28
|
+
const isRepo = await git.checkIsRepo();
|
|
29
|
+
if (!isRepo) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
// Get all remotes with their URLs
|
|
33
|
+
const remotes = await git.getRemotes(true);
|
|
34
|
+
if (remotes.length === 0) {
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
37
|
+
// Find GitHub remote (prefer 'origin', then any GitHub remote)
|
|
38
|
+
const githubRemote = remotes.find(remote => remote.name === 'origin' && this.isGitHubRemote(remote.refs.fetch || remote.refs.push || '')) || remotes.find(remote => this.isGitHubRemote(remote.refs.fetch || remote.refs.push || ''));
|
|
39
|
+
if (!githubRemote) {
|
|
40
|
+
return undefined;
|
|
41
|
+
}
|
|
42
|
+
const remoteUrl = githubRemote.refs.fetch || githubRemote.refs.push || '';
|
|
43
|
+
const repoInfo = this.extractRepoInfoFromGitHubUrl(remoteUrl);
|
|
44
|
+
return repoInfo;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
console.warn('Failed to get GitHub repository info:', error);
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
isGitHubRemote(remoteUrl) {
|
|
52
|
+
return remoteUrl.includes('github.com');
|
|
53
|
+
}
|
|
54
|
+
extractRepoInfoFromGitHubUrl(url) {
|
|
55
|
+
// Handle HTTPS URLs: https://github.com/owner/repo or https://github.com/owner/repo.git
|
|
56
|
+
const httpsMatch = url.match(/https:\/\/github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?$/);
|
|
57
|
+
if (httpsMatch) {
|
|
58
|
+
return {
|
|
59
|
+
owner: httpsMatch[1],
|
|
60
|
+
repo: httpsMatch[2]
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// Handle SSH URLs: git@github.com:owner/repo or git@github.com:owner/repo.git
|
|
64
|
+
const sshMatch = url.match(/git@github\.com:([^\/]+)\/([^\/]+?)(?:\.git)?$/);
|
|
65
|
+
if (sshMatch) {
|
|
66
|
+
return {
|
|
67
|
+
owner: sshMatch[1],
|
|
68
|
+
repo: sshMatch[2]
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
// Handle alternative SSH format: ssh://git@github.com/owner/repo or ssh://git@github.com/owner/repo.git
|
|
72
|
+
const sshAltMatch = url.match(/ssh:\/\/git@github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?$/);
|
|
73
|
+
if (sshAltMatch) {
|
|
74
|
+
return {
|
|
75
|
+
owner: sshAltMatch[1],
|
|
76
|
+
repo: sshAltMatch[2]
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return undefined;
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
exports.GitHubRepoServiceImpl = GitHubRepoServiceImpl;
|
|
83
|
+
exports.GitHubRepoServiceImpl = GitHubRepoServiceImpl = tslib_1.__decorate([
|
|
84
|
+
(0, inversify_1.injectable)()
|
|
85
|
+
], GitHubRepoServiceImpl);
|
|
86
|
+
//# sourceMappingURL=github-repo-service-impl.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"github-repo-service-impl.js","sourceRoot":"","sources":["../../src/node/github-repo-service-impl.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@theia/ai-ide",
|
|
3
|
-
"version": "1.66.0-next.
|
|
3
|
+
"version": "1.66.0-next.73+6d82794da",
|
|
4
4
|
"description": "AI IDE Agents Extension",
|
|
5
5
|
"license": "EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -15,36 +15,36 @@
|
|
|
15
15
|
"theia-extension"
|
|
16
16
|
],
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@theia/ai-chat": "1.66.0-next.
|
|
19
|
-
"@theia/ai-chat-ui": "1.66.0-next.
|
|
20
|
-
"@theia/ai-core": "1.66.0-next.
|
|
21
|
-
"@theia/ai-mcp": "1.66.0-next.
|
|
22
|
-
"@theia/core": "1.66.0-next.
|
|
23
|
-
"@theia/debug": "1.66.0-next.
|
|
24
|
-
"@theia/editor": "1.66.0-next.
|
|
25
|
-
"@theia/filesystem": "1.66.0-next.
|
|
26
|
-
"@theia/
|
|
27
|
-
"@theia/
|
|
28
|
-
"@theia/
|
|
29
|
-
"@theia/
|
|
30
|
-
"@theia/
|
|
31
|
-
"@theia/
|
|
32
|
-
"@theia/
|
|
33
|
-
"@theia/
|
|
34
|
-
"@theia/
|
|
35
|
-
"@theia/workspace": "1.66.0-next.44+5b6e49d13",
|
|
18
|
+
"@theia/ai-chat": "1.66.0-next.73+6d82794da",
|
|
19
|
+
"@theia/ai-chat-ui": "1.66.0-next.73+6d82794da",
|
|
20
|
+
"@theia/ai-core": "1.66.0-next.73+6d82794da",
|
|
21
|
+
"@theia/ai-mcp": "1.66.0-next.73+6d82794da",
|
|
22
|
+
"@theia/core": "1.66.0-next.73+6d82794da",
|
|
23
|
+
"@theia/debug": "1.66.0-next.73+6d82794da",
|
|
24
|
+
"@theia/editor": "1.66.0-next.73+6d82794da",
|
|
25
|
+
"@theia/filesystem": "1.66.0-next.73+6d82794da",
|
|
26
|
+
"@theia/markers": "1.66.0-next.73+6d82794da",
|
|
27
|
+
"@theia/monaco": "1.66.0-next.73+6d82794da",
|
|
28
|
+
"@theia/navigator": "1.66.0-next.73+6d82794da",
|
|
29
|
+
"@theia/preferences": "1.66.0-next.73+6d82794da",
|
|
30
|
+
"@theia/scm": "1.66.0-next.73+6d82794da",
|
|
31
|
+
"@theia/search-in-workspace": "1.66.0-next.73+6d82794da",
|
|
32
|
+
"@theia/task": "1.66.0-next.73+6d82794da",
|
|
33
|
+
"@theia/terminal": "1.66.0-next.73+6d82794da",
|
|
34
|
+
"@theia/workspace": "1.66.0-next.73+6d82794da",
|
|
36
35
|
"date-fns": "^4.1.0",
|
|
37
36
|
"ignore": "^6.0.0",
|
|
38
37
|
"js-yaml": "^4.1.0",
|
|
39
38
|
"minimatch": "^10.0.3",
|
|
40
|
-
"puppeteer-core": "^24.10.0"
|
|
39
|
+
"puppeteer-core": "^24.10.0",
|
|
40
|
+
"simple-git": "^3.25.0"
|
|
41
41
|
},
|
|
42
42
|
"publishConfig": {
|
|
43
43
|
"access": "public"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
|
-
"@theia/cli": "1.66.0-next.
|
|
47
|
-
"@theia/test": "1.66.0-next.
|
|
46
|
+
"@theia/cli": "1.66.0-next.73+6d82794da",
|
|
47
|
+
"@theia/test": "1.66.0-next.73+6d82794da"
|
|
48
48
|
},
|
|
49
49
|
"theiaExtensions": [
|
|
50
50
|
{
|
|
@@ -68,5 +68,5 @@
|
|
|
68
68
|
"nyc": {
|
|
69
69
|
"extends": "../../configs/nyc.json"
|
|
70
70
|
},
|
|
71
|
-
"gitHead": "
|
|
71
|
+
"gitHead": "6d82794da24e16f75d70eaac3a4178a29e822e82"
|
|
72
72
|
}
|
|
@@ -320,12 +320,14 @@ export class AIAgentConfigurationWidget extends ReactWidget {
|
|
|
320
320
|
|
|
321
321
|
// Multiple locations - show quick picker
|
|
322
322
|
const quickPick = this.quickInputService.createQuickPick();
|
|
323
|
-
quickPick.title = 'Select Location for Custom Agents File';
|
|
324
|
-
quickPick.placeholder = 'Choose where to create or open a custom agents file';
|
|
323
|
+
quickPick.title = nls.localize('theia/ai/ide/agentConfiguration/customAgentLocationQuickPick/title', 'Select Location for Custom Agents File');
|
|
324
|
+
quickPick.placeholder = nls.localize('theia/ai/ide/agentConfiguration/customAgentLocationQuickPick/placeholder', 'Choose where to create or open a custom agents file');
|
|
325
325
|
|
|
326
326
|
quickPick.items = locations.map(location => ({
|
|
327
327
|
label: location.uri.path.toString(),
|
|
328
|
-
description: location.exists
|
|
328
|
+
description: location.exists
|
|
329
|
+
? nls.localize('theia/ai/ide/agentConfiguration/customAgentLocationQuickPick/openExistingFile', 'Open existing file')
|
|
330
|
+
: nls.localize('theia/ai/ide/agentConfiguration/customAgentLocationQuickPick/createNewFile', 'Create new file'),
|
|
329
331
|
location
|
|
330
332
|
}));
|
|
331
333
|
|
|
@@ -103,7 +103,9 @@ export const LanguageModelRenderer: React.FC<LanguageModelSettingsProps> = (
|
|
|
103
103
|
<option value=""></option>
|
|
104
104
|
{/* Aliases first, then languange models */}
|
|
105
105
|
{aliases?.sort((a, b) => a.id.localeCompare(b.id)).map(alias => (
|
|
106
|
-
<option key={`alias/${alias.id}`} value={alias.id} className='ai-language-model-item-ready'>
|
|
106
|
+
<option key={`alias/${alias.id}`} value={alias.id} className='ai-language-model-item-ready'>
|
|
107
|
+
{nls.localize('theia/ai/core/languageModelRenderer/alias', '[alias] {0}', alias.id)}
|
|
108
|
+
</option>
|
|
107
109
|
))}
|
|
108
110
|
{languageModels?.sort((a, b) => (a.name ?? a.id).localeCompare(b.name ?? b.id)).map(model => {
|
|
109
111
|
const isNotReady = model.status.status !== 'ready';
|
|
@@ -128,15 +130,18 @@ export const LanguageModelRenderer: React.FC<LanguageModelSettingsProps> = (
|
|
|
128
130
|
<span className="ai-alias-evaluates-to-value">
|
|
129
131
|
{resolvedModel.name ?? resolvedModel.id}
|
|
130
132
|
{resolvedModel.status.status === 'ready' ? (
|
|
131
|
-
<span className="ai-model-status-ready"
|
|
133
|
+
<span className="ai-model-status-ready"
|
|
134
|
+
title={nls.localize('theia/ai/core/modelAliasesConfiguration/modelReadyTooltip', 'Ready')}>✓</span>
|
|
132
135
|
) : (
|
|
133
|
-
<span className="ai-model-status-not-ready" title={resolvedModel.status.message
|
|
136
|
+
<span className="ai-model-status-not-ready" title={resolvedModel.status.message
|
|
137
|
+
|| nls.localize('theia/ai/core/modelAliasesConfiguration/modelNotReadyTooltip', 'Not ready')}>✗</span>
|
|
134
138
|
)}
|
|
135
139
|
</span>
|
|
136
140
|
) : (
|
|
137
141
|
<span className="ai-alias-evaluates-to-unresolved">
|
|
138
142
|
{nls.localize('theia/ai/core/modelAliasesConfiguration/noResolvedModel', 'No model ready for this alias.')}
|
|
139
|
-
<span className="ai-model-status-not-ready"
|
|
143
|
+
<span className="ai-model-status-not-ready"
|
|
144
|
+
title={nls.localize('theia/ai/core/modelAliasesConfiguration/noModelReadyTooltip', 'No model ready')}>✗</span>
|
|
140
145
|
</span>
|
|
141
146
|
)}
|
|
142
147
|
</div>
|
|
@@ -229,11 +229,13 @@ export class ModelAliasesConfigurationWidget extends ReactWidget {
|
|
|
229
229
|
<li key={modelId}>
|
|
230
230
|
{isReady ? (
|
|
231
231
|
<span className={modelId === resolvedModel?.id ? 'ai-alias-priority-item-resolved' : 'ai-alias-priority-item-ready'}>
|
|
232
|
-
{modelId} <span className="ai-model-status-ready"
|
|
232
|
+
{modelId} <span className="ai-model-status-ready"
|
|
233
|
+
title={nls.localize('theia/ai/core/modelAliasesConfiguration/modelReadyTooltip', 'Ready')}>✓</span>
|
|
233
234
|
</span>
|
|
234
235
|
) : (
|
|
235
236
|
<span className="ai-model-default-not-ready">
|
|
236
|
-
{modelId} <span className="ai-model-status-not-ready"
|
|
237
|
+
{modelId} <span className="ai-model-status-not-ready"
|
|
238
|
+
title={nls.localize('theia/ai/core/modelAliasesConfiguration/modelNotReadyTooltip', 'Not ready')}>✗</span>
|
|
237
239
|
</span>
|
|
238
240
|
)}
|
|
239
241
|
</li>
|
|
@@ -246,9 +248,11 @@ export class ModelAliasesConfigurationWidget extends ReactWidget {
|
|
|
246
248
|
<span className="ai-alias-evaluates-to-value">
|
|
247
249
|
{resolvedModel.name ?? resolvedModel.id}
|
|
248
250
|
{resolvedModel.status.status === 'ready' ? (
|
|
249
|
-
<span className="ai-model-status-ready"
|
|
251
|
+
<span className="ai-model-status-ready"
|
|
252
|
+
title={nls.localize('theia/ai/core/modelAliasesConfiguration/modelReadyTooltip', 'Ready')}>✓</span>
|
|
250
253
|
) : (
|
|
251
|
-
<span className="ai-model-status-not-ready" title={resolvedModel.status.message
|
|
254
|
+
<span className="ai-model-status-not-ready" title={resolvedModel.status.message
|
|
255
|
+
|| nls.localize('theia/ai/core/modelAliasesConfiguration/modelNotReadyTooltip', 'Not ready')}>✗</span>
|
|
252
256
|
)}
|
|
253
257
|
</span>
|
|
254
258
|
) : (
|
|
@@ -86,7 +86,7 @@ export const PromptVariantRenderer: React.FC<PromptVariantRendererProps> = ({
|
|
|
86
86
|
)}
|
|
87
87
|
{variantIds.map(variantId => (
|
|
88
88
|
<option key={variantId} value={variantId}>
|
|
89
|
-
{variantId === defaultVariantId ? variantId + ' (default)' : variantId}
|
|
89
|
+
{variantId === defaultVariantId ? variantId + ' ' + nls.localizeByDefault('(default)') : variantId}
|
|
90
90
|
</option>
|
|
91
91
|
))}
|
|
92
92
|
</select>
|
|
@@ -57,7 +57,7 @@ export class AITokenUsageConfigurationWidget extends ReactWidget {
|
|
|
57
57
|
this.tokenUsageData = await this.tokenUsageService.getTokenUsageData();
|
|
58
58
|
this.update();
|
|
59
59
|
} catch (error) {
|
|
60
|
-
this.messageService.error(
|
|
60
|
+
this.messageService.error(nls.localize('theia/ai/tokenUsage/failedToGetTokenUsageData', 'Failed to fetch token usage data: {0}', error));
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
|
|
@@ -18,20 +18,20 @@ import { ReactWidget, ConfirmDialog } from '@theia/core/lib/browser';
|
|
|
18
18
|
import { inject, injectable, postConstruct } from '@theia/core/shared/inversify';
|
|
19
19
|
import * as React from '@theia/core/shared/react';
|
|
20
20
|
import { ToolInvocationRegistry } from '@theia/ai-core';
|
|
21
|
-
import { PreferenceService } from '@theia/core';
|
|
21
|
+
import { nls, PreferenceService } from '@theia/core';
|
|
22
22
|
import { ToolConfirmationManager } from '@theia/ai-chat/lib/browser/chat-tool-preference-bindings';
|
|
23
23
|
import { ToolConfirmationMode } from '@theia/ai-chat/lib/common/chat-tool-preferences';
|
|
24
24
|
|
|
25
25
|
const TOOL_OPTIONS: { value: ToolConfirmationMode, label: string, icon: string }[] = [
|
|
26
|
-
{ value: ToolConfirmationMode.DISABLED, label: 'Disabled', icon: 'close' },
|
|
27
|
-
{ value: ToolConfirmationMode.CONFIRM, label: 'Confirm', icon: 'question' },
|
|
28
|
-
{ value: ToolConfirmationMode.ALWAYS_ALLOW, label: 'Always Allow', icon: 'thumbsup' },
|
|
26
|
+
{ value: ToolConfirmationMode.DISABLED, label: nls.localizeByDefault('Disabled'), icon: 'close' },
|
|
27
|
+
{ value: ToolConfirmationMode.CONFIRM, label: nls.localize('theia/ai/ide/toolsConfiguration/toolOptions/confirm/label', 'Confirm'), icon: 'question' },
|
|
28
|
+
{ value: ToolConfirmationMode.ALWAYS_ALLOW, label: nls.localize('theia/ai/ide/toolsConfiguration/toolOptions/alwaysAllow/label', 'Always Allow'), icon: 'thumbsup' },
|
|
29
29
|
];
|
|
30
30
|
|
|
31
31
|
@injectable()
|
|
32
32
|
export class AIToolsConfigurationWidget extends ReactWidget {
|
|
33
33
|
static readonly ID = 'ai-tools-configuration-widget';
|
|
34
|
-
static readonly LABEL = 'Tools';
|
|
34
|
+
static readonly LABEL = nls.localize('theia/ai/ide/toolsConfiguration/label', 'Tools');
|
|
35
35
|
|
|
36
36
|
@inject(ToolConfirmationManager)
|
|
37
37
|
protected readonly confirmationManager: ToolConfirmationManager;
|
|
@@ -106,10 +106,11 @@ export class AIToolsConfigurationWidget extends ReactWidget {
|
|
|
106
106
|
|
|
107
107
|
protected async resetAllToolsToDefault(): Promise<void> {
|
|
108
108
|
const dialog = new ConfirmDialog({
|
|
109
|
-
title: 'Reset All Tool Confirmation Modes',
|
|
110
|
-
msg:
|
|
111
|
-
|
|
112
|
-
|
|
109
|
+
title: nls.localize('theia/ai/ide/toolsConfiguration/resetAllConfirmDialog/title', 'Reset All Tool Confirmation Modes'),
|
|
110
|
+
msg: nls.localize('theia/ai/ide/toolsConfiguration/resetAllConfirmDialog/msg',
|
|
111
|
+
'Are you sure you want to reset all tool confirmation modes to the default? This will remove all custom settings.'),
|
|
112
|
+
ok: nls.localize('theia/ai/ide/toolsConfiguration/resetAll', 'Reset All'),
|
|
113
|
+
cancel: nls.localizeByDefault('Cancel')
|
|
113
114
|
});
|
|
114
115
|
const shouldReset = await dialog.open();
|
|
115
116
|
if (shouldReset) {
|
|
@@ -119,11 +120,11 @@ export class AIToolsConfigurationWidget extends ReactWidget {
|
|
|
119
120
|
|
|
120
121
|
protected render(): React.ReactNode {
|
|
121
122
|
if (this.loading) {
|
|
122
|
-
return <div>Loading tools
|
|
123
|
+
return <div>{nls.localize('theia/ai/ide/toolsConfiguration/loading', 'Loading tools...')}</div>;
|
|
123
124
|
}
|
|
124
125
|
return <div className='ai-tools-configuration-container'>
|
|
125
126
|
<div className='ai-tools-configuration-default-section' style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
|
|
126
|
-
<div className='ai-tools-configuration-default-label'>Default Tool Confirmation Mode
|
|
127
|
+
<div className='ai-tools-configuration-default-label'>{nls.localize('theia/ai/ide/toolsConfiguration/default/label', 'Default Tool Confirmation Mode:')}</div>
|
|
127
128
|
<select
|
|
128
129
|
className="ai-tools-configuration-default-select"
|
|
129
130
|
value={this.defaultState}
|
|
@@ -137,14 +138,14 @@ export class AIToolsConfigurationWidget extends ReactWidget {
|
|
|
137
138
|
<button
|
|
138
139
|
className='ai-tools-configuration-reset-btn'
|
|
139
140
|
style={{ marginLeft: 'auto' }}
|
|
140
|
-
title='Reset all tools to default'
|
|
141
|
+
title={nls.localize('theia/ai/ide/toolsConfiguration/resetAllTooltip', 'Reset all tools to default')}
|
|
141
142
|
onClick={() => this.resetAllToolsToDefault()}
|
|
142
143
|
>
|
|
143
|
-
Reset All
|
|
144
|
+
{nls.localize('theia/ai/ide/toolsConfiguration/resetAll', 'Reset All')}
|
|
144
145
|
</button>
|
|
145
146
|
</div>
|
|
146
147
|
<div className='ai-tools-configuration-tools-section'>
|
|
147
|
-
<div className='ai-tools-configuration-tools-label'>Tools</div>
|
|
148
|
+
<div className='ai-tools-configuration-tools-label'>{nls.localize('theia/ai/ide/toolsConfiguration/tools/label', 'Tools')}</div>
|
|
148
149
|
<ul className='ai-tools-configuration-tools-list'>
|
|
149
150
|
{this.tools.map(tool => {
|
|
150
151
|
const state = this.toolConfirmationModes[tool] || this.defaultState;
|
|
@@ -20,12 +20,13 @@ import { inject, injectable, postConstruct } from '@theia/core/shared/inversify'
|
|
|
20
20
|
import * as React from '@theia/core/shared/react';
|
|
21
21
|
import { AIAgentConfigurationWidget } from './agent-configuration-widget';
|
|
22
22
|
import { AIConfigurationSelectionService } from './ai-configuration-service';
|
|
23
|
+
import { nls } from '@theia/core';
|
|
23
24
|
|
|
24
25
|
@injectable()
|
|
25
26
|
export class AIVariableConfigurationWidget extends ReactWidget {
|
|
26
27
|
|
|
27
28
|
static readonly ID = 'ai-variable-configuration-container-widget';
|
|
28
|
-
static readonly LABEL = 'Variables';
|
|
29
|
+
static readonly LABEL = nls.localizeByDefault('Variables');
|
|
29
30
|
|
|
30
31
|
@inject(AIVariableService)
|
|
31
32
|
protected readonly variableService: AIVariableService;
|
|
@@ -68,7 +69,7 @@ export class AIVariableConfigurationWidget extends ReactWidget {
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
return <div>
|
|
71
|
-
<h3>Agents</h3>
|
|
72
|
+
<h3>{nls.localize('theia/ai/ide/variableConfiguration/agents', 'Agents')}</h3>
|
|
72
73
|
<ul className='variable-references'>
|
|
73
74
|
{agents.map(agent => <li key={agent.id} className='theia-TreeNode theia-CompositeTreeNode theia-ExpandableTreeNode theia-mod-selected'>
|
|
74
75
|
<div onClick={() => { this.showAgentConfiguration(agent); }} className='variable-reference'>
|
|
@@ -85,7 +86,7 @@ export class AIVariableConfigurationWidget extends ReactWidget {
|
|
|
85
86
|
}
|
|
86
87
|
|
|
87
88
|
return <div className='variable-args-container'>
|
|
88
|
-
<h3>Variable Arguments</h3>
|
|
89
|
+
<h3>{nls.localize('theia/ai/ide/variableConfiguration/variableArgs', 'Variable Arguments')}</h3>
|
|
89
90
|
<div className='variable-args'>
|
|
90
91
|
{variable.args.map(arg =>
|
|
91
92
|
<React.Fragment key={arg.name}>
|
|
@@ -58,16 +58,20 @@ export class AppTesterChatAgent extends AbstractStreamParsingChatAgent {
|
|
|
58
58
|
if (await this.requiresStartingServers()) {
|
|
59
59
|
// Ask the user if they want to start the server
|
|
60
60
|
request.response.response.addContent(new QuestionResponseContentImpl(
|
|
61
|
-
|
|
61
|
+
nls.localize('theia/ai/ide/app-tester/startPlaywrightServers/question',
|
|
62
|
+
'The Playwright MCP servers are not running. Would you like to start them now? This may install the Playwright MCP servers.'),
|
|
62
63
|
[
|
|
63
|
-
{ text: 'Yes, start the servers', value: 'yes' },
|
|
64
|
-
{ text: 'No, cancel', value: 'no' }
|
|
64
|
+
{ text: nls.localize('theia/ai/ide/app-tester/startPlaywrightServers/yes', 'Yes, start the servers'), value: 'yes' },
|
|
65
|
+
{ text: nls.localize('theia/ai/ide/app-tester/startPlaywrightServers/no', 'No, cancel'), value: 'no' }
|
|
65
66
|
],
|
|
66
67
|
request,
|
|
67
68
|
async selectedOption => {
|
|
68
69
|
if (selectedOption.value === 'yes') {
|
|
69
70
|
// Show progress
|
|
70
|
-
const progress = request.response.addProgressMessage({
|
|
71
|
+
const progress = request.response.addProgressMessage({
|
|
72
|
+
content: nls.localize('theia/ai/ide/app-tester/startPlaywrightServers/progress', 'Starting Playwright MCP servers.'),
|
|
73
|
+
show: 'whileIncomplete'
|
|
74
|
+
});
|
|
71
75
|
try {
|
|
72
76
|
await this.startServers();
|
|
73
77
|
// Remove progress, continue with normal flow
|
|
@@ -75,13 +79,16 @@ export class AppTesterChatAgent extends AbstractStreamParsingChatAgent {
|
|
|
75
79
|
await super.invoke(request);
|
|
76
80
|
} catch (error) {
|
|
77
81
|
request.response.response.addContent(new ErrorChatResponseContentImpl(
|
|
78
|
-
new Error('Failed to start Playwright MCP server: '
|
|
82
|
+
new Error(nls.localize('theia/ai/ide/app-tester/startPlaywrightServers/error', 'Failed to start Playwright MCP server: {0}',
|
|
83
|
+
error instanceof Error ? error.message : String(error)))
|
|
79
84
|
));
|
|
80
85
|
request.response.complete();
|
|
81
86
|
}
|
|
82
87
|
} else {
|
|
83
88
|
// Continue without starting the server
|
|
84
|
-
request.response.response.addContent(new MarkdownChatResponseContentImpl(
|
|
89
|
+
request.response.response.addContent(new MarkdownChatResponseContentImpl(
|
|
90
|
+
nls.localize('theia/ai/ide/app-tester/startPlaywrightServers/canceled', 'Please setup the MCP servers.')
|
|
91
|
+
));
|
|
85
92
|
request.response.complete();
|
|
86
93
|
}
|
|
87
94
|
}
|
|
@@ -93,7 +100,8 @@ export class AppTesterChatAgent extends AbstractStreamParsingChatAgent {
|
|
|
93
100
|
await super.invoke(request);
|
|
94
101
|
} catch (error) {
|
|
95
102
|
request.response.response.addContent(new ErrorChatResponseContentImpl(
|
|
96
|
-
new Error('Error checking Playwright MCP server status: '
|
|
103
|
+
new Error(nls.localize('theia/ai/ide/app-tester/errorCheckingPlaywrightServerStatus', 'Error checking Playwright MCP server status: {0}',
|
|
104
|
+
error instanceof Error ? error.message : String(error)))
|
|
97
105
|
));
|
|
98
106
|
request.response.complete();
|
|
99
107
|
}
|
|
@@ -51,8 +51,10 @@ export class ArchitectAgent extends AbstractStreamParsingChatAgent {
|
|
|
51
51
|
if (!(model instanceof MutableChatModel) || !session) { return; }
|
|
52
52
|
if (!model.isEmpty()) {
|
|
53
53
|
model.setSuggestions([
|
|
54
|
-
new MarkdownStringImpl(`[
|
|
55
|
-
|
|
54
|
+
new MarkdownStringImpl(`[${nls.localize('theia/ai/ide/architectAgent/suggestion/summarizeSessionAsTaskForCoder',
|
|
55
|
+
'Summarize this session as a task for Coder')}](command:${AI_SUMMARIZE_SESSION_AS_TASK_FOR_CODER.id}).`),
|
|
56
|
+
new MarkdownStringImpl(`[${nls.localize('theia/ai/ide/architectAgent/suggestion/updateTaskContext',
|
|
57
|
+
'Update current task context')}](command:${AI_UPDATE_TASK_CONTEXT_COMMAND.id}).`)
|
|
56
58
|
]);
|
|
57
59
|
}
|
|
58
60
|
}
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
// *****************************************************************************
|
|
16
16
|
import { AbstractStreamParsingChatAgent, ChatRequestModel, ChatService, ChatSession, MutableChatModel, MutableChatRequestModel } from '@theia/ai-chat/lib/common';
|
|
17
17
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
18
|
-
import { CODER_SYSTEM_PROMPT_ID, getCoderAgentModePromptTemplate, getCoderPromptTemplateEdit, getCoderPromptTemplateSimpleEdit }
|
|
18
|
+
import { CODER_SYSTEM_PROMPT_ID, getCoderAgentModePromptTemplate, getCoderPromptTemplateEdit, getCoderPromptTemplateEditNext, getCoderPromptTemplateSimpleEdit }
|
|
19
|
+
from '../common/coder-replace-prompt-template';
|
|
19
20
|
import { LanguageModelRequirement, PromptVariantSet } from '@theia/ai-core';
|
|
20
21
|
import { nls } from '@theia/core';
|
|
21
22
|
import { MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering';
|
|
@@ -39,7 +40,7 @@ export class CoderAgent extends AbstractStreamParsingChatAgent {
|
|
|
39
40
|
override prompts: PromptVariantSet[] = [{
|
|
40
41
|
id: CODER_SYSTEM_PROMPT_ID,
|
|
41
42
|
defaultVariant: getCoderPromptTemplateEdit(),
|
|
42
|
-
variants: [getCoderPromptTemplateSimpleEdit(), getCoderAgentModePromptTemplate()]
|
|
43
|
+
variants: [getCoderPromptTemplateSimpleEdit(), getCoderAgentModePromptTemplate(), getCoderPromptTemplateEditNext()]
|
|
43
44
|
}];
|
|
44
45
|
protected override systemPromptId: string | undefined = CODER_SYSTEM_PROMPT_ID;
|
|
45
46
|
override async invoke(request: MutableChatRequestModel): Promise<void> {
|
|
@@ -55,13 +56,16 @@ export class CoderAgent extends AbstractStreamParsingChatAgent {
|
|
|
55
56
|
model.setSuggestions([
|
|
56
57
|
{
|
|
57
58
|
kind: 'callback',
|
|
58
|
-
callback: () => this.chatService.sendRequest(session.id, {
|
|
59
|
-
|
|
59
|
+
callback: () => this.chatService.sendRequest(session.id, {
|
|
60
|
+
text: `@Coder ${nls.localize('theia/ai/ide/coderAgent/suggestion/fixProblems/prompt', 'please look at {1} and fix any problems.', '#_f')}`
|
|
61
|
+
}),
|
|
62
|
+
content: nls.localize('theia/ai/ide/coderAgent/suggestion/fixProblems/content', '[Fix problems]({0}) in the current file.', '_callback')
|
|
60
63
|
},
|
|
61
64
|
]);
|
|
62
65
|
} else {
|
|
63
|
-
model.setSuggestions([new MarkdownStringImpl(
|
|
64
|
-
|
|
66
|
+
model.setSuggestions([new MarkdownStringImpl(nls.localize('theia/ai/ide/coderAgent/suggestion/startNewChat',
|
|
67
|
+
'Keep chats short and focused. [Start a new chat]({0}) for a new task or [start a new chat with a summary of this one]({1}).',
|
|
68
|
+
`command:${AI_CHAT_NEW_CHAT_WINDOW_COMMAND.id}`, `command:${ChatCommands.AI_CHAT_NEW_WITH_TASK_CONTEXT.id}`))]);
|
|
65
69
|
}
|
|
66
70
|
}
|
|
67
71
|
|
|
@@ -30,7 +30,9 @@ import {
|
|
|
30
30
|
GetProposedFileState,
|
|
31
31
|
ReplaceContentInFileFunctionHelper,
|
|
32
32
|
FileChangeSetTitleProvider,
|
|
33
|
-
DefaultFileChangeSetTitleProvider
|
|
33
|
+
DefaultFileChangeSetTitleProvider,
|
|
34
|
+
ReplaceContentInFileFunctionHelperV2,
|
|
35
|
+
SuggestFileReplacements_Next
|
|
34
36
|
} from './file-changeset-functions';
|
|
35
37
|
import { MutableChatRequestModel, MutableChatResponseModel, ChangeSet, ChangeSetElement, MutableChatModel } from '@theia/ai-chat';
|
|
36
38
|
import { Container } from '@theia/core/shared/inversify';
|
|
@@ -108,6 +110,8 @@ describe('File Changeset Functions Cancellation Tests', () => {
|
|
|
108
110
|
container.bind(WriteFileReplacements).toSelf();
|
|
109
111
|
container.bind(ClearFileChanges).toSelf();
|
|
110
112
|
container.bind(GetProposedFileState).toSelf();
|
|
113
|
+
container.bind(ReplaceContentInFileFunctionHelperV2).toSelf();
|
|
114
|
+
container.bind(SuggestFileReplacements_Next).toSelf();
|
|
111
115
|
});
|
|
112
116
|
|
|
113
117
|
afterEach(() => {
|
|
@@ -209,4 +213,27 @@ describe('File Changeset Functions Cancellation Tests', () => {
|
|
|
209
213
|
expect(jsonResponse.error).to.equal('Operation cancelled by user');
|
|
210
214
|
|
|
211
215
|
});
|
|
216
|
+
|
|
217
|
+
it('SuggestFileReplacements_Next should respect cancellation token', async () => {
|
|
218
|
+
const suggestFileReplacementsNext = container.get(SuggestFileReplacements_Next);
|
|
219
|
+
cancellationTokenSource.cancel();
|
|
220
|
+
|
|
221
|
+
const handler = suggestFileReplacementsNext.getTool().handler;
|
|
222
|
+
const result = await handler(
|
|
223
|
+
JSON.stringify({
|
|
224
|
+
path: 'test.txt',
|
|
225
|
+
replacements: [{ oldContent: 'old', newContent: 'new', multiple: true }]
|
|
226
|
+
}),
|
|
227
|
+
mockCtx as MutableChatRequestModel
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
const jsonResponse = typeof result === 'string' ? JSON.parse(result) : result;
|
|
231
|
+
expect(jsonResponse.error).to.equal('Operation cancelled by user');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('SuggestFileReplacements_Next should have correct ID', () => {
|
|
235
|
+
const suggestFileReplacementsNext = container.get(SuggestFileReplacements_Next);
|
|
236
|
+
expect(SuggestFileReplacements_Next.ID).to.equal('suggestFileReplacements_Next');
|
|
237
|
+
expect(suggestFileReplacementsNext.getTool().id).to.equal('suggestFileReplacements_Next');
|
|
238
|
+
});
|
|
212
239
|
});
|
|
@@ -16,7 +16,8 @@
|
|
|
16
16
|
import { ChangeSet, MutableChatRequestModel } from '@theia/ai-chat';
|
|
17
17
|
import { ChangeSetElementArgs, ChangeSetFileElement, ChangeSetFileElementFactory } from '@theia/ai-chat/lib/browser/change-set-file-element';
|
|
18
18
|
import { ToolProvider, ToolRequest, ToolRequestParameters, ToolRequestParametersProperties } from '@theia/ai-core';
|
|
19
|
-
import {
|
|
19
|
+
import { ContentReplacerV1Impl, Replacement, ContentReplacer } from '@theia/core/lib/common/content-replacer';
|
|
20
|
+
import { ContentReplacerV2Impl } from '@theia/core/lib/common/content-replacer-v2-impl';
|
|
20
21
|
import { URI } from '@theia/core/lib/common/uri';
|
|
21
22
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
22
23
|
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|
@@ -29,7 +30,8 @@ import {
|
|
|
29
30
|
SUGGEST_FILE_CONTENT_ID,
|
|
30
31
|
SUGGEST_FILE_REPLACEMENTS_ID,
|
|
31
32
|
WRITE_FILE_CONTENT_ID,
|
|
32
|
-
WRITE_FILE_REPLACEMENTS_ID
|
|
33
|
+
WRITE_FILE_REPLACEMENTS_ID,
|
|
34
|
+
SUGGEST_FILE_REPLACEMENTS_NEXT_ID
|
|
33
35
|
} from '../common/file-changeset-function-ids';
|
|
34
36
|
|
|
35
37
|
export const FileChangeSetTitleProvider = Symbol('FileChangeSetTitleProvider');
|
|
@@ -206,7 +208,11 @@ export class ReplaceContentInFileFunctionHelper {
|
|
|
206
208
|
private replacer: ContentReplacer;
|
|
207
209
|
|
|
208
210
|
constructor() {
|
|
209
|
-
this.replacer = new
|
|
211
|
+
this.replacer = new ContentReplacerV1Impl();
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
protected setReplacer(replacer: ContentReplacer): void {
|
|
215
|
+
this.replacer = replacer;
|
|
210
216
|
}
|
|
211
217
|
|
|
212
218
|
getToolMetadata(supportMultipleReplace: boolean = false, immediateApplication: boolean = false): { description: string, parameters: ToolRequestParameters } {
|
|
@@ -596,6 +602,44 @@ export class GetProposedFileState implements ToolProvider {
|
|
|
596
602
|
}
|
|
597
603
|
}
|
|
598
604
|
|
|
605
|
+
@injectable()
|
|
606
|
+
export class ReplaceContentInFileFunctionHelperV2 extends ReplaceContentInFileFunctionHelper {
|
|
607
|
+
constructor() {
|
|
608
|
+
super();
|
|
609
|
+
this.setReplacer(new ContentReplacerV2Impl());
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
@injectable()
|
|
614
|
+
export class SuggestFileReplacements_Next implements ToolProvider {
|
|
615
|
+
static ID = SUGGEST_FILE_REPLACEMENTS_NEXT_ID;
|
|
616
|
+
|
|
617
|
+
@inject(ReplaceContentInFileFunctionHelperV2)
|
|
618
|
+
protected readonly replaceContentInFileFunctionHelper: ReplaceContentInFileFunctionHelperV2;
|
|
619
|
+
|
|
620
|
+
getTool(): ToolRequest {
|
|
621
|
+
const metadata = this.replaceContentInFileFunctionHelper.getToolMetadata(true);
|
|
622
|
+
return {
|
|
623
|
+
id: SuggestFileReplacements_Next.ID,
|
|
624
|
+
name: SuggestFileReplacements_Next.ID,
|
|
625
|
+
description: `Proposes to replace sections of content in an existing file by providing a list of replacements.
|
|
626
|
+
Each replacement consists of oldContent to be matched and newContent to insert in its place.
|
|
627
|
+
By default, a single occurrence of each oldContent is expected. If the 'multiple' flag is set to true, all occurrences will be replaced.
|
|
628
|
+
If the expected number of occurrences is not found, the function will return an error. In this case try a different approach.
|
|
629
|
+
For deletions, use an empty newContent.
|
|
630
|
+
The proposed changes will be applied when the user accepts.
|
|
631
|
+
Multiple calls for the same file will merge replacements unless the reset parameter is set to true.`,
|
|
632
|
+
parameters: metadata.parameters,
|
|
633
|
+
handler: async (args: string, ctx: MutableChatRequestModel): Promise<string> => {
|
|
634
|
+
if (ctx?.response?.cancellationToken?.isCancellationRequested) {
|
|
635
|
+
return JSON.stringify({ error: 'Operation cancelled by user' });
|
|
636
|
+
}
|
|
637
|
+
return this.replaceContentInFileFunctionHelper.createChangesetFromToolCall(args, ctx);
|
|
638
|
+
}
|
|
639
|
+
};
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
599
643
|
@injectable()
|
|
600
644
|
export class DefaultFileChangeSetTitleProvider implements FileChangeSetTitleProvider {
|
|
601
645
|
getChangeSetTitle(ctx: MutableChatRequestModel): string {
|