@ddse/acm-aicoder 0.5.0 → 0.5.2
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/dist/src/capability-map.d.ts +4 -0
- package/dist/src/capability-map.d.ts.map +1 -0
- package/dist/src/capability-map.js +289 -0
- package/dist/src/capability-map.js.map +1 -0
- package/dist/src/registries.d.ts +3 -0
- package/dist/src/registries.d.ts.map +1 -1
- package/dist/src/registries.js +11 -0
- package/dist/src/registries.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +21 -7
- package/.aicoder/index.json +0 -304
- package/AICODER_IMPLEMENTATION_PLAN_PHASE2.md +0 -284
- package/bin/interactive.tsx +0 -232
- package/docs/AICODER.png +0 -0
- package/docs/INTERACTIVE_CLI_GUIDE.md +0 -201
- package/docs/TUI_MOCKUP.md +0 -180
- package/src/config/providers.ts +0 -174
- package/src/config/session.ts +0 -143
- package/src/context/bm25.ts +0 -173
- package/src/context/code-search.ts +0 -188
- package/src/context/context-pack.ts +0 -133
- package/src/context/dependency-mapper.ts +0 -72
- package/src/context/index.ts +0 -8
- package/src/context/symbol-extractor.ts +0 -149
- package/src/context/test-mapper.ts +0 -77
- package/src/context/types.ts +0 -69
- package/src/context/workspace-indexer.ts +0 -249
- package/src/index.ts +0 -5
- package/src/registries.ts +0 -118
- package/src/runtime/budget-manager.ts +0 -118
- package/src/runtime/interactive-runtime.ts +0 -423
- package/src/tasks-v2/analysis-tasks.ts +0 -311
- package/src/tasks-v2/developer-tasks.ts +0 -437
- package/src/tasks-v2/index.ts +0 -3
- package/src/tools-v2/edit-tools.ts +0 -153
- package/src/tools-v2/index.ts +0 -6
- package/src/tools-v2/read-tools.ts +0 -286
- package/src/tools-v2/search-tools.ts +0 -175
- package/src/tools-v2/test-tools.ts +0 -147
- package/src/tools-v2/workspace-context.ts +0 -428
- package/src/ui/App.tsx +0 -392
- package/src/ui/components/ChatPane.tsx +0 -84
- package/src/ui/components/EventsPane.tsx +0 -81
- package/src/ui/components/GoalsTasksPane.tsx +0 -149
- package/src/ui/store.ts +0 -362
- package/tests/integration.test.ts +0 -537
- package/tsconfig.json +0 -22
|
@@ -1,428 +0,0 @@
|
|
|
1
|
-
// Workspace Context Retrieval Tool
|
|
2
|
-
import { Tool, type ContextRetrievalArtifact } from '@ddse/acm-sdk';
|
|
3
|
-
import * as fs from 'fs/promises';
|
|
4
|
-
import * as path from 'path';
|
|
5
|
-
import { CodeSearch, WorkspaceIndexer, type WorkspaceIndex } from '../context/index.js';
|
|
6
|
-
import { GrepTool } from './search-tools.js';
|
|
7
|
-
|
|
8
|
-
export type WorkspaceContextOperation =
|
|
9
|
-
| {
|
|
10
|
-
type: 'search';
|
|
11
|
-
query: string;
|
|
12
|
-
k?: number;
|
|
13
|
-
includeContext?: boolean;
|
|
14
|
-
contextLines?: number;
|
|
15
|
-
preferTypes?: string[];
|
|
16
|
-
rationale?: string;
|
|
17
|
-
}
|
|
18
|
-
| {
|
|
19
|
-
type: 'symbol';
|
|
20
|
-
symbol: string;
|
|
21
|
-
k?: number;
|
|
22
|
-
rationale?: string;
|
|
23
|
-
}
|
|
24
|
-
| {
|
|
25
|
-
type: 'grep';
|
|
26
|
-
pattern: string;
|
|
27
|
-
regex?: boolean;
|
|
28
|
-
caseInsensitive?: boolean;
|
|
29
|
-
maxResults?: number;
|
|
30
|
-
rationale?: string;
|
|
31
|
-
}
|
|
32
|
-
| {
|
|
33
|
-
type: 'file';
|
|
34
|
-
path: string;
|
|
35
|
-
startLine?: number;
|
|
36
|
-
endLine?: number;
|
|
37
|
-
maxBytes?: number;
|
|
38
|
-
rationale?: string;
|
|
39
|
-
}
|
|
40
|
-
| {
|
|
41
|
-
type: 'recent';
|
|
42
|
-
limit?: number;
|
|
43
|
-
languages?: string[];
|
|
44
|
-
rationale?: string;
|
|
45
|
-
}
|
|
46
|
-
| {
|
|
47
|
-
type: 'metadata';
|
|
48
|
-
summary?: boolean;
|
|
49
|
-
languages?: string[];
|
|
50
|
-
rationale?: string;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export interface WorkspaceContextRequest {
|
|
54
|
-
directive?: string;
|
|
55
|
-
goal?: string;
|
|
56
|
-
operations?: WorkspaceContextOperation[];
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const DEFAULT_MAX_BYTES = 16_000;
|
|
60
|
-
|
|
61
|
-
export class WorkspaceContextRetrievalTool extends Tool<
|
|
62
|
-
WorkspaceContextRequest,
|
|
63
|
-
ContextRetrievalArtifact[]
|
|
64
|
-
> {
|
|
65
|
-
private rootPath: string;
|
|
66
|
-
private codeSearch?: CodeSearch;
|
|
67
|
-
private indexer: WorkspaceIndexer;
|
|
68
|
-
private index?: WorkspaceIndex;
|
|
69
|
-
private grepTool: GrepTool;
|
|
70
|
-
private indexingPromise?: Promise<void>;
|
|
71
|
-
|
|
72
|
-
constructor(rootPath: string = process.cwd()) {
|
|
73
|
-
super();
|
|
74
|
-
this.rootPath = path.resolve(rootPath);
|
|
75
|
-
this.indexer = new WorkspaceIndexer(this.rootPath);
|
|
76
|
-
this.grepTool = new GrepTool(this.rootPath);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
name(): string {
|
|
80
|
-
return 'workspace_context';
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
async call(request: WorkspaceContextRequest): Promise<ContextRetrievalArtifact[]> {
|
|
84
|
-
const operations = request.operations && request.operations.length > 0
|
|
85
|
-
? request.operations
|
|
86
|
-
: this.deriveOperationsFromDirective(request.directive);
|
|
87
|
-
|
|
88
|
-
if (!operations || operations.length === 0) {
|
|
89
|
-
return [];
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
await this.ensureSearchIndex();
|
|
93
|
-
|
|
94
|
-
const artifacts: ContextRetrievalArtifact[] = [];
|
|
95
|
-
const seen = new Set<string>();
|
|
96
|
-
|
|
97
|
-
for (const operation of operations) {
|
|
98
|
-
try {
|
|
99
|
-
if (operation.type === 'search') {
|
|
100
|
-
const searchArtifacts = await this.handleSearch(operation);
|
|
101
|
-
this.pushArtifacts(searchArtifacts, artifacts, seen);
|
|
102
|
-
} else if (operation.type === 'symbol') {
|
|
103
|
-
const symbolArtifacts = await this.handleSymbol(operation);
|
|
104
|
-
this.pushArtifacts(symbolArtifacts, artifacts, seen);
|
|
105
|
-
} else if (operation.type === 'grep') {
|
|
106
|
-
const grepArtifacts = await this.handleGrep(operation);
|
|
107
|
-
this.pushArtifacts(grepArtifacts, artifacts, seen);
|
|
108
|
-
} else if (operation.type === 'file') {
|
|
109
|
-
const fileArtifact = await this.handleFile(operation);
|
|
110
|
-
this.pushArtifacts(fileArtifact, artifacts, seen);
|
|
111
|
-
} else if (operation.type === 'recent') {
|
|
112
|
-
const recentArtifact = await this.handleRecent(operation);
|
|
113
|
-
this.pushArtifacts(recentArtifact, artifacts, seen);
|
|
114
|
-
} else if (operation.type === 'metadata') {
|
|
115
|
-
const metadataArtifact = await this.handleMetadata(operation);
|
|
116
|
-
this.pushArtifacts(metadataArtifact, artifacts, seen);
|
|
117
|
-
}
|
|
118
|
-
} catch (error) {
|
|
119
|
-
// Skip failing operation but surface metadata for debugging
|
|
120
|
-
this.pushArtifacts(
|
|
121
|
-
{
|
|
122
|
-
type: 'workspace.debug',
|
|
123
|
-
content: {
|
|
124
|
-
operation,
|
|
125
|
-
error: error instanceof Error ? error.message : String(error),
|
|
126
|
-
},
|
|
127
|
-
promote: false,
|
|
128
|
-
provenance: {
|
|
129
|
-
tool: this.name(),
|
|
130
|
-
stage: 'operation-error',
|
|
131
|
-
},
|
|
132
|
-
},
|
|
133
|
-
artifacts,
|
|
134
|
-
seen
|
|
135
|
-
);
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
return artifacts;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
private async ensureSearchIndex(): Promise<void> {
|
|
143
|
-
if (this.index && this.codeSearch) {
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
if (!this.indexingPromise) {
|
|
148
|
-
this.indexingPromise = (async () => {
|
|
149
|
-
this.index = await this.indexer.buildIndex({ useCache: true });
|
|
150
|
-
this.codeSearch = new CodeSearch(this.rootPath);
|
|
151
|
-
await this.codeSearch.indexFiles(this.index);
|
|
152
|
-
})();
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
await this.indexingPromise;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
private deriveOperationsFromDirective(directive?: string): WorkspaceContextOperation[] {
|
|
159
|
-
if (!directive) {
|
|
160
|
-
return [];
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
const separatorIndex = directive.indexOf(':');
|
|
164
|
-
const payload = separatorIndex >= 0 ? directive.slice(separatorIndex + 1).trim() : '';
|
|
165
|
-
|
|
166
|
-
if (payload.startsWith('{')) {
|
|
167
|
-
try {
|
|
168
|
-
const parsed = JSON.parse(payload);
|
|
169
|
-
if (Array.isArray(parsed.operations)) {
|
|
170
|
-
return parsed.operations as WorkspaceContextOperation[];
|
|
171
|
-
}
|
|
172
|
-
if (typeof parsed.query === 'string' && parsed.query.length > 0) {
|
|
173
|
-
return [
|
|
174
|
-
{
|
|
175
|
-
type: 'search',
|
|
176
|
-
query: parsed.query,
|
|
177
|
-
k: parsed.k,
|
|
178
|
-
includeContext: parsed.includeContext ?? true,
|
|
179
|
-
rationale: parsed.rationale,
|
|
180
|
-
},
|
|
181
|
-
];
|
|
182
|
-
}
|
|
183
|
-
} catch {
|
|
184
|
-
// fall back to text parsing
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
if (payload.length > 0) {
|
|
189
|
-
return [
|
|
190
|
-
{
|
|
191
|
-
type: 'search',
|
|
192
|
-
query: payload,
|
|
193
|
-
includeContext: true,
|
|
194
|
-
},
|
|
195
|
-
{
|
|
196
|
-
type: 'grep',
|
|
197
|
-
pattern: payload,
|
|
198
|
-
maxResults: 20,
|
|
199
|
-
},
|
|
200
|
-
];
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return [];
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
private pushArtifacts(
|
|
207
|
-
artifact: ContextRetrievalArtifact | ContextRetrievalArtifact[] | null | undefined,
|
|
208
|
-
collection: ContextRetrievalArtifact[],
|
|
209
|
-
seen: Set<string>
|
|
210
|
-
): void {
|
|
211
|
-
if (!artifact) return;
|
|
212
|
-
const artifacts = Array.isArray(artifact) ? artifact : [artifact];
|
|
213
|
-
|
|
214
|
-
for (const entry of artifacts) {
|
|
215
|
-
if (!entry || typeof entry.type !== 'string') continue;
|
|
216
|
-
|
|
217
|
-
const provenance = entry.provenance ?? { tool: this.name() };
|
|
218
|
-
const keySource = JSON.stringify([
|
|
219
|
-
entry.type,
|
|
220
|
-
provenance.tool,
|
|
221
|
-
(entry.content && (entry.content.path || entry.content.id || entry.content.key)) ??
|
|
222
|
-
JSON.stringify(entry.content),
|
|
223
|
-
]);
|
|
224
|
-
|
|
225
|
-
if (seen.has(keySource)) {
|
|
226
|
-
continue;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
seen.add(keySource);
|
|
230
|
-
collection.push({
|
|
231
|
-
...entry,
|
|
232
|
-
provenance,
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
private async handleSearch(operation: Extract<WorkspaceContextOperation, { type: 'search' }>): Promise<ContextRetrievalArtifact[]> {
|
|
238
|
-
if (!this.codeSearch) {
|
|
239
|
-
return [];
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
const results = await this.codeSearch.search(operation.query, {
|
|
243
|
-
k: Math.min(operation.k ?? 8, 20),
|
|
244
|
-
includeContext: operation.includeContext ?? true,
|
|
245
|
-
contextLines: operation.contextLines ?? 2,
|
|
246
|
-
preferTypes: operation.preferTypes,
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
return results.map(result => ({
|
|
250
|
-
type: 'workspace.snippet',
|
|
251
|
-
promote: true,
|
|
252
|
-
content: {
|
|
253
|
-
path: result.path,
|
|
254
|
-
line: result.line,
|
|
255
|
-
snippet: result.snippet,
|
|
256
|
-
score: result.score,
|
|
257
|
-
operation: 'bm25-search',
|
|
258
|
-
query: operation.query,
|
|
259
|
-
rationale: operation.rationale,
|
|
260
|
-
},
|
|
261
|
-
provenance: {
|
|
262
|
-
tool: this.name(),
|
|
263
|
-
operation: 'search',
|
|
264
|
-
},
|
|
265
|
-
}));
|
|
266
|
-
}
|
|
267
|
-
|
|
268
|
-
private async handleSymbol(operation: Extract<WorkspaceContextOperation, { type: 'symbol' }>): Promise<ContextRetrievalArtifact[]> {
|
|
269
|
-
if (!this.codeSearch) {
|
|
270
|
-
return [];
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
const results = await this.codeSearch.searchSymbol(operation.symbol);
|
|
274
|
-
return results.slice(0, Math.min(operation.k ?? 5, 10)).map(result => ({
|
|
275
|
-
type: 'workspace.snippet',
|
|
276
|
-
promote: true,
|
|
277
|
-
content: {
|
|
278
|
-
path: result.path,
|
|
279
|
-
line: result.line,
|
|
280
|
-
snippet: result.snippet,
|
|
281
|
-
score: result.score,
|
|
282
|
-
operation: 'symbol-search',
|
|
283
|
-
symbol: operation.symbol,
|
|
284
|
-
rationale: operation.rationale,
|
|
285
|
-
},
|
|
286
|
-
provenance: {
|
|
287
|
-
tool: this.name(),
|
|
288
|
-
operation: 'symbol',
|
|
289
|
-
},
|
|
290
|
-
}));
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
private async handleGrep(operation: Extract<WorkspaceContextOperation, { type: 'grep' }>): Promise<ContextRetrievalArtifact[]> {
|
|
294
|
-
const result = await this.grepTool.call({
|
|
295
|
-
pattern: operation.pattern,
|
|
296
|
-
regex: operation.regex,
|
|
297
|
-
caseInsensitive: operation.caseInsensitive,
|
|
298
|
-
maxResults: Math.min(operation.maxResults ?? 30, 100),
|
|
299
|
-
});
|
|
300
|
-
|
|
301
|
-
return result.matches.map(match => ({
|
|
302
|
-
type: 'workspace.match',
|
|
303
|
-
promote: true,
|
|
304
|
-
content: {
|
|
305
|
-
path: match.path,
|
|
306
|
-
line: match.line,
|
|
307
|
-
column: match.column,
|
|
308
|
-
preview: match.preview,
|
|
309
|
-
operation: 'grep',
|
|
310
|
-
pattern: operation.pattern,
|
|
311
|
-
rationale: operation.rationale,
|
|
312
|
-
},
|
|
313
|
-
provenance: {
|
|
314
|
-
tool: this.name(),
|
|
315
|
-
operation: 'grep',
|
|
316
|
-
},
|
|
317
|
-
}));
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
private async handleFile(operation: Extract<WorkspaceContextOperation, { type: 'file' }>): Promise<ContextRetrievalArtifact | null> {
|
|
321
|
-
const absolutePath = path.isAbsolute(operation.path)
|
|
322
|
-
? operation.path
|
|
323
|
-
: path.join(this.rootPath, operation.path);
|
|
324
|
-
|
|
325
|
-
const relativePath = path.relative(this.rootPath, absolutePath);
|
|
326
|
-
|
|
327
|
-
try {
|
|
328
|
-
let content = await fs.readFile(absolutePath, 'utf-8');
|
|
329
|
-
const maxBytes = operation.maxBytes ?? DEFAULT_MAX_BYTES;
|
|
330
|
-
if (content.length > maxBytes) {
|
|
331
|
-
content = content.slice(0, maxBytes);
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
let snippet = content;
|
|
335
|
-
let startLine = 1;
|
|
336
|
-
let endLine = content.split('\n').length;
|
|
337
|
-
|
|
338
|
-
if (operation.startLine || operation.endLine) {
|
|
339
|
-
const lines = content.split('\n');
|
|
340
|
-
startLine = Math.max(operation.startLine ?? 1, 1);
|
|
341
|
-
endLine = Math.min(operation.endLine ?? lines.length, lines.length);
|
|
342
|
-
snippet = lines.slice(startLine - 1, endLine).join('\n');
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
return {
|
|
346
|
-
type: 'workspace.file',
|
|
347
|
-
promote: true,
|
|
348
|
-
content: {
|
|
349
|
-
path: relativePath,
|
|
350
|
-
snippet,
|
|
351
|
-
startLine,
|
|
352
|
-
endLine,
|
|
353
|
-
rationale: operation.rationale,
|
|
354
|
-
},
|
|
355
|
-
provenance: {
|
|
356
|
-
tool: this.name(),
|
|
357
|
-
operation: 'file',
|
|
358
|
-
},
|
|
359
|
-
};
|
|
360
|
-
} catch {
|
|
361
|
-
return {
|
|
362
|
-
type: 'workspace.debug',
|
|
363
|
-
promote: false,
|
|
364
|
-
content: {
|
|
365
|
-
operation,
|
|
366
|
-
error: `Failed to read file: ${relativePath}`,
|
|
367
|
-
},
|
|
368
|
-
provenance: {
|
|
369
|
-
tool: this.name(),
|
|
370
|
-
operation: 'file',
|
|
371
|
-
},
|
|
372
|
-
};
|
|
373
|
-
}
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
private async handleRecent(operation: Extract<WorkspaceContextOperation, { type: 'recent' }>): Promise<ContextRetrievalArtifact | null> {
|
|
377
|
-
if (!this.index) {
|
|
378
|
-
return null;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
const limit = Math.min(operation.limit ?? 5, 20);
|
|
382
|
-
const files = WorkspaceIndexer.getRecentFiles(this.index, limit);
|
|
383
|
-
const filtered = operation.languages && operation.languages.length > 0
|
|
384
|
-
? files.filter(file => operation.languages!.includes(file.language))
|
|
385
|
-
: files;
|
|
386
|
-
|
|
387
|
-
return {
|
|
388
|
-
type: 'workspace.metadata',
|
|
389
|
-
promote: true,
|
|
390
|
-
content: {
|
|
391
|
-
kind: 'recent-files',
|
|
392
|
-
files: filtered.map(file => ({
|
|
393
|
-
path: file.path,
|
|
394
|
-
mtime: file.mtime,
|
|
395
|
-
language: file.language,
|
|
396
|
-
size: file.size,
|
|
397
|
-
})),
|
|
398
|
-
rationale: operation.rationale,
|
|
399
|
-
},
|
|
400
|
-
provenance: {
|
|
401
|
-
tool: this.name(),
|
|
402
|
-
operation: 'recent',
|
|
403
|
-
},
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
private async handleMetadata(operation: Extract<WorkspaceContextOperation, { type: 'metadata' }>): Promise<ContextRetrievalArtifact | null> {
|
|
408
|
-
if (!this.index) {
|
|
409
|
-
return null;
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
return {
|
|
413
|
-
type: 'workspace.metadata',
|
|
414
|
-
promote: true,
|
|
415
|
-
content: {
|
|
416
|
-
kind: 'summary',
|
|
417
|
-
files: this.index.totalFiles,
|
|
418
|
-
totalSize: this.index.totalSize,
|
|
419
|
-
languages: Array.from(new Set(this.index.files.map(f => f.language))).slice(0, 32),
|
|
420
|
-
rationale: operation.rationale,
|
|
421
|
-
},
|
|
422
|
-
provenance: {
|
|
423
|
-
tool: this.name(),
|
|
424
|
-
operation: 'metadata',
|
|
425
|
-
},
|
|
426
|
-
};
|
|
427
|
-
}
|
|
428
|
-
}
|