@swarmvaultai/engine 0.1.31 → 0.1.33
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 +4 -1
- package/dist/index.d.ts +7 -1
- package/dist/index.js +605 -47
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -174,11 +174,13 @@ This matters because many "OpenAI-compatible" backends only implement part of th
|
|
|
174
174
|
- `addInput(rootDir, input, { author, contributor })` captures supported URLs into normalized markdown before ingesting them, or falls back to generic URL ingest
|
|
175
175
|
- `ingestDirectory(rootDir, inputDir, { repoRoot, include, exclude, maxFiles, gitignore, extractClasses })` recursively ingests a local directory as a repo-aware code/content source tree
|
|
176
176
|
- `importInbox(rootDir, inputDir?)` recursively imports supported inbox files plus markdown and HTML browser-clipper style bundles
|
|
177
|
-
- JavaScript, TypeScript, Python, Go, Rust, Java, C#, C, C++, PHP, Ruby, and
|
|
177
|
+
- JavaScript, TypeScript, Python, Go, Rust, Java, C#, C, C++, PHP, Ruby, PowerShell, Kotlin, and Scala inputs are treated as code sources and compiled into both source pages and `wiki/code/` module pages
|
|
178
|
+
- `.rst` and `.rest` inputs are treated as first-class text sources with lightweight heading and directive normalization before analysis
|
|
178
179
|
- code manifests can carry `repoRelativePath`, and compile writes `state/code-index.json` so local imports can resolve across an ingested repo tree
|
|
179
180
|
- repo-aware manifests, graph nodes, and graph pages can also carry `sourceClass` so first-party, third-party, resource, and generated material can be filtered and reported separately
|
|
180
181
|
- HTML and markdown URL ingests localize remote image references into `raw/assets/<sourceId>/` by default and rewrite the stored markdown to local relative paths
|
|
181
182
|
- PDF and DOCX ingests now write extracted-text and metadata sidecars under `state/extracts/`, and image ingest keeps the same sidecar model for vision extraction
|
|
183
|
+
- Tree-sitter-backed languages now verify runtime and grammar compatibility per language; failures stay local to the affected source and surface as diagnostics instead of aborting the whole compile
|
|
182
184
|
|
|
183
185
|
### Compile + Query
|
|
184
186
|
|
|
@@ -206,6 +208,7 @@ This matters because many "OpenAI-compatible" backends only implement part of th
|
|
|
206
208
|
- `getWatchStatus(rootDir)` reads the latest watch-status artifact plus pending semantic refresh entries
|
|
207
209
|
- `syncTrackedRepos(rootDir)` refreshes previously ingested repo roots, updates changed manifests, and removes deleted repo manifests
|
|
208
210
|
- `syncTrackedReposForWatch(rootDir)` is the repo-watch sync path that defers non-code semantic refresh into `state/watch/`
|
|
211
|
+
- large ingest and compile passes emit low-noise progress on TTYs, and report presentation rolls up tiny fragmented communities without mutating the canonical graph artifact
|
|
209
212
|
- `installGitHooks(rootDir)`, `uninstallGitHooks(rootDir)`, and `getGitHookStatus(rootDir)` manage local `post-commit` and `post-checkout` hook blocks for the nearest git repository
|
|
210
213
|
- `installAgent(rootDir, agent, { hook })` writes agent instructions and returns the primary `target`, all touched `targets`, and optional merge warnings for agents such as Aider
|
|
211
214
|
- `lintVault(rootDir, options)` runs structural lint, optional deep lint, optional contradiction-only filtering through `{ conflicts: true }`, and optional web-augmented evidence gathering
|
package/dist/index.d.ts
CHANGED
|
@@ -57,7 +57,7 @@ type ApprovalChangeType = "create" | "update" | "delete" | "promote";
|
|
|
57
57
|
type SourceKind = "markdown" | "text" | "pdf" | "image" | "html" | "docx" | "binary" | "code";
|
|
58
58
|
type SourceCaptureType = "arxiv" | "doi" | "tweet" | "article" | "url";
|
|
59
59
|
type SourceClass = "first_party" | "third_party" | "resource" | "generated";
|
|
60
|
-
type CodeLanguage = "javascript" | "jsx" | "typescript" | "tsx" | "python" | "go" | "rust" | "java" | "csharp" | "c" | "cpp" | "php" | "ruby" | "powershell";
|
|
60
|
+
type CodeLanguage = "javascript" | "jsx" | "typescript" | "tsx" | "python" | "go" | "rust" | "java" | "kotlin" | "scala" | "csharp" | "c" | "cpp" | "php" | "ruby" | "powershell";
|
|
61
61
|
type CodeSymbolKind = "function" | "class" | "interface" | "type_alias" | "enum" | "variable" | "struct" | "trait";
|
|
62
62
|
type OrchestrationRole = "research" | "audit" | "context" | "safety";
|
|
63
63
|
declare const webSearchProviderTypeSchema: z.ZodEnum<{
|
|
@@ -966,6 +966,12 @@ interface GraphReportArtifact {
|
|
|
966
966
|
path?: string;
|
|
967
967
|
title?: string;
|
|
968
968
|
}>;
|
|
969
|
+
fragmentedCommunityRollup?: {
|
|
970
|
+
totalCommunities: number;
|
|
971
|
+
rolledUpCount: number;
|
|
972
|
+
rolledUpNodes: number;
|
|
973
|
+
exampleLabels: string[];
|
|
974
|
+
};
|
|
969
975
|
surprisingConnections: Array<{
|
|
970
976
|
id: string;
|
|
971
977
|
sourceNodeId: string;
|
package/dist/index.js
CHANGED
|
@@ -1741,7 +1741,9 @@ import fs5 from "fs/promises";
|
|
|
1741
1741
|
import { createRequire } from "module";
|
|
1742
1742
|
import path5 from "path";
|
|
1743
1743
|
var require2 = createRequire(import.meta.url);
|
|
1744
|
-
var
|
|
1744
|
+
var TREE_SITTER_RUNTIME_PACKAGE = "@vscode/tree-sitter-wasm";
|
|
1745
|
+
var TREE_SITTER_EXTRA_GRAMMARS_PACKAGE = "tree-sitter-wasms";
|
|
1746
|
+
var packageRootCache = /* @__PURE__ */ new Map();
|
|
1745
1747
|
var RATIONALE_MARKERS = ["NOTE:", "IMPORTANT:", "HACK:", "WHY:", "RATIONALE:"];
|
|
1746
1748
|
function stripKnownCommentPrefix(line) {
|
|
1747
1749
|
let next = line.trim();
|
|
@@ -1755,21 +1757,41 @@ function stripKnownCommentPrefix(line) {
|
|
|
1755
1757
|
var treeSitterModulePromise;
|
|
1756
1758
|
var treeSitterInitPromise;
|
|
1757
1759
|
var languageCache = /* @__PURE__ */ new Map();
|
|
1758
|
-
var
|
|
1759
|
-
python: "tree-sitter-python.wasm",
|
|
1760
|
-
go: "tree-sitter-go.wasm",
|
|
1761
|
-
rust: "tree-sitter-rust.wasm",
|
|
1762
|
-
java: "tree-sitter-java.wasm",
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1760
|
+
var grammarAssetByLanguage = {
|
|
1761
|
+
python: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-python.wasm" },
|
|
1762
|
+
go: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-go.wasm" },
|
|
1763
|
+
rust: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-rust.wasm" },
|
|
1764
|
+
java: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-java.wasm" },
|
|
1765
|
+
kotlin: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-kotlin.wasm" },
|
|
1766
|
+
scala: { packageName: TREE_SITTER_EXTRA_GRAMMARS_PACKAGE, relativePath: "out/tree-sitter-scala.wasm" },
|
|
1767
|
+
csharp: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-c-sharp.wasm" },
|
|
1768
|
+
c: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-cpp.wasm" },
|
|
1769
|
+
cpp: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-cpp.wasm" },
|
|
1770
|
+
php: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-php.wasm" },
|
|
1771
|
+
ruby: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-ruby.wasm" },
|
|
1772
|
+
powershell: { packageName: TREE_SITTER_RUNTIME_PACKAGE, relativePath: "wasm/tree-sitter-powershell.wasm" }
|
|
1769
1773
|
};
|
|
1774
|
+
function resolvePackageRoot(packageName) {
|
|
1775
|
+
const cached = packageRootCache.get(packageName);
|
|
1776
|
+
if (cached) {
|
|
1777
|
+
return cached;
|
|
1778
|
+
}
|
|
1779
|
+
let resolved;
|
|
1780
|
+
try {
|
|
1781
|
+
resolved = path5.dirname(require2.resolve(`${packageName}/package.json`));
|
|
1782
|
+
} catch {
|
|
1783
|
+
resolved = path5.dirname(path5.dirname(require2.resolve(packageName)));
|
|
1784
|
+
}
|
|
1785
|
+
packageRootCache.set(packageName, resolved);
|
|
1786
|
+
return resolved;
|
|
1787
|
+
}
|
|
1788
|
+
function grammarAssetPath(language) {
|
|
1789
|
+
const asset = grammarAssetByLanguage[language];
|
|
1790
|
+
return path5.join(resolvePackageRoot(asset.packageName), asset.relativePath);
|
|
1791
|
+
}
|
|
1770
1792
|
async function getTreeSitterModule() {
|
|
1771
1793
|
if (!treeSitterModulePromise) {
|
|
1772
|
-
treeSitterModulePromise = import(require2.resolve(
|
|
1794
|
+
treeSitterModulePromise = import(require2.resolve(TREE_SITTER_RUNTIME_PACKAGE)).then(
|
|
1773
1795
|
(module) => module.default ?? module
|
|
1774
1796
|
);
|
|
1775
1797
|
}
|
|
@@ -1777,8 +1799,9 @@ async function getTreeSitterModule() {
|
|
|
1777
1799
|
}
|
|
1778
1800
|
async function ensureTreeSitterInit(module) {
|
|
1779
1801
|
if (!treeSitterInitPromise) {
|
|
1802
|
+
const runtimeRoot = resolvePackageRoot(TREE_SITTER_RUNTIME_PACKAGE);
|
|
1780
1803
|
treeSitterInitPromise = module.Parser.init({
|
|
1781
|
-
locateFile: () => path5.join(
|
|
1804
|
+
locateFile: () => path5.join(runtimeRoot, "wasm", "tree-sitter.wasm")
|
|
1782
1805
|
});
|
|
1783
1806
|
}
|
|
1784
1807
|
return treeSitterInitPromise;
|
|
@@ -1791,7 +1814,7 @@ async function loadLanguage(language) {
|
|
|
1791
1814
|
const loader = (async () => {
|
|
1792
1815
|
const module = await getTreeSitterModule();
|
|
1793
1816
|
await ensureTreeSitterInit(module);
|
|
1794
|
-
const bytes = await fs5.readFile(
|
|
1817
|
+
const bytes = await fs5.readFile(grammarAssetPath(language));
|
|
1795
1818
|
return module.Language.load(bytes);
|
|
1796
1819
|
})();
|
|
1797
1820
|
languageCache.set(language, loader);
|
|
@@ -1805,7 +1828,7 @@ function normalizeSymbolReference(value) {
|
|
|
1805
1828
|
return lastSegment.replace(/[,:;]+$/g, "").trim();
|
|
1806
1829
|
}
|
|
1807
1830
|
function stripCodeExtension(filePath) {
|
|
1808
|
-
return filePath.replace(/\.(?:[cm]?jsx?|tsx?|mts|cts|py|go|rs|java|cs|php|c|cc|cpp|cxx|h|hh|hpp|hxx)$/i, "");
|
|
1831
|
+
return filePath.replace(/\.(?:[cm]?jsx?|tsx?|mts|cts|py|go|rs|java|kt|kts|scala|sc|cs|php|c|cc|cpp|cxx|h|hh|hpp|hxx)$/i, "");
|
|
1809
1832
|
}
|
|
1810
1833
|
function manifestModuleName(manifest, language) {
|
|
1811
1834
|
const repoPath = manifest.repoRelativePath ?? path5.basename(manifest.originalPath ?? manifest.storedPath);
|
|
@@ -2044,6 +2067,7 @@ function extractIdentifier(node) {
|
|
|
2044
2067
|
}
|
|
2045
2068
|
if ([
|
|
2046
2069
|
"identifier",
|
|
2070
|
+
"simple_identifier",
|
|
2047
2071
|
"field_identifier",
|
|
2048
2072
|
"type_identifier",
|
|
2049
2073
|
"name",
|
|
@@ -2057,6 +2081,7 @@ function extractIdentifier(node) {
|
|
|
2057
2081
|
const preferred = node.childForFieldName("name") ?? node.namedChildren.find(
|
|
2058
2082
|
(child) => child && [
|
|
2059
2083
|
"identifier",
|
|
2084
|
+
"simple_identifier",
|
|
2060
2085
|
"field_identifier",
|
|
2061
2086
|
"type_identifier",
|
|
2062
2087
|
"name",
|
|
@@ -2119,6 +2144,25 @@ function diagnosticsFromTree(rootNode) {
|
|
|
2119
2144
|
visit(rootNode);
|
|
2120
2145
|
return diagnostics.slice(0, 20);
|
|
2121
2146
|
}
|
|
2147
|
+
function treeSitterCompatibilityMessage(language, error) {
|
|
2148
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
2149
|
+
if (typeof error === "object" && error && "code" in error && error.code === "MODULE_NOT_FOUND") {
|
|
2150
|
+
return `Tree-sitter runtime support for ${language} is unavailable. Reinstall @swarmvaultai/engine so the packaged parser runtime is present.`;
|
|
2151
|
+
}
|
|
2152
|
+
if (typeof error === "object" && error && "code" in error && error.code === "ENOENT") {
|
|
2153
|
+
return `Missing tree-sitter grammar asset for ${language}. Reinstall @swarmvaultai/engine so the packaged grammar files are present.`;
|
|
2154
|
+
}
|
|
2155
|
+
return `Tree-sitter support for ${language} could not load: ${truncate(normalizeWhitespace(message), 220)}.`;
|
|
2156
|
+
}
|
|
2157
|
+
function treeSitterCompatibilityDiagnostic(language, error) {
|
|
2158
|
+
return {
|
|
2159
|
+
code: 9010,
|
|
2160
|
+
category: "error",
|
|
2161
|
+
message: treeSitterCompatibilityMessage(language, error),
|
|
2162
|
+
line: 1,
|
|
2163
|
+
column: 1
|
|
2164
|
+
};
|
|
2165
|
+
}
|
|
2122
2166
|
function parsePythonImportStatement(text) {
|
|
2123
2167
|
const match = text.trim().match(/^import\s+(.+)$/);
|
|
2124
2168
|
if (!match) {
|
|
@@ -2188,6 +2232,36 @@ function parseJavaImport(text) {
|
|
|
2188
2232
|
reExport: false
|
|
2189
2233
|
};
|
|
2190
2234
|
}
|
|
2235
|
+
function parseKotlinImport(text) {
|
|
2236
|
+
const cleaned = text.trim().replace(/^import\s+/, "");
|
|
2237
|
+
if (!cleaned) {
|
|
2238
|
+
return void 0;
|
|
2239
|
+
}
|
|
2240
|
+
const aliasMatch = cleaned.match(/^(.+?)\s+as\s+([A-Za-z_]\w*)$/);
|
|
2241
|
+
const specifier = (aliasMatch ? aliasMatch[1] : cleaned).trim();
|
|
2242
|
+
if (!specifier) {
|
|
2243
|
+
return void 0;
|
|
2244
|
+
}
|
|
2245
|
+
return {
|
|
2246
|
+
specifier,
|
|
2247
|
+
importedSymbols: [],
|
|
2248
|
+
namespaceImport: aliasMatch?.[2],
|
|
2249
|
+
isExternal: !specifier.startsWith("."),
|
|
2250
|
+
reExport: false
|
|
2251
|
+
};
|
|
2252
|
+
}
|
|
2253
|
+
function parseScalaImport(text) {
|
|
2254
|
+
const cleaned = text.trim().replace(/^import\s+/, "");
|
|
2255
|
+
if (!cleaned) {
|
|
2256
|
+
return [];
|
|
2257
|
+
}
|
|
2258
|
+
return cleaned.split(",").map((item) => item.trim()).filter(Boolean).map((item) => ({
|
|
2259
|
+
specifier: item.replace(/\s*=>\s*/g, " => "),
|
|
2260
|
+
importedSymbols: [],
|
|
2261
|
+
isExternal: !item.startsWith("."),
|
|
2262
|
+
reExport: false
|
|
2263
|
+
}));
|
|
2264
|
+
}
|
|
2191
2265
|
function parseCSharpUsing(text) {
|
|
2192
2266
|
const aliasMatch = text.trim().match(/^using\s+([A-Za-z_]\w*)\s*=\s*([^;]+);$/);
|
|
2193
2267
|
if (aliasMatch) {
|
|
@@ -2280,6 +2354,38 @@ function parsePowerShellImport(commandNode) {
|
|
|
2280
2354
|
}
|
|
2281
2355
|
return void 0;
|
|
2282
2356
|
}
|
|
2357
|
+
function keywordVisible(text, hiddenKeywords) {
|
|
2358
|
+
return !hiddenKeywords.some((keyword) => new RegExp(`\\b${keyword}\\b`).test(text));
|
|
2359
|
+
}
|
|
2360
|
+
function declarationVisible(node, hiddenKeywords) {
|
|
2361
|
+
const modifierText = nodeText(findNamedChild(node, "modifiers") ?? node.childForFieldName("modifiers"));
|
|
2362
|
+
return modifierText ? keywordVisible(modifierText, hiddenKeywords) : true;
|
|
2363
|
+
}
|
|
2364
|
+
function kotlinClassKind(text) {
|
|
2365
|
+
const trimmed = text.trimStart();
|
|
2366
|
+
if (trimmed.startsWith("interface ")) {
|
|
2367
|
+
return "interface";
|
|
2368
|
+
}
|
|
2369
|
+
if (trimmed.startsWith("enum class ")) {
|
|
2370
|
+
return "enum";
|
|
2371
|
+
}
|
|
2372
|
+
return "class";
|
|
2373
|
+
}
|
|
2374
|
+
function scalaDefinitionKind(node) {
|
|
2375
|
+
if (node.type === "trait_definition") {
|
|
2376
|
+
return "trait";
|
|
2377
|
+
}
|
|
2378
|
+
if (node.type === "class_definition") {
|
|
2379
|
+
return /\bcase\s+class\b/.test(node.text) ? "class" : "class";
|
|
2380
|
+
}
|
|
2381
|
+
if (node.type === "object_definition") {
|
|
2382
|
+
return "class";
|
|
2383
|
+
}
|
|
2384
|
+
if (node.type === "function_definition") {
|
|
2385
|
+
return "function";
|
|
2386
|
+
}
|
|
2387
|
+
return void 0;
|
|
2388
|
+
}
|
|
2283
2389
|
function pythonCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
2284
2390
|
const imports = [];
|
|
2285
2391
|
const draftSymbols = [];
|
|
@@ -2533,6 +2639,181 @@ function javaCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
|
2533
2639
|
namespace: packageName
|
|
2534
2640
|
});
|
|
2535
2641
|
}
|
|
2642
|
+
function kotlinCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
2643
|
+
const imports = [];
|
|
2644
|
+
const draftSymbols = [];
|
|
2645
|
+
const exportLabels = [];
|
|
2646
|
+
let packageName;
|
|
2647
|
+
const pushBodyFunctions = (bodyNode, scopeName) => {
|
|
2648
|
+
if (!bodyNode) {
|
|
2649
|
+
return;
|
|
2650
|
+
}
|
|
2651
|
+
for (const child of bodyNode.namedChildren) {
|
|
2652
|
+
if (!child || child.type !== "function_declaration") {
|
|
2653
|
+
continue;
|
|
2654
|
+
}
|
|
2655
|
+
const functionName = extractIdentifier(child.childForFieldName("name") ?? findNamedChild(child, "simple_identifier"));
|
|
2656
|
+
if (!functionName) {
|
|
2657
|
+
continue;
|
|
2658
|
+
}
|
|
2659
|
+
const exported = declarationVisible(child, ["private", "internal", "protected"]);
|
|
2660
|
+
const symbolName = scopeName ? `${scopeName}.${functionName}` : functionName;
|
|
2661
|
+
draftSymbols.push({
|
|
2662
|
+
name: symbolName,
|
|
2663
|
+
kind: "function",
|
|
2664
|
+
signature: singleLineSignature(child.text),
|
|
2665
|
+
exported,
|
|
2666
|
+
callNames: [],
|
|
2667
|
+
extendsNames: [],
|
|
2668
|
+
implementsNames: [],
|
|
2669
|
+
bodyText: nodeText(child.childForFieldName("body") ?? findNamedChild(child, "function_body"))
|
|
2670
|
+
});
|
|
2671
|
+
if (exported) {
|
|
2672
|
+
exportLabels.push(symbolName);
|
|
2673
|
+
}
|
|
2674
|
+
}
|
|
2675
|
+
};
|
|
2676
|
+
for (const child of rootNode.namedChildren) {
|
|
2677
|
+
if (!child) {
|
|
2678
|
+
continue;
|
|
2679
|
+
}
|
|
2680
|
+
if (child.type === "package_header") {
|
|
2681
|
+
packageName = nodeText(findNamedChild(child, "identifier") ?? child.namedChildren.at(0) ?? null) || packageName;
|
|
2682
|
+
continue;
|
|
2683
|
+
}
|
|
2684
|
+
if (child.type === "import_list") {
|
|
2685
|
+
for (const importNode of child.descendantsOfType("import_header").filter((item) => item !== null)) {
|
|
2686
|
+
const parsed = parseKotlinImport(importNode.text);
|
|
2687
|
+
if (parsed) {
|
|
2688
|
+
imports.push(parsed);
|
|
2689
|
+
}
|
|
2690
|
+
}
|
|
2691
|
+
continue;
|
|
2692
|
+
}
|
|
2693
|
+
if (child.type === "function_declaration") {
|
|
2694
|
+
pushBodyFunctions({
|
|
2695
|
+
...child,
|
|
2696
|
+
namedChildren: [child]
|
|
2697
|
+
});
|
|
2698
|
+
continue;
|
|
2699
|
+
}
|
|
2700
|
+
if (child.type !== "class_declaration" && child.type !== "object_declaration") {
|
|
2701
|
+
continue;
|
|
2702
|
+
}
|
|
2703
|
+
const name = extractIdentifier(child.childForFieldName("name") ?? findNamedChild(child, "type_identifier"));
|
|
2704
|
+
if (!name) {
|
|
2705
|
+
continue;
|
|
2706
|
+
}
|
|
2707
|
+
const kind = child.type === "object_declaration" ? "class" : kotlinClassKind(child.text);
|
|
2708
|
+
const delegationNames = uniqueBy(
|
|
2709
|
+
child.namedChildren.filter((item) => item !== null && item.type === "delegation_specifier").flatMap((item) => descendantTypeNames(item)),
|
|
2710
|
+
(item) => item
|
|
2711
|
+
);
|
|
2712
|
+
const exported = declarationVisible(child, ["private", "internal"]);
|
|
2713
|
+
const bodyNode = findNamedChild(child, "class_body") ?? child.childForFieldName("body");
|
|
2714
|
+
draftSymbols.push({
|
|
2715
|
+
name,
|
|
2716
|
+
kind,
|
|
2717
|
+
signature: singleLineSignature(child.text),
|
|
2718
|
+
exported,
|
|
2719
|
+
callNames: [],
|
|
2720
|
+
extendsNames: kind === "interface" ? delegationNames : delegationNames.slice(0, 1),
|
|
2721
|
+
implementsNames: kind === "class" ? delegationNames.slice(1) : [],
|
|
2722
|
+
bodyText: nodeText(bodyNode) || child.text
|
|
2723
|
+
});
|
|
2724
|
+
if (exported) {
|
|
2725
|
+
exportLabels.push(name);
|
|
2726
|
+
}
|
|
2727
|
+
pushBodyFunctions(bodyNode, name);
|
|
2728
|
+
}
|
|
2729
|
+
return finalizeCodeAnalysis(manifest, "kotlin", imports, draftSymbols, exportLabels, diagnostics, {
|
|
2730
|
+
namespace: packageName
|
|
2731
|
+
});
|
|
2732
|
+
}
|
|
2733
|
+
function scalaCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
2734
|
+
const imports = [];
|
|
2735
|
+
const draftSymbols = [];
|
|
2736
|
+
const exportLabels = [];
|
|
2737
|
+
let packageName;
|
|
2738
|
+
const pushTemplateFunctions = (bodyNode, scopeName) => {
|
|
2739
|
+
if (!bodyNode) {
|
|
2740
|
+
return;
|
|
2741
|
+
}
|
|
2742
|
+
for (const child of bodyNode.namedChildren) {
|
|
2743
|
+
if (!child || child.type !== "function_definition") {
|
|
2744
|
+
continue;
|
|
2745
|
+
}
|
|
2746
|
+
const functionName = extractIdentifier(child.childForFieldName("name") ?? findNamedChild(child, "identifier"));
|
|
2747
|
+
if (!functionName) {
|
|
2748
|
+
continue;
|
|
2749
|
+
}
|
|
2750
|
+
const exported = declarationVisible(child, ["private", "protected"]);
|
|
2751
|
+
const symbolName = scopeName ? `${scopeName}.${functionName}` : functionName;
|
|
2752
|
+
draftSymbols.push({
|
|
2753
|
+
name: symbolName,
|
|
2754
|
+
kind: "function",
|
|
2755
|
+
signature: singleLineSignature(child.text),
|
|
2756
|
+
exported,
|
|
2757
|
+
callNames: [],
|
|
2758
|
+
extendsNames: [],
|
|
2759
|
+
implementsNames: [],
|
|
2760
|
+
bodyText: child.text
|
|
2761
|
+
});
|
|
2762
|
+
if (exported) {
|
|
2763
|
+
exportLabels.push(symbolName);
|
|
2764
|
+
}
|
|
2765
|
+
}
|
|
2766
|
+
};
|
|
2767
|
+
for (const child of rootNode.namedChildren) {
|
|
2768
|
+
if (!child) {
|
|
2769
|
+
continue;
|
|
2770
|
+
}
|
|
2771
|
+
if (child.type === "package_clause") {
|
|
2772
|
+
packageName = nodeText(findNamedChild(child, "package_identifier") ?? child.namedChildren.at(0) ?? null) || packageName;
|
|
2773
|
+
continue;
|
|
2774
|
+
}
|
|
2775
|
+
if (child.type === "import_declaration") {
|
|
2776
|
+
imports.push(...parseScalaImport(child.text));
|
|
2777
|
+
continue;
|
|
2778
|
+
}
|
|
2779
|
+
if (child.type === "function_definition") {
|
|
2780
|
+
pushTemplateFunctions({
|
|
2781
|
+
...child,
|
|
2782
|
+
namedChildren: [child]
|
|
2783
|
+
});
|
|
2784
|
+
continue;
|
|
2785
|
+
}
|
|
2786
|
+
if (!["trait_definition", "class_definition", "object_definition"].includes(child.type)) {
|
|
2787
|
+
continue;
|
|
2788
|
+
}
|
|
2789
|
+
const name = extractIdentifier(child.childForFieldName("name") ?? findNamedChild(child, "identifier"));
|
|
2790
|
+
const kind = scalaDefinitionKind(child);
|
|
2791
|
+
if (!name || !kind) {
|
|
2792
|
+
continue;
|
|
2793
|
+
}
|
|
2794
|
+
const extendsClause = findNamedChild(child, "extends_clause") ?? child.childForFieldName("extends");
|
|
2795
|
+
const inheritance = uniqueBy(descendantTypeNames(extendsClause), (item) => item);
|
|
2796
|
+
const bodyNode = findNamedChild(child, "template_body") ?? child.childForFieldName("body");
|
|
2797
|
+
const exported = declarationVisible(child, ["private", "protected"]);
|
|
2798
|
+
draftSymbols.push({
|
|
2799
|
+
name,
|
|
2800
|
+
kind,
|
|
2801
|
+
signature: singleLineSignature(child.text),
|
|
2802
|
+
exported,
|
|
2803
|
+
callNames: [],
|
|
2804
|
+
extendsNames: kind === "trait" ? inheritance : inheritance.slice(0, 1),
|
|
2805
|
+
implementsNames: kind === "class" ? inheritance.slice(1) : [],
|
|
2806
|
+
bodyText: nodeText(bodyNode) || child.text
|
|
2807
|
+
});
|
|
2808
|
+
if (exported) {
|
|
2809
|
+
exportLabels.push(name);
|
|
2810
|
+
}
|
|
2811
|
+
pushTemplateFunctions(bodyNode, name);
|
|
2812
|
+
}
|
|
2813
|
+
return finalizeCodeAnalysis(manifest, "scala", imports, draftSymbols, exportLabels, diagnostics, {
|
|
2814
|
+
namespace: packageName
|
|
2815
|
+
});
|
|
2816
|
+
}
|
|
2536
2817
|
function csharpCodeAnalysis(manifest, rootNode, diagnostics) {
|
|
2537
2818
|
const imports = [];
|
|
2538
2819
|
const draftSymbols = [];
|
|
@@ -2915,11 +3196,19 @@ function cFamilyCodeAnalysis(manifest, language, rootNode, diagnostics) {
|
|
|
2915
3196
|
return finalizeCodeAnalysis(manifest, language, imports, draftSymbols, exportLabels, diagnostics);
|
|
2916
3197
|
}
|
|
2917
3198
|
async function analyzeTreeSitterCode(manifest, content, language) {
|
|
2918
|
-
|
|
2919
|
-
|
|
2920
|
-
|
|
2921
|
-
|
|
2922
|
-
|
|
3199
|
+
let tree = null;
|
|
3200
|
+
try {
|
|
3201
|
+
const module = await getTreeSitterModule();
|
|
3202
|
+
await ensureTreeSitterInit(module);
|
|
3203
|
+
const parser = new module.Parser();
|
|
3204
|
+
parser.setLanguage(await loadLanguage(language));
|
|
3205
|
+
tree = parser.parse(content);
|
|
3206
|
+
} catch (error) {
|
|
3207
|
+
return {
|
|
3208
|
+
code: finalizeCodeAnalysis(manifest, language, [], [], [], [treeSitterCompatibilityDiagnostic(language, error)]),
|
|
3209
|
+
rationales: []
|
|
3210
|
+
};
|
|
3211
|
+
}
|
|
2923
3212
|
if (!tree) {
|
|
2924
3213
|
return {
|
|
2925
3214
|
code: finalizeCodeAnalysis(
|
|
@@ -2953,6 +3242,10 @@ async function analyzeTreeSitterCode(manifest, content, language) {
|
|
|
2953
3242
|
return { code: rustCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
|
|
2954
3243
|
case "java":
|
|
2955
3244
|
return { code: javaCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
|
|
3245
|
+
case "kotlin":
|
|
3246
|
+
return { code: kotlinCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
|
|
3247
|
+
case "scala":
|
|
3248
|
+
return { code: scalaCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
|
|
2956
3249
|
case "csharp":
|
|
2957
3250
|
return { code: csharpCodeAnalysis(manifest, tree.rootNode, diagnostics), rationales };
|
|
2958
3251
|
case "php":
|
|
@@ -2964,6 +3257,26 @@ async function analyzeTreeSitterCode(manifest, content, language) {
|
|
|
2964
3257
|
case "c":
|
|
2965
3258
|
case "cpp":
|
|
2966
3259
|
return { code: cFamilyCodeAnalysis(manifest, language, tree.rootNode, diagnostics), rationales };
|
|
3260
|
+
default:
|
|
3261
|
+
return {
|
|
3262
|
+
code: finalizeCodeAnalysis(
|
|
3263
|
+
manifest,
|
|
3264
|
+
language,
|
|
3265
|
+
[],
|
|
3266
|
+
[],
|
|
3267
|
+
[],
|
|
3268
|
+
[
|
|
3269
|
+
{
|
|
3270
|
+
code: 9011,
|
|
3271
|
+
category: "error",
|
|
3272
|
+
message: `No parser-backed analyzer is registered for ${language}.`,
|
|
3273
|
+
line: 1,
|
|
3274
|
+
column: 1
|
|
3275
|
+
}
|
|
3276
|
+
]
|
|
3277
|
+
),
|
|
3278
|
+
rationales
|
|
3279
|
+
};
|
|
2967
3280
|
}
|
|
2968
3281
|
} finally {
|
|
2969
3282
|
tree.delete();
|
|
@@ -3219,7 +3532,7 @@ function makeRationale2(manifest, index, text, kind, symbolName) {
|
|
|
3219
3532
|
};
|
|
3220
3533
|
}
|
|
3221
3534
|
function stripCodeExtension2(filePath) {
|
|
3222
|
-
return filePath.replace(/\.(?:[cm]?jsx?|tsx?|mts|cts|py|go|rs|java|cs|php|c|cc|cpp|cxx|h|hh|hpp|hxx)$/i, "");
|
|
3535
|
+
return filePath.replace(/\.(?:[cm]?jsx?|tsx?|mts|cts|py|go|rs|java|kt|kts|scala|sc|cs|php|c|cc|cpp|cxx|h|hh|hpp|hxx)$/i, "");
|
|
3223
3536
|
}
|
|
3224
3537
|
function manifestModuleName2(manifest, language) {
|
|
3225
3538
|
const repoPath = manifest.repoRelativePath ?? path6.basename(manifest.originalPath ?? manifest.storedPath);
|
|
@@ -3550,6 +3863,12 @@ function inferCodeLanguage(filePath, mimeType = "") {
|
|
|
3550
3863
|
if (extension === ".java") {
|
|
3551
3864
|
return "java";
|
|
3552
3865
|
}
|
|
3866
|
+
if (extension === ".kt" || extension === ".kts") {
|
|
3867
|
+
return "kotlin";
|
|
3868
|
+
}
|
|
3869
|
+
if (extension === ".scala" || extension === ".sc") {
|
|
3870
|
+
return "scala";
|
|
3871
|
+
}
|
|
3553
3872
|
if (extension === ".cs") {
|
|
3554
3873
|
return "csharp";
|
|
3555
3874
|
}
|
|
@@ -3654,6 +3973,10 @@ function candidateExtensionsFor(language) {
|
|
|
3654
3973
|
return [".rs"];
|
|
3655
3974
|
case "java":
|
|
3656
3975
|
return [".java"];
|
|
3976
|
+
case "kotlin":
|
|
3977
|
+
return [".kt", ".kts"];
|
|
3978
|
+
case "scala":
|
|
3979
|
+
return [".scala", ".sc"];
|
|
3657
3980
|
case "csharp":
|
|
3658
3981
|
return [".cs"];
|
|
3659
3982
|
case "php":
|
|
@@ -3714,10 +4037,17 @@ async function buildCodeIndex(rootDir, manifests, analyses) {
|
|
|
3714
4037
|
break;
|
|
3715
4038
|
}
|
|
3716
4039
|
case "java":
|
|
4040
|
+
case "kotlin":
|
|
4041
|
+
case "scala":
|
|
3717
4042
|
case "csharp":
|
|
3718
4043
|
if (normalizedNamespace) {
|
|
3719
4044
|
recordAlias(aliases, `${normalizedNamespace}.${basename}`);
|
|
3720
4045
|
}
|
|
4046
|
+
if (normalizedNamespace) {
|
|
4047
|
+
for (const symbol of analysis.code.symbols) {
|
|
4048
|
+
recordAlias(aliases, `${normalizedNamespace}.${symbol.name}`);
|
|
4049
|
+
}
|
|
4050
|
+
}
|
|
3721
4051
|
break;
|
|
3722
4052
|
case "php":
|
|
3723
4053
|
if (normalizedNamespace) {
|
|
@@ -3824,6 +4154,8 @@ function findImportCandidates(manifest, codeImport, lookup) {
|
|
|
3824
4154
|
return aliasMatches(lookup, codeImport.specifier);
|
|
3825
4155
|
case "go":
|
|
3826
4156
|
case "java":
|
|
4157
|
+
case "kotlin":
|
|
4158
|
+
case "scala":
|
|
3827
4159
|
case "csharp":
|
|
3828
4160
|
return aliasMatches(lookup, codeImport.specifier);
|
|
3829
4161
|
case "php":
|
|
@@ -3870,6 +4202,8 @@ function importLooksLocal(manifest, codeImport, candidates) {
|
|
|
3870
4202
|
case "powershell":
|
|
3871
4203
|
case "c":
|
|
3872
4204
|
case "cpp":
|
|
4205
|
+
case "kotlin":
|
|
4206
|
+
case "scala":
|
|
3873
4207
|
return !codeImport.isExternal;
|
|
3874
4208
|
default:
|
|
3875
4209
|
return false;
|
|
@@ -4520,6 +4854,9 @@ async function markPagesStaleForSources(rootDir, sourceIds) {
|
|
|
4520
4854
|
var DEFAULT_MAX_ASSET_SIZE = 10 * 1024 * 1024;
|
|
4521
4855
|
var DEFAULT_MAX_DIRECTORY_FILES = 5e3;
|
|
4522
4856
|
var HARD_REPO_IGNORES = /* @__PURE__ */ new Set([".git", ".venv"]);
|
|
4857
|
+
var PROGRESS_FILE_THRESHOLD = 150;
|
|
4858
|
+
var PROGRESS_UPDATE_INTERVAL = 100;
|
|
4859
|
+
var RST_HEADING_MARKERS = /* @__PURE__ */ new Set(["=", "-", "~", "^", '"', "#", "*", "+"]);
|
|
4523
4860
|
function uniqueStrings(values) {
|
|
4524
4861
|
return [...new Set(values.filter(Boolean))];
|
|
4525
4862
|
}
|
|
@@ -4527,6 +4864,9 @@ function inferKind(mimeType, filePath) {
|
|
|
4527
4864
|
if (inferCodeLanguage(filePath, mimeType)) {
|
|
4528
4865
|
return "code";
|
|
4529
4866
|
}
|
|
4867
|
+
if (isRstFilePath(filePath)) {
|
|
4868
|
+
return "text";
|
|
4869
|
+
}
|
|
4530
4870
|
if (mimeType.includes("markdown")) {
|
|
4531
4871
|
return "markdown";
|
|
4532
4872
|
}
|
|
@@ -4547,13 +4887,146 @@ function inferKind(mimeType, filePath) {
|
|
|
4547
4887
|
}
|
|
4548
4888
|
return "binary";
|
|
4549
4889
|
}
|
|
4550
|
-
function
|
|
4890
|
+
function isRstFilePath(filePath) {
|
|
4891
|
+
const extension = path11.extname(filePath).toLowerCase();
|
|
4892
|
+
return extension === ".rst" || extension === ".rest";
|
|
4893
|
+
}
|
|
4894
|
+
function titleFromText(fallback, content, filePath) {
|
|
4895
|
+
if (filePath && isRstFilePath(filePath)) {
|
|
4896
|
+
const rstTitle = titleFromRst(fallback, content);
|
|
4897
|
+
if (rstTitle) {
|
|
4898
|
+
return rstTitle;
|
|
4899
|
+
}
|
|
4900
|
+
}
|
|
4551
4901
|
const heading = content.match(/^#\s+(.+)$/m)?.[1]?.trim();
|
|
4552
4902
|
return heading || fallback;
|
|
4553
4903
|
}
|
|
4554
4904
|
function guessMimeType(target) {
|
|
4905
|
+
if (isRstFilePath(target)) {
|
|
4906
|
+
return "text/x-rst";
|
|
4907
|
+
}
|
|
4555
4908
|
return mime.lookup(target) || "application/octet-stream";
|
|
4556
4909
|
}
|
|
4910
|
+
function rstAdornmentLine(line) {
|
|
4911
|
+
const trimmed = line.trim();
|
|
4912
|
+
if (trimmed.length < 3) {
|
|
4913
|
+
return void 0;
|
|
4914
|
+
}
|
|
4915
|
+
const marker = trimmed[0] ?? "";
|
|
4916
|
+
if (!RST_HEADING_MARKERS.has(marker) || ![...trimmed].every((char) => char === marker)) {
|
|
4917
|
+
return void 0;
|
|
4918
|
+
}
|
|
4919
|
+
return marker;
|
|
4920
|
+
}
|
|
4921
|
+
function rstHeadingLevel(marker) {
|
|
4922
|
+
switch (marker) {
|
|
4923
|
+
case "=":
|
|
4924
|
+
return "#";
|
|
4925
|
+
case "-":
|
|
4926
|
+
return "##";
|
|
4927
|
+
case "~":
|
|
4928
|
+
return "###";
|
|
4929
|
+
case "^":
|
|
4930
|
+
return "####";
|
|
4931
|
+
default:
|
|
4932
|
+
return "##";
|
|
4933
|
+
}
|
|
4934
|
+
}
|
|
4935
|
+
function normalizeRstDirective(line) {
|
|
4936
|
+
const trimmed = line.trim();
|
|
4937
|
+
if (!trimmed.startsWith(".. ")) {
|
|
4938
|
+
return void 0;
|
|
4939
|
+
}
|
|
4940
|
+
const directiveMatch = trimmed.match(/^\.\.\s+([A-Za-z][\w-]*)::\s*(.*)$/);
|
|
4941
|
+
if (!directiveMatch) {
|
|
4942
|
+
return "";
|
|
4943
|
+
}
|
|
4944
|
+
const label = directiveMatch[1].replace(/-/g, " ").split(/\s+/).filter(Boolean).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
|
|
4945
|
+
const detail = directiveMatch[2]?.trim();
|
|
4946
|
+
return detail ? `${label}: ${detail}` : `${label}:`;
|
|
4947
|
+
}
|
|
4948
|
+
function normalizeRstExtractedText(content) {
|
|
4949
|
+
const lines = content.split(/\r?\n/);
|
|
4950
|
+
const normalized = [];
|
|
4951
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
4952
|
+
const current = lines[index] ?? "";
|
|
4953
|
+
const next = lines[index + 1] ?? "";
|
|
4954
|
+
const afterNext = lines[index + 2] ?? "";
|
|
4955
|
+
const trimmed = current.trim();
|
|
4956
|
+
if (!trimmed) {
|
|
4957
|
+
normalized.push("");
|
|
4958
|
+
continue;
|
|
4959
|
+
}
|
|
4960
|
+
const currentAdornment = rstAdornmentLine(current);
|
|
4961
|
+
const nextAdornment = rstAdornmentLine(next);
|
|
4962
|
+
const afterNextAdornment = rstAdornmentLine(afterNext);
|
|
4963
|
+
if (currentAdornment && next.trim() && afterNextAdornment && currentAdornment === afterNextAdornment) {
|
|
4964
|
+
normalized.push(`${rstHeadingLevel(currentAdornment)} ${next.trim()}`);
|
|
4965
|
+
normalized.push("");
|
|
4966
|
+
index += 2;
|
|
4967
|
+
continue;
|
|
4968
|
+
}
|
|
4969
|
+
if (nextAdornment && trimmed.length > 0) {
|
|
4970
|
+
normalized.push(`${rstHeadingLevel(nextAdornment)} ${trimmed}`);
|
|
4971
|
+
normalized.push("");
|
|
4972
|
+
index += 1;
|
|
4973
|
+
continue;
|
|
4974
|
+
}
|
|
4975
|
+
const directive = normalizeRstDirective(current);
|
|
4976
|
+
if (directive !== void 0) {
|
|
4977
|
+
if (directive) {
|
|
4978
|
+
normalized.push(directive);
|
|
4979
|
+
}
|
|
4980
|
+
continue;
|
|
4981
|
+
}
|
|
4982
|
+
normalized.push(current);
|
|
4983
|
+
}
|
|
4984
|
+
return normalized.join("\n").replace(/[ \t]+\n/g, "\n").replace(/\n{3,}/g, "\n\n").trim();
|
|
4985
|
+
}
|
|
4986
|
+
function titleFromRst(fallback, content) {
|
|
4987
|
+
const normalized = normalizeRstExtractedText(content);
|
|
4988
|
+
const heading = normalized.match(/^#+\s+(.+)$/m)?.[1]?.trim();
|
|
4989
|
+
return heading || fallback;
|
|
4990
|
+
}
|
|
4991
|
+
function extractedTextForPlainSource(filePath, sourceKind, content) {
|
|
4992
|
+
if (sourceKind === "text" && isRstFilePath(filePath)) {
|
|
4993
|
+
return normalizeRstExtractedText(content);
|
|
4994
|
+
}
|
|
4995
|
+
return content;
|
|
4996
|
+
}
|
|
4997
|
+
function shouldEmitProgress(totalItems) {
|
|
4998
|
+
return totalItems >= PROGRESS_FILE_THRESHOLD && Boolean(process.stderr?.isTTY);
|
|
4999
|
+
}
|
|
5000
|
+
function createProgressReporter(prefix, totalItems) {
|
|
5001
|
+
if (!shouldEmitProgress(totalItems)) {
|
|
5002
|
+
return {
|
|
5003
|
+
tick: () => {
|
|
5004
|
+
},
|
|
5005
|
+
finish: () => {
|
|
5006
|
+
}
|
|
5007
|
+
};
|
|
5008
|
+
}
|
|
5009
|
+
let completed = 0;
|
|
5010
|
+
let nextUpdate = Math.min(PROGRESS_UPDATE_INTERVAL, totalItems);
|
|
5011
|
+
process.stderr.write(`[swarmvault ${prefix}] starting ${totalItems} file(s)
|
|
5012
|
+
`);
|
|
5013
|
+
return {
|
|
5014
|
+
tick: () => {
|
|
5015
|
+
completed += 1;
|
|
5016
|
+
if (completed >= nextUpdate || completed === totalItems) {
|
|
5017
|
+
process.stderr.write(`[swarmvault ${prefix}] ${completed}/${totalItems}
|
|
5018
|
+
`);
|
|
5019
|
+
while (completed >= nextUpdate) {
|
|
5020
|
+
nextUpdate += PROGRESS_UPDATE_INTERVAL;
|
|
5021
|
+
}
|
|
5022
|
+
}
|
|
5023
|
+
},
|
|
5024
|
+
finish: (summary) => {
|
|
5025
|
+
process.stderr.write(`[swarmvault ${prefix}] finished ${totalItems} file(s)${summary ? ` (${summary})` : ""}
|
|
5026
|
+
`);
|
|
5027
|
+
}
|
|
5028
|
+
};
|
|
5029
|
+
}
|
|
4557
5030
|
function normalizeIngestOptions(options) {
|
|
4558
5031
|
return {
|
|
4559
5032
|
includeAssets: options?.includeAssets ?? true,
|
|
@@ -5490,6 +5963,7 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
|
5490
5963
|
}))
|
|
5491
5964
|
);
|
|
5492
5965
|
scannedCount += files.length;
|
|
5966
|
+
const progress = createProgressReporter("sync", files.length);
|
|
5493
5967
|
const currentPaths = new Set(files.map((absolutePath) => path11.resolve(absolutePath)));
|
|
5494
5968
|
for (const absolutePath of files) {
|
|
5495
5969
|
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path11.relative(repoRoot, absolutePath));
|
|
@@ -5500,7 +5974,9 @@ async function syncTrackedRepos(rootDir, options, repoRoots) {
|
|
|
5500
5974
|
} else if (result.wasUpdated) {
|
|
5501
5975
|
updated.push(result.manifest);
|
|
5502
5976
|
}
|
|
5977
|
+
progress.tick();
|
|
5503
5978
|
}
|
|
5979
|
+
progress.finish(`repo=${toPosix(path11.relative(rootDir, repoRoot)) || "."}`);
|
|
5504
5980
|
for (const manifest of repoManifests) {
|
|
5505
5981
|
const originalPath = manifest.originalPath ? path11.resolve(manifest.originalPath) : null;
|
|
5506
5982
|
if (originalPath && !currentPaths.has(originalPath)) {
|
|
@@ -5590,6 +6066,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
5590
6066
|
}))
|
|
5591
6067
|
);
|
|
5592
6068
|
scannedCount += files.length;
|
|
6069
|
+
const progress = createProgressReporter("sync-watch", files.length);
|
|
5593
6070
|
const currentPaths = new Set(files.map((absolutePath) => path11.resolve(absolutePath)));
|
|
5594
6071
|
for (const absolutePath of files) {
|
|
5595
6072
|
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path11.relative(repoRoot, absolutePath));
|
|
@@ -5616,6 +6093,7 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
5616
6093
|
staleSourceIds.add(existing.sourceId);
|
|
5617
6094
|
}
|
|
5618
6095
|
}
|
|
6096
|
+
progress.tick();
|
|
5619
6097
|
continue;
|
|
5620
6098
|
}
|
|
5621
6099
|
const result = await persistPreparedInput(rootDir, prepared, paths);
|
|
@@ -5624,7 +6102,9 @@ async function syncTrackedReposForWatch(rootDir, options, repoRoots) {
|
|
|
5624
6102
|
} else if (result.wasUpdated) {
|
|
5625
6103
|
updated.push(result.manifest);
|
|
5626
6104
|
}
|
|
6105
|
+
progress.tick();
|
|
5627
6106
|
}
|
|
6107
|
+
progress.finish(`repo=${toPosix(path11.relative(rootDir, repoRoot)) || "."}`);
|
|
5628
6108
|
for (const manifest of repoManifests) {
|
|
5629
6109
|
const originalPath = manifest.originalPath ? path11.resolve(manifest.originalPath) : null;
|
|
5630
6110
|
if (originalPath && !currentPaths.has(originalPath)) {
|
|
@@ -5685,8 +6165,8 @@ async function prepareFileInput(rootDir, absoluteInput, repoRoot, sourceClass) {
|
|
|
5685
6165
|
let extractedText;
|
|
5686
6166
|
let extractionArtifact;
|
|
5687
6167
|
if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
|
|
5688
|
-
extractedText = payloadBytes.toString("utf8");
|
|
5689
|
-
title = titleFromText(path11.basename(absoluteInput, path11.extname(absoluteInput)), extractedText);
|
|
6168
|
+
extractedText = extractedTextForPlainSource(absoluteInput, sourceKind, payloadBytes.toString("utf8"));
|
|
6169
|
+
title = titleFromText(path11.basename(absoluteInput, path11.extname(absoluteInput)), extractedText, absoluteInput);
|
|
5690
6170
|
extractionArtifact = createPlainTextExtractionArtifact(sourceKind, mimeType);
|
|
5691
6171
|
} else if (sourceKind === "html") {
|
|
5692
6172
|
const html = payloadBytes.toString("utf8");
|
|
@@ -5796,8 +6276,8 @@ async function prepareUrlInput(rootDir, input, options) {
|
|
|
5796
6276
|
const extension = path11.extname(inputUrl.pathname);
|
|
5797
6277
|
storedExtension = extension || `.${mime.extension(mimeType) || "bin"}`;
|
|
5798
6278
|
if (sourceKind === "markdown" || sourceKind === "text" || sourceKind === "code") {
|
|
5799
|
-
extractedText = payloadBytes.toString("utf8");
|
|
5800
|
-
title = titleFromText(title || inputUrl.hostname, extractedText);
|
|
6279
|
+
extractedText = extractedTextForPlainSource(inputUrl.pathname, sourceKind, payloadBytes.toString("utf8"));
|
|
6280
|
+
title = titleFromText(title || inputUrl.hostname, extractedText, inputUrl.pathname);
|
|
5801
6281
|
extractionArtifact = createPlainTextExtractionArtifact(sourceKind, mimeType);
|
|
5802
6282
|
if (sourceKind === "markdown" && options.includeAssets) {
|
|
5803
6283
|
const { attachments: remoteAttachments, skippedCount } = await collectRemoteImageAttachments(
|
|
@@ -6090,6 +6570,7 @@ async function ingestDirectory(rootDir, inputDir, options) {
|
|
|
6090
6570
|
const { files, skipped } = await collectDirectoryFiles(rootDir, absoluteInputDir, repoRoot, normalizedOptions);
|
|
6091
6571
|
const imported = [];
|
|
6092
6572
|
const updated = [];
|
|
6573
|
+
const progress = createProgressReporter("ingest", files.length);
|
|
6093
6574
|
for (const absolutePath of files) {
|
|
6094
6575
|
const relativePath = repoRelativePathFor(absolutePath, repoRoot) ?? toPosix(path11.relative(repoRoot, absolutePath));
|
|
6095
6576
|
const prepared = await prepareFileInput(rootDir, absolutePath, repoRoot, sourceClassForRelativePath(relativePath, normalizedOptions));
|
|
@@ -6101,7 +6582,9 @@ async function ingestDirectory(rootDir, inputDir, options) {
|
|
|
6101
6582
|
} else {
|
|
6102
6583
|
skipped.push({ path: toPosix(path11.relative(rootDir, absolutePath)), reason: "duplicate_content" });
|
|
6103
6584
|
}
|
|
6585
|
+
progress.tick();
|
|
6104
6586
|
}
|
|
6587
|
+
progress.finish(`imported=${imported.length}, updated=${updated.length}, skipped=${skipped.length}`);
|
|
6105
6588
|
await appendLogEntry(rootDir, "ingest_directory", toPosix(path11.relative(rootDir, absoluteInputDir)) || ".", [
|
|
6106
6589
|
`repo_root=${toPosix(path11.relative(rootDir, repoRoot)) || "."}`,
|
|
6107
6590
|
`scanned=${files.length}`,
|
|
@@ -8889,6 +9372,35 @@ function topGroupPatterns(graph) {
|
|
|
8889
9372
|
(left, right) => right.confidence - left.confidence || right.nodeIds.length - left.nodeIds.length || left.label.localeCompare(right.label)
|
|
8890
9373
|
).slice(0, 8);
|
|
8891
9374
|
}
|
|
9375
|
+
function fragmentedCommunityPresentation(graph, communityPages) {
|
|
9376
|
+
const thinCommunities = (graph.communities ?? []).filter((community) => community.nodeIds.length <= 2).sort((left, right) => right.nodeIds.length - left.nodeIds.length || left.label.localeCompare(right.label));
|
|
9377
|
+
const visibleCommunities = thinCommunities.slice(0, 6).map((community) => {
|
|
9378
|
+
const page = communityPages.find((candidate) => candidate.id === `graph:${community.id}`);
|
|
9379
|
+
return {
|
|
9380
|
+
id: community.id,
|
|
9381
|
+
label: community.label,
|
|
9382
|
+
nodeCount: community.nodeIds.length,
|
|
9383
|
+
pageId: page?.id,
|
|
9384
|
+
path: page?.path,
|
|
9385
|
+
title: page?.title
|
|
9386
|
+
};
|
|
9387
|
+
});
|
|
9388
|
+
const rolledUp = thinCommunities.slice(visibleCommunities.length);
|
|
9389
|
+
if (!rolledUp.length) {
|
|
9390
|
+
return {
|
|
9391
|
+
thinCommunities: visibleCommunities
|
|
9392
|
+
};
|
|
9393
|
+
}
|
|
9394
|
+
return {
|
|
9395
|
+
thinCommunities: visibleCommunities,
|
|
9396
|
+
fragmentedCommunityRollup: {
|
|
9397
|
+
totalCommunities: graph.communities?.length ?? 0,
|
|
9398
|
+
rolledUpCount: rolledUp.length,
|
|
9399
|
+
rolledUpNodes: rolledUp.reduce((sum, community) => sum + community.nodeIds.length, 0),
|
|
9400
|
+
exampleLabels: rolledUp.slice(0, 4).map((community) => community.label)
|
|
9401
|
+
}
|
|
9402
|
+
};
|
|
9403
|
+
}
|
|
8892
9404
|
function suggestedGraphQuestions(graph) {
|
|
8893
9405
|
const thinCommunities = (graph.communities ?? []).filter((community) => community.nodeIds.length <= 2);
|
|
8894
9406
|
const bridgeNodes = graph.nodes.filter((node) => (node.bridgeScore ?? 0) > 0).sort((left, right) => (right.bridgeScore ?? 0) - (left.bridgeScore ?? 0)).slice(0, 3);
|
|
@@ -8903,17 +9415,7 @@ function buildGraphReportArtifact(input) {
|
|
|
8903
9415
|
const pagesById = new Map(reportGraph.pages.map((page) => [page.id, page]));
|
|
8904
9416
|
const godNodes = reportGraph.nodes.filter((node) => node.isGodNode).sort((left, right) => (right.degree ?? 0) - (left.degree ?? 0)).slice(0, 8);
|
|
8905
9417
|
const bridgeNodes = reportGraph.nodes.filter((node) => (node.bridgeScore ?? 0) > 0).sort((left, right) => (right.bridgeScore ?? 0) - (left.bridgeScore ?? 0)).slice(0, 8);
|
|
8906
|
-
const
|
|
8907
|
-
const page = input.communityPages.find((candidate) => candidate.id === `graph:${community.id}`);
|
|
8908
|
-
return {
|
|
8909
|
-
id: community.id,
|
|
8910
|
-
label: community.label,
|
|
8911
|
-
nodeCount: community.nodeIds.length,
|
|
8912
|
-
pageId: page?.id,
|
|
8913
|
-
path: page?.path,
|
|
8914
|
-
title: page?.title
|
|
8915
|
-
};
|
|
8916
|
-
});
|
|
9418
|
+
const communityPresentation = fragmentedCommunityPresentation(reportGraph, input.communityPages);
|
|
8917
9419
|
const surprisingConnections = topSurprisingConnections(reportGraph, pagesById);
|
|
8918
9420
|
const groupPatterns = topGroupPatterns(reportGraph);
|
|
8919
9421
|
const breakdown = sourceClassBreakdown(input.graph);
|
|
@@ -8927,6 +9429,11 @@ function buildGraphReportArtifact(input) {
|
|
|
8927
9429
|
`Non-first-party material accounts for ${(nonFirstPartyNodes / Math.max(1, input.graph.nodes.length) * 100).toFixed(1)}% of graph nodes.`
|
|
8928
9430
|
);
|
|
8929
9431
|
}
|
|
9432
|
+
if (communityPresentation.fragmentedCommunityRollup) {
|
|
9433
|
+
warnings.push(
|
|
9434
|
+
`First-party report view is fragmented: ${communityPresentation.fragmentedCommunityRollup.rolledUpCount} tiny communities were rolled up for readability.`
|
|
9435
|
+
);
|
|
9436
|
+
}
|
|
8930
9437
|
return {
|
|
8931
9438
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
8932
9439
|
graphHash: input.graphHash,
|
|
@@ -8964,10 +9471,11 @@ function buildGraphReportArtifact(input) {
|
|
|
8964
9471
|
degree: node.degree,
|
|
8965
9472
|
bridgeScore: node.bridgeScore
|
|
8966
9473
|
})),
|
|
8967
|
-
thinCommunities,
|
|
9474
|
+
thinCommunities: communityPresentation.thinCommunities,
|
|
9475
|
+
fragmentedCommunityRollup: communityPresentation.fragmentedCommunityRollup,
|
|
8968
9476
|
surprisingConnections,
|
|
8969
9477
|
groupPatterns,
|
|
8970
|
-
suggestedQuestions: suggestedGraphQuestions(
|
|
9478
|
+
suggestedQuestions: suggestedGraphQuestions(reportGraph),
|
|
8971
9479
|
communityPages: input.communityPages.map((page) => ({
|
|
8972
9480
|
id: page.id,
|
|
8973
9481
|
path: page.path,
|
|
@@ -9094,6 +9602,10 @@ function buildGraphReportPage(input) {
|
|
|
9094
9602
|
...input.report.thinCommunities.length ? input.report.thinCommunities.map(
|
|
9095
9603
|
(community) => community.path ? `- [[${community.path.replace(/\.md$/, "")}|${community.title ?? community.label}]] (${community.nodeCount} node(s))` : `- ${community.label} (${community.nodeCount} node(s))`
|
|
9096
9604
|
) : ["- No thin communities detected."],
|
|
9605
|
+
...input.report.fragmentedCommunityRollup ? [
|
|
9606
|
+
`- Rolled up ${input.report.fragmentedCommunityRollup.rolledUpCount} additional tiny communities covering ${input.report.fragmentedCommunityRollup.rolledUpNodes} node(s).`,
|
|
9607
|
+
`- Example rolled-up labels: ${input.report.fragmentedCommunityRollup.exampleLabels.join(", ")}`
|
|
9608
|
+
] : [],
|
|
9097
9609
|
"",
|
|
9098
9610
|
"## Surprising Connections",
|
|
9099
9611
|
"",
|
|
@@ -10236,9 +10748,41 @@ function searchPages(dbPath, query, limitOrOptions = 5) {
|
|
|
10236
10748
|
}
|
|
10237
10749
|
|
|
10238
10750
|
// src/vault.ts
|
|
10751
|
+
var COMPILE_PROGRESS_THRESHOLD = 120;
|
|
10752
|
+
var COMPILE_PROGRESS_UPDATE_INTERVAL = 50;
|
|
10239
10753
|
function uniqueStrings3(values) {
|
|
10240
10754
|
return uniqueBy(values.filter(Boolean), (value) => value);
|
|
10241
10755
|
}
|
|
10756
|
+
function createCompileProgressReporter(phase, totalItems) {
|
|
10757
|
+
if (totalItems < COMPILE_PROGRESS_THRESHOLD || !process.stderr?.isTTY) {
|
|
10758
|
+
return {
|
|
10759
|
+
tick: () => {
|
|
10760
|
+
},
|
|
10761
|
+
finish: () => {
|
|
10762
|
+
}
|
|
10763
|
+
};
|
|
10764
|
+
}
|
|
10765
|
+
let completed = 0;
|
|
10766
|
+
let nextUpdate = Math.min(COMPILE_PROGRESS_UPDATE_INTERVAL, totalItems);
|
|
10767
|
+
process.stderr.write(`[swarmvault compile] ${phase}: 0/${totalItems}
|
|
10768
|
+
`);
|
|
10769
|
+
return {
|
|
10770
|
+
tick: (label) => {
|
|
10771
|
+
completed += 1;
|
|
10772
|
+
if (completed >= nextUpdate || completed === totalItems) {
|
|
10773
|
+
process.stderr.write(`[swarmvault compile] ${phase}: ${completed}/${totalItems}${label ? ` (${label})` : ""}
|
|
10774
|
+
`);
|
|
10775
|
+
while (completed >= nextUpdate) {
|
|
10776
|
+
nextUpdate += COMPILE_PROGRESS_UPDATE_INTERVAL;
|
|
10777
|
+
}
|
|
10778
|
+
}
|
|
10779
|
+
},
|
|
10780
|
+
finish: (summary) => {
|
|
10781
|
+
process.stderr.write(`[swarmvault compile] ${phase}: ${totalItems}/${totalItems}${summary ? ` (${summary})` : ""}
|
|
10782
|
+
`);
|
|
10783
|
+
}
|
|
10784
|
+
};
|
|
10785
|
+
}
|
|
10242
10786
|
function normalizeOutputFormat2(format) {
|
|
10243
10787
|
return format === "report" || format === "slides" || format === "chart" || format === "image" ? format : "markdown";
|
|
10244
10788
|
}
|
|
@@ -13117,34 +13661,41 @@ async function compileVault(rootDir, options = {}) {
|
|
|
13117
13661
|
candidatePageCount: (graph?.pages ?? []).filter((page) => page.status === "candidate").length
|
|
13118
13662
|
};
|
|
13119
13663
|
}
|
|
13664
|
+
const analysisProgress = createCompileProgressReporter("analyze", manifests.length);
|
|
13120
13665
|
const [dirtyAnalyses, cleanAnalyses] = await Promise.all([
|
|
13121
13666
|
Promise.all(
|
|
13122
|
-
dirty.map(
|
|
13123
|
-
|
|
13667
|
+
dirty.map(async (manifest) => {
|
|
13668
|
+
const analysis = await analyzeSource(
|
|
13124
13669
|
manifest,
|
|
13125
13670
|
await readExtractedText(rootDir, manifest),
|
|
13126
13671
|
provider,
|
|
13127
13672
|
paths,
|
|
13128
13673
|
getEffectiveSchema(schemas, sourceProjects[manifest.sourceId] ?? null)
|
|
13129
|
-
)
|
|
13130
|
-
|
|
13674
|
+
);
|
|
13675
|
+
analysisProgress.tick(manifest.title);
|
|
13676
|
+
return analysis;
|
|
13677
|
+
})
|
|
13131
13678
|
),
|
|
13132
13679
|
Promise.all(
|
|
13133
13680
|
clean.map(async (manifest) => {
|
|
13134
13681
|
const cached = await readJsonFile(path21.join(paths.analysesDir, `${manifest.sourceId}.json`));
|
|
13135
13682
|
if (cached) {
|
|
13683
|
+
analysisProgress.tick(manifest.title);
|
|
13136
13684
|
return cached;
|
|
13137
13685
|
}
|
|
13138
|
-
|
|
13686
|
+
const analysis = await analyzeSource(
|
|
13139
13687
|
manifest,
|
|
13140
13688
|
await readExtractedText(rootDir, manifest),
|
|
13141
13689
|
provider,
|
|
13142
13690
|
paths,
|
|
13143
13691
|
getEffectiveSchema(schemas, sourceProjects[manifest.sourceId] ?? null)
|
|
13144
13692
|
);
|
|
13693
|
+
analysisProgress.tick(manifest.title);
|
|
13694
|
+
return analysis;
|
|
13145
13695
|
})
|
|
13146
13696
|
)
|
|
13147
13697
|
]);
|
|
13698
|
+
analysisProgress.finish(`dirty=${dirty.length}, clean=${clean.length}`);
|
|
13148
13699
|
const initialAnalyses = [...dirtyAnalyses, ...cleanAnalyses];
|
|
13149
13700
|
const codeIndex = await buildCodeIndex(rootDir, manifests, initialAnalyses);
|
|
13150
13701
|
const analyses = await Promise.all(
|
|
@@ -13913,7 +14464,7 @@ async function bootstrapDemo(rootDir, input) {
|
|
|
13913
14464
|
}
|
|
13914
14465
|
|
|
13915
14466
|
// src/mcp.ts
|
|
13916
|
-
var SERVER_VERSION = "0.1.
|
|
14467
|
+
var SERVER_VERSION = "0.1.33";
|
|
13917
14468
|
async function createMcpServer(rootDir) {
|
|
13918
14469
|
const server = new McpServer({
|
|
13919
14470
|
name: "swarmvault",
|
|
@@ -14743,6 +15294,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
14743
15294
|
let consecutiveFailures = 0;
|
|
14744
15295
|
let currentDebounceMs = baseDebounceMs;
|
|
14745
15296
|
const reasons = /* @__PURE__ */ new Set();
|
|
15297
|
+
let activeCycle = null;
|
|
14746
15298
|
const watcher = chokidar.watch(watchTargets, {
|
|
14747
15299
|
ignoreInitial: true,
|
|
14748
15300
|
usePolling: true,
|
|
@@ -14787,7 +15339,12 @@ async function watchVault(rootDir, options = {}) {
|
|
|
14787
15339
|
clearTimeout(timer);
|
|
14788
15340
|
}
|
|
14789
15341
|
timer = setTimeout(() => {
|
|
14790
|
-
|
|
15342
|
+
const cycle = runCycle();
|
|
15343
|
+
activeCycle = cycle.finally(() => {
|
|
15344
|
+
if (activeCycle === cycle) {
|
|
15345
|
+
activeCycle = null;
|
|
15346
|
+
}
|
|
15347
|
+
});
|
|
14791
15348
|
}, currentDebounceMs);
|
|
14792
15349
|
};
|
|
14793
15350
|
const runCycle = async () => {
|
|
@@ -14955,6 +15512,7 @@ async function watchVault(rootDir, options = {}) {
|
|
|
14955
15512
|
clearTimeout(timer);
|
|
14956
15513
|
}
|
|
14957
15514
|
await watcher.close();
|
|
15515
|
+
await activeCycle;
|
|
14958
15516
|
}
|
|
14959
15517
|
};
|
|
14960
15518
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmvaultai/engine",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.33",
|
|
4
4
|
"description": "Core engine for SwarmVault: ingest, compile, query, lint, and provider abstractions.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -57,6 +57,7 @@
|
|
|
57
57
|
"mime-types": "^3.0.1",
|
|
58
58
|
"neo4j-driver": "^5.28.3",
|
|
59
59
|
"pdfjs-dist": "^5.4.394",
|
|
60
|
+
"tree-sitter-wasms": "^0.1.13",
|
|
60
61
|
"turndown": "^7.2.1",
|
|
61
62
|
"typescript": "^5.9.3",
|
|
62
63
|
"yaml": "^2.8.1",
|