@cesarandreslopez/occ 0.8.12 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +170 -79
- package/dist/src/cli-validation.js +3 -11
- package/dist/src/cli-validation.js.map +1 -1
- package/dist/src/cli.js +23 -11
- package/dist/src/cli.js.map +1 -1
- package/dist/src/code/build.d.ts +6 -7
- package/dist/src/code/build.js +62 -28
- package/dist/src/code/build.js.map +1 -1
- package/dist/src/code/cache.js +3 -1
- package/dist/src/code/cache.js.map +1 -1
- package/dist/src/code/chunk.d.ts +2 -0
- package/dist/src/code/chunk.js +16 -4
- package/dist/src/code/chunk.js.map +1 -1
- package/dist/src/code/command.js +147 -37
- package/dist/src/code/command.js.map +1 -1
- package/dist/src/code/diff.d.ts +264 -0
- package/dist/src/code/diff.js +504 -0
- package/dist/src/code/diff.js.map +1 -0
- package/dist/src/code/discover.js +5 -5
- package/dist/src/code/discover.js.map +1 -1
- package/dist/src/code/focus.js +5 -5
- package/dist/src/code/focus.js.map +1 -1
- package/dist/src/code/incremental.js +11 -9
- package/dist/src/code/incremental.js.map +1 -1
- package/dist/src/code/index-io.d.ts +2 -0
- package/dist/src/code/index-io.js +2 -2
- package/dist/src/code/index-io.js.map +1 -1
- package/dist/src/code/isolated-runner.js.map +1 -1
- package/dist/src/code/isolated.js +2 -2
- package/dist/src/code/isolated.js.map +1 -1
- package/dist/src/code/languages.js +2 -2
- package/dist/src/code/languages.js.map +1 -1
- package/dist/src/code/map.d.ts +12 -0
- package/dist/src/code/map.js +22 -9
- package/dist/src/code/map.js.map +1 -1
- package/dist/src/code/output.d.ts +3 -0
- package/dist/src/code/output.js +39 -22
- package/dist/src/code/output.js.map +1 -1
- package/dist/src/code/parsers.js +87 -26
- package/dist/src/code/parsers.js.map +1 -1
- package/dist/src/code/query.js +44 -38
- package/dist/src/code/query.js.map +1 -1
- package/dist/src/code/rank.js +30 -17
- package/dist/src/code/rank.js.map +1 -1
- package/dist/src/code/session.js.map +1 -1
- package/dist/src/code/slim.d.ts +2 -0
- package/dist/src/code/slim.js +23 -8
- package/dist/src/code/slim.js.map +1 -1
- package/dist/src/code/store.d.ts +6 -8
- package/dist/src/code/store.js +57 -41
- package/dist/src/code/store.js.map +1 -1
- package/dist/src/code/types.js +15 -3
- package/dist/src/code/types.js.map +1 -1
- package/dist/src/doc/batch.js +6 -2
- package/dist/src/doc/batch.js.map +1 -1
- package/dist/src/doc/chunk.js +9 -5
- package/dist/src/doc/chunk.js.map +1 -1
- package/dist/src/doc/command.js +18 -9
- package/dist/src/doc/command.js.map +1 -1
- package/dist/src/doc/discover.d.ts +1 -1
- package/dist/src/doc/discover.js.map +1 -1
- package/dist/src/doc/entities.js +90 -10
- package/dist/src/doc/entities.js.map +1 -1
- package/dist/src/doc/inspect-docx.js +5 -4
- package/dist/src/doc/inspect-docx.js.map +1 -1
- package/dist/src/doc/inspect-odt.js.map +1 -1
- package/dist/src/doc/inspect-pdf.js +1 -1
- package/dist/src/doc/inspect-pdf.js.map +1 -1
- package/dist/src/doc/inspect.js +4 -4
- package/dist/src/doc/inspect.js.map +1 -1
- package/dist/src/doc/output.js +11 -8
- package/dist/src/doc/output.js.map +1 -1
- package/dist/src/doc/references.js +4 -4
- package/dist/src/doc/references.js.map +1 -1
- package/dist/src/errors.js +2 -4
- package/dist/src/errors.js.map +1 -1
- package/dist/src/git/api.d.ts +34 -0
- package/dist/src/git/api.js +247 -0
- package/dist/src/git/api.js.map +1 -0
- package/dist/src/git/client.d.ts +48 -0
- package/dist/src/git/client.js +336 -0
- package/dist/src/git/client.js.map +1 -0
- package/dist/src/git/command.d.ts +2 -0
- package/dist/src/git/command.js +173 -0
- package/dist/src/git/command.js.map +1 -0
- package/dist/src/git/index.d.ts +5 -0
- package/dist/src/git/index.js +6 -0
- package/dist/src/git/index.js.map +1 -0
- package/dist/src/git/output.d.ts +8 -0
- package/dist/src/git/output.js +38 -0
- package/dist/src/git/output.js.map +1 -0
- package/dist/src/git/parse.d.ts +19 -0
- package/dist/src/git/parse.js +354 -0
- package/dist/src/git/parse.js.map +1 -0
- package/dist/src/git/types.d.ts +334 -0
- package/dist/src/git/types.js +135 -0
- package/dist/src/git/types.js.map +1 -0
- package/dist/src/index.d.ts +38 -12
- package/dist/src/index.js +27 -7
- package/dist/src/index.js.map +1 -1
- package/dist/src/inspect/shared.js.map +1 -1
- package/dist/src/markdown/convert.js +17 -11
- package/dist/src/markdown/convert.js.map +1 -1
- package/dist/src/output/tabular.js +25 -17
- package/dist/src/output/tabular.js.map +1 -1
- package/dist/src/output/tree.js +7 -7
- package/dist/src/output/tree.js.map +1 -1
- package/dist/src/parsers/docx.js +1 -1
- package/dist/src/parsers/docx.js.map +1 -1
- package/dist/src/parsers/index.js +9 -7
- package/dist/src/parsers/index.js.map +1 -1
- package/dist/src/parsers/pdf.js +1 -1
- package/dist/src/parsers/pdf.js.map +1 -1
- package/dist/src/parsers/pptx.js.map +1 -1
- package/dist/src/parsers/xlsx.js +5 -3
- package/dist/src/parsers/xlsx.js.map +1 -1
- package/dist/src/scc.js +9 -5
- package/dist/src/scc.js.map +1 -1
- package/dist/src/sheet/command.js +11 -7
- package/dist/src/sheet/command.js.map +1 -1
- package/dist/src/sheet/inspect.js +14 -14
- package/dist/src/sheet/inspect.js.map +1 -1
- package/dist/src/sheet/output.js +10 -15
- package/dist/src/sheet/output.js.map +1 -1
- package/dist/src/skills/command.d.ts +2 -0
- package/dist/src/skills/command.js +188 -0
- package/dist/src/skills/command.js.map +1 -0
- package/dist/src/slide/command.js +8 -6
- package/dist/src/slide/command.js.map +1 -1
- package/dist/src/slide/inspect-odp.js.map +1 -1
- package/dist/src/slide/inspect-pptx.js +5 -2
- package/dist/src/slide/inspect-pptx.js.map +1 -1
- package/dist/src/slide/inspect.js +4 -2
- package/dist/src/slide/inspect.js.map +1 -1
- package/dist/src/slide/output.js +10 -15
- package/dist/src/slide/output.js.map +1 -1
- package/dist/src/slide/types.js.map +1 -1
- package/dist/src/stats.js +31 -3
- package/dist/src/stats.js.map +1 -1
- package/dist/src/table/command.js +8 -6
- package/dist/src/table/command.js.map +1 -1
- package/dist/src/table/inspect-docx.js +1 -1
- package/dist/src/table/inspect-docx.js.map +1 -1
- package/dist/src/table/inspect-odt.js.map +1 -1
- package/dist/src/table/inspect-pptx.js +5 -2
- package/dist/src/table/inspect-pptx.js.map +1 -1
- package/dist/src/table/inspect-xlsx.js +1 -1
- package/dist/src/table/inspect-xlsx.js.map +1 -1
- package/dist/src/table/inspect.js +3 -1
- package/dist/src/table/inspect.js.map +1 -1
- package/dist/src/table/output.js +7 -11
- package/dist/src/table/output.js.map +1 -1
- package/dist/src/table/types.js.map +1 -1
- package/dist/src/tokens.js +1 -1
- package/dist/src/tokens.js.map +1 -1
- package/dist/src/utils.d.ts +14 -0
- package/dist/src/utils.js +32 -11
- package/dist/src/utils.js.map +1 -1
- package/dist/src/walker.js +2 -4
- package/dist/src/walker.js.map +1 -1
- package/dist/src/workspace/analyze.js +4 -4
- package/dist/src/workspace/analyze.js.map +1 -1
- package/dist/src/workspace/bundle-io.d.ts +14 -0
- package/dist/src/workspace/bundle-io.js +81 -0
- package/dist/src/workspace/bundle-io.js.map +1 -0
- package/dist/src/workspace/bundle-runner.d.ts +5 -0
- package/dist/src/workspace/bundle-runner.js +50 -0
- package/dist/src/workspace/bundle-runner.js.map +1 -0
- package/dist/src/workspace/bundle.d.ts +14 -0
- package/dist/src/workspace/bundle.js +315 -71
- package/dist/src/workspace/bundle.js.map +1 -1
- package/dist/src/workspace/command.js +31 -16
- package/dist/src/workspace/command.js.map +1 -1
- package/dist/src/workspace/describe-output.js +10 -9
- package/dist/src/workspace/describe-output.js.map +1 -1
- package/dist/src/workspace/describe.js +54 -47
- package/dist/src/workspace/describe.js.map +1 -1
- package/dist/src/workspace/documents.js +28 -8
- package/dist/src/workspace/documents.js.map +1 -1
- package/dist/src/workspace/prepare-runner.js.map +1 -1
- package/dist/src/workspace/prepare-types.js.map +1 -1
- package/dist/src/workspace/prepare.js +38 -19
- package/dist/src/workspace/prepare.js.map +1 -1
- package/dist/src/workspace/types.js +4 -2
- package/dist/src/workspace/types.js.map +1 -1
- package/package.json +57 -4
- package/scripts/postinstall.js +13 -9
- package/skills/occ/SKILL.md +77 -0
- package/skills/occ/reference/code.md +69 -0
- package/skills/occ/reference/commands.md +66 -0
- package/skills/occ/reference/documents.md +65 -0
- package/skills/occ/reference/git.md +55 -0
- package/skills/occ/reference/workspace.md +65 -0
|
@@ -1,15 +1,20 @@
|
|
|
1
|
+
import { fork } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { rm } from 'node:fs/promises';
|
|
1
4
|
import path from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
2
6
|
import { analyzeWorkspace } from './analyze.js';
|
|
3
7
|
import { describeWorkspace } from './describe.js';
|
|
4
8
|
import { inspectWorkspaceDocumentSet } from './documents.js';
|
|
9
|
+
import { readSectionedWorkspaceBundle } from './bundle-io.js';
|
|
5
10
|
import { buildCodebaseIndexIsolated } from '../code/isolated.js';
|
|
6
11
|
import { previewCodebaseSize } from '../code/preview.js';
|
|
7
12
|
import { openCodeIndexStore } from '../code/store.js';
|
|
8
|
-
import { slimifyIndex } from '../code/slim.js';
|
|
9
|
-
import { chunkFromIndex } from '../code/chunk.js';
|
|
10
|
-
import { chunkDocument, chunkDocumentFromMarkdown } from '../doc/chunk.js';
|
|
11
|
-
import { abortIfNeeded, mapWithConcurrency, normalizeRelativePath } from '../utils.js';
|
|
12
|
-
import { isOccAbortError } from '../errors.js';
|
|
13
|
+
import { slimifyIndex, slimifyIndexCooperative } from '../code/slim.js';
|
|
14
|
+
import { chunkFromIndex, chunkFromIndexCooperative } from '../code/chunk.js';
|
|
15
|
+
import { chunkDocument, chunkDocumentFromMarkdown, } from '../doc/chunk.js';
|
|
16
|
+
import { abortIfNeeded, createYielder, isCooperative, mapWithConcurrency, normalizeRelativePath, sanitizeForkExecArgv, } from '../utils.js';
|
|
17
|
+
import { OccAbortError, isOccAbortError } from '../errors.js';
|
|
13
18
|
const DEFAULT_EXCLUDE_DIR = ['node_modules', '.git', 'dist', 'vendor', 'build', 'coverage', 'target'];
|
|
14
19
|
// Step descriptors shared by the sequential and concurrent phase paths.
|
|
15
20
|
const STEP_DESCRIPTION = { phase: 'description', detail: 'Describe workspace' };
|
|
@@ -68,7 +73,9 @@ function buildOutline(rootDir, description, documents, codeIndex) {
|
|
|
68
73
|
continue;
|
|
69
74
|
moduleCounts.set(firstSegment, (moduleCounts.get(firstSegment) ?? 0) + 1);
|
|
70
75
|
}
|
|
71
|
-
for (const [modulePath, files] of [...moduleCounts.entries()]
|
|
76
|
+
for (const [modulePath, files] of [...moduleCounts.entries()]
|
|
77
|
+
.sort((left, right) => right[1] - left[1])
|
|
78
|
+
.slice(0, 20)) {
|
|
72
79
|
root.children.push({
|
|
73
80
|
id: `module:${modulePath}`,
|
|
74
81
|
kind: 'module',
|
|
@@ -89,7 +96,7 @@ function buildOutline(rootDir, description, documents, codeIndex) {
|
|
|
89
96
|
tokenEstimate: doc.tokenEstimate,
|
|
90
97
|
headingCount: doc.headingCount,
|
|
91
98
|
},
|
|
92
|
-
children: doc.topLevelSections.map(
|
|
99
|
+
children: doc.topLevelSections.map(section => ({
|
|
93
100
|
id: `section:${doc.relativePath}:${section}`,
|
|
94
101
|
kind: 'section',
|
|
95
102
|
title: section,
|
|
@@ -107,18 +114,20 @@ function detectCodeDocumentReferences(codeIndex, documents, limit) {
|
|
|
107
114
|
if (!codeIndex || !documents || limit <= 0)
|
|
108
115
|
return [];
|
|
109
116
|
const symbolNodes = codeIndex.nodes
|
|
110
|
-
.filter(
|
|
111
|
-
.filter(
|
|
112
|
-
.sort((left, right) =>
|
|
117
|
+
.filter(node => ['class', 'interface', 'type-alias', 'enum', 'function'].includes(node.type))
|
|
118
|
+
.filter(node => node.name.length >= 3)
|
|
119
|
+
.sort((left, right) => right.name.length - left.name.length || left.name.localeCompare(right.name));
|
|
113
120
|
const seenNames = new Set();
|
|
114
|
-
const symbols = symbolNodes
|
|
121
|
+
const symbols = symbolNodes
|
|
122
|
+
.filter(node => {
|
|
115
123
|
if (seenNames.has(node.name))
|
|
116
124
|
return false;
|
|
117
125
|
seenNames.add(node.name);
|
|
118
126
|
return true;
|
|
119
|
-
})
|
|
127
|
+
})
|
|
128
|
+
.slice(0, 1_000);
|
|
120
129
|
// Compile each symbol pattern once instead of per line.
|
|
121
|
-
const compiled = symbols.map(
|
|
130
|
+
const compiled = symbols.map(symbol => ({
|
|
122
131
|
symbol,
|
|
123
132
|
pattern: new RegExp(`\\b${escapeRegExp(symbol.name)}\\b`),
|
|
124
133
|
}));
|
|
@@ -128,7 +137,7 @@ function detectCodeDocumentReferences(codeIndex, documents, limit) {
|
|
|
128
137
|
let prescreen = null;
|
|
129
138
|
if (symbols.length > 0) {
|
|
130
139
|
try {
|
|
131
|
-
prescreen = new RegExp(`\\b(?:${symbols.map(
|
|
140
|
+
prescreen = new RegExp(`\\b(?:${symbols.map(symbol => escapeRegExp(symbol.name)).join('|')})\\b`);
|
|
132
141
|
}
|
|
133
142
|
catch {
|
|
134
143
|
prescreen = null; // Fall back to scanning every line with the compiled patterns.
|
|
@@ -164,7 +173,77 @@ function detectCodeDocumentReferences(codeIndex, documents, limit) {
|
|
|
164
173
|
}
|
|
165
174
|
return references;
|
|
166
175
|
}
|
|
167
|
-
async function
|
|
176
|
+
async function detectCodeDocumentReferencesCooperative(codeIndex, documents, limit, yielder) {
|
|
177
|
+
if (!codeIndex || !documents || limit <= 0)
|
|
178
|
+
return [];
|
|
179
|
+
const symbolNodes = codeIndex.nodes
|
|
180
|
+
.filter(node => ['class', 'interface', 'type-alias', 'enum', 'function'].includes(node.type))
|
|
181
|
+
.filter(node => node.name.length >= 3)
|
|
182
|
+
.sort((left, right) => right.name.length - left.name.length || left.name.localeCompare(right.name));
|
|
183
|
+
const seenNames = new Set();
|
|
184
|
+
const symbols = symbolNodes
|
|
185
|
+
.filter(node => {
|
|
186
|
+
if (seenNames.has(node.name))
|
|
187
|
+
return false;
|
|
188
|
+
seenNames.add(node.name);
|
|
189
|
+
return true;
|
|
190
|
+
})
|
|
191
|
+
.slice(0, 1_000);
|
|
192
|
+
const compiled = symbols.map(symbol => ({
|
|
193
|
+
symbol,
|
|
194
|
+
pattern: new RegExp(`\\b${escapeRegExp(symbol.name)}\\b`),
|
|
195
|
+
}));
|
|
196
|
+
let prescreen = null;
|
|
197
|
+
if (symbols.length > 0) {
|
|
198
|
+
try {
|
|
199
|
+
prescreen = new RegExp(`\\b(?:${symbols.map(symbol => escapeRegExp(symbol.name)).join('|')})\\b`);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
prescreen = null;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const references = [];
|
|
206
|
+
for (const doc of documents.documents) {
|
|
207
|
+
if (!doc.markdownContent) {
|
|
208
|
+
await yielder.tick();
|
|
209
|
+
continue;
|
|
210
|
+
}
|
|
211
|
+
const lines = doc.markdownContent.split(/\r?\n/);
|
|
212
|
+
for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) {
|
|
213
|
+
const line = lines[lineIndex];
|
|
214
|
+
if (!line.trim()) {
|
|
215
|
+
if ((lineIndex + 1) % 2_000 === 0)
|
|
216
|
+
await yielder.tick();
|
|
217
|
+
continue;
|
|
218
|
+
}
|
|
219
|
+
if (prescreen && !prescreen.test(line)) {
|
|
220
|
+
if ((lineIndex + 1) % 2_000 === 0)
|
|
221
|
+
await yielder.tick();
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
for (const { symbol, pattern } of compiled) {
|
|
225
|
+
if (!pattern.test(line))
|
|
226
|
+
continue;
|
|
227
|
+
references.push({
|
|
228
|
+
documentPath: doc.relativePath,
|
|
229
|
+
symbolName: symbol.name,
|
|
230
|
+
symbolType: symbol.type,
|
|
231
|
+
symbolPath: symbol.relativePath ?? normalizeRelativePath(path.relative(codeIndex.repoRoot, symbol.path)),
|
|
232
|
+
symbolLine: symbol.line,
|
|
233
|
+
line: lineIndex + 1,
|
|
234
|
+
context: line.trim().slice(0, 240),
|
|
235
|
+
});
|
|
236
|
+
if (references.length >= limit)
|
|
237
|
+
return references;
|
|
238
|
+
}
|
|
239
|
+
if ((lineIndex + 1) % 2_000 === 0)
|
|
240
|
+
await yielder.tick();
|
|
241
|
+
}
|
|
242
|
+
await yielder.tick();
|
|
243
|
+
}
|
|
244
|
+
return references;
|
|
245
|
+
}
|
|
246
|
+
async function buildDocumentChunks(documents, options, onProgress, yielder) {
|
|
168
247
|
if (!documents)
|
|
169
248
|
return [];
|
|
170
249
|
const maxChunks = options.maxDocumentChunks ?? 500;
|
|
@@ -183,19 +262,20 @@ async function buildDocumentChunks(documents, options, onProgress) {
|
|
|
183
262
|
return undefined;
|
|
184
263
|
let docChunks;
|
|
185
264
|
try {
|
|
186
|
-
docChunks =
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
265
|
+
docChunks =
|
|
266
|
+
doc.markdownContent != null
|
|
267
|
+
? // Markdown was already produced by document inspection — reuse it
|
|
268
|
+
// instead of re-reading and re-converting the file. Per-document
|
|
269
|
+
// values are spread last so caller chunk options cannot clobber them.
|
|
270
|
+
chunkDocumentFromMarkdown(doc.markdownContent, {
|
|
271
|
+
...options.chunk,
|
|
272
|
+
filePath: doc.filePath,
|
|
273
|
+
relativePath: doc.relativePath,
|
|
274
|
+
})
|
|
275
|
+
: await chunkDocument(doc.filePath, {
|
|
276
|
+
...options.chunk,
|
|
277
|
+
relativePath: doc.relativePath,
|
|
278
|
+
});
|
|
199
279
|
}
|
|
200
280
|
catch {
|
|
201
281
|
// Document conversion errors are already represented in inspectDocuments.
|
|
@@ -210,6 +290,8 @@ async function buildDocumentChunks(documents, options, onProgress) {
|
|
|
210
290
|
currentPath: doc.filePath,
|
|
211
291
|
scope: 'document',
|
|
212
292
|
});
|
|
293
|
+
if (yielder)
|
|
294
|
+
await yielder.tick();
|
|
213
295
|
return docChunks;
|
|
214
296
|
}, options.signal);
|
|
215
297
|
const chunks = [];
|
|
@@ -219,13 +301,13 @@ async function buildDocumentChunks(documents, options, onProgress) {
|
|
|
219
301
|
}
|
|
220
302
|
return chunks.slice(0, maxChunks);
|
|
221
303
|
}
|
|
222
|
-
function buildCodeChunks(codeIndex, options, onProgress) {
|
|
304
|
+
async function buildCodeChunks(codeIndex, options, onProgress, yielder) {
|
|
223
305
|
if (!codeIndex)
|
|
224
306
|
return [];
|
|
225
|
-
const chunks =
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
307
|
+
const chunks = yielder
|
|
308
|
+
? await chunkFromIndexCooperative(codeIndex, options.codeChunk, yielder)
|
|
309
|
+
: chunkFromIndex(codeIndex, options.codeChunk);
|
|
310
|
+
const capped = options.maxCodeChunks != null ? chunks.slice(0, Math.max(0, options.maxCodeChunks)) : chunks;
|
|
229
311
|
onProgress?.({
|
|
230
312
|
phase: 'code-chunk',
|
|
231
313
|
total: capped.length,
|
|
@@ -253,7 +335,7 @@ function mapCodeChunk(chunk) {
|
|
|
253
335
|
}
|
|
254
336
|
function mapDocumentChunksToCorpus(chunks) {
|
|
255
337
|
const perFileIndex = new Map();
|
|
256
|
-
return chunks.map(
|
|
338
|
+
return chunks.map(chunk => {
|
|
257
339
|
const chunkIndex = perFileIndex.get(chunk.relativePath) ?? 0;
|
|
258
340
|
perFileIndex.set(chunk.relativePath, chunkIndex + 1);
|
|
259
341
|
return {
|
|
@@ -274,19 +356,172 @@ function mapDocumentChunksToCorpus(chunks) {
|
|
|
274
356
|
};
|
|
275
357
|
});
|
|
276
358
|
}
|
|
277
|
-
function buildCorpusChunks(codeChunks, documentChunks, options) {
|
|
359
|
+
async function buildCorpusChunks(codeChunks, documentChunks, options, yielder) {
|
|
278
360
|
if (!options.includeCorpusChunks)
|
|
279
361
|
return undefined;
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
...mapDocumentChunksToCorpus(documentChunks ?? [])
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
362
|
+
let chunks;
|
|
363
|
+
if (!yielder) {
|
|
364
|
+
chunks = [...(codeChunks ?? []).map(mapCodeChunk), ...mapDocumentChunksToCorpus(documentChunks ?? [])];
|
|
365
|
+
}
|
|
366
|
+
else {
|
|
367
|
+
chunks = [];
|
|
368
|
+
let mapped = 0;
|
|
369
|
+
for (const chunk of codeChunks ?? []) {
|
|
370
|
+
chunks.push(mapCodeChunk(chunk));
|
|
371
|
+
if (++mapped % 512 === 0)
|
|
372
|
+
await yielder.tick();
|
|
373
|
+
}
|
|
374
|
+
const perFileIndex = new Map();
|
|
375
|
+
for (const chunk of documentChunks ?? []) {
|
|
376
|
+
const chunkIndex = perFileIndex.get(chunk.relativePath) ?? 0;
|
|
377
|
+
perFileIndex.set(chunk.relativePath, chunkIndex + 1);
|
|
378
|
+
chunks.push({
|
|
379
|
+
kind: 'document',
|
|
380
|
+
chunkId: chunk.chunkId,
|
|
381
|
+
filePath: chunk.filePath,
|
|
382
|
+
relativePath: chunk.relativePath,
|
|
383
|
+
title: chunk.title,
|
|
384
|
+
content: chunk.content,
|
|
385
|
+
chunkIndex,
|
|
386
|
+
startLine: chunk.startLine,
|
|
387
|
+
endLine: chunk.endLine,
|
|
388
|
+
format: chunk.format,
|
|
389
|
+
headingPath: chunk.headingPath,
|
|
390
|
+
anchor: chunk.anchor,
|
|
391
|
+
tokenEstimate: chunk.tokenEstimate,
|
|
392
|
+
wordCount: chunk.wordCount,
|
|
393
|
+
});
|
|
394
|
+
if (++mapped % 512 === 0)
|
|
395
|
+
await yielder.tick();
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
chunks.sort((left, right) => left.relativePath.localeCompare(right.relativePath) ||
|
|
399
|
+
left.kind.localeCompare(right.kind) ||
|
|
400
|
+
left.chunkIndex - right.chunkIndex);
|
|
286
401
|
const maxChunks = options.maxCorpusChunks;
|
|
287
402
|
return maxChunks != null ? chunks.slice(0, Math.max(0, maxChunks)) : chunks;
|
|
288
403
|
}
|
|
404
|
+
function resolveBundleRunnerPath() {
|
|
405
|
+
const directory = path.dirname(fileURLToPath(import.meta.url));
|
|
406
|
+
const builtRunner = path.join(directory, 'bundle-runner.js');
|
|
407
|
+
if (existsSync(builtRunner))
|
|
408
|
+
return builtRunner;
|
|
409
|
+
return path.join(directory, 'bundle-runner.ts');
|
|
410
|
+
}
|
|
411
|
+
function serializableBundleOptions(options) {
|
|
412
|
+
const { signal: _signal, onProgress: _onProgress, store: _store, cooperative: _cooperative, ...rest } = options;
|
|
413
|
+
return {
|
|
414
|
+
...rest,
|
|
415
|
+
offThread: false,
|
|
416
|
+
chunk: rest.chunk
|
|
417
|
+
? {
|
|
418
|
+
maxTokens: rest.chunk.maxTokens,
|
|
419
|
+
overlapTokens: rest.chunk.overlapTokens,
|
|
420
|
+
}
|
|
421
|
+
: undefined,
|
|
422
|
+
codeChunk: rest.codeChunk
|
|
423
|
+
? {
|
|
424
|
+
maxTokens: rest.codeChunk.maxTokens,
|
|
425
|
+
overlapTokens: rest.codeChunk.overlapTokens,
|
|
426
|
+
files: rest.codeChunk.files,
|
|
427
|
+
}
|
|
428
|
+
: undefined,
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
export async function bundleWorkspaceIsolated(rootDir, options = {}, onProgress) {
|
|
432
|
+
if (options.store) {
|
|
433
|
+
throw new Error('offThread bundle requires cacheDir or no store; a caller-provided store instance cannot cross the process boundary');
|
|
434
|
+
}
|
|
435
|
+
abortIfNeeded(options.signal);
|
|
436
|
+
const runnerPath = resolveBundleRunnerPath();
|
|
437
|
+
const progress = onProgress ?? options.onProgress;
|
|
438
|
+
const childOptions = serializableBundleOptions(options);
|
|
439
|
+
return new Promise((resolve, reject) => {
|
|
440
|
+
let settled = false;
|
|
441
|
+
let abortTimer;
|
|
442
|
+
const child = fork(runnerPath, [], {
|
|
443
|
+
stdio: ['ignore', 'ignore', 'pipe', 'ipc'],
|
|
444
|
+
execArgv: sanitizeForkExecArgv(),
|
|
445
|
+
});
|
|
446
|
+
const cleanup = () => {
|
|
447
|
+
if (abortTimer)
|
|
448
|
+
clearTimeout(abortTimer);
|
|
449
|
+
options.signal?.removeEventListener('abort', handleAbort);
|
|
450
|
+
};
|
|
451
|
+
const rejectAbort = () => {
|
|
452
|
+
cleanup();
|
|
453
|
+
reject(new OccAbortError());
|
|
454
|
+
};
|
|
455
|
+
const handleAbort = () => {
|
|
456
|
+
if (settled)
|
|
457
|
+
return;
|
|
458
|
+
child.send({ type: 'abort' });
|
|
459
|
+
abortTimer = setTimeout(() => {
|
|
460
|
+
if (!settled)
|
|
461
|
+
child.kill();
|
|
462
|
+
}, 1000);
|
|
463
|
+
};
|
|
464
|
+
options.signal?.addEventListener('abort', handleAbort, { once: true });
|
|
465
|
+
let stderr = '';
|
|
466
|
+
child.stderr?.on('data', (chunk) => {
|
|
467
|
+
stderr += chunk.toString();
|
|
468
|
+
});
|
|
469
|
+
child.on('message', (msg) => {
|
|
470
|
+
if (msg.type === 'progress' && msg.event) {
|
|
471
|
+
progress?.(msg.event);
|
|
472
|
+
}
|
|
473
|
+
else if (msg.type === 'aborted') {
|
|
474
|
+
settled = true;
|
|
475
|
+
rejectAbort();
|
|
476
|
+
}
|
|
477
|
+
else if (msg.type === 'complete' && msg.resultPath) {
|
|
478
|
+
settled = true;
|
|
479
|
+
void (async () => {
|
|
480
|
+
try {
|
|
481
|
+
const bundle = await readSectionedWorkspaceBundle(msg.resultPath, options.signal);
|
|
482
|
+
await rm(msg.resultPath, { recursive: true, force: true }).catch(() => undefined);
|
|
483
|
+
cleanup();
|
|
484
|
+
resolve(bundle);
|
|
485
|
+
}
|
|
486
|
+
catch (err) {
|
|
487
|
+
cleanup();
|
|
488
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
489
|
+
}
|
|
490
|
+
})();
|
|
491
|
+
}
|
|
492
|
+
else if (msg.type === 'error') {
|
|
493
|
+
settled = true;
|
|
494
|
+
cleanup();
|
|
495
|
+
reject(new Error(msg.message ?? 'Off-thread bundle failed'));
|
|
496
|
+
}
|
|
497
|
+
});
|
|
498
|
+
child.on('error', err => {
|
|
499
|
+
settled = true;
|
|
500
|
+
cleanup();
|
|
501
|
+
reject(err);
|
|
502
|
+
});
|
|
503
|
+
child.on('close', code => {
|
|
504
|
+
if (settled)
|
|
505
|
+
return;
|
|
506
|
+
cleanup();
|
|
507
|
+
if (options.signal?.aborted) {
|
|
508
|
+
rejectAbort();
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
if (code !== 0) {
|
|
512
|
+
reject(new Error(`Bundle runner exited with code ${code}: ${stderr.trim()}`));
|
|
513
|
+
}
|
|
514
|
+
else {
|
|
515
|
+
reject(new Error('Bundle runner exited without sending a result'));
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
child.send({ type: 'start', rootDir: path.resolve(rootDir), options: childOptions });
|
|
519
|
+
});
|
|
520
|
+
}
|
|
289
521
|
export async function bundleWorkspace(rootDir, options = {}) {
|
|
522
|
+
if (options.offThread) {
|
|
523
|
+
return bundleWorkspaceIsolated(rootDir, options, options.onProgress);
|
|
524
|
+
}
|
|
290
525
|
const startedAt = Date.now();
|
|
291
526
|
const resolvedRoot = path.resolve(rootDir);
|
|
292
527
|
const includeDescription = options.includeDescription !== false;
|
|
@@ -300,6 +535,8 @@ export async function bundleWorkspace(rootDir, options = {}) {
|
|
|
300
535
|
const includeCodeDocumentReferences = options.includeCodeDocumentReferences ?? true;
|
|
301
536
|
const concurrentPhases = options.concurrentPhases !== false;
|
|
302
537
|
const onProgress = options.onProgress;
|
|
538
|
+
const cooperative = isCooperative(options.cooperative);
|
|
539
|
+
const yielder = cooperative ? createYielder(options.cooperative, options.signal) : undefined;
|
|
303
540
|
const errors = [];
|
|
304
541
|
const shared = commonWorkspaceOptions(options);
|
|
305
542
|
const steps = [
|
|
@@ -313,36 +550,36 @@ export async function bundleWorkspace(rootDir, options = {}) {
|
|
|
313
550
|
STEP_OUTLINE,
|
|
314
551
|
];
|
|
315
552
|
let completedSteps = 0;
|
|
316
|
-
const scopedCodeProgress =
|
|
553
|
+
const scopedCodeProgress = event => onProgress?.({ ...event, scope: event.scope ?? 'code' });
|
|
317
554
|
// Resolves the code index either through a persistent store (cacheDir/store
|
|
318
555
|
// options — cache hits skip parsing, misses build in-process) or through the
|
|
319
556
|
// pre-existing isolated subprocess build.
|
|
320
557
|
const acquireCodeIndex = async () => {
|
|
321
558
|
const slim = options.slimCodeIndex ?? true;
|
|
322
|
-
const contentMode = shouldBuildCodeChunks
|
|
323
|
-
? 'full'
|
|
324
|
-
: (options.contentMode ?? (slim ? 'none' : 'excerpt'));
|
|
559
|
+
const contentMode = shouldBuildCodeChunks ? 'full' : (options.contentMode ?? (slim ? 'none' : 'excerpt'));
|
|
325
560
|
if (options.store || options.cacheDir) {
|
|
326
|
-
const store = options.store ??
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
561
|
+
const store = options.store ??
|
|
562
|
+
openCodeIndexStore({
|
|
563
|
+
repoRoot: resolvedRoot,
|
|
564
|
+
cacheDir: options.cacheDir,
|
|
565
|
+
excludeDir: shared.excludeDir,
|
|
566
|
+
noGitignore: shared.noGitignore,
|
|
567
|
+
ignorePatterns: shared.ignorePatterns,
|
|
568
|
+
overlayIgnorePatterns: shared.overlayIgnorePatterns,
|
|
569
|
+
contentMode,
|
|
570
|
+
maxFiles: options.maxCodeFiles,
|
|
571
|
+
maxBytes: options.maxCodeBytes,
|
|
572
|
+
onBudgetExceeded: 'truncate',
|
|
573
|
+
});
|
|
338
574
|
const session = await store.getSession({
|
|
339
575
|
strategy: options.cacheStrategy ?? 'ensure-fresh',
|
|
340
576
|
signal: options.signal,
|
|
341
577
|
onProgress: scopedCodeProgress,
|
|
578
|
+
cooperative: options.cooperative,
|
|
342
579
|
});
|
|
343
580
|
const index = session.getIndex();
|
|
344
581
|
return {
|
|
345
|
-
codeIndex: slim ? slimifyIndex(index) : index,
|
|
582
|
+
codeIndex: slim ? (yielder ? await slimifyIndexCooperative(index, yielder) : slimifyIndex(index)) : index,
|
|
346
583
|
chunkSource: index.contentMode === 'full' ? index : undefined,
|
|
347
584
|
};
|
|
348
585
|
}
|
|
@@ -357,7 +594,11 @@ export async function bundleWorkspace(rootDir, options = {}) {
|
|
|
357
594
|
signal: options.signal,
|
|
358
595
|
}, scopedCodeProgress);
|
|
359
596
|
return {
|
|
360
|
-
codeIndex: slim
|
|
597
|
+
codeIndex: slim
|
|
598
|
+
? yielder
|
|
599
|
+
? await slimifyIndexCooperative(index, yielder)
|
|
600
|
+
: slimifyIndex(index)
|
|
601
|
+
: index,
|
|
361
602
|
chunkSource: index.contentMode === 'full' ? index : undefined,
|
|
362
603
|
};
|
|
363
604
|
};
|
|
@@ -379,11 +620,11 @@ export async function bundleWorkspace(rootDir, options = {}) {
|
|
|
379
620
|
// post-join abortIfNeeded turns the abort into a rejection.
|
|
380
621
|
let caughtAbort;
|
|
381
622
|
const phaseErrors = {
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
623
|
+
description: [],
|
|
624
|
+
analysis: [],
|
|
625
|
+
documents: [],
|
|
385
626
|
'code-preview': [],
|
|
386
|
-
|
|
627
|
+
code: [],
|
|
387
628
|
};
|
|
388
629
|
const capture = (scope, err) => {
|
|
389
630
|
if (isOccAbortError(err)) {
|
|
@@ -418,7 +659,7 @@ export async function bundleWorkspace(rootDir, options = {}) {
|
|
|
418
659
|
...shared,
|
|
419
660
|
includeDocuments,
|
|
420
661
|
includeCode,
|
|
421
|
-
},
|
|
662
|
+
}, event => onProgress?.({ ...event, scope: event.scope ?? 'workspace' }));
|
|
422
663
|
}
|
|
423
664
|
catch (err) {
|
|
424
665
|
capture('analysis', err);
|
|
@@ -439,7 +680,7 @@ export async function bundleWorkspace(rootDir, options = {}) {
|
|
|
439
680
|
includeDataFiles: options.includeDataFiles,
|
|
440
681
|
maxFiles: options.maxDocumentFiles,
|
|
441
682
|
maxReferenceFiles: options.maxReferenceFiles,
|
|
442
|
-
},
|
|
683
|
+
}, event => onProgress?.({ ...event, scope: event.scope ?? 'document' }));
|
|
443
684
|
}
|
|
444
685
|
catch (err) {
|
|
445
686
|
capture('documents', err);
|
|
@@ -509,7 +750,7 @@ export async function bundleWorkspace(rootDir, options = {}) {
|
|
|
509
750
|
...shared,
|
|
510
751
|
includeDocuments,
|
|
511
752
|
includeCode,
|
|
512
|
-
},
|
|
753
|
+
}, event => onProgress?.({ ...event, scope: event.scope ?? 'workspace' }));
|
|
513
754
|
}
|
|
514
755
|
catch (err) {
|
|
515
756
|
const error = err;
|
|
@@ -526,7 +767,7 @@ export async function bundleWorkspace(rootDir, options = {}) {
|
|
|
526
767
|
includeDataFiles: options.includeDataFiles,
|
|
527
768
|
maxFiles: options.maxDocumentFiles,
|
|
528
769
|
maxReferenceFiles: options.maxReferenceFiles,
|
|
529
|
-
},
|
|
770
|
+
}, event => onProgress?.({ ...event, scope: event.scope ?? 'document' }));
|
|
530
771
|
}
|
|
531
772
|
catch (err) {
|
|
532
773
|
const error = err;
|
|
@@ -569,7 +810,7 @@ export async function bundleWorkspace(rootDir, options = {}) {
|
|
|
569
810
|
if (!codeChunkSource) {
|
|
570
811
|
throw new Error('Code chunks require a full-content code index.');
|
|
571
812
|
}
|
|
572
|
-
codeChunks = buildCodeChunks(codeChunkSource, options, onProgress);
|
|
813
|
+
codeChunks = await buildCodeChunks(codeChunkSource, options, onProgress, yielder);
|
|
573
814
|
}
|
|
574
815
|
catch (err) {
|
|
575
816
|
const error = err;
|
|
@@ -579,13 +820,16 @@ export async function bundleWorkspace(rootDir, options = {}) {
|
|
|
579
820
|
reportStep(onProgress, steps[completedSteps], ++completedSteps, steps.length, startedAt);
|
|
580
821
|
}
|
|
581
822
|
const documentChunks = includeDocumentChunks
|
|
582
|
-
? await buildDocumentChunks(documents, options, onProgress)
|
|
823
|
+
? await buildDocumentChunks(documents, options, onProgress, yielder)
|
|
583
824
|
: undefined;
|
|
584
825
|
if (includeDocumentChunks) {
|
|
585
826
|
reportStep(onProgress, steps[completedSteps], ++completedSteps, steps.length, startedAt);
|
|
586
827
|
}
|
|
587
828
|
const outline = buildOutline(resolvedRoot, description, documents, codeIndex);
|
|
588
|
-
const
|
|
829
|
+
const referenceLimit = includeCodeDocumentReferences ? (options.maxCodeDocumentReferences ?? 200) : 0;
|
|
830
|
+
const codeDocumentReferences = yielder
|
|
831
|
+
? await detectCodeDocumentReferencesCooperative(codeIndex, documents, referenceLimit, yielder)
|
|
832
|
+
: detectCodeDocumentReferences(codeIndex, documents, referenceLimit);
|
|
589
833
|
reportStep(onProgress, steps[completedSteps], ++completedSteps, steps.length, startedAt);
|
|
590
834
|
reportStep(onProgress, { phase: 'bundle', detail: 'Workspace bundle complete' }, steps.length, steps.length, startedAt);
|
|
591
835
|
return {
|
|
@@ -599,7 +843,7 @@ export async function bundleWorkspace(rootDir, options = {}) {
|
|
|
599
843
|
codeIndex,
|
|
600
844
|
documentChunks,
|
|
601
845
|
codeChunks,
|
|
602
|
-
corpusChunks: buildCorpusChunks(codeChunks, documentChunks, options),
|
|
846
|
+
corpusChunks: await buildCorpusChunks(codeChunks, documentChunks, options, yielder),
|
|
603
847
|
outline,
|
|
604
848
|
codeDocumentReferences,
|
|
605
849
|
errors,
|