@swarmvaultai/cli 3.16.1 → 3.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +68 -16
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -80,6 +80,7 @@ import {
|
|
|
80
80
|
readContextPack,
|
|
81
81
|
readGraphReport,
|
|
82
82
|
readMemoryTask,
|
|
83
|
+
readPage,
|
|
83
84
|
rebuildRetrievalIndex,
|
|
84
85
|
refreshGraphClusters,
|
|
85
86
|
registerLocalWhisperProvider,
|
|
@@ -336,9 +337,9 @@ program.addHelpText(
|
|
|
336
337
|
function readCliVersion() {
|
|
337
338
|
try {
|
|
338
339
|
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
339
|
-
return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "3.
|
|
340
|
+
return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "3.18.0";
|
|
340
341
|
} catch {
|
|
341
|
-
return "3.
|
|
342
|
+
return "3.18.0";
|
|
342
343
|
}
|
|
343
344
|
}
|
|
344
345
|
function parsePositiveInt(value, fallback) {
|
|
@@ -791,17 +792,29 @@ function getCommandPath(command) {
|
|
|
791
792
|
}
|
|
792
793
|
async function runGraphUpdateCommand(targetPath, options) {
|
|
793
794
|
const overrideRoots = targetPath ? [path2.resolve(process2.cwd(), targetPath)] : void 0;
|
|
795
|
+
const files = options.file?.length ? options.file.map((candidate2) => path2.resolve(process2.cwd(), candidate2)) : void 0;
|
|
794
796
|
const result = await runWatchCycle(process2.cwd(), {
|
|
795
797
|
repo: true,
|
|
796
798
|
codeOnly: true,
|
|
797
799
|
lint: options.lint ?? false,
|
|
798
800
|
force: options.force ?? false,
|
|
799
|
-
overrideRoots
|
|
801
|
+
overrideRoots,
|
|
802
|
+
files
|
|
800
803
|
});
|
|
801
804
|
if (isJson()) {
|
|
802
805
|
emitJson(result);
|
|
803
806
|
return;
|
|
804
807
|
}
|
|
808
|
+
if (result.queuedFiles?.length && result.scannedCount === 0) {
|
|
809
|
+
log(`Another refresh holds the lock. Queued ${result.queuedFiles.length} file(s) for the active refresh to fold in.`);
|
|
810
|
+
return;
|
|
811
|
+
}
|
|
812
|
+
if (files) {
|
|
813
|
+
log(
|
|
814
|
+
`Refreshed ${result.scannedCount} file(s). Imported ${result.repoImportedCount}, updated ${result.repoUpdatedCount}, removed ${result.repoRemovedCount}, pending semantic refresh ${result.pendingSemanticRefreshCount}.`
|
|
815
|
+
);
|
|
816
|
+
return;
|
|
817
|
+
}
|
|
805
818
|
log(
|
|
806
819
|
`Updated graph from ${result.watchedRepoRoots.length} repo root${result.watchedRepoRoots.length === 1 ? "" : "s"}. Imported ${result.repoImportedCount}, updated ${result.repoUpdatedCount}, removed ${result.repoRemovedCount}, pending semantic refresh ${result.pendingSemanticRefreshCount}.`
|
|
807
820
|
);
|
|
@@ -1814,7 +1827,7 @@ program.command("lint").description("Run anti-drift and wiki-health checks.").op
|
|
|
1814
1827
|
});
|
|
1815
1828
|
var graph = program.command("graph").description("Graph-related commands.").enablePositionalOptions();
|
|
1816
1829
|
var graphPush = graph.command("push").description("Push the compiled graph into external sinks.");
|
|
1817
|
-
graph.command("update").alias("refresh").description("Refresh code-derived graph artifacts from tracked repo roots
|
|
1830
|
+
graph.command("update").alias("refresh").description("Refresh code-derived graph artifacts from tracked repo roots, one explicit repo path, or explicit files.").argument("[path]", "Optional repo root to refresh instead of configured/tracked roots").option("--file <path>", "Refresh only this file (repeatable); skips the full tracked-root walk", collectRepeated, []).option("--lint", "Run lint after the refresh cycle", false).option("--force", "Allow graph updates even when node or edge counts shrink sharply", false).action(runGraphUpdateCommand);
|
|
1818
1831
|
graph.command("tree").description("Write a collapsible source/module/symbol tree for the compiled graph.").option("--output <html>", "Output HTML path (default: wiki/graph/tree.html)").option("--root <path>", "Vault root to read instead of the current directory").option("--label <name>", "Tree title").option("--max-children <n>", "Maximum children to render per tree node", "250").action(runGraphTreeCommand);
|
|
1819
1832
|
graph.command("merge").description("Merge SwarmVault or node-link JSON graph files into one namespaced graph artifact.").argument("<graphs...>", "Graph JSON files to merge").requiredOption("--out <path>", "Output graph JSON path").option("--label <name>", "Label/prefix to use when merging one graph").action(runGraphMergeCommand);
|
|
1820
1833
|
graph.command("status").description("Read-only check for graph/report presence and tracked repo changes.").argument("[path]", "Optional repo root to check instead of configured/tracked roots").action(showGraphStatusCommand);
|
|
@@ -2030,6 +2043,17 @@ graph.command("query").description("Traverse the compiled graph deterministicall
|
|
|
2030
2043
|
return;
|
|
2031
2044
|
}
|
|
2032
2045
|
log(result.summary);
|
|
2046
|
+
if (result.topMatchPagePath) {
|
|
2047
|
+
const page = await readPage(process2.cwd(), result.topMatchPagePath).catch(() => null);
|
|
2048
|
+
if (page?.content) {
|
|
2049
|
+
const limit = 1600;
|
|
2050
|
+
const excerpt = page.content.length > limit ? `${page.content.slice(0, limit)}
|
|
2051
|
+
\u2026 (truncated \u2014 read wiki/${result.topMatchPagePath} for the rest)` : page.content;
|
|
2052
|
+
log(`
|
|
2053
|
+
--- Top match page: wiki/${result.topMatchPagePath} ---
|
|
2054
|
+
${excerpt}`);
|
|
2055
|
+
}
|
|
2056
|
+
}
|
|
2033
2057
|
}
|
|
2034
2058
|
);
|
|
2035
2059
|
graph.command("path").description("Find the shortest graph path between two nodes or pages.").argument("<from>", "Source node/page label or id").argument("<to>", "Target node/page label or id").action(async (from, to) => {
|
|
@@ -2459,31 +2483,31 @@ async function showWatchStatus() {
|
|
|
2459
2483
|
watch.command("status").description("Show the latest watch run plus pending semantic refresh entries.").action(showWatchStatus);
|
|
2460
2484
|
program.command("watch-status").description("Show the latest watch run plus pending semantic refresh entries.").action(showWatchStatus);
|
|
2461
2485
|
program.command("check-update", { hidden: true }).description("Compatibility alias for graph status: read-only graph/report freshness and tracked repo change check.").argument("[path]", "Optional repo root to check instead of configured/tracked roots").action(showGraphStatusCommand);
|
|
2462
|
-
program.command("update", { hidden: true }).description("Compatibility alias for graph update: refresh code-derived graph artifacts from tracked repo roots.").argument("[path]", "Optional repo root to refresh instead of configured/tracked roots").option("--lint", "Run lint after the refresh cycle", false).option("--force", "Allow graph updates even when node or edge counts shrink sharply", false).action(runGraphUpdateCommand);
|
|
2486
|
+
program.command("update", { hidden: true }).description("Compatibility alias for graph update: refresh code-derived graph artifacts from tracked repo roots.").argument("[path]", "Optional repo root to refresh instead of configured/tracked roots").option("--file <path>", "Refresh only this file (repeatable); skips the full tracked-root walk", collectRepeated, []).option("--lint", "Run lint after the refresh cycle", false).option("--force", "Allow graph updates even when node or edge counts shrink sharply", false).action(runGraphUpdateCommand);
|
|
2463
2487
|
program.command("cluster-only", { hidden: true }).description("Compatibility alias for graph cluster: recompute graph communities and report artifacts without re-ingesting.").argument("[vault]", "Optional vault root to cluster instead of the current directory").option("--resolution <number>", "Override the Louvain community resolution for this run").action(
|
|
2464
2488
|
(vaultPath, options) => runGraphClusterCommand(options, vaultPath ? path2.resolve(process2.cwd(), vaultPath) : process2.cwd())
|
|
2465
2489
|
);
|
|
2466
2490
|
program.command("tree", { hidden: true }).description("Compatibility alias for graph tree: write a collapsible source/module/symbol tree for the compiled graph.").option("--output <html>", "Output HTML path (default: wiki/graph/tree.html)").option("--root <path>", "Vault root to read instead of the current directory").option("--label <name>", "Tree title").option("--max-children <n>", "Maximum children to render per tree node", "250").action(runGraphTreeCommand);
|
|
2467
2491
|
program.command("merge-graphs", { hidden: true }).description("Compatibility alias for graph merge: combine graph JSON files into one namespaced graph artifact.").argument("<graphs...>", "Graph JSON files to merge").requiredOption("--out <path>", "Output graph JSON path").option("--label <name>", "Label/prefix to use when merging one graph").action(runGraphMergeCommand);
|
|
2468
2492
|
var hook = program.command("hook").description("Install local git hooks that keep tracked repos and the vault in sync.");
|
|
2469
|
-
hook.command("install").description("Install post-commit and post-checkout hooks for the nearest git repository.").action(async () => {
|
|
2470
|
-
const status = await installGitHooks(process2.cwd());
|
|
2493
|
+
hook.command("install").description("Install post-commit and post-checkout hooks for the nearest git repository, or an explicit repo below the vault root.").argument("[repo]", "Optional git repo path when the tracked repo lives below the vault root").action(async (repo) => {
|
|
2494
|
+
const status = await installGitHooks(process2.cwd(), { repoPath: repo });
|
|
2471
2495
|
if (isJson()) {
|
|
2472
2496
|
emitJson(status);
|
|
2473
2497
|
return;
|
|
2474
2498
|
}
|
|
2475
2499
|
log(`Installed hooks in ${status.repoRoot}`);
|
|
2476
2500
|
});
|
|
2477
|
-
hook.command("uninstall").description("Remove the SwarmVault-managed git hook blocks from the nearest git repository.").action(async () => {
|
|
2478
|
-
const status = await uninstallGitHooks(process2.cwd());
|
|
2501
|
+
hook.command("uninstall").description("Remove the SwarmVault-managed git hook blocks from the nearest git repository or an explicit repo path.").argument("[repo]", "Optional git repo path when the tracked repo lives below the vault root").action(async (repo) => {
|
|
2502
|
+
const status = await uninstallGitHooks(process2.cwd(), { repoPath: repo });
|
|
2479
2503
|
if (isJson()) {
|
|
2480
2504
|
emitJson(status);
|
|
2481
2505
|
return;
|
|
2482
2506
|
}
|
|
2483
2507
|
log(`Removed SwarmVault hook blocks from ${status.repoRoot ?? "the current workspace"}`);
|
|
2484
2508
|
});
|
|
2485
|
-
hook.command("status").description("Show whether SwarmVault-managed git hooks are installed.").action(async () => {
|
|
2486
|
-
const status = await getGitHookStatus(process2.cwd());
|
|
2509
|
+
hook.command("status").description("Show whether SwarmVault-managed git hooks are installed.").argument("[repo]", "Optional git repo path when the tracked repo lives below the vault root").action(async (repo) => {
|
|
2510
|
+
const status = await getGitHookStatus(process2.cwd(), { repoPath: repo });
|
|
2487
2511
|
if (isJson()) {
|
|
2488
2512
|
emitJson(status);
|
|
2489
2513
|
return;
|
|
@@ -2571,9 +2595,13 @@ program.command("mcp").description("Run SwarmVault as a local MCP server over st
|
|
|
2571
2595
|
});
|
|
2572
2596
|
});
|
|
2573
2597
|
var install = program.command("install").description("Install SwarmVault instructions for an agent in the current project.");
|
|
2574
|
-
install.command("status").description("Show whether SwarmVault instructions are installed for an agent.").requiredOption("--agent <agent>", "Agent name").option("--hook", "Include hook/plugin targets in the status check", false).option("--scope <scope>", "Install scope to inspect: project or user", "project").action(async (options) => {
|
|
2598
|
+
install.command("status").description("Show whether SwarmVault instructions are installed for an agent.").requiredOption("--agent <agent>", "Agent name").option("--hook", "Include hook/plugin targets in the status check", false).option("--mcp", "Include MCP config targets in the status check", false).option("--scope <scope>", "Install scope to inspect: project or user", "project").action(async (options) => {
|
|
2575
2599
|
const scope = options.scope === "user" ? "user" : "project";
|
|
2576
|
-
const result = await getAgentInstallStatus(process2.cwd(), options.agent, {
|
|
2600
|
+
const result = await getAgentInstallStatus(process2.cwd(), options.agent, {
|
|
2601
|
+
hook: options.hook ?? false,
|
|
2602
|
+
mcp: options.mcp ?? false,
|
|
2603
|
+
scope
|
|
2604
|
+
});
|
|
2577
2605
|
if (isJson()) {
|
|
2578
2606
|
emitJson(result);
|
|
2579
2607
|
return;
|
|
@@ -2586,7 +2614,10 @@ install.command("status").description("Show whether SwarmVault instructions are
|
|
|
2586
2614
|
install.option(
|
|
2587
2615
|
"--agent <agent>",
|
|
2588
2616
|
"claude, codex, cursor, gemini, goose, opencode, copilot, aider, droid, pi, trae, claw, kiro, kilo, hermes, antigravity, vscode, amp, augment, adal, bob, cline, codebuddy, command-code, continue, cortex, crush, deepagents, devin, firebender, iflow, junie, kilo-code, kimi, kode, mcpjam, mistral-vibe, mux, neovate, openclaw, openhands, pochi, qoder, qwen-code, replit, roo-code, trae-cn, warp, windsurf, or zencoder"
|
|
2589
|
-
).option("--hook", "Also install hook/plugin guidance when the target agent supports it", false).option("--
|
|
2617
|
+
).option("--hook", "Also install hook/plugin guidance when the target agent supports it", false).option("--mcp", "Also register the SwarmVault MCP server in the agent's project MCP config", false).option(
|
|
2618
|
+
"--graph-first [mode]",
|
|
2619
|
+
"Opt in to graph-first search enforcement for installed hooks: deny (default when the flag is passed), context, or off; persisted as hooks.graphFirst"
|
|
2620
|
+
).option("--scope <scope>", "Install scope: project or user", "project").action(async (options) => {
|
|
2590
2621
|
if (!options.agent) {
|
|
2591
2622
|
throw new Error("Specify --agent <agent>.");
|
|
2592
2623
|
}
|
|
@@ -2594,12 +2625,33 @@ install.option(
|
|
|
2594
2625
|
if (options.hook && !hookCapableAgents.has(options.agent)) {
|
|
2595
2626
|
throw new Error("--hook is only supported for --agent codex, claude, opencode, gemini, copilot, or kilo");
|
|
2596
2627
|
}
|
|
2628
|
+
if (options.mcp && options.agent !== "claude") {
|
|
2629
|
+
throw new Error("--mcp is currently only supported for --agent claude (project-level .mcp.json)");
|
|
2630
|
+
}
|
|
2631
|
+
let graphFirst;
|
|
2632
|
+
if (options.graphFirst !== void 0) {
|
|
2633
|
+
const mode = options.graphFirst === true ? "deny" : String(options.graphFirst).toLowerCase();
|
|
2634
|
+
if (mode !== "deny" && mode !== "context" && mode !== "off") {
|
|
2635
|
+
throw new Error("--graph-first accepts deny, context, or off");
|
|
2636
|
+
}
|
|
2637
|
+
graphFirst = mode;
|
|
2638
|
+
}
|
|
2597
2639
|
const scope = options.scope === "user" ? "user" : "project";
|
|
2598
|
-
const result = await installAgent(process2.cwd(), options.agent, {
|
|
2640
|
+
const result = await installAgent(process2.cwd(), options.agent, {
|
|
2641
|
+
hook: options.hook ?? false,
|
|
2642
|
+
mcp: options.mcp ?? false,
|
|
2643
|
+
graphFirst,
|
|
2644
|
+
scope
|
|
2645
|
+
});
|
|
2599
2646
|
if (isJson()) {
|
|
2600
|
-
emitJson({ ...result, hook: options.hook ?? false, scope });
|
|
2647
|
+
emitJson({ ...result, hook: options.hook ?? false, mcp: options.mcp ?? false, graphFirst: graphFirst ?? null, scope });
|
|
2601
2648
|
} else {
|
|
2602
2649
|
log(`Installed rules into ${result.target}`);
|
|
2650
|
+
if (graphFirst) {
|
|
2651
|
+
log(`Graph-first hook mode set to "${graphFirst}" in swarmvault.config.json.`);
|
|
2652
|
+
} else if (options.hook) {
|
|
2653
|
+
log('Hooks run in advisory mode by default; add --graph-first to opt in to search enforcement (hooks.graphFirst: "deny").');
|
|
2654
|
+
}
|
|
2603
2655
|
if (result.targets.length > 1) {
|
|
2604
2656
|
log(`Also wrote: ${result.targets.filter((entry) => entry !== result.target).join(", ")}`);
|
|
2605
2657
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmvaultai/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.18.0",
|
|
4
4
|
"description": "Global CLI for SwarmVault.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"prepublishOnly": "node ../../scripts/check-release-sync.mjs && node ../../scripts/check-published-manifests.mjs"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@swarmvaultai/engine": "3.
|
|
47
|
+
"@swarmvaultai/engine": "3.18.0",
|
|
48
48
|
"commander": "^14.0.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|