@theia/ai-ide 1.62.0-next.3 → 1.62.1
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 +3 -3
- package/lib/browser/ai-configuration/agent-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/agent-configuration-widget.js +24 -18
- package/lib/browser/ai-configuration/agent-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-view-contribution.js +1 -1
- package/lib/browser/ai-configuration/ai-configuration-view-contribution.js.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-widget.d.ts +2 -0
- package/lib/browser/ai-configuration/ai-configuration-widget.d.ts.map +1 -1
- package/lib/browser/ai-configuration/ai-configuration-widget.js +6 -0
- package/lib/browser/ai-configuration/ai-configuration-widget.js.map +1 -1
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts +138 -0
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts.map +1 -0
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js +492 -0
- package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js.map +1 -0
- package/lib/browser/ai-configuration/template-settings-renderer.d.ts +4 -5
- package/lib/browser/ai-configuration/template-settings-renderer.d.ts.map +1 -1
- package/lib/browser/ai-configuration/template-settings-renderer.js +15 -31
- package/lib/browser/ai-configuration/template-settings-renderer.js.map +1 -1
- package/lib/browser/architect-agent.d.ts +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 +2 -2
- package/lib/browser/coder-agent.d.ts.map +1 -1
- package/lib/browser/coder-agent.js +7 -3
- package/lib/browser/coder-agent.js.map +1 -1
- package/lib/browser/file-changeset-functions.d.ts +14 -1
- package/lib/browser/file-changeset-functions.d.ts.map +1 -1
- package/lib/browser/file-changeset-functions.js +153 -14
- package/lib/browser/file-changeset-functions.js.map +1 -1
- package/lib/browser/frontend-module.d.ts.map +1 -1
- package/lib/browser/frontend-module.js +11 -1
- package/lib/browser/frontend-module.js.map +1 -1
- package/lib/browser/template-preference-contribution.d.ts +2 -2
- package/lib/browser/template-preference-contribution.d.ts.map +1 -1
- package/lib/browser/template-preference-contribution.js +2 -2
- package/lib/browser/template-preference-contribution.js.map +1 -1
- package/lib/browser/workspace-functions.d.ts +1 -1
- package/lib/browser/workspace-functions.js +13 -13
- package/lib/common/architect-prompt-template.d.ts +2 -3
- package/lib/common/architect-prompt-template.d.ts.map +1 -1
- package/lib/common/architect-prompt-template.js +49 -11
- package/lib/common/architect-prompt-template.js.map +1 -1
- package/lib/common/coder-replace-prompt-template.d.ts +6 -3
- package/lib/common/coder-replace-prompt-template.d.ts.map +1 -1
- package/lib/common/coder-replace-prompt-template.js +148 -22
- package/lib/common/coder-replace-prompt-template.js.map +1 -1
- package/lib/common/command-chat-agents.d.ts +1 -1
- package/lib/common/command-chat-agents.d.ts.map +1 -1
- package/lib/common/command-chat-agents.js +4 -4
- package/lib/common/command-chat-agents.js.map +1 -1
- package/lib/common/command-prompt-template.d.ts +2 -2
- package/lib/common/command-prompt-template.d.ts.map +1 -1
- package/lib/common/command-prompt-template.js +18 -15
- package/lib/common/command-prompt-template.js.map +1 -1
- package/lib/common/orchestrator-chat-agent.d.ts +2 -3
- package/lib/common/orchestrator-chat-agent.d.ts.map +1 -1
- package/lib/common/orchestrator-chat-agent.js +11 -26
- package/lib/common/orchestrator-chat-agent.js.map +1 -1
- package/lib/common/orchestrator-prompt-template.d.ts +2 -2
- package/lib/common/orchestrator-prompt-template.d.ts.map +1 -1
- package/lib/common/orchestrator-prompt-template.js +4 -1
- package/lib/common/orchestrator-prompt-template.js.map +1 -1
- package/lib/common/universal-chat-agent.d.ts +5 -1
- package/lib/common/universal-chat-agent.d.ts.map +1 -1
- package/lib/common/universal-chat-agent.js +2 -2
- package/lib/common/universal-chat-agent.js.map +1 -1
- package/lib/common/universal-prompt-template.d.ts +3 -3
- package/lib/common/universal-prompt-template.d.ts.map +1 -1
- package/lib/common/universal-prompt-template.js +1 -2
- package/lib/common/universal-prompt-template.js.map +1 -1
- package/package.json +17 -17
- package/src/browser/ai-configuration/agent-configuration-widget.tsx +31 -24
- package/src/browser/ai-configuration/ai-configuration-view-contribution.ts +1 -1
- package/src/browser/ai-configuration/ai-configuration-widget.tsx +6 -0
- package/src/browser/ai-configuration/prompt-fragments-configuration-widget.tsx +710 -0
- package/src/browser/ai-configuration/template-settings-renderer.tsx +18 -38
- package/src/browser/architect-agent.ts +3 -3
- package/src/browser/coder-agent.ts +10 -5
- package/src/browser/file-changeset-functions.ts +152 -14
- package/src/browser/frontend-module.ts +14 -2
- package/src/browser/style/index.css +320 -0
- package/src/browser/template-preference-contribution.ts +4 -4
- package/src/browser/workspace-functions.ts +3 -3
- package/src/common/architect-prompt-template.ts +54 -14
- package/src/common/coder-replace-prompt-template.ts +150 -24
- package/src/common/command-chat-agents.ts +4 -4
- package/src/common/command-prompt-template.ts +21 -18
- package/src/common/orchestrator-chat-agent.ts +12 -28
- package/src/common/orchestrator-prompt-template.ts +7 -4
- package/src/common/universal-chat-agent.ts +2 -2
- package/src/common/universal-prompt-template.ts +4 -5
|
@@ -13,84 +13,64 @@
|
|
|
13
13
|
//
|
|
14
14
|
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
15
|
// *****************************************************************************
|
|
16
|
-
import { AISettingsService,
|
|
16
|
+
import { AISettingsService, PromptService, PromptVariantSet } from '@theia/ai-core/lib/common';
|
|
17
17
|
import * as React from '@theia/core/shared/react';
|
|
18
18
|
import { nls } from '@theia/core/lib/common/nls';
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
export interface TemplateRendererProps {
|
|
20
|
+
export interface PromptVariantRendererProps {
|
|
23
21
|
agentId: string;
|
|
24
|
-
|
|
25
|
-
promptCustomizationService: PromptCustomizationService;
|
|
22
|
+
promptVariantSet: PromptVariantSet;
|
|
26
23
|
promptService: PromptService;
|
|
27
24
|
aiSettingsService: AISettingsService;
|
|
28
25
|
}
|
|
29
26
|
|
|
30
|
-
export const
|
|
27
|
+
export const PromptVariantRenderer: React.FC<PromptVariantRendererProps> = ({
|
|
31
28
|
agentId,
|
|
32
|
-
|
|
33
|
-
promptCustomizationService,
|
|
29
|
+
promptVariantSet,
|
|
34
30
|
promptService,
|
|
35
31
|
aiSettingsService,
|
|
36
32
|
}) => {
|
|
37
|
-
const variantIds =
|
|
38
|
-
const
|
|
33
|
+
const variantIds = promptService.getVariantIds(promptVariantSet.id);
|
|
34
|
+
const defaultVariantId = promptService.getDefaultVariantId(promptVariantSet.id);
|
|
35
|
+
const [selectedVariant, setSelectedVariant] = React.useState<string>(defaultVariantId!);
|
|
39
36
|
|
|
40
37
|
React.useEffect(() => {
|
|
41
38
|
(async () => {
|
|
42
|
-
const agentSettings = await aiSettingsService.getAgentSettings(agentId);
|
|
43
39
|
const currentVariant =
|
|
44
|
-
|
|
45
|
-
setSelectedVariant(currentVariant);
|
|
40
|
+
await promptService.getSelectedVariantId(promptVariantSet.id);
|
|
41
|
+
setSelectedVariant(currentVariant!);
|
|
46
42
|
})();
|
|
47
|
-
}, [
|
|
43
|
+
}, [promptVariantSet.id, aiSettingsService, agentId]);
|
|
48
44
|
|
|
49
45
|
const isInvalidVariant = !variantIds.includes(selectedVariant);
|
|
50
46
|
|
|
51
47
|
const handleVariantChange = async (event: React.ChangeEvent<HTMLSelectElement>) => {
|
|
52
48
|
const newVariant = event.target.value;
|
|
53
49
|
setSelectedVariant(newVariant);
|
|
54
|
-
|
|
55
|
-
const agentSettings = await aiSettingsService.getAgentSettings(agentId);
|
|
56
|
-
const selectedVariants = agentSettings?.selectedVariants || {};
|
|
57
|
-
|
|
58
|
-
const updatedVariants = { ...selectedVariants };
|
|
59
|
-
if (newVariant === DEFAULT_VARIANT) {
|
|
60
|
-
delete updatedVariants[template.id];
|
|
61
|
-
} else {
|
|
62
|
-
updatedVariants[template.id] = newVariant;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
await aiSettingsService.updateAgentSettings(agentId, {
|
|
66
|
-
selectedVariants: updatedVariants,
|
|
67
|
-
});
|
|
50
|
+
promptService.updateSelectedVariantId(agentId, promptVariantSet.id, newVariant);
|
|
68
51
|
};
|
|
69
52
|
|
|
70
53
|
const openTemplate = () => {
|
|
71
|
-
|
|
72
|
-
const selectedTemplate = promptService.getRawPrompt(templateId);
|
|
73
|
-
promptCustomizationService.editTemplate(templateId, selectedTemplate?.template || '');
|
|
54
|
+
promptService.editBuiltInCustomization(selectedVariant);
|
|
74
55
|
};
|
|
75
56
|
|
|
76
57
|
const resetTemplate = () => {
|
|
77
|
-
|
|
78
|
-
promptCustomizationService.resetTemplate(templateId);
|
|
58
|
+
promptService.resetToBuiltIn(selectedVariant);
|
|
79
59
|
};
|
|
80
60
|
|
|
81
61
|
return (
|
|
82
62
|
<div className="template-renderer">
|
|
83
63
|
<div className="settings-section-title template-header">
|
|
84
|
-
<strong>{
|
|
64
|
+
<strong>{promptVariantSet.id}</strong>
|
|
85
65
|
</div>
|
|
86
66
|
<div className="template-controls">
|
|
87
67
|
{(variantIds.length > 1 || isInvalidVariant) && (
|
|
88
68
|
<>
|
|
89
|
-
<label htmlFor={`variant-selector-${
|
|
69
|
+
<label htmlFor={`variant-selector-${promptVariantSet.id}`} className="template-select-label">
|
|
90
70
|
{nls.localize('theia/ai/core/templateSettings/selectVariant', 'Select Variant:')}
|
|
91
71
|
</label>
|
|
92
72
|
<select
|
|
93
|
-
id={`variant-selector-${
|
|
73
|
+
id={`variant-selector-${promptVariantSet.id}`}
|
|
94
74
|
className={`theia-select template-variant-selector ${isInvalidVariant ? 'error' : ''}`}
|
|
95
75
|
value={isInvalidVariant ? 'invalid' : selectedVariant}
|
|
96
76
|
onChange={handleVariantChange}
|
|
@@ -102,7 +82,7 @@ export const TemplateRenderer: React.FC<TemplateRendererProps> = ({
|
|
|
102
82
|
)}
|
|
103
83
|
{variantIds.map(variantId => (
|
|
104
84
|
<option key={variantId} value={variantId}>
|
|
105
|
-
{variantId}
|
|
85
|
+
{variantId === defaultVariantId ? variantId + ' (default)' : variantId}
|
|
106
86
|
</option>
|
|
107
87
|
))}
|
|
108
88
|
</select>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
import { AbstractStreamParsingChatAgent, ChatRequestModel, ChatService, ChatSession, MutableChatModel, MutableChatRequestModel } from '@theia/ai-chat/lib/common';
|
|
17
17
|
import { LanguageModelRequirement } from '@theia/ai-core';
|
|
18
18
|
import { inject, injectable } from '@theia/core/shared/inversify';
|
|
19
|
-
import {
|
|
19
|
+
import { architectVariants } from '../common/architect-prompt-template';
|
|
20
20
|
import { FILE_CONTENT_FUNCTION_ID, GET_WORKSPACE_FILE_LIST_FUNCTION_ID } from '../common/workspace-functions';
|
|
21
21
|
import { nls } from '@theia/core';
|
|
22
22
|
import { MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering';
|
|
@@ -38,9 +38,9 @@ export class ArchitectAgent extends AbstractStreamParsingChatAgent {
|
|
|
38
38
|
'An AI assistant integrated into Theia IDE, designed to assist software developers. This agent can access the users workspace, it can get a list of all available files \
|
|
39
39
|
and folders and retrieve their content. It cannot modify files. It can therefore answer questions about the current project, project files and source code in the \
|
|
40
40
|
workspace, such as how to build the project, where to put source code, where to find specific code or configurations, etc.');
|
|
41
|
-
override
|
|
41
|
+
override prompts = [architectVariants];
|
|
42
42
|
override functions = [GET_WORKSPACE_FILE_LIST_FUNCTION_ID, FILE_CONTENT_FUNCTION_ID];
|
|
43
|
-
protected override systemPromptId: string | undefined =
|
|
43
|
+
protected override systemPromptId: string | undefined = architectVariants.id;
|
|
44
44
|
|
|
45
45
|
override async invoke(request: MutableChatRequestModel): Promise<void> {
|
|
46
46
|
await super.invoke(request);
|
|
@@ -16,9 +16,9 @@
|
|
|
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
18
|
import { FILE_CONTENT_FUNCTION_ID, GET_WORKSPACE_FILE_LIST_FUNCTION_ID, GET_WORKSPACE_DIRECTORY_STRUCTURE_FUNCTION_ID } from '../common/workspace-functions';
|
|
19
|
-
import {
|
|
19
|
+
import { CODER_SYSTEM_PROMPT_ID, getCoderAgentModePromptTemplate, getCoderReplacePromptTemplate, getCoderReplacePromptTemplateNext } from '../common/coder-replace-prompt-template';
|
|
20
20
|
import { WriteChangeToFileProvider } from './file-changeset-functions';
|
|
21
|
-
import { LanguageModelRequirement } from '@theia/ai-core';
|
|
21
|
+
import { LanguageModelRequirement, PromptVariantSet } from '@theia/ai-core';
|
|
22
22
|
import { nls } from '@theia/core';
|
|
23
23
|
import { MarkdownStringImpl } from '@theia/core/lib/common/markdown-rendering';
|
|
24
24
|
import { AI_CHAT_NEW_CHAT_WINDOW_COMMAND, ChatCommands } from '@theia/ai-chat-ui/lib/browser/chat-view-commands';
|
|
@@ -36,11 +36,15 @@ export class CoderAgent extends AbstractStreamParsingChatAgent {
|
|
|
36
36
|
|
|
37
37
|
override description = nls.localize('theia/ai/workspace/coderAgent/description',
|
|
38
38
|
'An AI assistant integrated into Theia IDE, designed to assist software developers. This agent can access the users workspace, it can get a list of all available files \
|
|
39
|
-
and folders and retrieve their content.
|
|
39
|
+
and folders and retrieve their content. Furthermore, it can suggest modifications of files to the user. It can therefore assist the user with coding tasks or other \
|
|
40
40
|
tasks involving file changes.');
|
|
41
|
-
override
|
|
41
|
+
override prompts: PromptVariantSet[] = [{
|
|
42
|
+
id: CODER_SYSTEM_PROMPT_ID,
|
|
43
|
+
defaultVariant: getCoderReplacePromptTemplate(true),
|
|
44
|
+
variants: [getCoderReplacePromptTemplate(false), getCoderReplacePromptTemplateNext(), getCoderAgentModePromptTemplate()]
|
|
45
|
+
}];
|
|
42
46
|
override functions = [GET_WORKSPACE_DIRECTORY_STRUCTURE_FUNCTION_ID, GET_WORKSPACE_FILE_LIST_FUNCTION_ID, FILE_CONTENT_FUNCTION_ID, WriteChangeToFileProvider.ID];
|
|
43
|
-
protected override systemPromptId: string | undefined =
|
|
47
|
+
protected override systemPromptId: string | undefined = CODER_SYSTEM_PROMPT_ID;
|
|
44
48
|
override async invoke(request: MutableChatRequestModel): Promise<void> {
|
|
45
49
|
await super.invoke(request);
|
|
46
50
|
this.suggest(request);
|
|
@@ -63,4 +67,5 @@ export class CoderAgent extends AbstractStreamParsingChatAgent {
|
|
|
63
67
|
+ ` or [start a new chat with a summary of this one](command:${ChatCommands.AI_CHAT_NEW_WITH_TASK_CONTEXT.id}).`)]);
|
|
64
68
|
}
|
|
65
69
|
}
|
|
70
|
+
|
|
66
71
|
}
|
|
@@ -16,10 +16,11 @@
|
|
|
16
16
|
import { injectable, inject } from '@theia/core/shared/inversify';
|
|
17
17
|
import { ToolProvider, ToolRequest, ToolRequestParameters, ToolRequestParametersProperties } from '@theia/ai-core';
|
|
18
18
|
import { WorkspaceFunctionScope } from './workspace-functions';
|
|
19
|
-
import { ChangeSetFileElementFactory } from '@theia/ai-chat/lib/browser/change-set-file-element';
|
|
20
|
-
import { ChangeSetImpl, MutableChatRequestModel } from '@theia/ai-chat';
|
|
19
|
+
import { ChangeSetFileElement, ChangeSetFileElementFactory } from '@theia/ai-chat/lib/browser/change-set-file-element';
|
|
20
|
+
import { ChangeSet, ChangeSetImpl, MutableChatRequestModel } from '@theia/ai-chat';
|
|
21
21
|
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|
22
22
|
import { ContentReplacer, Replacement } from '@theia/core/lib/common/content-replacer';
|
|
23
|
+
import { URI } from '@theia/core/lib/common/uri';
|
|
23
24
|
|
|
24
25
|
@injectable()
|
|
25
26
|
export class WriteChangeToFileProvider implements ToolProvider {
|
|
@@ -106,7 +107,7 @@ export class ReplaceContentInFileFunctionHelper {
|
|
|
106
107
|
this.replacer = new ContentReplacer();
|
|
107
108
|
}
|
|
108
109
|
|
|
109
|
-
getToolMetadata(
|
|
110
|
+
getToolMetadata(supportMultipleReplace: boolean = false): { description: string, parameters: ToolRequestParameters } {
|
|
110
111
|
const replacementProperties: ToolRequestParametersProperties = {
|
|
111
112
|
oldContent: {
|
|
112
113
|
type: 'string',
|
|
@@ -118,7 +119,7 @@ export class ReplaceContentInFileFunctionHelper {
|
|
|
118
119
|
}
|
|
119
120
|
};
|
|
120
121
|
|
|
121
|
-
if (
|
|
122
|
+
if (supportMultipleReplace) {
|
|
122
123
|
replacementProperties.multiple = {
|
|
123
124
|
type: 'boolean',
|
|
124
125
|
description: 'Set to true if multiple occurrences of the oldContent are expected to be replaced.'
|
|
@@ -139,12 +140,16 @@ export class ReplaceContentInFileFunctionHelper {
|
|
|
139
140
|
required: ['oldContent', 'newContent']
|
|
140
141
|
},
|
|
141
142
|
description: 'An array of replacement objects, each containing oldContent and newContent strings.'
|
|
143
|
+
},
|
|
144
|
+
reset: {
|
|
145
|
+
type: 'boolean',
|
|
146
|
+
description: 'Set to true to clear any existing pending changes for this file and start fresh. Default is false, which merges with existing changes.'
|
|
142
147
|
}
|
|
143
148
|
},
|
|
144
149
|
required: ['path', 'replacements']
|
|
145
150
|
} as ToolRequestParameters;
|
|
146
151
|
|
|
147
|
-
const replacementSentence =
|
|
152
|
+
const replacementSentence = supportMultipleReplace
|
|
148
153
|
? 'By default, a single occurrence of each old content in the tuples is expected to be replaced. If the optional \'multiple\' flag is set to true, all occurrences will\
|
|
149
154
|
be replaced. In either case, if the number of occurrences in the file does not match the expectation the function will return an error. \
|
|
150
155
|
In that case try a different approach.'
|
|
@@ -152,10 +157,10 @@ export class ReplaceContentInFileFunctionHelper {
|
|
|
152
157
|
the function will return an error. In that case try a different approach.';
|
|
153
158
|
|
|
154
159
|
const replacementDescription = `Propose to replace sections of content in an existing file by providing a list of tuples with old content to be matched and replaced.
|
|
155
|
-
${replacementSentence}. For deletions, use an empty new content in the tuple
|
|
156
|
-
Make sure you use the same line endings and whitespace as in the original file content. The proposed changes will be applied when the user accepts
|
|
157
|
-
|
|
158
|
-
|
|
160
|
+
${replacementSentence}. For deletions, use an empty new content in the tuple.
|
|
161
|
+
Make sure you use the same line endings and whitespace as in the original file content. The proposed changes will be applied when the user accepts.
|
|
162
|
+
Multiple calls for the same file will merge replacements unless the reset parameter is set to true. Use the reset parameter to clear previous changes and start
|
|
163
|
+
fresh if needed.`;
|
|
159
164
|
|
|
160
165
|
return {
|
|
161
166
|
description: replacementDescription,
|
|
@@ -166,17 +171,33 @@ export class ReplaceContentInFileFunctionHelper {
|
|
|
166
171
|
|
|
167
172
|
async createChangesetFromToolCall(toolCallString: string, ctx: MutableChatRequestModel): Promise<string> {
|
|
168
173
|
try {
|
|
169
|
-
const { path, replacements } = JSON.parse(toolCallString) as { path: string, replacements: Replacement[] };
|
|
174
|
+
const { path, replacements, reset } = JSON.parse(toolCallString) as { path: string, replacements: Replacement[], reset?: boolean };
|
|
170
175
|
const fileUri = await this.workspaceFunctionScope.resolveRelativePath(path);
|
|
171
|
-
const fileContent = (await this.fileService.read(fileUri)).value.toString();
|
|
172
176
|
|
|
173
|
-
|
|
177
|
+
// Get the starting content - either original file or existing proposed state
|
|
178
|
+
let startingContent: string;
|
|
179
|
+
if (reset || !ctx.session.changeSet) {
|
|
180
|
+
// Start from original file content
|
|
181
|
+
startingContent = (await this.fileService.read(fileUri)).value.toString();
|
|
182
|
+
} else {
|
|
183
|
+
// Start from existing proposed state if available
|
|
184
|
+
const existingElement = this.findExistingChangeElement(ctx.session.changeSet, fileUri);
|
|
185
|
+
if (existingElement) {
|
|
186
|
+
startingContent = existingElement.targetState || (await this.fileService.read(fileUri)).value.toString();
|
|
187
|
+
} else {
|
|
188
|
+
startingContent = (await this.fileService.read(fileUri)).value.toString();
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const { updatedContent, errors } = this.replacer.applyReplacements(startingContent, replacements);
|
|
174
193
|
|
|
175
194
|
if (errors.length > 0) {
|
|
176
195
|
return `Errors encountered: ${errors.join('; ')}`;
|
|
177
196
|
}
|
|
178
197
|
|
|
179
|
-
if
|
|
198
|
+
// Only create/update changeset if content actually changed
|
|
199
|
+
const originalContent = (await this.fileService.read(fileUri)).value.toString();
|
|
200
|
+
if (updatedContent !== originalContent) {
|
|
180
201
|
let changeSet = ctx.session.changeSet;
|
|
181
202
|
if (!changeSet) {
|
|
182
203
|
changeSet = new ChangeSetImpl('Changes proposed by Coder');
|
|
@@ -194,12 +215,70 @@ export class ReplaceContentInFileFunctionHelper {
|
|
|
194
215
|
})
|
|
195
216
|
);
|
|
196
217
|
}
|
|
197
|
-
|
|
218
|
+
|
|
219
|
+
const action = reset ? 'reset and applied' : 'applied';
|
|
220
|
+
return `Proposed replacements ${action} to file ${path}. The user will review and potentially apply the changes.`;
|
|
198
221
|
} catch (error) {
|
|
199
222
|
console.debug('Error processing replacements:', error.message);
|
|
200
223
|
return JSON.stringify({ error: error.message });
|
|
201
224
|
}
|
|
202
225
|
}
|
|
226
|
+
|
|
227
|
+
private findExistingChangeElement(changeSet: ChangeSet, fileUri: URI): ChangeSetFileElement | undefined {
|
|
228
|
+
return changeSet.getElementByURI(fileUri) as ChangeSetFileElement;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
async clearFileChanges(path: string, ctx: MutableChatRequestModel): Promise<string> {
|
|
232
|
+
try {
|
|
233
|
+
const fileUri = await this.workspaceFunctionScope.resolveRelativePath(path);
|
|
234
|
+
|
|
235
|
+
if (!ctx.session.changeSet) {
|
|
236
|
+
return `No pending changes found for file ${path}.`;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const elements = ctx.session.changeSet.getElements();
|
|
240
|
+
const indicesToRemove: number[] = [];
|
|
241
|
+
elements.forEach((element, index) => {
|
|
242
|
+
if (element.uri && element.uri.toString() === fileUri.toString()) {
|
|
243
|
+
indicesToRemove.push(index);
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
if (indicesToRemove.length > 0) {
|
|
248
|
+
ctx.session.changeSet.removeElements(...indicesToRemove);
|
|
249
|
+
return `Cleared ${indicesToRemove.length} pending change(s) for file ${path}.`;
|
|
250
|
+
} else {
|
|
251
|
+
return `No pending changes found for file ${path}.`;
|
|
252
|
+
}
|
|
253
|
+
} catch (error) {
|
|
254
|
+
console.debug('Error clearing file changes:', error.message);
|
|
255
|
+
return JSON.stringify({ error: error.message });
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
async getProposedFileState(path: string, ctx: MutableChatRequestModel): Promise<string> {
|
|
260
|
+
try {
|
|
261
|
+
const fileUri = await this.workspaceFunctionScope.resolveRelativePath(path);
|
|
262
|
+
|
|
263
|
+
if (!ctx.session.changeSet) {
|
|
264
|
+
// No changeset exists, return original file content
|
|
265
|
+
const originalContent = (await this.fileService.read(fileUri)).value.toString();
|
|
266
|
+
return `File ${path} has no pending changes. Original content:\n\n${originalContent}`;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const existingElement = this.findExistingChangeElement(ctx.session.changeSet, fileUri);
|
|
270
|
+
if (existingElement && existingElement.targetState) {
|
|
271
|
+
return `File ${path} has pending changes. Proposed content:\n\n${existingElement.targetState}`;
|
|
272
|
+
} else {
|
|
273
|
+
// No pending changes for this file
|
|
274
|
+
const originalContent = (await this.fileService.read(fileUri)).value.toString();
|
|
275
|
+
return `File ${path} has no pending changes. Original content:\n\n${originalContent}`;
|
|
276
|
+
}
|
|
277
|
+
} catch (error) {
|
|
278
|
+
console.debug('Error getting proposed file state:', error.message);
|
|
279
|
+
return JSON.stringify({ error: error.message });
|
|
280
|
+
}
|
|
281
|
+
}
|
|
203
282
|
}
|
|
204
283
|
|
|
205
284
|
@injectable()
|
|
@@ -239,3 +318,62 @@ export class ReplaceContentInFileProvider implements ToolProvider {
|
|
|
239
318
|
};
|
|
240
319
|
}
|
|
241
320
|
}
|
|
321
|
+
|
|
322
|
+
@injectable()
|
|
323
|
+
export class ClearFileChangesProvider implements ToolProvider {
|
|
324
|
+
static ID = 'changeSet_clearFileChanges';
|
|
325
|
+
@inject(ReplaceContentInFileFunctionHelper)
|
|
326
|
+
protected readonly replaceContentInFileFunctionHelper: ReplaceContentInFileFunctionHelper;
|
|
327
|
+
|
|
328
|
+
getTool(): ToolRequest {
|
|
329
|
+
return {
|
|
330
|
+
id: ClearFileChangesProvider.ID,
|
|
331
|
+
name: ClearFileChangesProvider.ID,
|
|
332
|
+
description: 'Clears all pending changes for a specific file, allowing you to start fresh with new modifications.',
|
|
333
|
+
parameters: {
|
|
334
|
+
type: 'object',
|
|
335
|
+
properties: {
|
|
336
|
+
path: {
|
|
337
|
+
type: 'string',
|
|
338
|
+
description: 'The path of the file to clear pending changes for.'
|
|
339
|
+
}
|
|
340
|
+
},
|
|
341
|
+
required: ['path']
|
|
342
|
+
},
|
|
343
|
+
handler: async (args: string, ctx: MutableChatRequestModel): Promise<string> => {
|
|
344
|
+
const { path } = JSON.parse(args);
|
|
345
|
+
return this.replaceContentInFileFunctionHelper.clearFileChanges(path, ctx);
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
@injectable()
|
|
352
|
+
export class GetProposedFileStateProvider implements ToolProvider {
|
|
353
|
+
static ID = 'changeSet_getProposedFileState';
|
|
354
|
+
@inject(ReplaceContentInFileFunctionHelper)
|
|
355
|
+
protected readonly replaceContentInFileFunctionHelper: ReplaceContentInFileFunctionHelper;
|
|
356
|
+
|
|
357
|
+
getTool(): ToolRequest {
|
|
358
|
+
return {
|
|
359
|
+
id: GetProposedFileStateProvider.ID,
|
|
360
|
+
name: GetProposedFileStateProvider.ID,
|
|
361
|
+
description: 'Returns the current proposed state of a file, including all pending changes that have been proposed ' +
|
|
362
|
+
'but not yet applied. This allows you to inspect the current state before making additional changes.',
|
|
363
|
+
parameters: {
|
|
364
|
+
type: 'object',
|
|
365
|
+
properties: {
|
|
366
|
+
path: {
|
|
367
|
+
type: 'string',
|
|
368
|
+
description: 'The path of the file to get the proposed state for.'
|
|
369
|
+
}
|
|
370
|
+
},
|
|
371
|
+
required: ['path']
|
|
372
|
+
},
|
|
373
|
+
handler: async (args: string, ctx: MutableChatRequestModel): Promise<string> => {
|
|
374
|
+
const { path } = JSON.parse(args);
|
|
375
|
+
return this.replaceContentInFileFunctionHelper.getProposedFileState(path, ctx);
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
}
|
|
379
|
+
}
|
|
@@ -20,12 +20,14 @@ import { Agent, AIVariableContribution, bindToolProvider } from '@theia/ai-core/
|
|
|
20
20
|
import { ArchitectAgent } from './architect-agent';
|
|
21
21
|
import { CoderAgent } from './coder-agent';
|
|
22
22
|
import { SummarizeSessionCommandContribution } from './summarize-session-command-contribution';
|
|
23
|
-
import { FileContentFunction,
|
|
23
|
+
import { FileContentFunction, FileDiagnosticProvider, GetWorkspaceDirectoryStructure, GetWorkspaceFileList, WorkspaceFunctionScope } from './workspace-functions';
|
|
24
24
|
import { WorkspaceSearchProvider } from './workspace-search-provider';
|
|
25
25
|
import { FrontendApplicationContribution, PreferenceContribution, WidgetFactory, bindViewContribution } from '@theia/core/lib/browser';
|
|
26
26
|
import { TaskListProvider, TaskRunnerProvider } from './workspace-task-provider';
|
|
27
27
|
import { WorkspacePreferencesSchema } from './workspace-preferences';
|
|
28
28
|
import {
|
|
29
|
+
ClearFileChangesProvider,
|
|
30
|
+
GetProposedFileStateProvider,
|
|
29
31
|
ReplaceContentInFileFunctionHelper,
|
|
30
32
|
ReplaceContentInFileProvider,
|
|
31
33
|
SimpleReplaceContentInFileProvider,
|
|
@@ -52,6 +54,7 @@ import { TaskContextSummaryVariableContribution } from './task-background-summar
|
|
|
52
54
|
import { TaskContextFileStorageService } from './task-context-file-storage-service';
|
|
53
55
|
import { TaskContextStorageService } from '@theia/ai-chat/lib/browser/task-context-service';
|
|
54
56
|
import { CommandContribution } from '@theia/core';
|
|
57
|
+
import { AIPromptFragmentsConfigurationWidget } from './ai-configuration/prompt-fragments-configuration-widget';
|
|
55
58
|
|
|
56
59
|
export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
|
|
57
60
|
bind(PreferenceContribution).toConstantValue({ schema: WorkspacePreferencesSchema });
|
|
@@ -84,7 +87,7 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
|
|
|
84
87
|
bindToolProvider(GetWorkspaceFileList, bind);
|
|
85
88
|
bindToolProvider(FileContentFunction, bind);
|
|
86
89
|
bindToolProvider(GetWorkspaceDirectoryStructure, bind);
|
|
87
|
-
bindToolProvider(
|
|
90
|
+
bindToolProvider(FileDiagnosticProvider, bind);
|
|
88
91
|
bind(WorkspaceFunctionScope).toSelf().inSingletonScope();
|
|
89
92
|
bindToolProvider(WorkspaceSearchProvider, bind);
|
|
90
93
|
|
|
@@ -124,6 +127,8 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
|
|
|
124
127
|
.inSingletonScope();
|
|
125
128
|
|
|
126
129
|
bindToolProvider(SimpleReplaceContentInFileProvider, bind);
|
|
130
|
+
bindToolProvider(ClearFileChangesProvider, bind);
|
|
131
|
+
bindToolProvider(GetProposedFileStateProvider, bind);
|
|
127
132
|
bindToolProvider(AddFileToChatContext, bind);
|
|
128
133
|
bind(AIVariableContribution).to(ContextFilesVariableContribution).inSingletonScope();
|
|
129
134
|
bind(PreferenceContribution).toConstantValue({ schema: AiConfigurationPreferences });
|
|
@@ -152,4 +157,11 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
|
|
|
152
157
|
rebind(TaskContextStorageService).toService(TaskContextFileStorageService);
|
|
153
158
|
|
|
154
159
|
bind(CommandContribution).to(SummarizeSessionCommandContribution);
|
|
160
|
+
bind(AIPromptFragmentsConfigurationWidget).toSelf();
|
|
161
|
+
bind(WidgetFactory)
|
|
162
|
+
.toDynamicValue(ctx => ({
|
|
163
|
+
id: AIPromptFragmentsConfigurationWidget.ID,
|
|
164
|
+
createWidget: () => ctx.container.get(AIPromptFragmentsConfigurationWidget)
|
|
165
|
+
}))
|
|
166
|
+
.inSingletonScope();
|
|
155
167
|
});
|