@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
|
@@ -56,7 +56,9 @@ import {
|
|
|
56
56
|
WriteFileReplacements,
|
|
57
57
|
SimpleWriteFileReplacements,
|
|
58
58
|
FileChangeSetTitleProvider,
|
|
59
|
-
DefaultFileChangeSetTitleProvider
|
|
59
|
+
DefaultFileChangeSetTitleProvider,
|
|
60
|
+
ReplaceContentInFileFunctionHelperV2,
|
|
61
|
+
SuggestFileReplacements_Next
|
|
60
62
|
} from './file-changeset-functions';
|
|
61
63
|
import { OrchestratorChatAgent, OrchestratorChatAgentId } from '../common/orchestrator-chat-agent';
|
|
62
64
|
import { UniversalChatAgent, UniversalChatAgentId } from '../common/universal-chat-agent';
|
|
@@ -84,6 +86,7 @@ import { TaskContextStorageService } from '@theia/ai-chat/lib/browser/task-conte
|
|
|
84
86
|
import { CommandContribution, PreferenceContribution } from '@theia/core';
|
|
85
87
|
import { AIPromptFragmentsConfigurationWidget } from './ai-configuration/prompt-fragments-configuration-widget';
|
|
86
88
|
import { BrowserAutomation, browserAutomationPath } from '../common/browser-automation-protocol';
|
|
89
|
+
import { GitHubRepoService, githubRepoServicePath } from '../common/github-repo-protocol';
|
|
87
90
|
import { CloseBrowserProvider, IsBrowserRunningProvider, LaunchBrowserProvider, QueryDomProvider } from './app-tester-chat-functions';
|
|
88
91
|
import { ModelAliasesConfigurationWidget } from './ai-configuration/model-aliases-configuration-widget';
|
|
89
92
|
import { aiIdePreferenceSchema } from '../common/ai-ide-preferences';
|
|
@@ -159,6 +162,8 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
|
|
|
159
162
|
bind(FileChangeSetTitleProvider).to(DefaultFileChangeSetTitleProvider).inSingletonScope();
|
|
160
163
|
bindToolProvider(SuggestFileReplacements, bind);
|
|
161
164
|
bindToolProvider(WriteFileReplacements, bind);
|
|
165
|
+
bind(ReplaceContentInFileFunctionHelperV2).toSelf().inSingletonScope();
|
|
166
|
+
bindToolProvider(SuggestFileReplacements_Next, bind);
|
|
162
167
|
bindToolProvider(ListChatContext, bind);
|
|
163
168
|
bindToolProvider(ResolveChatContext, bind);
|
|
164
169
|
bind(AIConfigurationSelectionService).toSelf().inSingletonScope();
|
|
@@ -240,6 +245,11 @@ export default new ContainerModule((bind, _unbind, _isBound, rebind) => {
|
|
|
240
245
|
bind(TaskContextSummaryVariableContribution).toSelf().inSingletonScope();
|
|
241
246
|
bind(AIVariableContribution).toService(TaskContextSummaryVariableContribution);
|
|
242
247
|
|
|
248
|
+
bind(GitHubRepoService).toDynamicValue(ctx => {
|
|
249
|
+
const provider = ctx.container.get<ServiceConnectionProvider>(RemoteConnectionProvider);
|
|
250
|
+
return provider.createProxy<GitHubRepoService>(githubRepoServicePath);
|
|
251
|
+
}).inSingletonScope();
|
|
252
|
+
|
|
243
253
|
bind(GitHubRepoVariableContribution).toSelf().inSingletonScope();
|
|
244
254
|
bind(AIVariableContribution).toService(GitHubRepoVariableContribution);
|
|
245
255
|
bind(TaskContextFileStorageService).toSelf().inSingletonScope();
|
|
@@ -54,7 +54,7 @@ export class GitHubChatAgent extends AbstractStreamParsingChatAgent {
|
|
|
54
54
|
identifier: 'default/code',
|
|
55
55
|
}];
|
|
56
56
|
protected defaultLanguageModelPurpose: string = 'chat';
|
|
57
|
-
override description = nls.localize('theia/ai/
|
|
57
|
+
override description = nls.localize('theia/ai/ide/github/description', 'This agent helps you interact with GitHub repositories, issues, pull requests, and other GitHub '
|
|
58
58
|
+ 'features through the GitHub MCP server. '
|
|
59
59
|
+ 'It can help you manage your repositories, create issues, handle pull requests, and perform various GitHub operations.');
|
|
60
60
|
|
|
@@ -70,26 +70,26 @@ export class GitHubChatAgent extends AbstractStreamParsingChatAgent {
|
|
|
70
70
|
try {
|
|
71
71
|
if (await this.requiresConfiguration()) {
|
|
72
72
|
// Ask the user if they want to configure the GitHub server
|
|
73
|
-
request.response.response.addContent(new QuestionResponseContentImpl(
|
|
73
|
+
request.response.response.addContent(new QuestionResponseContentImpl(nls.localize('theia/ai/ide/github/configureGitHubServer/question',
|
|
74
74
|
'The GitHub MCP server is not configured. Would you like to configure it now? '
|
|
75
|
-
+ 'This will open the settings.json file where you can add your GitHub access token.',
|
|
75
|
+
+ 'This will open the settings.json file where you can add your GitHub access token.'),
|
|
76
76
|
[
|
|
77
|
-
{ text: 'Yes, configure GitHub server', value: 'configure' },
|
|
78
|
-
{ text: 'No, cancel', value: 'cancel' }
|
|
77
|
+
{ text: nls.localize('theia/ai/ide/github/configureGitHubServer/yes', 'Yes, configure GitHub server'), value: 'configure' },
|
|
78
|
+
{ text: nls.localize('theia/ai/ide/github/configureGitHubServer/no', 'No, cancel'), value: 'cancel' }
|
|
79
79
|
],
|
|
80
80
|
request,
|
|
81
81
|
async selectedOption => {
|
|
82
82
|
if (selectedOption.value === 'configure') {
|
|
83
83
|
await this.offerConfiguration();
|
|
84
|
-
request.response.response.addContent(new MarkdownChatResponseContentImpl(
|
|
84
|
+
request.response.response.addContent(new MarkdownChatResponseContentImpl(nls.localize('theia/ai/ide/github/configureGitHubServer/followup',
|
|
85
85
|
'Settings file opened. Please add your GitHub Personal Access Token to the `serverAuthToken` property in the GitHub server configuration, then '
|
|
86
86
|
+ ' save and try again.\n\n' +
|
|
87
87
|
'You can create a Personal Access Token at: https://github.com/settings/tokens'
|
|
88
|
-
));
|
|
88
|
+
)));
|
|
89
89
|
request.response.complete();
|
|
90
90
|
} else {
|
|
91
|
-
request.response.response.addContent(new MarkdownChatResponseContentImpl(
|
|
92
|
-
|
|
91
|
+
request.response.response.addContent(new MarkdownChatResponseContentImpl(nls.localize('theia/ai/ide/github/configureGitHubServer/canceled',
|
|
92
|
+
'GitHub server configuration cancelled. Please configure the GitHub MCP server to use this agent.')));
|
|
93
93
|
request.response.complete();
|
|
94
94
|
}
|
|
95
95
|
}
|
|
@@ -100,28 +100,33 @@ export class GitHubChatAgent extends AbstractStreamParsingChatAgent {
|
|
|
100
100
|
|
|
101
101
|
if (await this.requiresStartingServer()) {
|
|
102
102
|
// Ask the user if they want to start the server
|
|
103
|
-
request.response.response.addContent(new QuestionResponseContentImpl(
|
|
104
|
-
'The GitHub MCP server is configured but not running. Would you like to start it now?',
|
|
103
|
+
request.response.response.addContent(new QuestionResponseContentImpl(nls.localize('theia/ai/ide/github/startGitHubServer/question',
|
|
104
|
+
'The GitHub MCP server is configured but not running. Would you like to start it now?'),
|
|
105
105
|
[
|
|
106
|
-
{ text: 'Yes, start the server', value: 'yes' },
|
|
107
|
-
{ text: 'No, cancel', value: 'no' }
|
|
106
|
+
{ text: nls.localize('theia/ai/ide/github/startGitHubServer/yes', 'Yes, start the server'), value: 'yes' },
|
|
107
|
+
{ text: nls.localize('theia/ai/ide/github/startGitHubServer/no', 'No, cancel'), value: 'no' }
|
|
108
108
|
],
|
|
109
109
|
request,
|
|
110
110
|
async selectedOption => {
|
|
111
111
|
if (selectedOption.value === 'yes') {
|
|
112
|
-
const progress = request.response.addProgressMessage({
|
|
112
|
+
const progress = request.response.addProgressMessage({
|
|
113
|
+
content: nls.localize('theia/ai/ide/github/startGitHubServer/progress', 'Starting GitHub MCP server.'),
|
|
114
|
+
show: 'whileIncomplete'
|
|
115
|
+
});
|
|
113
116
|
try {
|
|
114
117
|
await this.startServer();
|
|
115
118
|
request.response.updateProgressMessage({ ...progress, show: 'whileIncomplete', status: 'completed' });
|
|
116
119
|
await super.invoke(request);
|
|
117
120
|
} catch (error) {
|
|
118
121
|
request.response.response.addContent(new ErrorChatResponseContentImpl(
|
|
119
|
-
new Error('Failed to start GitHub MCP server: '
|
|
122
|
+
new Error(nls.localize('theia/ai/ide/github/startGitHubServer/error', 'Failed to start GitHub MCP server: {0}',
|
|
123
|
+
error instanceof Error ? error.message : String(error)))
|
|
120
124
|
));
|
|
121
125
|
request.response.complete();
|
|
122
126
|
}
|
|
123
127
|
} else {
|
|
124
|
-
request.response.response.addContent(new MarkdownChatResponseContentImpl(
|
|
128
|
+
request.response.response.addContent(new MarkdownChatResponseContentImpl(nls.localize('theia/ai/ide/github/startGitHubServer/canceled',
|
|
129
|
+
'Please start the GitHub MCP server to use this agent.')));
|
|
125
130
|
request.response.complete();
|
|
126
131
|
}
|
|
127
132
|
}
|
|
@@ -134,7 +139,8 @@ export class GitHubChatAgent extends AbstractStreamParsingChatAgent {
|
|
|
134
139
|
await super.invoke(request);
|
|
135
140
|
} catch (error) {
|
|
136
141
|
request.response.response.addContent(new ErrorChatResponseContentImpl(
|
|
137
|
-
new Error('Error checking GitHub MCP server status: '
|
|
142
|
+
new Error(nls.localize('theia/ai/ide/github/errorCheckingGitHubServerStatus', 'Error checking GitHub MCP server status: {0}',
|
|
143
|
+
error instanceof Error ? error.message : String(error)))
|
|
138
144
|
));
|
|
139
145
|
request.response.complete();
|
|
140
146
|
}
|
|
@@ -25,9 +25,10 @@ import {
|
|
|
25
25
|
ResolvedAIVariable,
|
|
26
26
|
AIVariable
|
|
27
27
|
} from '@theia/ai-core/lib/common';
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
|
|
28
|
+
import { WorkspaceService } from '@theia/workspace/lib/browser';
|
|
29
|
+
import { FileService } from '@theia/filesystem/lib/browser/file-service';
|
|
30
|
+
|
|
31
|
+
import { GitHubRepoService } from '../common/github-repo-protocol';
|
|
31
32
|
|
|
32
33
|
export const GITHUB_REPO_NAME_VARIABLE: AIVariable = {
|
|
33
34
|
id: 'github-repo-name-provider',
|
|
@@ -38,59 +39,50 @@ export const GITHUB_REPO_NAME_VARIABLE: AIVariable = {
|
|
|
38
39
|
@injectable()
|
|
39
40
|
export class GitHubRepoVariableContribution implements AIVariableContribution, AIVariableResolver {
|
|
40
41
|
|
|
41
|
-
@inject(
|
|
42
|
-
protected readonly
|
|
42
|
+
@inject(WorkspaceService)
|
|
43
|
+
protected readonly workspaceService: WorkspaceService;
|
|
43
44
|
|
|
44
|
-
@inject(
|
|
45
|
-
protected readonly
|
|
45
|
+
@inject(FileService)
|
|
46
|
+
protected readonly fileService: FileService;
|
|
46
47
|
|
|
47
|
-
@inject(
|
|
48
|
-
protected readonly
|
|
48
|
+
@inject(GitHubRepoService)
|
|
49
|
+
protected readonly gitHubRepoService: GitHubRepoService;
|
|
49
50
|
|
|
50
51
|
registerVariables(service: AIVariableService): void {
|
|
51
52
|
service.registerResolver(GITHUB_REPO_NAME_VARIABLE, this);
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
canResolve(request: AIVariableResolutionRequest,
|
|
55
|
+
canResolve(request: AIVariableResolutionRequest, _context: AIVariableContext): MaybePromise<number> {
|
|
55
56
|
if (request.variable.name !== GITHUB_REPO_NAME_VARIABLE.name) {
|
|
56
57
|
return 0;
|
|
57
58
|
}
|
|
58
59
|
|
|
59
|
-
const selectedRepo = this.repositoryProvider.selectedRepository;
|
|
60
|
-
if (!selectedRepo) {
|
|
61
|
-
return 0;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
60
|
return 1;
|
|
65
61
|
}
|
|
66
62
|
|
|
67
|
-
async resolve(request: AIVariableResolutionRequest,
|
|
63
|
+
async resolve(request: AIVariableResolutionRequest, _context: AIVariableContext): Promise<ResolvedAIVariable | undefined> {
|
|
68
64
|
if (request.variable.name !== GITHUB_REPO_NAME_VARIABLE.name) {
|
|
69
65
|
return undefined;
|
|
70
66
|
}
|
|
71
67
|
|
|
72
|
-
const repository = this.repositoryProvider.selectedRepository;
|
|
73
|
-
if (!repository) {
|
|
74
|
-
return { variable: request.variable, value: 'No GitHub repository is currently selected or detected.' };
|
|
75
|
-
}
|
|
76
|
-
|
|
77
68
|
try {
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
// Find GitHub remote (prefer 'origin', then any GitHub remote)
|
|
81
|
-
const githubRemote = remotes.find(remote =>
|
|
82
|
-
remote.name === 'origin' && this.isGitHubRemote(remote.fetch)
|
|
83
|
-
) || remotes.find(remote => this.isGitHubRemote(remote.fetch));
|
|
84
|
-
|
|
85
|
-
if (!githubRemote) {
|
|
69
|
+
const workspaceRoots = await this.workspaceService.roots;
|
|
70
|
+
if (workspaceRoots.length === 0) {
|
|
86
71
|
return { variable: request.variable, value: 'No GitHub repository is currently selected or detected.' };
|
|
87
72
|
}
|
|
88
73
|
|
|
89
|
-
|
|
90
|
-
|
|
74
|
+
// Get the filesystem path from the workspace root URI
|
|
75
|
+
const workspaceRoot = workspaceRoots[0].resource;
|
|
76
|
+
const workspacePath = workspaceRoot.path.fsPath();
|
|
77
|
+
|
|
78
|
+
// Use the backend service to get GitHub repository information
|
|
79
|
+
const repoInfo = await this.gitHubRepoService.getGitHubRepoInfo(workspacePath);
|
|
80
|
+
|
|
81
|
+
if (!repoInfo) {
|
|
91
82
|
return { variable: request.variable, value: 'No GitHub repository is currently selected or detected.' };
|
|
92
83
|
}
|
|
93
84
|
|
|
85
|
+
const repoName = `${repoInfo.owner}/${repoInfo.repo}`;
|
|
94
86
|
return { variable: request.variable, value: `You are currently working with the GitHub repository: **${repoName}**` };
|
|
95
87
|
|
|
96
88
|
} catch (error) {
|
|
@@ -98,23 +90,4 @@ export class GitHubRepoVariableContribution implements AIVariableContribution, A
|
|
|
98
90
|
return { variable: request.variable, value: 'No GitHub repository is currently selected or detected.' };
|
|
99
91
|
}
|
|
100
92
|
}
|
|
101
|
-
|
|
102
|
-
private isGitHubRemote(remoteUrl: string): boolean {
|
|
103
|
-
return remoteUrl.includes('github.com');
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
private extractRepoNameFromGitHubUrl(url: string): string | undefined {
|
|
107
|
-
|
|
108
|
-
const httpsMatch = url.match(/https:\/\/github\.com\/([^\/]+\/[^\/]+?)(?:\.git)?$/);
|
|
109
|
-
if (httpsMatch) {
|
|
110
|
-
return httpsMatch[1];
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const sshMatch = url.match(/git@github\.com:([^\/]+\/[^\/]+?)(?:\.git)?$/);
|
|
114
|
-
if (sshMatch) {
|
|
115
|
-
return sshMatch[1];
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
return undefined;
|
|
119
|
-
}
|
|
120
93
|
}
|
|
@@ -28,13 +28,15 @@ import {
|
|
|
28
28
|
SUGGEST_FILE_REPLACEMENTS_ID,
|
|
29
29
|
WRITE_FILE_REPLACEMENTS_ID,
|
|
30
30
|
CLEAR_FILE_CHANGES_ID,
|
|
31
|
-
GET_PROPOSED_CHANGES_ID
|
|
31
|
+
GET_PROPOSED_CHANGES_ID,
|
|
32
|
+
SUGGEST_FILE_REPLACEMENTS_NEXT_ID
|
|
32
33
|
} from './file-changeset-function-ids';
|
|
33
34
|
|
|
34
35
|
export const CODER_SYSTEM_PROMPT_ID = 'coder-system';
|
|
35
36
|
|
|
36
37
|
export const CODER_SIMPLE_EDIT_TEMPLATE_ID = 'coder-system-simple-edit';
|
|
37
38
|
export const CODER_EDIT_TEMPLATE_ID = 'coder-system-edit';
|
|
39
|
+
export const CODER_EDIT_NEXT_TEMPLATE_ID = 'coder-system-edit-next';
|
|
38
40
|
export const CODER_AGENT_MODE_TEMPLATE_ID = 'coder-system-agent-mode';
|
|
39
41
|
|
|
40
42
|
export function getCoderAgentModePromptTemplate(): BasePromptFragment {
|
|
@@ -159,10 +161,8 @@ You are an autonomous AI agent. Do not stop until:
|
|
|
159
161
|
};
|
|
160
162
|
}
|
|
161
163
|
|
|
162
|
-
|
|
163
|
-
return {
|
|
164
|
-
id: CODER_EDIT_TEMPLATE_ID,
|
|
165
|
-
template: `{{!-- This prompt is licensed under the MIT License (https://opensource.org/license/mit).
|
|
164
|
+
function getCoderEditPromptTemplate(suggestFileReplacementsId: string): string {
|
|
165
|
+
return `{{!-- This prompt is licensed under the MIT License (https://opensource.org/license/mit).
|
|
166
166
|
Made improvements or adaptations to this prompt template? We'd love for you to share it with the community! Contribute back here:
|
|
167
167
|
https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
|
|
168
168
|
You are an AI assistant integrated into Theia IDE, designed to assist software developers with code tasks. You can interact with the code base and suggest changes, \
|
|
@@ -189,9 +189,9 @@ This also applies for newly created files!
|
|
|
189
189
|
- **Always Retrieve Current Content**: Use getFileContent to get the original content of the target file.
|
|
190
190
|
- **View Pending Changes**: Use ~{${GET_PROPOSED_CHANGES_ID}} to see the current proposed state of a file, including all pending changes.
|
|
191
191
|
- **Change Content**: Use one of these methods to propose changes:
|
|
192
|
-
- ~{${
|
|
192
|
+
- ~{${suggestFileReplacementsId}}: For targeted replacements of specific text sections. Multiple calls will merge changes unless you set the reset parameter to true.
|
|
193
193
|
- ~{${SUGGEST_FILE_CONTENT_ID}}: For complete file rewrites when you need to replace the entire content.
|
|
194
|
-
- If ~{${
|
|
194
|
+
- If ~{${suggestFileReplacementsId}} continuously fails use ~{${SUGGEST_FILE_CONTENT_ID}}.
|
|
195
195
|
- ~{${CLEAR_FILE_CHANGES_ID}}: To clear all pending changes for a file and start fresh.
|
|
196
196
|
|
|
197
197
|
The changes will be presented as an applicable diff to the user in any case. The user can then accept or reject each change individually. Before you run tasks that depend on the \
|
|
@@ -229,7 +229,22 @@ You have previously proposed changes for the following files. Some suggestions m
|
|
|
229
229
|
- Tasks such as building or liniting run on the workspace state, the user has to accept the changes beforehand
|
|
230
230
|
- Do not run a build or any error checking before the users asks you to
|
|
231
231
|
- Focus on the task that the user described
|
|
232
|
-
|
|
232
|
+
`;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export function getCoderPromptTemplateEdit(): BasePromptFragment {
|
|
236
|
+
return {
|
|
237
|
+
id: CODER_EDIT_TEMPLATE_ID,
|
|
238
|
+
template: getCoderEditPromptTemplate(SUGGEST_FILE_REPLACEMENTS_ID)
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
export function getCoderPromptTemplateEditNext(): BasePromptFragment {
|
|
243
|
+
return {
|
|
244
|
+
id: CODER_EDIT_NEXT_TEMPLATE_ID,
|
|
245
|
+
template: getCoderEditPromptTemplate(SUGGEST_FILE_REPLACEMENTS_NEXT_ID),
|
|
246
|
+
...({ variantOf: CODER_EDIT_TEMPLATE_ID })
|
|
247
|
+
};
|
|
233
248
|
}
|
|
234
249
|
|
|
235
250
|
export function getCoderPromptTemplateSimpleEdit(): BasePromptFragment {
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
CommandRegistry,
|
|
30
30
|
MessageService,
|
|
31
31
|
generateUuid,
|
|
32
|
+
nls,
|
|
32
33
|
} from '@theia/core';
|
|
33
34
|
|
|
34
35
|
import { commandTemplate } from './command-prompt-template';
|
|
@@ -55,12 +56,13 @@ export class CommandChatAgent extends AbstractTextToModelParsingChatAgent<Parsed
|
|
|
55
56
|
}];
|
|
56
57
|
protected defaultLanguageModelPurpose: string = 'command';
|
|
57
58
|
|
|
58
|
-
override description = '
|
|
59
|
-
|
|
59
|
+
override description = nls.localize('theia/ai/ide/commandAgent/description',
|
|
60
|
+
'This agent is aware of all commands that the user can execute within the Theia IDE, the tool that the user is currently working with. ' +
|
|
61
|
+
'Based on the user request, it can find the right command and then let the user execute it.');
|
|
60
62
|
override prompts = [commandTemplate];
|
|
61
63
|
override agentSpecificVariables = [{
|
|
62
64
|
name: 'command-ids',
|
|
63
|
-
description: 'The list of available commands in Theia.',
|
|
65
|
+
description: nls.localize('theia/ai/ide/commandAgent/vars/commandIds/description', 'The list of available commands in Theia.'),
|
|
64
66
|
usedInPrompt: true
|
|
65
67
|
}];
|
|
66
68
|
|
|
@@ -107,7 +109,7 @@ export class CommandChatAgent extends AbstractTextToModelParsingChatAgent<Parsed
|
|
|
107
109
|
|
|
108
110
|
return new HorizontalLayoutChatResponseContentImpl([
|
|
109
111
|
new MarkdownChatResponseContentImpl(
|
|
110
|
-
'I found this command that might help you:'
|
|
112
|
+
nls.localize('theia/ai/ide/commandAgent/response/theiaCommand', 'I found this command that might help you:')
|
|
111
113
|
),
|
|
112
114
|
new CommandChatResponseContentImpl(theiaCommand, undefined, args),
|
|
113
115
|
]);
|
|
@@ -116,22 +118,24 @@ export class CommandChatAgent extends AbstractTextToModelParsingChatAgent<Parsed
|
|
|
116
118
|
const commandArgs = parsedCommand.arguments !== undefined && parsedCommand.arguments.length > 0 ? parsedCommand.arguments : [];
|
|
117
119
|
const args = [id, ...commandArgs];
|
|
118
120
|
const customCallback: CustomCallback = {
|
|
119
|
-
label: 'AI command',
|
|
121
|
+
label: nls.localize('theia/ai/ide/commandAgent/commandCallback/label', 'AI command'),
|
|
120
122
|
callback: () => this.commandCallback(...args),
|
|
121
123
|
};
|
|
122
124
|
return new HorizontalLayoutChatResponseContentImpl([
|
|
123
125
|
new MarkdownChatResponseContentImpl(
|
|
124
|
-
'Try executing this:'
|
|
126
|
+
nls.localize('theia/ai/ide/commandAgent/response/customHandler', 'Try executing this:')
|
|
125
127
|
),
|
|
126
128
|
new CommandChatResponseContentImpl(undefined, customCallback, args),
|
|
127
129
|
]);
|
|
128
130
|
} else {
|
|
129
|
-
return new MarkdownChatResponseContentImpl(parsedCommand.message ?? '
|
|
131
|
+
return new MarkdownChatResponseContentImpl(parsedCommand.message ?? nls.localize('theia/ai/ide/commandAgent/response/noCommand',
|
|
132
|
+
'Sorry, I can\'t find such a command'));
|
|
130
133
|
}
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
protected async commandCallback(...commandArgs: unknown[]): Promise<void> {
|
|
134
|
-
this.messageService.info(
|
|
135
|
-
|
|
137
|
+
this.messageService.info(nls.localize('theia/ai/ide/commandAgent/commandCallback/message',
|
|
138
|
+
'Executing callback with args {0}. The first arg is the command id registered for the dynamically registered command. ' +
|
|
139
|
+
'The other args are the actual args for the handler.', commandArgs.join(', ')), nls.localize('theia/ai/ide/commandAgent/commandCallback/confirmAction', 'Got it'));
|
|
136
140
|
}
|
|
137
141
|
}
|
|
@@ -20,3 +20,4 @@ export const SUGGEST_FILE_REPLACEMENTS_ID = 'suggestFileReplacements';
|
|
|
20
20
|
export const WRITE_FILE_REPLACEMENTS_ID = 'writeFileReplacements';
|
|
21
21
|
export const CLEAR_FILE_CHANGES_ID = 'clearFileChanges';
|
|
22
22
|
export const GET_PROPOSED_CHANGES_ID = 'getProposedFileState';
|
|
23
|
+
export const SUGGEST_FILE_REPLACEMENTS_NEXT_ID = 'suggestFileReplacements_Next';
|
|
@@ -0,0 +1,32 @@
|
|
|
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
|
+
export const GitHubRepoService = Symbol('GitHubRepoService');
|
|
18
|
+
export const githubRepoServicePath = '/services/github-repo';
|
|
19
|
+
|
|
20
|
+
export interface GitHubRepoInfo {
|
|
21
|
+
owner: string;
|
|
22
|
+
repo: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface GitHubRepoService {
|
|
26
|
+
/**
|
|
27
|
+
* Gets the GitHub repository information for the given workspace path.
|
|
28
|
+
* @param workspacePath The absolute path to the workspace directory
|
|
29
|
+
* @returns GitHub repository info (owner/repo) or undefined if not a GitHub repository
|
|
30
|
+
*/
|
|
31
|
+
getGitHubRepoInfo(workspacePath: string): Promise<GitHubRepoInfo | undefined>;
|
|
32
|
+
}
|
|
@@ -50,7 +50,7 @@ export class OrchestratorChatAgent extends AbstractStreamParsingChatAgent {
|
|
|
50
50
|
protected chatAgentService: ChatAgentService;
|
|
51
51
|
|
|
52
52
|
override async invoke(request: MutableChatRequestModel): Promise<void> {
|
|
53
|
-
request.response.addProgressMessage({ content: 'Determining the most appropriate agent', status: 'inProgress' });
|
|
53
|
+
request.response.addProgressMessage({ content: nls.localize('theia/ai/ide/orchestrator/progressMessage', 'Determining the most appropriate agent'), status: 'inProgress' });
|
|
54
54
|
// We use a dedicated id for the orchestrator request
|
|
55
55
|
const orchestratorRequestId = generateUuid();
|
|
56
56
|
request.addData(OrchestratorRequestIdKey, orchestratorRequestId);
|
|
@@ -114,15 +114,16 @@ export class OrchestratorChatAgent extends AbstractStreamParsingChatAgent {
|
|
|
114
114
|
if (firstRegisteredAgent) {
|
|
115
115
|
agentIds = [firstRegisteredAgent];
|
|
116
116
|
} else {
|
|
117
|
-
throw new Error(
|
|
117
|
+
throw new Error(nls.localize('theia/ai/ide/orchestrator/error/noAgents',
|
|
118
|
+
'No chat agent available to handle request. Please check your configuration whether any are enabled.'));
|
|
118
119
|
}
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
// TODO support delegating to more than one agent
|
|
122
123
|
const delegatedToAgent = agentIds[0];
|
|
123
124
|
request.response.response.addContent(new InformationalChatResponseContentImpl(
|
|
124
|
-
`*Orchestrator*: Delegating to
|
|
125
|
-
|
|
125
|
+
`*Orchestrator*: ${nls.localize('theia/ai/ide/orchestrator/response/delegatingToAgent', 'Delegating to \`@{0}\`', delegatedToAgent)}
|
|
126
|
+
|
|
126
127
|
---
|
|
127
128
|
|
|
128
129
|
`
|
|
@@ -34,13 +34,13 @@ export const WorkspacePreferencesSchema: PreferenceSchema = {
|
|
|
34
34
|
title: nls.localize('theia/ai/workspace/considerGitignore/title', 'Consider .gitignore'),
|
|
35
35
|
description: nls.localize('theia/ai/workspace/considerGitignore/description',
|
|
36
36
|
'If enabled, excludes files/folders specified in a global .gitignore file (expected location is the workspace root).'),
|
|
37
|
-
default:
|
|
37
|
+
default: true
|
|
38
38
|
},
|
|
39
39
|
[USER_EXCLUDE_PATTERN_PREF]: {
|
|
40
40
|
type: 'array',
|
|
41
41
|
title: nls.localize('theia/ai/workspace/excludedPattern/title', 'Excluded File Patterns'),
|
|
42
42
|
description: nls.localize('theia/ai/workspace/excludedPattern/description', 'List of patterns (glob or regex) for files/folders to exclude.'),
|
|
43
|
-
default: ['node_modules', 'lib'
|
|
43
|
+
default: ['node_modules', 'lib'],
|
|
44
44
|
items: {
|
|
45
45
|
type: 'string'
|
|
46
46
|
}
|
|
@@ -22,6 +22,8 @@ import { ConnectionContainerModule } from '@theia/core/lib/node/messaging/connec
|
|
|
22
22
|
import { WorkspacePreferencesSchema } from '../common/workspace-preferences';
|
|
23
23
|
import { AiConfigurationPreferences } from '../common/ai-configuration-preferences';
|
|
24
24
|
import { aiIdePreferenceSchema } from '../common/ai-ide-preferences';
|
|
25
|
+
import { GitHubRepoService, githubRepoServicePath } from '../common/github-repo-protocol';
|
|
26
|
+
import { GitHubRepoServiceImpl } from './github-repo-service-impl';
|
|
25
27
|
|
|
26
28
|
const browserAutomationModule = ConnectionContainerModule.create(({ bind, bindBackendService, bindFrontendService }) => {
|
|
27
29
|
bind(BrowserAutomation).to(BrowserAutomationImpl).inSingletonScope();
|
|
@@ -42,4 +44,9 @@ export default new ContainerModule(bind => {
|
|
|
42
44
|
|
|
43
45
|
bind(ConnectionContainerModule).toConstantValue(browserAutomationModule);
|
|
44
46
|
|
|
47
|
+
bind(GitHubRepoService).to(GitHubRepoServiceImpl).inSingletonScope();
|
|
48
|
+
bind(ConnectionHandler).toDynamicValue(ctx =>
|
|
49
|
+
new RpcConnectionHandler(githubRepoServicePath, () => ctx.container.get<GitHubRepoService>(GitHubRepoService))
|
|
50
|
+
).inSingletonScope();
|
|
51
|
+
|
|
45
52
|
});
|
|
@@ -0,0 +1,98 @@
|
|
|
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 { injectable } from '@theia/core/shared/inversify';
|
|
18
|
+
import { simpleGit, SimpleGit } from 'simple-git';
|
|
19
|
+
import { GitHubRepoService, GitHubRepoInfo } from '../common/github-repo-protocol';
|
|
20
|
+
|
|
21
|
+
@injectable()
|
|
22
|
+
export class GitHubRepoServiceImpl implements GitHubRepoService {
|
|
23
|
+
|
|
24
|
+
async getGitHubRepoInfo(workspacePath: string): Promise<GitHubRepoInfo | undefined> {
|
|
25
|
+
try {
|
|
26
|
+
// Initialize simple-git with the workspace path
|
|
27
|
+
const git: SimpleGit = simpleGit(workspacePath);
|
|
28
|
+
|
|
29
|
+
// Check if this is a git repository
|
|
30
|
+
const isRepo = await git.checkIsRepo();
|
|
31
|
+
if (!isRepo) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Get all remotes with their URLs
|
|
36
|
+
const remotes = await git.getRemotes(true);
|
|
37
|
+
|
|
38
|
+
if (remotes.length === 0) {
|
|
39
|
+
return undefined;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Find GitHub remote (prefer 'origin', then any GitHub remote)
|
|
43
|
+
const githubRemote = remotes.find(remote =>
|
|
44
|
+
remote.name === 'origin' && this.isGitHubRemote(remote.refs.fetch || remote.refs.push || '')
|
|
45
|
+
) || remotes.find(remote =>
|
|
46
|
+
this.isGitHubRemote(remote.refs.fetch || remote.refs.push || '')
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (!githubRemote) {
|
|
50
|
+
return undefined;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const remoteUrl = githubRemote.refs.fetch || githubRemote.refs.push || '';
|
|
54
|
+
const repoInfo = this.extractRepoInfoFromGitHubUrl(remoteUrl);
|
|
55
|
+
|
|
56
|
+
return repoInfo;
|
|
57
|
+
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.warn('Failed to get GitHub repository info:', error);
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private isGitHubRemote(remoteUrl: string): boolean {
|
|
65
|
+
return remoteUrl.includes('github.com');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private extractRepoInfoFromGitHubUrl(url: string): GitHubRepoInfo | undefined {
|
|
69
|
+
// Handle HTTPS URLs: https://github.com/owner/repo or https://github.com/owner/repo.git
|
|
70
|
+
const httpsMatch = url.match(/https:\/\/github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?$/);
|
|
71
|
+
if (httpsMatch) {
|
|
72
|
+
return {
|
|
73
|
+
owner: httpsMatch[1],
|
|
74
|
+
repo: httpsMatch[2]
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Handle SSH URLs: git@github.com:owner/repo or git@github.com:owner/repo.git
|
|
79
|
+
const sshMatch = url.match(/git@github\.com:([^\/]+)\/([^\/]+?)(?:\.git)?$/);
|
|
80
|
+
if (sshMatch) {
|
|
81
|
+
return {
|
|
82
|
+
owner: sshMatch[1],
|
|
83
|
+
repo: sshMatch[2]
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Handle alternative SSH format: ssh://git@github.com/owner/repo or ssh://git@github.com/owner/repo.git
|
|
88
|
+
const sshAltMatch = url.match(/ssh:\/\/git@github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?$/);
|
|
89
|
+
if (sshAltMatch) {
|
|
90
|
+
return {
|
|
91
|
+
owner: sshAltMatch[1],
|
|
92
|
+
repo: sshAltMatch[2]
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return undefined;
|
|
97
|
+
}
|
|
98
|
+
}
|