@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.
Files changed (97) hide show
  1. package/lib/browser/ai-configuration/agent-configuration-widget.d.ts.map +1 -1
  2. package/lib/browser/ai-configuration/agent-configuration-widget.js +5 -3
  3. package/lib/browser/ai-configuration/agent-configuration-widget.js.map +1 -1
  4. package/lib/browser/ai-configuration/language-model-renderer.d.ts.map +1 -1
  5. package/lib/browser/ai-configuration/language-model-renderer.js +4 -3
  6. package/lib/browser/ai-configuration/language-model-renderer.js.map +1 -1
  7. package/lib/browser/ai-configuration/model-aliases-configuration-widget.d.ts.map +1 -1
  8. package/lib/browser/ai-configuration/model-aliases-configuration-widget.js +4 -3
  9. package/lib/browser/ai-configuration/model-aliases-configuration-widget.js.map +1 -1
  10. package/lib/browser/ai-configuration/template-settings-renderer.js +1 -1
  11. package/lib/browser/ai-configuration/template-settings-renderer.js.map +1 -1
  12. package/lib/browser/ai-configuration/token-usage-configuration-widget.js +1 -1
  13. package/lib/browser/ai-configuration/token-usage-configuration-widget.js.map +1 -1
  14. package/lib/browser/ai-configuration/tools-configuration-widget.d.ts +1 -1
  15. package/lib/browser/ai-configuration/tools-configuration-widget.d.ts.map +1 -1
  16. package/lib/browser/ai-configuration/tools-configuration-widget.js +12 -12
  17. package/lib/browser/ai-configuration/tools-configuration-widget.js.map +1 -1
  18. package/lib/browser/ai-configuration/variable-configuration-widget.d.ts +1 -1
  19. package/lib/browser/ai-configuration/variable-configuration-widget.d.ts.map +1 -1
  20. package/lib/browser/ai-configuration/variable-configuration-widget.js +4 -3
  21. package/lib/browser/ai-configuration/variable-configuration-widget.js.map +1 -1
  22. package/lib/browser/app-tester-chat-agent.d.ts.map +1 -1
  23. package/lib/browser/app-tester-chat-agent.js +10 -7
  24. package/lib/browser/app-tester-chat-agent.js.map +1 -1
  25. package/lib/browser/architect-agent.d.ts.map +1 -1
  26. package/lib/browser/architect-agent.js +2 -2
  27. package/lib/browser/architect-agent.js.map +1 -1
  28. package/lib/browser/coder-agent.d.ts.map +1 -1
  29. package/lib/browser/coder-agent.js +6 -5
  30. package/lib/browser/coder-agent.js.map +1 -1
  31. package/lib/browser/file-changeset-functions.d.ts +10 -0
  32. package/lib/browser/file-changeset-functions.d.ts.map +1 -1
  33. package/lib/browser/file-changeset-functions.js +51 -3
  34. package/lib/browser/file-changeset-functions.js.map +1 -1
  35. package/lib/browser/file-changeset-functions.spec.js +18 -0
  36. package/lib/browser/file-changeset-functions.spec.js.map +1 -1
  37. package/lib/browser/frontend-module.d.ts.map +1 -1
  38. package/lib/browser/frontend-module.js +7 -0
  39. package/lib/browser/frontend-module.js.map +1 -1
  40. package/lib/browser/github-chat-agent.d.ts.map +1 -1
  41. package/lib/browser/github-chat-agent.js +18 -16
  42. package/lib/browser/github-chat-agent.js.map +1 -1
  43. package/lib/browser/github-repo-variable-contribution.d.ts +8 -10
  44. package/lib/browser/github-repo-variable-contribution.d.ts.map +1 -1
  45. package/lib/browser/github-repo-variable-contribution.js +22 -41
  46. package/lib/browser/github-repo-variable-contribution.js.map +1 -1
  47. package/lib/common/coder-replace-prompt-template.d.ts +2 -0
  48. package/lib/common/coder-replace-prompt-template.d.ts.map +1 -1
  49. package/lib/common/coder-replace-prompt-template.js +20 -8
  50. package/lib/common/coder-replace-prompt-template.js.map +1 -1
  51. package/lib/common/command-chat-agents.d.ts.map +1 -1
  52. package/lib/common/command-chat-agents.js +9 -9
  53. package/lib/common/command-chat-agents.js.map +1 -1
  54. package/lib/common/file-changeset-function-ids.d.ts +1 -0
  55. package/lib/common/file-changeset-function-ids.d.ts.map +1 -1
  56. package/lib/common/file-changeset-function-ids.js +2 -1
  57. package/lib/common/file-changeset-function-ids.js.map +1 -1
  58. package/lib/common/github-repo-protocol.d.ts +15 -0
  59. package/lib/common/github-repo-protocol.d.ts.map +1 -0
  60. package/lib/common/github-repo-protocol.js +21 -0
  61. package/lib/common/github-repo-protocol.js.map +1 -0
  62. package/lib/common/orchestrator-chat-agent.d.ts.map +1 -1
  63. package/lib/common/orchestrator-chat-agent.js +4 -4
  64. package/lib/common/orchestrator-chat-agent.js.map +1 -1
  65. package/lib/common/workspace-preferences.js +2 -2
  66. package/lib/common/workspace-preferences.js.map +1 -1
  67. package/lib/node/backend-module.d.ts.map +1 -1
  68. package/lib/node/backend-module.js +4 -0
  69. package/lib/node/backend-module.js.map +1 -1
  70. package/lib/node/github-repo-service-impl.d.ts +7 -0
  71. package/lib/node/github-repo-service-impl.d.ts.map +1 -0
  72. package/lib/node/github-repo-service-impl.js +86 -0
  73. package/lib/node/github-repo-service-impl.js.map +1 -0
  74. package/package.json +23 -23
  75. package/src/browser/ai-configuration/agent-configuration-widget.tsx +5 -3
  76. package/src/browser/ai-configuration/language-model-renderer.tsx +9 -4
  77. package/src/browser/ai-configuration/model-aliases-configuration-widget.tsx +8 -4
  78. package/src/browser/ai-configuration/template-settings-renderer.tsx +1 -1
  79. package/src/browser/ai-configuration/token-usage-configuration-widget.tsx +1 -1
  80. package/src/browser/ai-configuration/tools-configuration-widget.tsx +15 -14
  81. package/src/browser/ai-configuration/variable-configuration-widget.tsx +4 -3
  82. package/src/browser/app-tester-chat-agent.ts +15 -7
  83. package/src/browser/architect-agent.ts +4 -2
  84. package/src/browser/coder-agent.ts +10 -6
  85. package/src/browser/file-changeset-functions.spec.ts +28 -1
  86. package/src/browser/file-changeset-functions.ts +47 -3
  87. package/src/browser/frontend-module.ts +11 -1
  88. package/src/browser/github-chat-agent.ts +23 -17
  89. package/src/browser/github-repo-variable-contribution.ts +23 -50
  90. package/src/common/coder-replace-prompt-template.ts +23 -8
  91. package/src/common/command-chat-agents.ts +13 -9
  92. package/src/common/file-changeset-function-ids.ts +1 -0
  93. package/src/common/github-repo-protocol.ts +32 -0
  94. package/src/common/orchestrator-chat-agent.ts +5 -4
  95. package/src/common/workspace-preferences.ts +2 -2
  96. package/src/node/backend-module.ts +7 -0
  97. 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/chat/github/description', 'This agent helps you interact with GitHub repositories, issues, pull requests, and other GitHub '
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('GitHub server configuration cancelled.'
92
- + ' Please configure the GitHub MCP server to use this agent.'));
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({ content: 'Starting GitHub MCP server.', show: 'whileIncomplete' });
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: ' + (error instanceof Error ? error.message : String(error)))
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('Please start the GitHub MCP server to use this agent.'));
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: ' + (error instanceof Error ? error.message : String(error)))
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 { ScmService } from '@theia/scm/lib/browser/scm-service';
29
- import { GitRepositoryProvider } from '@theia/git/lib/browser/git-repository-provider';
30
- import { Git } from '@theia/git/lib/common';
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(ScmService)
42
- protected readonly scmService: ScmService;
42
+ @inject(WorkspaceService)
43
+ protected readonly workspaceService: WorkspaceService;
43
44
 
44
- @inject(GitRepositoryProvider)
45
- protected readonly repositoryProvider: GitRepositoryProvider;
45
+ @inject(FileService)
46
+ protected readonly fileService: FileService;
46
47
 
47
- @inject(Git)
48
- protected readonly git: Git;
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, context: AIVariableContext): MaybePromise<number> {
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, context: AIVariableContext): Promise<ResolvedAIVariable | undefined> {
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 remotes = await this.git.remote(repository, { verbose: true });
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
- const repoName = this.extractRepoNameFromGitHubUrl(githubRemote.fetch);
90
- if (!repoName) {
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
- export function getCoderPromptTemplateEdit(): BasePromptFragment {
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
- - ~{${SUGGEST_FILE_REPLACEMENTS_ID}}: For targeted replacements of specific text sections. Multiple calls will merge changes unless you set the reset parameter to true.
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 ~{${SUGGEST_FILE_REPLACEMENTS_ID}} continuously fails use ~{${SUGGEST_FILE_CONTENT_ID}}.
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 = '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. \
59
- Based on the user request, it can find the right command and then let the user execute it.';
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 ?? 'Sorry, I can\'t find such a command');
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(`Executing callback with args ${commandArgs.join(', ')}. The first arg is the command id registered for the dynamically registered command. \
135
- The other args are the actual args for the handler.`, 'Got it');
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('No chat agent available to handle request. Please check your configuration whether any are enabled.');
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 \`@${delegatedToAgent}\`
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: false
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
+ }