@swarmvaultai/engine 0.1.17 → 0.1.18
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 +5 -2
- package/dist/chunk-NCSZ4AKP.js +1057 -0
- package/dist/index.d.ts +42 -3
- package/dist/index.js +1773 -1035
- package/dist/registry-JFEW5RUP.js +12 -0
- package/package.json +3 -1
package/dist/index.js
CHANGED
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
uniqueBy,
|
|
22
22
|
writeFileIfChanged,
|
|
23
23
|
writeJsonFile
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-NCSZ4AKP.js";
|
|
25
25
|
|
|
26
26
|
// src/agents.ts
|
|
27
27
|
import fs from "fs/promises";
|
|
@@ -129,80 +129,106 @@ async function installConfiguredAgents(rootDir) {
|
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
// src/ingest.ts
|
|
132
|
-
import
|
|
133
|
-
import
|
|
132
|
+
import fs5 from "fs/promises";
|
|
133
|
+
import path5 from "path";
|
|
134
134
|
import { Readability } from "@mozilla/readability";
|
|
135
|
+
import ignore from "ignore";
|
|
135
136
|
import { JSDOM } from "jsdom";
|
|
136
137
|
import mime from "mime-types";
|
|
137
138
|
import TurndownService from "turndown";
|
|
138
139
|
|
|
139
140
|
// src/code-analysis.ts
|
|
140
|
-
import
|
|
141
|
+
import fs3 from "fs/promises";
|
|
142
|
+
import path3 from "path";
|
|
141
143
|
import ts from "typescript";
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
144
|
+
|
|
145
|
+
// src/code-tree-sitter.ts
|
|
146
|
+
import fs2 from "fs/promises";
|
|
147
|
+
import { createRequire } from "module";
|
|
148
|
+
import path2 from "path";
|
|
149
|
+
var require2 = createRequire(import.meta.url);
|
|
150
|
+
var TREE_SITTER_PACKAGE_ROOT = path2.dirname(path2.dirname(require2.resolve("@vscode/tree-sitter-wasm")));
|
|
151
|
+
var treeSitterModulePromise;
|
|
152
|
+
var treeSitterInitPromise;
|
|
153
|
+
var languageCache = /* @__PURE__ */ new Map();
|
|
154
|
+
var grammarFileByLanguage = {
|
|
155
|
+
python: "tree-sitter-python.wasm",
|
|
156
|
+
go: "tree-sitter-go.wasm",
|
|
157
|
+
rust: "tree-sitter-rust.wasm",
|
|
158
|
+
java: "tree-sitter-java.wasm",
|
|
159
|
+
csharp: "tree-sitter-c-sharp.wasm",
|
|
160
|
+
c: "tree-sitter-cpp.wasm",
|
|
161
|
+
cpp: "tree-sitter-cpp.wasm",
|
|
162
|
+
php: "tree-sitter-php.wasm"
|
|
163
|
+
};
|
|
164
|
+
async function getTreeSitterModule() {
|
|
165
|
+
if (!treeSitterModulePromise) {
|
|
166
|
+
treeSitterModulePromise = import(require2.resolve("@vscode/tree-sitter-wasm")).then(
|
|
167
|
+
(module) => module.default ?? module
|
|
168
|
+
);
|
|
152
169
|
}
|
|
170
|
+
return treeSitterModulePromise;
|
|
153
171
|
}
|
|
154
|
-
function
|
|
155
|
-
|
|
172
|
+
async function ensureTreeSitterInit(module) {
|
|
173
|
+
if (!treeSitterInitPromise) {
|
|
174
|
+
treeSitterInitPromise = module.Parser.init({
|
|
175
|
+
locateFile: () => path2.join(TREE_SITTER_PACKAGE_ROOT, "wasm", "tree-sitter.wasm")
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
return treeSitterInitPromise;
|
|
156
179
|
}
|
|
157
|
-
function
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
case ts.DiagnosticCategory.Warning:
|
|
162
|
-
return "warning";
|
|
163
|
-
case ts.DiagnosticCategory.Suggestion:
|
|
164
|
-
return "suggestion";
|
|
165
|
-
default:
|
|
166
|
-
return "message";
|
|
180
|
+
async function loadLanguage(language) {
|
|
181
|
+
const cached = languageCache.get(language);
|
|
182
|
+
if (cached) {
|
|
183
|
+
return cached;
|
|
167
184
|
}
|
|
185
|
+
const loader = (async () => {
|
|
186
|
+
const module = await getTreeSitterModule();
|
|
187
|
+
await ensureTreeSitterInit(module);
|
|
188
|
+
const bytes = await fs2.readFile(path2.join(TREE_SITTER_PACKAGE_ROOT, "wasm", grammarFileByLanguage[language]));
|
|
189
|
+
return module.Language.load(bytes);
|
|
190
|
+
})();
|
|
191
|
+
languageCache.set(language, loader);
|
|
192
|
+
return loader;
|
|
168
193
|
}
|
|
169
|
-
function
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
|
|
194
|
+
function normalizeSymbolReference(value) {
|
|
195
|
+
const withoutGenerics = value.replace(/<[^>]*>/g, "");
|
|
196
|
+
const withoutDecorators = withoutGenerics.replace(/['"&*()[\]{}]/g, " ");
|
|
197
|
+
const trimmed = withoutDecorators.trim();
|
|
198
|
+
const lastSegment = trimmed.split(/::|\\|\.|->/).filter(Boolean).at(-1) ?? trimmed;
|
|
199
|
+
return lastSegment.replace(/[,:;]+$/g, "").trim();
|
|
200
|
+
}
|
|
201
|
+
function stripCodeExtension(filePath) {
|
|
202
|
+
return filePath.replace(/\.(?:[cm]?jsx?|tsx?|mts|cts|py|go|rs|java|cs|php|c|cc|cpp|cxx|h|hh|hpp|hxx)$/i, "");
|
|
203
|
+
}
|
|
204
|
+
function manifestModuleName(manifest, language) {
|
|
205
|
+
const repoPath = manifest.repoRelativePath ?? path2.basename(manifest.originalPath ?? manifest.storedPath);
|
|
206
|
+
const normalized = toPosix(stripCodeExtension(repoPath)).replace(/^\.\/+/, "");
|
|
207
|
+
if (!normalized) {
|
|
208
|
+
return void 0;
|
|
173
209
|
}
|
|
174
|
-
if (
|
|
175
|
-
const
|
|
176
|
-
return
|
|
177
|
-
normalizeWhitespace(
|
|
178
|
-
sourceText.slice(node.getStart(sourceFile), membersPos).replace(/\{\s*$/, "").trim()
|
|
179
|
-
),
|
|
180
|
-
180
|
|
181
|
-
);
|
|
210
|
+
if (language === "python") {
|
|
211
|
+
const dotted = normalized.replace(/\/__init__$/i, "").replace(/\//g, ".").replace(/^src\./, "");
|
|
212
|
+
return dotted || path2.posix.basename(normalized);
|
|
182
213
|
}
|
|
183
|
-
return
|
|
214
|
+
return normalized.endsWith("/index") ? normalized.slice(0, -"/index".length) || path2.posix.basename(normalized) : normalized;
|
|
184
215
|
}
|
|
185
|
-
function
|
|
186
|
-
return
|
|
216
|
+
function singleLineSignature(value) {
|
|
217
|
+
return truncate(
|
|
218
|
+
normalizeWhitespace(
|
|
219
|
+
value.replace(/\{\s*$/, "").replace(/:\s*$/, ":").trim()
|
|
220
|
+
),
|
|
221
|
+
180
|
|
222
|
+
);
|
|
187
223
|
}
|
|
188
|
-
function
|
|
189
|
-
|
|
224
|
+
function makeSymbolId(sourceId, name, seen) {
|
|
225
|
+
const base = slugify(name);
|
|
226
|
+
const count = (seen.get(base) ?? 0) + 1;
|
|
227
|
+
seen.set(base, count);
|
|
228
|
+
return `symbol:${sourceId}:${count === 1 ? base : `${base}-${count}`}`;
|
|
190
229
|
}
|
|
191
|
-
function
|
|
192
|
-
|
|
193
|
-
return [];
|
|
194
|
-
}
|
|
195
|
-
const names = [];
|
|
196
|
-
const visit = (node) => {
|
|
197
|
-
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && availableNames.has(node.expression.text)) {
|
|
198
|
-
if (node.expression.text !== selfName) {
|
|
199
|
-
names.push(node.expression.text);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
ts.forEachChild(node, visit);
|
|
203
|
-
};
|
|
204
|
-
visit(root);
|
|
205
|
-
return uniqueBy(names, (name) => name);
|
|
230
|
+
function escapeRegExp(value) {
|
|
231
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
206
232
|
}
|
|
207
233
|
function collectCallNamesFromText(text, availableNames, selfName) {
|
|
208
234
|
if (!text) {
|
|
@@ -220,132 +246,7 @@ function collectCallNamesFromText(text, availableNames, selfName) {
|
|
|
220
246
|
}
|
|
221
247
|
return uniqueBy(names, (name) => name);
|
|
222
248
|
}
|
|
223
|
-
function
|
|
224
|
-
return uniqueBy(
|
|
225
|
-
(clauses ?? []).filter((clause) => clause.token === token).flatMap(
|
|
226
|
-
(clause) => clause.types.map((typeNode) => {
|
|
227
|
-
if (ts.isIdentifier(typeNode.expression)) {
|
|
228
|
-
return typeNode.expression.text;
|
|
229
|
-
}
|
|
230
|
-
if (ts.isPropertyAccessExpression(typeNode.expression)) {
|
|
231
|
-
return typeNode.expression.getText();
|
|
232
|
-
}
|
|
233
|
-
return typeNode.getText();
|
|
234
|
-
})
|
|
235
|
-
),
|
|
236
|
-
(name) => name
|
|
237
|
-
);
|
|
238
|
-
}
|
|
239
|
-
function isNodeExported(node) {
|
|
240
|
-
return Boolean(
|
|
241
|
-
ts.canHaveModifiers(node) && ts.getModifiers(node)?.some(
|
|
242
|
-
(modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword || modifier.kind === ts.SyntaxKind.DefaultKeyword
|
|
243
|
-
)
|
|
244
|
-
);
|
|
245
|
-
}
|
|
246
|
-
function makeSymbolId(sourceId, name, seen) {
|
|
247
|
-
const base = slugify(name);
|
|
248
|
-
const count = (seen.get(base) ?? 0) + 1;
|
|
249
|
-
seen.set(base, count);
|
|
250
|
-
return `symbol:${sourceId}:${count === 1 ? base : `${base}-${count}`}`;
|
|
251
|
-
}
|
|
252
|
-
function summarizeModule(manifest, code) {
|
|
253
|
-
const localImports = code.imports.filter((item) => !item.isExternal && !item.reExport).length;
|
|
254
|
-
const externalImports = code.imports.filter((item) => item.isExternal).length;
|
|
255
|
-
const exportedCount = code.symbols.filter((symbol) => symbol.exported).length;
|
|
256
|
-
const parts = [`${code.language} module`, `defining ${code.symbols.length} top-level symbol(s)`, `exporting ${exportedCount} symbol(s)`];
|
|
257
|
-
if (localImports > 0) {
|
|
258
|
-
parts.push(`importing ${localImports} local module(s)`);
|
|
259
|
-
}
|
|
260
|
-
if (externalImports > 0) {
|
|
261
|
-
parts.push(`depending on ${externalImports} external package import(s)`);
|
|
262
|
-
}
|
|
263
|
-
if (code.diagnostics.length > 0) {
|
|
264
|
-
parts.push(`with ${code.diagnostics.length} parser diagnostic(s)`);
|
|
265
|
-
}
|
|
266
|
-
return `${manifest.title} is a ${parts.join(", ")}.`;
|
|
267
|
-
}
|
|
268
|
-
function codeClaims(manifest, code) {
|
|
269
|
-
const claims = [];
|
|
270
|
-
if (code.exports.length > 0) {
|
|
271
|
-
claims.push({
|
|
272
|
-
text: `${manifest.title} exports ${code.exports.slice(0, 4).join(", ")}${code.exports.length > 4 ? ", and more" : ""}.`,
|
|
273
|
-
confidence: 1,
|
|
274
|
-
status: "extracted",
|
|
275
|
-
polarity: "neutral",
|
|
276
|
-
citation: manifest.sourceId
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
|
-
if (code.symbols.length > 0) {
|
|
280
|
-
claims.push({
|
|
281
|
-
text: `${manifest.title} defines ${code.symbols.slice(0, 5).map((symbol) => symbol.name).join(", ")}${code.symbols.length > 5 ? ", and more" : ""}.`,
|
|
282
|
-
confidence: 1,
|
|
283
|
-
status: "extracted",
|
|
284
|
-
polarity: "neutral",
|
|
285
|
-
citation: manifest.sourceId
|
|
286
|
-
});
|
|
287
|
-
}
|
|
288
|
-
if (code.imports.length > 0) {
|
|
289
|
-
claims.push({
|
|
290
|
-
text: `${manifest.title} imports ${code.imports.slice(0, 4).map((item) => item.specifier).join(", ")}${code.imports.length > 4 ? ", and more" : ""}.`,
|
|
291
|
-
confidence: 1,
|
|
292
|
-
status: "extracted",
|
|
293
|
-
polarity: "neutral",
|
|
294
|
-
citation: manifest.sourceId
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
if (code.diagnostics.length > 0) {
|
|
298
|
-
claims.push({
|
|
299
|
-
text: `${manifest.title} has ${code.diagnostics.length} parser diagnostic(s) that should be reviewed before trusting the module summary.`,
|
|
300
|
-
confidence: 1,
|
|
301
|
-
status: "extracted",
|
|
302
|
-
polarity: "negative",
|
|
303
|
-
citation: manifest.sourceId
|
|
304
|
-
});
|
|
305
|
-
}
|
|
306
|
-
return claims.slice(0, 4).map((claim, index) => ({
|
|
307
|
-
id: `claim:${manifest.sourceId}:${index + 1}`,
|
|
308
|
-
...claim
|
|
309
|
-
}));
|
|
310
|
-
}
|
|
311
|
-
function codeQuestions(manifest, code) {
|
|
312
|
-
const questions = [
|
|
313
|
-
code.exports.length > 0 ? `Which downstream pages should explain how ${manifest.title} exports are consumed?` : "",
|
|
314
|
-
code.imports.some((item) => !item.isExternal) ? `How does ${manifest.title} coordinate with its imported local modules?` : "",
|
|
315
|
-
code.dependencies[0] ? `Why does ${manifest.title} depend on ${code.dependencies[0]}?` : "",
|
|
316
|
-
`What broader responsibility does ${manifest.title} serve in the codebase?`
|
|
317
|
-
].filter(Boolean);
|
|
318
|
-
return uniqueBy(questions, (question) => question).slice(0, 4);
|
|
319
|
-
}
|
|
320
|
-
function resolveVariableKind(statement) {
|
|
321
|
-
return statement.declarationList.flags & ts.NodeFlags.Const ? "variable" : "variable";
|
|
322
|
-
}
|
|
323
|
-
function splitLines(content) {
|
|
324
|
-
return content.split(/\r?\n/);
|
|
325
|
-
}
|
|
326
|
-
function leadingIndent(line) {
|
|
327
|
-
const match = line.match(/^[ \t]*/);
|
|
328
|
-
return match ? match[0].replace(/\t/g, " ").length : 0;
|
|
329
|
-
}
|
|
330
|
-
function normalizeSymbolReference(value) {
|
|
331
|
-
const withoutGenerics = value.replace(/<[^>]*>/g, "");
|
|
332
|
-
const withoutDecorators = withoutGenerics.replace(/['"&*()[\]{}]/g, " ");
|
|
333
|
-
const trimmed = withoutDecorators.trim();
|
|
334
|
-
const lastSegment = trimmed.split(/::|\./).filter(Boolean).at(-1) ?? trimmed;
|
|
335
|
-
return lastSegment.replace(/[,:;]+$/g, "").trim();
|
|
336
|
-
}
|
|
337
|
-
function singleLineSignature(line) {
|
|
338
|
-
return truncate(
|
|
339
|
-
normalizeWhitespace(
|
|
340
|
-
line.replace(/\{\s*$/, "").replace(/:\s*$/, ":").trim()
|
|
341
|
-
),
|
|
342
|
-
180
|
|
343
|
-
);
|
|
344
|
-
}
|
|
345
|
-
function buildDiagnostic(code, message, line, column = 1, category = "warning") {
|
|
346
|
-
return { code, category, message, line, column };
|
|
347
|
-
}
|
|
348
|
-
function finalizeCodeAnalysis(manifest, language, imports, draftSymbols, exportLabels, diagnostics) {
|
|
249
|
+
function finalizeCodeAnalysis(manifest, language, imports, draftSymbols, exportLabels, diagnostics, metadata) {
|
|
349
250
|
const topLevelNames = new Set(draftSymbols.map((symbol) => symbol.name));
|
|
350
251
|
for (const symbol of draftSymbols) {
|
|
351
252
|
if (symbol.callNames.length === 0 && symbol.bodyText) {
|
|
@@ -366,6 +267,8 @@ function finalizeCodeAnalysis(manifest, language, imports, draftSymbols, exportL
|
|
|
366
267
|
return {
|
|
367
268
|
moduleId: `module:${manifest.sourceId}`,
|
|
368
269
|
language,
|
|
270
|
+
moduleName: metadata?.moduleName ?? manifestModuleName(manifest, language),
|
|
271
|
+
namespace: metadata?.namespace,
|
|
369
272
|
imports,
|
|
370
273
|
dependencies: uniqueBy(
|
|
371
274
|
imports.filter((item) => item.isExternal).map((item) => item.specifier),
|
|
@@ -376,631 +279,957 @@ function finalizeCodeAnalysis(manifest, language, imports, draftSymbols, exportL
|
|
|
376
279
|
diagnostics
|
|
377
280
|
};
|
|
378
281
|
}
|
|
379
|
-
function
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
}
|
|
389
|
-
if (leadingIndent(line) <= startIndent) {
|
|
390
|
-
break;
|
|
391
|
-
}
|
|
392
|
-
parts.push(line);
|
|
282
|
+
function nodeText(node) {
|
|
283
|
+
return node?.text ?? "";
|
|
284
|
+
}
|
|
285
|
+
function findNamedChild(node, type) {
|
|
286
|
+
return node?.namedChildren.find((child) => child?.type === type) ?? null;
|
|
287
|
+
}
|
|
288
|
+
function extractIdentifier(node) {
|
|
289
|
+
if (!node) {
|
|
290
|
+
return void 0;
|
|
393
291
|
}
|
|
394
|
-
|
|
292
|
+
if (["identifier", "field_identifier", "type_identifier", "name", "package_identifier"].includes(node.type)) {
|
|
293
|
+
return node.text.trim();
|
|
294
|
+
}
|
|
295
|
+
const preferred = node.childForFieldName("name") ?? node.namedChildren.find(
|
|
296
|
+
(child) => child && ["identifier", "field_identifier", "type_identifier", "name", "package_identifier"].includes(child.type)
|
|
297
|
+
) ?? node.namedChildren.at(-1) ?? null;
|
|
298
|
+
return preferred ? extractIdentifier(preferred) : void 0;
|
|
395
299
|
}
|
|
396
|
-
function
|
|
397
|
-
return
|
|
398
|
-
const [rawSpecifier, rawAlias] = item.split(/\s+as\s+/i);
|
|
399
|
-
return {
|
|
400
|
-
specifier: rawSpecifier.trim(),
|
|
401
|
-
alias: rawAlias?.trim()
|
|
402
|
-
};
|
|
403
|
-
});
|
|
300
|
+
function exportedByCapitalization(name) {
|
|
301
|
+
return /^[A-Z]/.test(name);
|
|
404
302
|
}
|
|
405
|
-
function
|
|
406
|
-
|
|
407
|
-
|
|
303
|
+
function parseCommaSeparatedReferences(value) {
|
|
304
|
+
return uniqueBy(
|
|
305
|
+
value.split(",").map((item) => item.replace(/\b(public|private|protected|internal|virtual|sealed|static|readonly|new|abstract|final)\b/g, "").trim()).map((item) => normalizeSymbolReference(item)).filter(Boolean),
|
|
306
|
+
(item) => item
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
function descendantTypeNames(node) {
|
|
310
|
+
if (!node) {
|
|
408
311
|
return [];
|
|
409
312
|
}
|
|
410
313
|
return uniqueBy(
|
|
411
|
-
|
|
314
|
+
node.descendantsOfType(["type_identifier", "identifier", "name"]).filter((item) => item !== null).map((item) => normalizeSymbolReference(item.text)).filter(Boolean),
|
|
412
315
|
(item) => item
|
|
413
316
|
);
|
|
414
317
|
}
|
|
415
|
-
function
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
318
|
+
function quotedPath(value) {
|
|
319
|
+
return value.replace(/^["'<]+|[">]+$/g, "").trim();
|
|
320
|
+
}
|
|
321
|
+
function diagnosticsFromTree(rootNode) {
|
|
322
|
+
if (!rootNode.hasError) {
|
|
323
|
+
return [];
|
|
324
|
+
}
|
|
420
325
|
const diagnostics = [];
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
if (!trimmed || trimmed.startsWith("#") || leadingIndent(rawLine) > 0) {
|
|
426
|
-
continue;
|
|
326
|
+
const seen = /* @__PURE__ */ new Set();
|
|
327
|
+
const visit = (node) => {
|
|
328
|
+
if (!node) {
|
|
329
|
+
return;
|
|
427
330
|
}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
331
|
+
if (node.isError || node.isMissing) {
|
|
332
|
+
const key = `${node.startPosition.row}:${node.startPosition.column}:${node.type}:${node.text}`;
|
|
333
|
+
if (!seen.has(key)) {
|
|
334
|
+
seen.add(key);
|
|
335
|
+
diagnostics.push({
|
|
336
|
+
code: node.isMissing ? 9002 : 9001,
|
|
337
|
+
category: "error",
|
|
338
|
+
message: node.isMissing ? `Missing ${node.type} near \`${truncate(normalizeWhitespace(node.text), 80)}\`.` : `Syntax error near \`${truncate(normalizeWhitespace(node.text), 80)}\`.`,
|
|
339
|
+
line: node.startPosition.row + 1,
|
|
340
|
+
column: node.startPosition.column + 1
|
|
437
341
|
});
|
|
438
342
|
}
|
|
439
|
-
continue;
|
|
440
|
-
}
|
|
441
|
-
const fromImportMatch = trimmed.match(/^from\s+([.\w]+)\s+import\s+(.+)$/);
|
|
442
|
-
if (fromImportMatch) {
|
|
443
|
-
const importedSymbols = fromImportMatch[2].split(",").map((item) => item.trim()).filter(Boolean);
|
|
444
|
-
imports.push({
|
|
445
|
-
specifier: fromImportMatch[1],
|
|
446
|
-
importedSymbols,
|
|
447
|
-
isExternal: !isRelativeSpecifier(fromImportMatch[1]),
|
|
448
|
-
reExport: false
|
|
449
|
-
});
|
|
450
|
-
continue;
|
|
451
|
-
}
|
|
452
|
-
const classMatch = trimmed.match(/^class\s+([A-Za-z_]\w*)\s*(?:\(([^)]*)\))?\s*:/);
|
|
453
|
-
if (classMatch) {
|
|
454
|
-
const baseNames = classMatch[2] ? classMatch[2].split(",").map((item) => normalizeSymbolReference(item)).filter(Boolean) : [];
|
|
455
|
-
draftSymbols.push({
|
|
456
|
-
name: classMatch[1],
|
|
457
|
-
kind: "class",
|
|
458
|
-
signature: singleLineSignature(trimmed),
|
|
459
|
-
exported: !classMatch[1].startsWith("_"),
|
|
460
|
-
callNames: [],
|
|
461
|
-
extendsNames: baseNames,
|
|
462
|
-
implementsNames: [],
|
|
463
|
-
bodyText: collectPythonBlock(lines, index)
|
|
464
|
-
});
|
|
465
|
-
continue;
|
|
466
|
-
}
|
|
467
|
-
if (trimmed.startsWith("class ")) {
|
|
468
|
-
diagnostics.push(buildDiagnostic(1001, "Python class declaration is missing a trailing colon.", index + 1));
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
const functionMatch = trimmed.match(/^(?:async\s+)?def\s+([A-Za-z_]\w*)\s*\(/);
|
|
472
|
-
if (functionMatch) {
|
|
473
|
-
draftSymbols.push({
|
|
474
|
-
name: functionMatch[1],
|
|
475
|
-
kind: "function",
|
|
476
|
-
signature: singleLineSignature(trimmed),
|
|
477
|
-
exported: !functionMatch[1].startsWith("_"),
|
|
478
|
-
callNames: [],
|
|
479
|
-
extendsNames: [],
|
|
480
|
-
implementsNames: [],
|
|
481
|
-
bodyText: collectPythonBlock(lines, index)
|
|
482
|
-
});
|
|
483
|
-
continue;
|
|
484
343
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
continue;
|
|
488
|
-
}
|
|
489
|
-
const allMatch = trimmed.match(/^__all__\s*=\s*\[(.*)\]\s*$/);
|
|
490
|
-
if (allMatch) {
|
|
491
|
-
explicitExports = parsePythonAllExportList(trimmed);
|
|
492
|
-
continue;
|
|
493
|
-
}
|
|
494
|
-
const variableMatch = trimmed.match(/^([A-Za-z_]\w*)\s*(?::[^=]+)?=\s*(.+)$/);
|
|
495
|
-
if (variableMatch && !["True", "False", "None"].includes(variableMatch[1])) {
|
|
496
|
-
draftSymbols.push({
|
|
497
|
-
name: variableMatch[1],
|
|
498
|
-
kind: "variable",
|
|
499
|
-
signature: singleLineSignature(trimmed),
|
|
500
|
-
exported: !variableMatch[1].startsWith("_"),
|
|
501
|
-
callNames: [],
|
|
502
|
-
extendsNames: [],
|
|
503
|
-
implementsNames: [],
|
|
504
|
-
bodyText: variableMatch[2]
|
|
505
|
-
});
|
|
344
|
+
for (const child of node.children) {
|
|
345
|
+
visit(child);
|
|
506
346
|
}
|
|
347
|
+
};
|
|
348
|
+
visit(rootNode);
|
|
349
|
+
return diagnostics.slice(0, 20);
|
|
350
|
+
}
|
|
351
|
+
function parsePythonImportStatement(text) {
|
|
352
|
+
const match = text.trim().match(/^import\s+(.+)$/);
|
|
353
|
+
if (!match) {
|
|
354
|
+
return [];
|
|
507
355
|
}
|
|
508
|
-
|
|
509
|
-
const
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
if (started && depth <= 0) {
|
|
533
|
-
return { text: parts.join("\n"), endIndex: index };
|
|
356
|
+
return match[1].split(",").map((item) => item.trim()).filter(Boolean).map((item) => {
|
|
357
|
+
const [specifier, alias] = item.split(/\s+as\s+/i);
|
|
358
|
+
return {
|
|
359
|
+
specifier: specifier.trim(),
|
|
360
|
+
importedSymbols: [],
|
|
361
|
+
namespaceImport: alias?.trim(),
|
|
362
|
+
isExternal: !specifier.trim().startsWith("."),
|
|
363
|
+
reExport: false
|
|
364
|
+
};
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
function parsePythonFromImportStatement(text) {
|
|
368
|
+
const match = text.trim().match(/^from\s+([.\w]+)\s+import\s+(.+)$/);
|
|
369
|
+
if (!match) {
|
|
370
|
+
return [];
|
|
371
|
+
}
|
|
372
|
+
return [
|
|
373
|
+
{
|
|
374
|
+
specifier: match[1],
|
|
375
|
+
importedSymbols: match[2].split(",").map((item) => item.trim()).filter(Boolean),
|
|
376
|
+
isExternal: !match[1].startsWith("."),
|
|
377
|
+
reExport: false
|
|
534
378
|
}
|
|
379
|
+
];
|
|
380
|
+
}
|
|
381
|
+
function parseGoImport(text) {
|
|
382
|
+
const match = text.trim().match(/^(?:([._A-Za-z]\w*)\s+)?"([^"]+)"$/);
|
|
383
|
+
if (!match) {
|
|
384
|
+
return void 0;
|
|
535
385
|
}
|
|
536
386
|
return {
|
|
537
|
-
|
|
538
|
-
|
|
387
|
+
specifier: match[2],
|
|
388
|
+
importedSymbols: [],
|
|
389
|
+
namespaceImport: match[1] && ![".", "_"].includes(match[1]) ? match[1] : void 0,
|
|
390
|
+
isExternal: !match[2].startsWith("."),
|
|
391
|
+
reExport: false
|
|
539
392
|
};
|
|
540
393
|
}
|
|
541
|
-
function
|
|
542
|
-
|
|
394
|
+
function parseRustUse(text) {
|
|
395
|
+
const cleaned = text.replace(/^pub\s+/, "").replace(/^use\s+/, "").replace(/;$/, "").trim();
|
|
396
|
+
const aliasMatch = cleaned.match(/\s+as\s+([A-Za-z_]\w*)$/);
|
|
397
|
+
const withoutAlias = aliasMatch ? cleaned.slice(0, aliasMatch.index).trim() : cleaned;
|
|
398
|
+
const braceMatch = withoutAlias.match(/^(.*)::\{(.+)\}$/);
|
|
399
|
+
const importedSymbols = braceMatch ? braceMatch[2].split(",").map((item) => item.trim()).filter(Boolean) : [aliasMatch ? `${normalizeSymbolReference(withoutAlias)} as ${aliasMatch[1]}` : normalizeSymbolReference(withoutAlias)].filter(
|
|
400
|
+
Boolean
|
|
401
|
+
);
|
|
402
|
+
const specifier = braceMatch ? braceMatch[1].trim() : withoutAlias;
|
|
403
|
+
return {
|
|
404
|
+
specifier,
|
|
405
|
+
importedSymbols,
|
|
406
|
+
isExternal: !/^(crate|self|super)::/.test(specifier),
|
|
407
|
+
reExport: text.trim().startsWith("pub use ")
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function parseJavaImport(text) {
|
|
411
|
+
const cleaned = text.replace(/^import\s+/, "").replace(/^static\s+/, "").replace(/;$/, "").trim();
|
|
412
|
+
const symbolName = normalizeSymbolReference(cleaned.replace(/\.\*$/, ""));
|
|
413
|
+
return {
|
|
414
|
+
specifier: cleaned.replace(/\.\*$/, ""),
|
|
415
|
+
importedSymbols: symbolName ? [symbolName] : [],
|
|
416
|
+
isExternal: true,
|
|
417
|
+
reExport: false
|
|
418
|
+
};
|
|
543
419
|
}
|
|
544
|
-
function
|
|
545
|
-
const
|
|
420
|
+
function parseCSharpUsing(text) {
|
|
421
|
+
const aliasMatch = text.trim().match(/^using\s+([A-Za-z_]\w*)\s*=\s*([^;]+);$/);
|
|
422
|
+
if (aliasMatch) {
|
|
423
|
+
return {
|
|
424
|
+
specifier: aliasMatch[2].trim(),
|
|
425
|
+
importedSymbols: [],
|
|
426
|
+
namespaceImport: aliasMatch[1],
|
|
427
|
+
isExternal: !aliasMatch[2].trim().startsWith("."),
|
|
428
|
+
reExport: false
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
const match = text.trim().match(/^using\s+([^;]+);$/);
|
|
546
432
|
if (!match) {
|
|
547
433
|
return void 0;
|
|
548
434
|
}
|
|
549
435
|
return {
|
|
550
|
-
specifier: match[
|
|
436
|
+
specifier: match[1].trim(),
|
|
551
437
|
importedSymbols: [],
|
|
552
|
-
|
|
553
|
-
isExternal: !isRelativeSpecifier(match[2]),
|
|
438
|
+
isExternal: !match[1].trim().startsWith("."),
|
|
554
439
|
reExport: false
|
|
555
440
|
};
|
|
556
441
|
}
|
|
557
|
-
function
|
|
558
|
-
const
|
|
559
|
-
return
|
|
442
|
+
function parsePhpUse(text) {
|
|
443
|
+
const cleaned = text.trim().replace(/^use\s+/, "").replace(/;$/, "");
|
|
444
|
+
return cleaned.split(",").map((item) => item.trim()).filter(Boolean).map((item) => {
|
|
445
|
+
const aliasMatch = item.match(/^(.+?)\s+as\s+([A-Za-z_]\w*)$/i);
|
|
446
|
+
const specifier = (aliasMatch ? aliasMatch[1] : item).trim();
|
|
447
|
+
return {
|
|
448
|
+
specifier,
|
|
449
|
+
importedSymbols: [],
|
|
450
|
+
namespaceImport: aliasMatch?.[2],
|
|
451
|
+
isExternal: !specifier.startsWith("."),
|
|
452
|
+
reExport: false
|
|
453
|
+
};
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
function parseCppInclude(text) {
|
|
457
|
+
const match = text.trim().match(/^#include\s+([<"].+[>"])$/);
|
|
458
|
+
if (!match) {
|
|
459
|
+
return void 0;
|
|
460
|
+
}
|
|
461
|
+
const specifier = quotedPath(match[1]);
|
|
462
|
+
return {
|
|
463
|
+
specifier,
|
|
464
|
+
importedSymbols: [],
|
|
465
|
+
isExternal: match[1].startsWith("<"),
|
|
466
|
+
reExport: false
|
|
467
|
+
};
|
|
560
468
|
}
|
|
561
|
-
function
|
|
562
|
-
const lines = splitLines(content);
|
|
469
|
+
function pythonCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
563
470
|
const imports = [];
|
|
564
471
|
const draftSymbols = [];
|
|
565
|
-
const
|
|
566
|
-
|
|
567
|
-
let inImportBlock = false;
|
|
568
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
569
|
-
const rawLine = lines[index] ?? "";
|
|
570
|
-
const trimmed = rawLine.trim();
|
|
571
|
-
if (!trimmed || trimmed.startsWith("//")) {
|
|
472
|
+
for (const child of rootNode.namedChildren) {
|
|
473
|
+
if (!child) {
|
|
572
474
|
continue;
|
|
573
475
|
}
|
|
574
|
-
if (
|
|
575
|
-
|
|
576
|
-
inImportBlock = false;
|
|
577
|
-
continue;
|
|
578
|
-
}
|
|
579
|
-
const parsed = parseGoImportLine(trimmed);
|
|
580
|
-
if (parsed) {
|
|
581
|
-
imports.push(parsed);
|
|
582
|
-
}
|
|
476
|
+
if (child.type === "import_statement") {
|
|
477
|
+
imports.push(...parsePythonImportStatement(child.text));
|
|
583
478
|
continue;
|
|
584
479
|
}
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
inImportBlock = true;
|
|
480
|
+
if (child.type === "import_from_statement") {
|
|
481
|
+
imports.push(...parsePythonFromImportStatement(child.text));
|
|
588
482
|
continue;
|
|
589
483
|
}
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
imports.push(parsed);
|
|
484
|
+
if (child.type === "class_definition") {
|
|
485
|
+
const name = extractIdentifier(child.childForFieldName("name"));
|
|
486
|
+
if (!name) {
|
|
487
|
+
continue;
|
|
595
488
|
}
|
|
596
|
-
|
|
597
|
-
}
|
|
598
|
-
const typeMatch = trimmed.match(/^type\s+([A-Za-z_]\w*)\s+(struct|interface)\b/);
|
|
599
|
-
if (typeMatch) {
|
|
600
|
-
const exported = exportedByCapitalization(typeMatch[1]);
|
|
489
|
+
const superclasses = parseCommaSeparatedReferences(nodeText(child.childForFieldName("superclasses")).replace(/^\(|\)$/g, ""));
|
|
601
490
|
draftSymbols.push({
|
|
602
|
-
name
|
|
603
|
-
kind:
|
|
604
|
-
signature: singleLineSignature(
|
|
605
|
-
exported,
|
|
491
|
+
name,
|
|
492
|
+
kind: "class",
|
|
493
|
+
signature: singleLineSignature(child.text),
|
|
494
|
+
exported: !name.startsWith("_"),
|
|
606
495
|
callNames: [],
|
|
607
|
-
extendsNames:
|
|
496
|
+
extendsNames: superclasses,
|
|
608
497
|
implementsNames: [],
|
|
609
|
-
bodyText:
|
|
498
|
+
bodyText: nodeText(child.childForFieldName("body"))
|
|
610
499
|
});
|
|
611
|
-
if (exported) {
|
|
612
|
-
exportLabels.push(typeMatch[1]);
|
|
613
|
-
}
|
|
614
500
|
continue;
|
|
615
501
|
}
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
name: aliasTypeMatch[1],
|
|
621
|
-
kind: "type_alias",
|
|
622
|
-
signature: singleLineSignature(trimmed),
|
|
623
|
-
exported,
|
|
624
|
-
callNames: [],
|
|
625
|
-
extendsNames: [],
|
|
626
|
-
implementsNames: [],
|
|
627
|
-
bodyText: trimmed
|
|
628
|
-
});
|
|
629
|
-
if (exported) {
|
|
630
|
-
exportLabels.push(aliasTypeMatch[1]);
|
|
502
|
+
if (child.type === "function_definition") {
|
|
503
|
+
const name = extractIdentifier(child.childForFieldName("name"));
|
|
504
|
+
if (!name) {
|
|
505
|
+
continue;
|
|
631
506
|
}
|
|
632
|
-
continue;
|
|
633
|
-
}
|
|
634
|
-
const funcMatch = trimmed.match(/^func\s+(?:\(([^)]*)\)\s*)?([A-Za-z_]\w*)\s*\(/);
|
|
635
|
-
if (funcMatch) {
|
|
636
|
-
const receiverType = funcMatch[1] ? receiverTypeName(funcMatch[1]) : "";
|
|
637
|
-
const symbolName = receiverType ? `${receiverType}.${funcMatch[2]}` : funcMatch[2];
|
|
638
|
-
const exported = exportedByCapitalization(funcMatch[2]);
|
|
639
|
-
const block = collectBracedBlock(lines, index);
|
|
640
507
|
draftSymbols.push({
|
|
641
|
-
name
|
|
508
|
+
name,
|
|
642
509
|
kind: "function",
|
|
643
|
-
signature: singleLineSignature(
|
|
644
|
-
exported,
|
|
645
|
-
callNames: [],
|
|
646
|
-
extendsNames: [],
|
|
647
|
-
implementsNames: [],
|
|
648
|
-
bodyText: block.text
|
|
649
|
-
});
|
|
650
|
-
if (exported) {
|
|
651
|
-
exportLabels.push(symbolName);
|
|
652
|
-
}
|
|
653
|
-
index = block.endIndex;
|
|
654
|
-
continue;
|
|
655
|
-
}
|
|
656
|
-
const variableMatch = trimmed.match(/^(?:var|const)\s+([A-Za-z_]\w*)\b/);
|
|
657
|
-
if (variableMatch) {
|
|
658
|
-
const exported = exportedByCapitalization(variableMatch[1]);
|
|
659
|
-
draftSymbols.push({
|
|
660
|
-
name: variableMatch[1],
|
|
661
|
-
kind: "variable",
|
|
662
|
-
signature: singleLineSignature(trimmed),
|
|
663
|
-
exported,
|
|
510
|
+
signature: singleLineSignature(child.text),
|
|
511
|
+
exported: !name.startsWith("_"),
|
|
664
512
|
callNames: [],
|
|
665
513
|
extendsNames: [],
|
|
666
514
|
implementsNames: [],
|
|
667
|
-
bodyText:
|
|
515
|
+
bodyText: nodeText(child.childForFieldName("body"))
|
|
668
516
|
});
|
|
669
|
-
if (exported) {
|
|
670
|
-
exportLabels.push(variableMatch[1]);
|
|
671
|
-
}
|
|
672
517
|
}
|
|
673
518
|
}
|
|
674
|
-
return finalizeCodeAnalysis(manifest, "
|
|
675
|
-
}
|
|
676
|
-
function analyzeRustUseStatement(statement) {
|
|
677
|
-
const cleaned = statement.replace(/^pub\s+/, "").replace(/^use\s+/, "").replace(/;$/, "").trim();
|
|
678
|
-
const aliasMatch = cleaned.match(/\s+as\s+([A-Za-z_]\w*)$/);
|
|
679
|
-
const withoutAlias = aliasMatch ? cleaned.slice(0, aliasMatch.index).trim() : cleaned;
|
|
680
|
-
const braceMatch = withoutAlias.match(/^(.*)::\{(.+)\}$/);
|
|
681
|
-
const importedSymbols = braceMatch ? braceMatch[2].split(",").map((item) => item.trim()).filter(Boolean) : [aliasMatch ? `${normalizeSymbolReference(withoutAlias)} as ${aliasMatch[1]}` : normalizeSymbolReference(withoutAlias)].filter(
|
|
682
|
-
Boolean
|
|
683
|
-
);
|
|
684
|
-
const specifier = braceMatch ? braceMatch[1].trim() : withoutAlias;
|
|
685
|
-
return {
|
|
686
|
-
specifier,
|
|
687
|
-
importedSymbols,
|
|
688
|
-
isExternal: !/^(crate|self|super)::/.test(specifier),
|
|
689
|
-
reExport: statement.trim().startsWith("pub use ")
|
|
690
|
-
};
|
|
519
|
+
return finalizeCodeAnalysis(manifest, "python", imports, draftSymbols, [], diagnostics);
|
|
691
520
|
}
|
|
692
|
-
function
|
|
693
|
-
return /^(pub(?:\([^)]*\))?\s+)/.test(trimmed);
|
|
694
|
-
}
|
|
695
|
-
function analyzeRustCode(manifest, content) {
|
|
696
|
-
const lines = splitLines(content);
|
|
521
|
+
function goCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
697
522
|
const imports = [];
|
|
698
523
|
const draftSymbols = [];
|
|
699
524
|
const exportLabels = [];
|
|
700
|
-
|
|
701
|
-
const
|
|
702
|
-
|
|
703
|
-
const rawLine = lines[index] ?? "";
|
|
704
|
-
const trimmed = rawLine.trim();
|
|
705
|
-
if (!trimmed || trimmed.startsWith("//")) {
|
|
525
|
+
let packageName;
|
|
526
|
+
for (const child of rootNode.namedChildren) {
|
|
527
|
+
if (!child) {
|
|
706
528
|
continue;
|
|
707
529
|
}
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
imports.push(analyzeRustUseStatement(trimmed));
|
|
530
|
+
if (child.type === "package_clause") {
|
|
531
|
+
packageName = extractIdentifier(child.namedChildren.at(0) ?? null);
|
|
711
532
|
continue;
|
|
712
533
|
}
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
kind: "function",
|
|
720
|
-
signature: singleLineSignature(trimmed),
|
|
721
|
-
exported,
|
|
722
|
-
callNames: [],
|
|
723
|
-
extendsNames: [],
|
|
724
|
-
implementsNames: [],
|
|
725
|
-
bodyText: block.text
|
|
726
|
-
};
|
|
727
|
-
draftSymbols.push(symbol);
|
|
728
|
-
symbolByName.set(symbol.name, symbol);
|
|
729
|
-
if (exported) {
|
|
730
|
-
exportLabels.push(symbol.name);
|
|
534
|
+
if (child.type === "import_declaration") {
|
|
535
|
+
for (const spec of child.descendantsOfType("import_spec")) {
|
|
536
|
+
const parsed = spec ? parseGoImport(spec.text) : void 0;
|
|
537
|
+
if (parsed) {
|
|
538
|
+
imports.push(parsed);
|
|
539
|
+
}
|
|
731
540
|
}
|
|
732
|
-
index = block.endIndex;
|
|
733
541
|
continue;
|
|
734
542
|
}
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
name
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
543
|
+
if (child.type === "type_declaration") {
|
|
544
|
+
for (const spec of child.descendantsOfType("type_spec")) {
|
|
545
|
+
if (!spec) {
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
const name = extractIdentifier(spec.childForFieldName("name"));
|
|
549
|
+
const typeNode = spec.childForFieldName("type");
|
|
550
|
+
if (!name || !typeNode) {
|
|
551
|
+
continue;
|
|
552
|
+
}
|
|
553
|
+
const kind = typeNode.type === "struct_type" ? "struct" : typeNode.type === "interface_type" ? "interface" : "type_alias";
|
|
554
|
+
const exported = exportedByCapitalization(name);
|
|
555
|
+
draftSymbols.push({
|
|
556
|
+
name,
|
|
557
|
+
kind,
|
|
558
|
+
signature: singleLineSignature(spec.text),
|
|
559
|
+
exported,
|
|
560
|
+
callNames: [],
|
|
561
|
+
extendsNames: [],
|
|
562
|
+
implementsNames: [],
|
|
563
|
+
bodyText: typeNode.text
|
|
564
|
+
});
|
|
565
|
+
if (exported) {
|
|
566
|
+
exportLabels.push(name);
|
|
567
|
+
}
|
|
753
568
|
}
|
|
754
|
-
index = block.endIndex;
|
|
755
569
|
continue;
|
|
756
570
|
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
const symbol = {
|
|
762
|
-
name: enumMatch[1],
|
|
763
|
-
kind: "enum",
|
|
764
|
-
signature: singleLineSignature(trimmed),
|
|
765
|
-
exported,
|
|
766
|
-
callNames: [],
|
|
767
|
-
extendsNames: [],
|
|
768
|
-
implementsNames: [],
|
|
769
|
-
bodyText: block.text
|
|
770
|
-
};
|
|
771
|
-
draftSymbols.push(symbol);
|
|
772
|
-
symbolByName.set(symbol.name, symbol);
|
|
773
|
-
if (exported) {
|
|
774
|
-
exportLabels.push(symbol.name);
|
|
571
|
+
if (child.type === "function_declaration" || child.type === "method_declaration") {
|
|
572
|
+
const name = extractIdentifier(child.childForFieldName("name"));
|
|
573
|
+
if (!name) {
|
|
574
|
+
continue;
|
|
775
575
|
}
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
const symbol = {
|
|
784
|
-
name: traitMatch[1],
|
|
785
|
-
kind: "interface",
|
|
786
|
-
signature: singleLineSignature(trimmed),
|
|
576
|
+
const receiverType = child.type === "method_declaration" ? normalizeSymbolReference(nodeText(child.childForFieldName("receiver")).replace(/[()]/g, " ").split(/\s+/).at(-1) ?? "") : "";
|
|
577
|
+
const symbolName = receiverType ? `${receiverType}.${name}` : name;
|
|
578
|
+
const exported = exportedByCapitalization(name);
|
|
579
|
+
draftSymbols.push({
|
|
580
|
+
name: symbolName,
|
|
581
|
+
kind: "function",
|
|
582
|
+
signature: singleLineSignature(child.text),
|
|
787
583
|
exported,
|
|
788
584
|
callNames: [],
|
|
789
585
|
extendsNames: [],
|
|
790
586
|
implementsNames: [],
|
|
791
|
-
bodyText:
|
|
792
|
-
};
|
|
793
|
-
draftSymbols.push(symbol);
|
|
794
|
-
symbolByName.set(symbol.name, symbol);
|
|
587
|
+
bodyText: nodeText(child.childForFieldName("body"))
|
|
588
|
+
});
|
|
795
589
|
if (exported) {
|
|
796
|
-
exportLabels.push(
|
|
590
|
+
exportLabels.push(symbolName);
|
|
797
591
|
}
|
|
798
|
-
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
return finalizeCodeAnalysis(manifest, "go", imports, draftSymbols, exportLabels, diagnostics, {
|
|
595
|
+
namespace: packageName
|
|
596
|
+
});
|
|
597
|
+
}
|
|
598
|
+
function rustCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
599
|
+
const imports = [];
|
|
600
|
+
const draftSymbols = [];
|
|
601
|
+
const exportLabels = [];
|
|
602
|
+
const symbolsByName = /* @__PURE__ */ new Map();
|
|
603
|
+
for (const child of rootNode.namedChildren) {
|
|
604
|
+
if (!child) {
|
|
799
605
|
continue;
|
|
800
606
|
}
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
const exported = rustVisibilityPrefix(trimmed);
|
|
804
|
-
const symbol = {
|
|
805
|
-
name: aliasMatch[1],
|
|
806
|
-
kind: "type_alias",
|
|
807
|
-
signature: singleLineSignature(trimmed),
|
|
808
|
-
exported,
|
|
809
|
-
callNames: [],
|
|
810
|
-
extendsNames: [],
|
|
811
|
-
implementsNames: [],
|
|
812
|
-
bodyText: trimmed
|
|
813
|
-
};
|
|
814
|
-
draftSymbols.push(symbol);
|
|
815
|
-
symbolByName.set(symbol.name, symbol);
|
|
816
|
-
if (exported) {
|
|
817
|
-
exportLabels.push(symbol.name);
|
|
818
|
-
}
|
|
607
|
+
if (child.type === "use_declaration") {
|
|
608
|
+
imports.push(parseRustUse(child.text));
|
|
819
609
|
continue;
|
|
820
610
|
}
|
|
821
|
-
const
|
|
822
|
-
if (
|
|
823
|
-
const
|
|
824
|
-
const
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
exported,
|
|
829
|
-
callNames: [],
|
|
830
|
-
extendsNames: [],
|
|
831
|
-
implementsNames: [],
|
|
832
|
-
bodyText: trimmed
|
|
833
|
-
};
|
|
834
|
-
draftSymbols.push(symbol);
|
|
835
|
-
symbolByName.set(symbol.name, symbol);
|
|
836
|
-
if (exported) {
|
|
837
|
-
exportLabels.push(symbol.name);
|
|
611
|
+
const name = child.type === "function_item" ? extractIdentifier(child.childForFieldName("name")) : extractIdentifier(child.childForFieldName("name"));
|
|
612
|
+
if (child.type === "impl_item") {
|
|
613
|
+
const traitName = normalizeSymbolReference(nodeText(child.childForFieldName("trait")));
|
|
614
|
+
const typeName = normalizeSymbolReference(nodeText(child.childForFieldName("type")));
|
|
615
|
+
const target = symbolsByName.get(typeName);
|
|
616
|
+
if (target && traitName) {
|
|
617
|
+
target.implementsNames.push(traitName);
|
|
838
618
|
}
|
|
839
619
|
continue;
|
|
840
620
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
const traitName = normalizeSymbolReference(implMatch[1]);
|
|
844
|
-
const typeName = normalizeSymbolReference(implMatch[2]);
|
|
845
|
-
const symbol = symbolByName.get(typeName);
|
|
846
|
-
if (symbol && traitName) {
|
|
847
|
-
symbol.implementsNames.push(traitName);
|
|
848
|
-
}
|
|
621
|
+
if (!name) {
|
|
622
|
+
continue;
|
|
849
623
|
}
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
if (
|
|
624
|
+
let kind;
|
|
625
|
+
let extendsNames = [];
|
|
626
|
+
if (child.type === "struct_item") {
|
|
627
|
+
kind = "struct";
|
|
628
|
+
} else if (child.type === "trait_item") {
|
|
629
|
+
kind = "trait";
|
|
630
|
+
extendsNames = parseCommaSeparatedReferences(nodeText(child.childForFieldName("bounds")).replace(/\+/g, ","));
|
|
631
|
+
} else if (child.type === "enum_item") {
|
|
632
|
+
kind = "enum";
|
|
633
|
+
} else if (child.type === "function_item") {
|
|
634
|
+
kind = "function";
|
|
635
|
+
} else if (child.type === "type_item") {
|
|
636
|
+
kind = "type_alias";
|
|
637
|
+
} else if (child.type === "const_item" || child.type === "static_item") {
|
|
638
|
+
kind = "variable";
|
|
639
|
+
}
|
|
640
|
+
if (!kind) {
|
|
855
641
|
continue;
|
|
856
642
|
}
|
|
857
|
-
const exported =
|
|
643
|
+
const exported = child.namedChildren.some((item) => item?.type === "visibility_modifier");
|
|
858
644
|
const symbol = {
|
|
859
|
-
name
|
|
860
|
-
kind
|
|
861
|
-
signature: singleLineSignature(
|
|
645
|
+
name,
|
|
646
|
+
kind,
|
|
647
|
+
signature: singleLineSignature(child.text),
|
|
862
648
|
exported,
|
|
863
649
|
callNames: [],
|
|
864
|
-
extendsNames
|
|
650
|
+
extendsNames,
|
|
865
651
|
implementsNames: [],
|
|
866
|
-
bodyText:
|
|
652
|
+
bodyText: nodeText(child.childForFieldName("body")) || child.text
|
|
867
653
|
};
|
|
868
654
|
draftSymbols.push(symbol);
|
|
869
|
-
|
|
655
|
+
symbolsByName.set(name, symbol);
|
|
870
656
|
if (exported) {
|
|
871
|
-
exportLabels.push(
|
|
657
|
+
exportLabels.push(name);
|
|
872
658
|
}
|
|
873
659
|
}
|
|
874
660
|
return finalizeCodeAnalysis(manifest, "rust", imports, draftSymbols, exportLabels, diagnostics);
|
|
875
661
|
}
|
|
876
|
-
function
|
|
877
|
-
const cleaned = statement.replace(/^import\s+/, "").replace(/^static\s+/, "").replace(/;$/, "").trim();
|
|
878
|
-
const symbolName = normalizeSymbolReference(cleaned.replace(/\.\*$/, ""));
|
|
879
|
-
return {
|
|
880
|
-
specifier: cleaned.replace(/\.\*$/, ""),
|
|
881
|
-
importedSymbols: symbolName ? [symbolName] : [],
|
|
882
|
-
isExternal: true,
|
|
883
|
-
reExport: false
|
|
884
|
-
};
|
|
885
|
-
}
|
|
886
|
-
function parseJavaImplements(value) {
|
|
887
|
-
return (value ?? "").split(",").map((item) => normalizeSymbolReference(item)).filter(Boolean);
|
|
888
|
-
}
|
|
889
|
-
function analyzeJavaCode(manifest, content) {
|
|
890
|
-
const lines = splitLines(content);
|
|
662
|
+
function javaCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
891
663
|
const imports = [];
|
|
892
664
|
const draftSymbols = [];
|
|
893
665
|
const exportLabels = [];
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
}
|
|
903
|
-
if (
|
|
904
|
-
|
|
905
|
-
|
|
666
|
+
let packageName;
|
|
667
|
+
for (const child of rootNode.namedChildren) {
|
|
668
|
+
if (!child) {
|
|
669
|
+
continue;
|
|
670
|
+
}
|
|
671
|
+
if (child.type === "package_declaration") {
|
|
672
|
+
packageName = child.text.replace(/^package\s+/, "").replace(/;$/, "").trim();
|
|
673
|
+
continue;
|
|
674
|
+
}
|
|
675
|
+
if (child.type === "import_declaration") {
|
|
676
|
+
imports.push(parseJavaImport(child.text));
|
|
677
|
+
continue;
|
|
678
|
+
}
|
|
679
|
+
const name = extractIdentifier(child.childForFieldName("name"));
|
|
680
|
+
if (!name) {
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
let kind;
|
|
684
|
+
let extendsNames = [];
|
|
685
|
+
let implementsNames = [];
|
|
686
|
+
if (child.type === "class_declaration") {
|
|
687
|
+
kind = "class";
|
|
688
|
+
extendsNames = descendantTypeNames(child.childForFieldName("superclass"));
|
|
689
|
+
implementsNames = descendantTypeNames(child.childForFieldName("interfaces"));
|
|
690
|
+
} else if (child.type === "interface_declaration") {
|
|
691
|
+
kind = "interface";
|
|
692
|
+
extendsNames = descendantTypeNames(
|
|
693
|
+
child.descendantsOfType("extends_interfaces").find((item) => item !== null) ?? null
|
|
906
694
|
);
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
695
|
+
} else if (child.type === "enum_declaration") {
|
|
696
|
+
kind = "enum";
|
|
697
|
+
}
|
|
698
|
+
if (!kind) {
|
|
699
|
+
continue;
|
|
700
|
+
}
|
|
701
|
+
const exported = /\bpublic\b/.test(child.text);
|
|
702
|
+
draftSymbols.push({
|
|
703
|
+
name,
|
|
704
|
+
kind,
|
|
705
|
+
signature: singleLineSignature(child.text),
|
|
706
|
+
exported,
|
|
707
|
+
callNames: [],
|
|
708
|
+
extendsNames,
|
|
709
|
+
implementsNames,
|
|
710
|
+
bodyText: nodeText(child.childForFieldName("body")) || child.text
|
|
711
|
+
});
|
|
712
|
+
if (exported) {
|
|
713
|
+
exportLabels.push(name);
|
|
714
|
+
}
|
|
715
|
+
}
|
|
716
|
+
return finalizeCodeAnalysis(manifest, "java", imports, draftSymbols, exportLabels, diagnostics, {
|
|
717
|
+
namespace: packageName
|
|
718
|
+
});
|
|
719
|
+
}
|
|
720
|
+
function csharpCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
721
|
+
const imports = [];
|
|
722
|
+
const draftSymbols = [];
|
|
723
|
+
const exportLabels = [];
|
|
724
|
+
let namespaceName;
|
|
725
|
+
for (const child of rootNode.namedChildren) {
|
|
726
|
+
if (!child) {
|
|
727
|
+
continue;
|
|
728
|
+
}
|
|
729
|
+
if (child.type === "using_directive") {
|
|
730
|
+
const parsed = parseCSharpUsing(child.text);
|
|
731
|
+
if (parsed) {
|
|
732
|
+
imports.push(parsed);
|
|
926
733
|
}
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
const
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
734
|
+
continue;
|
|
735
|
+
}
|
|
736
|
+
if (child.type === "file_scoped_namespace_declaration" || child.type === "namespace_declaration") {
|
|
737
|
+
namespaceName = nodeText(child.childForFieldName("name")) || namespaceName;
|
|
738
|
+
if (child.type === "namespace_declaration") {
|
|
739
|
+
for (const nested of child.namedChildren) {
|
|
740
|
+
if (nested && nested !== child.childForFieldName("name")) {
|
|
741
|
+
if (["class_declaration", "interface_declaration", "enum_declaration", "struct_declaration", "record_declaration"].includes(
|
|
742
|
+
nested.type
|
|
743
|
+
)) {
|
|
744
|
+
const nestedName = extractIdentifier(nested.childForFieldName("name"));
|
|
745
|
+
if (!nestedName) {
|
|
746
|
+
continue;
|
|
747
|
+
}
|
|
748
|
+
const effectiveBaseList = parseCommaSeparatedReferences(
|
|
749
|
+
nodeText(findNamedChild(nested, "base_list") ?? nested.childForFieldName("base_list")).replace(/^:/, "")
|
|
750
|
+
);
|
|
751
|
+
const kind2 = nested.type === "interface_declaration" ? "interface" : nested.type === "enum_declaration" ? "enum" : nested.type === "struct_declaration" ? "struct" : "class";
|
|
752
|
+
const exported2 = /\b(public|internal|protected)\b/.test(nested.text);
|
|
753
|
+
const extendsNames2 = kind2 === "class" || kind2 === "struct" ? effectiveBaseList.slice(0, 1) : [];
|
|
754
|
+
const implementsNames2 = kind2 === "interface" ? [] : kind2 === "enum" ? [] : effectiveBaseList.slice(kind2 === "class" || kind2 === "struct" ? 1 : 0);
|
|
755
|
+
draftSymbols.push({
|
|
756
|
+
name: nestedName,
|
|
757
|
+
kind: kind2,
|
|
758
|
+
signature: singleLineSignature(nested.text),
|
|
759
|
+
exported: exported2,
|
|
760
|
+
callNames: [],
|
|
761
|
+
extendsNames: extendsNames2,
|
|
762
|
+
implementsNames: implementsNames2,
|
|
763
|
+
bodyText: nodeText(nested.childForFieldName("body")) || nested.text
|
|
764
|
+
});
|
|
765
|
+
if (exported2) {
|
|
766
|
+
exportLabels.push(nestedName);
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
}
|
|
945
770
|
}
|
|
946
|
-
|
|
947
|
-
|
|
771
|
+
}
|
|
772
|
+
if (child.type === "namespace_declaration") {
|
|
948
773
|
continue;
|
|
949
774
|
}
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
775
|
+
}
|
|
776
|
+
if (!["class_declaration", "interface_declaration", "enum_declaration", "struct_declaration", "record_declaration"].includes(child.type)) {
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
const name = extractIdentifier(child.childForFieldName("name"));
|
|
780
|
+
if (!name) {
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
const baseList = parseCommaSeparatedReferences(
|
|
784
|
+
nodeText(findNamedChild(child, "base_list") ?? child.childForFieldName("base_list")).replace(/^:/, "")
|
|
785
|
+
);
|
|
786
|
+
const kind = child.type === "interface_declaration" ? "interface" : child.type === "enum_declaration" ? "enum" : child.type === "struct_declaration" ? "struct" : "class";
|
|
787
|
+
const exported = /\b(public|internal|protected)\b/.test(child.text);
|
|
788
|
+
const extendsNames = kind === "class" || kind === "struct" ? baseList.slice(0, 1) : [];
|
|
789
|
+
const implementsNames = kind === "interface" ? [] : kind === "enum" ? [] : baseList.slice(kind === "class" || kind === "struct" ? 1 : 0);
|
|
790
|
+
draftSymbols.push({
|
|
791
|
+
name,
|
|
792
|
+
kind,
|
|
793
|
+
signature: singleLineSignature(child.text),
|
|
794
|
+
exported,
|
|
795
|
+
callNames: [],
|
|
796
|
+
extendsNames,
|
|
797
|
+
implementsNames,
|
|
798
|
+
bodyText: nodeText(child.childForFieldName("body")) || child.text
|
|
799
|
+
});
|
|
800
|
+
if (exported) {
|
|
801
|
+
exportLabels.push(name);
|
|
802
|
+
}
|
|
803
|
+
}
|
|
804
|
+
return finalizeCodeAnalysis(manifest, "csharp", imports, draftSymbols, exportLabels, diagnostics, {
|
|
805
|
+
namespace: namespaceName
|
|
806
|
+
});
|
|
807
|
+
}
|
|
808
|
+
function phpCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
809
|
+
const imports = [];
|
|
810
|
+
const draftSymbols = [];
|
|
811
|
+
const exportLabels = [];
|
|
812
|
+
let namespaceName;
|
|
813
|
+
for (const child of rootNode.namedChildren) {
|
|
814
|
+
if (!child || child.type === "php_tag") {
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
if (child.type === "namespace_definition") {
|
|
818
|
+
namespaceName = nodeText(child.childForFieldName("name")) || namespaceName;
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
if (child.type === "namespace_use_declaration") {
|
|
822
|
+
imports.push(...parsePhpUse(child.text));
|
|
823
|
+
continue;
|
|
824
|
+
}
|
|
825
|
+
const name = extractIdentifier(child.childForFieldName("name"));
|
|
826
|
+
if (!name) {
|
|
827
|
+
continue;
|
|
828
|
+
}
|
|
829
|
+
let kind;
|
|
830
|
+
let extendsNames = [];
|
|
831
|
+
let implementsNames = [];
|
|
832
|
+
if (child.type === "class_declaration") {
|
|
833
|
+
kind = "class";
|
|
834
|
+
extendsNames = parseCommaSeparatedReferences(
|
|
835
|
+
nodeText(findNamedChild(child, "base_clause") ?? child.childForFieldName("base_clause"))
|
|
836
|
+
);
|
|
837
|
+
implementsNames = parseCommaSeparatedReferences(
|
|
838
|
+
nodeText(findNamedChild(child, "class_interface_clause") ?? child.childForFieldName("class_interface_clause"))
|
|
839
|
+
);
|
|
840
|
+
} else if (child.type === "interface_declaration") {
|
|
841
|
+
kind = "interface";
|
|
842
|
+
extendsNames = parseCommaSeparatedReferences(
|
|
843
|
+
nodeText(findNamedChild(child, "base_clause") ?? child.childForFieldName("base_clause"))
|
|
844
|
+
);
|
|
845
|
+
} else if (child.type === "trait_declaration") {
|
|
846
|
+
kind = "trait";
|
|
847
|
+
} else if (child.type === "enum_declaration") {
|
|
848
|
+
kind = "enum";
|
|
849
|
+
} else if (child.type === "function_definition") {
|
|
850
|
+
kind = "function";
|
|
851
|
+
}
|
|
852
|
+
if (!kind) {
|
|
853
|
+
continue;
|
|
854
|
+
}
|
|
855
|
+
draftSymbols.push({
|
|
856
|
+
name,
|
|
857
|
+
kind,
|
|
858
|
+
signature: singleLineSignature(child.text),
|
|
859
|
+
exported: true,
|
|
860
|
+
callNames: [],
|
|
861
|
+
extendsNames,
|
|
862
|
+
implementsNames,
|
|
863
|
+
bodyText: nodeText(child.childForFieldName("body")) || child.text
|
|
864
|
+
});
|
|
865
|
+
exportLabels.push(name);
|
|
866
|
+
}
|
|
867
|
+
return finalizeCodeAnalysis(manifest, "php", imports, draftSymbols, exportLabels, diagnostics, {
|
|
868
|
+
namespace: namespaceName
|
|
869
|
+
});
|
|
870
|
+
}
|
|
871
|
+
function cFamilyCodeAnalysis(manifest, language, rootNode, diagnostics) {
|
|
872
|
+
const imports = [];
|
|
873
|
+
const draftSymbols = [];
|
|
874
|
+
const exportLabels = [];
|
|
875
|
+
const functionNameFromDeclarator = (node) => {
|
|
876
|
+
if (!node) {
|
|
877
|
+
return void 0;
|
|
878
|
+
}
|
|
879
|
+
const declarator = node.childForFieldName("declarator");
|
|
880
|
+
if (declarator) {
|
|
881
|
+
return functionNameFromDeclarator(declarator);
|
|
882
|
+
}
|
|
883
|
+
return extractIdentifier(node);
|
|
884
|
+
};
|
|
885
|
+
for (const child of rootNode.namedChildren) {
|
|
886
|
+
if (!child) {
|
|
887
|
+
continue;
|
|
888
|
+
}
|
|
889
|
+
if (child.type === "preproc_include") {
|
|
890
|
+
const parsed = parseCppInclude(child.text);
|
|
891
|
+
if (parsed) {
|
|
892
|
+
imports.push(parsed);
|
|
893
|
+
}
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
if (["class_specifier", "struct_specifier", "enum_specifier"].includes(child.type)) {
|
|
897
|
+
const name = extractIdentifier(child.childForFieldName("name"));
|
|
898
|
+
if (!name) {
|
|
969
899
|
continue;
|
|
970
900
|
}
|
|
971
|
-
const
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
901
|
+
const kind = child.type === "enum_specifier" ? "enum" : child.type === "struct_specifier" ? "struct" : "class";
|
|
902
|
+
const baseClassClause = findNamedChild(child, "base_class_clause") ?? child.childForFieldName("base_class_clause");
|
|
903
|
+
const bases = baseClassClause ? uniqueBy(
|
|
904
|
+
baseClassClause.namedChildren.filter((item) => item !== null && item.type !== "access_specifier").map((item) => normalizeSymbolReference(item.text.replace(/\b(public|private|protected|virtual)\b/g, "").trim())).filter(Boolean),
|
|
905
|
+
(item) => item
|
|
906
|
+
) : [];
|
|
907
|
+
const exported = !/\bstatic\b/.test(child.text);
|
|
908
|
+
draftSymbols.push({
|
|
909
|
+
name,
|
|
910
|
+
kind,
|
|
911
|
+
signature: singleLineSignature(child.text),
|
|
912
|
+
exported,
|
|
913
|
+
callNames: [],
|
|
914
|
+
extendsNames: bases,
|
|
915
|
+
implementsNames: [],
|
|
916
|
+
bodyText: nodeText(child.childForFieldName("body")) || child.text
|
|
917
|
+
});
|
|
918
|
+
if (exported) {
|
|
919
|
+
exportLabels.push(name);
|
|
920
|
+
}
|
|
921
|
+
continue;
|
|
922
|
+
}
|
|
923
|
+
if (child.type === "function_definition") {
|
|
924
|
+
const name = functionNameFromDeclarator(child.childForFieldName("declarator"));
|
|
925
|
+
if (!name) {
|
|
992
926
|
continue;
|
|
993
927
|
}
|
|
928
|
+
const exported = !/\bstatic\b/.test(child.text);
|
|
929
|
+
draftSymbols.push({
|
|
930
|
+
name,
|
|
931
|
+
kind: "function",
|
|
932
|
+
signature: singleLineSignature(child.text),
|
|
933
|
+
exported,
|
|
934
|
+
callNames: [],
|
|
935
|
+
extendsNames: [],
|
|
936
|
+
implementsNames: [],
|
|
937
|
+
bodyText: nodeText(child.childForFieldName("body")) || child.text
|
|
938
|
+
});
|
|
939
|
+
if (exported) {
|
|
940
|
+
exportLabels.push(name);
|
|
941
|
+
}
|
|
994
942
|
}
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
943
|
+
}
|
|
944
|
+
return finalizeCodeAnalysis(manifest, language, imports, draftSymbols, exportLabels, diagnostics);
|
|
945
|
+
}
|
|
946
|
+
async function analyzeTreeSitterCode(manifest, content, language) {
|
|
947
|
+
const module = await getTreeSitterModule();
|
|
948
|
+
await ensureTreeSitterInit(module);
|
|
949
|
+
const parser = new module.Parser();
|
|
950
|
+
parser.setLanguage(await loadLanguage(language));
|
|
951
|
+
const tree = parser.parse(content);
|
|
952
|
+
if (!tree) {
|
|
953
|
+
return finalizeCodeAnalysis(
|
|
954
|
+
manifest,
|
|
955
|
+
language,
|
|
956
|
+
[],
|
|
957
|
+
[],
|
|
958
|
+
[],
|
|
959
|
+
[
|
|
960
|
+
{
|
|
961
|
+
code: 9e3,
|
|
962
|
+
category: "error",
|
|
963
|
+
message: `Failed to parse ${language} source.`,
|
|
964
|
+
line: 1,
|
|
965
|
+
column: 1
|
|
966
|
+
}
|
|
967
|
+
]
|
|
968
|
+
);
|
|
969
|
+
}
|
|
970
|
+
try {
|
|
971
|
+
const diagnostics = diagnosticsFromTree(tree.rootNode);
|
|
972
|
+
switch (language) {
|
|
973
|
+
case "python":
|
|
974
|
+
return pythonCodeAnalysis(manifest, tree.rootNode, diagnostics);
|
|
975
|
+
case "go":
|
|
976
|
+
return goCodeAnalysis(manifest, tree.rootNode, diagnostics);
|
|
977
|
+
case "rust":
|
|
978
|
+
return rustCodeAnalysis(manifest, tree.rootNode, diagnostics);
|
|
979
|
+
case "java":
|
|
980
|
+
return javaCodeAnalysis(manifest, tree.rootNode, diagnostics);
|
|
981
|
+
case "csharp":
|
|
982
|
+
return csharpCodeAnalysis(manifest, tree.rootNode, diagnostics);
|
|
983
|
+
case "php":
|
|
984
|
+
return phpCodeAnalysis(manifest, tree.rootNode, diagnostics);
|
|
985
|
+
case "c":
|
|
986
|
+
case "cpp":
|
|
987
|
+
return cFamilyCodeAnalysis(manifest, language, tree.rootNode, diagnostics);
|
|
988
|
+
}
|
|
989
|
+
} finally {
|
|
990
|
+
tree.delete();
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
|
|
994
|
+
// src/code-analysis.ts
|
|
995
|
+
function scriptKindFor(language) {
|
|
996
|
+
switch (language) {
|
|
997
|
+
case "typescript":
|
|
998
|
+
return ts.ScriptKind.TS;
|
|
999
|
+
case "tsx":
|
|
1000
|
+
return ts.ScriptKind.TSX;
|
|
1001
|
+
case "jsx":
|
|
1002
|
+
return ts.ScriptKind.JSX;
|
|
1003
|
+
default:
|
|
1004
|
+
return ts.ScriptKind.JS;
|
|
1005
|
+
}
|
|
1006
|
+
}
|
|
1007
|
+
function isRelativeSpecifier(specifier) {
|
|
1008
|
+
return specifier.startsWith("./") || specifier.startsWith("../") || specifier.startsWith(".");
|
|
1009
|
+
}
|
|
1010
|
+
function isLocalIncludeSpecifier(specifier) {
|
|
1011
|
+
return specifier.startsWith(".") || specifier.startsWith("/") || specifier.includes("/");
|
|
1012
|
+
}
|
|
1013
|
+
function formatDiagnosticCategory(category) {
|
|
1014
|
+
switch (category) {
|
|
1015
|
+
case ts.DiagnosticCategory.Error:
|
|
1016
|
+
return "error";
|
|
1017
|
+
case ts.DiagnosticCategory.Warning:
|
|
1018
|
+
return "warning";
|
|
1019
|
+
case ts.DiagnosticCategory.Suggestion:
|
|
1020
|
+
return "suggestion";
|
|
1021
|
+
default:
|
|
1022
|
+
return "message";
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
function declarationSignature(node, sourceFile) {
|
|
1026
|
+
const sourceText = sourceFile.getFullText();
|
|
1027
|
+
if (ts.isFunctionDeclaration(node) && node.body) {
|
|
1028
|
+
return truncate(normalizeWhitespace(sourceText.slice(node.getStart(sourceFile), node.body.getStart(sourceFile)).trim()), 180);
|
|
1029
|
+
}
|
|
1030
|
+
if (ts.isClassDeclaration(node) || ts.isInterfaceDeclaration(node) || ts.isEnumDeclaration(node)) {
|
|
1031
|
+
const membersPos = node.members.pos;
|
|
1032
|
+
return truncate(
|
|
1033
|
+
normalizeWhitespace(
|
|
1034
|
+
sourceText.slice(node.getStart(sourceFile), membersPos).replace(/\{\s*$/, "").trim()
|
|
1035
|
+
),
|
|
1036
|
+
180
|
|
1037
|
+
);
|
|
1038
|
+
}
|
|
1039
|
+
return truncate(normalizeWhitespace(node.getText(sourceFile)), 180);
|
|
1040
|
+
}
|
|
1041
|
+
function importSpecifierText(specifier) {
|
|
1042
|
+
return specifier.propertyName ? `${specifier.propertyName.text} as ${specifier.name.text}` : specifier.name.text;
|
|
1043
|
+
}
|
|
1044
|
+
function exportSpecifierText(specifier) {
|
|
1045
|
+
return specifier.propertyName ? `${specifier.propertyName.text} as ${specifier.name.text}` : specifier.name.text;
|
|
1046
|
+
}
|
|
1047
|
+
function collectCallNames(root, availableNames, selfName) {
|
|
1048
|
+
if (!root) {
|
|
1049
|
+
return [];
|
|
1050
|
+
}
|
|
1051
|
+
const names = [];
|
|
1052
|
+
const visit = (node) => {
|
|
1053
|
+
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && availableNames.has(node.expression.text)) {
|
|
1054
|
+
if (node.expression.text !== selfName) {
|
|
1055
|
+
names.push(node.expression.text);
|
|
1000
1056
|
}
|
|
1001
1057
|
}
|
|
1058
|
+
ts.forEachChild(node, visit);
|
|
1059
|
+
};
|
|
1060
|
+
visit(root);
|
|
1061
|
+
return uniqueBy(names, (name) => name);
|
|
1062
|
+
}
|
|
1063
|
+
function collectCallNamesFromText2(text, availableNames, selfName) {
|
|
1064
|
+
if (!text) {
|
|
1065
|
+
return [];
|
|
1066
|
+
}
|
|
1067
|
+
const names = [];
|
|
1068
|
+
for (const name of availableNames) {
|
|
1069
|
+
if (name === selfName) {
|
|
1070
|
+
continue;
|
|
1071
|
+
}
|
|
1072
|
+
const pattern = new RegExp(`\\b${escapeRegExp2(name)}\\s*\\(`, "g");
|
|
1073
|
+
if (pattern.test(text)) {
|
|
1074
|
+
names.push(name);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
return uniqueBy(names, (name) => name);
|
|
1078
|
+
}
|
|
1079
|
+
function heritageNames(clauses, token) {
|
|
1080
|
+
return uniqueBy(
|
|
1081
|
+
(clauses ?? []).filter((clause) => clause.token === token).flatMap(
|
|
1082
|
+
(clause) => clause.types.map((typeNode) => {
|
|
1083
|
+
if (ts.isIdentifier(typeNode.expression)) {
|
|
1084
|
+
return typeNode.expression.text;
|
|
1085
|
+
}
|
|
1086
|
+
if (ts.isPropertyAccessExpression(typeNode.expression)) {
|
|
1087
|
+
return typeNode.expression.getText();
|
|
1088
|
+
}
|
|
1089
|
+
return typeNode.getText();
|
|
1090
|
+
})
|
|
1091
|
+
),
|
|
1092
|
+
(name) => name
|
|
1093
|
+
);
|
|
1094
|
+
}
|
|
1095
|
+
function isNodeExported(node) {
|
|
1096
|
+
return Boolean(
|
|
1097
|
+
ts.canHaveModifiers(node) && ts.getModifiers(node)?.some(
|
|
1098
|
+
(modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword || modifier.kind === ts.SyntaxKind.DefaultKeyword
|
|
1099
|
+
)
|
|
1100
|
+
);
|
|
1101
|
+
}
|
|
1102
|
+
function makeSymbolId2(sourceId, name, seen) {
|
|
1103
|
+
const base = slugify(name);
|
|
1104
|
+
const count = (seen.get(base) ?? 0) + 1;
|
|
1105
|
+
seen.set(base, count);
|
|
1106
|
+
return `symbol:${sourceId}:${count === 1 ? base : `${base}-${count}`}`;
|
|
1107
|
+
}
|
|
1108
|
+
function summarizeModule(manifest, code) {
|
|
1109
|
+
const localImports = code.imports.filter((item) => !item.isExternal && !item.reExport).length;
|
|
1110
|
+
const externalImports = code.imports.filter((item) => item.isExternal).length;
|
|
1111
|
+
const exportedCount = code.symbols.filter((symbol) => symbol.exported).length;
|
|
1112
|
+
const parts = [`${code.language} module`, `defining ${code.symbols.length} top-level symbol(s)`, `exporting ${exportedCount} symbol(s)`];
|
|
1113
|
+
if (localImports > 0) {
|
|
1114
|
+
parts.push(`importing ${localImports} local module(s)`);
|
|
1115
|
+
}
|
|
1116
|
+
if (externalImports > 0) {
|
|
1117
|
+
parts.push(`depending on ${externalImports} external package import(s)`);
|
|
1118
|
+
}
|
|
1119
|
+
if (code.diagnostics.length > 0) {
|
|
1120
|
+
parts.push(`with ${code.diagnostics.length} parser diagnostic(s)`);
|
|
1121
|
+
}
|
|
1122
|
+
return `${manifest.title} is a ${parts.join(", ")}.`;
|
|
1123
|
+
}
|
|
1124
|
+
function codeClaims(manifest, code) {
|
|
1125
|
+
const claims = [];
|
|
1126
|
+
if (code.exports.length > 0) {
|
|
1127
|
+
claims.push({
|
|
1128
|
+
text: `${manifest.title} exports ${code.exports.slice(0, 4).join(", ")}${code.exports.length > 4 ? ", and more" : ""}.`,
|
|
1129
|
+
confidence: 1,
|
|
1130
|
+
status: "extracted",
|
|
1131
|
+
polarity: "neutral",
|
|
1132
|
+
citation: manifest.sourceId
|
|
1133
|
+
});
|
|
1134
|
+
}
|
|
1135
|
+
if (code.symbols.length > 0) {
|
|
1136
|
+
claims.push({
|
|
1137
|
+
text: `${manifest.title} defines ${code.symbols.slice(0, 5).map((symbol) => symbol.name).join(", ")}${code.symbols.length > 5 ? ", and more" : ""}.`,
|
|
1138
|
+
confidence: 1,
|
|
1139
|
+
status: "extracted",
|
|
1140
|
+
polarity: "neutral",
|
|
1141
|
+
citation: manifest.sourceId
|
|
1142
|
+
});
|
|
1143
|
+
}
|
|
1144
|
+
if (code.imports.length > 0) {
|
|
1145
|
+
claims.push({
|
|
1146
|
+
text: `${manifest.title} imports ${code.imports.slice(0, 4).map((item) => item.specifier).join(", ")}${code.imports.length > 4 ? ", and more" : ""}.`,
|
|
1147
|
+
confidence: 1,
|
|
1148
|
+
status: "extracted",
|
|
1149
|
+
polarity: "neutral",
|
|
1150
|
+
citation: manifest.sourceId
|
|
1151
|
+
});
|
|
1152
|
+
}
|
|
1153
|
+
if (code.diagnostics.length > 0) {
|
|
1154
|
+
claims.push({
|
|
1155
|
+
text: `${manifest.title} has ${code.diagnostics.length} parser diagnostic(s) that should be reviewed before trusting the module summary.`,
|
|
1156
|
+
confidence: 1,
|
|
1157
|
+
status: "extracted",
|
|
1158
|
+
polarity: "negative",
|
|
1159
|
+
citation: manifest.sourceId
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
return claims.slice(0, 4).map((claim, index) => ({
|
|
1163
|
+
id: `claim:${manifest.sourceId}:${index + 1}`,
|
|
1164
|
+
...claim
|
|
1165
|
+
}));
|
|
1166
|
+
}
|
|
1167
|
+
function codeQuestions(manifest, code) {
|
|
1168
|
+
const questions = [
|
|
1169
|
+
code.exports.length > 0 ? `Which downstream pages should explain how ${manifest.title} exports are consumed?` : "",
|
|
1170
|
+
code.imports.some((item) => !item.isExternal) ? `How does ${manifest.title} coordinate with its imported local modules?` : "",
|
|
1171
|
+
code.dependencies[0] ? `Why does ${manifest.title} depend on ${code.dependencies[0]}?` : "",
|
|
1172
|
+
`What broader responsibility does ${manifest.title} serve in the codebase?`
|
|
1173
|
+
].filter(Boolean);
|
|
1174
|
+
return uniqueBy(questions, (question) => question).slice(0, 4);
|
|
1175
|
+
}
|
|
1176
|
+
function resolveVariableKind(statement) {
|
|
1177
|
+
return statement.declarationList.flags & ts.NodeFlags.Const ? "variable" : "variable";
|
|
1178
|
+
}
|
|
1179
|
+
function normalizeSymbolReference2(value) {
|
|
1180
|
+
const withoutGenerics = value.replace(/<[^>]*>/g, "");
|
|
1181
|
+
const withoutDecorators = withoutGenerics.replace(/['"&*()[\]{}]/g, " ");
|
|
1182
|
+
const trimmed = withoutDecorators.trim();
|
|
1183
|
+
const lastSegment = trimmed.split(/::|\./).filter(Boolean).at(-1) ?? trimmed;
|
|
1184
|
+
return lastSegment.replace(/[,:;]+$/g, "").trim();
|
|
1185
|
+
}
|
|
1186
|
+
function stripCodeExtension2(filePath) {
|
|
1187
|
+
return filePath.replace(/\.(?:[cm]?jsx?|tsx?|mts|cts|py|go|rs|java|cs|php|c|cc|cpp|cxx|h|hh|hpp|hxx)$/i, "");
|
|
1188
|
+
}
|
|
1189
|
+
function manifestModuleName2(manifest, language) {
|
|
1190
|
+
const repoPath = manifest.repoRelativePath ?? path3.basename(manifest.originalPath ?? manifest.storedPath);
|
|
1191
|
+
const normalized = toPosix(stripCodeExtension2(repoPath)).replace(/^\.\/+/, "");
|
|
1192
|
+
if (!normalized) {
|
|
1193
|
+
return void 0;
|
|
1194
|
+
}
|
|
1195
|
+
if (language === "python") {
|
|
1196
|
+
const dotted = normalized.replace(/\/__init__$/i, "").replace(/\//g, ".").replace(/^src\./, "");
|
|
1197
|
+
return dotted || path3.posix.basename(normalized);
|
|
1198
|
+
}
|
|
1199
|
+
return normalized.endsWith("/index") ? normalized.slice(0, -"/index".length) || path3.posix.basename(normalized) : normalized;
|
|
1200
|
+
}
|
|
1201
|
+
function finalizeCodeAnalysis2(manifest, language, imports, draftSymbols, exportLabels, diagnostics, metadata) {
|
|
1202
|
+
const topLevelNames = new Set(draftSymbols.map((symbol) => symbol.name));
|
|
1203
|
+
for (const symbol of draftSymbols) {
|
|
1204
|
+
if (symbol.callNames.length === 0 && symbol.bodyText) {
|
|
1205
|
+
symbol.callNames = collectCallNamesFromText2(symbol.bodyText, topLevelNames, symbol.name);
|
|
1206
|
+
}
|
|
1002
1207
|
}
|
|
1003
|
-
|
|
1208
|
+
const seenSymbolIds = /* @__PURE__ */ new Map();
|
|
1209
|
+
const symbols = draftSymbols.map((symbol) => ({
|
|
1210
|
+
id: makeSymbolId2(manifest.sourceId, symbol.name, seenSymbolIds),
|
|
1211
|
+
name: symbol.name,
|
|
1212
|
+
kind: symbol.kind,
|
|
1213
|
+
signature: symbol.signature,
|
|
1214
|
+
exported: symbol.exported,
|
|
1215
|
+
calls: uniqueBy(symbol.callNames, (name) => name),
|
|
1216
|
+
extends: uniqueBy(symbol.extendsNames.map((name) => normalizeSymbolReference2(name)).filter(Boolean), (name) => name),
|
|
1217
|
+
implements: uniqueBy(symbol.implementsNames.map((name) => normalizeSymbolReference2(name)).filter(Boolean), (name) => name)
|
|
1218
|
+
}));
|
|
1219
|
+
return {
|
|
1220
|
+
moduleId: `module:${manifest.sourceId}`,
|
|
1221
|
+
language,
|
|
1222
|
+
moduleName: metadata?.moduleName ?? manifestModuleName2(manifest, language),
|
|
1223
|
+
namespace: metadata?.namespace,
|
|
1224
|
+
imports,
|
|
1225
|
+
dependencies: uniqueBy(
|
|
1226
|
+
imports.filter((item) => item.isExternal).map((item) => item.specifier),
|
|
1227
|
+
(specifier) => specifier
|
|
1228
|
+
),
|
|
1229
|
+
symbols,
|
|
1230
|
+
exports: uniqueBy([...symbols.filter((symbol) => symbol.exported).map((symbol) => symbol.name), ...exportLabels], (label) => label),
|
|
1231
|
+
diagnostics
|
|
1232
|
+
};
|
|
1004
1233
|
}
|
|
1005
1234
|
function analyzeTypeScriptLikeCode(manifest, content) {
|
|
1006
1235
|
const language = manifest.language ?? inferCodeLanguage(manifest.originalPath ?? manifest.storedPath, manifest.mimeType) ?? "typescript";
|
|
@@ -1214,10 +1443,10 @@ function analyzeTypeScriptLikeCode(manifest, content) {
|
|
|
1214
1443
|
column: (position?.character ?? 0) + 1
|
|
1215
1444
|
};
|
|
1216
1445
|
});
|
|
1217
|
-
return
|
|
1446
|
+
return finalizeCodeAnalysis2(manifest, language, imports, draftSymbols, exportLabels, diagnostics);
|
|
1218
1447
|
}
|
|
1219
1448
|
function inferCodeLanguage(filePath, mimeType = "") {
|
|
1220
|
-
const extension =
|
|
1449
|
+
const extension = path3.extname(filePath).toLowerCase();
|
|
1221
1450
|
if (extension === ".ts" || extension === ".mts" || extension === ".cts") {
|
|
1222
1451
|
return "typescript";
|
|
1223
1452
|
}
|
|
@@ -1242,78 +1471,344 @@ function inferCodeLanguage(filePath, mimeType = "") {
|
|
|
1242
1471
|
if (extension === ".java") {
|
|
1243
1472
|
return "java";
|
|
1244
1473
|
}
|
|
1474
|
+
if (extension === ".cs") {
|
|
1475
|
+
return "csharp";
|
|
1476
|
+
}
|
|
1477
|
+
if (extension === ".php") {
|
|
1478
|
+
return "php";
|
|
1479
|
+
}
|
|
1480
|
+
if (extension === ".c") {
|
|
1481
|
+
return "c";
|
|
1482
|
+
}
|
|
1483
|
+
if ([".cc", ".cpp", ".cxx", ".h", ".hh", ".hpp", ".hxx"].includes(extension)) {
|
|
1484
|
+
return "cpp";
|
|
1485
|
+
}
|
|
1245
1486
|
return void 0;
|
|
1246
1487
|
}
|
|
1247
1488
|
function modulePageTitle(manifest) {
|
|
1248
1489
|
return `${manifest.title} module`;
|
|
1249
1490
|
}
|
|
1250
1491
|
function importResolutionCandidates(basePath, specifier, extensions) {
|
|
1251
|
-
const resolved =
|
|
1252
|
-
if (
|
|
1253
|
-
return [
|
|
1492
|
+
const resolved = path3.posix.normalize(path3.posix.join(path3.posix.dirname(basePath), specifier));
|
|
1493
|
+
if (path3.posix.extname(resolved)) {
|
|
1494
|
+
return [resolved];
|
|
1254
1495
|
}
|
|
1255
|
-
const direct = extensions.map((extension) =>
|
|
1256
|
-
const indexFiles = extensions.map((extension) =>
|
|
1257
|
-
return uniqueBy([
|
|
1496
|
+
const direct = extensions.map((extension) => path3.posix.normalize(`${resolved}${extension}`));
|
|
1497
|
+
const indexFiles = extensions.map((extension) => path3.posix.normalize(path3.posix.join(resolved, `index${extension}`)));
|
|
1498
|
+
return uniqueBy([resolved, ...direct, ...indexFiles], (candidate) => candidate);
|
|
1258
1499
|
}
|
|
1259
|
-
function
|
|
1260
|
-
|
|
1261
|
-
|
|
1500
|
+
function normalizeAlias(value) {
|
|
1501
|
+
return value.replace(/\\/g, "/").replace(/\/+/g, "/").trim();
|
|
1502
|
+
}
|
|
1503
|
+
function recordAlias(target, value) {
|
|
1504
|
+
const normalized = normalizeAlias(value ?? "");
|
|
1505
|
+
if (!normalized) {
|
|
1506
|
+
return;
|
|
1507
|
+
}
|
|
1508
|
+
target.add(normalized);
|
|
1509
|
+
const lowered = normalized.toLowerCase();
|
|
1510
|
+
if (lowered !== normalized) {
|
|
1511
|
+
target.add(lowered);
|
|
1262
1512
|
}
|
|
1263
|
-
const candidates = new Set(
|
|
1264
|
-
importResolutionCandidates(manifest.originalPath, specifier, [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"])
|
|
1265
|
-
);
|
|
1266
|
-
return manifests.find(
|
|
1267
|
-
(candidate) => candidate.sourceKind === "code" && candidate.originalPath && candidates.has(path2.normalize(candidate.originalPath))
|
|
1268
|
-
)?.sourceId;
|
|
1269
1513
|
}
|
|
1270
|
-
function
|
|
1271
|
-
|
|
1272
|
-
|
|
1514
|
+
function manifestBasenameWithoutExtension(manifest) {
|
|
1515
|
+
const target = manifest.repoRelativePath ?? manifest.originalPath ?? manifest.storedPath;
|
|
1516
|
+
return path3.posix.basename(stripCodeExtension2(normalizeAlias(target)));
|
|
1517
|
+
}
|
|
1518
|
+
async function readNearestGoModulePath(startPath, cache) {
|
|
1519
|
+
let current = path3.resolve(startPath);
|
|
1520
|
+
try {
|
|
1521
|
+
const stat = await fs3.stat(current);
|
|
1522
|
+
if (!stat.isDirectory()) {
|
|
1523
|
+
current = path3.dirname(current);
|
|
1524
|
+
}
|
|
1525
|
+
} catch {
|
|
1526
|
+
current = path3.dirname(current);
|
|
1273
1527
|
}
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
const
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
return
|
|
1528
|
+
while (true) {
|
|
1529
|
+
if (cache.has(current)) {
|
|
1530
|
+
const cached = cache.get(current);
|
|
1531
|
+
return cached === null ? void 0 : cached;
|
|
1532
|
+
}
|
|
1533
|
+
const goModPath = path3.join(current, "go.mod");
|
|
1534
|
+
if (await fs3.access(goModPath).then(() => true).catch(() => false)) {
|
|
1535
|
+
const content = await fs3.readFile(goModPath, "utf8");
|
|
1536
|
+
const match = content.match(/^\s*module\s+(\S+)/m);
|
|
1537
|
+
const modulePath = match?.[1]?.trim() ?? null;
|
|
1538
|
+
cache.set(current, modulePath);
|
|
1539
|
+
return modulePath ?? void 0;
|
|
1540
|
+
}
|
|
1541
|
+
const parent = path3.dirname(current);
|
|
1542
|
+
if (parent === current) {
|
|
1543
|
+
cache.set(current, null);
|
|
1544
|
+
return void 0;
|
|
1545
|
+
}
|
|
1546
|
+
current = parent;
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
function rustModuleAlias(repoRelativePath) {
|
|
1550
|
+
const withoutExt = stripCodeExtension2(normalizeAlias(repoRelativePath));
|
|
1551
|
+
const trimmed = withoutExt.replace(/^src\//, "").replace(/\/mod$/i, "");
|
|
1552
|
+
if (!trimmed || trimmed === "lib" || trimmed === "main") {
|
|
1553
|
+
return "crate";
|
|
1554
|
+
}
|
|
1555
|
+
return `crate::${trimmed.replace(/\//g, "::")}`;
|
|
1556
|
+
}
|
|
1557
|
+
function candidateExtensionsFor(language) {
|
|
1558
|
+
switch (language) {
|
|
1559
|
+
case "javascript":
|
|
1560
|
+
case "jsx":
|
|
1561
|
+
case "typescript":
|
|
1562
|
+
case "tsx":
|
|
1563
|
+
return [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"];
|
|
1564
|
+
case "python":
|
|
1565
|
+
return [".py"];
|
|
1566
|
+
case "go":
|
|
1567
|
+
return [".go"];
|
|
1568
|
+
case "rust":
|
|
1569
|
+
return [".rs"];
|
|
1570
|
+
case "java":
|
|
1571
|
+
return [".java"];
|
|
1572
|
+
case "csharp":
|
|
1573
|
+
return [".cs"];
|
|
1574
|
+
case "php":
|
|
1575
|
+
return [".php"];
|
|
1576
|
+
case "c":
|
|
1577
|
+
return [".c", ".h"];
|
|
1578
|
+
case "cpp":
|
|
1579
|
+
return [".cc", ".cpp", ".cxx", ".h", ".hh", ".hpp", ".hxx"];
|
|
1580
|
+
}
|
|
1581
|
+
}
|
|
1582
|
+
async function buildCodeIndex(rootDir, manifests, analyses) {
|
|
1583
|
+
const analysesBySourceId = new Map(analyses.map((analysis) => [analysis.sourceId, analysis]));
|
|
1584
|
+
const goModuleCache = /* @__PURE__ */ new Map();
|
|
1585
|
+
const entries = [];
|
|
1586
|
+
for (const manifest of manifests) {
|
|
1587
|
+
const analysis = analysesBySourceId.get(manifest.sourceId);
|
|
1588
|
+
if (!analysis?.code) {
|
|
1589
|
+
continue;
|
|
1590
|
+
}
|
|
1591
|
+
const aliases = /* @__PURE__ */ new Set();
|
|
1592
|
+
const repoRelativePath = manifest.repoRelativePath ? normalizeAlias(manifest.repoRelativePath) : void 0;
|
|
1593
|
+
const normalizedModuleName = analysis.code.moduleName ? normalizeAlias(analysis.code.moduleName) : void 0;
|
|
1594
|
+
const normalizedNamespace = analysis.code.namespace ? normalizeAlias(analysis.code.namespace) : void 0;
|
|
1595
|
+
const basename = manifestBasenameWithoutExtension(manifest);
|
|
1596
|
+
if (repoRelativePath) {
|
|
1597
|
+
recordAlias(aliases, repoRelativePath);
|
|
1598
|
+
recordAlias(aliases, stripCodeExtension2(repoRelativePath));
|
|
1599
|
+
if (stripCodeExtension2(repoRelativePath).endsWith("/index")) {
|
|
1600
|
+
recordAlias(aliases, stripCodeExtension2(repoRelativePath).slice(0, -"/index".length));
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
recordAlias(aliases, normalizedModuleName);
|
|
1604
|
+
recordAlias(aliases, normalizedNamespace);
|
|
1605
|
+
switch (analysis.code.language) {
|
|
1606
|
+
case "python":
|
|
1607
|
+
recordAlias(aliases, normalizedModuleName?.replace(/\//g, "."));
|
|
1608
|
+
break;
|
|
1609
|
+
case "rust":
|
|
1610
|
+
if (repoRelativePath) {
|
|
1611
|
+
recordAlias(aliases, rustModuleAlias(repoRelativePath));
|
|
1612
|
+
}
|
|
1613
|
+
break;
|
|
1614
|
+
case "go": {
|
|
1615
|
+
if (normalizedNamespace) {
|
|
1616
|
+
recordAlias(aliases, normalizedNamespace);
|
|
1617
|
+
}
|
|
1618
|
+
const originalPath = manifest.originalPath ? path3.resolve(manifest.originalPath) : path3.resolve(rootDir, manifest.storedPath);
|
|
1619
|
+
const goModulePath = await readNearestGoModulePath(originalPath, goModuleCache);
|
|
1620
|
+
if (goModulePath && repoRelativePath) {
|
|
1621
|
+
const dir = path3.posix.dirname(repoRelativePath);
|
|
1622
|
+
const packageAlias = dir === "." ? goModulePath : `${goModulePath}/${dir}`;
|
|
1623
|
+
recordAlias(aliases, packageAlias);
|
|
1624
|
+
}
|
|
1625
|
+
break;
|
|
1626
|
+
}
|
|
1627
|
+
case "java":
|
|
1628
|
+
case "csharp":
|
|
1629
|
+
if (normalizedNamespace) {
|
|
1630
|
+
recordAlias(aliases, `${normalizedNamespace}.${basename}`);
|
|
1631
|
+
}
|
|
1632
|
+
break;
|
|
1633
|
+
case "php":
|
|
1634
|
+
if (normalizedNamespace) {
|
|
1635
|
+
recordAlias(aliases, `${normalizedNamespace}\\${basename}`);
|
|
1636
|
+
}
|
|
1637
|
+
break;
|
|
1638
|
+
default:
|
|
1639
|
+
break;
|
|
1640
|
+
}
|
|
1641
|
+
entries.push({
|
|
1642
|
+
sourceId: manifest.sourceId,
|
|
1643
|
+
moduleId: analysis.code.moduleId,
|
|
1644
|
+
language: analysis.code.language,
|
|
1645
|
+
repoRelativePath,
|
|
1646
|
+
originalPath: manifest.originalPath,
|
|
1647
|
+
moduleName: analysis.code.moduleName,
|
|
1648
|
+
namespace: analysis.code.namespace,
|
|
1649
|
+
aliases: [...aliases].sort((left, right) => left.localeCompare(right))
|
|
1650
|
+
});
|
|
1651
|
+
}
|
|
1652
|
+
return {
|
|
1653
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
1654
|
+
entries
|
|
1655
|
+
};
|
|
1656
|
+
}
|
|
1657
|
+
function createCodeIndexLookup(artifact) {
|
|
1658
|
+
const bySourceId = /* @__PURE__ */ new Map();
|
|
1659
|
+
const byRepoPath = /* @__PURE__ */ new Map();
|
|
1660
|
+
const byAlias = /* @__PURE__ */ new Map();
|
|
1661
|
+
for (const entry of artifact.entries) {
|
|
1662
|
+
bySourceId.set(entry.sourceId, entry);
|
|
1663
|
+
if (entry.repoRelativePath) {
|
|
1664
|
+
byRepoPath.set(normalizeAlias(entry.repoRelativePath), entry);
|
|
1665
|
+
}
|
|
1666
|
+
for (const alias of entry.aliases) {
|
|
1667
|
+
const key = normalizeAlias(alias);
|
|
1668
|
+
const bucket = byAlias.get(key) ?? [];
|
|
1669
|
+
bucket.push(entry);
|
|
1670
|
+
byAlias.set(key, bucket);
|
|
1291
1671
|
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1672
|
+
}
|
|
1673
|
+
return { artifact, bySourceId, byRepoPath, byAlias };
|
|
1674
|
+
}
|
|
1675
|
+
function aliasMatches(lookup, ...aliases) {
|
|
1676
|
+
return uniqueBy(
|
|
1677
|
+
aliases.flatMap(
|
|
1678
|
+
(alias) => alias ? lookup.byAlias.get(normalizeAlias(alias)) ?? lookup.byAlias.get(normalizeAlias(alias).toLowerCase()) ?? [] : []
|
|
1679
|
+
),
|
|
1680
|
+
(entry) => entry.sourceId
|
|
1681
|
+
);
|
|
1682
|
+
}
|
|
1683
|
+
function repoPathMatches(lookup, ...repoPaths) {
|
|
1684
|
+
return uniqueBy(
|
|
1685
|
+
repoPaths.map((repoPath) => lookup.byRepoPath.get(normalizeAlias(repoPath))).filter((entry) => Boolean(entry)),
|
|
1686
|
+
(entry) => entry.sourceId
|
|
1687
|
+
);
|
|
1295
1688
|
}
|
|
1296
|
-
function
|
|
1689
|
+
function resolvePythonRelativeAliases(repoRelativePath, specifier) {
|
|
1690
|
+
const dotMatch = specifier.match(/^\.+/);
|
|
1691
|
+
const depth = dotMatch ? dotMatch[0].length : 0;
|
|
1692
|
+
const relativeModule = specifier.slice(depth).replace(/\./g, "/");
|
|
1693
|
+
const baseDir = path3.posix.dirname(repoRelativePath);
|
|
1694
|
+
const parentDir = path3.posix.normalize(path3.posix.join(baseDir, ...Array(Math.max(depth - 1, 0)).fill("..")));
|
|
1695
|
+
const moduleBase = relativeModule ? path3.posix.join(parentDir, relativeModule) : parentDir;
|
|
1696
|
+
return uniqueBy([`${moduleBase}.py`, path3.posix.join(moduleBase, "__init__.py")], (item) => item);
|
|
1697
|
+
}
|
|
1698
|
+
function resolveRustAliases(manifest, specifier) {
|
|
1699
|
+
const repoRelativePath = manifest.repoRelativePath ? normalizeAlias(manifest.repoRelativePath) : "";
|
|
1700
|
+
const currentAlias = repoRelativePath ? rustModuleAlias(repoRelativePath) : void 0;
|
|
1701
|
+
if (!specifier.startsWith("self::") && !specifier.startsWith("super::")) {
|
|
1702
|
+
return [specifier];
|
|
1703
|
+
}
|
|
1704
|
+
if (!currentAlias) {
|
|
1705
|
+
return [];
|
|
1706
|
+
}
|
|
1707
|
+
const currentParts = currentAlias.replace(/^crate::?/, "").split("::").filter(Boolean);
|
|
1708
|
+
if (specifier.startsWith("self::")) {
|
|
1709
|
+
return [`crate${currentParts.length ? `::${currentParts.join("::")}` : ""}::${specifier.slice("self::".length)}`];
|
|
1710
|
+
}
|
|
1711
|
+
return [
|
|
1712
|
+
`crate${currentParts.length > 1 ? `::${currentParts.slice(0, -1).join("::")}` : ""}::${specifier.slice("super::".length)}`.replace(/::+/g, "::").replace(/::$/, "")
|
|
1713
|
+
];
|
|
1714
|
+
}
|
|
1715
|
+
function findImportCandidates(manifest, codeImport, lookup) {
|
|
1297
1716
|
const language = manifest.language ?? inferCodeLanguage(manifest.originalPath ?? manifest.storedPath, manifest.mimeType);
|
|
1717
|
+
const repoRelativePath = manifest.repoRelativePath ? normalizeAlias(manifest.repoRelativePath) : void 0;
|
|
1718
|
+
if (!language) {
|
|
1719
|
+
return [];
|
|
1720
|
+
}
|
|
1298
1721
|
switch (language) {
|
|
1299
1722
|
case "javascript":
|
|
1300
1723
|
case "jsx":
|
|
1301
1724
|
case "typescript":
|
|
1302
1725
|
case "tsx":
|
|
1303
|
-
return
|
|
1726
|
+
return repoRelativePath && isRelativeSpecifier(codeImport.specifier) ? repoPathMatches(lookup, ...importResolutionCandidates(repoRelativePath, codeImport.specifier, candidateExtensionsFor(language))) : aliasMatches(lookup, codeImport.specifier);
|
|
1304
1727
|
case "python":
|
|
1305
|
-
|
|
1728
|
+
if (repoRelativePath && codeImport.specifier.startsWith(".")) {
|
|
1729
|
+
return repoPathMatches(lookup, ...resolvePythonRelativeAliases(repoRelativePath, codeImport.specifier));
|
|
1730
|
+
}
|
|
1731
|
+
return aliasMatches(lookup, codeImport.specifier);
|
|
1732
|
+
case "go":
|
|
1733
|
+
case "java":
|
|
1734
|
+
case "csharp":
|
|
1735
|
+
return aliasMatches(lookup, codeImport.specifier);
|
|
1736
|
+
case "php":
|
|
1737
|
+
if (repoRelativePath && isLocalIncludeSpecifier(codeImport.specifier)) {
|
|
1738
|
+
return repoPathMatches(
|
|
1739
|
+
lookup,
|
|
1740
|
+
...importResolutionCandidates(repoRelativePath, codeImport.specifier, candidateExtensionsFor(language))
|
|
1741
|
+
);
|
|
1742
|
+
}
|
|
1743
|
+
return aliasMatches(lookup, codeImport.specifier, codeImport.specifier.replace(/\\/g, "/"));
|
|
1744
|
+
case "rust":
|
|
1745
|
+
return aliasMatches(lookup, codeImport.specifier, ...resolveRustAliases(manifest, codeImport.specifier));
|
|
1746
|
+
case "c":
|
|
1747
|
+
case "cpp":
|
|
1748
|
+
return repoRelativePath && !codeImport.isExternal ? repoPathMatches(lookup, ...importResolutionCandidates(repoRelativePath, codeImport.specifier, candidateExtensionsFor(language))) : aliasMatches(lookup, codeImport.specifier);
|
|
1306
1749
|
default:
|
|
1307
|
-
return
|
|
1750
|
+
return [];
|
|
1308
1751
|
}
|
|
1309
1752
|
}
|
|
1310
|
-
function
|
|
1753
|
+
function importLooksLocal(manifest, codeImport, candidates) {
|
|
1754
|
+
if (candidates.length > 0) {
|
|
1755
|
+
return true;
|
|
1756
|
+
}
|
|
1757
|
+
const language = manifest.language ?? inferCodeLanguage(manifest.originalPath ?? manifest.storedPath, manifest.mimeType);
|
|
1758
|
+
switch (language) {
|
|
1759
|
+
case "javascript":
|
|
1760
|
+
case "jsx":
|
|
1761
|
+
case "typescript":
|
|
1762
|
+
case "tsx":
|
|
1763
|
+
return isRelativeSpecifier(codeImport.specifier);
|
|
1764
|
+
case "python":
|
|
1765
|
+
return codeImport.specifier.startsWith(".");
|
|
1766
|
+
case "rust":
|
|
1767
|
+
return /^(crate|self|super)::/.test(codeImport.specifier);
|
|
1768
|
+
case "php":
|
|
1769
|
+
case "c":
|
|
1770
|
+
case "cpp":
|
|
1771
|
+
return !codeImport.isExternal;
|
|
1772
|
+
default:
|
|
1773
|
+
return false;
|
|
1774
|
+
}
|
|
1775
|
+
}
|
|
1776
|
+
function resolveCodeImport(manifest, codeImport, lookup) {
|
|
1777
|
+
const candidates = findImportCandidates(manifest, codeImport, lookup);
|
|
1778
|
+
const resolved = candidates.length === 1 ? candidates[0] : void 0;
|
|
1779
|
+
return {
|
|
1780
|
+
...codeImport,
|
|
1781
|
+
isExternal: importLooksLocal(manifest, codeImport, candidates) ? false : codeImport.isExternal,
|
|
1782
|
+
resolvedSourceId: resolved?.sourceId,
|
|
1783
|
+
resolvedRepoPath: resolved?.repoRelativePath
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
function enrichResolvedCodeImports(manifest, analysis, artifact) {
|
|
1787
|
+
if (!analysis.code) {
|
|
1788
|
+
return analysis;
|
|
1789
|
+
}
|
|
1790
|
+
const lookup = createCodeIndexLookup(artifact);
|
|
1791
|
+
const imports = analysis.code.imports.map((codeImport) => resolveCodeImport(manifest, codeImport, lookup));
|
|
1792
|
+
return {
|
|
1793
|
+
...analysis,
|
|
1794
|
+
code: {
|
|
1795
|
+
...analysis.code,
|
|
1796
|
+
imports,
|
|
1797
|
+
dependencies: uniqueBy(
|
|
1798
|
+
imports.filter((item) => item.isExternal).map((item) => item.specifier),
|
|
1799
|
+
(specifier) => specifier
|
|
1800
|
+
)
|
|
1801
|
+
}
|
|
1802
|
+
};
|
|
1803
|
+
}
|
|
1804
|
+
function escapeRegExp2(value) {
|
|
1311
1805
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1312
1806
|
}
|
|
1313
|
-
function analyzeCodeSource(manifest, extractedText, schemaHash) {
|
|
1807
|
+
async function analyzeCodeSource(manifest, extractedText, schemaHash) {
|
|
1314
1808
|
const language = manifest.language ?? inferCodeLanguage(manifest.originalPath ?? manifest.storedPath, manifest.mimeType) ?? "typescript";
|
|
1315
|
-
const code = language === "
|
|
1809
|
+
const code = language === "javascript" || language === "jsx" || language === "typescript" || language === "tsx" ? analyzeTypeScriptLikeCode(manifest, extractedText) : await analyzeTreeSitterCode(manifest, extractedText, language);
|
|
1316
1810
|
return {
|
|
1811
|
+
analysisVersion: 3,
|
|
1317
1812
|
sourceId: manifest.sourceId,
|
|
1318
1813
|
sourceHash: manifest.contentHash,
|
|
1319
1814
|
schemaHash,
|
|
@@ -1329,18 +1824,18 @@ function analyzeCodeSource(manifest, extractedText, schemaHash) {
|
|
|
1329
1824
|
}
|
|
1330
1825
|
|
|
1331
1826
|
// src/logs.ts
|
|
1332
|
-
import
|
|
1333
|
-
import
|
|
1827
|
+
import fs4 from "fs/promises";
|
|
1828
|
+
import path4 from "path";
|
|
1334
1829
|
import matter from "gray-matter";
|
|
1335
1830
|
async function resolveUniqueSessionPath(rootDir, operation, title, startedAt) {
|
|
1336
1831
|
const { paths } = await initWorkspace(rootDir);
|
|
1337
1832
|
await ensureDir(paths.sessionsDir);
|
|
1338
1833
|
const timestamp = startedAt.replace(/[:.]/g, "-");
|
|
1339
1834
|
const baseName = `${timestamp}-${operation}-${slugify(title)}`;
|
|
1340
|
-
let candidate =
|
|
1835
|
+
let candidate = path4.join(paths.sessionsDir, `${baseName}.md`);
|
|
1341
1836
|
let counter = 2;
|
|
1342
1837
|
while (await fileExists(candidate)) {
|
|
1343
|
-
candidate =
|
|
1838
|
+
candidate = path4.join(paths.sessionsDir, `${baseName}-${counter}.md`);
|
|
1344
1839
|
counter++;
|
|
1345
1840
|
}
|
|
1346
1841
|
return candidate;
|
|
@@ -1348,11 +1843,11 @@ async function resolveUniqueSessionPath(rootDir, operation, title, startedAt) {
|
|
|
1348
1843
|
async function appendLogEntry(rootDir, action, title, lines = []) {
|
|
1349
1844
|
const { paths } = await initWorkspace(rootDir);
|
|
1350
1845
|
await ensureDir(paths.wikiDir);
|
|
1351
|
-
const logPath =
|
|
1846
|
+
const logPath = path4.join(paths.wikiDir, "log.md");
|
|
1352
1847
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
1353
1848
|
const entry = [`## [${timestamp}] ${action} | ${title}`, ...lines.map((line) => `- ${line}`), ""].join("\n");
|
|
1354
|
-
const existing = await fileExists(logPath) ? await
|
|
1355
|
-
await
|
|
1849
|
+
const existing = await fileExists(logPath) ? await fs4.readFile(logPath, "utf8") : "# Log\n\n";
|
|
1850
|
+
await fs4.writeFile(logPath, `${existing}${entry}
|
|
1356
1851
|
`, "utf8");
|
|
1357
1852
|
}
|
|
1358
1853
|
async function recordSession(rootDir, input) {
|
|
@@ -1362,8 +1857,8 @@ async function recordSession(rootDir, input) {
|
|
|
1362
1857
|
const finishedAtIso = new Date(input.finishedAt ?? input.startedAt).toISOString();
|
|
1363
1858
|
const durationMs = Math.max(0, new Date(finishedAtIso).getTime() - new Date(startedAtIso).getTime());
|
|
1364
1859
|
const sessionPath = await resolveUniqueSessionPath(rootDir, input.operation, input.title, startedAtIso);
|
|
1365
|
-
const sessionId =
|
|
1366
|
-
const relativeSessionPath =
|
|
1860
|
+
const sessionId = path4.basename(sessionPath, ".md");
|
|
1861
|
+
const relativeSessionPath = path4.relative(rootDir, sessionPath).split(path4.sep).join(path4.posix.sep);
|
|
1367
1862
|
const frontmatter = Object.fromEntries(
|
|
1368
1863
|
Object.entries({
|
|
1369
1864
|
session_id: sessionId,
|
|
@@ -1411,7 +1906,7 @@ async function recordSession(rootDir, input) {
|
|
|
1411
1906
|
frontmatter
|
|
1412
1907
|
);
|
|
1413
1908
|
await writeFileIfChanged(sessionPath, content);
|
|
1414
|
-
const logPath =
|
|
1909
|
+
const logPath = path4.join(paths.wikiDir, "log.md");
|
|
1415
1910
|
const timestamp = startedAtIso.slice(0, 19).replace("T", " ");
|
|
1416
1911
|
const entry = [
|
|
1417
1912
|
`## [${timestamp}] ${input.operation} | ${input.title}`,
|
|
@@ -1419,8 +1914,8 @@ async function recordSession(rootDir, input) {
|
|
|
1419
1914
|
...(input.lines ?? []).map((line) => `- ${line}`),
|
|
1420
1915
|
""
|
|
1421
1916
|
].join("\n");
|
|
1422
|
-
const existing = await fileExists(logPath) ? await
|
|
1423
|
-
await
|
|
1917
|
+
const existing = await fileExists(logPath) ? await fs4.readFile(logPath, "utf8") : "# Log\n\n";
|
|
1918
|
+
await fs4.writeFile(logPath, `${existing}${entry}
|
|
1424
1919
|
`, "utf8");
|
|
1425
1920
|
return { sessionPath, sessionId };
|
|
1426
1921
|
}
|
|
@@ -1431,6 +1926,8 @@ async function appendWatchRun(rootDir, run) {
|
|
|
1431
1926
|
|
|
1432
1927
|
// src/ingest.ts
|
|
1433
1928
|
var DEFAULT_MAX_ASSET_SIZE = 10 * 1024 * 1024;
|
|
1929
|
+
var DEFAULT_MAX_DIRECTORY_FILES = 5e3;
|
|
1930
|
+
var BUILT_IN_REPO_IGNORES = /* @__PURE__ */ new Set([".git", "node_modules", "dist", "build", ".next", "coverage", ".venv", "vendor", "target"]);
|
|
1434
1931
|
function inferKind(mimeType, filePath) {
|
|
1435
1932
|
if (inferCodeLanguage(filePath, mimeType)) {
|
|
1436
1933
|
return "code";
|
|
@@ -1462,9 +1959,67 @@ function guessMimeType(target) {
|
|
|
1462
1959
|
function normalizeIngestOptions(options) {
|
|
1463
1960
|
return {
|
|
1464
1961
|
includeAssets: options?.includeAssets ?? true,
|
|
1465
|
-
maxAssetSize: Math.max(0, Math.floor(options?.maxAssetSize ?? DEFAULT_MAX_ASSET_SIZE))
|
|
1962
|
+
maxAssetSize: Math.max(0, Math.floor(options?.maxAssetSize ?? DEFAULT_MAX_ASSET_SIZE)),
|
|
1963
|
+
repoRoot: options?.repoRoot ? path5.resolve(options.repoRoot) : void 0,
|
|
1964
|
+
include: (options?.include ?? []).map((pattern) => pattern.trim()).filter(Boolean),
|
|
1965
|
+
exclude: (options?.exclude ?? []).map((pattern) => pattern.trim()).filter(Boolean),
|
|
1966
|
+
maxFiles: Math.max(1, Math.floor(options?.maxFiles ?? DEFAULT_MAX_DIRECTORY_FILES)),
|
|
1967
|
+
gitignore: options?.gitignore ?? true
|
|
1466
1968
|
};
|
|
1467
1969
|
}
|
|
1970
|
+
function matchesAnyGlob(relativePath, patterns) {
|
|
1971
|
+
return patterns.some(
|
|
1972
|
+
(pattern) => path5.matchesGlob(relativePath, pattern) || path5.matchesGlob(path5.posix.basename(relativePath), pattern)
|
|
1973
|
+
);
|
|
1974
|
+
}
|
|
1975
|
+
function supportedDirectoryKind(sourceKind) {
|
|
1976
|
+
return sourceKind !== "binary";
|
|
1977
|
+
}
|
|
1978
|
+
async function findNearestGitRoot(startPath) {
|
|
1979
|
+
let current = path5.resolve(startPath);
|
|
1980
|
+
try {
|
|
1981
|
+
const stat = await fs5.stat(current);
|
|
1982
|
+
if (!stat.isDirectory()) {
|
|
1983
|
+
current = path5.dirname(current);
|
|
1984
|
+
}
|
|
1985
|
+
} catch {
|
|
1986
|
+
current = path5.dirname(current);
|
|
1987
|
+
}
|
|
1988
|
+
while (true) {
|
|
1989
|
+
if (await fileExists(path5.join(current, ".git"))) {
|
|
1990
|
+
return current;
|
|
1991
|
+
}
|
|
1992
|
+
const parent = path5.dirname(current);
|
|
1993
|
+
if (parent === current) {
|
|
1994
|
+
return null;
|
|
1995
|
+
}
|
|
1996
|
+
current = parent;
|
|
1997
|
+
}
|
|
1998
|
+
}
|
|
1999
|
+
function withinRoot(rootPath, targetPath) {
|
|
2000
|
+
const relative = path5.relative(rootPath, targetPath);
|
|
2001
|
+
return relative === "" || !relative.startsWith("..") && !path5.isAbsolute(relative);
|
|
2002
|
+
}
|
|
2003
|
+
function repoRelativePathFor(absolutePath, repoRoot) {
|
|
2004
|
+
if (!repoRoot || !withinRoot(repoRoot, absolutePath)) {
|
|
2005
|
+
return void 0;
|
|
2006
|
+
}
|
|
2007
|
+
const relative = toPosix(path5.relative(repoRoot, absolutePath));
|
|
2008
|
+
return relative && !relative.startsWith("..") ? relative : void 0;
|
|
2009
|
+
}
|
|
2010
|
+
function normalizeOriginUrl(input) {
|
|
2011
|
+
try {
|
|
2012
|
+
return new URL(input).toString();
|
|
2013
|
+
} catch {
|
|
2014
|
+
return input;
|
|
2015
|
+
}
|
|
2016
|
+
}
|
|
2017
|
+
function manifestMatchesOrigin(manifest, prepared) {
|
|
2018
|
+
if (prepared.originType === "url") {
|
|
2019
|
+
return Boolean(prepared.url && manifest.url && normalizeOriginUrl(manifest.url) === normalizeOriginUrl(prepared.url));
|
|
2020
|
+
}
|
|
2021
|
+
return Boolean(prepared.originalPath && manifest.originalPath && toPosix(manifest.originalPath) === toPosix(prepared.originalPath));
|
|
2022
|
+
}
|
|
1468
2023
|
function buildCompositeHash(payloadBytes, attachments = []) {
|
|
1469
2024
|
if (!attachments.length) {
|
|
1470
2025
|
return sha256(payloadBytes);
|
|
@@ -1473,7 +2028,7 @@ function buildCompositeHash(payloadBytes, attachments = []) {
|
|
|
1473
2028
|
return sha256(`${sha256(payloadBytes)}|${attachmentSignature}`);
|
|
1474
2029
|
}
|
|
1475
2030
|
function sanitizeAssetRelativePath(value) {
|
|
1476
|
-
const normalized =
|
|
2031
|
+
const normalized = path5.posix.normalize(value.replace(/\\/g, "/"));
|
|
1477
2032
|
const segments = normalized.split("/").filter(Boolean).map((segment) => {
|
|
1478
2033
|
if (segment === ".") {
|
|
1479
2034
|
return "";
|
|
@@ -1493,7 +2048,7 @@ function normalizeLocalReference(value) {
|
|
|
1493
2048
|
return null;
|
|
1494
2049
|
}
|
|
1495
2050
|
const lowered = candidate.toLowerCase();
|
|
1496
|
-
if (lowered.startsWith("http://") || lowered.startsWith("https://") || lowered.startsWith("data:") || lowered.startsWith("mailto:") || lowered.startsWith("#") ||
|
|
2051
|
+
if (lowered.startsWith("http://") || lowered.startsWith("https://") || lowered.startsWith("data:") || lowered.startsWith("mailto:") || lowered.startsWith("#") || path5.isAbsolute(candidate)) {
|
|
1497
2052
|
return null;
|
|
1498
2053
|
}
|
|
1499
2054
|
return candidate.replace(/\\/g, "/");
|
|
@@ -1555,18 +2110,107 @@ async function convertHtmlToMarkdown(html, url) {
|
|
|
1555
2110
|
};
|
|
1556
2111
|
}
|
|
1557
2112
|
async function readManifestByHash(manifestsDir, contentHash) {
|
|
1558
|
-
const entries = await
|
|
2113
|
+
const entries = await fs5.readdir(manifestsDir, { withFileTypes: true }).catch(() => []);
|
|
1559
2114
|
for (const entry of entries) {
|
|
1560
2115
|
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
1561
2116
|
continue;
|
|
1562
2117
|
}
|
|
1563
|
-
const manifest = await readJsonFile(
|
|
2118
|
+
const manifest = await readJsonFile(path5.join(manifestsDir, entry.name));
|
|
1564
2119
|
if (manifest?.contentHash === contentHash) {
|
|
1565
2120
|
return manifest;
|
|
1566
2121
|
}
|
|
1567
2122
|
}
|
|
1568
2123
|
return null;
|
|
1569
2124
|
}
|
|
2125
|
+
async function readManifestByOrigin(manifestsDir, prepared) {
|
|
2126
|
+
const entries = await fs5.readdir(manifestsDir, { withFileTypes: true }).catch(() => []);
|
|
2127
|
+
for (const entry of entries) {
|
|
2128
|
+
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
2129
|
+
continue;
|
|
2130
|
+
}
|
|
2131
|
+
const manifest = await readJsonFile(path5.join(manifestsDir, entry.name));
|
|
2132
|
+
if (manifest && manifestMatchesOrigin(manifest, prepared)) {
|
|
2133
|
+
return manifest;
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
return null;
|
|
2137
|
+
}
|
|
2138
|
+
async function loadGitignoreMatcher(repoRoot, enabled) {
|
|
2139
|
+
if (!enabled) {
|
|
2140
|
+
return null;
|
|
2141
|
+
}
|
|
2142
|
+
const gitignorePath = path5.join(repoRoot, ".gitignore");
|
|
2143
|
+
if (!await fileExists(gitignorePath)) {
|
|
2144
|
+
return null;
|
|
2145
|
+
}
|
|
2146
|
+
const matcher = ignore();
|
|
2147
|
+
matcher.add(await fs5.readFile(gitignorePath, "utf8"));
|
|
2148
|
+
return matcher;
|
|
2149
|
+
}
|
|
2150
|
+
function builtInIgnoreReason(relativePath) {
|
|
2151
|
+
for (const segment of relativePath.split("/")) {
|
|
2152
|
+
if (BUILT_IN_REPO_IGNORES.has(segment)) {
|
|
2153
|
+
return `built_in_ignore:${segment}`;
|
|
2154
|
+
}
|
|
2155
|
+
}
|
|
2156
|
+
return null;
|
|
2157
|
+
}
|
|
2158
|
+
async function collectDirectoryFiles(rootDir, inputDir, repoRoot, options) {
|
|
2159
|
+
const matcher = await loadGitignoreMatcher(repoRoot, options.gitignore);
|
|
2160
|
+
const skipped = [];
|
|
2161
|
+
const files = [];
|
|
2162
|
+
const stack = [inputDir];
|
|
2163
|
+
while (stack.length > 0) {
|
|
2164
|
+
const currentDir = stack.pop();
|
|
2165
|
+
if (!currentDir) {
|
|
2166
|
+
continue;
|
|
2167
|
+
}
|
|
2168
|
+
const entries = await fs5.readdir(currentDir, { withFileTypes: true });
|
|
2169
|
+
entries.sort((left, right) => left.name.localeCompare(right.name));
|
|
2170
|
+
for (const entry of entries) {
|
|
2171
|
+
const absolutePath = path5.join(currentDir, entry.name);
|
|
2172
|
+
const relativeToRepo = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path5.relative(inputDir, absolutePath));
|
|
2173
|
+
const relativePath = relativeToRepo || entry.name;
|
|
2174
|
+
const builtInReason = builtInIgnoreReason(relativePath);
|
|
2175
|
+
if (builtInReason) {
|
|
2176
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: builtInReason });
|
|
2177
|
+
continue;
|
|
2178
|
+
}
|
|
2179
|
+
if (matcher?.ignores(relativePath)) {
|
|
2180
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: "gitignore" });
|
|
2181
|
+
continue;
|
|
2182
|
+
}
|
|
2183
|
+
if (matchesAnyGlob(relativePath, options.exclude)) {
|
|
2184
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: "exclude_glob" });
|
|
2185
|
+
continue;
|
|
2186
|
+
}
|
|
2187
|
+
if (entry.isDirectory()) {
|
|
2188
|
+
stack.push(absolutePath);
|
|
2189
|
+
continue;
|
|
2190
|
+
}
|
|
2191
|
+
if (!entry.isFile()) {
|
|
2192
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: "unsupported_entry" });
|
|
2193
|
+
continue;
|
|
2194
|
+
}
|
|
2195
|
+
if (options.include.length > 0 && !matchesAnyGlob(relativePath, options.include)) {
|
|
2196
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: "include_glob" });
|
|
2197
|
+
continue;
|
|
2198
|
+
}
|
|
2199
|
+
const mimeType = guessMimeType(absolutePath);
|
|
2200
|
+
const sourceKind = inferKind(mimeType, absolutePath);
|
|
2201
|
+
if (!supportedDirectoryKind(sourceKind)) {
|
|
2202
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
|
|
2203
|
+
continue;
|
|
2204
|
+
}
|
|
2205
|
+
if (files.length >= options.maxFiles) {
|
|
2206
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: "max_files" });
|
|
2207
|
+
continue;
|
|
2208
|
+
}
|
|
2209
|
+
files.push(absolutePath);
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
return { files: files.sort((left, right) => left.localeCompare(right)), skipped };
|
|
2213
|
+
}
|
|
1570
2214
|
function resolveUrlMimeType(input, response) {
|
|
1571
2215
|
const headerMimeType = response.headers.get("content-type")?.split(";")[0]?.trim();
|
|
1572
2216
|
const guessedMimeType = guessMimeType(new URL(input).pathname);
|
|
@@ -1581,12 +2225,12 @@ function resolveUrlMimeType(input, response) {
|
|
|
1581
2225
|
function buildRemoteAssetRelativePath(assetUrl, mimeType) {
|
|
1582
2226
|
const url = new URL(assetUrl);
|
|
1583
2227
|
const normalized = sanitizeAssetRelativePath(`${url.hostname}${url.pathname || "/asset"}`);
|
|
1584
|
-
const extension =
|
|
1585
|
-
const directory =
|
|
1586
|
-
const basename = extension ?
|
|
2228
|
+
const extension = path5.posix.extname(normalized);
|
|
2229
|
+
const directory = path5.posix.dirname(normalized);
|
|
2230
|
+
const basename = extension ? path5.posix.basename(normalized, extension) : path5.posix.basename(normalized);
|
|
1587
2231
|
const resolvedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
|
|
1588
2232
|
const hashedName = `${basename || "asset"}-${sha256(assetUrl).slice(0, 8)}${resolvedExtension}`;
|
|
1589
|
-
return directory === "." ? hashedName :
|
|
2233
|
+
return directory === "." ? hashedName : path5.posix.join(directory, hashedName);
|
|
1590
2234
|
}
|
|
1591
2235
|
async function readResponseBytesWithinLimit(response, maxBytes) {
|
|
1592
2236
|
const contentLength = Number.parseInt(response.headers.get("content-length") ?? "", 10);
|
|
@@ -1710,26 +2354,38 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
1710
2354
|
await ensureDir(paths.extractsDir);
|
|
1711
2355
|
const attachments = prepared.attachments ?? [];
|
|
1712
2356
|
const contentHash = prepared.contentHash ?? buildCompositeHash(prepared.payloadBytes, attachments);
|
|
1713
|
-
const
|
|
1714
|
-
|
|
1715
|
-
|
|
2357
|
+
const existingByOrigin = await readManifestByOrigin(paths.manifestsDir, prepared);
|
|
2358
|
+
const existingByHash = existingByOrigin ? null : await readManifestByHash(paths.manifestsDir, contentHash);
|
|
2359
|
+
if (existingByOrigin && existingByOrigin.contentHash === contentHash && existingByOrigin.title === prepared.title && existingByOrigin.sourceKind === prepared.sourceKind && existingByOrigin.language === prepared.language && existingByOrigin.mimeType === prepared.mimeType && existingByOrigin.repoRelativePath === prepared.repoRelativePath) {
|
|
2360
|
+
return { manifest: existingByOrigin, isNew: false, wasUpdated: false };
|
|
2361
|
+
}
|
|
2362
|
+
if (existingByHash) {
|
|
2363
|
+
return { manifest: existingByHash, isNew: false, wasUpdated: false };
|
|
1716
2364
|
}
|
|
2365
|
+
const previous = existingByOrigin ?? void 0;
|
|
2366
|
+
const sourceId = previous?.sourceId ?? `${slugify(prepared.title)}-${contentHash.slice(0, 8)}`;
|
|
1717
2367
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1718
|
-
const
|
|
1719
|
-
const
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
2368
|
+
const storedPath = path5.join(paths.rawSourcesDir, `${sourceId}${prepared.storedExtension}`);
|
|
2369
|
+
const extractedTextPath = prepared.extractedText ? path5.join(paths.extractsDir, `${sourceId}.md`) : void 0;
|
|
2370
|
+
const attachmentsDir = path5.join(paths.rawAssetsDir, sourceId);
|
|
2371
|
+
if (previous?.storedPath) {
|
|
2372
|
+
await fs5.rm(path5.resolve(rootDir, previous.storedPath), { force: true });
|
|
2373
|
+
}
|
|
2374
|
+
if (previous?.extractedTextPath) {
|
|
2375
|
+
await fs5.rm(path5.resolve(rootDir, previous.extractedTextPath), { force: true });
|
|
2376
|
+
}
|
|
2377
|
+
await fs5.rm(attachmentsDir, { recursive: true, force: true });
|
|
2378
|
+
await fs5.writeFile(storedPath, prepared.payloadBytes);
|
|
2379
|
+
if (prepared.extractedText && extractedTextPath) {
|
|
2380
|
+
await fs5.writeFile(extractedTextPath, prepared.extractedText, "utf8");
|
|
1725
2381
|
}
|
|
1726
2382
|
const manifestAttachments = [];
|
|
1727
2383
|
for (const attachment of attachments) {
|
|
1728
|
-
const absoluteAttachmentPath =
|
|
1729
|
-
await ensureDir(
|
|
1730
|
-
await
|
|
2384
|
+
const absoluteAttachmentPath = path5.join(attachmentsDir, attachment.relativePath);
|
|
2385
|
+
await ensureDir(path5.dirname(absoluteAttachmentPath));
|
|
2386
|
+
await fs5.writeFile(absoluteAttachmentPath, attachment.bytes);
|
|
1731
2387
|
manifestAttachments.push({
|
|
1732
|
-
path: toPosix(
|
|
2388
|
+
path: toPosix(path5.relative(rootDir, absoluteAttachmentPath)),
|
|
1733
2389
|
mimeType: attachment.mimeType,
|
|
1734
2390
|
originalPath: attachment.originalPath
|
|
1735
2391
|
});
|
|
@@ -1741,37 +2397,39 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
1741
2397
|
sourceKind: prepared.sourceKind,
|
|
1742
2398
|
language: prepared.language,
|
|
1743
2399
|
originalPath: prepared.originalPath,
|
|
2400
|
+
repoRelativePath: prepared.repoRelativePath,
|
|
1744
2401
|
url: prepared.url,
|
|
1745
|
-
storedPath: toPosix(
|
|
1746
|
-
extractedTextPath: extractedTextPath ? toPosix(
|
|
2402
|
+
storedPath: toPosix(path5.relative(rootDir, storedPath)),
|
|
2403
|
+
extractedTextPath: extractedTextPath ? toPosix(path5.relative(rootDir, extractedTextPath)) : void 0,
|
|
1747
2404
|
mimeType: prepared.mimeType,
|
|
1748
2405
|
contentHash,
|
|
1749
|
-
createdAt: now,
|
|
2406
|
+
createdAt: previous?.createdAt ?? now,
|
|
1750
2407
|
updatedAt: now,
|
|
1751
2408
|
attachments: manifestAttachments.length ? manifestAttachments : void 0
|
|
1752
2409
|
};
|
|
1753
|
-
await writeJsonFile(
|
|
2410
|
+
await writeJsonFile(path5.join(paths.manifestsDir, `${sourceId}.json`), manifest);
|
|
1754
2411
|
await appendLogEntry(rootDir, "ingest", prepared.title, [
|
|
1755
2412
|
`source_id=${sourceId}`,
|
|
1756
2413
|
`kind=${prepared.sourceKind}`,
|
|
1757
2414
|
`attachments=${manifestAttachments.length}`,
|
|
2415
|
+
`updated=${previous ? "true" : "false"}`,
|
|
1758
2416
|
...prepared.logDetails ?? []
|
|
1759
2417
|
]);
|
|
1760
|
-
return { manifest, isNew:
|
|
2418
|
+
return { manifest, isNew: !previous, wasUpdated: Boolean(previous) };
|
|
1761
2419
|
}
|
|
1762
|
-
async function prepareFileInput(_rootDir, absoluteInput) {
|
|
1763
|
-
const payloadBytes = await
|
|
2420
|
+
async function prepareFileInput(_rootDir, absoluteInput, repoRoot) {
|
|
2421
|
+
const payloadBytes = await fs5.readFile(absoluteInput);
|
|
1764
2422
|
const mimeType = guessMimeType(absoluteInput);
|
|
1765
2423
|
const sourceKind = inferKind(mimeType, absoluteInput);
|
|
1766
2424
|
const language = inferCodeLanguage(absoluteInput, mimeType);
|
|
1767
|
-
const storedExtension =
|
|
2425
|
+
const storedExtension = path5.extname(absoluteInput) || `.${mime.extension(mimeType) || "bin"}`;
|
|
1768
2426
|
let title;
|
|
1769
2427
|
let extractedText;
|
|
1770
2428
|
if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
|
|
1771
2429
|
extractedText = payloadBytes.toString("utf8");
|
|
1772
|
-
title = titleFromText(
|
|
2430
|
+
title = titleFromText(path5.basename(absoluteInput, path5.extname(absoluteInput)), extractedText);
|
|
1773
2431
|
} else {
|
|
1774
|
-
title =
|
|
2432
|
+
title = path5.basename(absoluteInput, path5.extname(absoluteInput));
|
|
1775
2433
|
}
|
|
1776
2434
|
return {
|
|
1777
2435
|
title,
|
|
@@ -1779,6 +2437,7 @@ async function prepareFileInput(_rootDir, absoluteInput) {
|
|
|
1779
2437
|
sourceKind,
|
|
1780
2438
|
language,
|
|
1781
2439
|
originalPath: toPosix(absoluteInput),
|
|
2440
|
+
repoRelativePath: repoRelativePathFor(absoluteInput, repoRoot),
|
|
1782
2441
|
mimeType,
|
|
1783
2442
|
storedExtension,
|
|
1784
2443
|
payloadBytes,
|
|
@@ -1840,7 +2499,7 @@ async function prepareUrlInput(input, options) {
|
|
|
1840
2499
|
sourceKind = "markdown";
|
|
1841
2500
|
storedExtension = ".md";
|
|
1842
2501
|
} else {
|
|
1843
|
-
const extension =
|
|
2502
|
+
const extension = path5.extname(inputUrl.pathname);
|
|
1844
2503
|
storedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
|
|
1845
2504
|
if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
|
|
1846
2505
|
extractedText = payloadBytes.toString("utf8");
|
|
@@ -1890,14 +2549,14 @@ async function collectInboxAttachmentRefs(inputDir, files) {
|
|
|
1890
2549
|
if (sourceKind !== "markdown") {
|
|
1891
2550
|
continue;
|
|
1892
2551
|
}
|
|
1893
|
-
const content = await
|
|
2552
|
+
const content = await fs5.readFile(absolutePath, "utf8");
|
|
1894
2553
|
const refs = extractMarkdownReferences(content);
|
|
1895
2554
|
if (!refs.length) {
|
|
1896
2555
|
continue;
|
|
1897
2556
|
}
|
|
1898
2557
|
const sourceRefs = [];
|
|
1899
2558
|
for (const ref of refs) {
|
|
1900
|
-
const resolved =
|
|
2559
|
+
const resolved = path5.resolve(path5.dirname(absolutePath), ref);
|
|
1901
2560
|
if (!resolved.startsWith(inputDir) || !await fileExists(resolved)) {
|
|
1902
2561
|
continue;
|
|
1903
2562
|
}
|
|
@@ -1931,12 +2590,12 @@ function rewriteMarkdownReferences(content, replacements) {
|
|
|
1931
2590
|
});
|
|
1932
2591
|
}
|
|
1933
2592
|
async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
|
|
1934
|
-
const originalBytes = await
|
|
2593
|
+
const originalBytes = await fs5.readFile(absolutePath);
|
|
1935
2594
|
const originalText = originalBytes.toString("utf8");
|
|
1936
|
-
const title = titleFromText(
|
|
2595
|
+
const title = titleFromText(path5.basename(absolutePath, path5.extname(absolutePath)), originalText);
|
|
1937
2596
|
const attachments = [];
|
|
1938
2597
|
for (const attachmentRef of attachmentRefs) {
|
|
1939
|
-
const bytes = await
|
|
2598
|
+
const bytes = await fs5.readFile(attachmentRef.absolutePath);
|
|
1940
2599
|
attachments.push({
|
|
1941
2600
|
relativePath: sanitizeAssetRelativePath(attachmentRef.relativeRef),
|
|
1942
2601
|
mimeType: guessMimeType(attachmentRef.absolutePath),
|
|
@@ -1959,7 +2618,7 @@ async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
|
|
|
1959
2618
|
sourceKind: "markdown",
|
|
1960
2619
|
originalPath: toPosix(absolutePath),
|
|
1961
2620
|
mimeType: "text/markdown",
|
|
1962
|
-
storedExtension:
|
|
2621
|
+
storedExtension: path5.extname(absolutePath) || ".md",
|
|
1963
2622
|
payloadBytes: Buffer.from(rewrittenText, "utf8"),
|
|
1964
2623
|
extractedText: rewrittenText,
|
|
1965
2624
|
attachments,
|
|
@@ -1972,13 +2631,53 @@ function isSupportedInboxKind(sourceKind) {
|
|
|
1972
2631
|
async function ingestInput(rootDir, input, options) {
|
|
1973
2632
|
const { paths } = await initWorkspace(rootDir);
|
|
1974
2633
|
const normalizedOptions = normalizeIngestOptions(options);
|
|
1975
|
-
const
|
|
2634
|
+
const absoluteInput = path5.resolve(rootDir, input);
|
|
2635
|
+
const repoRoot = /^https?:\/\//i.test(input) || normalizedOptions.repoRoot ? normalizedOptions.repoRoot : await findNearestGitRoot(absoluteInput).then((value) => value ?? path5.dirname(absoluteInput));
|
|
2636
|
+
const prepared = /^https?:\/\//i.test(input) ? await prepareUrlInput(input, normalizedOptions) : await prepareFileInput(rootDir, absoluteInput, repoRoot);
|
|
1976
2637
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
1977
2638
|
return result.manifest;
|
|
1978
2639
|
}
|
|
2640
|
+
async function ingestDirectory(rootDir, inputDir, options) {
|
|
2641
|
+
const { paths } = await initWorkspace(rootDir);
|
|
2642
|
+
const normalizedOptions = normalizeIngestOptions(options);
|
|
2643
|
+
const absoluteInputDir = path5.resolve(rootDir, inputDir);
|
|
2644
|
+
const repoRoot = normalizedOptions.repoRoot ?? await findNearestGitRoot(absoluteInputDir) ?? absoluteInputDir;
|
|
2645
|
+
if (!await fileExists(absoluteInputDir)) {
|
|
2646
|
+
throw new Error(`Directory not found: ${absoluteInputDir}`);
|
|
2647
|
+
}
|
|
2648
|
+
const { files, skipped } = await collectDirectoryFiles(rootDir, absoluteInputDir, repoRoot, normalizedOptions);
|
|
2649
|
+
const imported = [];
|
|
2650
|
+
const updated = [];
|
|
2651
|
+
for (const absolutePath of files) {
|
|
2652
|
+
const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot);
|
|
2653
|
+
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
2654
|
+
if (result.isNew) {
|
|
2655
|
+
imported.push(result.manifest);
|
|
2656
|
+
} else if (result.wasUpdated) {
|
|
2657
|
+
updated.push(result.manifest);
|
|
2658
|
+
} else {
|
|
2659
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: "duplicate_content" });
|
|
2660
|
+
}
|
|
2661
|
+
}
|
|
2662
|
+
await appendLogEntry(rootDir, "ingest_directory", toPosix(path5.relative(rootDir, absoluteInputDir)) || ".", [
|
|
2663
|
+
`repo_root=${toPosix(path5.relative(rootDir, repoRoot)) || "."}`,
|
|
2664
|
+
`scanned=${files.length}`,
|
|
2665
|
+
`imported=${imported.length}`,
|
|
2666
|
+
`updated=${updated.length}`,
|
|
2667
|
+
`skipped=${skipped.length}`
|
|
2668
|
+
]);
|
|
2669
|
+
return {
|
|
2670
|
+
inputDir: absoluteInputDir,
|
|
2671
|
+
repoRoot,
|
|
2672
|
+
scannedCount: files.length,
|
|
2673
|
+
imported,
|
|
2674
|
+
updated,
|
|
2675
|
+
skipped
|
|
2676
|
+
};
|
|
2677
|
+
}
|
|
1979
2678
|
async function importInbox(rootDir, inputDir) {
|
|
1980
2679
|
const { paths } = await initWorkspace(rootDir);
|
|
1981
|
-
const effectiveInputDir =
|
|
2680
|
+
const effectiveInputDir = path5.resolve(rootDir, inputDir ?? paths.inboxDir);
|
|
1982
2681
|
if (!await fileExists(effectiveInputDir)) {
|
|
1983
2682
|
throw new Error(`Inbox directory not found: ${effectiveInputDir}`);
|
|
1984
2683
|
}
|
|
@@ -1989,31 +2688,31 @@ async function importInbox(rootDir, inputDir) {
|
|
|
1989
2688
|
const skipped = [];
|
|
1990
2689
|
let attachmentCount = 0;
|
|
1991
2690
|
for (const absolutePath of files) {
|
|
1992
|
-
const basename =
|
|
2691
|
+
const basename = path5.basename(absolutePath);
|
|
1993
2692
|
if (basename.startsWith(".")) {
|
|
1994
|
-
skipped.push({ path: toPosix(
|
|
2693
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: "hidden_file" });
|
|
1995
2694
|
continue;
|
|
1996
2695
|
}
|
|
1997
2696
|
if (claimedAttachments.has(absolutePath)) {
|
|
1998
|
-
skipped.push({ path: toPosix(
|
|
2697
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: "referenced_attachment" });
|
|
1999
2698
|
continue;
|
|
2000
2699
|
}
|
|
2001
2700
|
const mimeType = guessMimeType(absolutePath);
|
|
2002
2701
|
const sourceKind = inferKind(mimeType, absolutePath);
|
|
2003
2702
|
if (!isSupportedInboxKind(sourceKind)) {
|
|
2004
|
-
skipped.push({ path: toPosix(
|
|
2703
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
|
|
2005
2704
|
continue;
|
|
2006
2705
|
}
|
|
2007
2706
|
const prepared = sourceKind === "markdown" && refsBySource.has(absolutePath) ? await prepareInboxMarkdownInput(absolutePath, refsBySource.get(absolutePath) ?? []) : await prepareFileInput(rootDir, absolutePath);
|
|
2008
2707
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
2009
2708
|
if (!result.isNew) {
|
|
2010
|
-
skipped.push({ path: toPosix(
|
|
2709
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: "duplicate_content" });
|
|
2011
2710
|
continue;
|
|
2012
2711
|
}
|
|
2013
2712
|
attachmentCount += result.manifest.attachments?.length ?? 0;
|
|
2014
2713
|
imported.push(result.manifest);
|
|
2015
2714
|
}
|
|
2016
|
-
await appendLogEntry(rootDir, "inbox_import", toPosix(
|
|
2715
|
+
await appendLogEntry(rootDir, "inbox_import", toPosix(path5.relative(rootDir, effectiveInputDir)) || ".", [
|
|
2017
2716
|
`scanned=${files.length}`,
|
|
2018
2717
|
`imported=${imported.length}`,
|
|
2019
2718
|
`attachments=${attachmentCount}`,
|
|
@@ -2032,9 +2731,9 @@ async function listManifests(rootDir) {
|
|
|
2032
2731
|
if (!await fileExists(paths.manifestsDir)) {
|
|
2033
2732
|
return [];
|
|
2034
2733
|
}
|
|
2035
|
-
const entries = await
|
|
2734
|
+
const entries = await fs5.readdir(paths.manifestsDir);
|
|
2036
2735
|
const manifests = await Promise.all(
|
|
2037
|
-
entries.filter((entry) => entry.endsWith(".json")).map((entry) => readJsonFile(
|
|
2736
|
+
entries.filter((entry) => entry.endsWith(".json")).map((entry) => readJsonFile(path5.join(paths.manifestsDir, entry)))
|
|
2038
2737
|
);
|
|
2039
2738
|
return manifests.filter((manifest) => Boolean(manifest));
|
|
2040
2739
|
}
|
|
@@ -2042,28 +2741,28 @@ async function readExtractedText(rootDir, manifest) {
|
|
|
2042
2741
|
if (!manifest.extractedTextPath) {
|
|
2043
2742
|
return void 0;
|
|
2044
2743
|
}
|
|
2045
|
-
const absolutePath =
|
|
2744
|
+
const absolutePath = path5.resolve(rootDir, manifest.extractedTextPath);
|
|
2046
2745
|
if (!await fileExists(absolutePath)) {
|
|
2047
2746
|
return void 0;
|
|
2048
2747
|
}
|
|
2049
|
-
return
|
|
2748
|
+
return fs5.readFile(absolutePath, "utf8");
|
|
2050
2749
|
}
|
|
2051
2750
|
|
|
2052
2751
|
// src/mcp.ts
|
|
2053
|
-
import
|
|
2054
|
-
import
|
|
2752
|
+
import fs12 from "fs/promises";
|
|
2753
|
+
import path15 from "path";
|
|
2055
2754
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2056
2755
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2057
2756
|
import { z as z7 } from "zod";
|
|
2058
2757
|
|
|
2059
2758
|
// src/schema.ts
|
|
2060
|
-
import
|
|
2061
|
-
import
|
|
2759
|
+
import fs6 from "fs/promises";
|
|
2760
|
+
import path6 from "path";
|
|
2062
2761
|
function normalizeSchemaContent(content) {
|
|
2063
2762
|
return content.trim() ? content.trim() : defaultVaultSchema().trim();
|
|
2064
2763
|
}
|
|
2065
2764
|
async function readSchemaFile(schemaPath, fallback = defaultVaultSchema()) {
|
|
2066
|
-
const content = await fileExists(schemaPath) ? await
|
|
2765
|
+
const content = await fileExists(schemaPath) ? await fs6.readFile(schemaPath, "utf8") : fallback;
|
|
2067
2766
|
const normalized = normalizeSchemaContent(content);
|
|
2068
2767
|
return {
|
|
2069
2768
|
path: schemaPath,
|
|
@@ -2072,7 +2771,7 @@ async function readSchemaFile(schemaPath, fallback = defaultVaultSchema()) {
|
|
|
2072
2771
|
};
|
|
2073
2772
|
}
|
|
2074
2773
|
function resolveProjectSchemaPath(rootDir, schemaPath) {
|
|
2075
|
-
return
|
|
2774
|
+
return path6.resolve(rootDir, schemaPath);
|
|
2076
2775
|
}
|
|
2077
2776
|
function composeVaultSchema(root, projectSchemas = []) {
|
|
2078
2777
|
if (!projectSchemas.length) {
|
|
@@ -2088,7 +2787,7 @@ function composeVaultSchema(root, projectSchemas = []) {
|
|
|
2088
2787
|
(schema) => [
|
|
2089
2788
|
`## Project Schema`,
|
|
2090
2789
|
"",
|
|
2091
|
-
`Path: ${toPosix(
|
|
2790
|
+
`Path: ${toPosix(path6.relative(path6.dirname(root.path), schema.path) || schema.path)}`,
|
|
2092
2791
|
"",
|
|
2093
2792
|
schema.content
|
|
2094
2793
|
].join("\n")
|
|
@@ -2164,14 +2863,15 @@ function buildSchemaPrompt(schema, instruction) {
|
|
|
2164
2863
|
}
|
|
2165
2864
|
|
|
2166
2865
|
// src/vault.ts
|
|
2167
|
-
import
|
|
2168
|
-
import
|
|
2866
|
+
import fs11 from "fs/promises";
|
|
2867
|
+
import path14 from "path";
|
|
2169
2868
|
import matter7 from "gray-matter";
|
|
2170
2869
|
import { z as z6 } from "zod";
|
|
2171
2870
|
|
|
2172
2871
|
// src/analysis.ts
|
|
2173
|
-
import
|
|
2872
|
+
import path7 from "path";
|
|
2174
2873
|
import { z } from "zod";
|
|
2874
|
+
var ANALYSIS_FORMAT_VERSION = 3;
|
|
2175
2875
|
var sourceAnalysisSchema = z.object({
|
|
2176
2876
|
title: z.string().min(1),
|
|
2177
2877
|
summary: z.string().min(1),
|
|
@@ -2272,6 +2972,7 @@ function heuristicAnalysis(manifest, text, schemaHash) {
|
|
|
2272
2972
|
}));
|
|
2273
2973
|
const claimSentences = normalized.split(/(?<=[.!?])\s+/).filter(Boolean).slice(0, 4);
|
|
2274
2974
|
return {
|
|
2975
|
+
analysisVersion: ANALYSIS_FORMAT_VERSION,
|
|
2275
2976
|
sourceId: manifest.sourceId,
|
|
2276
2977
|
sourceHash: manifest.contentHash,
|
|
2277
2978
|
schemaHash,
|
|
@@ -2316,6 +3017,7 @@ ${truncate(text, 18e3)}`
|
|
|
2316
3017
|
sourceAnalysisSchema
|
|
2317
3018
|
);
|
|
2318
3019
|
return {
|
|
3020
|
+
analysisVersion: ANALYSIS_FORMAT_VERSION,
|
|
2319
3021
|
sourceId: manifest.sourceId,
|
|
2320
3022
|
sourceHash: manifest.contentHash,
|
|
2321
3023
|
schemaHash: schema.hash,
|
|
@@ -2344,17 +3046,18 @@ ${truncate(text, 18e3)}`
|
|
|
2344
3046
|
};
|
|
2345
3047
|
}
|
|
2346
3048
|
async function analyzeSource(manifest, extractedText, provider, paths, schema) {
|
|
2347
|
-
const cachePath =
|
|
3049
|
+
const cachePath = path7.join(paths.analysesDir, `${manifest.sourceId}.json`);
|
|
2348
3050
|
const cached = await readJsonFile(cachePath);
|
|
2349
|
-
if (cached && cached.sourceHash === manifest.contentHash && cached.schemaHash === schema.hash) {
|
|
3051
|
+
if (cached && cached.analysisVersion === ANALYSIS_FORMAT_VERSION && cached.sourceHash === manifest.contentHash && cached.schemaHash === schema.hash) {
|
|
2350
3052
|
return cached;
|
|
2351
3053
|
}
|
|
2352
3054
|
const content = normalizeWhitespace(extractedText ?? "");
|
|
2353
3055
|
let analysis;
|
|
2354
3056
|
if (manifest.sourceKind === "code" && content) {
|
|
2355
|
-
analysis = analyzeCodeSource(manifest, extractedText ?? "", schema.hash);
|
|
3057
|
+
analysis = await analyzeCodeSource(manifest, extractedText ?? "", schema.hash);
|
|
2356
3058
|
} else if (!content) {
|
|
2357
3059
|
analysis = {
|
|
3060
|
+
analysisVersion: ANALYSIS_FORMAT_VERSION,
|
|
2358
3061
|
sourceId: manifest.sourceId,
|
|
2359
3062
|
sourceHash: manifest.contentHash,
|
|
2360
3063
|
schemaHash: schema.hash,
|
|
@@ -2399,8 +3102,8 @@ function conflictConfidence(claimA, claimB) {
|
|
|
2399
3102
|
}
|
|
2400
3103
|
|
|
2401
3104
|
// src/deep-lint.ts
|
|
2402
|
-
import
|
|
2403
|
-
import
|
|
3105
|
+
import fs7 from "fs/promises";
|
|
3106
|
+
import path10 from "path";
|
|
2404
3107
|
import matter2 from "gray-matter";
|
|
2405
3108
|
import { z as z4 } from "zod";
|
|
2406
3109
|
|
|
@@ -2421,7 +3124,7 @@ function normalizeFindingSeverity(value) {
|
|
|
2421
3124
|
|
|
2422
3125
|
// src/orchestration.ts
|
|
2423
3126
|
import { spawn } from "child_process";
|
|
2424
|
-
import
|
|
3127
|
+
import path8 from "path";
|
|
2425
3128
|
import { z as z2 } from "zod";
|
|
2426
3129
|
var orchestrationRoleResultSchema = z2.object({
|
|
2427
3130
|
summary: z2.string().optional(),
|
|
@@ -2514,7 +3217,7 @@ async function runProviderRole(rootDir, role, roleConfig, input) {
|
|
|
2514
3217
|
}
|
|
2515
3218
|
async function runCommandRole(rootDir, role, executor, input) {
|
|
2516
3219
|
const [command, ...args] = executor.command;
|
|
2517
|
-
const cwd = executor.cwd ?
|
|
3220
|
+
const cwd = executor.cwd ? path8.resolve(rootDir, executor.cwd) : rootDir;
|
|
2518
3221
|
const child = spawn(command, args, {
|
|
2519
3222
|
cwd,
|
|
2520
3223
|
env: {
|
|
@@ -2608,7 +3311,7 @@ function summarizeRoleQuestions(results) {
|
|
|
2608
3311
|
}
|
|
2609
3312
|
|
|
2610
3313
|
// src/web-search/registry.ts
|
|
2611
|
-
import
|
|
3314
|
+
import path9 from "path";
|
|
2612
3315
|
import { pathToFileURL } from "url";
|
|
2613
3316
|
import { z as z3 } from "zod";
|
|
2614
3317
|
|
|
@@ -2706,7 +3409,7 @@ async function createWebSearchAdapter(id, config, rootDir) {
|
|
|
2706
3409
|
if (!config.module) {
|
|
2707
3410
|
throw new Error(`Web search provider ${id} is type "custom" but no module path was configured.`);
|
|
2708
3411
|
}
|
|
2709
|
-
const resolvedModule =
|
|
3412
|
+
const resolvedModule = path9.isAbsolute(config.module) ? config.module : path9.resolve(rootDir, config.module);
|
|
2710
3413
|
const loaded = await import(pathToFileURL(resolvedModule).href);
|
|
2711
3414
|
const parsed = customWebSearchModuleSchema.parse(loaded);
|
|
2712
3415
|
return parsed.createAdapter(id, config, rootDir);
|
|
@@ -2766,8 +3469,8 @@ async function loadContextPages(rootDir, graph) {
|
|
|
2766
3469
|
);
|
|
2767
3470
|
return Promise.all(
|
|
2768
3471
|
contextPages.slice(0, 18).map(async (page) => {
|
|
2769
|
-
const absolutePath =
|
|
2770
|
-
const raw = await
|
|
3472
|
+
const absolutePath = path10.join(paths.wikiDir, page.path);
|
|
3473
|
+
const raw = await fs7.readFile(absolutePath, "utf8").catch(() => "");
|
|
2771
3474
|
const parsed = matter2(raw);
|
|
2772
3475
|
return {
|
|
2773
3476
|
id: page.id,
|
|
@@ -2815,7 +3518,7 @@ function heuristicDeepFindings(contextPages, structuralFindings, graph) {
|
|
|
2815
3518
|
code: "missing_citation",
|
|
2816
3519
|
message: finding.message,
|
|
2817
3520
|
pagePath: finding.pagePath,
|
|
2818
|
-
suggestedQuery: finding.pagePath ? `Which sources support the claims in ${
|
|
3521
|
+
suggestedQuery: finding.pagePath ? `Which sources support the claims in ${path10.basename(finding.pagePath, ".md")}?` : void 0
|
|
2819
3522
|
});
|
|
2820
3523
|
}
|
|
2821
3524
|
for (const page of contextPages.filter((item) => item.kind === "source").slice(0, 3)) {
|
|
@@ -3169,9 +3872,7 @@ function buildModulePage(input) {
|
|
|
3169
3872
|
const relatedOutputs = input.relatedOutputs ?? [];
|
|
3170
3873
|
const backlinks = uniqueStrings([sourcePage.id, ...localModuleBacklinks, ...relatedOutputs.map((page) => page.id)]);
|
|
3171
3874
|
const importsSection = code.imports.length ? code.imports.map((item) => {
|
|
3172
|
-
const localModule = input.localModules.find(
|
|
3173
|
-
(moduleRef) => moduleRef.specifier === item.specifier && moduleRef.reExport === item.reExport
|
|
3174
|
-
);
|
|
3875
|
+
const localModule = item.resolvedSourceId ? input.localModules.find((moduleRef) => moduleRef.sourceId === item.resolvedSourceId && moduleRef.reExport === item.reExport) : void 0;
|
|
3175
3876
|
const importedBits = [
|
|
3176
3877
|
item.defaultImport ? `default \`${item.defaultImport}\`` : "",
|
|
3177
3878
|
item.namespaceImport ? `namespace \`${item.namespaceImport}\`` : "",
|
|
@@ -3182,6 +3883,7 @@ function buildModulePage(input) {
|
|
|
3182
3883
|
const suffix = importedBits.length ? ` (${importedBits.join("; ")})` : "";
|
|
3183
3884
|
return `- ${mode} ${importTarget}${suffix}`;
|
|
3184
3885
|
}) : ["- No imports detected."];
|
|
3886
|
+
const unresolvedLocalImports = code.imports.filter((item) => !item.isExternal && !item.resolvedSourceId).map((item) => `- \`${item.specifier}\`${item.resolvedRepoPath ? ` (expected near \`${item.resolvedRepoPath}\`)` : ""}`);
|
|
3185
3887
|
const exportsSection = code.exports.length ? code.exports.map((item) => `- \`${item}\``) : ["- No exports detected."];
|
|
3186
3888
|
const symbolsSection = code.symbols.length ? code.symbols.map(
|
|
3187
3889
|
(symbol) => `- \`${symbol.name}\` (${symbol.kind}${symbol.exported ? ", exported" : ""}): ${symbol.signature || "No signature recorded."}`
|
|
@@ -3228,7 +3930,10 @@ function buildModulePage(input) {
|
|
|
3228
3930
|
"",
|
|
3229
3931
|
`Source ID: \`${manifest.sourceId}\``,
|
|
3230
3932
|
`Source Path: \`${manifest.originalPath ?? manifest.storedPath}\``,
|
|
3933
|
+
...manifest.repoRelativePath ? [`Repo Path: \`${manifest.repoRelativePath}\``] : [],
|
|
3231
3934
|
`Language: \`${code.language}\``,
|
|
3935
|
+
...code.moduleName ? [`Module Name: \`${code.moduleName}\``] : [],
|
|
3936
|
+
...code.namespace ? [`Namespace/Package: \`${code.namespace}\``] : [],
|
|
3232
3937
|
`Source Page: [[${sourcePage.path.replace(/\.md$/, "")}|${sourcePage.title}]]`,
|
|
3233
3938
|
"",
|
|
3234
3939
|
"## Summary",
|
|
@@ -3251,6 +3956,10 @@ function buildModulePage(input) {
|
|
|
3251
3956
|
"",
|
|
3252
3957
|
...code.dependencies.length ? code.dependencies.map((dependency) => `- \`${dependency}\``) : ["- No external dependencies detected."],
|
|
3253
3958
|
"",
|
|
3959
|
+
"## Unresolved Local References",
|
|
3960
|
+
"",
|
|
3961
|
+
...unresolvedLocalImports.length ? unresolvedLocalImports : ["- No unresolved local references detected."],
|
|
3962
|
+
"",
|
|
3254
3963
|
"## Inheritance",
|
|
3255
3964
|
"",
|
|
3256
3965
|
...inheritanceSection.length ? inheritanceSection : ["- No inheritance relationships detected."],
|
|
@@ -3992,13 +4701,13 @@ function buildOutputAssetManifest(input) {
|
|
|
3992
4701
|
}
|
|
3993
4702
|
|
|
3994
4703
|
// src/outputs.ts
|
|
3995
|
-
import
|
|
3996
|
-
import
|
|
4704
|
+
import fs9 from "fs/promises";
|
|
4705
|
+
import path12 from "path";
|
|
3997
4706
|
import matter5 from "gray-matter";
|
|
3998
4707
|
|
|
3999
4708
|
// src/pages.ts
|
|
4000
|
-
import
|
|
4001
|
-
import
|
|
4709
|
+
import fs8 from "fs/promises";
|
|
4710
|
+
import path11 from "path";
|
|
4002
4711
|
import matter4 from "gray-matter";
|
|
4003
4712
|
function normalizeStringArray(value) {
|
|
4004
4713
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
@@ -4070,7 +4779,7 @@ async function loadExistingManagedPageState(absolutePath, defaults = {}) {
|
|
|
4070
4779
|
updatedAt: updatedFallback
|
|
4071
4780
|
};
|
|
4072
4781
|
}
|
|
4073
|
-
const content = await
|
|
4782
|
+
const content = await fs8.readFile(absolutePath, "utf8");
|
|
4074
4783
|
const parsed = matter4(content);
|
|
4075
4784
|
return {
|
|
4076
4785
|
status: normalizePageStatus(parsed.data.status, defaults.status ?? "active"),
|
|
@@ -4109,7 +4818,7 @@ function parseStoredPage(relativePath, content, defaults = {}) {
|
|
|
4109
4818
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4110
4819
|
const fallbackCreatedAt = defaults.createdAt ?? now;
|
|
4111
4820
|
const fallbackUpdatedAt = defaults.updatedAt ?? fallbackCreatedAt;
|
|
4112
|
-
const title = typeof parsed.data.title === "string" ? parsed.data.title :
|
|
4821
|
+
const title = typeof parsed.data.title === "string" ? parsed.data.title : path11.basename(relativePath, ".md");
|
|
4113
4822
|
const kind = inferPageKind(relativePath, parsed.data.kind);
|
|
4114
4823
|
const sourceIds = normalizeStringArray(parsed.data.source_ids);
|
|
4115
4824
|
const projectIds = normalizeProjectIds(parsed.data.project_ids);
|
|
@@ -4148,18 +4857,18 @@ function parseStoredPage(relativePath, content, defaults = {}) {
|
|
|
4148
4857
|
};
|
|
4149
4858
|
}
|
|
4150
4859
|
async function loadInsightPages(wikiDir) {
|
|
4151
|
-
const insightsDir =
|
|
4860
|
+
const insightsDir = path11.join(wikiDir, "insights");
|
|
4152
4861
|
if (!await fileExists(insightsDir)) {
|
|
4153
4862
|
return [];
|
|
4154
4863
|
}
|
|
4155
|
-
const files = (await listFilesRecursive(insightsDir)).filter((filePath) => filePath.endsWith(".md")).filter((filePath) =>
|
|
4864
|
+
const files = (await listFilesRecursive(insightsDir)).filter((filePath) => filePath.endsWith(".md")).filter((filePath) => path11.basename(filePath) !== "index.md").sort((left, right) => left.localeCompare(right));
|
|
4156
4865
|
const insights = [];
|
|
4157
4866
|
for (const absolutePath of files) {
|
|
4158
|
-
const relativePath = toPosix(
|
|
4159
|
-
const content = await
|
|
4867
|
+
const relativePath = toPosix(path11.relative(wikiDir, absolutePath));
|
|
4868
|
+
const content = await fs8.readFile(absolutePath, "utf8");
|
|
4160
4869
|
const parsed = matter4(content);
|
|
4161
|
-
const stats = await
|
|
4162
|
-
const title = typeof parsed.data.title === "string" ? parsed.data.title :
|
|
4870
|
+
const stats = await fs8.stat(absolutePath);
|
|
4871
|
+
const title = typeof parsed.data.title === "string" ? parsed.data.title : path11.basename(absolutePath, ".md");
|
|
4163
4872
|
const sourceIds = normalizeStringArray(parsed.data.source_ids);
|
|
4164
4873
|
const projectIds = normalizeProjectIds(parsed.data.project_ids);
|
|
4165
4874
|
const nodeIds = normalizeStringArray(parsed.data.node_ids);
|
|
@@ -4221,27 +4930,27 @@ function relatedOutputsForPage(targetPage, outputPages) {
|
|
|
4221
4930
|
return outputPages.map((page) => ({ page, rank: relationRank(page, targetPage) })).filter((item) => item.rank > 0).sort((left, right) => right.rank - left.rank || left.page.title.localeCompare(right.page.title)).map((item) => item.page);
|
|
4222
4931
|
}
|
|
4223
4932
|
async function resolveUniqueOutputSlug(wikiDir, baseSlug) {
|
|
4224
|
-
const outputsDir =
|
|
4933
|
+
const outputsDir = path12.join(wikiDir, "outputs");
|
|
4225
4934
|
const root = baseSlug || "output";
|
|
4226
4935
|
let candidate = root;
|
|
4227
4936
|
let counter = 2;
|
|
4228
|
-
while (await fileExists(
|
|
4937
|
+
while (await fileExists(path12.join(outputsDir, `${candidate}.md`))) {
|
|
4229
4938
|
candidate = `${root}-${counter}`;
|
|
4230
4939
|
counter++;
|
|
4231
4940
|
}
|
|
4232
4941
|
return candidate;
|
|
4233
4942
|
}
|
|
4234
4943
|
async function loadSavedOutputPages(wikiDir) {
|
|
4235
|
-
const outputsDir =
|
|
4236
|
-
const entries = await
|
|
4944
|
+
const outputsDir = path12.join(wikiDir, "outputs");
|
|
4945
|
+
const entries = await fs9.readdir(outputsDir, { withFileTypes: true }).catch(() => []);
|
|
4237
4946
|
const outputs = [];
|
|
4238
4947
|
for (const entry of entries) {
|
|
4239
4948
|
if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name === "index.md") {
|
|
4240
4949
|
continue;
|
|
4241
4950
|
}
|
|
4242
|
-
const relativePath =
|
|
4243
|
-
const absolutePath =
|
|
4244
|
-
const content = await
|
|
4951
|
+
const relativePath = path12.posix.join("outputs", entry.name);
|
|
4952
|
+
const absolutePath = path12.join(outputsDir, entry.name);
|
|
4953
|
+
const content = await fs9.readFile(absolutePath, "utf8");
|
|
4245
4954
|
const parsed = matter5(content);
|
|
4246
4955
|
const slug = entry.name.replace(/\.md$/, "");
|
|
4247
4956
|
const title = typeof parsed.data.title === "string" ? parsed.data.title : slug;
|
|
@@ -4254,7 +4963,7 @@ async function loadSavedOutputPages(wikiDir) {
|
|
|
4254
4963
|
const relatedSourceIds = normalizeStringArray(parsed.data.related_source_ids);
|
|
4255
4964
|
const backlinks = normalizeStringArray(parsed.data.backlinks);
|
|
4256
4965
|
const compiledFrom = normalizeStringArray(parsed.data.compiled_from);
|
|
4257
|
-
const stats = await
|
|
4966
|
+
const stats = await fs9.stat(absolutePath);
|
|
4258
4967
|
const createdAt = typeof parsed.data.created_at === "string" ? parsed.data.created_at : stats.birthtimeMs > 0 ? stats.birthtime.toISOString() : stats.mtime.toISOString();
|
|
4259
4968
|
const updatedAt = typeof parsed.data.updated_at === "string" ? parsed.data.updated_at : stats.mtime.toISOString();
|
|
4260
4969
|
outputs.push({
|
|
@@ -4292,8 +5001,8 @@ async function loadSavedOutputPages(wikiDir) {
|
|
|
4292
5001
|
}
|
|
4293
5002
|
|
|
4294
5003
|
// src/search.ts
|
|
4295
|
-
import
|
|
4296
|
-
import
|
|
5004
|
+
import fs10 from "fs/promises";
|
|
5005
|
+
import path13 from "path";
|
|
4297
5006
|
import matter6 from "gray-matter";
|
|
4298
5007
|
function getDatabaseSync() {
|
|
4299
5008
|
const builtin = process.getBuiltinModule?.("node:sqlite");
|
|
@@ -4313,7 +5022,7 @@ function normalizeStatus(value) {
|
|
|
4313
5022
|
return value === "draft" || value === "candidate" || value === "active" || value === "archived" ? value : void 0;
|
|
4314
5023
|
}
|
|
4315
5024
|
async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
4316
|
-
await ensureDir(
|
|
5025
|
+
await ensureDir(path13.dirname(dbPath));
|
|
4317
5026
|
const DatabaseSync = getDatabaseSync();
|
|
4318
5027
|
const db = new DatabaseSync(dbPath);
|
|
4319
5028
|
db.exec("PRAGMA journal_mode = WAL;");
|
|
@@ -4343,8 +5052,8 @@ async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
|
4343
5052
|
"INSERT INTO pages (id, path, title, body, kind, status, project_ids, project_key) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
|
|
4344
5053
|
);
|
|
4345
5054
|
for (const page of pages) {
|
|
4346
|
-
const absolutePath =
|
|
4347
|
-
const content = await
|
|
5055
|
+
const absolutePath = path13.join(wikiDir, page.path);
|
|
5056
|
+
const content = await fs10.readFile(absolutePath, "utf8");
|
|
4348
5057
|
const parsed = matter6(content);
|
|
4349
5058
|
insertPage.run(
|
|
4350
5059
|
page.id,
|
|
@@ -4447,7 +5156,7 @@ function outputFormatInstruction(format) {
|
|
|
4447
5156
|
}
|
|
4448
5157
|
}
|
|
4449
5158
|
function outputAssetPath(slug, fileName) {
|
|
4450
|
-
return toPosix(
|
|
5159
|
+
return toPosix(path14.join("outputs", "assets", slug, fileName));
|
|
4451
5160
|
}
|
|
4452
5161
|
function outputAssetId(slug, role) {
|
|
4453
5162
|
return `output:${slug}:asset:${role}`;
|
|
@@ -4587,7 +5296,7 @@ async function resolveImageGenerationProvider(rootDir) {
|
|
|
4587
5296
|
if (!providerConfig) {
|
|
4588
5297
|
throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
|
|
4589
5298
|
}
|
|
4590
|
-
const { createProvider: createProvider2 } = await import("./registry-
|
|
5299
|
+
const { createProvider: createProvider2 } = await import("./registry-JFEW5RUP.js");
|
|
4591
5300
|
return createProvider2(preferredProviderId, providerConfig, rootDir);
|
|
4592
5301
|
}
|
|
4593
5302
|
async function generateOutputArtifacts(rootDir, input) {
|
|
@@ -4785,7 +5494,7 @@ async function generateOutputArtifacts(rootDir, input) {
|
|
|
4785
5494
|
};
|
|
4786
5495
|
}
|
|
4787
5496
|
function normalizeProjectRoot(root) {
|
|
4788
|
-
const normalized = toPosix(
|
|
5497
|
+
const normalized = toPosix(path14.posix.normalize(root.replace(/\\/g, "/"))).replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
4789
5498
|
return normalized;
|
|
4790
5499
|
}
|
|
4791
5500
|
function projectEntries(config) {
|
|
@@ -4811,10 +5520,10 @@ function manifestPathForProject(rootDir, manifest) {
|
|
|
4811
5520
|
if (!rawPath) {
|
|
4812
5521
|
return toPosix(manifest.storedPath);
|
|
4813
5522
|
}
|
|
4814
|
-
if (!
|
|
5523
|
+
if (!path14.isAbsolute(rawPath)) {
|
|
4815
5524
|
return normalizeProjectRoot(rawPath);
|
|
4816
5525
|
}
|
|
4817
|
-
const relative = toPosix(
|
|
5526
|
+
const relative = toPosix(path14.relative(rootDir, rawPath));
|
|
4818
5527
|
return relative.startsWith("..") ? toPosix(rawPath) : normalizeProjectRoot(relative);
|
|
4819
5528
|
}
|
|
4820
5529
|
function prefixMatches(value, prefix) {
|
|
@@ -4987,7 +5696,7 @@ function pageHashes(pages) {
|
|
|
4987
5696
|
return Object.fromEntries(pages.map((page) => [page.page.id, page.contentHash]));
|
|
4988
5697
|
}
|
|
4989
5698
|
async function buildManagedGraphPage(absolutePath, defaults, build) {
|
|
4990
|
-
const existingContent = await fileExists(absolutePath) ? await
|
|
5699
|
+
const existingContent = await fileExists(absolutePath) ? await fs11.readFile(absolutePath, "utf8") : null;
|
|
4991
5700
|
let existing = await loadExistingManagedPageState(absolutePath, {
|
|
4992
5701
|
status: defaults.status ?? "active",
|
|
4993
5702
|
managedBy: defaults.managedBy
|
|
@@ -5025,7 +5734,7 @@ async function buildManagedGraphPage(absolutePath, defaults, build) {
|
|
|
5025
5734
|
return built;
|
|
5026
5735
|
}
|
|
5027
5736
|
async function buildManagedContent(absolutePath, defaults, build) {
|
|
5028
|
-
const existingContent = await fileExists(absolutePath) ? await
|
|
5737
|
+
const existingContent = await fileExists(absolutePath) ? await fs11.readFile(absolutePath, "utf8") : null;
|
|
5029
5738
|
let existing = await loadExistingManagedPageState(absolutePath, {
|
|
5030
5739
|
status: defaults.status ?? "active",
|
|
5031
5740
|
managedBy: defaults.managedBy
|
|
@@ -5144,7 +5853,7 @@ function deriveGraphMetrics(nodes, edges) {
|
|
|
5144
5853
|
communities
|
|
5145
5854
|
};
|
|
5146
5855
|
}
|
|
5147
|
-
function buildGraph(manifests, analyses, pages, sourceProjects) {
|
|
5856
|
+
function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
5148
5857
|
const sourceNodes = manifests.map((manifest) => ({
|
|
5149
5858
|
id: `source:${manifest.sourceId}`,
|
|
5150
5859
|
type: "source",
|
|
@@ -5283,11 +5992,16 @@ function buildGraph(manifests, analyses, pages, sourceProjects) {
|
|
|
5283
5992
|
const symbolIdsByName = new Map(analysis.code.symbols.map((symbol) => [symbol.name, symbol.id]));
|
|
5284
5993
|
const importedSymbolIdsByName = /* @__PURE__ */ new Map();
|
|
5285
5994
|
for (const codeImport of analysis.code.imports.filter((item) => !item.isExternal)) {
|
|
5286
|
-
const targetSourceId =
|
|
5995
|
+
const targetSourceId = codeImport.resolvedSourceId;
|
|
5287
5996
|
const targetAnalysis = targetSourceId ? analysesBySourceId.get(targetSourceId) : void 0;
|
|
5288
5997
|
if (!targetSourceId || !targetAnalysis?.code) {
|
|
5289
5998
|
continue;
|
|
5290
5999
|
}
|
|
6000
|
+
if (codeImport.importedSymbols.length === 0) {
|
|
6001
|
+
for (const targetSymbol of targetAnalysis.code.symbols.filter((symbol) => symbol.exported)) {
|
|
6002
|
+
importedSymbolIdsByName.set(targetSymbol.name, targetSymbol.id);
|
|
6003
|
+
}
|
|
6004
|
+
}
|
|
5291
6005
|
for (const importedSymbol of codeImport.importedSymbols) {
|
|
5292
6006
|
const [rawExportedName, rawLocalName] = importedSymbol.split(/\s+as\s+/i);
|
|
5293
6007
|
const exportedName = (rawExportedName ?? "").trim();
|
|
@@ -5349,7 +6063,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects) {
|
|
|
5349
6063
|
}
|
|
5350
6064
|
}
|
|
5351
6065
|
for (const codeImport of analysis.code.imports) {
|
|
5352
|
-
const targetSourceId =
|
|
6066
|
+
const targetSourceId = codeImport.resolvedSourceId;
|
|
5353
6067
|
if (!targetSourceId) {
|
|
5354
6068
|
continue;
|
|
5355
6069
|
}
|
|
@@ -5417,7 +6131,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects) {
|
|
|
5417
6131
|
};
|
|
5418
6132
|
}
|
|
5419
6133
|
async function writePage(wikiDir, relativePath, content, changedPages) {
|
|
5420
|
-
const absolutePath =
|
|
6134
|
+
const absolutePath = path14.resolve(wikiDir, relativePath);
|
|
5421
6135
|
const changed = await writeFileIfChanged(absolutePath, content);
|
|
5422
6136
|
if (changed) {
|
|
5423
6137
|
changedPages.push(relativePath);
|
|
@@ -5477,15 +6191,16 @@ function recordsEqual(left, right) {
|
|
|
5477
6191
|
async function requiredCompileArtifactsExist(paths) {
|
|
5478
6192
|
const requiredPaths = [
|
|
5479
6193
|
paths.graphPath,
|
|
6194
|
+
paths.codeIndexPath,
|
|
5480
6195
|
paths.searchDbPath,
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
6196
|
+
path14.join(paths.wikiDir, "index.md"),
|
|
6197
|
+
path14.join(paths.wikiDir, "sources", "index.md"),
|
|
6198
|
+
path14.join(paths.wikiDir, "code", "index.md"),
|
|
6199
|
+
path14.join(paths.wikiDir, "concepts", "index.md"),
|
|
6200
|
+
path14.join(paths.wikiDir, "entities", "index.md"),
|
|
6201
|
+
path14.join(paths.wikiDir, "outputs", "index.md"),
|
|
6202
|
+
path14.join(paths.wikiDir, "projects", "index.md"),
|
|
6203
|
+
path14.join(paths.wikiDir, "candidates", "index.md")
|
|
5489
6204
|
];
|
|
5490
6205
|
const checks = await Promise.all(requiredPaths.map((filePath) => fileExists(filePath)));
|
|
5491
6206
|
return checks.every(Boolean);
|
|
@@ -5493,7 +6208,7 @@ async function requiredCompileArtifactsExist(paths) {
|
|
|
5493
6208
|
async function loadCachedAnalyses(paths, manifests) {
|
|
5494
6209
|
return Promise.all(
|
|
5495
6210
|
manifests.map(async (manifest) => {
|
|
5496
|
-
const cached = await readJsonFile(
|
|
6211
|
+
const cached = await readJsonFile(path14.join(paths.analysesDir, `${manifest.sourceId}.json`));
|
|
5497
6212
|
if (!cached) {
|
|
5498
6213
|
throw new Error(`Missing cached analysis for ${manifest.sourceId}. Run \`swarmvault compile\` first.`);
|
|
5499
6214
|
}
|
|
@@ -5502,10 +6217,10 @@ async function loadCachedAnalyses(paths, manifests) {
|
|
|
5502
6217
|
);
|
|
5503
6218
|
}
|
|
5504
6219
|
function approvalManifestPath(paths, approvalId) {
|
|
5505
|
-
return
|
|
6220
|
+
return path14.join(paths.approvalsDir, approvalId, "manifest.json");
|
|
5506
6221
|
}
|
|
5507
6222
|
function approvalGraphPath(paths, approvalId) {
|
|
5508
|
-
return
|
|
6223
|
+
return path14.join(paths.approvalsDir, approvalId, "state", "graph.json");
|
|
5509
6224
|
}
|
|
5510
6225
|
async function readApprovalManifest(paths, approvalId) {
|
|
5511
6226
|
const manifest = await readJsonFile(approvalManifestPath(paths, approvalId));
|
|
@@ -5515,7 +6230,7 @@ async function readApprovalManifest(paths, approvalId) {
|
|
|
5515
6230
|
return manifest;
|
|
5516
6231
|
}
|
|
5517
6232
|
async function writeApprovalManifest(paths, manifest) {
|
|
5518
|
-
await
|
|
6233
|
+
await fs11.writeFile(approvalManifestPath(paths, manifest.approvalId), `${JSON.stringify(manifest, null, 2)}
|
|
5519
6234
|
`, "utf8");
|
|
5520
6235
|
}
|
|
5521
6236
|
async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousGraph, graph) {
|
|
@@ -5530,7 +6245,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
5530
6245
|
continue;
|
|
5531
6246
|
}
|
|
5532
6247
|
const previousPage = previousPagesById.get(nextPage.id);
|
|
5533
|
-
const currentExists = await fileExists(
|
|
6248
|
+
const currentExists = await fileExists(path14.join(paths.wikiDir, file.relativePath));
|
|
5534
6249
|
if (previousPage && previousPage.path !== nextPage.path) {
|
|
5535
6250
|
entries.push({
|
|
5536
6251
|
pageId: nextPage.id,
|
|
@@ -5563,7 +6278,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
5563
6278
|
const previousPage = previousPagesByPath.get(deletedPath);
|
|
5564
6279
|
entries.push({
|
|
5565
6280
|
pageId: previousPage?.id ?? `page:${slugify(deletedPath)}`,
|
|
5566
|
-
title: previousPage?.title ??
|
|
6281
|
+
title: previousPage?.title ?? path14.basename(deletedPath, ".md"),
|
|
5567
6282
|
kind: previousPage?.kind ?? "index",
|
|
5568
6283
|
changeType: "delete",
|
|
5569
6284
|
status: "pending",
|
|
@@ -5575,16 +6290,16 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
5575
6290
|
}
|
|
5576
6291
|
async function stageApprovalBundle(paths, changedFiles, deletedPaths, previousGraph, graph) {
|
|
5577
6292
|
const approvalId = `compile-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
5578
|
-
const approvalDir =
|
|
6293
|
+
const approvalDir = path14.join(paths.approvalsDir, approvalId);
|
|
5579
6294
|
await ensureDir(approvalDir);
|
|
5580
|
-
await ensureDir(
|
|
5581
|
-
await ensureDir(
|
|
6295
|
+
await ensureDir(path14.join(approvalDir, "wiki"));
|
|
6296
|
+
await ensureDir(path14.join(approvalDir, "state"));
|
|
5582
6297
|
for (const file of changedFiles) {
|
|
5583
|
-
const targetPath =
|
|
5584
|
-
await ensureDir(
|
|
5585
|
-
await
|
|
6298
|
+
const targetPath = path14.join(approvalDir, "wiki", file.relativePath);
|
|
6299
|
+
await ensureDir(path14.dirname(targetPath));
|
|
6300
|
+
await fs11.writeFile(targetPath, file.content, "utf8");
|
|
5586
6301
|
}
|
|
5587
|
-
await
|
|
6302
|
+
await fs11.writeFile(path14.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
|
|
5588
6303
|
await writeApprovalManifest(paths, {
|
|
5589
6304
|
approvalId,
|
|
5590
6305
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5644,7 +6359,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5644
6359
|
confidence: 1
|
|
5645
6360
|
});
|
|
5646
6361
|
const sourceRecord = await buildManagedGraphPage(
|
|
5647
|
-
|
|
6362
|
+
path14.join(paths.wikiDir, preview.path),
|
|
5648
6363
|
{
|
|
5649
6364
|
managedBy: "system",
|
|
5650
6365
|
confidence: 1,
|
|
@@ -5666,7 +6381,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5666
6381
|
records.push(sourceRecord);
|
|
5667
6382
|
if (modulePreview && analysis.code) {
|
|
5668
6383
|
const localModules = analysis.code.imports.map((codeImport) => {
|
|
5669
|
-
const resolvedSourceId =
|
|
6384
|
+
const resolvedSourceId = codeImport.resolvedSourceId;
|
|
5670
6385
|
if (!resolvedSourceId) {
|
|
5671
6386
|
return null;
|
|
5672
6387
|
}
|
|
@@ -5689,7 +6404,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5689
6404
|
);
|
|
5690
6405
|
records.push(
|
|
5691
6406
|
await buildManagedGraphPage(
|
|
5692
|
-
|
|
6407
|
+
path14.join(paths.wikiDir, modulePreview.path),
|
|
5693
6408
|
{
|
|
5694
6409
|
managedBy: "system",
|
|
5695
6410
|
confidence: 1,
|
|
@@ -5722,8 +6437,8 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5722
6437
|
const promoted = previousEntry?.status === "active" || promoteCandidates && shouldPromoteCandidate(previousEntry, sourceIds);
|
|
5723
6438
|
const relativePath = promoted ? activeAggregatePath(itemKind, slug) : candidatePagePathFor(itemKind, slug);
|
|
5724
6439
|
const fallbackPaths = [
|
|
5725
|
-
|
|
5726
|
-
|
|
6440
|
+
path14.join(paths.wikiDir, activeAggregatePath(itemKind, slug)),
|
|
6441
|
+
path14.join(paths.wikiDir, candidatePagePathFor(itemKind, slug))
|
|
5727
6442
|
];
|
|
5728
6443
|
const confidence = nodeConfidence(aggregate.sourceAnalyses.length);
|
|
5729
6444
|
const preview = emptyGraphPage({
|
|
@@ -5740,7 +6455,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5740
6455
|
status: promoted ? "active" : "candidate"
|
|
5741
6456
|
});
|
|
5742
6457
|
const pageRecord = await buildManagedGraphPage(
|
|
5743
|
-
|
|
6458
|
+
path14.join(paths.wikiDir, relativePath),
|
|
5744
6459
|
{
|
|
5745
6460
|
status: promoted ? "active" : "candidate",
|
|
5746
6461
|
managedBy: "system",
|
|
@@ -5780,7 +6495,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5780
6495
|
}
|
|
5781
6496
|
const compiledPages = records.map((record) => record.page);
|
|
5782
6497
|
const allPages = [...compiledPages, ...input.outputPages, ...input.insightPages];
|
|
5783
|
-
const graph = buildGraph(input.manifests, input.analyses, allPages, input.sourceProjects);
|
|
6498
|
+
const graph = buildGraph(input.manifests, input.analyses, allPages, input.sourceProjects, input.codeIndex);
|
|
5784
6499
|
const activeConceptPages = allPages.filter((page) => page.kind === "concept" && page.status !== "candidate");
|
|
5785
6500
|
const activeEntityPages = allPages.filter((page) => page.kind === "entity" && page.status !== "candidate");
|
|
5786
6501
|
const modulePages = allPages.filter((page) => page.kind === "module");
|
|
@@ -5814,7 +6529,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5814
6529
|
confidence: 1
|
|
5815
6530
|
}),
|
|
5816
6531
|
content: await buildManagedContent(
|
|
5817
|
-
|
|
6532
|
+
path14.join(paths.wikiDir, "projects", "index.md"),
|
|
5818
6533
|
{
|
|
5819
6534
|
managedBy: "system",
|
|
5820
6535
|
compiledFrom: indexCompiledFrom(projectIndexRefs)
|
|
@@ -5838,7 +6553,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5838
6553
|
records.push({
|
|
5839
6554
|
page: projectIndexRef,
|
|
5840
6555
|
content: await buildManagedContent(
|
|
5841
|
-
|
|
6556
|
+
path14.join(paths.wikiDir, projectIndexRef.path),
|
|
5842
6557
|
{
|
|
5843
6558
|
managedBy: "system",
|
|
5844
6559
|
compiledFrom: indexCompiledFrom(Object.values(sections).flat())
|
|
@@ -5866,7 +6581,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5866
6581
|
confidence: 1
|
|
5867
6582
|
}),
|
|
5868
6583
|
content: await buildManagedContent(
|
|
5869
|
-
|
|
6584
|
+
path14.join(paths.wikiDir, "index.md"),
|
|
5870
6585
|
{
|
|
5871
6586
|
managedBy: "system",
|
|
5872
6587
|
compiledFrom: indexCompiledFrom(allPages)
|
|
@@ -5896,7 +6611,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5896
6611
|
confidence: 1
|
|
5897
6612
|
}),
|
|
5898
6613
|
content: await buildManagedContent(
|
|
5899
|
-
|
|
6614
|
+
path14.join(paths.wikiDir, relativePath),
|
|
5900
6615
|
{
|
|
5901
6616
|
managedBy: "system",
|
|
5902
6617
|
compiledFrom: indexCompiledFrom(pages)
|
|
@@ -5907,12 +6622,12 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5907
6622
|
}
|
|
5908
6623
|
const nextPagePaths = new Set(records.map((record) => record.page.path));
|
|
5909
6624
|
const obsoleteGraphPaths = (previousGraph?.pages ?? []).filter((page) => page.kind !== "output" && page.kind !== "insight").map((page) => page.path).filter((relativePath) => !nextPagePaths.has(relativePath));
|
|
5910
|
-
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(
|
|
6625
|
+
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path14.relative(paths.wikiDir, absolutePath))).filter((relativePath) => !nextPagePaths.has(relativePath));
|
|
5911
6626
|
const obsoletePaths = uniqueStrings2([...obsoleteGraphPaths, ...existingProjectIndexPaths]);
|
|
5912
6627
|
const changedFiles = [];
|
|
5913
6628
|
for (const record of records) {
|
|
5914
|
-
const absolutePath =
|
|
5915
|
-
const current = await fileExists(absolutePath) ? await
|
|
6629
|
+
const absolutePath = path14.join(paths.wikiDir, record.page.path);
|
|
6630
|
+
const current = await fileExists(absolutePath) ? await fs11.readFile(absolutePath, "utf8") : null;
|
|
5916
6631
|
if (current !== record.content) {
|
|
5917
6632
|
changedPages.push(record.page.path);
|
|
5918
6633
|
changedFiles.push({ relativePath: record.page.path, content: record.content });
|
|
@@ -5937,9 +6652,10 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5937
6652
|
await writePage(paths.wikiDir, record.page.path, record.content, writeChanges);
|
|
5938
6653
|
}
|
|
5939
6654
|
for (const relativePath of obsoletePaths) {
|
|
5940
|
-
await
|
|
6655
|
+
await fs11.rm(path14.join(paths.wikiDir, relativePath), { force: true });
|
|
5941
6656
|
}
|
|
5942
6657
|
await writeJsonFile(paths.graphPath, graph);
|
|
6658
|
+
await writeJsonFile(paths.codeIndexPath, input.codeIndex);
|
|
5943
6659
|
await writeJsonFile(paths.compileStatePath, {
|
|
5944
6660
|
generatedAt: graph.generatedAt,
|
|
5945
6661
|
rootSchemaHash: input.schemas.root.hash,
|
|
@@ -5990,15 +6706,15 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
5990
6706
|
})
|
|
5991
6707
|
);
|
|
5992
6708
|
await Promise.all([
|
|
5993
|
-
ensureDir(
|
|
5994
|
-
ensureDir(
|
|
5995
|
-
ensureDir(
|
|
5996
|
-
ensureDir(
|
|
5997
|
-
ensureDir(
|
|
5998
|
-
ensureDir(
|
|
5999
|
-
ensureDir(
|
|
6709
|
+
ensureDir(path14.join(paths.wikiDir, "sources")),
|
|
6710
|
+
ensureDir(path14.join(paths.wikiDir, "code")),
|
|
6711
|
+
ensureDir(path14.join(paths.wikiDir, "concepts")),
|
|
6712
|
+
ensureDir(path14.join(paths.wikiDir, "entities")),
|
|
6713
|
+
ensureDir(path14.join(paths.wikiDir, "outputs")),
|
|
6714
|
+
ensureDir(path14.join(paths.wikiDir, "projects")),
|
|
6715
|
+
ensureDir(path14.join(paths.wikiDir, "candidates"))
|
|
6000
6716
|
]);
|
|
6001
|
-
const projectsIndexPath =
|
|
6717
|
+
const projectsIndexPath = path14.join(paths.wikiDir, "projects", "index.md");
|
|
6002
6718
|
await writeFileIfChanged(
|
|
6003
6719
|
projectsIndexPath,
|
|
6004
6720
|
await buildManagedContent(
|
|
@@ -6019,7 +6735,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
6019
6735
|
outputs: pages.filter((page) => page.kind === "output" && page.projectIds.includes(project.id)),
|
|
6020
6736
|
candidates: pages.filter((page) => page.status === "candidate" && page.projectIds.includes(project.id))
|
|
6021
6737
|
};
|
|
6022
|
-
const absolutePath =
|
|
6738
|
+
const absolutePath = path14.join(paths.wikiDir, "projects", project.id, "index.md");
|
|
6023
6739
|
await writeFileIfChanged(
|
|
6024
6740
|
absolutePath,
|
|
6025
6741
|
await buildManagedContent(
|
|
@@ -6037,7 +6753,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
6037
6753
|
)
|
|
6038
6754
|
);
|
|
6039
6755
|
}
|
|
6040
|
-
const rootIndexPath =
|
|
6756
|
+
const rootIndexPath = path14.join(paths.wikiDir, "index.md");
|
|
6041
6757
|
await writeFileIfChanged(
|
|
6042
6758
|
rootIndexPath,
|
|
6043
6759
|
await buildManagedContent(
|
|
@@ -6057,7 +6773,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
6057
6773
|
["outputs/index.md", "outputs", pages.filter((page) => page.kind === "output")],
|
|
6058
6774
|
["candidates/index.md", "candidates", pages.filter((page) => page.status === "candidate")]
|
|
6059
6775
|
]) {
|
|
6060
|
-
const absolutePath =
|
|
6776
|
+
const absolutePath = path14.join(paths.wikiDir, relativePath);
|
|
6061
6777
|
await writeFileIfChanged(
|
|
6062
6778
|
absolutePath,
|
|
6063
6779
|
await buildManagedContent(
|
|
@@ -6070,13 +6786,13 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
6070
6786
|
)
|
|
6071
6787
|
);
|
|
6072
6788
|
}
|
|
6073
|
-
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(
|
|
6789
|
+
const existingProjectIndexPaths = (await listFilesRecursive(paths.projectsDir)).filter((absolutePath) => absolutePath.endsWith(".md")).map((absolutePath) => toPosix(path14.relative(paths.wikiDir, absolutePath)));
|
|
6074
6790
|
const allowedProjectIndexPaths = /* @__PURE__ */ new Set([
|
|
6075
6791
|
"projects/index.md",
|
|
6076
6792
|
...configuredProjects.map((project) => `projects/${project.id}/index.md`)
|
|
6077
6793
|
]);
|
|
6078
6794
|
await Promise.all(
|
|
6079
|
-
existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) =>
|
|
6795
|
+
existingProjectIndexPaths.filter((relativePath) => !allowedProjectIndexPaths.has(relativePath)).map((relativePath) => fs11.rm(path14.join(paths.wikiDir, relativePath), { force: true }))
|
|
6080
6796
|
);
|
|
6081
6797
|
await rebuildSearchIndex(paths.searchDbPath, pages, paths.wikiDir);
|
|
6082
6798
|
}
|
|
@@ -6096,7 +6812,7 @@ async function prepareOutputPageSave(rootDir, input) {
|
|
|
6096
6812
|
confidence: 0.74
|
|
6097
6813
|
}
|
|
6098
6814
|
});
|
|
6099
|
-
const absolutePath =
|
|
6815
|
+
const absolutePath = path14.join(paths.wikiDir, output.page.path);
|
|
6100
6816
|
return {
|
|
6101
6817
|
page: output.page,
|
|
6102
6818
|
savedPath: absolutePath,
|
|
@@ -6108,15 +6824,15 @@ async function prepareOutputPageSave(rootDir, input) {
|
|
|
6108
6824
|
async function persistOutputPage(rootDir, input) {
|
|
6109
6825
|
const { paths } = await loadVaultConfig(rootDir);
|
|
6110
6826
|
const prepared = await prepareOutputPageSave(rootDir, input);
|
|
6111
|
-
await ensureDir(
|
|
6112
|
-
await
|
|
6827
|
+
await ensureDir(path14.dirname(prepared.savedPath));
|
|
6828
|
+
await fs11.writeFile(prepared.savedPath, prepared.content, "utf8");
|
|
6113
6829
|
for (const assetFile of prepared.assetFiles) {
|
|
6114
|
-
const assetPath =
|
|
6115
|
-
await ensureDir(
|
|
6830
|
+
const assetPath = path14.join(paths.wikiDir, assetFile.relativePath);
|
|
6831
|
+
await ensureDir(path14.dirname(assetPath));
|
|
6116
6832
|
if (typeof assetFile.content === "string") {
|
|
6117
|
-
await
|
|
6833
|
+
await fs11.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
|
|
6118
6834
|
} else {
|
|
6119
|
-
await
|
|
6835
|
+
await fs11.writeFile(assetPath, assetFile.content);
|
|
6120
6836
|
}
|
|
6121
6837
|
}
|
|
6122
6838
|
return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
|
|
@@ -6137,7 +6853,7 @@ async function prepareExploreHubSave(rootDir, input) {
|
|
|
6137
6853
|
confidence: 0.76
|
|
6138
6854
|
}
|
|
6139
6855
|
});
|
|
6140
|
-
const absolutePath =
|
|
6856
|
+
const absolutePath = path14.join(paths.wikiDir, hub.page.path);
|
|
6141
6857
|
return {
|
|
6142
6858
|
page: hub.page,
|
|
6143
6859
|
savedPath: absolutePath,
|
|
@@ -6149,15 +6865,15 @@ async function prepareExploreHubSave(rootDir, input) {
|
|
|
6149
6865
|
async function persistExploreHub(rootDir, input) {
|
|
6150
6866
|
const { paths } = await loadVaultConfig(rootDir);
|
|
6151
6867
|
const prepared = await prepareExploreHubSave(rootDir, input);
|
|
6152
|
-
await ensureDir(
|
|
6153
|
-
await
|
|
6868
|
+
await ensureDir(path14.dirname(prepared.savedPath));
|
|
6869
|
+
await fs11.writeFile(prepared.savedPath, prepared.content, "utf8");
|
|
6154
6870
|
for (const assetFile of prepared.assetFiles) {
|
|
6155
|
-
const assetPath =
|
|
6156
|
-
await ensureDir(
|
|
6871
|
+
const assetPath = path14.join(paths.wikiDir, assetFile.relativePath);
|
|
6872
|
+
await ensureDir(path14.dirname(assetPath));
|
|
6157
6873
|
if (typeof assetFile.content === "string") {
|
|
6158
|
-
await
|
|
6874
|
+
await fs11.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
|
|
6159
6875
|
} else {
|
|
6160
|
-
await
|
|
6876
|
+
await fs11.writeFile(assetPath, assetFile.content);
|
|
6161
6877
|
}
|
|
6162
6878
|
}
|
|
6163
6879
|
return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
|
|
@@ -6174,17 +6890,17 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
|
|
|
6174
6890
|
}))
|
|
6175
6891
|
]);
|
|
6176
6892
|
const approvalId = `schedule-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
6177
|
-
const approvalDir =
|
|
6893
|
+
const approvalDir = path14.join(paths.approvalsDir, approvalId);
|
|
6178
6894
|
await ensureDir(approvalDir);
|
|
6179
|
-
await ensureDir(
|
|
6180
|
-
await ensureDir(
|
|
6895
|
+
await ensureDir(path14.join(approvalDir, "wiki"));
|
|
6896
|
+
await ensureDir(path14.join(approvalDir, "state"));
|
|
6181
6897
|
for (const file of changedFiles) {
|
|
6182
|
-
const targetPath =
|
|
6183
|
-
await ensureDir(
|
|
6898
|
+
const targetPath = path14.join(approvalDir, "wiki", file.relativePath);
|
|
6899
|
+
await ensureDir(path14.dirname(targetPath));
|
|
6184
6900
|
if ("binary" in file && file.binary) {
|
|
6185
|
-
await
|
|
6901
|
+
await fs11.writeFile(targetPath, Buffer.from(file.content, "base64"));
|
|
6186
6902
|
} else {
|
|
6187
|
-
await
|
|
6903
|
+
await fs11.writeFile(targetPath, file.content, "utf8");
|
|
6188
6904
|
}
|
|
6189
6905
|
}
|
|
6190
6906
|
const nextPages = sortGraphPages([
|
|
@@ -6198,7 +6914,7 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
|
|
|
6198
6914
|
sources: previousGraph?.sources ?? [],
|
|
6199
6915
|
pages: nextPages
|
|
6200
6916
|
};
|
|
6201
|
-
await
|
|
6917
|
+
await fs11.writeFile(path14.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
|
|
6202
6918
|
await writeApprovalManifest(paths, {
|
|
6203
6919
|
approvalId,
|
|
6204
6920
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -6227,9 +6943,9 @@ async function executeQuery(rootDir, question, format) {
|
|
|
6227
6943
|
const searchResults = searchPages(paths.searchDbPath, question, 5);
|
|
6228
6944
|
const excerpts = await Promise.all(
|
|
6229
6945
|
searchResults.map(async (result) => {
|
|
6230
|
-
const absolutePath =
|
|
6946
|
+
const absolutePath = path14.join(paths.wikiDir, result.path);
|
|
6231
6947
|
try {
|
|
6232
|
-
const content = await
|
|
6948
|
+
const content = await fs11.readFile(absolutePath, "utf8");
|
|
6233
6949
|
const parsed = matter7(content);
|
|
6234
6950
|
return `# ${result.title}
|
|
6235
6951
|
${truncate(normalizeWhitespace(parsed.content), 1200)}`;
|
|
@@ -6338,13 +7054,19 @@ async function refreshVaultAfterOutputSave(rootDir) {
|
|
|
6338
7054
|
const schemas = await loadVaultSchemas(rootDir);
|
|
6339
7055
|
const manifests = await listManifests(rootDir);
|
|
6340
7056
|
const sourceProjects = resolveSourceProjects(rootDir, manifests, config);
|
|
6341
|
-
const
|
|
7057
|
+
const cachedAnalyses = manifests.length ? await loadCachedAnalyses(paths, manifests) : [];
|
|
7058
|
+
const codeIndex = await buildCodeIndex(rootDir, manifests, cachedAnalyses);
|
|
7059
|
+
const analyses = cachedAnalyses.map((analysis) => {
|
|
7060
|
+
const manifest = manifests.find((item) => item.sourceId === analysis.sourceId);
|
|
7061
|
+
return manifest ? enrichResolvedCodeImports(manifest, analysis, codeIndex) : analysis;
|
|
7062
|
+
});
|
|
6342
7063
|
const storedOutputs = await loadSavedOutputPages(paths.wikiDir);
|
|
6343
7064
|
const storedInsights = await loadInsightPages(paths.wikiDir);
|
|
6344
7065
|
await syncVaultArtifacts(rootDir, {
|
|
6345
7066
|
schemas,
|
|
6346
7067
|
manifests,
|
|
6347
7068
|
analyses,
|
|
7069
|
+
codeIndex,
|
|
6348
7070
|
sourceProjects,
|
|
6349
7071
|
outputPages: storedOutputs.map((page) => page.page),
|
|
6350
7072
|
insightPages: storedInsights.map((page) => page.page),
|
|
@@ -6405,7 +7127,7 @@ function sortGraphPages(pages) {
|
|
|
6405
7127
|
async function listApprovals(rootDir) {
|
|
6406
7128
|
const { paths } = await loadVaultConfig(rootDir);
|
|
6407
7129
|
const manifests = await Promise.all(
|
|
6408
|
-
(await
|
|
7130
|
+
(await fs11.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => {
|
|
6409
7131
|
try {
|
|
6410
7132
|
return await readApprovalManifest(paths, entry.name);
|
|
6411
7133
|
} catch {
|
|
@@ -6421,8 +7143,8 @@ async function readApproval(rootDir, approvalId) {
|
|
|
6421
7143
|
const details = await Promise.all(
|
|
6422
7144
|
manifest.entries.map(async (entry) => {
|
|
6423
7145
|
const currentPath = entry.previousPath ?? entry.nextPath;
|
|
6424
|
-
const currentContent = currentPath ? await
|
|
6425
|
-
const stagedContent = entry.nextPath ? await
|
|
7146
|
+
const currentContent = currentPath ? await fs11.readFile(path14.join(paths.wikiDir, currentPath), "utf8").catch(() => void 0) : void 0;
|
|
7147
|
+
const stagedContent = entry.nextPath ? await fs11.readFile(path14.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath), "utf8").catch(() => void 0) : void 0;
|
|
6426
7148
|
return {
|
|
6427
7149
|
...entry,
|
|
6428
7150
|
currentContent,
|
|
@@ -6450,26 +7172,26 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
|
|
|
6450
7172
|
if (!entry.nextPath) {
|
|
6451
7173
|
throw new Error(`Approval entry ${entry.pageId} is missing a staged path.`);
|
|
6452
7174
|
}
|
|
6453
|
-
const stagedAbsolutePath =
|
|
6454
|
-
const stagedContent = await
|
|
6455
|
-
const targetAbsolutePath =
|
|
6456
|
-
await ensureDir(
|
|
6457
|
-
await
|
|
7175
|
+
const stagedAbsolutePath = path14.join(paths.approvalsDir, approvalId, "wiki", entry.nextPath);
|
|
7176
|
+
const stagedContent = await fs11.readFile(stagedAbsolutePath, "utf8");
|
|
7177
|
+
const targetAbsolutePath = path14.join(paths.wikiDir, entry.nextPath);
|
|
7178
|
+
await ensureDir(path14.dirname(targetAbsolutePath));
|
|
7179
|
+
await fs11.writeFile(targetAbsolutePath, stagedContent, "utf8");
|
|
6458
7180
|
if (entry.changeType === "promote" && entry.previousPath) {
|
|
6459
|
-
await
|
|
7181
|
+
await fs11.rm(path14.join(paths.wikiDir, entry.previousPath), { force: true });
|
|
6460
7182
|
}
|
|
6461
7183
|
const nextPage = bundleGraph?.pages.find((page) => page.id === entry.pageId && page.path === entry.nextPath) ?? parseStoredPage(entry.nextPath, stagedContent);
|
|
6462
7184
|
if (nextPage.kind === "output" && nextPage.outputAssets?.length) {
|
|
6463
|
-
const outputAssetDir =
|
|
6464
|
-
await
|
|
7185
|
+
const outputAssetDir = path14.join(paths.wikiDir, "outputs", "assets", path14.basename(nextPage.path, ".md"));
|
|
7186
|
+
await fs11.rm(outputAssetDir, { recursive: true, force: true });
|
|
6465
7187
|
for (const asset of nextPage.outputAssets) {
|
|
6466
|
-
const stagedAssetPath =
|
|
7188
|
+
const stagedAssetPath = path14.join(paths.approvalsDir, approvalId, "wiki", asset.path);
|
|
6467
7189
|
if (!await fileExists(stagedAssetPath)) {
|
|
6468
7190
|
continue;
|
|
6469
7191
|
}
|
|
6470
|
-
const targetAssetPath =
|
|
6471
|
-
await ensureDir(
|
|
6472
|
-
await
|
|
7192
|
+
const targetAssetPath = path14.join(paths.wikiDir, asset.path);
|
|
7193
|
+
await ensureDir(path14.dirname(targetAssetPath));
|
|
7194
|
+
await fs11.copyFile(stagedAssetPath, targetAssetPath);
|
|
6473
7195
|
}
|
|
6474
7196
|
}
|
|
6475
7197
|
nextPages = nextPages.filter(
|
|
@@ -6480,10 +7202,10 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
|
|
|
6480
7202
|
} else {
|
|
6481
7203
|
const deletedPage = nextPages.find((page) => page.id === entry.pageId || page.path === entry.previousPath) ?? bundleGraph?.pages.find((page) => page.id === entry.pageId || page.path === entry.previousPath) ?? null;
|
|
6482
7204
|
if (entry.previousPath) {
|
|
6483
|
-
await
|
|
7205
|
+
await fs11.rm(path14.join(paths.wikiDir, entry.previousPath), { force: true });
|
|
6484
7206
|
}
|
|
6485
7207
|
if (deletedPage?.kind === "output") {
|
|
6486
|
-
await
|
|
7208
|
+
await fs11.rm(path14.join(paths.wikiDir, "outputs", "assets", path14.basename(deletedPage.path, ".md")), {
|
|
6487
7209
|
recursive: true,
|
|
6488
7210
|
force: true
|
|
6489
7211
|
});
|
|
@@ -6573,7 +7295,7 @@ async function promoteCandidate(rootDir, target) {
|
|
|
6573
7295
|
const { paths } = await loadVaultConfig(rootDir);
|
|
6574
7296
|
const graph = await readJsonFile(paths.graphPath);
|
|
6575
7297
|
const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
|
|
6576
|
-
const raw = await
|
|
7298
|
+
const raw = await fs11.readFile(path14.join(paths.wikiDir, candidate.path), "utf8");
|
|
6577
7299
|
const parsed = matter7(raw);
|
|
6578
7300
|
const nextUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6579
7301
|
const nextContent = matter7.stringify(parsed.content, {
|
|
@@ -6585,10 +7307,10 @@ async function promoteCandidate(rootDir, target) {
|
|
|
6585
7307
|
)
|
|
6586
7308
|
});
|
|
6587
7309
|
const nextPath = candidateActivePath(candidate);
|
|
6588
|
-
const nextAbsolutePath =
|
|
6589
|
-
await ensureDir(
|
|
6590
|
-
await
|
|
6591
|
-
await
|
|
7310
|
+
const nextAbsolutePath = path14.join(paths.wikiDir, nextPath);
|
|
7311
|
+
await ensureDir(path14.dirname(nextAbsolutePath));
|
|
7312
|
+
await fs11.writeFile(nextAbsolutePath, nextContent, "utf8");
|
|
7313
|
+
await fs11.rm(path14.join(paths.wikiDir, candidate.path), { force: true });
|
|
6592
7314
|
const nextPage = parseStoredPage(nextPath, nextContent, { createdAt: candidate.createdAt, updatedAt: nextUpdatedAt });
|
|
6593
7315
|
const nextPages = sortGraphPages(
|
|
6594
7316
|
(graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path).concat(nextPage)
|
|
@@ -6632,7 +7354,7 @@ async function archiveCandidate(rootDir, target) {
|
|
|
6632
7354
|
const { paths } = await loadVaultConfig(rootDir);
|
|
6633
7355
|
const graph = await readJsonFile(paths.graphPath);
|
|
6634
7356
|
const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
|
|
6635
|
-
await
|
|
7357
|
+
await fs11.rm(path14.join(paths.wikiDir, candidate.path), { force: true });
|
|
6636
7358
|
const nextPages = sortGraphPages((graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path));
|
|
6637
7359
|
const nextGraph = {
|
|
6638
7360
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -6670,18 +7392,18 @@ async function archiveCandidate(rootDir, target) {
|
|
|
6670
7392
|
}
|
|
6671
7393
|
async function ensureObsidianWorkspace(rootDir) {
|
|
6672
7394
|
const { config } = await loadVaultConfig(rootDir);
|
|
6673
|
-
const obsidianDir =
|
|
7395
|
+
const obsidianDir = path14.join(rootDir, ".obsidian");
|
|
6674
7396
|
const projectIds = projectEntries(config).map((project) => project.id);
|
|
6675
7397
|
await ensureDir(obsidianDir);
|
|
6676
7398
|
await Promise.all([
|
|
6677
|
-
writeJsonFile(
|
|
7399
|
+
writeJsonFile(path14.join(obsidianDir, "app.json"), {
|
|
6678
7400
|
alwaysUpdateLinks: true,
|
|
6679
7401
|
newFileLocation: "folder",
|
|
6680
7402
|
newFileFolderPath: "wiki/insights",
|
|
6681
7403
|
useMarkdownLinks: false,
|
|
6682
7404
|
attachmentFolderPath: "raw/assets"
|
|
6683
7405
|
}),
|
|
6684
|
-
writeJsonFile(
|
|
7406
|
+
writeJsonFile(path14.join(obsidianDir, "core-plugins.json"), [
|
|
6685
7407
|
"file-explorer",
|
|
6686
7408
|
"global-search",
|
|
6687
7409
|
"switcher",
|
|
@@ -6691,7 +7413,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
6691
7413
|
"tag-pane",
|
|
6692
7414
|
"page-preview"
|
|
6693
7415
|
]),
|
|
6694
|
-
writeJsonFile(
|
|
7416
|
+
writeJsonFile(path14.join(obsidianDir, "graph.json"), {
|
|
6695
7417
|
"collapse-filter": false,
|
|
6696
7418
|
search: "",
|
|
6697
7419
|
showTags: true,
|
|
@@ -6703,7 +7425,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
6703
7425
|
})),
|
|
6704
7426
|
localJumps: false
|
|
6705
7427
|
}),
|
|
6706
|
-
writeJsonFile(
|
|
7428
|
+
writeJsonFile(path14.join(obsidianDir, "workspace.json"), {
|
|
6707
7429
|
active: "root",
|
|
6708
7430
|
lastOpenFiles: ["wiki/index.md", "wiki/projects/index.md", "wiki/candidates/index.md", "wiki/insights/index.md"],
|
|
6709
7431
|
left: {
|
|
@@ -6718,7 +7440,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
6718
7440
|
async function initVault(rootDir, options = {}) {
|
|
6719
7441
|
const { paths } = await initWorkspace(rootDir);
|
|
6720
7442
|
await installConfiguredAgents(rootDir);
|
|
6721
|
-
const insightsIndexPath =
|
|
7443
|
+
const insightsIndexPath = path14.join(paths.wikiDir, "insights", "index.md");
|
|
6722
7444
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6723
7445
|
await writeFileIfChanged(
|
|
6724
7446
|
insightsIndexPath,
|
|
@@ -6754,7 +7476,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
6754
7476
|
)
|
|
6755
7477
|
);
|
|
6756
7478
|
await writeFileIfChanged(
|
|
6757
|
-
|
|
7479
|
+
path14.join(paths.wikiDir, "projects", "index.md"),
|
|
6758
7480
|
matter7.stringify(["# Projects", "", "- Run `swarmvault compile` to build project rollups.", ""].join("\n"), {
|
|
6759
7481
|
page_id: "projects:index",
|
|
6760
7482
|
kind: "index",
|
|
@@ -6776,7 +7498,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
6776
7498
|
})
|
|
6777
7499
|
);
|
|
6778
7500
|
await writeFileIfChanged(
|
|
6779
|
-
|
|
7501
|
+
path14.join(paths.wikiDir, "candidates", "index.md"),
|
|
6780
7502
|
matter7.stringify(["# Candidates", "", "- Run `swarmvault compile` to stage candidate pages.", ""].join("\n"), {
|
|
6781
7503
|
page_id: "candidates:index",
|
|
6782
7504
|
kind: "index",
|
|
@@ -6893,7 +7615,7 @@ async function compileVault(rootDir, options = {}) {
|
|
|
6893
7615
|
),
|
|
6894
7616
|
Promise.all(
|
|
6895
7617
|
clean.map(async (manifest) => {
|
|
6896
|
-
const cached = await readJsonFile(
|
|
7618
|
+
const cached = await readJsonFile(path14.join(paths.analysesDir, `${manifest.sourceId}.json`));
|
|
6897
7619
|
if (cached) {
|
|
6898
7620
|
return cached;
|
|
6899
7621
|
}
|
|
@@ -6907,23 +7629,38 @@ async function compileVault(rootDir, options = {}) {
|
|
|
6907
7629
|
})
|
|
6908
7630
|
)
|
|
6909
7631
|
]);
|
|
6910
|
-
const
|
|
7632
|
+
const initialAnalyses = [...dirtyAnalyses, ...cleanAnalyses];
|
|
7633
|
+
const codeIndex = await buildCodeIndex(rootDir, manifests, initialAnalyses);
|
|
7634
|
+
const analyses = await Promise.all(
|
|
7635
|
+
initialAnalyses.map(async (analysis) => {
|
|
7636
|
+
const manifest = manifests.find((item) => item.sourceId === analysis.sourceId);
|
|
7637
|
+
if (!manifest || !analysis.code) {
|
|
7638
|
+
return analysis;
|
|
7639
|
+
}
|
|
7640
|
+
const enriched = enrichResolvedCodeImports(manifest, analysis, codeIndex);
|
|
7641
|
+
if (analysisSignature(enriched) !== analysisSignature(analysis)) {
|
|
7642
|
+
await writeJsonFile(path14.join(paths.analysesDir, `${analysis.sourceId}.json`), enriched);
|
|
7643
|
+
}
|
|
7644
|
+
return enriched;
|
|
7645
|
+
})
|
|
7646
|
+
);
|
|
6911
7647
|
await Promise.all([
|
|
6912
|
-
ensureDir(
|
|
6913
|
-
ensureDir(
|
|
6914
|
-
ensureDir(
|
|
6915
|
-
ensureDir(
|
|
6916
|
-
ensureDir(
|
|
6917
|
-
ensureDir(
|
|
6918
|
-
ensureDir(
|
|
6919
|
-
ensureDir(
|
|
6920
|
-
ensureDir(
|
|
6921
|
-
ensureDir(
|
|
7648
|
+
ensureDir(path14.join(paths.wikiDir, "sources")),
|
|
7649
|
+
ensureDir(path14.join(paths.wikiDir, "code")),
|
|
7650
|
+
ensureDir(path14.join(paths.wikiDir, "concepts")),
|
|
7651
|
+
ensureDir(path14.join(paths.wikiDir, "entities")),
|
|
7652
|
+
ensureDir(path14.join(paths.wikiDir, "outputs")),
|
|
7653
|
+
ensureDir(path14.join(paths.wikiDir, "projects")),
|
|
7654
|
+
ensureDir(path14.join(paths.wikiDir, "insights")),
|
|
7655
|
+
ensureDir(path14.join(paths.wikiDir, "candidates")),
|
|
7656
|
+
ensureDir(path14.join(paths.wikiDir, "candidates", "concepts")),
|
|
7657
|
+
ensureDir(path14.join(paths.wikiDir, "candidates", "entities"))
|
|
6922
7658
|
]);
|
|
6923
7659
|
const sync = await syncVaultArtifacts(rootDir, {
|
|
6924
7660
|
schemas,
|
|
6925
7661
|
manifests,
|
|
6926
7662
|
analyses,
|
|
7663
|
+
codeIndex,
|
|
6927
7664
|
sourceProjects,
|
|
6928
7665
|
outputPages,
|
|
6929
7666
|
insightPages,
|
|
@@ -7059,7 +7796,7 @@ async function queryVault(rootDir, options) {
|
|
|
7059
7796
|
assetFiles: staged.assetFiles
|
|
7060
7797
|
}
|
|
7061
7798
|
]);
|
|
7062
|
-
stagedPath =
|
|
7799
|
+
stagedPath = path14.join(approval.approvalDir, "wiki", staged.page.path);
|
|
7063
7800
|
savedPageId = staged.page.id;
|
|
7064
7801
|
approvalId = approval.approvalId;
|
|
7065
7802
|
approvalDir = approval.approvalDir;
|
|
@@ -7315,9 +8052,9 @@ ${orchestrationNotes.join("\n")}
|
|
|
7315
8052
|
approvalId = approval.approvalId;
|
|
7316
8053
|
approvalDir = approval.approvalDir;
|
|
7317
8054
|
stepResults.forEach((result, index) => {
|
|
7318
|
-
result.stagedPath =
|
|
8055
|
+
result.stagedPath = path14.join(approval.approvalDir, "wiki", stagedStepPages[index]?.page.path ?? "");
|
|
7319
8056
|
});
|
|
7320
|
-
stagedHubPath =
|
|
8057
|
+
stagedHubPath = path14.join(approval.approvalDir, "wiki", hubPage.path);
|
|
7321
8058
|
} else {
|
|
7322
8059
|
await refreshVaultAfterOutputSave(rootDir);
|
|
7323
8060
|
}
|
|
@@ -7374,15 +8111,15 @@ async function listPages(rootDir) {
|
|
|
7374
8111
|
}
|
|
7375
8112
|
async function readPage(rootDir, relativePath) {
|
|
7376
8113
|
const { paths } = await loadVaultConfig(rootDir);
|
|
7377
|
-
const absolutePath =
|
|
8114
|
+
const absolutePath = path14.resolve(paths.wikiDir, relativePath);
|
|
7378
8115
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
7379
8116
|
return null;
|
|
7380
8117
|
}
|
|
7381
|
-
const raw = await
|
|
8118
|
+
const raw = await fs11.readFile(absolutePath, "utf8");
|
|
7382
8119
|
const parsed = matter7(raw);
|
|
7383
8120
|
return {
|
|
7384
8121
|
path: relativePath,
|
|
7385
|
-
title: typeof parsed.data.title === "string" ? parsed.data.title :
|
|
8122
|
+
title: typeof parsed.data.title === "string" ? parsed.data.title : path14.basename(relativePath, path14.extname(relativePath)),
|
|
7386
8123
|
frontmatter: parsed.data,
|
|
7387
8124
|
content: parsed.content
|
|
7388
8125
|
};
|
|
@@ -7418,7 +8155,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
7418
8155
|
severity: "warning",
|
|
7419
8156
|
code: "stale_page",
|
|
7420
8157
|
message: `Page ${page.title} is stale because the vault schema changed.`,
|
|
7421
|
-
pagePath:
|
|
8158
|
+
pagePath: path14.join(paths.wikiDir, page.path),
|
|
7422
8159
|
relatedPageIds: [page.id]
|
|
7423
8160
|
});
|
|
7424
8161
|
}
|
|
@@ -7429,7 +8166,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
7429
8166
|
severity: "warning",
|
|
7430
8167
|
code: "stale_page",
|
|
7431
8168
|
message: `Page ${page.title} is stale because source ${sourceId} changed.`,
|
|
7432
|
-
pagePath:
|
|
8169
|
+
pagePath: path14.join(paths.wikiDir, page.path),
|
|
7433
8170
|
relatedSourceIds: [sourceId],
|
|
7434
8171
|
relatedPageIds: [page.id]
|
|
7435
8172
|
});
|
|
@@ -7440,13 +8177,13 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
7440
8177
|
severity: "info",
|
|
7441
8178
|
code: "orphan_page",
|
|
7442
8179
|
message: `Page ${page.title} has no backlinks.`,
|
|
7443
|
-
pagePath:
|
|
8180
|
+
pagePath: path14.join(paths.wikiDir, page.path),
|
|
7444
8181
|
relatedPageIds: [page.id]
|
|
7445
8182
|
});
|
|
7446
8183
|
}
|
|
7447
|
-
const absolutePath =
|
|
8184
|
+
const absolutePath = path14.join(paths.wikiDir, page.path);
|
|
7448
8185
|
if (await fileExists(absolutePath)) {
|
|
7449
|
-
const content = await
|
|
8186
|
+
const content = await fs11.readFile(absolutePath, "utf8");
|
|
7450
8187
|
if (content.includes("## Claims")) {
|
|
7451
8188
|
const uncited = content.split("\n").filter((line) => line.startsWith("- ") && !line.includes("[source:"));
|
|
7452
8189
|
if (uncited.length) {
|
|
@@ -7526,7 +8263,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
7526
8263
|
}
|
|
7527
8264
|
|
|
7528
8265
|
// src/mcp.ts
|
|
7529
|
-
var SERVER_VERSION = "0.1.
|
|
8266
|
+
var SERVER_VERSION = "0.1.18";
|
|
7530
8267
|
async function createMcpServer(rootDir) {
|
|
7531
8268
|
const server = new McpServer({
|
|
7532
8269
|
name: "swarmvault",
|
|
@@ -7707,7 +8444,7 @@ async function createMcpServer(rootDir) {
|
|
|
7707
8444
|
},
|
|
7708
8445
|
async () => {
|
|
7709
8446
|
const { paths } = await loadVaultConfig(rootDir);
|
|
7710
|
-
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(
|
|
8447
|
+
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path15.relative(paths.sessionsDir, filePath))).sort();
|
|
7711
8448
|
return asTextResource("swarmvault://sessions", JSON.stringify(files, null, 2));
|
|
7712
8449
|
}
|
|
7713
8450
|
);
|
|
@@ -7740,8 +8477,8 @@ async function createMcpServer(rootDir) {
|
|
|
7740
8477
|
return asTextResource(`swarmvault://pages/${encodedPath}`, `Page not found: ${relativePath}`);
|
|
7741
8478
|
}
|
|
7742
8479
|
const { paths } = await loadVaultConfig(rootDir);
|
|
7743
|
-
const absolutePath =
|
|
7744
|
-
return asTextResource(`swarmvault://pages/${encodedPath}`, await
|
|
8480
|
+
const absolutePath = path15.resolve(paths.wikiDir, relativePath);
|
|
8481
|
+
return asTextResource(`swarmvault://pages/${encodedPath}`, await fs12.readFile(absolutePath, "utf8"));
|
|
7745
8482
|
}
|
|
7746
8483
|
);
|
|
7747
8484
|
server.registerResource(
|
|
@@ -7749,11 +8486,11 @@ async function createMcpServer(rootDir) {
|
|
|
7749
8486
|
new ResourceTemplate("swarmvault://sessions/{path}", {
|
|
7750
8487
|
list: async () => {
|
|
7751
8488
|
const { paths } = await loadVaultConfig(rootDir);
|
|
7752
|
-
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(
|
|
8489
|
+
const files = (await listFilesRecursive(paths.sessionsDir)).filter((filePath) => filePath.endsWith(".md")).map((filePath) => toPosix(path15.relative(paths.sessionsDir, filePath))).sort();
|
|
7753
8490
|
return {
|
|
7754
8491
|
resources: files.map((relativePath) => ({
|
|
7755
8492
|
uri: `swarmvault://sessions/${encodeURIComponent(relativePath)}`,
|
|
7756
|
-
name:
|
|
8493
|
+
name: path15.basename(relativePath, ".md"),
|
|
7757
8494
|
title: relativePath,
|
|
7758
8495
|
description: "SwarmVault session artifact",
|
|
7759
8496
|
mimeType: "text/markdown"
|
|
@@ -7770,11 +8507,11 @@ async function createMcpServer(rootDir) {
|
|
|
7770
8507
|
const { paths } = await loadVaultConfig(rootDir);
|
|
7771
8508
|
const encodedPath = typeof variables.path === "string" ? variables.path : "";
|
|
7772
8509
|
const relativePath = decodeURIComponent(encodedPath);
|
|
7773
|
-
const absolutePath =
|
|
8510
|
+
const absolutePath = path15.resolve(paths.sessionsDir, relativePath);
|
|
7774
8511
|
if (!absolutePath.startsWith(paths.sessionsDir) || !await fileExists(absolutePath)) {
|
|
7775
8512
|
return asTextResource(`swarmvault://sessions/${encodedPath}`, `Session not found: ${relativePath}`);
|
|
7776
8513
|
}
|
|
7777
|
-
return asTextResource(`swarmvault://sessions/${encodedPath}`, await
|
|
8514
|
+
return asTextResource(`swarmvault://sessions/${encodedPath}`, await fs12.readFile(absolutePath, "utf8"));
|
|
7778
8515
|
}
|
|
7779
8516
|
);
|
|
7780
8517
|
return server;
|
|
@@ -7822,13 +8559,13 @@ function asTextResource(uri, text) {
|
|
|
7822
8559
|
}
|
|
7823
8560
|
|
|
7824
8561
|
// src/schedule.ts
|
|
7825
|
-
import
|
|
7826
|
-
import
|
|
8562
|
+
import fs13 from "fs/promises";
|
|
8563
|
+
import path16 from "path";
|
|
7827
8564
|
function scheduleStatePath(schedulesDir, jobId) {
|
|
7828
|
-
return
|
|
8565
|
+
return path16.join(schedulesDir, `${encodeURIComponent(jobId)}.json`);
|
|
7829
8566
|
}
|
|
7830
8567
|
function scheduleLockPath(schedulesDir, jobId) {
|
|
7831
|
-
return
|
|
8568
|
+
return path16.join(schedulesDir, `${encodeURIComponent(jobId)}.lock`);
|
|
7832
8569
|
}
|
|
7833
8570
|
function parseEveryDuration(value) {
|
|
7834
8571
|
const match = value.trim().match(/^(\d+)(m|h|d)$/i);
|
|
@@ -7931,13 +8668,13 @@ async function acquireJobLease(rootDir, jobId) {
|
|
|
7931
8668
|
const { paths } = await loadVaultConfig(rootDir);
|
|
7932
8669
|
const leasePath = scheduleLockPath(paths.schedulesDir, jobId);
|
|
7933
8670
|
await ensureDir(paths.schedulesDir);
|
|
7934
|
-
const handle = await
|
|
8671
|
+
const handle = await fs13.open(leasePath, "wx");
|
|
7935
8672
|
await handle.writeFile(`${process.pid}
|
|
7936
8673
|
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
7937
8674
|
`);
|
|
7938
8675
|
await handle.close();
|
|
7939
8676
|
return async () => {
|
|
7940
|
-
await
|
|
8677
|
+
await fs13.rm(leasePath, { force: true });
|
|
7941
8678
|
};
|
|
7942
8679
|
}
|
|
7943
8680
|
async function listSchedules(rootDir) {
|
|
@@ -8085,24 +8822,24 @@ async function serveSchedules(rootDir, pollMs = 3e4) {
|
|
|
8085
8822
|
|
|
8086
8823
|
// src/viewer.ts
|
|
8087
8824
|
import { execFile } from "child_process";
|
|
8088
|
-
import
|
|
8825
|
+
import fs14 from "fs/promises";
|
|
8089
8826
|
import http from "http";
|
|
8090
|
-
import
|
|
8827
|
+
import path17 from "path";
|
|
8091
8828
|
import { promisify } from "util";
|
|
8092
8829
|
import matter8 from "gray-matter";
|
|
8093
8830
|
import mime2 from "mime-types";
|
|
8094
8831
|
var execFileAsync = promisify(execFile);
|
|
8095
8832
|
async function readViewerPage(rootDir, relativePath) {
|
|
8096
8833
|
const { paths } = await loadVaultConfig(rootDir);
|
|
8097
|
-
const absolutePath =
|
|
8834
|
+
const absolutePath = path17.resolve(paths.wikiDir, relativePath);
|
|
8098
8835
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
8099
8836
|
return null;
|
|
8100
8837
|
}
|
|
8101
|
-
const raw = await
|
|
8838
|
+
const raw = await fs14.readFile(absolutePath, "utf8");
|
|
8102
8839
|
const parsed = matter8(raw);
|
|
8103
8840
|
return {
|
|
8104
8841
|
path: relativePath,
|
|
8105
|
-
title: typeof parsed.data.title === "string" ? parsed.data.title :
|
|
8842
|
+
title: typeof parsed.data.title === "string" ? parsed.data.title : path17.basename(relativePath, path17.extname(relativePath)),
|
|
8106
8843
|
frontmatter: parsed.data,
|
|
8107
8844
|
content: parsed.content,
|
|
8108
8845
|
assets: normalizeOutputAssets(parsed.data.output_assets)
|
|
@@ -8110,12 +8847,12 @@ async function readViewerPage(rootDir, relativePath) {
|
|
|
8110
8847
|
}
|
|
8111
8848
|
async function readViewerAsset(rootDir, relativePath) {
|
|
8112
8849
|
const { paths } = await loadVaultConfig(rootDir);
|
|
8113
|
-
const absolutePath =
|
|
8850
|
+
const absolutePath = path17.resolve(paths.wikiDir, relativePath);
|
|
8114
8851
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
8115
8852
|
return null;
|
|
8116
8853
|
}
|
|
8117
8854
|
return {
|
|
8118
|
-
buffer: await
|
|
8855
|
+
buffer: await fs14.readFile(absolutePath),
|
|
8119
8856
|
mimeType: mime2.lookup(absolutePath) || "application/octet-stream"
|
|
8120
8857
|
};
|
|
8121
8858
|
}
|
|
@@ -8138,12 +8875,12 @@ async function readJsonBody(request) {
|
|
|
8138
8875
|
return JSON.parse(raw);
|
|
8139
8876
|
}
|
|
8140
8877
|
async function ensureViewerDist(viewerDistDir) {
|
|
8141
|
-
const indexPath =
|
|
8878
|
+
const indexPath = path17.join(viewerDistDir, "index.html");
|
|
8142
8879
|
if (await fileExists(indexPath)) {
|
|
8143
8880
|
return;
|
|
8144
8881
|
}
|
|
8145
|
-
const viewerProjectDir =
|
|
8146
|
-
if (await fileExists(
|
|
8882
|
+
const viewerProjectDir = path17.dirname(viewerDistDir);
|
|
8883
|
+
if (await fileExists(path17.join(viewerProjectDir, "package.json"))) {
|
|
8147
8884
|
await execFileAsync("pnpm", ["build"], { cwd: viewerProjectDir });
|
|
8148
8885
|
}
|
|
8149
8886
|
}
|
|
@@ -8160,7 +8897,7 @@ async function startGraphServer(rootDir, port) {
|
|
|
8160
8897
|
return;
|
|
8161
8898
|
}
|
|
8162
8899
|
response.writeHead(200, { "content-type": "application/json" });
|
|
8163
|
-
response.end(await
|
|
8900
|
+
response.end(await fs14.readFile(paths.graphPath, "utf8"));
|
|
8164
8901
|
return;
|
|
8165
8902
|
}
|
|
8166
8903
|
if (url.pathname === "/api/search") {
|
|
@@ -8259,8 +8996,8 @@ async function startGraphServer(rootDir, port) {
|
|
|
8259
8996
|
return;
|
|
8260
8997
|
}
|
|
8261
8998
|
const relativePath = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
|
|
8262
|
-
const target =
|
|
8263
|
-
const fallback =
|
|
8999
|
+
const target = path17.join(paths.viewerDistDir, relativePath);
|
|
9000
|
+
const fallback = path17.join(paths.viewerDistDir, "index.html");
|
|
8264
9001
|
const filePath = await fileExists(target) ? target : fallback;
|
|
8265
9002
|
if (!await fileExists(filePath)) {
|
|
8266
9003
|
response.writeHead(503, { "content-type": "text/plain" });
|
|
@@ -8268,7 +9005,7 @@ async function startGraphServer(rootDir, port) {
|
|
|
8268
9005
|
return;
|
|
8269
9006
|
}
|
|
8270
9007
|
response.writeHead(200, { "content-type": mime2.lookup(filePath) || "text/plain" });
|
|
8271
|
-
response.end(await
|
|
9008
|
+
response.end(await fs14.readFile(filePath));
|
|
8272
9009
|
});
|
|
8273
9010
|
await new Promise((resolve) => {
|
|
8274
9011
|
server.listen(effectivePort, resolve);
|
|
@@ -8295,7 +9032,7 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
8295
9032
|
throw new Error("Graph artifact not found. Run `swarmvault compile` first.");
|
|
8296
9033
|
}
|
|
8297
9034
|
await ensureViewerDist(paths.viewerDistDir);
|
|
8298
|
-
const indexPath =
|
|
9035
|
+
const indexPath = path17.join(paths.viewerDistDir, "index.html");
|
|
8299
9036
|
if (!await fileExists(indexPath)) {
|
|
8300
9037
|
throw new Error("Viewer build not found. Run `pnpm build` first.");
|
|
8301
9038
|
}
|
|
@@ -8319,16 +9056,16 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
8319
9056
|
} : null;
|
|
8320
9057
|
})
|
|
8321
9058
|
);
|
|
8322
|
-
const rawHtml = await
|
|
9059
|
+
const rawHtml = await fs14.readFile(indexPath, "utf8");
|
|
8323
9060
|
const scriptMatch = rawHtml.match(/<script type="module" crossorigin src="([^"]+)"><\/script>/);
|
|
8324
9061
|
const styleMatch = rawHtml.match(/<link rel="stylesheet" crossorigin href="([^"]+)">/);
|
|
8325
|
-
const scriptPath = scriptMatch?.[1] ?
|
|
8326
|
-
const stylePath = styleMatch?.[1] ?
|
|
9062
|
+
const scriptPath = scriptMatch?.[1] ? path17.join(paths.viewerDistDir, scriptMatch[1].replace(/^\//, "")) : null;
|
|
9063
|
+
const stylePath = styleMatch?.[1] ? path17.join(paths.viewerDistDir, styleMatch[1].replace(/^\//, "")) : null;
|
|
8327
9064
|
if (!scriptPath || !await fileExists(scriptPath)) {
|
|
8328
9065
|
throw new Error("Viewer script bundle not found. Run `pnpm build` first.");
|
|
8329
9066
|
}
|
|
8330
|
-
const script = await
|
|
8331
|
-
const style = stylePath && await fileExists(stylePath) ? await
|
|
9067
|
+
const script = await fs14.readFile(scriptPath, "utf8");
|
|
9068
|
+
const style = stylePath && await fileExists(stylePath) ? await fs14.readFile(stylePath, "utf8") : "";
|
|
8332
9069
|
const embeddedData = JSON.stringify({ graph, pages: pages.filter(Boolean) }, null, 2).replace(/</g, "\\u003c");
|
|
8333
9070
|
const html = [
|
|
8334
9071
|
"<!doctype html>",
|
|
@@ -8347,13 +9084,13 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
8347
9084
|
"</html>",
|
|
8348
9085
|
""
|
|
8349
9086
|
].filter(Boolean).join("\n");
|
|
8350
|
-
await
|
|
8351
|
-
await
|
|
8352
|
-
return
|
|
9087
|
+
await fs14.mkdir(path17.dirname(outputPath), { recursive: true });
|
|
9088
|
+
await fs14.writeFile(outputPath, html, "utf8");
|
|
9089
|
+
return path17.resolve(outputPath);
|
|
8353
9090
|
}
|
|
8354
9091
|
|
|
8355
9092
|
// src/watch.ts
|
|
8356
|
-
import
|
|
9093
|
+
import path18 from "path";
|
|
8357
9094
|
import process2 from "process";
|
|
8358
9095
|
import chokidar from "chokidar";
|
|
8359
9096
|
var MAX_BACKOFF_MS = 3e4;
|
|
@@ -8498,7 +9235,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
8498
9235
|
};
|
|
8499
9236
|
}
|
|
8500
9237
|
function toWatchReason(baseDir, targetPath) {
|
|
8501
|
-
return
|
|
9238
|
+
return path18.relative(baseDir, targetPath) || ".";
|
|
8502
9239
|
}
|
|
8503
9240
|
export {
|
|
8504
9241
|
acceptApproval,
|
|
@@ -8517,6 +9254,7 @@ export {
|
|
|
8517
9254
|
getWebSearchAdapterForTask,
|
|
8518
9255
|
getWorkspaceInfo,
|
|
8519
9256
|
importInbox,
|
|
9257
|
+
ingestDirectory,
|
|
8520
9258
|
ingestInput,
|
|
8521
9259
|
initVault,
|
|
8522
9260
|
initWorkspace,
|