@theia/ai-ide 1.63.0-next.24 → 1.63.0-next.52

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 (69) hide show
  1. package/lib/browser/ai-configuration/ai-configuration-widget.d.ts +0 -1
  2. package/lib/browser/ai-configuration/ai-configuration-widget.d.ts.map +1 -1
  3. package/lib/browser/ai-configuration/ai-configuration-widget.js +0 -1
  4. package/lib/browser/ai-configuration/ai-configuration-widget.js.map +1 -1
  5. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts +0 -1
  6. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.d.ts.map +1 -1
  7. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js +0 -1
  8. package/lib/browser/ai-configuration/prompt-fragments-configuration-widget.js.map +1 -1
  9. package/lib/browser/ai-configuration/tools-configuration-widget.d.ts +0 -1
  10. package/lib/browser/ai-configuration/tools-configuration-widget.d.ts.map +1 -1
  11. package/lib/browser/ai-configuration/tools-configuration-widget.js +6 -21
  12. package/lib/browser/ai-configuration/tools-configuration-widget.js.map +1 -1
  13. package/lib/browser/architect-agent.d.ts.map +1 -1
  14. package/lib/browser/architect-agent.js +4 -1
  15. package/lib/browser/architect-agent.js.map +1 -1
  16. package/lib/browser/frontend-module.d.ts +1 -0
  17. package/lib/browser/frontend-module.d.ts.map +1 -1
  18. package/lib/browser/frontend-module.js +1 -0
  19. package/lib/browser/frontend-module.js.map +1 -1
  20. package/lib/browser/summarize-session-command-contribution.d.ts +6 -1
  21. package/lib/browser/summarize-session-command-contribution.d.ts.map +1 -1
  22. package/lib/browser/summarize-session-command-contribution.js +53 -4
  23. package/lib/browser/summarize-session-command-contribution.js.map +1 -1
  24. package/lib/browser/task-context-file-storage-service.d.ts +4 -2
  25. package/lib/browser/task-context-file-storage-service.d.ts.map +1 -1
  26. package/lib/browser/task-context-file-storage-service.js +19 -9
  27. package/lib/browser/task-context-file-storage-service.js.map +1 -1
  28. package/lib/browser/workspace-preferences.d.ts +1 -0
  29. package/lib/browser/workspace-preferences.d.ts.map +1 -1
  30. package/lib/browser/workspace-preferences.js +9 -1
  31. package/lib/browser/workspace-preferences.js.map +1 -1
  32. package/lib/browser/workspace-search-provider.d.ts +5 -1
  33. package/lib/browser/workspace-search-provider.d.ts.map +1 -1
  34. package/lib/browser/workspace-search-provider.js +57 -17
  35. package/lib/browser/workspace-search-provider.js.map +1 -1
  36. package/lib/browser/workspace-search-provider.spec.d.ts +2 -0
  37. package/lib/browser/workspace-search-provider.spec.d.ts.map +1 -0
  38. package/lib/browser/workspace-search-provider.spec.js +227 -0
  39. package/lib/browser/workspace-search-provider.spec.js.map +1 -0
  40. package/lib/common/architect-prompt-template.d.ts +1 -0
  41. package/lib/common/architect-prompt-template.d.ts.map +1 -1
  42. package/lib/common/architect-prompt-template.js +166 -5
  43. package/lib/common/architect-prompt-template.js.map +1 -1
  44. package/lib/common/coder-replace-prompt-template.js +2 -2
  45. package/lib/common/coder-replace-prompt-template.js.map +1 -1
  46. package/lib/common/summarize-session-commands.d.ts +1 -0
  47. package/lib/common/summarize-session-commands.d.ts.map +1 -1
  48. package/lib/common/summarize-session-commands.js +5 -1
  49. package/lib/common/summarize-session-commands.js.map +1 -1
  50. package/lib/common/workspace-search-provider-util.d.ts +17 -0
  51. package/lib/common/workspace-search-provider-util.d.ts.map +1 -0
  52. package/lib/common/workspace-search-provider-util.js +51 -0
  53. package/lib/common/workspace-search-provider-util.js.map +1 -0
  54. package/package.json +19 -18
  55. package/src/browser/ai-configuration/ai-configuration-widget.tsx +0 -1
  56. package/src/browser/ai-configuration/prompt-fragments-configuration-widget.tsx +0 -1
  57. package/src/browser/ai-configuration/tools-configuration-widget.tsx +8 -23
  58. package/src/browser/architect-agent.ts +5 -2
  59. package/src/browser/frontend-module.ts +2 -0
  60. package/src/browser/style/index.css +23 -28
  61. package/src/browser/summarize-session-command-contribution.ts +58 -6
  62. package/src/browser/task-context-file-storage-service.ts +20 -10
  63. package/src/browser/workspace-preferences.ts +9 -0
  64. package/src/browser/workspace-search-provider.spec.ts +255 -0
  65. package/src/browser/workspace-search-provider.ts +62 -16
  66. package/src/common/architect-prompt-template.ts +165 -5
  67. package/src/common/coder-replace-prompt-template.ts +2 -2
  68. package/src/common/summarize-session-commands.ts +5 -0
  69. package/src/common/workspace-search-provider-util.ts +50 -0
@@ -16,12 +16,16 @@
16
16
 
17
17
  import { MutableChatRequestModel } from '@theia/ai-chat';
18
18
  import { ToolProvider, ToolRequest } from '@theia/ai-core';
19
- import { CancellationToken, URI } from '@theia/core';
19
+ import { CancellationToken } from '@theia/core';
20
+ import { PreferenceService } from '@theia/core/lib/browser/preferences/preference-service';
20
21
  import { inject, injectable } from '@theia/core/shared/inversify';
22
+ import { FileService } from '@theia/filesystem/lib/browser/file-service';
21
23
  import { SearchInWorkspaceService, SearchInWorkspaceCallbacks } from '@theia/search-in-workspace/lib/browser/search-in-workspace-service';
22
24
  import { SearchInWorkspaceResult, SearchInWorkspaceOptions } from '@theia/search-in-workspace/lib/common/search-in-workspace-interface';
23
25
  import { SEARCH_IN_WORKSPACE_FUNCTION_ID } from '../common/workspace-functions';
24
26
  import { WorkspaceFunctionScope } from './workspace-functions';
27
+ import { SEARCH_IN_WORKSPACE_MAX_RESULTS_PREF } from './workspace-preferences';
28
+ import { optimizeSearchResults } from '../common/workspace-search-provider-util';
25
29
 
26
30
  @injectable()
27
31
  export class WorkspaceSearchProvider implements ToolProvider {
@@ -32,7 +36,11 @@ export class WorkspaceSearchProvider implements ToolProvider {
32
36
  @inject(WorkspaceFunctionScope)
33
37
  protected readonly workspaceScope: WorkspaceFunctionScope;
34
38
 
35
- private readonly MAX_RESULTS = 50;
39
+ @inject(PreferenceService)
40
+ protected readonly preferenceService: PreferenceService;
41
+
42
+ @inject(FileService)
43
+ protected readonly fileService: FileService;
36
44
 
37
45
  getTool(): ToolRequest {
38
46
  return {
@@ -42,7 +50,7 @@ export class WorkspaceSearchProvider implements ToolProvider {
42
50
  The search uses case-insensitive string matching or regular expressions (controlled by the `useRegExp` parameter). \
43
51
  It returns a list of matching files, including the file path (URI), the line number, and the full text content of each matching line. \
44
52
  Multi-word patterns must match exactly (including spaces, case-insensitively). \
45
- For best results, use specific search terms and consider filtering by file extensions to avoid overwhelming results. \
53
+ For best results, use specific search terms and consider filtering by file extensions or limiting to specific subdirectories to avoid overwhelming results. \
46
54
  For complex searches, prefer multiple simpler queries over one complex query or regular expression.',
47
55
  parameters: {
48
56
  type: 'object',
@@ -61,6 +69,11 @@ export class WorkspaceSearchProvider implements ToolProvider {
61
69
  type: 'string'
62
70
  },
63
71
  description: 'Optional array of file extensions to search in (e.g., ["ts", "js", "py"]). If not specified, searches all files.'
72
+ },
73
+ subDirectoryPath: {
74
+ type: 'string',
75
+ description: 'Optional subdirectory path to limit search scope. Use relative paths from workspace root ' +
76
+ '(e.g., "packages/ai-ide/src", "packages/core/src/browser"). If not specified, searches entire workspace.'
64
77
  }
65
78
  },
66
79
  required: ['query', 'useRegExp']
@@ -69,14 +82,36 @@ export class WorkspaceSearchProvider implements ToolProvider {
69
82
  };
70
83
  }
71
84
 
85
+ private async determineSearchRoots(subDirectoryPath?: string): Promise<string[]> {
86
+ const workspaceRoot = await this.workspaceScope.getWorkspaceRoot();
87
+
88
+ if (!subDirectoryPath) {
89
+ return [workspaceRoot.toString()];
90
+ }
91
+
92
+ const subDirUri = workspaceRoot.resolve(subDirectoryPath);
93
+ this.workspaceScope.ensureWithinWorkspace(subDirUri, workspaceRoot);
94
+
95
+ try {
96
+ const stat = await this.fileService.resolve(subDirUri);
97
+ if (!stat || !stat.isDirectory) {
98
+ throw new Error(`Subdirectory '${subDirectoryPath}' does not exist or is not a directory`);
99
+ }
100
+ } catch (error) {
101
+ throw new Error(`Invalid subdirectory path '${subDirectoryPath}': ${error.message}`);
102
+ }
103
+
104
+ return [subDirUri.toString()];
105
+ }
106
+
72
107
  private async handleSearch(argString: string, cancellationToken?: CancellationToken): Promise<string> {
73
108
  try {
74
- const args: { query: string, useRegExp: boolean, fileExtensions?: string[] } = JSON.parse(argString);
109
+ const args: { query: string, useRegExp: boolean, fileExtensions?: string[], subDirectoryPath?: string } = JSON.parse(argString);
75
110
  const results: SearchInWorkspaceResult[] = [];
76
111
  let expectedSearchId: number | undefined;
77
112
  let searchCompleted = false;
78
113
 
79
- const searchPromise = new Promise<SearchInWorkspaceResult[]>((resolve, reject) => {
114
+ const searchPromise = new Promise<SearchInWorkspaceResult[]>(async (resolve, reject) => {
80
115
  const callbacks: SearchInWorkspaceCallbacks = {
81
116
  onResult: (id, result) => {
82
117
  if (expectedSearchId !== undefined && id !== expectedSearchId) {
@@ -100,25 +135,28 @@ export class WorkspaceSearchProvider implements ToolProvider {
100
135
 
101
136
  searchCompleted = true;
102
137
  if (error) {
103
- reject(new Error(`Search failed: ${error}`));
138
+ reject(new Error('Search failed: ' + error));
104
139
  } else {
105
140
  resolve(results);
106
141
  }
107
142
  }
108
143
  };
109
144
 
145
+ // Use one more than our actual maximum. this way we can determine if we have more results than our maximum and warn the user
146
+ const maxResultsForTheiaAPI = this.preferenceService.get<number>(SEARCH_IN_WORKSPACE_MAX_RESULTS_PREF, 30) + 1;
110
147
  const options: SearchInWorkspaceOptions = {
111
148
  useRegExp: args.useRegExp,
112
149
  matchCase: false,
113
150
  matchWholeWord: false,
114
- maxResults: this.MAX_RESULTS,
151
+ maxResults: maxResultsForTheiaAPI,
115
152
  };
116
153
 
117
154
  if (args.fileExtensions && args.fileExtensions.length > 0) {
118
155
  options.include = args.fileExtensions.map(ext => `**/*.${ext}`);
119
156
  }
120
157
 
121
- this.searchService.search(args.query, callbacks, options)
158
+ await this.determineSearchRoots(args.subDirectoryPath)
159
+ .then(rootUris => this.searchService.searchWithCallback(args.query, rootUris, callbacks, options))
122
160
  .then(id => {
123
161
  expectedSearchId = id;
124
162
  cancellationToken?.onCancellationRequested(() => {
@@ -143,16 +181,23 @@ export class WorkspaceSearchProvider implements ToolProvider {
143
181
  });
144
182
 
145
183
  const finalResults = await Promise.race([searchPromise, timeoutPromise]);
184
+ const maxResults = this.preferenceService.get<number>(SEARCH_IN_WORKSPACE_MAX_RESULTS_PREF, 30);
146
185
 
147
186
  const workspaceRoot = await this.workspaceScope.getWorkspaceRoot();
148
- const formattedResults = finalResults.map(r => {
149
- const fileUri = new URI(r.fileUri);
150
- const relativePath = workspaceRoot.relative(fileUri);
151
- return {
152
- file: relativePath ? relativePath.toString() : r.fileUri,
153
- matches: r.matches.map(m => ({ line: m.line, text: m.lineText }))
154
- };
155
- });
187
+ const formattedResults = optimizeSearchResults(finalResults, workspaceRoot);
188
+
189
+ let numberOfMatchesInFinalResults = 0;
190
+ for (const result of finalResults) {
191
+ numberOfMatchesInFinalResults += result.matches.length;
192
+ }
193
+ if (numberOfMatchesInFinalResults > maxResults) {
194
+ return JSON.stringify({
195
+ info: 'Search limit exceeded: Found ' + maxResults + '+ results. ' +
196
+ 'Please refine your search with more specific terms or use file extension filters. ' +
197
+ 'You can increase the limit in preferences under \'ai-features.workspaceFunctions.searchMaxResults\'.',
198
+ incompleteResults: formattedResults
199
+ });
200
+ }
156
201
 
157
202
  return JSON.stringify(formattedResults);
158
203
 
@@ -161,3 +206,4 @@ export class WorkspaceSearchProvider implements ToolProvider {
161
206
  }
162
207
  }
163
208
  }
209
+
@@ -17,13 +17,14 @@ import { CONTEXT_FILES_VARIABLE_ID, TASK_CONTEXT_SUMMARY_VARIABLE_ID } from './c
17
17
  import { UPDATE_CONTEXT_FILES_FUNCTION_ID } from './context-functions';
18
18
 
19
19
  export const ARCHITECT_TASK_SUMMARY_PROMPT_TEMPLATE_ID = 'architect-task-summary';
20
+ export const ARCHITECT_TASK_SUMMARY_UPDATE_PROMPT_TEMPLATE_ID = 'architect-update-task-summary';
20
21
 
21
22
  export const architectVariants = <PromptVariantSet>{
22
23
  id: 'architect-system',
23
24
  defaultVariant: {
24
25
  id: 'architect-system-default',
25
26
  template: `{{!-- This prompt is licensed under the MIT License (https://opensource.org/license/mit).
26
- Made improvements or adaptations to this prompt template? Wed love for you to share it with the community! Contribute back here:
27
+ Made improvements or adaptations to this prompt template? We'd love for you to share it with the community! Contribute back here:
27
28
  https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
28
29
  # Instructions
29
30
 
@@ -55,12 +56,13 @@ Always look at the relevant files to understand your task using the function ~{$
55
56
  {
56
57
  id: 'architect-system-next',
57
58
  template: `{{!-- This prompt is licensed under the MIT License (https://opensource.org/license/mit).
58
- Made improvements or adaptations to this prompt template? Wed love for you to share it with the community! Contribute back here:
59
+ Made improvements or adaptations to this prompt template? We'd love for you to share it with the community! Contribute back here:
59
60
  https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
60
61
  # Instructions
61
62
 
62
- You are an AI assistant integrated into Theia IDE, designed to assist software developers. You can't change any files, but you can navigate and read the users workspace using \
63
- the provided functions. Therefore describe and explain the details or procedures necessary to achieve the desired outcome. If file changes are necessary to help the user, be \
63
+ You are an AI assistant integrated into Theia IDE, designed to assist software developers. You can only change the files added to the context, but you can navigate and read the
64
+ users workspace using the provided functions.\
65
+ Therefore describe and explain the details or procedures necessary to achieve the desired outcome. If file changes are necessary to help the user, be \
64
66
  aware that there is another agent called 'Coder' that can suggest file changes. In this case you can create a description on what to do and tell the user to ask '@Coder' to \
65
67
  implement the change plan. If you refer to files, always mention the workspace-relative path.\
66
68
 
@@ -105,7 +107,165 @@ Skip irrelevant information, e.g. for discussions, only sum up the final result.
105
107
  4. If any part of the task is ambiguous, note the ambiguity so that it can be clarified later.
106
108
 
107
109
  Focus on providing actionable steps and implementation guidance. The coding agent needs practical help with this specific coding task.
110
+
111
+ Use the following format, but only include the sections that were discussed in the conversation:
112
+
113
+ # Task Context: [Title Here]
114
+
115
+ ---
116
+
117
+ ## 1. 📚 Task Definition
118
+
119
+ **Problem Statement / Goal:**
120
+ [Describe what needs to be achieved and why.]
121
+
122
+ **Scope:**
123
+ - **In Scope:**
124
+ [Features, components, or behaviors to be included.]
125
+ - **Out of Scope:**
126
+ [What explicitly won't be part of this task.]
127
+
128
+ ---
129
+
130
+ ## 2. 🧠 Design and Implementation
131
+
132
+ **Design Overview:**
133
+ [Summary of architecture and major design decisions.]
134
+
135
+ **Implementation Plan:**
136
+ 1. [First major step]
137
+ 2. [Second major step]
138
+ 3. [Third major step]
139
+
140
+ **Technology Choices:**
141
+ - [Frameworks, libraries, services, tools]
142
+
143
+ ---
144
+
145
+ ## 3. 🧪 Testing
146
+
147
+ ### 3.1 🛠️ Automated Testing (by Coder)
148
+
149
+ **Automated Test Strategy:**
150
+ [What should be covered by automated tests.]
151
+
152
+ **Test Cases Implemented:**
153
+ - [Unit test 1]
154
+ - [Integration test 1]
155
+ - [E2E test 1]
156
+
157
+ **Test Coverage Targets:**
158
+ [e.g., Minimum 80% code coverage, all workflows tested.]
159
+
160
+ ---
161
+
162
+ ### 3.2 🎯 Manual Testing (by Tester)
163
+
164
+ **Manual Testing Strategy:**
165
+ [What manual tests will focus on (e.g., usability, edge cases, exploratory testing).]
166
+
167
+ **Test Setup Instructions:**
168
+ - [Environment setup steps, accounts needed, special configurations]
169
+
170
+ **Test Cases / Test Steps:**
171
+ 1. [Action 1]
172
+ 2. [Action 2]
173
+ 3. [Action 3]
174
+
175
+ **Expected Results:**
176
+ - [Expected behavior at each step]
177
+
178
+ **Known Risks / Focus Areas:**
179
+ - [Potential weak spots, UX concerns, edge cases]
180
+
181
+ ---
182
+
183
+ ## 4. 📦 Deliverables
184
+
185
+ **Expected Artifacts:**
186
+ - [Code modules]
187
+ - [Documentation]
188
+ - [Configuration files]
189
+ - [Test reports]
190
+
191
+ **PR Information:**
192
+ - **PR Title:** [Suggested title for the pull request]
193
+ - **PR Description:** [What was implemented, high-level changes, decisions]
194
+ - **Verification Steps:** [Instructions for verifying the PR manually or automatically]
195
+
196
+ **Additional Notes:**
197
+ - [Dependencies]
198
+ - [Migration steps if needed]
199
+ - [Special reviewer instructions]
200
+
201
+ ---
202
+
203
+ ## 5. 🔄 Current Status
204
+
205
+ **Progress Summary:**
206
+ [Short free-text update about how far the task has progressed.]
207
+
208
+ **Completed Items:**
209
+ - [List of what has been fully implemented, tested, or merged.]
210
+
211
+ **Open Items:**
212
+ - [List of remaining tasks, missing parts.]
213
+
214
+ **Current Issues / Risks:**
215
+ - [Open problems, bugs found during testing, architectural blockers.]
216
+
217
+ **Next Steps:**
218
+ - [Immediate action items, who should act next.]
108
219
  `
109
- }
220
+ },
221
+ {
222
+ id: ARCHITECT_TASK_SUMMARY_UPDATE_PROMPT_TEMPLATE_ID,
223
+ template: `{{!-- This prompt is licensed under the MIT License (https://opensource.org/license/mit).
224
+ Made improvements or adaptations to this prompt template? We'd love for you to share it with the community! Contribute back here:
225
+ https://github.com/eclipse-theia/theia/discussions/new?category=prompt-template-contribution --}}
226
+ You are an AI assistant integrated into Theia IDE, designed to update task context files. You can interact provided task context file and propose changes.
227
+
228
+ # Task Document Update Instructions
229
+
230
+ You are an AI agent tasked with updating a technical document based on the current discussion. Your job is to provide the COMPLETE UPDATED DOCUMENT as your response, not\
231
+ commentary about the document.
232
+
233
+ ## Analysis Requirements
234
+
235
+ 1. **Review the Current Discussion**:
236
+ - Analyze the entire conversation
237
+ - Identify new information, decisions, and changes
238
+
239
+ 2. **Examine the Existing Document**:
240
+ - Understand its structure and purpose
241
+ - Identify sections that need updates
242
+
243
+ 3. **Update the Document**:
244
+ - Maintain the original structure and formatting
245
+ - Add new information from the discussion
246
+ - Update existing information
247
+ - Remove outdated information if necessary
248
+ - Ensure coherence and organization
249
+
250
+ ## IMPORTANT: Response Format
251
+
252
+ YOUR ENTIRE RESPONSE MUST BE THE UPDATED DOCUMENT ONLY. Do not include:
253
+ - Any commentary about what you changed
254
+ - Introduction or explanation text
255
+ - Markdown fences or syntax indicators
256
+ - Clarifying questions
257
+
258
+ Simply output the complete updated document as plain text, which will directly replace the existing document.
259
+
260
+ ## Guidelines
261
+
262
+ - Be thorough in capturing all relevant information
263
+ - Maintain the original document's style and tone
264
+ - Use clear, concise language
265
+ - Preserve all formatting from the original document
266
+ - Ensure technical accuracy in all updates
267
+
268
+ {{${TASK_CONTEXT_SUMMARY_VARIABLE_ID}}}
269
+ ` }
110
270
  ]
111
271
  };
@@ -85,9 +85,9 @@ search for files you already know the path for)
85
85
  - Use:
86
86
  - ~{${WRITE_FILE_REPLACEMENTS_ID}} — to immediately apply targeted code changes (no user review)
87
87
  - ~{${WRITE_FILE_CONTENT_ID}} — to immediately overwrite a file with new content (no user review)
88
- - ~{${CLEAR_FILE_CHANGES_ID}} — clear all pending changes for a file
88
+
89
89
  - For incremental changes, use multiple ~{${WRITE_FILE_REPLACEMENTS_ID}} calls
90
- - Use the reset parameter with ~{${WRITE_FILE_REPLACEMENTS_ID}} to clear previous changes
90
+ - If ~{${WRITE_FILE_REPLACEMENTS_ID}} continuously fails use ~{${WRITE_FILE_CONTENT_ID}}.
91
91
 
92
92
  ### Validation
93
93
  - ~{${GET_FILE_DIAGNOSTICS_ID}} — detect syntax, lint, or type errors
@@ -20,3 +20,8 @@ export const AI_SUMMARIZE_SESSION_AS_TASK_FOR_CODER = Command.toLocalizedCommand
20
20
  id: 'ai-chat:summarize-session-as-task-for-coder',
21
21
  label: 'Summarize Session as Task for Coder'
22
22
  });
23
+
24
+ export const AI_UPDATE_TASK_CONTEXT_COMMAND = Command.toLocalizedCommand({
25
+ id: 'ai.updateTaskContext',
26
+ label: 'Update Current Task Context'
27
+ });
@@ -0,0 +1,50 @@
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 { LinePreview, SearchInWorkspaceResult } from '@theia/search-in-workspace/lib/common/search-in-workspace-interface';
18
+ import { URI } from '@theia/core';
19
+
20
+ /**
21
+ * Optimizes search results for token efficiency while preserving all information.
22
+ * - Groups matches by file to reduce repetition
23
+ * - Trims leading/trailing whitespace from line text
24
+ * - Uses relative file paths
25
+ * - Preserves all line numbers and content
26
+ */
27
+ export function optimizeSearchResults(results: SearchInWorkspaceResult[], workspaceRoot: URI): Array<{ file: string; matches: Array<{ line: number; text: string }> }> {
28
+ return results.map(result => {
29
+ const fileUri = new URI(result.fileUri);
30
+ const relativePath = workspaceRoot.relative(fileUri);
31
+
32
+ return {
33
+ file: relativePath ? relativePath.toString() : result.fileUri,
34
+ matches: result.matches.map(match => {
35
+ let lineText: string;
36
+ if (typeof match.lineText === 'string') {
37
+ lineText = match.lineText;
38
+ } else {
39
+ const linePreview = match.lineText as LinePreview;
40
+ lineText = linePreview.text || '';
41
+ }
42
+
43
+ return {
44
+ line: match.line,
45
+ text: lineText.trim()
46
+ };
47
+ })
48
+ };
49
+ });
50
+ }