@swarmvaultai/cli 3.9.0 → 3.10.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/README.md +35 -0
- package/dist/index.js +70 -61
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -65,10 +65,13 @@ swarmvault explore "What should I research next?" --steps 3
|
|
|
65
65
|
swarmvault lint --deep
|
|
66
66
|
swarmvault graph blast ./src/index.ts
|
|
67
67
|
swarmvault graph status ./src
|
|
68
|
+
swarmvault check-update ./src
|
|
68
69
|
swarmvault graph stats
|
|
69
70
|
swarmvault graph validate --strict
|
|
70
71
|
swarmvault graph update .
|
|
72
|
+
swarmvault update .
|
|
71
73
|
swarmvault graph cluster
|
|
74
|
+
swarmvault cluster-only
|
|
72
75
|
swarmvault graph tree --output ./exports/tree.html
|
|
73
76
|
swarmvault graph query "Which nodes bridge the biggest clusters?"
|
|
74
77
|
swarmvault graph explain "concept:drift"
|
|
@@ -79,6 +82,7 @@ swarmvault graph serve
|
|
|
79
82
|
swarmvault graph export --report ./exports/report.html
|
|
80
83
|
swarmvault graph export --html ./exports/graph.html
|
|
81
84
|
swarmvault graph export --cypher ./exports/graph.cypher
|
|
85
|
+
swarmvault graph export --neo4j ./exports/graph.cypher
|
|
82
86
|
swarmvault graph merge ./exports/graph.json ./other-graph.json --out ./exports/merged-graph.json
|
|
83
87
|
swarmvault graph push neo4j --dry-run
|
|
84
88
|
```
|
|
@@ -410,6 +414,22 @@ Refresh code-derived graph artifacts from tracked repo roots or one explicit rep
|
|
|
410
414
|
- aborts if nodes or edges drop by more than 25% compared with the existing graph; pass `--force` or set `SWARMVAULT_FORCE_UPDATE=1` when the shrink is expected
|
|
411
415
|
- `--json` returns the same one-shot watch result shape, including repo import/update/remove counts and pending semantic refresh entries
|
|
412
416
|
|
|
417
|
+
### `swarmvault check-update [path]`
|
|
418
|
+
|
|
419
|
+
Compatibility alias for `swarmvault graph status [path]`.
|
|
420
|
+
|
|
421
|
+
- performs the same read-only graph/report freshness check
|
|
422
|
+
- keeps automation-friendly JSON output for cron or hook wrappers
|
|
423
|
+
- recommends `swarmvault update`/`swarmvault graph update` for code-only drift and `swarmvault compile` when semantic refresh is required
|
|
424
|
+
|
|
425
|
+
### `swarmvault update [path]`
|
|
426
|
+
|
|
427
|
+
Compatibility alias for `swarmvault graph update [path]`.
|
|
428
|
+
|
|
429
|
+
- runs the same code-only repo refresh path
|
|
430
|
+
- accepts `--lint` and `--force`
|
|
431
|
+
- returns the same JSON shape as `graph update`
|
|
432
|
+
|
|
413
433
|
### `swarmvault graph tree [--output <html>] [--root <path>] [--label <name>] [--max-children <n>]`
|
|
414
434
|
|
|
415
435
|
Write a collapsible HTML source tree for the current `state/graph.json`.
|
|
@@ -470,6 +490,21 @@ Recompute communities, node degrees, bridge scores, god-node flags, and graph re
|
|
|
470
490
|
- splits oversized or low-cohesion communities after the initial Louvain pass so large-repo reports stay scannable
|
|
471
491
|
- `--json` returns counts plus the graph/report paths
|
|
472
492
|
|
|
493
|
+
### `swarmvault cluster-only [vault] [--resolution <n>]`
|
|
494
|
+
|
|
495
|
+
Compatibility alias for `swarmvault graph cluster`.
|
|
496
|
+
|
|
497
|
+
- recomputes communities and graph report artifacts without ingest or semantic analysis
|
|
498
|
+
- accepts an optional vault root when the command is run from outside the vault
|
|
499
|
+
- returns the same JSON shape as `graph cluster`
|
|
500
|
+
|
|
501
|
+
### `swarmvault graph export --neo4j <path>`
|
|
502
|
+
|
|
503
|
+
Compatibility alias for `swarmvault graph export --cypher <path>`.
|
|
504
|
+
|
|
505
|
+
- writes a Neo4j-ready Cypher import file
|
|
506
|
+
- can be combined with other `graph export` formats in the same run
|
|
507
|
+
|
|
473
508
|
### `swarmvault hook install|uninstall|status`
|
|
474
509
|
|
|
475
510
|
Manage SwarmVault's local git hook blocks for the nearest git repository.
|
package/dist/index.js
CHANGED
|
@@ -315,9 +315,9 @@ program.name("swarmvault").description("SwarmVault is a local-first knowledge co
|
|
|
315
315
|
function readCliVersion() {
|
|
316
316
|
try {
|
|
317
317
|
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
318
|
-
return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "3.
|
|
318
|
+
return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "3.10.0";
|
|
319
319
|
} catch {
|
|
320
|
-
return "3.
|
|
320
|
+
return "3.10.0";
|
|
321
321
|
}
|
|
322
322
|
}
|
|
323
323
|
function parsePositiveInt(value, fallback) {
|
|
@@ -488,6 +488,63 @@ function getCommandPath(command) {
|
|
|
488
488
|
}
|
|
489
489
|
return names;
|
|
490
490
|
}
|
|
491
|
+
async function runGraphUpdateCommand(targetPath, options) {
|
|
492
|
+
const overrideRoots = targetPath ? [path2.resolve(process2.cwd(), targetPath)] : void 0;
|
|
493
|
+
const result = await runWatchCycle(process2.cwd(), {
|
|
494
|
+
repo: true,
|
|
495
|
+
codeOnly: true,
|
|
496
|
+
lint: options.lint ?? false,
|
|
497
|
+
force: options.force ?? false,
|
|
498
|
+
overrideRoots
|
|
499
|
+
});
|
|
500
|
+
if (isJson()) {
|
|
501
|
+
emitJson(result);
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
log(
|
|
505
|
+
`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}.`
|
|
506
|
+
);
|
|
507
|
+
}
|
|
508
|
+
async function showGraphStatusCommand(targetPath) {
|
|
509
|
+
const overrideRoots = targetPath ? [path2.resolve(process2.cwd(), targetPath)] : void 0;
|
|
510
|
+
const status = await getGraphStatus(process2.cwd(), { repoRoots: overrideRoots });
|
|
511
|
+
if (isJson()) {
|
|
512
|
+
emitJson(status);
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
log(`Graph: ${status.graphExists ? status.graphPath : `missing (${status.graphPath})`}`);
|
|
516
|
+
log(`Report: ${status.reportExists ? status.reportPath : `missing (${status.reportPath})`}`);
|
|
517
|
+
log(`Tracked repo roots: ${status.trackedRepoRoots.length ? status.trackedRepoRoots.join(", ") : "none"}`);
|
|
518
|
+
log(`Code changes: ${status.codeChangeCount}`);
|
|
519
|
+
log(`Semantic changes: ${status.semanticChangeCount}`);
|
|
520
|
+
log(`Pending semantic refresh: ${status.pendingSemanticRefresh.length}`);
|
|
521
|
+
if (status.changes.length) {
|
|
522
|
+
for (const change of status.changes.slice(0, 20)) {
|
|
523
|
+
log(`- ${change.refreshType} ${change.changeType} ${change.path}`);
|
|
524
|
+
}
|
|
525
|
+
if (status.changes.length > 20) {
|
|
526
|
+
log(`... and ${status.changes.length - 20} more`);
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
log(`State: ${status.stale ? "stale" : "fresh"}`);
|
|
530
|
+
if (status.recommendedCommand) {
|
|
531
|
+
log(`Recommended: ${status.recommendedCommand}`);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
async function runGraphClusterCommand(options, rootDir = process2.cwd()) {
|
|
535
|
+
const resolution = parsePositiveNumber(options.resolution);
|
|
536
|
+
if (options.resolution && resolution === void 0) {
|
|
537
|
+
throw new Error("--resolution must be a positive number.");
|
|
538
|
+
}
|
|
539
|
+
const result = await refreshGraphClusters(rootDir, { resolution });
|
|
540
|
+
if (isJson()) {
|
|
541
|
+
emitJson(result);
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
log(
|
|
545
|
+
`Refreshed ${result.communityCount} communities across ${result.nodeCount} nodes and ${result.edgeCount} edges. Report: ${result.reportPath}`
|
|
546
|
+
);
|
|
547
|
+
}
|
|
491
548
|
program.hook("postAction", async (_thisCommand, actionCommand) => {
|
|
492
549
|
const notices = await collectCliNotices({
|
|
493
550
|
commandPath: getCommandPath(actionCommand),
|
|
@@ -1157,23 +1214,7 @@ program.command("lint").description("Run anti-drift and wiki-health checks.").op
|
|
|
1157
1214
|
});
|
|
1158
1215
|
var graph = program.command("graph").description("Graph-related commands.").enablePositionalOptions();
|
|
1159
1216
|
var graphPush = graph.command("push").description("Push the compiled graph into external sinks.");
|
|
1160
|
-
graph.command("update").alias("refresh").description("Refresh code-derived graph artifacts from tracked repo roots or one explicit repo path.").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(
|
|
1161
|
-
const overrideRoots = targetPath ? [path2.resolve(process2.cwd(), targetPath)] : void 0;
|
|
1162
|
-
const result = await runWatchCycle(process2.cwd(), {
|
|
1163
|
-
repo: true,
|
|
1164
|
-
codeOnly: true,
|
|
1165
|
-
lint: options.lint ?? false,
|
|
1166
|
-
force: options.force ?? false,
|
|
1167
|
-
overrideRoots
|
|
1168
|
-
});
|
|
1169
|
-
if (isJson()) {
|
|
1170
|
-
emitJson(result);
|
|
1171
|
-
return;
|
|
1172
|
-
}
|
|
1173
|
-
log(
|
|
1174
|
-
`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}.`
|
|
1175
|
-
);
|
|
1176
|
-
});
|
|
1217
|
+
graph.command("update").alias("refresh").description("Refresh code-derived graph artifacts from tracked repo roots or one explicit repo path.").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);
|
|
1177
1218
|
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(async (options) => {
|
|
1178
1219
|
const rootDir = options.root ? path2.resolve(process2.cwd(), options.root) : process2.cwd();
|
|
1179
1220
|
const result = await exportGraphTree(rootDir, options.output, {
|
|
@@ -1204,32 +1245,7 @@ graph.command("merge").description("Merge SwarmVault or node-link JSON graph fil
|
|
|
1204
1245
|
log(`Warning: ${warning}`);
|
|
1205
1246
|
}
|
|
1206
1247
|
});
|
|
1207
|
-
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(
|
|
1208
|
-
const overrideRoots = targetPath ? [path2.resolve(process2.cwd(), targetPath)] : void 0;
|
|
1209
|
-
const status = await getGraphStatus(process2.cwd(), { repoRoots: overrideRoots });
|
|
1210
|
-
if (isJson()) {
|
|
1211
|
-
emitJson(status);
|
|
1212
|
-
return;
|
|
1213
|
-
}
|
|
1214
|
-
log(`Graph: ${status.graphExists ? status.graphPath : `missing (${status.graphPath})`}`);
|
|
1215
|
-
log(`Report: ${status.reportExists ? status.reportPath : `missing (${status.reportPath})`}`);
|
|
1216
|
-
log(`Tracked repo roots: ${status.trackedRepoRoots.length ? status.trackedRepoRoots.join(", ") : "none"}`);
|
|
1217
|
-
log(`Code changes: ${status.codeChangeCount}`);
|
|
1218
|
-
log(`Semantic changes: ${status.semanticChangeCount}`);
|
|
1219
|
-
log(`Pending semantic refresh: ${status.pendingSemanticRefresh.length}`);
|
|
1220
|
-
if (status.changes.length) {
|
|
1221
|
-
for (const change of status.changes.slice(0, 20)) {
|
|
1222
|
-
log(`- ${change.refreshType} ${change.changeType} ${change.path}`);
|
|
1223
|
-
}
|
|
1224
|
-
if (status.changes.length > 20) {
|
|
1225
|
-
log(`... and ${status.changes.length - 20} more`);
|
|
1226
|
-
}
|
|
1227
|
-
}
|
|
1228
|
-
log(`State: ${status.stale ? "stale" : "fresh"}`);
|
|
1229
|
-
if (status.recommendedCommand) {
|
|
1230
|
-
log(`Recommended: ${status.recommendedCommand}`);
|
|
1231
|
-
}
|
|
1232
|
-
});
|
|
1248
|
+
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);
|
|
1233
1249
|
graph.command("stats").description("Summarize compiled graph counts, node types, evidence classes, and relation mix.").action(async () => {
|
|
1234
1250
|
const stats = await graphStatsVault(process2.cwd());
|
|
1235
1251
|
if (isJson()) {
|
|
@@ -1270,20 +1286,7 @@ graph.command("validate").description("Validate a compiled graph artifact for da
|
|
|
1270
1286
|
process2.exitCode = 1;
|
|
1271
1287
|
}
|
|
1272
1288
|
});
|
|
1273
|
-
graph.command("cluster").alias("clusters").description("Recompute graph communities, degrees, god-node flags, and graph report artifacts without re-ingesting sources.").option("--resolution <number>", "Override the Louvain community resolution for this run").action(
|
|
1274
|
-
const resolution = parsePositiveNumber(options.resolution);
|
|
1275
|
-
if (options.resolution && resolution === void 0) {
|
|
1276
|
-
throw new Error("--resolution must be a positive number.");
|
|
1277
|
-
}
|
|
1278
|
-
const result = await refreshGraphClusters(process2.cwd(), { resolution });
|
|
1279
|
-
if (isJson()) {
|
|
1280
|
-
emitJson(result);
|
|
1281
|
-
return;
|
|
1282
|
-
}
|
|
1283
|
-
log(
|
|
1284
|
-
`Refreshed ${result.communityCount} communities across ${result.nodeCount} nodes and ${result.edgeCount} edges. Report: ${result.reportPath}`
|
|
1285
|
-
);
|
|
1286
|
-
});
|
|
1289
|
+
graph.command("cluster").alias("clusters").description("Recompute graph communities, degrees, god-node flags, and graph report artifacts without re-ingesting sources.").option("--resolution <number>", "Override the Louvain community resolution for this run").action((options) => runGraphClusterCommand(options));
|
|
1287
1290
|
graphPush.command("neo4j").description("Push the compiled graph directly into Neo4j over Bolt/Aura.").option("--uri <bolt-uri>", "Neo4j Bolt or Aura URI").option("--username <user>", "Neo4j username").option("--password-env <env-var>", "Environment variable containing the Neo4j password").option("--database <name>", "Neo4j database name").option("--vault-id <id>", "Stable vault identifier used for shared-database namespacing").option("--batch-size <n>", "Maximum rows to write per Neo4j transaction batch").option("--include-third-party", "Also push third-party repo material", false).option("--include-resources", "Also push resource-like content", false).option("--include-generated", "Also push generated output", false).option("--dry-run", "Show what would be pushed without writing to Neo4j", false).action(
|
|
1288
1291
|
async (options) => {
|
|
1289
1292
|
const batchSize = typeof options.batchSize === "string" && options.batchSize.trim() ? parsePositiveInt(options.batchSize, 0) || void 0 : void 0;
|
|
@@ -1339,7 +1342,7 @@ graph.command("serve").description("Serve the local graph viewer.").option("--po
|
|
|
1339
1342
|
});
|
|
1340
1343
|
graph.command("export").description(
|
|
1341
1344
|
"Export the graph as HTML, report, SVG, GraphML, Cypher, JSON, Obsidian vault, or Obsidian canvas. Combine flags to write multiple formats in one run."
|
|
1342
|
-
).option("--html <output>", "Output HTML file path").option("--html-standalone <output>", "Output lightweight standalone HTML file path (vis.js, no build tooling)").option("--report <output>", "Output self-contained HTML report (graph stats, key nodes, communities)").option("--svg <output>", "Output SVG file path").option("--graphml <output>", "Output GraphML file path").option("--cypher <output>", "Output Cypher file path").option("--json <output>", "Output JSON file path").option("--obsidian <output>", "Output Obsidian vault directory path").option("--canvas <output>", "Output Obsidian canvas file path").option("--full", "Include the full graph in HTML export (default; queries traverse complete graph)", true).option("--overview", "Use overview sampling for HTML export (smaller file, queries limited to sampled nodes)", false).action(
|
|
1345
|
+
).option("--html <output>", "Output HTML file path").option("--html-standalone <output>", "Output lightweight standalone HTML file path (vis.js, no build tooling)").option("--report <output>", "Output self-contained HTML report (graph stats, key nodes, communities)").option("--svg <output>", "Output SVG file path").option("--graphml <output>", "Output GraphML file path").option("--cypher <output>", "Output Cypher file path").option("--neo4j <output>", "Compatibility alias for --cypher, writing a Neo4j Cypher import file").option("--json <output>", "Output JSON file path").option("--obsidian <output>", "Output Obsidian vault directory path").option("--canvas <output>", "Output Obsidian canvas file path").option("--full", "Include the full graph in HTML export (default; queries traverse complete graph)", true).option("--overview", "Use overview sampling for HTML export (smaller file, queries limited to sampled nodes)", false).action(
|
|
1343
1346
|
async (options) => {
|
|
1344
1347
|
const useFullGraph = options.overview ? false : options.full ?? true;
|
|
1345
1348
|
const targets = [
|
|
@@ -1349,13 +1352,14 @@ graph.command("export").description(
|
|
|
1349
1352
|
options.svg ? { format: "svg", outputPath: options.svg } : null,
|
|
1350
1353
|
options.graphml ? { format: "graphml", outputPath: options.graphml } : null,
|
|
1351
1354
|
options.cypher ? { format: "cypher", outputPath: options.cypher } : null,
|
|
1355
|
+
options.neo4j ? { format: "cypher", outputPath: options.neo4j } : null,
|
|
1352
1356
|
options.json ? { format: "json", outputPath: options.json } : null,
|
|
1353
1357
|
options.obsidian ? { format: "obsidian", outputPath: options.obsidian } : null,
|
|
1354
1358
|
options.canvas ? { format: "canvas", outputPath: options.canvas } : null
|
|
1355
1359
|
].filter((target) => Boolean(target));
|
|
1356
1360
|
if (targets.length === 0) {
|
|
1357
1361
|
throw new Error(
|
|
1358
|
-
"Pass at least one of --html, --html-standalone, --report, --svg, --graphml, --cypher, --json, --obsidian, or --canvas."
|
|
1362
|
+
"Pass at least one of --html, --html-standalone, --report, --svg, --graphml, --cypher, --neo4j, --json, --obsidian, or --canvas."
|
|
1359
1363
|
);
|
|
1360
1364
|
}
|
|
1361
1365
|
const results = [];
|
|
@@ -1776,6 +1780,11 @@ async function showWatchStatus() {
|
|
|
1776
1780
|
}
|
|
1777
1781
|
watch.command("status").description("Show the latest watch run plus pending semantic refresh entries.").action(showWatchStatus);
|
|
1778
1782
|
program.command("watch-status").description("Show the latest watch run plus pending semantic refresh entries.").action(showWatchStatus);
|
|
1783
|
+
program.command("check-update").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);
|
|
1784
|
+
program.command("update").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);
|
|
1785
|
+
program.command("cluster-only").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(
|
|
1786
|
+
(vaultPath, options) => runGraphClusterCommand(options, vaultPath ? path2.resolve(process2.cwd(), vaultPath) : process2.cwd())
|
|
1787
|
+
);
|
|
1779
1788
|
var hook = program.command("hook").description("Install local git hooks that keep tracked repos and the vault in sync.");
|
|
1780
1789
|
hook.command("install").description("Install post-commit and post-checkout hooks for the nearest git repository.").action(async () => {
|
|
1781
1790
|
const status = await installGitHooks(process2.cwd());
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmvaultai/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.10.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.10.0",
|
|
48
48
|
"commander": "^14.0.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|