@swarmvaultai/engine 0.1.16 → 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-333AMRSV.js +1056 -0
- package/dist/chunk-NCSZ4AKP.js +1057 -0
- package/dist/index.d.ts +43 -3
- package/dist/index.js +1775 -1035
- package/dist/registry-JFEW5RUP.js +12 -0
- package/dist/registry-ZNW3FDED.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";
|
|
@@ -55,6 +55,7 @@ function targetPathForAgent(rootDir, agent) {
|
|
|
55
55
|
case "codex":
|
|
56
56
|
case "goose":
|
|
57
57
|
case "pi":
|
|
58
|
+
case "opencode":
|
|
58
59
|
return path.join(rootDir, "AGENTS.md");
|
|
59
60
|
case "claude":
|
|
60
61
|
return path.join(rootDir, "CLAUDE.md");
|
|
@@ -93,6 +94,7 @@ async function installAgent(rootDir, agent) {
|
|
|
93
94
|
case "codex":
|
|
94
95
|
case "goose":
|
|
95
96
|
case "pi":
|
|
97
|
+
case "opencode":
|
|
96
98
|
await upsertManagedBlock(target, buildManagedBlock("agents"));
|
|
97
99
|
return target;
|
|
98
100
|
case "claude": {
|
|
@@ -127,80 +129,106 @@ async function installConfiguredAgents(rootDir) {
|
|
|
127
129
|
}
|
|
128
130
|
|
|
129
131
|
// src/ingest.ts
|
|
130
|
-
import
|
|
131
|
-
import
|
|
132
|
+
import fs5 from "fs/promises";
|
|
133
|
+
import path5 from "path";
|
|
132
134
|
import { Readability } from "@mozilla/readability";
|
|
135
|
+
import ignore from "ignore";
|
|
133
136
|
import { JSDOM } from "jsdom";
|
|
134
137
|
import mime from "mime-types";
|
|
135
138
|
import TurndownService from "turndown";
|
|
136
139
|
|
|
137
140
|
// src/code-analysis.ts
|
|
138
|
-
import
|
|
141
|
+
import fs3 from "fs/promises";
|
|
142
|
+
import path3 from "path";
|
|
139
143
|
import ts from "typescript";
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
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
|
+
);
|
|
150
169
|
}
|
|
170
|
+
return treeSitterModulePromise;
|
|
151
171
|
}
|
|
152
|
-
function
|
|
153
|
-
|
|
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;
|
|
154
179
|
}
|
|
155
|
-
function
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
case ts.DiagnosticCategory.Warning:
|
|
160
|
-
return "warning";
|
|
161
|
-
case ts.DiagnosticCategory.Suggestion:
|
|
162
|
-
return "suggestion";
|
|
163
|
-
default:
|
|
164
|
-
return "message";
|
|
180
|
+
async function loadLanguage(language) {
|
|
181
|
+
const cached = languageCache.get(language);
|
|
182
|
+
if (cached) {
|
|
183
|
+
return cached;
|
|
165
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;
|
|
166
193
|
}
|
|
167
|
-
function
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
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;
|
|
171
209
|
}
|
|
172
|
-
if (
|
|
173
|
-
const
|
|
174
|
-
return
|
|
175
|
-
normalizeWhitespace(
|
|
176
|
-
sourceText.slice(node.getStart(sourceFile), membersPos).replace(/\{\s*$/, "").trim()
|
|
177
|
-
),
|
|
178
|
-
180
|
|
179
|
-
);
|
|
210
|
+
if (language === "python") {
|
|
211
|
+
const dotted = normalized.replace(/\/__init__$/i, "").replace(/\//g, ".").replace(/^src\./, "");
|
|
212
|
+
return dotted || path2.posix.basename(normalized);
|
|
180
213
|
}
|
|
181
|
-
return
|
|
214
|
+
return normalized.endsWith("/index") ? normalized.slice(0, -"/index".length) || path2.posix.basename(normalized) : normalized;
|
|
182
215
|
}
|
|
183
|
-
function
|
|
184
|
-
return
|
|
216
|
+
function singleLineSignature(value) {
|
|
217
|
+
return truncate(
|
|
218
|
+
normalizeWhitespace(
|
|
219
|
+
value.replace(/\{\s*$/, "").replace(/:\s*$/, ":").trim()
|
|
220
|
+
),
|
|
221
|
+
180
|
|
222
|
+
);
|
|
185
223
|
}
|
|
186
|
-
function
|
|
187
|
-
|
|
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}`}`;
|
|
188
229
|
}
|
|
189
|
-
function
|
|
190
|
-
|
|
191
|
-
return [];
|
|
192
|
-
}
|
|
193
|
-
const names = [];
|
|
194
|
-
const visit = (node) => {
|
|
195
|
-
if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && availableNames.has(node.expression.text)) {
|
|
196
|
-
if (node.expression.text !== selfName) {
|
|
197
|
-
names.push(node.expression.text);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
ts.forEachChild(node, visit);
|
|
201
|
-
};
|
|
202
|
-
visit(root);
|
|
203
|
-
return uniqueBy(names, (name) => name);
|
|
230
|
+
function escapeRegExp(value) {
|
|
231
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
204
232
|
}
|
|
205
233
|
function collectCallNamesFromText(text, availableNames, selfName) {
|
|
206
234
|
if (!text) {
|
|
@@ -218,132 +246,7 @@ function collectCallNamesFromText(text, availableNames, selfName) {
|
|
|
218
246
|
}
|
|
219
247
|
return uniqueBy(names, (name) => name);
|
|
220
248
|
}
|
|
221
|
-
function
|
|
222
|
-
return uniqueBy(
|
|
223
|
-
(clauses ?? []).filter((clause) => clause.token === token).flatMap(
|
|
224
|
-
(clause) => clause.types.map((typeNode) => {
|
|
225
|
-
if (ts.isIdentifier(typeNode.expression)) {
|
|
226
|
-
return typeNode.expression.text;
|
|
227
|
-
}
|
|
228
|
-
if (ts.isPropertyAccessExpression(typeNode.expression)) {
|
|
229
|
-
return typeNode.expression.getText();
|
|
230
|
-
}
|
|
231
|
-
return typeNode.getText();
|
|
232
|
-
})
|
|
233
|
-
),
|
|
234
|
-
(name) => name
|
|
235
|
-
);
|
|
236
|
-
}
|
|
237
|
-
function isNodeExported(node) {
|
|
238
|
-
return Boolean(
|
|
239
|
-
ts.canHaveModifiers(node) && ts.getModifiers(node)?.some(
|
|
240
|
-
(modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword || modifier.kind === ts.SyntaxKind.DefaultKeyword
|
|
241
|
-
)
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
function makeSymbolId(sourceId, name, seen) {
|
|
245
|
-
const base = slugify(name);
|
|
246
|
-
const count = (seen.get(base) ?? 0) + 1;
|
|
247
|
-
seen.set(base, count);
|
|
248
|
-
return `symbol:${sourceId}:${count === 1 ? base : `${base}-${count}`}`;
|
|
249
|
-
}
|
|
250
|
-
function summarizeModule(manifest, code) {
|
|
251
|
-
const localImports = code.imports.filter((item) => !item.isExternal && !item.reExport).length;
|
|
252
|
-
const externalImports = code.imports.filter((item) => item.isExternal).length;
|
|
253
|
-
const exportedCount = code.symbols.filter((symbol) => symbol.exported).length;
|
|
254
|
-
const parts = [`${code.language} module`, `defining ${code.symbols.length} top-level symbol(s)`, `exporting ${exportedCount} symbol(s)`];
|
|
255
|
-
if (localImports > 0) {
|
|
256
|
-
parts.push(`importing ${localImports} local module(s)`);
|
|
257
|
-
}
|
|
258
|
-
if (externalImports > 0) {
|
|
259
|
-
parts.push(`depending on ${externalImports} external package import(s)`);
|
|
260
|
-
}
|
|
261
|
-
if (code.diagnostics.length > 0) {
|
|
262
|
-
parts.push(`with ${code.diagnostics.length} parser diagnostic(s)`);
|
|
263
|
-
}
|
|
264
|
-
return `${manifest.title} is a ${parts.join(", ")}.`;
|
|
265
|
-
}
|
|
266
|
-
function codeClaims(manifest, code) {
|
|
267
|
-
const claims = [];
|
|
268
|
-
if (code.exports.length > 0) {
|
|
269
|
-
claims.push({
|
|
270
|
-
text: `${manifest.title} exports ${code.exports.slice(0, 4).join(", ")}${code.exports.length > 4 ? ", and more" : ""}.`,
|
|
271
|
-
confidence: 1,
|
|
272
|
-
status: "extracted",
|
|
273
|
-
polarity: "neutral",
|
|
274
|
-
citation: manifest.sourceId
|
|
275
|
-
});
|
|
276
|
-
}
|
|
277
|
-
if (code.symbols.length > 0) {
|
|
278
|
-
claims.push({
|
|
279
|
-
text: `${manifest.title} defines ${code.symbols.slice(0, 5).map((symbol) => symbol.name).join(", ")}${code.symbols.length > 5 ? ", and more" : ""}.`,
|
|
280
|
-
confidence: 1,
|
|
281
|
-
status: "extracted",
|
|
282
|
-
polarity: "neutral",
|
|
283
|
-
citation: manifest.sourceId
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
if (code.imports.length > 0) {
|
|
287
|
-
claims.push({
|
|
288
|
-
text: `${manifest.title} imports ${code.imports.slice(0, 4).map((item) => item.specifier).join(", ")}${code.imports.length > 4 ? ", and more" : ""}.`,
|
|
289
|
-
confidence: 1,
|
|
290
|
-
status: "extracted",
|
|
291
|
-
polarity: "neutral",
|
|
292
|
-
citation: manifest.sourceId
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
if (code.diagnostics.length > 0) {
|
|
296
|
-
claims.push({
|
|
297
|
-
text: `${manifest.title} has ${code.diagnostics.length} parser diagnostic(s) that should be reviewed before trusting the module summary.`,
|
|
298
|
-
confidence: 1,
|
|
299
|
-
status: "extracted",
|
|
300
|
-
polarity: "negative",
|
|
301
|
-
citation: manifest.sourceId
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
|
-
return claims.slice(0, 4).map((claim, index) => ({
|
|
305
|
-
id: `claim:${manifest.sourceId}:${index + 1}`,
|
|
306
|
-
...claim
|
|
307
|
-
}));
|
|
308
|
-
}
|
|
309
|
-
function codeQuestions(manifest, code) {
|
|
310
|
-
const questions = [
|
|
311
|
-
code.exports.length > 0 ? `Which downstream pages should explain how ${manifest.title} exports are consumed?` : "",
|
|
312
|
-
code.imports.some((item) => !item.isExternal) ? `How does ${manifest.title} coordinate with its imported local modules?` : "",
|
|
313
|
-
code.dependencies[0] ? `Why does ${manifest.title} depend on ${code.dependencies[0]}?` : "",
|
|
314
|
-
`What broader responsibility does ${manifest.title} serve in the codebase?`
|
|
315
|
-
].filter(Boolean);
|
|
316
|
-
return uniqueBy(questions, (question) => question).slice(0, 4);
|
|
317
|
-
}
|
|
318
|
-
function resolveVariableKind(statement) {
|
|
319
|
-
return statement.declarationList.flags & ts.NodeFlags.Const ? "variable" : "variable";
|
|
320
|
-
}
|
|
321
|
-
function splitLines(content) {
|
|
322
|
-
return content.split(/\r?\n/);
|
|
323
|
-
}
|
|
324
|
-
function leadingIndent(line) {
|
|
325
|
-
const match = line.match(/^[ \t]*/);
|
|
326
|
-
return match ? match[0].replace(/\t/g, " ").length : 0;
|
|
327
|
-
}
|
|
328
|
-
function normalizeSymbolReference(value) {
|
|
329
|
-
const withoutGenerics = value.replace(/<[^>]*>/g, "");
|
|
330
|
-
const withoutDecorators = withoutGenerics.replace(/['"&*()[\]{}]/g, " ");
|
|
331
|
-
const trimmed = withoutDecorators.trim();
|
|
332
|
-
const lastSegment = trimmed.split(/::|\./).filter(Boolean).at(-1) ?? trimmed;
|
|
333
|
-
return lastSegment.replace(/[,:;]+$/g, "").trim();
|
|
334
|
-
}
|
|
335
|
-
function singleLineSignature(line) {
|
|
336
|
-
return truncate(
|
|
337
|
-
normalizeWhitespace(
|
|
338
|
-
line.replace(/\{\s*$/, "").replace(/:\s*$/, ":").trim()
|
|
339
|
-
),
|
|
340
|
-
180
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
function buildDiagnostic(code, message, line, column = 1, category = "warning") {
|
|
344
|
-
return { code, category, message, line, column };
|
|
345
|
-
}
|
|
346
|
-
function finalizeCodeAnalysis(manifest, language, imports, draftSymbols, exportLabels, diagnostics) {
|
|
249
|
+
function finalizeCodeAnalysis(manifest, language, imports, draftSymbols, exportLabels, diagnostics, metadata) {
|
|
347
250
|
const topLevelNames = new Set(draftSymbols.map((symbol) => symbol.name));
|
|
348
251
|
for (const symbol of draftSymbols) {
|
|
349
252
|
if (symbol.callNames.length === 0 && symbol.bodyText) {
|
|
@@ -364,6 +267,8 @@ function finalizeCodeAnalysis(manifest, language, imports, draftSymbols, exportL
|
|
|
364
267
|
return {
|
|
365
268
|
moduleId: `module:${manifest.sourceId}`,
|
|
366
269
|
language,
|
|
270
|
+
moduleName: metadata?.moduleName ?? manifestModuleName(manifest, language),
|
|
271
|
+
namespace: metadata?.namespace,
|
|
367
272
|
imports,
|
|
368
273
|
dependencies: uniqueBy(
|
|
369
274
|
imports.filter((item) => item.isExternal).map((item) => item.specifier),
|
|
@@ -374,631 +279,957 @@ function finalizeCodeAnalysis(manifest, language, imports, draftSymbols, exportL
|
|
|
374
279
|
diagnostics
|
|
375
280
|
};
|
|
376
281
|
}
|
|
377
|
-
function
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
}
|
|
387
|
-
if (leadingIndent(line) <= startIndent) {
|
|
388
|
-
break;
|
|
389
|
-
}
|
|
390
|
-
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;
|
|
391
291
|
}
|
|
392
|
-
|
|
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;
|
|
393
299
|
}
|
|
394
|
-
function
|
|
395
|
-
return
|
|
396
|
-
const [rawSpecifier, rawAlias] = item.split(/\s+as\s+/i);
|
|
397
|
-
return {
|
|
398
|
-
specifier: rawSpecifier.trim(),
|
|
399
|
-
alias: rawAlias?.trim()
|
|
400
|
-
};
|
|
401
|
-
});
|
|
300
|
+
function exportedByCapitalization(name) {
|
|
301
|
+
return /^[A-Z]/.test(name);
|
|
402
302
|
}
|
|
403
|
-
function
|
|
404
|
-
|
|
405
|
-
|
|
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) {
|
|
406
311
|
return [];
|
|
407
312
|
}
|
|
408
313
|
return uniqueBy(
|
|
409
|
-
|
|
314
|
+
node.descendantsOfType(["type_identifier", "identifier", "name"]).filter((item) => item !== null).map((item) => normalizeSymbolReference(item.text)).filter(Boolean),
|
|
410
315
|
(item) => item
|
|
411
316
|
);
|
|
412
317
|
}
|
|
413
|
-
function
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
318
|
+
function quotedPath(value) {
|
|
319
|
+
return value.replace(/^["'<]+|[">]+$/g, "").trim();
|
|
320
|
+
}
|
|
321
|
+
function diagnosticsFromTree(rootNode) {
|
|
322
|
+
if (!rootNode.hasError) {
|
|
323
|
+
return [];
|
|
324
|
+
}
|
|
418
325
|
const diagnostics = [];
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
if (!trimmed || trimmed.startsWith("#") || leadingIndent(rawLine) > 0) {
|
|
424
|
-
continue;
|
|
326
|
+
const seen = /* @__PURE__ */ new Set();
|
|
327
|
+
const visit = (node) => {
|
|
328
|
+
if (!node) {
|
|
329
|
+
return;
|
|
425
330
|
}
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
|
435
341
|
});
|
|
436
342
|
}
|
|
437
|
-
continue;
|
|
438
|
-
}
|
|
439
|
-
const fromImportMatch = trimmed.match(/^from\s+([.\w]+)\s+import\s+(.+)$/);
|
|
440
|
-
if (fromImportMatch) {
|
|
441
|
-
const importedSymbols = fromImportMatch[2].split(",").map((item) => item.trim()).filter(Boolean);
|
|
442
|
-
imports.push({
|
|
443
|
-
specifier: fromImportMatch[1],
|
|
444
|
-
importedSymbols,
|
|
445
|
-
isExternal: !isRelativeSpecifier(fromImportMatch[1]),
|
|
446
|
-
reExport: false
|
|
447
|
-
});
|
|
448
|
-
continue;
|
|
449
|
-
}
|
|
450
|
-
const classMatch = trimmed.match(/^class\s+([A-Za-z_]\w*)\s*(?:\(([^)]*)\))?\s*:/);
|
|
451
|
-
if (classMatch) {
|
|
452
|
-
const baseNames = classMatch[2] ? classMatch[2].split(",").map((item) => normalizeSymbolReference(item)).filter(Boolean) : [];
|
|
453
|
-
draftSymbols.push({
|
|
454
|
-
name: classMatch[1],
|
|
455
|
-
kind: "class",
|
|
456
|
-
signature: singleLineSignature(trimmed),
|
|
457
|
-
exported: !classMatch[1].startsWith("_"),
|
|
458
|
-
callNames: [],
|
|
459
|
-
extendsNames: baseNames,
|
|
460
|
-
implementsNames: [],
|
|
461
|
-
bodyText: collectPythonBlock(lines, index)
|
|
462
|
-
});
|
|
463
|
-
continue;
|
|
464
|
-
}
|
|
465
|
-
if (trimmed.startsWith("class ")) {
|
|
466
|
-
diagnostics.push(buildDiagnostic(1001, "Python class declaration is missing a trailing colon.", index + 1));
|
|
467
|
-
continue;
|
|
468
|
-
}
|
|
469
|
-
const functionMatch = trimmed.match(/^(?:async\s+)?def\s+([A-Za-z_]\w*)\s*\(/);
|
|
470
|
-
if (functionMatch) {
|
|
471
|
-
draftSymbols.push({
|
|
472
|
-
name: functionMatch[1],
|
|
473
|
-
kind: "function",
|
|
474
|
-
signature: singleLineSignature(trimmed),
|
|
475
|
-
exported: !functionMatch[1].startsWith("_"),
|
|
476
|
-
callNames: [],
|
|
477
|
-
extendsNames: [],
|
|
478
|
-
implementsNames: [],
|
|
479
|
-
bodyText: collectPythonBlock(lines, index)
|
|
480
|
-
});
|
|
481
|
-
continue;
|
|
482
343
|
}
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
continue;
|
|
486
|
-
}
|
|
487
|
-
const allMatch = trimmed.match(/^__all__\s*=\s*\[(.*)\]\s*$/);
|
|
488
|
-
if (allMatch) {
|
|
489
|
-
explicitExports = parsePythonAllExportList(trimmed);
|
|
490
|
-
continue;
|
|
491
|
-
}
|
|
492
|
-
const variableMatch = trimmed.match(/^([A-Za-z_]\w*)\s*(?::[^=]+)?=\s*(.+)$/);
|
|
493
|
-
if (variableMatch && !["True", "False", "None"].includes(variableMatch[1])) {
|
|
494
|
-
draftSymbols.push({
|
|
495
|
-
name: variableMatch[1],
|
|
496
|
-
kind: "variable",
|
|
497
|
-
signature: singleLineSignature(trimmed),
|
|
498
|
-
exported: !variableMatch[1].startsWith("_"),
|
|
499
|
-
callNames: [],
|
|
500
|
-
extendsNames: [],
|
|
501
|
-
implementsNames: [],
|
|
502
|
-
bodyText: variableMatch[2]
|
|
503
|
-
});
|
|
344
|
+
for (const child of node.children) {
|
|
345
|
+
visit(child);
|
|
504
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 [];
|
|
505
355
|
}
|
|
506
|
-
|
|
507
|
-
const
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
}
|
|
529
|
-
}
|
|
530
|
-
if (started && depth <= 0) {
|
|
531
|
-
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
|
|
532
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;
|
|
533
385
|
}
|
|
534
386
|
return {
|
|
535
|
-
|
|
536
|
-
|
|
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
|
|
537
392
|
};
|
|
538
393
|
}
|
|
539
|
-
function
|
|
540
|
-
|
|
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
|
+
};
|
|
541
419
|
}
|
|
542
|
-
function
|
|
543
|
-
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+([^;]+);$/);
|
|
544
432
|
if (!match) {
|
|
545
433
|
return void 0;
|
|
546
434
|
}
|
|
547
435
|
return {
|
|
548
|
-
specifier: match[
|
|
436
|
+
specifier: match[1].trim(),
|
|
549
437
|
importedSymbols: [],
|
|
550
|
-
|
|
551
|
-
isExternal: !isRelativeSpecifier(match[2]),
|
|
438
|
+
isExternal: !match[1].trim().startsWith("."),
|
|
552
439
|
reExport: false
|
|
553
440
|
};
|
|
554
441
|
}
|
|
555
|
-
function
|
|
556
|
-
const
|
|
557
|
-
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
|
+
};
|
|
558
468
|
}
|
|
559
|
-
function
|
|
560
|
-
const lines = splitLines(content);
|
|
469
|
+
function pythonCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
561
470
|
const imports = [];
|
|
562
471
|
const draftSymbols = [];
|
|
563
|
-
const
|
|
564
|
-
|
|
565
|
-
let inImportBlock = false;
|
|
566
|
-
for (let index = 0; index < lines.length; index += 1) {
|
|
567
|
-
const rawLine = lines[index] ?? "";
|
|
568
|
-
const trimmed = rawLine.trim();
|
|
569
|
-
if (!trimmed || trimmed.startsWith("//")) {
|
|
472
|
+
for (const child of rootNode.namedChildren) {
|
|
473
|
+
if (!child) {
|
|
570
474
|
continue;
|
|
571
475
|
}
|
|
572
|
-
if (
|
|
573
|
-
|
|
574
|
-
inImportBlock = false;
|
|
575
|
-
continue;
|
|
576
|
-
}
|
|
577
|
-
const parsed = parseGoImportLine(trimmed);
|
|
578
|
-
if (parsed) {
|
|
579
|
-
imports.push(parsed);
|
|
580
|
-
}
|
|
476
|
+
if (child.type === "import_statement") {
|
|
477
|
+
imports.push(...parsePythonImportStatement(child.text));
|
|
581
478
|
continue;
|
|
582
479
|
}
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
inImportBlock = true;
|
|
480
|
+
if (child.type === "import_from_statement") {
|
|
481
|
+
imports.push(...parsePythonFromImportStatement(child.text));
|
|
586
482
|
continue;
|
|
587
483
|
}
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
imports.push(parsed);
|
|
484
|
+
if (child.type === "class_definition") {
|
|
485
|
+
const name = extractIdentifier(child.childForFieldName("name"));
|
|
486
|
+
if (!name) {
|
|
487
|
+
continue;
|
|
593
488
|
}
|
|
594
|
-
|
|
595
|
-
}
|
|
596
|
-
const typeMatch = trimmed.match(/^type\s+([A-Za-z_]\w*)\s+(struct|interface)\b/);
|
|
597
|
-
if (typeMatch) {
|
|
598
|
-
const exported = exportedByCapitalization(typeMatch[1]);
|
|
489
|
+
const superclasses = parseCommaSeparatedReferences(nodeText(child.childForFieldName("superclasses")).replace(/^\(|\)$/g, ""));
|
|
599
490
|
draftSymbols.push({
|
|
600
|
-
name
|
|
601
|
-
kind:
|
|
602
|
-
signature: singleLineSignature(
|
|
603
|
-
exported,
|
|
491
|
+
name,
|
|
492
|
+
kind: "class",
|
|
493
|
+
signature: singleLineSignature(child.text),
|
|
494
|
+
exported: !name.startsWith("_"),
|
|
604
495
|
callNames: [],
|
|
605
|
-
extendsNames:
|
|
496
|
+
extendsNames: superclasses,
|
|
606
497
|
implementsNames: [],
|
|
607
|
-
bodyText:
|
|
498
|
+
bodyText: nodeText(child.childForFieldName("body"))
|
|
608
499
|
});
|
|
609
|
-
if (exported) {
|
|
610
|
-
exportLabels.push(typeMatch[1]);
|
|
611
|
-
}
|
|
612
500
|
continue;
|
|
613
501
|
}
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
name: aliasTypeMatch[1],
|
|
619
|
-
kind: "type_alias",
|
|
620
|
-
signature: singleLineSignature(trimmed),
|
|
621
|
-
exported,
|
|
622
|
-
callNames: [],
|
|
623
|
-
extendsNames: [],
|
|
624
|
-
implementsNames: [],
|
|
625
|
-
bodyText: trimmed
|
|
626
|
-
});
|
|
627
|
-
if (exported) {
|
|
628
|
-
exportLabels.push(aliasTypeMatch[1]);
|
|
502
|
+
if (child.type === "function_definition") {
|
|
503
|
+
const name = extractIdentifier(child.childForFieldName("name"));
|
|
504
|
+
if (!name) {
|
|
505
|
+
continue;
|
|
629
506
|
}
|
|
630
|
-
continue;
|
|
631
|
-
}
|
|
632
|
-
const funcMatch = trimmed.match(/^func\s+(?:\(([^)]*)\)\s*)?([A-Za-z_]\w*)\s*\(/);
|
|
633
|
-
if (funcMatch) {
|
|
634
|
-
const receiverType = funcMatch[1] ? receiverTypeName(funcMatch[1]) : "";
|
|
635
|
-
const symbolName = receiverType ? `${receiverType}.${funcMatch[2]}` : funcMatch[2];
|
|
636
|
-
const exported = exportedByCapitalization(funcMatch[2]);
|
|
637
|
-
const block = collectBracedBlock(lines, index);
|
|
638
507
|
draftSymbols.push({
|
|
639
|
-
name
|
|
508
|
+
name,
|
|
640
509
|
kind: "function",
|
|
641
|
-
signature: singleLineSignature(
|
|
642
|
-
exported,
|
|
643
|
-
callNames: [],
|
|
644
|
-
extendsNames: [],
|
|
645
|
-
implementsNames: [],
|
|
646
|
-
bodyText: block.text
|
|
647
|
-
});
|
|
648
|
-
if (exported) {
|
|
649
|
-
exportLabels.push(symbolName);
|
|
650
|
-
}
|
|
651
|
-
index = block.endIndex;
|
|
652
|
-
continue;
|
|
653
|
-
}
|
|
654
|
-
const variableMatch = trimmed.match(/^(?:var|const)\s+([A-Za-z_]\w*)\b/);
|
|
655
|
-
if (variableMatch) {
|
|
656
|
-
const exported = exportedByCapitalization(variableMatch[1]);
|
|
657
|
-
draftSymbols.push({
|
|
658
|
-
name: variableMatch[1],
|
|
659
|
-
kind: "variable",
|
|
660
|
-
signature: singleLineSignature(trimmed),
|
|
661
|
-
exported,
|
|
510
|
+
signature: singleLineSignature(child.text),
|
|
511
|
+
exported: !name.startsWith("_"),
|
|
662
512
|
callNames: [],
|
|
663
513
|
extendsNames: [],
|
|
664
514
|
implementsNames: [],
|
|
665
|
-
bodyText:
|
|
515
|
+
bodyText: nodeText(child.childForFieldName("body"))
|
|
666
516
|
});
|
|
667
|
-
if (exported) {
|
|
668
|
-
exportLabels.push(variableMatch[1]);
|
|
669
|
-
}
|
|
670
517
|
}
|
|
671
518
|
}
|
|
672
|
-
return finalizeCodeAnalysis(manifest, "
|
|
673
|
-
}
|
|
674
|
-
function analyzeRustUseStatement(statement) {
|
|
675
|
-
const cleaned = statement.replace(/^pub\s+/, "").replace(/^use\s+/, "").replace(/;$/, "").trim();
|
|
676
|
-
const aliasMatch = cleaned.match(/\s+as\s+([A-Za-z_]\w*)$/);
|
|
677
|
-
const withoutAlias = aliasMatch ? cleaned.slice(0, aliasMatch.index).trim() : cleaned;
|
|
678
|
-
const braceMatch = withoutAlias.match(/^(.*)::\{(.+)\}$/);
|
|
679
|
-
const importedSymbols = braceMatch ? braceMatch[2].split(",").map((item) => item.trim()).filter(Boolean) : [aliasMatch ? `${normalizeSymbolReference(withoutAlias)} as ${aliasMatch[1]}` : normalizeSymbolReference(withoutAlias)].filter(
|
|
680
|
-
Boolean
|
|
681
|
-
);
|
|
682
|
-
const specifier = braceMatch ? braceMatch[1].trim() : withoutAlias;
|
|
683
|
-
return {
|
|
684
|
-
specifier,
|
|
685
|
-
importedSymbols,
|
|
686
|
-
isExternal: !/^(crate|self|super)::/.test(specifier),
|
|
687
|
-
reExport: statement.trim().startsWith("pub use ")
|
|
688
|
-
};
|
|
519
|
+
return finalizeCodeAnalysis(manifest, "python", imports, draftSymbols, [], diagnostics);
|
|
689
520
|
}
|
|
690
|
-
function
|
|
691
|
-
return /^(pub(?:\([^)]*\))?\s+)/.test(trimmed);
|
|
692
|
-
}
|
|
693
|
-
function analyzeRustCode(manifest, content) {
|
|
694
|
-
const lines = splitLines(content);
|
|
521
|
+
function goCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
695
522
|
const imports = [];
|
|
696
523
|
const draftSymbols = [];
|
|
697
524
|
const exportLabels = [];
|
|
698
|
-
|
|
699
|
-
const
|
|
700
|
-
|
|
701
|
-
const rawLine = lines[index] ?? "";
|
|
702
|
-
const trimmed = rawLine.trim();
|
|
703
|
-
if (!trimmed || trimmed.startsWith("//")) {
|
|
525
|
+
let packageName;
|
|
526
|
+
for (const child of rootNode.namedChildren) {
|
|
527
|
+
if (!child) {
|
|
704
528
|
continue;
|
|
705
529
|
}
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
imports.push(analyzeRustUseStatement(trimmed));
|
|
530
|
+
if (child.type === "package_clause") {
|
|
531
|
+
packageName = extractIdentifier(child.namedChildren.at(0) ?? null);
|
|
709
532
|
continue;
|
|
710
533
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
kind: "function",
|
|
718
|
-
signature: singleLineSignature(trimmed),
|
|
719
|
-
exported,
|
|
720
|
-
callNames: [],
|
|
721
|
-
extendsNames: [],
|
|
722
|
-
implementsNames: [],
|
|
723
|
-
bodyText: block.text
|
|
724
|
-
};
|
|
725
|
-
draftSymbols.push(symbol);
|
|
726
|
-
symbolByName.set(symbol.name, symbol);
|
|
727
|
-
if (exported) {
|
|
728
|
-
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
|
+
}
|
|
729
540
|
}
|
|
730
|
-
index = block.endIndex;
|
|
731
541
|
continue;
|
|
732
542
|
}
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
name
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
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
|
+
}
|
|
751
568
|
}
|
|
752
|
-
index = block.endIndex;
|
|
753
569
|
continue;
|
|
754
570
|
}
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
const symbol = {
|
|
760
|
-
name: enumMatch[1],
|
|
761
|
-
kind: "enum",
|
|
762
|
-
signature: singleLineSignature(trimmed),
|
|
763
|
-
exported,
|
|
764
|
-
callNames: [],
|
|
765
|
-
extendsNames: [],
|
|
766
|
-
implementsNames: [],
|
|
767
|
-
bodyText: block.text
|
|
768
|
-
};
|
|
769
|
-
draftSymbols.push(symbol);
|
|
770
|
-
symbolByName.set(symbol.name, symbol);
|
|
771
|
-
if (exported) {
|
|
772
|
-
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;
|
|
773
575
|
}
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
const symbol = {
|
|
782
|
-
name: traitMatch[1],
|
|
783
|
-
kind: "interface",
|
|
784
|
-
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),
|
|
785
583
|
exported,
|
|
786
584
|
callNames: [],
|
|
787
585
|
extendsNames: [],
|
|
788
586
|
implementsNames: [],
|
|
789
|
-
bodyText:
|
|
790
|
-
};
|
|
791
|
-
draftSymbols.push(symbol);
|
|
792
|
-
symbolByName.set(symbol.name, symbol);
|
|
587
|
+
bodyText: nodeText(child.childForFieldName("body"))
|
|
588
|
+
});
|
|
793
589
|
if (exported) {
|
|
794
|
-
exportLabels.push(
|
|
590
|
+
exportLabels.push(symbolName);
|
|
795
591
|
}
|
|
796
|
-
|
|
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) {
|
|
797
605
|
continue;
|
|
798
606
|
}
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
const exported = rustVisibilityPrefix(trimmed);
|
|
802
|
-
const symbol = {
|
|
803
|
-
name: aliasMatch[1],
|
|
804
|
-
kind: "type_alias",
|
|
805
|
-
signature: singleLineSignature(trimmed),
|
|
806
|
-
exported,
|
|
807
|
-
callNames: [],
|
|
808
|
-
extendsNames: [],
|
|
809
|
-
implementsNames: [],
|
|
810
|
-
bodyText: trimmed
|
|
811
|
-
};
|
|
812
|
-
draftSymbols.push(symbol);
|
|
813
|
-
symbolByName.set(symbol.name, symbol);
|
|
814
|
-
if (exported) {
|
|
815
|
-
exportLabels.push(symbol.name);
|
|
816
|
-
}
|
|
607
|
+
if (child.type === "use_declaration") {
|
|
608
|
+
imports.push(parseRustUse(child.text));
|
|
817
609
|
continue;
|
|
818
610
|
}
|
|
819
|
-
const
|
|
820
|
-
if (
|
|
821
|
-
const
|
|
822
|
-
const
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
exported,
|
|
827
|
-
callNames: [],
|
|
828
|
-
extendsNames: [],
|
|
829
|
-
implementsNames: [],
|
|
830
|
-
bodyText: trimmed
|
|
831
|
-
};
|
|
832
|
-
draftSymbols.push(symbol);
|
|
833
|
-
symbolByName.set(symbol.name, symbol);
|
|
834
|
-
if (exported) {
|
|
835
|
-
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);
|
|
836
618
|
}
|
|
837
619
|
continue;
|
|
838
620
|
}
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
const traitName = normalizeSymbolReference(implMatch[1]);
|
|
842
|
-
const typeName = normalizeSymbolReference(implMatch[2]);
|
|
843
|
-
const symbol = symbolByName.get(typeName);
|
|
844
|
-
if (symbol && traitName) {
|
|
845
|
-
symbol.implementsNames.push(traitName);
|
|
846
|
-
}
|
|
621
|
+
if (!name) {
|
|
622
|
+
continue;
|
|
847
623
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
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) {
|
|
853
641
|
continue;
|
|
854
642
|
}
|
|
855
|
-
const exported =
|
|
643
|
+
const exported = child.namedChildren.some((item) => item?.type === "visibility_modifier");
|
|
856
644
|
const symbol = {
|
|
857
|
-
name
|
|
858
|
-
kind
|
|
859
|
-
signature: singleLineSignature(
|
|
645
|
+
name,
|
|
646
|
+
kind,
|
|
647
|
+
signature: singleLineSignature(child.text),
|
|
860
648
|
exported,
|
|
861
649
|
callNames: [],
|
|
862
|
-
extendsNames
|
|
650
|
+
extendsNames,
|
|
863
651
|
implementsNames: [],
|
|
864
|
-
bodyText:
|
|
652
|
+
bodyText: nodeText(child.childForFieldName("body")) || child.text
|
|
865
653
|
};
|
|
866
654
|
draftSymbols.push(symbol);
|
|
867
|
-
|
|
655
|
+
symbolsByName.set(name, symbol);
|
|
868
656
|
if (exported) {
|
|
869
|
-
exportLabels.push(
|
|
657
|
+
exportLabels.push(name);
|
|
870
658
|
}
|
|
871
659
|
}
|
|
872
660
|
return finalizeCodeAnalysis(manifest, "rust", imports, draftSymbols, exportLabels, diagnostics);
|
|
873
661
|
}
|
|
874
|
-
function
|
|
875
|
-
const cleaned = statement.replace(/^import\s+/, "").replace(/^static\s+/, "").replace(/;$/, "").trim();
|
|
876
|
-
const symbolName = normalizeSymbolReference(cleaned.replace(/\.\*$/, ""));
|
|
877
|
-
return {
|
|
878
|
-
specifier: cleaned.replace(/\.\*$/, ""),
|
|
879
|
-
importedSymbols: symbolName ? [symbolName] : [],
|
|
880
|
-
isExternal: true,
|
|
881
|
-
reExport: false
|
|
882
|
-
};
|
|
883
|
-
}
|
|
884
|
-
function parseJavaImplements(value) {
|
|
885
|
-
return (value ?? "").split(",").map((item) => normalizeSymbolReference(item)).filter(Boolean);
|
|
886
|
-
}
|
|
887
|
-
function analyzeJavaCode(manifest, content) {
|
|
888
|
-
const lines = splitLines(content);
|
|
662
|
+
function javaCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
889
663
|
const imports = [];
|
|
890
664
|
const draftSymbols = [];
|
|
891
665
|
const exportLabels = [];
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
}
|
|
901
|
-
if (
|
|
902
|
-
|
|
903
|
-
|
|
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
|
|
904
694
|
);
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
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);
|
|
924
733
|
}
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
const
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
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
|
+
}
|
|
943
770
|
}
|
|
944
|
-
|
|
945
|
-
|
|
771
|
+
}
|
|
772
|
+
if (child.type === "namespace_declaration") {
|
|
946
773
|
continue;
|
|
947
774
|
}
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
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) {
|
|
967
899
|
continue;
|
|
968
900
|
}
|
|
969
|
-
const
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
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) {
|
|
990
926
|
continue;
|
|
991
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
|
+
}
|
|
992
942
|
}
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
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);
|
|
998
1056
|
}
|
|
999
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
|
+
}
|
|
1000
1207
|
}
|
|
1001
|
-
|
|
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
|
+
};
|
|
1002
1233
|
}
|
|
1003
1234
|
function analyzeTypeScriptLikeCode(manifest, content) {
|
|
1004
1235
|
const language = manifest.language ?? inferCodeLanguage(manifest.originalPath ?? manifest.storedPath, manifest.mimeType) ?? "typescript";
|
|
@@ -1212,10 +1443,10 @@ function analyzeTypeScriptLikeCode(manifest, content) {
|
|
|
1212
1443
|
column: (position?.character ?? 0) + 1
|
|
1213
1444
|
};
|
|
1214
1445
|
});
|
|
1215
|
-
return
|
|
1446
|
+
return finalizeCodeAnalysis2(manifest, language, imports, draftSymbols, exportLabels, diagnostics);
|
|
1216
1447
|
}
|
|
1217
1448
|
function inferCodeLanguage(filePath, mimeType = "") {
|
|
1218
|
-
const extension =
|
|
1449
|
+
const extension = path3.extname(filePath).toLowerCase();
|
|
1219
1450
|
if (extension === ".ts" || extension === ".mts" || extension === ".cts") {
|
|
1220
1451
|
return "typescript";
|
|
1221
1452
|
}
|
|
@@ -1240,78 +1471,344 @@ function inferCodeLanguage(filePath, mimeType = "") {
|
|
|
1240
1471
|
if (extension === ".java") {
|
|
1241
1472
|
return "java";
|
|
1242
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
|
+
}
|
|
1243
1486
|
return void 0;
|
|
1244
1487
|
}
|
|
1245
1488
|
function modulePageTitle(manifest) {
|
|
1246
1489
|
return `${manifest.title} module`;
|
|
1247
1490
|
}
|
|
1248
1491
|
function importResolutionCandidates(basePath, specifier, extensions) {
|
|
1249
|
-
const resolved =
|
|
1250
|
-
if (
|
|
1251
|
-
return [
|
|
1492
|
+
const resolved = path3.posix.normalize(path3.posix.join(path3.posix.dirname(basePath), specifier));
|
|
1493
|
+
if (path3.posix.extname(resolved)) {
|
|
1494
|
+
return [resolved];
|
|
1252
1495
|
}
|
|
1253
|
-
const direct = extensions.map((extension) =>
|
|
1254
|
-
const indexFiles = extensions.map((extension) =>
|
|
1255
|
-
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);
|
|
1256
1499
|
}
|
|
1257
|
-
function
|
|
1258
|
-
|
|
1259
|
-
|
|
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);
|
|
1260
1512
|
}
|
|
1261
|
-
const candidates = new Set(
|
|
1262
|
-
importResolutionCandidates(manifest.originalPath, specifier, [".ts", ".tsx", ".js", ".jsx", ".mts", ".cts", ".mjs", ".cjs"])
|
|
1263
|
-
);
|
|
1264
|
-
return manifests.find(
|
|
1265
|
-
(candidate) => candidate.sourceKind === "code" && candidate.originalPath && candidates.has(path2.normalize(candidate.originalPath))
|
|
1266
|
-
)?.sourceId;
|
|
1267
1513
|
}
|
|
1268
|
-
function
|
|
1269
|
-
|
|
1270
|
-
|
|
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);
|
|
1271
1527
|
}
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
const
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
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);
|
|
1289
1671
|
}
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
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
|
+
);
|
|
1293
1688
|
}
|
|
1294
|
-
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) {
|
|
1295
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
|
+
}
|
|
1296
1721
|
switch (language) {
|
|
1297
1722
|
case "javascript":
|
|
1298
1723
|
case "jsx":
|
|
1299
1724
|
case "typescript":
|
|
1300
1725
|
case "tsx":
|
|
1301
|
-
return
|
|
1726
|
+
return repoRelativePath && isRelativeSpecifier(codeImport.specifier) ? repoPathMatches(lookup, ...importResolutionCandidates(repoRelativePath, codeImport.specifier, candidateExtensionsFor(language))) : aliasMatches(lookup, codeImport.specifier);
|
|
1302
1727
|
case "python":
|
|
1303
|
-
|
|
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);
|
|
1304
1749
|
default:
|
|
1305
|
-
return
|
|
1750
|
+
return [];
|
|
1306
1751
|
}
|
|
1307
1752
|
}
|
|
1308
|
-
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) {
|
|
1309
1805
|
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
1310
1806
|
}
|
|
1311
|
-
function analyzeCodeSource(manifest, extractedText, schemaHash) {
|
|
1807
|
+
async function analyzeCodeSource(manifest, extractedText, schemaHash) {
|
|
1312
1808
|
const language = manifest.language ?? inferCodeLanguage(manifest.originalPath ?? manifest.storedPath, manifest.mimeType) ?? "typescript";
|
|
1313
|
-
const code = language === "
|
|
1809
|
+
const code = language === "javascript" || language === "jsx" || language === "typescript" || language === "tsx" ? analyzeTypeScriptLikeCode(manifest, extractedText) : await analyzeTreeSitterCode(manifest, extractedText, language);
|
|
1314
1810
|
return {
|
|
1811
|
+
analysisVersion: 3,
|
|
1315
1812
|
sourceId: manifest.sourceId,
|
|
1316
1813
|
sourceHash: manifest.contentHash,
|
|
1317
1814
|
schemaHash,
|
|
@@ -1327,18 +1824,18 @@ function analyzeCodeSource(manifest, extractedText, schemaHash) {
|
|
|
1327
1824
|
}
|
|
1328
1825
|
|
|
1329
1826
|
// src/logs.ts
|
|
1330
|
-
import
|
|
1331
|
-
import
|
|
1827
|
+
import fs4 from "fs/promises";
|
|
1828
|
+
import path4 from "path";
|
|
1332
1829
|
import matter from "gray-matter";
|
|
1333
1830
|
async function resolveUniqueSessionPath(rootDir, operation, title, startedAt) {
|
|
1334
1831
|
const { paths } = await initWorkspace(rootDir);
|
|
1335
1832
|
await ensureDir(paths.sessionsDir);
|
|
1336
1833
|
const timestamp = startedAt.replace(/[:.]/g, "-");
|
|
1337
1834
|
const baseName = `${timestamp}-${operation}-${slugify(title)}`;
|
|
1338
|
-
let candidate =
|
|
1835
|
+
let candidate = path4.join(paths.sessionsDir, `${baseName}.md`);
|
|
1339
1836
|
let counter = 2;
|
|
1340
1837
|
while (await fileExists(candidate)) {
|
|
1341
|
-
candidate =
|
|
1838
|
+
candidate = path4.join(paths.sessionsDir, `${baseName}-${counter}.md`);
|
|
1342
1839
|
counter++;
|
|
1343
1840
|
}
|
|
1344
1841
|
return candidate;
|
|
@@ -1346,11 +1843,11 @@ async function resolveUniqueSessionPath(rootDir, operation, title, startedAt) {
|
|
|
1346
1843
|
async function appendLogEntry(rootDir, action, title, lines = []) {
|
|
1347
1844
|
const { paths } = await initWorkspace(rootDir);
|
|
1348
1845
|
await ensureDir(paths.wikiDir);
|
|
1349
|
-
const logPath =
|
|
1846
|
+
const logPath = path4.join(paths.wikiDir, "log.md");
|
|
1350
1847
|
const timestamp = (/* @__PURE__ */ new Date()).toISOString().slice(0, 19).replace("T", " ");
|
|
1351
1848
|
const entry = [`## [${timestamp}] ${action} | ${title}`, ...lines.map((line) => `- ${line}`), ""].join("\n");
|
|
1352
|
-
const existing = await fileExists(logPath) ? await
|
|
1353
|
-
await
|
|
1849
|
+
const existing = await fileExists(logPath) ? await fs4.readFile(logPath, "utf8") : "# Log\n\n";
|
|
1850
|
+
await fs4.writeFile(logPath, `${existing}${entry}
|
|
1354
1851
|
`, "utf8");
|
|
1355
1852
|
}
|
|
1356
1853
|
async function recordSession(rootDir, input) {
|
|
@@ -1360,8 +1857,8 @@ async function recordSession(rootDir, input) {
|
|
|
1360
1857
|
const finishedAtIso = new Date(input.finishedAt ?? input.startedAt).toISOString();
|
|
1361
1858
|
const durationMs = Math.max(0, new Date(finishedAtIso).getTime() - new Date(startedAtIso).getTime());
|
|
1362
1859
|
const sessionPath = await resolveUniqueSessionPath(rootDir, input.operation, input.title, startedAtIso);
|
|
1363
|
-
const sessionId =
|
|
1364
|
-
const relativeSessionPath =
|
|
1860
|
+
const sessionId = path4.basename(sessionPath, ".md");
|
|
1861
|
+
const relativeSessionPath = path4.relative(rootDir, sessionPath).split(path4.sep).join(path4.posix.sep);
|
|
1365
1862
|
const frontmatter = Object.fromEntries(
|
|
1366
1863
|
Object.entries({
|
|
1367
1864
|
session_id: sessionId,
|
|
@@ -1409,7 +1906,7 @@ async function recordSession(rootDir, input) {
|
|
|
1409
1906
|
frontmatter
|
|
1410
1907
|
);
|
|
1411
1908
|
await writeFileIfChanged(sessionPath, content);
|
|
1412
|
-
const logPath =
|
|
1909
|
+
const logPath = path4.join(paths.wikiDir, "log.md");
|
|
1413
1910
|
const timestamp = startedAtIso.slice(0, 19).replace("T", " ");
|
|
1414
1911
|
const entry = [
|
|
1415
1912
|
`## [${timestamp}] ${input.operation} | ${input.title}`,
|
|
@@ -1417,8 +1914,8 @@ async function recordSession(rootDir, input) {
|
|
|
1417
1914
|
...(input.lines ?? []).map((line) => `- ${line}`),
|
|
1418
1915
|
""
|
|
1419
1916
|
].join("\n");
|
|
1420
|
-
const existing = await fileExists(logPath) ? await
|
|
1421
|
-
await
|
|
1917
|
+
const existing = await fileExists(logPath) ? await fs4.readFile(logPath, "utf8") : "# Log\n\n";
|
|
1918
|
+
await fs4.writeFile(logPath, `${existing}${entry}
|
|
1422
1919
|
`, "utf8");
|
|
1423
1920
|
return { sessionPath, sessionId };
|
|
1424
1921
|
}
|
|
@@ -1429,6 +1926,8 @@ async function appendWatchRun(rootDir, run) {
|
|
|
1429
1926
|
|
|
1430
1927
|
// src/ingest.ts
|
|
1431
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"]);
|
|
1432
1931
|
function inferKind(mimeType, filePath) {
|
|
1433
1932
|
if (inferCodeLanguage(filePath, mimeType)) {
|
|
1434
1933
|
return "code";
|
|
@@ -1460,9 +1959,67 @@ function guessMimeType(target) {
|
|
|
1460
1959
|
function normalizeIngestOptions(options) {
|
|
1461
1960
|
return {
|
|
1462
1961
|
includeAssets: options?.includeAssets ?? true,
|
|
1463
|
-
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
|
|
1464
1968
|
};
|
|
1465
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
|
+
}
|
|
1466
2023
|
function buildCompositeHash(payloadBytes, attachments = []) {
|
|
1467
2024
|
if (!attachments.length) {
|
|
1468
2025
|
return sha256(payloadBytes);
|
|
@@ -1471,7 +2028,7 @@ function buildCompositeHash(payloadBytes, attachments = []) {
|
|
|
1471
2028
|
return sha256(`${sha256(payloadBytes)}|${attachmentSignature}`);
|
|
1472
2029
|
}
|
|
1473
2030
|
function sanitizeAssetRelativePath(value) {
|
|
1474
|
-
const normalized =
|
|
2031
|
+
const normalized = path5.posix.normalize(value.replace(/\\/g, "/"));
|
|
1475
2032
|
const segments = normalized.split("/").filter(Boolean).map((segment) => {
|
|
1476
2033
|
if (segment === ".") {
|
|
1477
2034
|
return "";
|
|
@@ -1491,7 +2048,7 @@ function normalizeLocalReference(value) {
|
|
|
1491
2048
|
return null;
|
|
1492
2049
|
}
|
|
1493
2050
|
const lowered = candidate.toLowerCase();
|
|
1494
|
-
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)) {
|
|
1495
2052
|
return null;
|
|
1496
2053
|
}
|
|
1497
2054
|
return candidate.replace(/\\/g, "/");
|
|
@@ -1553,18 +2110,107 @@ async function convertHtmlToMarkdown(html, url) {
|
|
|
1553
2110
|
};
|
|
1554
2111
|
}
|
|
1555
2112
|
async function readManifestByHash(manifestsDir, contentHash) {
|
|
1556
|
-
const entries = await
|
|
2113
|
+
const entries = await fs5.readdir(manifestsDir, { withFileTypes: true }).catch(() => []);
|
|
1557
2114
|
for (const entry of entries) {
|
|
1558
2115
|
if (!entry.isFile() || !entry.name.endsWith(".json")) {
|
|
1559
2116
|
continue;
|
|
1560
2117
|
}
|
|
1561
|
-
const manifest = await readJsonFile(
|
|
2118
|
+
const manifest = await readJsonFile(path5.join(manifestsDir, entry.name));
|
|
1562
2119
|
if (manifest?.contentHash === contentHash) {
|
|
1563
2120
|
return manifest;
|
|
1564
2121
|
}
|
|
1565
2122
|
}
|
|
1566
2123
|
return null;
|
|
1567
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
|
+
}
|
|
1568
2214
|
function resolveUrlMimeType(input, response) {
|
|
1569
2215
|
const headerMimeType = response.headers.get("content-type")?.split(";")[0]?.trim();
|
|
1570
2216
|
const guessedMimeType = guessMimeType(new URL(input).pathname);
|
|
@@ -1579,12 +2225,12 @@ function resolveUrlMimeType(input, response) {
|
|
|
1579
2225
|
function buildRemoteAssetRelativePath(assetUrl, mimeType) {
|
|
1580
2226
|
const url = new URL(assetUrl);
|
|
1581
2227
|
const normalized = sanitizeAssetRelativePath(`${url.hostname}${url.pathname || "/asset"}`);
|
|
1582
|
-
const extension =
|
|
1583
|
-
const directory =
|
|
1584
|
-
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);
|
|
1585
2231
|
const resolvedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
|
|
1586
2232
|
const hashedName = `${basename || "asset"}-${sha256(assetUrl).slice(0, 8)}${resolvedExtension}`;
|
|
1587
|
-
return directory === "." ? hashedName :
|
|
2233
|
+
return directory === "." ? hashedName : path5.posix.join(directory, hashedName);
|
|
1588
2234
|
}
|
|
1589
2235
|
async function readResponseBytesWithinLimit(response, maxBytes) {
|
|
1590
2236
|
const contentLength = Number.parseInt(response.headers.get("content-length") ?? "", 10);
|
|
@@ -1708,26 +2354,38 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
1708
2354
|
await ensureDir(paths.extractsDir);
|
|
1709
2355
|
const attachments = prepared.attachments ?? [];
|
|
1710
2356
|
const contentHash = prepared.contentHash ?? buildCompositeHash(prepared.payloadBytes, attachments);
|
|
1711
|
-
const
|
|
1712
|
-
|
|
1713
|
-
|
|
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 };
|
|
1714
2364
|
}
|
|
2365
|
+
const previous = existingByOrigin ?? void 0;
|
|
2366
|
+
const sourceId = previous?.sourceId ?? `${slugify(prepared.title)}-${contentHash.slice(0, 8)}`;
|
|
1715
2367
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
1716
|
-
const
|
|
1717
|
-
const
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
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");
|
|
1723
2381
|
}
|
|
1724
2382
|
const manifestAttachments = [];
|
|
1725
2383
|
for (const attachment of attachments) {
|
|
1726
|
-
const absoluteAttachmentPath =
|
|
1727
|
-
await ensureDir(
|
|
1728
|
-
await
|
|
2384
|
+
const absoluteAttachmentPath = path5.join(attachmentsDir, attachment.relativePath);
|
|
2385
|
+
await ensureDir(path5.dirname(absoluteAttachmentPath));
|
|
2386
|
+
await fs5.writeFile(absoluteAttachmentPath, attachment.bytes);
|
|
1729
2387
|
manifestAttachments.push({
|
|
1730
|
-
path: toPosix(
|
|
2388
|
+
path: toPosix(path5.relative(rootDir, absoluteAttachmentPath)),
|
|
1731
2389
|
mimeType: attachment.mimeType,
|
|
1732
2390
|
originalPath: attachment.originalPath
|
|
1733
2391
|
});
|
|
@@ -1739,37 +2397,39 @@ async function persistPreparedInput(rootDir, prepared, paths) {
|
|
|
1739
2397
|
sourceKind: prepared.sourceKind,
|
|
1740
2398
|
language: prepared.language,
|
|
1741
2399
|
originalPath: prepared.originalPath,
|
|
2400
|
+
repoRelativePath: prepared.repoRelativePath,
|
|
1742
2401
|
url: prepared.url,
|
|
1743
|
-
storedPath: toPosix(
|
|
1744
|
-
extractedTextPath: extractedTextPath ? toPosix(
|
|
2402
|
+
storedPath: toPosix(path5.relative(rootDir, storedPath)),
|
|
2403
|
+
extractedTextPath: extractedTextPath ? toPosix(path5.relative(rootDir, extractedTextPath)) : void 0,
|
|
1745
2404
|
mimeType: prepared.mimeType,
|
|
1746
2405
|
contentHash,
|
|
1747
|
-
createdAt: now,
|
|
2406
|
+
createdAt: previous?.createdAt ?? now,
|
|
1748
2407
|
updatedAt: now,
|
|
1749
2408
|
attachments: manifestAttachments.length ? manifestAttachments : void 0
|
|
1750
2409
|
};
|
|
1751
|
-
await writeJsonFile(
|
|
2410
|
+
await writeJsonFile(path5.join(paths.manifestsDir, `${sourceId}.json`), manifest);
|
|
1752
2411
|
await appendLogEntry(rootDir, "ingest", prepared.title, [
|
|
1753
2412
|
`source_id=${sourceId}`,
|
|
1754
2413
|
`kind=${prepared.sourceKind}`,
|
|
1755
2414
|
`attachments=${manifestAttachments.length}`,
|
|
2415
|
+
`updated=${previous ? "true" : "false"}`,
|
|
1756
2416
|
...prepared.logDetails ?? []
|
|
1757
2417
|
]);
|
|
1758
|
-
return { manifest, isNew:
|
|
2418
|
+
return { manifest, isNew: !previous, wasUpdated: Boolean(previous) };
|
|
1759
2419
|
}
|
|
1760
|
-
async function prepareFileInput(_rootDir, absoluteInput) {
|
|
1761
|
-
const payloadBytes = await
|
|
2420
|
+
async function prepareFileInput(_rootDir, absoluteInput, repoRoot) {
|
|
2421
|
+
const payloadBytes = await fs5.readFile(absoluteInput);
|
|
1762
2422
|
const mimeType = guessMimeType(absoluteInput);
|
|
1763
2423
|
const sourceKind = inferKind(mimeType, absoluteInput);
|
|
1764
2424
|
const language = inferCodeLanguage(absoluteInput, mimeType);
|
|
1765
|
-
const storedExtension =
|
|
2425
|
+
const storedExtension = path5.extname(absoluteInput) || `.${mime.extension(mimeType) || "bin"}`;
|
|
1766
2426
|
let title;
|
|
1767
2427
|
let extractedText;
|
|
1768
2428
|
if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
|
|
1769
2429
|
extractedText = payloadBytes.toString("utf8");
|
|
1770
|
-
title = titleFromText(
|
|
2430
|
+
title = titleFromText(path5.basename(absoluteInput, path5.extname(absoluteInput)), extractedText);
|
|
1771
2431
|
} else {
|
|
1772
|
-
title =
|
|
2432
|
+
title = path5.basename(absoluteInput, path5.extname(absoluteInput));
|
|
1773
2433
|
}
|
|
1774
2434
|
return {
|
|
1775
2435
|
title,
|
|
@@ -1777,6 +2437,7 @@ async function prepareFileInput(_rootDir, absoluteInput) {
|
|
|
1777
2437
|
sourceKind,
|
|
1778
2438
|
language,
|
|
1779
2439
|
originalPath: toPosix(absoluteInput),
|
|
2440
|
+
repoRelativePath: repoRelativePathFor(absoluteInput, repoRoot),
|
|
1780
2441
|
mimeType,
|
|
1781
2442
|
storedExtension,
|
|
1782
2443
|
payloadBytes,
|
|
@@ -1838,7 +2499,7 @@ async function prepareUrlInput(input, options) {
|
|
|
1838
2499
|
sourceKind = "markdown";
|
|
1839
2500
|
storedExtension = ".md";
|
|
1840
2501
|
} else {
|
|
1841
|
-
const extension =
|
|
2502
|
+
const extension = path5.extname(inputUrl.pathname);
|
|
1842
2503
|
storedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
|
|
1843
2504
|
if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
|
|
1844
2505
|
extractedText = payloadBytes.toString("utf8");
|
|
@@ -1888,14 +2549,14 @@ async function collectInboxAttachmentRefs(inputDir, files) {
|
|
|
1888
2549
|
if (sourceKind !== "markdown") {
|
|
1889
2550
|
continue;
|
|
1890
2551
|
}
|
|
1891
|
-
const content = await
|
|
2552
|
+
const content = await fs5.readFile(absolutePath, "utf8");
|
|
1892
2553
|
const refs = extractMarkdownReferences(content);
|
|
1893
2554
|
if (!refs.length) {
|
|
1894
2555
|
continue;
|
|
1895
2556
|
}
|
|
1896
2557
|
const sourceRefs = [];
|
|
1897
2558
|
for (const ref of refs) {
|
|
1898
|
-
const resolved =
|
|
2559
|
+
const resolved = path5.resolve(path5.dirname(absolutePath), ref);
|
|
1899
2560
|
if (!resolved.startsWith(inputDir) || !await fileExists(resolved)) {
|
|
1900
2561
|
continue;
|
|
1901
2562
|
}
|
|
@@ -1929,12 +2590,12 @@ function rewriteMarkdownReferences(content, replacements) {
|
|
|
1929
2590
|
});
|
|
1930
2591
|
}
|
|
1931
2592
|
async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
|
|
1932
|
-
const originalBytes = await
|
|
2593
|
+
const originalBytes = await fs5.readFile(absolutePath);
|
|
1933
2594
|
const originalText = originalBytes.toString("utf8");
|
|
1934
|
-
const title = titleFromText(
|
|
2595
|
+
const title = titleFromText(path5.basename(absolutePath, path5.extname(absolutePath)), originalText);
|
|
1935
2596
|
const attachments = [];
|
|
1936
2597
|
for (const attachmentRef of attachmentRefs) {
|
|
1937
|
-
const bytes = await
|
|
2598
|
+
const bytes = await fs5.readFile(attachmentRef.absolutePath);
|
|
1938
2599
|
attachments.push({
|
|
1939
2600
|
relativePath: sanitizeAssetRelativePath(attachmentRef.relativeRef),
|
|
1940
2601
|
mimeType: guessMimeType(attachmentRef.absolutePath),
|
|
@@ -1957,7 +2618,7 @@ async function prepareInboxMarkdownInput(absolutePath, attachmentRefs) {
|
|
|
1957
2618
|
sourceKind: "markdown",
|
|
1958
2619
|
originalPath: toPosix(absolutePath),
|
|
1959
2620
|
mimeType: "text/markdown",
|
|
1960
|
-
storedExtension:
|
|
2621
|
+
storedExtension: path5.extname(absolutePath) || ".md",
|
|
1961
2622
|
payloadBytes: Buffer.from(rewrittenText, "utf8"),
|
|
1962
2623
|
extractedText: rewrittenText,
|
|
1963
2624
|
attachments,
|
|
@@ -1970,13 +2631,53 @@ function isSupportedInboxKind(sourceKind) {
|
|
|
1970
2631
|
async function ingestInput(rootDir, input, options) {
|
|
1971
2632
|
const { paths } = await initWorkspace(rootDir);
|
|
1972
2633
|
const normalizedOptions = normalizeIngestOptions(options);
|
|
1973
|
-
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);
|
|
1974
2637
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
1975
2638
|
return result.manifest;
|
|
1976
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
|
+
}
|
|
1977
2678
|
async function importInbox(rootDir, inputDir) {
|
|
1978
2679
|
const { paths } = await initWorkspace(rootDir);
|
|
1979
|
-
const effectiveInputDir =
|
|
2680
|
+
const effectiveInputDir = path5.resolve(rootDir, inputDir ?? paths.inboxDir);
|
|
1980
2681
|
if (!await fileExists(effectiveInputDir)) {
|
|
1981
2682
|
throw new Error(`Inbox directory not found: ${effectiveInputDir}`);
|
|
1982
2683
|
}
|
|
@@ -1987,31 +2688,31 @@ async function importInbox(rootDir, inputDir) {
|
|
|
1987
2688
|
const skipped = [];
|
|
1988
2689
|
let attachmentCount = 0;
|
|
1989
2690
|
for (const absolutePath of files) {
|
|
1990
|
-
const basename =
|
|
2691
|
+
const basename = path5.basename(absolutePath);
|
|
1991
2692
|
if (basename.startsWith(".")) {
|
|
1992
|
-
skipped.push({ path: toPosix(
|
|
2693
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: "hidden_file" });
|
|
1993
2694
|
continue;
|
|
1994
2695
|
}
|
|
1995
2696
|
if (claimedAttachments.has(absolutePath)) {
|
|
1996
|
-
skipped.push({ path: toPosix(
|
|
2697
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: "referenced_attachment" });
|
|
1997
2698
|
continue;
|
|
1998
2699
|
}
|
|
1999
2700
|
const mimeType = guessMimeType(absolutePath);
|
|
2000
2701
|
const sourceKind = inferKind(mimeType, absolutePath);
|
|
2001
2702
|
if (!isSupportedInboxKind(sourceKind)) {
|
|
2002
|
-
skipped.push({ path: toPosix(
|
|
2703
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: `unsupported_kind:${sourceKind}` });
|
|
2003
2704
|
continue;
|
|
2004
2705
|
}
|
|
2005
2706
|
const prepared = sourceKind === "markdown" && refsBySource.has(absolutePath) ? await prepareInboxMarkdownInput(absolutePath, refsBySource.get(absolutePath) ?? []) : await prepareFileInput(rootDir, absolutePath);
|
|
2006
2707
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
2007
2708
|
if (!result.isNew) {
|
|
2008
|
-
skipped.push({ path: toPosix(
|
|
2709
|
+
skipped.push({ path: toPosix(path5.relative(rootDir, absolutePath)), reason: "duplicate_content" });
|
|
2009
2710
|
continue;
|
|
2010
2711
|
}
|
|
2011
2712
|
attachmentCount += result.manifest.attachments?.length ?? 0;
|
|
2012
2713
|
imported.push(result.manifest);
|
|
2013
2714
|
}
|
|
2014
|
-
await appendLogEntry(rootDir, "inbox_import", toPosix(
|
|
2715
|
+
await appendLogEntry(rootDir, "inbox_import", toPosix(path5.relative(rootDir, effectiveInputDir)) || ".", [
|
|
2015
2716
|
`scanned=${files.length}`,
|
|
2016
2717
|
`imported=${imported.length}`,
|
|
2017
2718
|
`attachments=${attachmentCount}`,
|
|
@@ -2030,9 +2731,9 @@ async function listManifests(rootDir) {
|
|
|
2030
2731
|
if (!await fileExists(paths.manifestsDir)) {
|
|
2031
2732
|
return [];
|
|
2032
2733
|
}
|
|
2033
|
-
const entries = await
|
|
2734
|
+
const entries = await fs5.readdir(paths.manifestsDir);
|
|
2034
2735
|
const manifests = await Promise.all(
|
|
2035
|
-
entries.filter((entry) => entry.endsWith(".json")).map((entry) => readJsonFile(
|
|
2736
|
+
entries.filter((entry) => entry.endsWith(".json")).map((entry) => readJsonFile(path5.join(paths.manifestsDir, entry)))
|
|
2036
2737
|
);
|
|
2037
2738
|
return manifests.filter((manifest) => Boolean(manifest));
|
|
2038
2739
|
}
|
|
@@ -2040,28 +2741,28 @@ async function readExtractedText(rootDir, manifest) {
|
|
|
2040
2741
|
if (!manifest.extractedTextPath) {
|
|
2041
2742
|
return void 0;
|
|
2042
2743
|
}
|
|
2043
|
-
const absolutePath =
|
|
2744
|
+
const absolutePath = path5.resolve(rootDir, manifest.extractedTextPath);
|
|
2044
2745
|
if (!await fileExists(absolutePath)) {
|
|
2045
2746
|
return void 0;
|
|
2046
2747
|
}
|
|
2047
|
-
return
|
|
2748
|
+
return fs5.readFile(absolutePath, "utf8");
|
|
2048
2749
|
}
|
|
2049
2750
|
|
|
2050
2751
|
// src/mcp.ts
|
|
2051
|
-
import
|
|
2052
|
-
import
|
|
2752
|
+
import fs12 from "fs/promises";
|
|
2753
|
+
import path15 from "path";
|
|
2053
2754
|
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2054
2755
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
2055
2756
|
import { z as z7 } from "zod";
|
|
2056
2757
|
|
|
2057
2758
|
// src/schema.ts
|
|
2058
|
-
import
|
|
2059
|
-
import
|
|
2759
|
+
import fs6 from "fs/promises";
|
|
2760
|
+
import path6 from "path";
|
|
2060
2761
|
function normalizeSchemaContent(content) {
|
|
2061
2762
|
return content.trim() ? content.trim() : defaultVaultSchema().trim();
|
|
2062
2763
|
}
|
|
2063
2764
|
async function readSchemaFile(schemaPath, fallback = defaultVaultSchema()) {
|
|
2064
|
-
const content = await fileExists(schemaPath) ? await
|
|
2765
|
+
const content = await fileExists(schemaPath) ? await fs6.readFile(schemaPath, "utf8") : fallback;
|
|
2065
2766
|
const normalized = normalizeSchemaContent(content);
|
|
2066
2767
|
return {
|
|
2067
2768
|
path: schemaPath,
|
|
@@ -2070,7 +2771,7 @@ async function readSchemaFile(schemaPath, fallback = defaultVaultSchema()) {
|
|
|
2070
2771
|
};
|
|
2071
2772
|
}
|
|
2072
2773
|
function resolveProjectSchemaPath(rootDir, schemaPath) {
|
|
2073
|
-
return
|
|
2774
|
+
return path6.resolve(rootDir, schemaPath);
|
|
2074
2775
|
}
|
|
2075
2776
|
function composeVaultSchema(root, projectSchemas = []) {
|
|
2076
2777
|
if (!projectSchemas.length) {
|
|
@@ -2086,7 +2787,7 @@ function composeVaultSchema(root, projectSchemas = []) {
|
|
|
2086
2787
|
(schema) => [
|
|
2087
2788
|
`## Project Schema`,
|
|
2088
2789
|
"",
|
|
2089
|
-
`Path: ${toPosix(
|
|
2790
|
+
`Path: ${toPosix(path6.relative(path6.dirname(root.path), schema.path) || schema.path)}`,
|
|
2090
2791
|
"",
|
|
2091
2792
|
schema.content
|
|
2092
2793
|
].join("\n")
|
|
@@ -2162,14 +2863,15 @@ function buildSchemaPrompt(schema, instruction) {
|
|
|
2162
2863
|
}
|
|
2163
2864
|
|
|
2164
2865
|
// src/vault.ts
|
|
2165
|
-
import
|
|
2166
|
-
import
|
|
2866
|
+
import fs11 from "fs/promises";
|
|
2867
|
+
import path14 from "path";
|
|
2167
2868
|
import matter7 from "gray-matter";
|
|
2168
2869
|
import { z as z6 } from "zod";
|
|
2169
2870
|
|
|
2170
2871
|
// src/analysis.ts
|
|
2171
|
-
import
|
|
2872
|
+
import path7 from "path";
|
|
2172
2873
|
import { z } from "zod";
|
|
2874
|
+
var ANALYSIS_FORMAT_VERSION = 3;
|
|
2173
2875
|
var sourceAnalysisSchema = z.object({
|
|
2174
2876
|
title: z.string().min(1),
|
|
2175
2877
|
summary: z.string().min(1),
|
|
@@ -2270,6 +2972,7 @@ function heuristicAnalysis(manifest, text, schemaHash) {
|
|
|
2270
2972
|
}));
|
|
2271
2973
|
const claimSentences = normalized.split(/(?<=[.!?])\s+/).filter(Boolean).slice(0, 4);
|
|
2272
2974
|
return {
|
|
2975
|
+
analysisVersion: ANALYSIS_FORMAT_VERSION,
|
|
2273
2976
|
sourceId: manifest.sourceId,
|
|
2274
2977
|
sourceHash: manifest.contentHash,
|
|
2275
2978
|
schemaHash,
|
|
@@ -2314,6 +3017,7 @@ ${truncate(text, 18e3)}`
|
|
|
2314
3017
|
sourceAnalysisSchema
|
|
2315
3018
|
);
|
|
2316
3019
|
return {
|
|
3020
|
+
analysisVersion: ANALYSIS_FORMAT_VERSION,
|
|
2317
3021
|
sourceId: manifest.sourceId,
|
|
2318
3022
|
sourceHash: manifest.contentHash,
|
|
2319
3023
|
schemaHash: schema.hash,
|
|
@@ -2342,17 +3046,18 @@ ${truncate(text, 18e3)}`
|
|
|
2342
3046
|
};
|
|
2343
3047
|
}
|
|
2344
3048
|
async function analyzeSource(manifest, extractedText, provider, paths, schema) {
|
|
2345
|
-
const cachePath =
|
|
3049
|
+
const cachePath = path7.join(paths.analysesDir, `${manifest.sourceId}.json`);
|
|
2346
3050
|
const cached = await readJsonFile(cachePath);
|
|
2347
|
-
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) {
|
|
2348
3052
|
return cached;
|
|
2349
3053
|
}
|
|
2350
3054
|
const content = normalizeWhitespace(extractedText ?? "");
|
|
2351
3055
|
let analysis;
|
|
2352
3056
|
if (manifest.sourceKind === "code" && content) {
|
|
2353
|
-
analysis = analyzeCodeSource(manifest, extractedText ?? "", schema.hash);
|
|
3057
|
+
analysis = await analyzeCodeSource(manifest, extractedText ?? "", schema.hash);
|
|
2354
3058
|
} else if (!content) {
|
|
2355
3059
|
analysis = {
|
|
3060
|
+
analysisVersion: ANALYSIS_FORMAT_VERSION,
|
|
2356
3061
|
sourceId: manifest.sourceId,
|
|
2357
3062
|
sourceHash: manifest.contentHash,
|
|
2358
3063
|
schemaHash: schema.hash,
|
|
@@ -2397,8 +3102,8 @@ function conflictConfidence(claimA, claimB) {
|
|
|
2397
3102
|
}
|
|
2398
3103
|
|
|
2399
3104
|
// src/deep-lint.ts
|
|
2400
|
-
import
|
|
2401
|
-
import
|
|
3105
|
+
import fs7 from "fs/promises";
|
|
3106
|
+
import path10 from "path";
|
|
2402
3107
|
import matter2 from "gray-matter";
|
|
2403
3108
|
import { z as z4 } from "zod";
|
|
2404
3109
|
|
|
@@ -2419,7 +3124,7 @@ function normalizeFindingSeverity(value) {
|
|
|
2419
3124
|
|
|
2420
3125
|
// src/orchestration.ts
|
|
2421
3126
|
import { spawn } from "child_process";
|
|
2422
|
-
import
|
|
3127
|
+
import path8 from "path";
|
|
2423
3128
|
import { z as z2 } from "zod";
|
|
2424
3129
|
var orchestrationRoleResultSchema = z2.object({
|
|
2425
3130
|
summary: z2.string().optional(),
|
|
@@ -2512,7 +3217,7 @@ async function runProviderRole(rootDir, role, roleConfig, input) {
|
|
|
2512
3217
|
}
|
|
2513
3218
|
async function runCommandRole(rootDir, role, executor, input) {
|
|
2514
3219
|
const [command, ...args] = executor.command;
|
|
2515
|
-
const cwd = executor.cwd ?
|
|
3220
|
+
const cwd = executor.cwd ? path8.resolve(rootDir, executor.cwd) : rootDir;
|
|
2516
3221
|
const child = spawn(command, args, {
|
|
2517
3222
|
cwd,
|
|
2518
3223
|
env: {
|
|
@@ -2606,7 +3311,7 @@ function summarizeRoleQuestions(results) {
|
|
|
2606
3311
|
}
|
|
2607
3312
|
|
|
2608
3313
|
// src/web-search/registry.ts
|
|
2609
|
-
import
|
|
3314
|
+
import path9 from "path";
|
|
2610
3315
|
import { pathToFileURL } from "url";
|
|
2611
3316
|
import { z as z3 } from "zod";
|
|
2612
3317
|
|
|
@@ -2704,7 +3409,7 @@ async function createWebSearchAdapter(id, config, rootDir) {
|
|
|
2704
3409
|
if (!config.module) {
|
|
2705
3410
|
throw new Error(`Web search provider ${id} is type "custom" but no module path was configured.`);
|
|
2706
3411
|
}
|
|
2707
|
-
const resolvedModule =
|
|
3412
|
+
const resolvedModule = path9.isAbsolute(config.module) ? config.module : path9.resolve(rootDir, config.module);
|
|
2708
3413
|
const loaded = await import(pathToFileURL(resolvedModule).href);
|
|
2709
3414
|
const parsed = customWebSearchModuleSchema.parse(loaded);
|
|
2710
3415
|
return parsed.createAdapter(id, config, rootDir);
|
|
@@ -2764,8 +3469,8 @@ async function loadContextPages(rootDir, graph) {
|
|
|
2764
3469
|
);
|
|
2765
3470
|
return Promise.all(
|
|
2766
3471
|
contextPages.slice(0, 18).map(async (page) => {
|
|
2767
|
-
const absolutePath =
|
|
2768
|
-
const raw = await
|
|
3472
|
+
const absolutePath = path10.join(paths.wikiDir, page.path);
|
|
3473
|
+
const raw = await fs7.readFile(absolutePath, "utf8").catch(() => "");
|
|
2769
3474
|
const parsed = matter2(raw);
|
|
2770
3475
|
return {
|
|
2771
3476
|
id: page.id,
|
|
@@ -2813,7 +3518,7 @@ function heuristicDeepFindings(contextPages, structuralFindings, graph) {
|
|
|
2813
3518
|
code: "missing_citation",
|
|
2814
3519
|
message: finding.message,
|
|
2815
3520
|
pagePath: finding.pagePath,
|
|
2816
|
-
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
|
|
2817
3522
|
});
|
|
2818
3523
|
}
|
|
2819
3524
|
for (const page of contextPages.filter((item) => item.kind === "source").slice(0, 3)) {
|
|
@@ -3167,9 +3872,7 @@ function buildModulePage(input) {
|
|
|
3167
3872
|
const relatedOutputs = input.relatedOutputs ?? [];
|
|
3168
3873
|
const backlinks = uniqueStrings([sourcePage.id, ...localModuleBacklinks, ...relatedOutputs.map((page) => page.id)]);
|
|
3169
3874
|
const importsSection = code.imports.length ? code.imports.map((item) => {
|
|
3170
|
-
const localModule = input.localModules.find(
|
|
3171
|
-
(moduleRef) => moduleRef.specifier === item.specifier && moduleRef.reExport === item.reExport
|
|
3172
|
-
);
|
|
3875
|
+
const localModule = item.resolvedSourceId ? input.localModules.find((moduleRef) => moduleRef.sourceId === item.resolvedSourceId && moduleRef.reExport === item.reExport) : void 0;
|
|
3173
3876
|
const importedBits = [
|
|
3174
3877
|
item.defaultImport ? `default \`${item.defaultImport}\`` : "",
|
|
3175
3878
|
item.namespaceImport ? `namespace \`${item.namespaceImport}\`` : "",
|
|
@@ -3180,6 +3883,7 @@ function buildModulePage(input) {
|
|
|
3180
3883
|
const suffix = importedBits.length ? ` (${importedBits.join("; ")})` : "";
|
|
3181
3884
|
return `- ${mode} ${importTarget}${suffix}`;
|
|
3182
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}\`)` : ""}`);
|
|
3183
3887
|
const exportsSection = code.exports.length ? code.exports.map((item) => `- \`${item}\``) : ["- No exports detected."];
|
|
3184
3888
|
const symbolsSection = code.symbols.length ? code.symbols.map(
|
|
3185
3889
|
(symbol) => `- \`${symbol.name}\` (${symbol.kind}${symbol.exported ? ", exported" : ""}): ${symbol.signature || "No signature recorded."}`
|
|
@@ -3226,7 +3930,10 @@ function buildModulePage(input) {
|
|
|
3226
3930
|
"",
|
|
3227
3931
|
`Source ID: \`${manifest.sourceId}\``,
|
|
3228
3932
|
`Source Path: \`${manifest.originalPath ?? manifest.storedPath}\``,
|
|
3933
|
+
...manifest.repoRelativePath ? [`Repo Path: \`${manifest.repoRelativePath}\``] : [],
|
|
3229
3934
|
`Language: \`${code.language}\``,
|
|
3935
|
+
...code.moduleName ? [`Module Name: \`${code.moduleName}\``] : [],
|
|
3936
|
+
...code.namespace ? [`Namespace/Package: \`${code.namespace}\``] : [],
|
|
3230
3937
|
`Source Page: [[${sourcePage.path.replace(/\.md$/, "")}|${sourcePage.title}]]`,
|
|
3231
3938
|
"",
|
|
3232
3939
|
"## Summary",
|
|
@@ -3249,6 +3956,10 @@ function buildModulePage(input) {
|
|
|
3249
3956
|
"",
|
|
3250
3957
|
...code.dependencies.length ? code.dependencies.map((dependency) => `- \`${dependency}\``) : ["- No external dependencies detected."],
|
|
3251
3958
|
"",
|
|
3959
|
+
"## Unresolved Local References",
|
|
3960
|
+
"",
|
|
3961
|
+
...unresolvedLocalImports.length ? unresolvedLocalImports : ["- No unresolved local references detected."],
|
|
3962
|
+
"",
|
|
3252
3963
|
"## Inheritance",
|
|
3253
3964
|
"",
|
|
3254
3965
|
...inheritanceSection.length ? inheritanceSection : ["- No inheritance relationships detected."],
|
|
@@ -3990,13 +4701,13 @@ function buildOutputAssetManifest(input) {
|
|
|
3990
4701
|
}
|
|
3991
4702
|
|
|
3992
4703
|
// src/outputs.ts
|
|
3993
|
-
import
|
|
3994
|
-
import
|
|
4704
|
+
import fs9 from "fs/promises";
|
|
4705
|
+
import path12 from "path";
|
|
3995
4706
|
import matter5 from "gray-matter";
|
|
3996
4707
|
|
|
3997
4708
|
// src/pages.ts
|
|
3998
|
-
import
|
|
3999
|
-
import
|
|
4709
|
+
import fs8 from "fs/promises";
|
|
4710
|
+
import path11 from "path";
|
|
4000
4711
|
import matter4 from "gray-matter";
|
|
4001
4712
|
function normalizeStringArray(value) {
|
|
4002
4713
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
@@ -4068,7 +4779,7 @@ async function loadExistingManagedPageState(absolutePath, defaults = {}) {
|
|
|
4068
4779
|
updatedAt: updatedFallback
|
|
4069
4780
|
};
|
|
4070
4781
|
}
|
|
4071
|
-
const content = await
|
|
4782
|
+
const content = await fs8.readFile(absolutePath, "utf8");
|
|
4072
4783
|
const parsed = matter4(content);
|
|
4073
4784
|
return {
|
|
4074
4785
|
status: normalizePageStatus(parsed.data.status, defaults.status ?? "active"),
|
|
@@ -4107,7 +4818,7 @@ function parseStoredPage(relativePath, content, defaults = {}) {
|
|
|
4107
4818
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
4108
4819
|
const fallbackCreatedAt = defaults.createdAt ?? now;
|
|
4109
4820
|
const fallbackUpdatedAt = defaults.updatedAt ?? fallbackCreatedAt;
|
|
4110
|
-
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");
|
|
4111
4822
|
const kind = inferPageKind(relativePath, parsed.data.kind);
|
|
4112
4823
|
const sourceIds = normalizeStringArray(parsed.data.source_ids);
|
|
4113
4824
|
const projectIds = normalizeProjectIds(parsed.data.project_ids);
|
|
@@ -4146,18 +4857,18 @@ function parseStoredPage(relativePath, content, defaults = {}) {
|
|
|
4146
4857
|
};
|
|
4147
4858
|
}
|
|
4148
4859
|
async function loadInsightPages(wikiDir) {
|
|
4149
|
-
const insightsDir =
|
|
4860
|
+
const insightsDir = path11.join(wikiDir, "insights");
|
|
4150
4861
|
if (!await fileExists(insightsDir)) {
|
|
4151
4862
|
return [];
|
|
4152
4863
|
}
|
|
4153
|
-
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));
|
|
4154
4865
|
const insights = [];
|
|
4155
4866
|
for (const absolutePath of files) {
|
|
4156
|
-
const relativePath = toPosix(
|
|
4157
|
-
const content = await
|
|
4867
|
+
const relativePath = toPosix(path11.relative(wikiDir, absolutePath));
|
|
4868
|
+
const content = await fs8.readFile(absolutePath, "utf8");
|
|
4158
4869
|
const parsed = matter4(content);
|
|
4159
|
-
const stats = await
|
|
4160
|
-
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");
|
|
4161
4872
|
const sourceIds = normalizeStringArray(parsed.data.source_ids);
|
|
4162
4873
|
const projectIds = normalizeProjectIds(parsed.data.project_ids);
|
|
4163
4874
|
const nodeIds = normalizeStringArray(parsed.data.node_ids);
|
|
@@ -4219,27 +4930,27 @@ function relatedOutputsForPage(targetPage, outputPages) {
|
|
|
4219
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);
|
|
4220
4931
|
}
|
|
4221
4932
|
async function resolveUniqueOutputSlug(wikiDir, baseSlug) {
|
|
4222
|
-
const outputsDir =
|
|
4933
|
+
const outputsDir = path12.join(wikiDir, "outputs");
|
|
4223
4934
|
const root = baseSlug || "output";
|
|
4224
4935
|
let candidate = root;
|
|
4225
4936
|
let counter = 2;
|
|
4226
|
-
while (await fileExists(
|
|
4937
|
+
while (await fileExists(path12.join(outputsDir, `${candidate}.md`))) {
|
|
4227
4938
|
candidate = `${root}-${counter}`;
|
|
4228
4939
|
counter++;
|
|
4229
4940
|
}
|
|
4230
4941
|
return candidate;
|
|
4231
4942
|
}
|
|
4232
4943
|
async function loadSavedOutputPages(wikiDir) {
|
|
4233
|
-
const outputsDir =
|
|
4234
|
-
const entries = await
|
|
4944
|
+
const outputsDir = path12.join(wikiDir, "outputs");
|
|
4945
|
+
const entries = await fs9.readdir(outputsDir, { withFileTypes: true }).catch(() => []);
|
|
4235
4946
|
const outputs = [];
|
|
4236
4947
|
for (const entry of entries) {
|
|
4237
4948
|
if (!entry.isFile() || !entry.name.endsWith(".md") || entry.name === "index.md") {
|
|
4238
4949
|
continue;
|
|
4239
4950
|
}
|
|
4240
|
-
const relativePath =
|
|
4241
|
-
const absolutePath =
|
|
4242
|
-
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");
|
|
4243
4954
|
const parsed = matter5(content);
|
|
4244
4955
|
const slug = entry.name.replace(/\.md$/, "");
|
|
4245
4956
|
const title = typeof parsed.data.title === "string" ? parsed.data.title : slug;
|
|
@@ -4252,7 +4963,7 @@ async function loadSavedOutputPages(wikiDir) {
|
|
|
4252
4963
|
const relatedSourceIds = normalizeStringArray(parsed.data.related_source_ids);
|
|
4253
4964
|
const backlinks = normalizeStringArray(parsed.data.backlinks);
|
|
4254
4965
|
const compiledFrom = normalizeStringArray(parsed.data.compiled_from);
|
|
4255
|
-
const stats = await
|
|
4966
|
+
const stats = await fs9.stat(absolutePath);
|
|
4256
4967
|
const createdAt = typeof parsed.data.created_at === "string" ? parsed.data.created_at : stats.birthtimeMs > 0 ? stats.birthtime.toISOString() : stats.mtime.toISOString();
|
|
4257
4968
|
const updatedAt = typeof parsed.data.updated_at === "string" ? parsed.data.updated_at : stats.mtime.toISOString();
|
|
4258
4969
|
outputs.push({
|
|
@@ -4290,8 +5001,8 @@ async function loadSavedOutputPages(wikiDir) {
|
|
|
4290
5001
|
}
|
|
4291
5002
|
|
|
4292
5003
|
// src/search.ts
|
|
4293
|
-
import
|
|
4294
|
-
import
|
|
5004
|
+
import fs10 from "fs/promises";
|
|
5005
|
+
import path13 from "path";
|
|
4295
5006
|
import matter6 from "gray-matter";
|
|
4296
5007
|
function getDatabaseSync() {
|
|
4297
5008
|
const builtin = process.getBuiltinModule?.("node:sqlite");
|
|
@@ -4311,7 +5022,7 @@ function normalizeStatus(value) {
|
|
|
4311
5022
|
return value === "draft" || value === "candidate" || value === "active" || value === "archived" ? value : void 0;
|
|
4312
5023
|
}
|
|
4313
5024
|
async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
4314
|
-
await ensureDir(
|
|
5025
|
+
await ensureDir(path13.dirname(dbPath));
|
|
4315
5026
|
const DatabaseSync = getDatabaseSync();
|
|
4316
5027
|
const db = new DatabaseSync(dbPath);
|
|
4317
5028
|
db.exec("PRAGMA journal_mode = WAL;");
|
|
@@ -4341,8 +5052,8 @@ async function rebuildSearchIndex(dbPath, pages, wikiDir) {
|
|
|
4341
5052
|
"INSERT INTO pages (id, path, title, body, kind, status, project_ids, project_key) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
|
|
4342
5053
|
);
|
|
4343
5054
|
for (const page of pages) {
|
|
4344
|
-
const absolutePath =
|
|
4345
|
-
const content = await
|
|
5055
|
+
const absolutePath = path13.join(wikiDir, page.path);
|
|
5056
|
+
const content = await fs10.readFile(absolutePath, "utf8");
|
|
4346
5057
|
const parsed = matter6(content);
|
|
4347
5058
|
insertPage.run(
|
|
4348
5059
|
page.id,
|
|
@@ -4445,7 +5156,7 @@ function outputFormatInstruction(format) {
|
|
|
4445
5156
|
}
|
|
4446
5157
|
}
|
|
4447
5158
|
function outputAssetPath(slug, fileName) {
|
|
4448
|
-
return toPosix(
|
|
5159
|
+
return toPosix(path14.join("outputs", "assets", slug, fileName));
|
|
4449
5160
|
}
|
|
4450
5161
|
function outputAssetId(slug, role) {
|
|
4451
5162
|
return `output:${slug}:asset:${role}`;
|
|
@@ -4585,7 +5296,7 @@ async function resolveImageGenerationProvider(rootDir) {
|
|
|
4585
5296
|
if (!providerConfig) {
|
|
4586
5297
|
throw new Error(`No provider configured with id "${preferredProviderId}" for task "imageProvider".`);
|
|
4587
5298
|
}
|
|
4588
|
-
const { createProvider: createProvider2 } = await import("./registry-
|
|
5299
|
+
const { createProvider: createProvider2 } = await import("./registry-JFEW5RUP.js");
|
|
4589
5300
|
return createProvider2(preferredProviderId, providerConfig, rootDir);
|
|
4590
5301
|
}
|
|
4591
5302
|
async function generateOutputArtifacts(rootDir, input) {
|
|
@@ -4783,7 +5494,7 @@ async function generateOutputArtifacts(rootDir, input) {
|
|
|
4783
5494
|
};
|
|
4784
5495
|
}
|
|
4785
5496
|
function normalizeProjectRoot(root) {
|
|
4786
|
-
const normalized = toPosix(
|
|
5497
|
+
const normalized = toPosix(path14.posix.normalize(root.replace(/\\/g, "/"))).replace(/^\.\/+/, "").replace(/\/+$/, "");
|
|
4787
5498
|
return normalized;
|
|
4788
5499
|
}
|
|
4789
5500
|
function projectEntries(config) {
|
|
@@ -4809,10 +5520,10 @@ function manifestPathForProject(rootDir, manifest) {
|
|
|
4809
5520
|
if (!rawPath) {
|
|
4810
5521
|
return toPosix(manifest.storedPath);
|
|
4811
5522
|
}
|
|
4812
|
-
if (!
|
|
5523
|
+
if (!path14.isAbsolute(rawPath)) {
|
|
4813
5524
|
return normalizeProjectRoot(rawPath);
|
|
4814
5525
|
}
|
|
4815
|
-
const relative = toPosix(
|
|
5526
|
+
const relative = toPosix(path14.relative(rootDir, rawPath));
|
|
4816
5527
|
return relative.startsWith("..") ? toPosix(rawPath) : normalizeProjectRoot(relative);
|
|
4817
5528
|
}
|
|
4818
5529
|
function prefixMatches(value, prefix) {
|
|
@@ -4985,7 +5696,7 @@ function pageHashes(pages) {
|
|
|
4985
5696
|
return Object.fromEntries(pages.map((page) => [page.page.id, page.contentHash]));
|
|
4986
5697
|
}
|
|
4987
5698
|
async function buildManagedGraphPage(absolutePath, defaults, build) {
|
|
4988
|
-
const existingContent = await fileExists(absolutePath) ? await
|
|
5699
|
+
const existingContent = await fileExists(absolutePath) ? await fs11.readFile(absolutePath, "utf8") : null;
|
|
4989
5700
|
let existing = await loadExistingManagedPageState(absolutePath, {
|
|
4990
5701
|
status: defaults.status ?? "active",
|
|
4991
5702
|
managedBy: defaults.managedBy
|
|
@@ -5023,7 +5734,7 @@ async function buildManagedGraphPage(absolutePath, defaults, build) {
|
|
|
5023
5734
|
return built;
|
|
5024
5735
|
}
|
|
5025
5736
|
async function buildManagedContent(absolutePath, defaults, build) {
|
|
5026
|
-
const existingContent = await fileExists(absolutePath) ? await
|
|
5737
|
+
const existingContent = await fileExists(absolutePath) ? await fs11.readFile(absolutePath, "utf8") : null;
|
|
5027
5738
|
let existing = await loadExistingManagedPageState(absolutePath, {
|
|
5028
5739
|
status: defaults.status ?? "active",
|
|
5029
5740
|
managedBy: defaults.managedBy
|
|
@@ -5142,7 +5853,7 @@ function deriveGraphMetrics(nodes, edges) {
|
|
|
5142
5853
|
communities
|
|
5143
5854
|
};
|
|
5144
5855
|
}
|
|
5145
|
-
function buildGraph(manifests, analyses, pages, sourceProjects) {
|
|
5856
|
+
function buildGraph(manifests, analyses, pages, sourceProjects, _codeIndex) {
|
|
5146
5857
|
const sourceNodes = manifests.map((manifest) => ({
|
|
5147
5858
|
id: `source:${manifest.sourceId}`,
|
|
5148
5859
|
type: "source",
|
|
@@ -5281,11 +5992,16 @@ function buildGraph(manifests, analyses, pages, sourceProjects) {
|
|
|
5281
5992
|
const symbolIdsByName = new Map(analysis.code.symbols.map((symbol) => [symbol.name, symbol.id]));
|
|
5282
5993
|
const importedSymbolIdsByName = /* @__PURE__ */ new Map();
|
|
5283
5994
|
for (const codeImport of analysis.code.imports.filter((item) => !item.isExternal)) {
|
|
5284
|
-
const targetSourceId =
|
|
5995
|
+
const targetSourceId = codeImport.resolvedSourceId;
|
|
5285
5996
|
const targetAnalysis = targetSourceId ? analysesBySourceId.get(targetSourceId) : void 0;
|
|
5286
5997
|
if (!targetSourceId || !targetAnalysis?.code) {
|
|
5287
5998
|
continue;
|
|
5288
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
|
+
}
|
|
5289
6005
|
for (const importedSymbol of codeImport.importedSymbols) {
|
|
5290
6006
|
const [rawExportedName, rawLocalName] = importedSymbol.split(/\s+as\s+/i);
|
|
5291
6007
|
const exportedName = (rawExportedName ?? "").trim();
|
|
@@ -5347,7 +6063,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects) {
|
|
|
5347
6063
|
}
|
|
5348
6064
|
}
|
|
5349
6065
|
for (const codeImport of analysis.code.imports) {
|
|
5350
|
-
const targetSourceId =
|
|
6066
|
+
const targetSourceId = codeImport.resolvedSourceId;
|
|
5351
6067
|
if (!targetSourceId) {
|
|
5352
6068
|
continue;
|
|
5353
6069
|
}
|
|
@@ -5415,7 +6131,7 @@ function buildGraph(manifests, analyses, pages, sourceProjects) {
|
|
|
5415
6131
|
};
|
|
5416
6132
|
}
|
|
5417
6133
|
async function writePage(wikiDir, relativePath, content, changedPages) {
|
|
5418
|
-
const absolutePath =
|
|
6134
|
+
const absolutePath = path14.resolve(wikiDir, relativePath);
|
|
5419
6135
|
const changed = await writeFileIfChanged(absolutePath, content);
|
|
5420
6136
|
if (changed) {
|
|
5421
6137
|
changedPages.push(relativePath);
|
|
@@ -5475,15 +6191,16 @@ function recordsEqual(left, right) {
|
|
|
5475
6191
|
async function requiredCompileArtifactsExist(paths) {
|
|
5476
6192
|
const requiredPaths = [
|
|
5477
6193
|
paths.graphPath,
|
|
6194
|
+
paths.codeIndexPath,
|
|
5478
6195
|
paths.searchDbPath,
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
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")
|
|
5487
6204
|
];
|
|
5488
6205
|
const checks = await Promise.all(requiredPaths.map((filePath) => fileExists(filePath)));
|
|
5489
6206
|
return checks.every(Boolean);
|
|
@@ -5491,7 +6208,7 @@ async function requiredCompileArtifactsExist(paths) {
|
|
|
5491
6208
|
async function loadCachedAnalyses(paths, manifests) {
|
|
5492
6209
|
return Promise.all(
|
|
5493
6210
|
manifests.map(async (manifest) => {
|
|
5494
|
-
const cached = await readJsonFile(
|
|
6211
|
+
const cached = await readJsonFile(path14.join(paths.analysesDir, `${manifest.sourceId}.json`));
|
|
5495
6212
|
if (!cached) {
|
|
5496
6213
|
throw new Error(`Missing cached analysis for ${manifest.sourceId}. Run \`swarmvault compile\` first.`);
|
|
5497
6214
|
}
|
|
@@ -5500,10 +6217,10 @@ async function loadCachedAnalyses(paths, manifests) {
|
|
|
5500
6217
|
);
|
|
5501
6218
|
}
|
|
5502
6219
|
function approvalManifestPath(paths, approvalId) {
|
|
5503
|
-
return
|
|
6220
|
+
return path14.join(paths.approvalsDir, approvalId, "manifest.json");
|
|
5504
6221
|
}
|
|
5505
6222
|
function approvalGraphPath(paths, approvalId) {
|
|
5506
|
-
return
|
|
6223
|
+
return path14.join(paths.approvalsDir, approvalId, "state", "graph.json");
|
|
5507
6224
|
}
|
|
5508
6225
|
async function readApprovalManifest(paths, approvalId) {
|
|
5509
6226
|
const manifest = await readJsonFile(approvalManifestPath(paths, approvalId));
|
|
@@ -5513,7 +6230,7 @@ async function readApprovalManifest(paths, approvalId) {
|
|
|
5513
6230
|
return manifest;
|
|
5514
6231
|
}
|
|
5515
6232
|
async function writeApprovalManifest(paths, manifest) {
|
|
5516
|
-
await
|
|
6233
|
+
await fs11.writeFile(approvalManifestPath(paths, manifest.approvalId), `${JSON.stringify(manifest, null, 2)}
|
|
5517
6234
|
`, "utf8");
|
|
5518
6235
|
}
|
|
5519
6236
|
async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousGraph, graph) {
|
|
@@ -5528,7 +6245,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
5528
6245
|
continue;
|
|
5529
6246
|
}
|
|
5530
6247
|
const previousPage = previousPagesById.get(nextPage.id);
|
|
5531
|
-
const currentExists = await fileExists(
|
|
6248
|
+
const currentExists = await fileExists(path14.join(paths.wikiDir, file.relativePath));
|
|
5532
6249
|
if (previousPage && previousPage.path !== nextPage.path) {
|
|
5533
6250
|
entries.push({
|
|
5534
6251
|
pageId: nextPage.id,
|
|
@@ -5561,7 +6278,7 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
5561
6278
|
const previousPage = previousPagesByPath.get(deletedPath);
|
|
5562
6279
|
entries.push({
|
|
5563
6280
|
pageId: previousPage?.id ?? `page:${slugify(deletedPath)}`,
|
|
5564
|
-
title: previousPage?.title ??
|
|
6281
|
+
title: previousPage?.title ?? path14.basename(deletedPath, ".md"),
|
|
5565
6282
|
kind: previousPage?.kind ?? "index",
|
|
5566
6283
|
changeType: "delete",
|
|
5567
6284
|
status: "pending",
|
|
@@ -5573,16 +6290,16 @@ async function buildApprovalEntries(paths, changedFiles, deletedPaths, previousG
|
|
|
5573
6290
|
}
|
|
5574
6291
|
async function stageApprovalBundle(paths, changedFiles, deletedPaths, previousGraph, graph) {
|
|
5575
6292
|
const approvalId = `compile-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
5576
|
-
const approvalDir =
|
|
6293
|
+
const approvalDir = path14.join(paths.approvalsDir, approvalId);
|
|
5577
6294
|
await ensureDir(approvalDir);
|
|
5578
|
-
await ensureDir(
|
|
5579
|
-
await ensureDir(
|
|
6295
|
+
await ensureDir(path14.join(approvalDir, "wiki"));
|
|
6296
|
+
await ensureDir(path14.join(approvalDir, "state"));
|
|
5580
6297
|
for (const file of changedFiles) {
|
|
5581
|
-
const targetPath =
|
|
5582
|
-
await ensureDir(
|
|
5583
|
-
await
|
|
6298
|
+
const targetPath = path14.join(approvalDir, "wiki", file.relativePath);
|
|
6299
|
+
await ensureDir(path14.dirname(targetPath));
|
|
6300
|
+
await fs11.writeFile(targetPath, file.content, "utf8");
|
|
5584
6301
|
}
|
|
5585
|
-
await
|
|
6302
|
+
await fs11.writeFile(path14.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
|
|
5586
6303
|
await writeApprovalManifest(paths, {
|
|
5587
6304
|
approvalId,
|
|
5588
6305
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5642,7 +6359,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5642
6359
|
confidence: 1
|
|
5643
6360
|
});
|
|
5644
6361
|
const sourceRecord = await buildManagedGraphPage(
|
|
5645
|
-
|
|
6362
|
+
path14.join(paths.wikiDir, preview.path),
|
|
5646
6363
|
{
|
|
5647
6364
|
managedBy: "system",
|
|
5648
6365
|
confidence: 1,
|
|
@@ -5664,7 +6381,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5664
6381
|
records.push(sourceRecord);
|
|
5665
6382
|
if (modulePreview && analysis.code) {
|
|
5666
6383
|
const localModules = analysis.code.imports.map((codeImport) => {
|
|
5667
|
-
const resolvedSourceId =
|
|
6384
|
+
const resolvedSourceId = codeImport.resolvedSourceId;
|
|
5668
6385
|
if (!resolvedSourceId) {
|
|
5669
6386
|
return null;
|
|
5670
6387
|
}
|
|
@@ -5687,7 +6404,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5687
6404
|
);
|
|
5688
6405
|
records.push(
|
|
5689
6406
|
await buildManagedGraphPage(
|
|
5690
|
-
|
|
6407
|
+
path14.join(paths.wikiDir, modulePreview.path),
|
|
5691
6408
|
{
|
|
5692
6409
|
managedBy: "system",
|
|
5693
6410
|
confidence: 1,
|
|
@@ -5720,8 +6437,8 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5720
6437
|
const promoted = previousEntry?.status === "active" || promoteCandidates && shouldPromoteCandidate(previousEntry, sourceIds);
|
|
5721
6438
|
const relativePath = promoted ? activeAggregatePath(itemKind, slug) : candidatePagePathFor(itemKind, slug);
|
|
5722
6439
|
const fallbackPaths = [
|
|
5723
|
-
|
|
5724
|
-
|
|
6440
|
+
path14.join(paths.wikiDir, activeAggregatePath(itemKind, slug)),
|
|
6441
|
+
path14.join(paths.wikiDir, candidatePagePathFor(itemKind, slug))
|
|
5725
6442
|
];
|
|
5726
6443
|
const confidence = nodeConfidence(aggregate.sourceAnalyses.length);
|
|
5727
6444
|
const preview = emptyGraphPage({
|
|
@@ -5738,7 +6455,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5738
6455
|
status: promoted ? "active" : "candidate"
|
|
5739
6456
|
});
|
|
5740
6457
|
const pageRecord = await buildManagedGraphPage(
|
|
5741
|
-
|
|
6458
|
+
path14.join(paths.wikiDir, relativePath),
|
|
5742
6459
|
{
|
|
5743
6460
|
status: promoted ? "active" : "candidate",
|
|
5744
6461
|
managedBy: "system",
|
|
@@ -5778,7 +6495,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5778
6495
|
}
|
|
5779
6496
|
const compiledPages = records.map((record) => record.page);
|
|
5780
6497
|
const allPages = [...compiledPages, ...input.outputPages, ...input.insightPages];
|
|
5781
|
-
const graph = buildGraph(input.manifests, input.analyses, allPages, input.sourceProjects);
|
|
6498
|
+
const graph = buildGraph(input.manifests, input.analyses, allPages, input.sourceProjects, input.codeIndex);
|
|
5782
6499
|
const activeConceptPages = allPages.filter((page) => page.kind === "concept" && page.status !== "candidate");
|
|
5783
6500
|
const activeEntityPages = allPages.filter((page) => page.kind === "entity" && page.status !== "candidate");
|
|
5784
6501
|
const modulePages = allPages.filter((page) => page.kind === "module");
|
|
@@ -5812,7 +6529,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5812
6529
|
confidence: 1
|
|
5813
6530
|
}),
|
|
5814
6531
|
content: await buildManagedContent(
|
|
5815
|
-
|
|
6532
|
+
path14.join(paths.wikiDir, "projects", "index.md"),
|
|
5816
6533
|
{
|
|
5817
6534
|
managedBy: "system",
|
|
5818
6535
|
compiledFrom: indexCompiledFrom(projectIndexRefs)
|
|
@@ -5836,7 +6553,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5836
6553
|
records.push({
|
|
5837
6554
|
page: projectIndexRef,
|
|
5838
6555
|
content: await buildManagedContent(
|
|
5839
|
-
|
|
6556
|
+
path14.join(paths.wikiDir, projectIndexRef.path),
|
|
5840
6557
|
{
|
|
5841
6558
|
managedBy: "system",
|
|
5842
6559
|
compiledFrom: indexCompiledFrom(Object.values(sections).flat())
|
|
@@ -5864,7 +6581,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5864
6581
|
confidence: 1
|
|
5865
6582
|
}),
|
|
5866
6583
|
content: await buildManagedContent(
|
|
5867
|
-
|
|
6584
|
+
path14.join(paths.wikiDir, "index.md"),
|
|
5868
6585
|
{
|
|
5869
6586
|
managedBy: "system",
|
|
5870
6587
|
compiledFrom: indexCompiledFrom(allPages)
|
|
@@ -5894,7 +6611,7 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5894
6611
|
confidence: 1
|
|
5895
6612
|
}),
|
|
5896
6613
|
content: await buildManagedContent(
|
|
5897
|
-
|
|
6614
|
+
path14.join(paths.wikiDir, relativePath),
|
|
5898
6615
|
{
|
|
5899
6616
|
managedBy: "system",
|
|
5900
6617
|
compiledFrom: indexCompiledFrom(pages)
|
|
@@ -5905,12 +6622,12 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5905
6622
|
}
|
|
5906
6623
|
const nextPagePaths = new Set(records.map((record) => record.page.path));
|
|
5907
6624
|
const obsoleteGraphPaths = (previousGraph?.pages ?? []).filter((page) => page.kind !== "output" && page.kind !== "insight").map((page) => page.path).filter((relativePath) => !nextPagePaths.has(relativePath));
|
|
5908
|
-
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));
|
|
5909
6626
|
const obsoletePaths = uniqueStrings2([...obsoleteGraphPaths, ...existingProjectIndexPaths]);
|
|
5910
6627
|
const changedFiles = [];
|
|
5911
6628
|
for (const record of records) {
|
|
5912
|
-
const absolutePath =
|
|
5913
|
-
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;
|
|
5914
6631
|
if (current !== record.content) {
|
|
5915
6632
|
changedPages.push(record.page.path);
|
|
5916
6633
|
changedFiles.push({ relativePath: record.page.path, content: record.content });
|
|
@@ -5935,9 +6652,10 @@ async function syncVaultArtifacts(rootDir, input) {
|
|
|
5935
6652
|
await writePage(paths.wikiDir, record.page.path, record.content, writeChanges);
|
|
5936
6653
|
}
|
|
5937
6654
|
for (const relativePath of obsoletePaths) {
|
|
5938
|
-
await
|
|
6655
|
+
await fs11.rm(path14.join(paths.wikiDir, relativePath), { force: true });
|
|
5939
6656
|
}
|
|
5940
6657
|
await writeJsonFile(paths.graphPath, graph);
|
|
6658
|
+
await writeJsonFile(paths.codeIndexPath, input.codeIndex);
|
|
5941
6659
|
await writeJsonFile(paths.compileStatePath, {
|
|
5942
6660
|
generatedAt: graph.generatedAt,
|
|
5943
6661
|
rootSchemaHash: input.schemas.root.hash,
|
|
@@ -5988,15 +6706,15 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
5988
6706
|
})
|
|
5989
6707
|
);
|
|
5990
6708
|
await Promise.all([
|
|
5991
|
-
ensureDir(
|
|
5992
|
-
ensureDir(
|
|
5993
|
-
ensureDir(
|
|
5994
|
-
ensureDir(
|
|
5995
|
-
ensureDir(
|
|
5996
|
-
ensureDir(
|
|
5997
|
-
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"))
|
|
5998
6716
|
]);
|
|
5999
|
-
const projectsIndexPath =
|
|
6717
|
+
const projectsIndexPath = path14.join(paths.wikiDir, "projects", "index.md");
|
|
6000
6718
|
await writeFileIfChanged(
|
|
6001
6719
|
projectsIndexPath,
|
|
6002
6720
|
await buildManagedContent(
|
|
@@ -6017,7 +6735,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
6017
6735
|
outputs: pages.filter((page) => page.kind === "output" && page.projectIds.includes(project.id)),
|
|
6018
6736
|
candidates: pages.filter((page) => page.status === "candidate" && page.projectIds.includes(project.id))
|
|
6019
6737
|
};
|
|
6020
|
-
const absolutePath =
|
|
6738
|
+
const absolutePath = path14.join(paths.wikiDir, "projects", project.id, "index.md");
|
|
6021
6739
|
await writeFileIfChanged(
|
|
6022
6740
|
absolutePath,
|
|
6023
6741
|
await buildManagedContent(
|
|
@@ -6035,7 +6753,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
6035
6753
|
)
|
|
6036
6754
|
);
|
|
6037
6755
|
}
|
|
6038
|
-
const rootIndexPath =
|
|
6756
|
+
const rootIndexPath = path14.join(paths.wikiDir, "index.md");
|
|
6039
6757
|
await writeFileIfChanged(
|
|
6040
6758
|
rootIndexPath,
|
|
6041
6759
|
await buildManagedContent(
|
|
@@ -6055,7 +6773,7 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
6055
6773
|
["outputs/index.md", "outputs", pages.filter((page) => page.kind === "output")],
|
|
6056
6774
|
["candidates/index.md", "candidates", pages.filter((page) => page.status === "candidate")]
|
|
6057
6775
|
]) {
|
|
6058
|
-
const absolutePath =
|
|
6776
|
+
const absolutePath = path14.join(paths.wikiDir, relativePath);
|
|
6059
6777
|
await writeFileIfChanged(
|
|
6060
6778
|
absolutePath,
|
|
6061
6779
|
await buildManagedContent(
|
|
@@ -6068,13 +6786,13 @@ async function refreshIndexesAndSearch(rootDir, pages) {
|
|
|
6068
6786
|
)
|
|
6069
6787
|
);
|
|
6070
6788
|
}
|
|
6071
|
-
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)));
|
|
6072
6790
|
const allowedProjectIndexPaths = /* @__PURE__ */ new Set([
|
|
6073
6791
|
"projects/index.md",
|
|
6074
6792
|
...configuredProjects.map((project) => `projects/${project.id}/index.md`)
|
|
6075
6793
|
]);
|
|
6076
6794
|
await Promise.all(
|
|
6077
|
-
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 }))
|
|
6078
6796
|
);
|
|
6079
6797
|
await rebuildSearchIndex(paths.searchDbPath, pages, paths.wikiDir);
|
|
6080
6798
|
}
|
|
@@ -6094,7 +6812,7 @@ async function prepareOutputPageSave(rootDir, input) {
|
|
|
6094
6812
|
confidence: 0.74
|
|
6095
6813
|
}
|
|
6096
6814
|
});
|
|
6097
|
-
const absolutePath =
|
|
6815
|
+
const absolutePath = path14.join(paths.wikiDir, output.page.path);
|
|
6098
6816
|
return {
|
|
6099
6817
|
page: output.page,
|
|
6100
6818
|
savedPath: absolutePath,
|
|
@@ -6106,15 +6824,15 @@ async function prepareOutputPageSave(rootDir, input) {
|
|
|
6106
6824
|
async function persistOutputPage(rootDir, input) {
|
|
6107
6825
|
const { paths } = await loadVaultConfig(rootDir);
|
|
6108
6826
|
const prepared = await prepareOutputPageSave(rootDir, input);
|
|
6109
|
-
await ensureDir(
|
|
6110
|
-
await
|
|
6827
|
+
await ensureDir(path14.dirname(prepared.savedPath));
|
|
6828
|
+
await fs11.writeFile(prepared.savedPath, prepared.content, "utf8");
|
|
6111
6829
|
for (const assetFile of prepared.assetFiles) {
|
|
6112
|
-
const assetPath =
|
|
6113
|
-
await ensureDir(
|
|
6830
|
+
const assetPath = path14.join(paths.wikiDir, assetFile.relativePath);
|
|
6831
|
+
await ensureDir(path14.dirname(assetPath));
|
|
6114
6832
|
if (typeof assetFile.content === "string") {
|
|
6115
|
-
await
|
|
6833
|
+
await fs11.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
|
|
6116
6834
|
} else {
|
|
6117
|
-
await
|
|
6835
|
+
await fs11.writeFile(assetPath, assetFile.content);
|
|
6118
6836
|
}
|
|
6119
6837
|
}
|
|
6120
6838
|
return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
|
|
@@ -6135,7 +6853,7 @@ async function prepareExploreHubSave(rootDir, input) {
|
|
|
6135
6853
|
confidence: 0.76
|
|
6136
6854
|
}
|
|
6137
6855
|
});
|
|
6138
|
-
const absolutePath =
|
|
6856
|
+
const absolutePath = path14.join(paths.wikiDir, hub.page.path);
|
|
6139
6857
|
return {
|
|
6140
6858
|
page: hub.page,
|
|
6141
6859
|
savedPath: absolutePath,
|
|
@@ -6147,15 +6865,15 @@ async function prepareExploreHubSave(rootDir, input) {
|
|
|
6147
6865
|
async function persistExploreHub(rootDir, input) {
|
|
6148
6866
|
const { paths } = await loadVaultConfig(rootDir);
|
|
6149
6867
|
const prepared = await prepareExploreHubSave(rootDir, input);
|
|
6150
|
-
await ensureDir(
|
|
6151
|
-
await
|
|
6868
|
+
await ensureDir(path14.dirname(prepared.savedPath));
|
|
6869
|
+
await fs11.writeFile(prepared.savedPath, prepared.content, "utf8");
|
|
6152
6870
|
for (const assetFile of prepared.assetFiles) {
|
|
6153
|
-
const assetPath =
|
|
6154
|
-
await ensureDir(
|
|
6871
|
+
const assetPath = path14.join(paths.wikiDir, assetFile.relativePath);
|
|
6872
|
+
await ensureDir(path14.dirname(assetPath));
|
|
6155
6873
|
if (typeof assetFile.content === "string") {
|
|
6156
|
-
await
|
|
6874
|
+
await fs11.writeFile(assetPath, assetFile.content, assetFile.encoding ?? "utf8");
|
|
6157
6875
|
} else {
|
|
6158
|
-
await
|
|
6876
|
+
await fs11.writeFile(assetPath, assetFile.content);
|
|
6159
6877
|
}
|
|
6160
6878
|
}
|
|
6161
6879
|
return { page: prepared.page, savedPath: prepared.savedPath, outputAssets: prepared.outputAssets };
|
|
@@ -6172,17 +6890,17 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
|
|
|
6172
6890
|
}))
|
|
6173
6891
|
]);
|
|
6174
6892
|
const approvalId = `schedule-${(/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-")}`;
|
|
6175
|
-
const approvalDir =
|
|
6893
|
+
const approvalDir = path14.join(paths.approvalsDir, approvalId);
|
|
6176
6894
|
await ensureDir(approvalDir);
|
|
6177
|
-
await ensureDir(
|
|
6178
|
-
await ensureDir(
|
|
6895
|
+
await ensureDir(path14.join(approvalDir, "wiki"));
|
|
6896
|
+
await ensureDir(path14.join(approvalDir, "state"));
|
|
6179
6897
|
for (const file of changedFiles) {
|
|
6180
|
-
const targetPath =
|
|
6181
|
-
await ensureDir(
|
|
6898
|
+
const targetPath = path14.join(approvalDir, "wiki", file.relativePath);
|
|
6899
|
+
await ensureDir(path14.dirname(targetPath));
|
|
6182
6900
|
if ("binary" in file && file.binary) {
|
|
6183
|
-
await
|
|
6901
|
+
await fs11.writeFile(targetPath, Buffer.from(file.content, "base64"));
|
|
6184
6902
|
} else {
|
|
6185
|
-
await
|
|
6903
|
+
await fs11.writeFile(targetPath, file.content, "utf8");
|
|
6186
6904
|
}
|
|
6187
6905
|
}
|
|
6188
6906
|
const nextPages = sortGraphPages([
|
|
@@ -6196,7 +6914,7 @@ async function stageOutputApprovalBundle(rootDir, stagedPages) {
|
|
|
6196
6914
|
sources: previousGraph?.sources ?? [],
|
|
6197
6915
|
pages: nextPages
|
|
6198
6916
|
};
|
|
6199
|
-
await
|
|
6917
|
+
await fs11.writeFile(path14.join(approvalDir, "state", "graph.json"), JSON.stringify(graph, null, 2), "utf8");
|
|
6200
6918
|
await writeApprovalManifest(paths, {
|
|
6201
6919
|
approvalId,
|
|
6202
6920
|
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -6225,9 +6943,9 @@ async function executeQuery(rootDir, question, format) {
|
|
|
6225
6943
|
const searchResults = searchPages(paths.searchDbPath, question, 5);
|
|
6226
6944
|
const excerpts = await Promise.all(
|
|
6227
6945
|
searchResults.map(async (result) => {
|
|
6228
|
-
const absolutePath =
|
|
6946
|
+
const absolutePath = path14.join(paths.wikiDir, result.path);
|
|
6229
6947
|
try {
|
|
6230
|
-
const content = await
|
|
6948
|
+
const content = await fs11.readFile(absolutePath, "utf8");
|
|
6231
6949
|
const parsed = matter7(content);
|
|
6232
6950
|
return `# ${result.title}
|
|
6233
6951
|
${truncate(normalizeWhitespace(parsed.content), 1200)}`;
|
|
@@ -6336,13 +7054,19 @@ async function refreshVaultAfterOutputSave(rootDir) {
|
|
|
6336
7054
|
const schemas = await loadVaultSchemas(rootDir);
|
|
6337
7055
|
const manifests = await listManifests(rootDir);
|
|
6338
7056
|
const sourceProjects = resolveSourceProjects(rootDir, manifests, config);
|
|
6339
|
-
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
|
+
});
|
|
6340
7063
|
const storedOutputs = await loadSavedOutputPages(paths.wikiDir);
|
|
6341
7064
|
const storedInsights = await loadInsightPages(paths.wikiDir);
|
|
6342
7065
|
await syncVaultArtifacts(rootDir, {
|
|
6343
7066
|
schemas,
|
|
6344
7067
|
manifests,
|
|
6345
7068
|
analyses,
|
|
7069
|
+
codeIndex,
|
|
6346
7070
|
sourceProjects,
|
|
6347
7071
|
outputPages: storedOutputs.map((page) => page.page),
|
|
6348
7072
|
insightPages: storedInsights.map((page) => page.page),
|
|
@@ -6403,7 +7127,7 @@ function sortGraphPages(pages) {
|
|
|
6403
7127
|
async function listApprovals(rootDir) {
|
|
6404
7128
|
const { paths } = await loadVaultConfig(rootDir);
|
|
6405
7129
|
const manifests = await Promise.all(
|
|
6406
|
-
(await
|
|
7130
|
+
(await fs11.readdir(paths.approvalsDir, { withFileTypes: true }).catch(() => [])).filter((entry) => entry.isDirectory()).map(async (entry) => {
|
|
6407
7131
|
try {
|
|
6408
7132
|
return await readApprovalManifest(paths, entry.name);
|
|
6409
7133
|
} catch {
|
|
@@ -6419,8 +7143,8 @@ async function readApproval(rootDir, approvalId) {
|
|
|
6419
7143
|
const details = await Promise.all(
|
|
6420
7144
|
manifest.entries.map(async (entry) => {
|
|
6421
7145
|
const currentPath = entry.previousPath ?? entry.nextPath;
|
|
6422
|
-
const currentContent = currentPath ? await
|
|
6423
|
-
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;
|
|
6424
7148
|
return {
|
|
6425
7149
|
...entry,
|
|
6426
7150
|
currentContent,
|
|
@@ -6448,26 +7172,26 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
|
|
|
6448
7172
|
if (!entry.nextPath) {
|
|
6449
7173
|
throw new Error(`Approval entry ${entry.pageId} is missing a staged path.`);
|
|
6450
7174
|
}
|
|
6451
|
-
const stagedAbsolutePath =
|
|
6452
|
-
const stagedContent = await
|
|
6453
|
-
const targetAbsolutePath =
|
|
6454
|
-
await ensureDir(
|
|
6455
|
-
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");
|
|
6456
7180
|
if (entry.changeType === "promote" && entry.previousPath) {
|
|
6457
|
-
await
|
|
7181
|
+
await fs11.rm(path14.join(paths.wikiDir, entry.previousPath), { force: true });
|
|
6458
7182
|
}
|
|
6459
7183
|
const nextPage = bundleGraph?.pages.find((page) => page.id === entry.pageId && page.path === entry.nextPath) ?? parseStoredPage(entry.nextPath, stagedContent);
|
|
6460
7184
|
if (nextPage.kind === "output" && nextPage.outputAssets?.length) {
|
|
6461
|
-
const outputAssetDir =
|
|
6462
|
-
await
|
|
7185
|
+
const outputAssetDir = path14.join(paths.wikiDir, "outputs", "assets", path14.basename(nextPage.path, ".md"));
|
|
7186
|
+
await fs11.rm(outputAssetDir, { recursive: true, force: true });
|
|
6463
7187
|
for (const asset of nextPage.outputAssets) {
|
|
6464
|
-
const stagedAssetPath =
|
|
7188
|
+
const stagedAssetPath = path14.join(paths.approvalsDir, approvalId, "wiki", asset.path);
|
|
6465
7189
|
if (!await fileExists(stagedAssetPath)) {
|
|
6466
7190
|
continue;
|
|
6467
7191
|
}
|
|
6468
|
-
const targetAssetPath =
|
|
6469
|
-
await ensureDir(
|
|
6470
|
-
await
|
|
7192
|
+
const targetAssetPath = path14.join(paths.wikiDir, asset.path);
|
|
7193
|
+
await ensureDir(path14.dirname(targetAssetPath));
|
|
7194
|
+
await fs11.copyFile(stagedAssetPath, targetAssetPath);
|
|
6471
7195
|
}
|
|
6472
7196
|
}
|
|
6473
7197
|
nextPages = nextPages.filter(
|
|
@@ -6478,10 +7202,10 @@ async function acceptApproval(rootDir, approvalId, targets = []) {
|
|
|
6478
7202
|
} else {
|
|
6479
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;
|
|
6480
7204
|
if (entry.previousPath) {
|
|
6481
|
-
await
|
|
7205
|
+
await fs11.rm(path14.join(paths.wikiDir, entry.previousPath), { force: true });
|
|
6482
7206
|
}
|
|
6483
7207
|
if (deletedPage?.kind === "output") {
|
|
6484
|
-
await
|
|
7208
|
+
await fs11.rm(path14.join(paths.wikiDir, "outputs", "assets", path14.basename(deletedPage.path, ".md")), {
|
|
6485
7209
|
recursive: true,
|
|
6486
7210
|
force: true
|
|
6487
7211
|
});
|
|
@@ -6571,7 +7295,7 @@ async function promoteCandidate(rootDir, target) {
|
|
|
6571
7295
|
const { paths } = await loadVaultConfig(rootDir);
|
|
6572
7296
|
const graph = await readJsonFile(paths.graphPath);
|
|
6573
7297
|
const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
|
|
6574
|
-
const raw = await
|
|
7298
|
+
const raw = await fs11.readFile(path14.join(paths.wikiDir, candidate.path), "utf8");
|
|
6575
7299
|
const parsed = matter7(raw);
|
|
6576
7300
|
const nextUpdatedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
6577
7301
|
const nextContent = matter7.stringify(parsed.content, {
|
|
@@ -6583,10 +7307,10 @@ async function promoteCandidate(rootDir, target) {
|
|
|
6583
7307
|
)
|
|
6584
7308
|
});
|
|
6585
7309
|
const nextPath = candidateActivePath(candidate);
|
|
6586
|
-
const nextAbsolutePath =
|
|
6587
|
-
await ensureDir(
|
|
6588
|
-
await
|
|
6589
|
-
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 });
|
|
6590
7314
|
const nextPage = parseStoredPage(nextPath, nextContent, { createdAt: candidate.createdAt, updatedAt: nextUpdatedAt });
|
|
6591
7315
|
const nextPages = sortGraphPages(
|
|
6592
7316
|
(graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path).concat(nextPage)
|
|
@@ -6630,7 +7354,7 @@ async function archiveCandidate(rootDir, target) {
|
|
|
6630
7354
|
const { paths } = await loadVaultConfig(rootDir);
|
|
6631
7355
|
const graph = await readJsonFile(paths.graphPath);
|
|
6632
7356
|
const candidate = resolveCandidateTarget(graph?.pages ?? [], target);
|
|
6633
|
-
await
|
|
7357
|
+
await fs11.rm(path14.join(paths.wikiDir, candidate.path), { force: true });
|
|
6634
7358
|
const nextPages = sortGraphPages((graph?.pages ?? []).filter((page) => page.id !== candidate.id && page.path !== candidate.path));
|
|
6635
7359
|
const nextGraph = {
|
|
6636
7360
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -6668,18 +7392,18 @@ async function archiveCandidate(rootDir, target) {
|
|
|
6668
7392
|
}
|
|
6669
7393
|
async function ensureObsidianWorkspace(rootDir) {
|
|
6670
7394
|
const { config } = await loadVaultConfig(rootDir);
|
|
6671
|
-
const obsidianDir =
|
|
7395
|
+
const obsidianDir = path14.join(rootDir, ".obsidian");
|
|
6672
7396
|
const projectIds = projectEntries(config).map((project) => project.id);
|
|
6673
7397
|
await ensureDir(obsidianDir);
|
|
6674
7398
|
await Promise.all([
|
|
6675
|
-
writeJsonFile(
|
|
7399
|
+
writeJsonFile(path14.join(obsidianDir, "app.json"), {
|
|
6676
7400
|
alwaysUpdateLinks: true,
|
|
6677
7401
|
newFileLocation: "folder",
|
|
6678
7402
|
newFileFolderPath: "wiki/insights",
|
|
6679
7403
|
useMarkdownLinks: false,
|
|
6680
7404
|
attachmentFolderPath: "raw/assets"
|
|
6681
7405
|
}),
|
|
6682
|
-
writeJsonFile(
|
|
7406
|
+
writeJsonFile(path14.join(obsidianDir, "core-plugins.json"), [
|
|
6683
7407
|
"file-explorer",
|
|
6684
7408
|
"global-search",
|
|
6685
7409
|
"switcher",
|
|
@@ -6689,7 +7413,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
6689
7413
|
"tag-pane",
|
|
6690
7414
|
"page-preview"
|
|
6691
7415
|
]),
|
|
6692
|
-
writeJsonFile(
|
|
7416
|
+
writeJsonFile(path14.join(obsidianDir, "graph.json"), {
|
|
6693
7417
|
"collapse-filter": false,
|
|
6694
7418
|
search: "",
|
|
6695
7419
|
showTags: true,
|
|
@@ -6701,7 +7425,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
6701
7425
|
})),
|
|
6702
7426
|
localJumps: false
|
|
6703
7427
|
}),
|
|
6704
|
-
writeJsonFile(
|
|
7428
|
+
writeJsonFile(path14.join(obsidianDir, "workspace.json"), {
|
|
6705
7429
|
active: "root",
|
|
6706
7430
|
lastOpenFiles: ["wiki/index.md", "wiki/projects/index.md", "wiki/candidates/index.md", "wiki/insights/index.md"],
|
|
6707
7431
|
left: {
|
|
@@ -6716,7 +7440,7 @@ async function ensureObsidianWorkspace(rootDir) {
|
|
|
6716
7440
|
async function initVault(rootDir, options = {}) {
|
|
6717
7441
|
const { paths } = await initWorkspace(rootDir);
|
|
6718
7442
|
await installConfiguredAgents(rootDir);
|
|
6719
|
-
const insightsIndexPath =
|
|
7443
|
+
const insightsIndexPath = path14.join(paths.wikiDir, "insights", "index.md");
|
|
6720
7444
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
6721
7445
|
await writeFileIfChanged(
|
|
6722
7446
|
insightsIndexPath,
|
|
@@ -6752,7 +7476,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
6752
7476
|
)
|
|
6753
7477
|
);
|
|
6754
7478
|
await writeFileIfChanged(
|
|
6755
|
-
|
|
7479
|
+
path14.join(paths.wikiDir, "projects", "index.md"),
|
|
6756
7480
|
matter7.stringify(["# Projects", "", "- Run `swarmvault compile` to build project rollups.", ""].join("\n"), {
|
|
6757
7481
|
page_id: "projects:index",
|
|
6758
7482
|
kind: "index",
|
|
@@ -6774,7 +7498,7 @@ async function initVault(rootDir, options = {}) {
|
|
|
6774
7498
|
})
|
|
6775
7499
|
);
|
|
6776
7500
|
await writeFileIfChanged(
|
|
6777
|
-
|
|
7501
|
+
path14.join(paths.wikiDir, "candidates", "index.md"),
|
|
6778
7502
|
matter7.stringify(["# Candidates", "", "- Run `swarmvault compile` to stage candidate pages.", ""].join("\n"), {
|
|
6779
7503
|
page_id: "candidates:index",
|
|
6780
7504
|
kind: "index",
|
|
@@ -6891,7 +7615,7 @@ async function compileVault(rootDir, options = {}) {
|
|
|
6891
7615
|
),
|
|
6892
7616
|
Promise.all(
|
|
6893
7617
|
clean.map(async (manifest) => {
|
|
6894
|
-
const cached = await readJsonFile(
|
|
7618
|
+
const cached = await readJsonFile(path14.join(paths.analysesDir, `${manifest.sourceId}.json`));
|
|
6895
7619
|
if (cached) {
|
|
6896
7620
|
return cached;
|
|
6897
7621
|
}
|
|
@@ -6905,23 +7629,38 @@ async function compileVault(rootDir, options = {}) {
|
|
|
6905
7629
|
})
|
|
6906
7630
|
)
|
|
6907
7631
|
]);
|
|
6908
|
-
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
|
+
);
|
|
6909
7647
|
await Promise.all([
|
|
6910
|
-
ensureDir(
|
|
6911
|
-
ensureDir(
|
|
6912
|
-
ensureDir(
|
|
6913
|
-
ensureDir(
|
|
6914
|
-
ensureDir(
|
|
6915
|
-
ensureDir(
|
|
6916
|
-
ensureDir(
|
|
6917
|
-
ensureDir(
|
|
6918
|
-
ensureDir(
|
|
6919
|
-
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"))
|
|
6920
7658
|
]);
|
|
6921
7659
|
const sync = await syncVaultArtifacts(rootDir, {
|
|
6922
7660
|
schemas,
|
|
6923
7661
|
manifests,
|
|
6924
7662
|
analyses,
|
|
7663
|
+
codeIndex,
|
|
6925
7664
|
sourceProjects,
|
|
6926
7665
|
outputPages,
|
|
6927
7666
|
insightPages,
|
|
@@ -7057,7 +7796,7 @@ async function queryVault(rootDir, options) {
|
|
|
7057
7796
|
assetFiles: staged.assetFiles
|
|
7058
7797
|
}
|
|
7059
7798
|
]);
|
|
7060
|
-
stagedPath =
|
|
7799
|
+
stagedPath = path14.join(approval.approvalDir, "wiki", staged.page.path);
|
|
7061
7800
|
savedPageId = staged.page.id;
|
|
7062
7801
|
approvalId = approval.approvalId;
|
|
7063
7802
|
approvalDir = approval.approvalDir;
|
|
@@ -7313,9 +8052,9 @@ ${orchestrationNotes.join("\n")}
|
|
|
7313
8052
|
approvalId = approval.approvalId;
|
|
7314
8053
|
approvalDir = approval.approvalDir;
|
|
7315
8054
|
stepResults.forEach((result, index) => {
|
|
7316
|
-
result.stagedPath =
|
|
8055
|
+
result.stagedPath = path14.join(approval.approvalDir, "wiki", stagedStepPages[index]?.page.path ?? "");
|
|
7317
8056
|
});
|
|
7318
|
-
stagedHubPath =
|
|
8057
|
+
stagedHubPath = path14.join(approval.approvalDir, "wiki", hubPage.path);
|
|
7319
8058
|
} else {
|
|
7320
8059
|
await refreshVaultAfterOutputSave(rootDir);
|
|
7321
8060
|
}
|
|
@@ -7372,15 +8111,15 @@ async function listPages(rootDir) {
|
|
|
7372
8111
|
}
|
|
7373
8112
|
async function readPage(rootDir, relativePath) {
|
|
7374
8113
|
const { paths } = await loadVaultConfig(rootDir);
|
|
7375
|
-
const absolutePath =
|
|
8114
|
+
const absolutePath = path14.resolve(paths.wikiDir, relativePath);
|
|
7376
8115
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
7377
8116
|
return null;
|
|
7378
8117
|
}
|
|
7379
|
-
const raw = await
|
|
8118
|
+
const raw = await fs11.readFile(absolutePath, "utf8");
|
|
7380
8119
|
const parsed = matter7(raw);
|
|
7381
8120
|
return {
|
|
7382
8121
|
path: relativePath,
|
|
7383
|
-
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)),
|
|
7384
8123
|
frontmatter: parsed.data,
|
|
7385
8124
|
content: parsed.content
|
|
7386
8125
|
};
|
|
@@ -7416,7 +8155,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
7416
8155
|
severity: "warning",
|
|
7417
8156
|
code: "stale_page",
|
|
7418
8157
|
message: `Page ${page.title} is stale because the vault schema changed.`,
|
|
7419
|
-
pagePath:
|
|
8158
|
+
pagePath: path14.join(paths.wikiDir, page.path),
|
|
7420
8159
|
relatedPageIds: [page.id]
|
|
7421
8160
|
});
|
|
7422
8161
|
}
|
|
@@ -7427,7 +8166,7 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
7427
8166
|
severity: "warning",
|
|
7428
8167
|
code: "stale_page",
|
|
7429
8168
|
message: `Page ${page.title} is stale because source ${sourceId} changed.`,
|
|
7430
|
-
pagePath:
|
|
8169
|
+
pagePath: path14.join(paths.wikiDir, page.path),
|
|
7431
8170
|
relatedSourceIds: [sourceId],
|
|
7432
8171
|
relatedPageIds: [page.id]
|
|
7433
8172
|
});
|
|
@@ -7438,13 +8177,13 @@ function structuralLintFindings(_rootDir, paths, graph, schemas, manifests, sour
|
|
|
7438
8177
|
severity: "info",
|
|
7439
8178
|
code: "orphan_page",
|
|
7440
8179
|
message: `Page ${page.title} has no backlinks.`,
|
|
7441
|
-
pagePath:
|
|
8180
|
+
pagePath: path14.join(paths.wikiDir, page.path),
|
|
7442
8181
|
relatedPageIds: [page.id]
|
|
7443
8182
|
});
|
|
7444
8183
|
}
|
|
7445
|
-
const absolutePath =
|
|
8184
|
+
const absolutePath = path14.join(paths.wikiDir, page.path);
|
|
7446
8185
|
if (await fileExists(absolutePath)) {
|
|
7447
|
-
const content = await
|
|
8186
|
+
const content = await fs11.readFile(absolutePath, "utf8");
|
|
7448
8187
|
if (content.includes("## Claims")) {
|
|
7449
8188
|
const uncited = content.split("\n").filter((line) => line.startsWith("- ") && !line.includes("[source:"));
|
|
7450
8189
|
if (uncited.length) {
|
|
@@ -7524,7 +8263,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
7524
8263
|
}
|
|
7525
8264
|
|
|
7526
8265
|
// src/mcp.ts
|
|
7527
|
-
var SERVER_VERSION = "0.1.
|
|
8266
|
+
var SERVER_VERSION = "0.1.18";
|
|
7528
8267
|
async function createMcpServer(rootDir) {
|
|
7529
8268
|
const server = new McpServer({
|
|
7530
8269
|
name: "swarmvault",
|
|
@@ -7705,7 +8444,7 @@ async function createMcpServer(rootDir) {
|
|
|
7705
8444
|
},
|
|
7706
8445
|
async () => {
|
|
7707
8446
|
const { paths } = await loadVaultConfig(rootDir);
|
|
7708
|
-
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();
|
|
7709
8448
|
return asTextResource("swarmvault://sessions", JSON.stringify(files, null, 2));
|
|
7710
8449
|
}
|
|
7711
8450
|
);
|
|
@@ -7738,8 +8477,8 @@ async function createMcpServer(rootDir) {
|
|
|
7738
8477
|
return asTextResource(`swarmvault://pages/${encodedPath}`, `Page not found: ${relativePath}`);
|
|
7739
8478
|
}
|
|
7740
8479
|
const { paths } = await loadVaultConfig(rootDir);
|
|
7741
|
-
const absolutePath =
|
|
7742
|
-
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"));
|
|
7743
8482
|
}
|
|
7744
8483
|
);
|
|
7745
8484
|
server.registerResource(
|
|
@@ -7747,11 +8486,11 @@ async function createMcpServer(rootDir) {
|
|
|
7747
8486
|
new ResourceTemplate("swarmvault://sessions/{path}", {
|
|
7748
8487
|
list: async () => {
|
|
7749
8488
|
const { paths } = await loadVaultConfig(rootDir);
|
|
7750
|
-
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();
|
|
7751
8490
|
return {
|
|
7752
8491
|
resources: files.map((relativePath) => ({
|
|
7753
8492
|
uri: `swarmvault://sessions/${encodeURIComponent(relativePath)}`,
|
|
7754
|
-
name:
|
|
8493
|
+
name: path15.basename(relativePath, ".md"),
|
|
7755
8494
|
title: relativePath,
|
|
7756
8495
|
description: "SwarmVault session artifact",
|
|
7757
8496
|
mimeType: "text/markdown"
|
|
@@ -7768,11 +8507,11 @@ async function createMcpServer(rootDir) {
|
|
|
7768
8507
|
const { paths } = await loadVaultConfig(rootDir);
|
|
7769
8508
|
const encodedPath = typeof variables.path === "string" ? variables.path : "";
|
|
7770
8509
|
const relativePath = decodeURIComponent(encodedPath);
|
|
7771
|
-
const absolutePath =
|
|
8510
|
+
const absolutePath = path15.resolve(paths.sessionsDir, relativePath);
|
|
7772
8511
|
if (!absolutePath.startsWith(paths.sessionsDir) || !await fileExists(absolutePath)) {
|
|
7773
8512
|
return asTextResource(`swarmvault://sessions/${encodedPath}`, `Session not found: ${relativePath}`);
|
|
7774
8513
|
}
|
|
7775
|
-
return asTextResource(`swarmvault://sessions/${encodedPath}`, await
|
|
8514
|
+
return asTextResource(`swarmvault://sessions/${encodedPath}`, await fs12.readFile(absolutePath, "utf8"));
|
|
7776
8515
|
}
|
|
7777
8516
|
);
|
|
7778
8517
|
return server;
|
|
@@ -7820,13 +8559,13 @@ function asTextResource(uri, text) {
|
|
|
7820
8559
|
}
|
|
7821
8560
|
|
|
7822
8561
|
// src/schedule.ts
|
|
7823
|
-
import
|
|
7824
|
-
import
|
|
8562
|
+
import fs13 from "fs/promises";
|
|
8563
|
+
import path16 from "path";
|
|
7825
8564
|
function scheduleStatePath(schedulesDir, jobId) {
|
|
7826
|
-
return
|
|
8565
|
+
return path16.join(schedulesDir, `${encodeURIComponent(jobId)}.json`);
|
|
7827
8566
|
}
|
|
7828
8567
|
function scheduleLockPath(schedulesDir, jobId) {
|
|
7829
|
-
return
|
|
8568
|
+
return path16.join(schedulesDir, `${encodeURIComponent(jobId)}.lock`);
|
|
7830
8569
|
}
|
|
7831
8570
|
function parseEveryDuration(value) {
|
|
7832
8571
|
const match = value.trim().match(/^(\d+)(m|h|d)$/i);
|
|
@@ -7929,13 +8668,13 @@ async function acquireJobLease(rootDir, jobId) {
|
|
|
7929
8668
|
const { paths } = await loadVaultConfig(rootDir);
|
|
7930
8669
|
const leasePath = scheduleLockPath(paths.schedulesDir, jobId);
|
|
7931
8670
|
await ensureDir(paths.schedulesDir);
|
|
7932
|
-
const handle = await
|
|
8671
|
+
const handle = await fs13.open(leasePath, "wx");
|
|
7933
8672
|
await handle.writeFile(`${process.pid}
|
|
7934
8673
|
${(/* @__PURE__ */ new Date()).toISOString()}
|
|
7935
8674
|
`);
|
|
7936
8675
|
await handle.close();
|
|
7937
8676
|
return async () => {
|
|
7938
|
-
await
|
|
8677
|
+
await fs13.rm(leasePath, { force: true });
|
|
7939
8678
|
};
|
|
7940
8679
|
}
|
|
7941
8680
|
async function listSchedules(rootDir) {
|
|
@@ -8083,24 +8822,24 @@ async function serveSchedules(rootDir, pollMs = 3e4) {
|
|
|
8083
8822
|
|
|
8084
8823
|
// src/viewer.ts
|
|
8085
8824
|
import { execFile } from "child_process";
|
|
8086
|
-
import
|
|
8825
|
+
import fs14 from "fs/promises";
|
|
8087
8826
|
import http from "http";
|
|
8088
|
-
import
|
|
8827
|
+
import path17 from "path";
|
|
8089
8828
|
import { promisify } from "util";
|
|
8090
8829
|
import matter8 from "gray-matter";
|
|
8091
8830
|
import mime2 from "mime-types";
|
|
8092
8831
|
var execFileAsync = promisify(execFile);
|
|
8093
8832
|
async function readViewerPage(rootDir, relativePath) {
|
|
8094
8833
|
const { paths } = await loadVaultConfig(rootDir);
|
|
8095
|
-
const absolutePath =
|
|
8834
|
+
const absolutePath = path17.resolve(paths.wikiDir, relativePath);
|
|
8096
8835
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
8097
8836
|
return null;
|
|
8098
8837
|
}
|
|
8099
|
-
const raw = await
|
|
8838
|
+
const raw = await fs14.readFile(absolutePath, "utf8");
|
|
8100
8839
|
const parsed = matter8(raw);
|
|
8101
8840
|
return {
|
|
8102
8841
|
path: relativePath,
|
|
8103
|
-
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)),
|
|
8104
8843
|
frontmatter: parsed.data,
|
|
8105
8844
|
content: parsed.content,
|
|
8106
8845
|
assets: normalizeOutputAssets(parsed.data.output_assets)
|
|
@@ -8108,12 +8847,12 @@ async function readViewerPage(rootDir, relativePath) {
|
|
|
8108
8847
|
}
|
|
8109
8848
|
async function readViewerAsset(rootDir, relativePath) {
|
|
8110
8849
|
const { paths } = await loadVaultConfig(rootDir);
|
|
8111
|
-
const absolutePath =
|
|
8850
|
+
const absolutePath = path17.resolve(paths.wikiDir, relativePath);
|
|
8112
8851
|
if (!absolutePath.startsWith(paths.wikiDir) || !await fileExists(absolutePath)) {
|
|
8113
8852
|
return null;
|
|
8114
8853
|
}
|
|
8115
8854
|
return {
|
|
8116
|
-
buffer: await
|
|
8855
|
+
buffer: await fs14.readFile(absolutePath),
|
|
8117
8856
|
mimeType: mime2.lookup(absolutePath) || "application/octet-stream"
|
|
8118
8857
|
};
|
|
8119
8858
|
}
|
|
@@ -8136,12 +8875,12 @@ async function readJsonBody(request) {
|
|
|
8136
8875
|
return JSON.parse(raw);
|
|
8137
8876
|
}
|
|
8138
8877
|
async function ensureViewerDist(viewerDistDir) {
|
|
8139
|
-
const indexPath =
|
|
8878
|
+
const indexPath = path17.join(viewerDistDir, "index.html");
|
|
8140
8879
|
if (await fileExists(indexPath)) {
|
|
8141
8880
|
return;
|
|
8142
8881
|
}
|
|
8143
|
-
const viewerProjectDir =
|
|
8144
|
-
if (await fileExists(
|
|
8882
|
+
const viewerProjectDir = path17.dirname(viewerDistDir);
|
|
8883
|
+
if (await fileExists(path17.join(viewerProjectDir, "package.json"))) {
|
|
8145
8884
|
await execFileAsync("pnpm", ["build"], { cwd: viewerProjectDir });
|
|
8146
8885
|
}
|
|
8147
8886
|
}
|
|
@@ -8158,7 +8897,7 @@ async function startGraphServer(rootDir, port) {
|
|
|
8158
8897
|
return;
|
|
8159
8898
|
}
|
|
8160
8899
|
response.writeHead(200, { "content-type": "application/json" });
|
|
8161
|
-
response.end(await
|
|
8900
|
+
response.end(await fs14.readFile(paths.graphPath, "utf8"));
|
|
8162
8901
|
return;
|
|
8163
8902
|
}
|
|
8164
8903
|
if (url.pathname === "/api/search") {
|
|
@@ -8257,8 +8996,8 @@ async function startGraphServer(rootDir, port) {
|
|
|
8257
8996
|
return;
|
|
8258
8997
|
}
|
|
8259
8998
|
const relativePath = url.pathname === "/" ? "index.html" : url.pathname.slice(1);
|
|
8260
|
-
const target =
|
|
8261
|
-
const fallback =
|
|
8999
|
+
const target = path17.join(paths.viewerDistDir, relativePath);
|
|
9000
|
+
const fallback = path17.join(paths.viewerDistDir, "index.html");
|
|
8262
9001
|
const filePath = await fileExists(target) ? target : fallback;
|
|
8263
9002
|
if (!await fileExists(filePath)) {
|
|
8264
9003
|
response.writeHead(503, { "content-type": "text/plain" });
|
|
@@ -8266,7 +9005,7 @@ async function startGraphServer(rootDir, port) {
|
|
|
8266
9005
|
return;
|
|
8267
9006
|
}
|
|
8268
9007
|
response.writeHead(200, { "content-type": mime2.lookup(filePath) || "text/plain" });
|
|
8269
|
-
response.end(await
|
|
9008
|
+
response.end(await fs14.readFile(filePath));
|
|
8270
9009
|
});
|
|
8271
9010
|
await new Promise((resolve) => {
|
|
8272
9011
|
server.listen(effectivePort, resolve);
|
|
@@ -8293,7 +9032,7 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
8293
9032
|
throw new Error("Graph artifact not found. Run `swarmvault compile` first.");
|
|
8294
9033
|
}
|
|
8295
9034
|
await ensureViewerDist(paths.viewerDistDir);
|
|
8296
|
-
const indexPath =
|
|
9035
|
+
const indexPath = path17.join(paths.viewerDistDir, "index.html");
|
|
8297
9036
|
if (!await fileExists(indexPath)) {
|
|
8298
9037
|
throw new Error("Viewer build not found. Run `pnpm build` first.");
|
|
8299
9038
|
}
|
|
@@ -8317,16 +9056,16 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
8317
9056
|
} : null;
|
|
8318
9057
|
})
|
|
8319
9058
|
);
|
|
8320
|
-
const rawHtml = await
|
|
9059
|
+
const rawHtml = await fs14.readFile(indexPath, "utf8");
|
|
8321
9060
|
const scriptMatch = rawHtml.match(/<script type="module" crossorigin src="([^"]+)"><\/script>/);
|
|
8322
9061
|
const styleMatch = rawHtml.match(/<link rel="stylesheet" crossorigin href="([^"]+)">/);
|
|
8323
|
-
const scriptPath = scriptMatch?.[1] ?
|
|
8324
|
-
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;
|
|
8325
9064
|
if (!scriptPath || !await fileExists(scriptPath)) {
|
|
8326
9065
|
throw new Error("Viewer script bundle not found. Run `pnpm build` first.");
|
|
8327
9066
|
}
|
|
8328
|
-
const script = await
|
|
8329
|
-
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") : "";
|
|
8330
9069
|
const embeddedData = JSON.stringify({ graph, pages: pages.filter(Boolean) }, null, 2).replace(/</g, "\\u003c");
|
|
8331
9070
|
const html = [
|
|
8332
9071
|
"<!doctype html>",
|
|
@@ -8345,13 +9084,13 @@ async function exportGraphHtml(rootDir, outputPath) {
|
|
|
8345
9084
|
"</html>",
|
|
8346
9085
|
""
|
|
8347
9086
|
].filter(Boolean).join("\n");
|
|
8348
|
-
await
|
|
8349
|
-
await
|
|
8350
|
-
return
|
|
9087
|
+
await fs14.mkdir(path17.dirname(outputPath), { recursive: true });
|
|
9088
|
+
await fs14.writeFile(outputPath, html, "utf8");
|
|
9089
|
+
return path17.resolve(outputPath);
|
|
8351
9090
|
}
|
|
8352
9091
|
|
|
8353
9092
|
// src/watch.ts
|
|
8354
|
-
import
|
|
9093
|
+
import path18 from "path";
|
|
8355
9094
|
import process2 from "process";
|
|
8356
9095
|
import chokidar from "chokidar";
|
|
8357
9096
|
var MAX_BACKOFF_MS = 3e4;
|
|
@@ -8496,7 +9235,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
8496
9235
|
};
|
|
8497
9236
|
}
|
|
8498
9237
|
function toWatchReason(baseDir, targetPath) {
|
|
8499
|
-
return
|
|
9238
|
+
return path18.relative(baseDir, targetPath) || ".";
|
|
8500
9239
|
}
|
|
8501
9240
|
export {
|
|
8502
9241
|
acceptApproval,
|
|
@@ -8515,6 +9254,7 @@ export {
|
|
|
8515
9254
|
getWebSearchAdapterForTask,
|
|
8516
9255
|
getWorkspaceInfo,
|
|
8517
9256
|
importInbox,
|
|
9257
|
+
ingestDirectory,
|
|
8518
9258
|
ingestInput,
|
|
8519
9259
|
initVault,
|
|
8520
9260
|
initWorkspace,
|