@swarmvaultai/cli 0.1.27 → 0.1.29
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 +26 -4
- package/dist/index.js +63 -47
- package/package.json +8 -8
- package/LICENSE +0 -21
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`@swarmvaultai/cli` is the global command-line entry point for SwarmVault.
|
|
4
4
|
|
|
5
|
-
It gives you the `swarmvault` command for building a local-first knowledge vault from files, URLs, browser clips, saved query outputs, and guided exploration runs.
|
|
5
|
+
It gives you the `swarmvault` command for building a local-first knowledge vault from files, DOCX documents, URLs, browser clips, saved query outputs, and guided exploration runs.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
@@ -70,6 +70,7 @@ Ingest a local file path, directory path, or URL into immutable source storage a
|
|
|
70
70
|
- directory ingest respects `.gitignore` unless you pass `--no-gitignore`
|
|
71
71
|
- repo-aware directory ingest records `repoRelativePath` and later compile writes `state/code-index.json`
|
|
72
72
|
- URL ingest still localizes remote image references by default
|
|
73
|
+
- local file ingest supports markdown, text, HTML, PDF, DOCX, images, and code
|
|
73
74
|
- code-aware directory ingest currently covers JavaScript, TypeScript, Python, Go, Rust, Java, C#, C, C++, PHP, Ruby, and PowerShell
|
|
74
75
|
|
|
75
76
|
Useful flags:
|
|
@@ -101,7 +102,7 @@ Capture supported URLs through a normalized markdown layer before ingesting them
|
|
|
101
102
|
|
|
102
103
|
### `swarmvault inbox import [dir]`
|
|
103
104
|
|
|
104
|
-
Import supported files from the configured inbox directory. This is meant for browser-clipper style markdown bundles and other capture workflows. Local image and asset references are preserved and copied into canonical storage under `raw/assets/`.
|
|
105
|
+
Import supported files from the configured inbox directory. This is meant for browser-clipper style markdown bundles, HTML clip bundles, and other capture workflows. Local image and asset references are preserved and copied into canonical storage under `raw/assets/`.
|
|
105
106
|
|
|
106
107
|
### `swarmvault compile [--approve]`
|
|
107
108
|
|
|
@@ -255,16 +256,37 @@ Export the current graph as one of four formats:
|
|
|
255
256
|
- `--graphml` for graph-tool interoperability
|
|
256
257
|
- `--cypher` for Neo4j-style import scripts
|
|
257
258
|
|
|
258
|
-
### `swarmvault install --agent <codex|claude|cursor|goose|pi|gemini|opencode>`
|
|
259
|
+
### `swarmvault install --agent <codex|claude|cursor|goose|pi|gemini|opencode|aider|copilot>`
|
|
259
260
|
|
|
260
261
|
Install agent-specific rules into the current project so an agent understands the SwarmVault workspace contract and workflow.
|
|
261
262
|
|
|
262
|
-
|
|
263
|
+
Hook-capable installs:
|
|
263
264
|
|
|
264
265
|
```bash
|
|
265
266
|
swarmvault install --agent claude --hook
|
|
267
|
+
swarmvault install --agent gemini --hook
|
|
268
|
+
swarmvault install --agent opencode --hook
|
|
269
|
+
swarmvault install --agent copilot --hook
|
|
266
270
|
```
|
|
267
271
|
|
|
272
|
+
Agent target mapping:
|
|
273
|
+
|
|
274
|
+
- `codex`, `goose`, `pi`, and `opencode` share `AGENTS.md`
|
|
275
|
+
- `claude` writes `CLAUDE.md`
|
|
276
|
+
- `gemini` writes `GEMINI.md`
|
|
277
|
+
- `aider` writes `CONVENTIONS.md` and merges `.aider.conf.yml`
|
|
278
|
+
- `copilot` writes `.github/copilot-instructions.md` plus `AGENTS.md`
|
|
279
|
+
- `cursor` writes `.cursor/rules/swarmvault.mdc`
|
|
280
|
+
|
|
281
|
+
Hook semantics:
|
|
282
|
+
|
|
283
|
+
- `claude --hook` writes `.claude/settings.json` plus `.claude/hooks/swarmvault-graph-first.js` and adds model-visible advisory context through structured hook JSON
|
|
284
|
+
- `gemini --hook` writes `.gemini/settings.json` plus `.gemini/hooks/swarmvault-graph-first.js` and stays advisory/model-visible
|
|
285
|
+
- `opencode --hook` writes `.opencode/plugins/swarmvault-graph-first.js` and stays advisory/log-only
|
|
286
|
+
- `copilot --hook` writes `.github/hooks/swarmvault-graph-first.json` plus `.github/hooks/swarmvault-graph-first.js` and remains decision-based rather than advisory
|
|
287
|
+
|
|
288
|
+
`aider` is intentionally file/config-based in this release rather than hook-based.
|
|
289
|
+
|
|
268
290
|
## Provider Configuration
|
|
269
291
|
|
|
270
292
|
SwarmVault defaults to a local `heuristic` provider so the CLI works without API keys, but real vaults will usually point at an actual model provider.
|
package/dist/index.js
CHANGED
|
@@ -216,11 +216,16 @@ program.name("swarmvault").description("SwarmVault is a local-first LLM wiki com
|
|
|
216
216
|
function readCliVersion() {
|
|
217
217
|
try {
|
|
218
218
|
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
219
|
-
return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "0.1.
|
|
219
|
+
return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "0.1.29";
|
|
220
220
|
} catch {
|
|
221
|
-
return "0.1.
|
|
221
|
+
return "0.1.29";
|
|
222
222
|
}
|
|
223
223
|
}
|
|
224
|
+
function parsePositiveInt(value, fallback) {
|
|
225
|
+
if (value === void 0) return fallback;
|
|
226
|
+
const parsed = Number.parseInt(value, 10);
|
|
227
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
228
|
+
}
|
|
224
229
|
function isJson() {
|
|
225
230
|
return program.opts().json === true;
|
|
226
231
|
}
|
|
@@ -273,8 +278,8 @@ program.command("init").description("Initialize a SwarmVault workspace in the cu
|
|
|
273
278
|
});
|
|
274
279
|
program.command("ingest").description("Ingest a local file path, directory path, or URL into the raw SwarmVault workspace.").argument("<input>", "Local file path, directory path, or URL").option("--include-assets", "Download remote image assets when ingesting URLs", true).option("--no-include-assets", "Skip downloading remote image assets when ingesting URLs").option("--max-asset-size <bytes>", "Maximum number of bytes to fetch for a single remote image asset").option("--repo-root <path>", "Override the detected repo root when ingesting a directory").option("--include <glob...>", "Only ingest files matching one or more glob patterns").option("--exclude <glob...>", "Skip files matching one or more glob patterns").option("--max-files <n>", "Maximum number of files to ingest from a directory").option("--include-third-party", "Also ingest repo files classified as third-party", false).option("--include-resources", "Also ingest repo files classified as resources", false).option("--include-generated", "Also ingest repo files classified as generated output", false).option("--no-gitignore", "Ignore .gitignore rules when ingesting a directory").action(
|
|
275
280
|
async (input, options) => {
|
|
276
|
-
const maxAssetSize = typeof options.maxAssetSize === "string" && options.maxAssetSize.trim() ?
|
|
277
|
-
const maxFiles = typeof options.maxFiles === "string" && options.maxFiles.trim() ?
|
|
281
|
+
const maxAssetSize = typeof options.maxAssetSize === "string" && options.maxAssetSize.trim() ? parsePositiveInt(options.maxAssetSize, 0) || void 0 : void 0;
|
|
282
|
+
const maxFiles = typeof options.maxFiles === "string" && options.maxFiles.trim() ? parsePositiveInt(options.maxFiles, 0) || void 0 : void 0;
|
|
278
283
|
const extractClasses = [
|
|
279
284
|
"first_party",
|
|
280
285
|
...options.includeThirdParty ? ["third_party"] : [],
|
|
@@ -283,11 +288,11 @@ program.command("ingest").description("Ingest a local file path, directory path,
|
|
|
283
288
|
];
|
|
284
289
|
const commonOptions = {
|
|
285
290
|
includeAssets: options.includeAssets,
|
|
286
|
-
maxAssetSize
|
|
291
|
+
maxAssetSize,
|
|
287
292
|
repoRoot: options.repoRoot,
|
|
288
293
|
include: options.include,
|
|
289
294
|
exclude: options.exclude,
|
|
290
|
-
maxFiles
|
|
295
|
+
maxFiles,
|
|
291
296
|
gitignore: options.gitignore,
|
|
292
297
|
extractClasses
|
|
293
298
|
};
|
|
@@ -366,10 +371,10 @@ program.command("query").description("Query the compiled SwarmVault wiki.").argu
|
|
|
366
371
|
program.command("explore").description("Run a save-first multi-step exploration loop against the vault.").argument("<question>", "Root question to explore").option("--steps <n>", "Maximum number of exploration steps", "3").addOption(
|
|
367
372
|
new Option("--format <format>", "Output format for step pages").choices(["markdown", "report", "slides", "chart", "image"]).default("markdown")
|
|
368
373
|
).action(async (question, options) => {
|
|
369
|
-
const stepCount =
|
|
374
|
+
const stepCount = parsePositiveInt(options.steps, 3);
|
|
370
375
|
const result = await exploreVault(process2.cwd(), {
|
|
371
376
|
question,
|
|
372
|
-
steps:
|
|
377
|
+
steps: stepCount,
|
|
373
378
|
format: options.format
|
|
374
379
|
});
|
|
375
380
|
if (isJson()) {
|
|
@@ -410,7 +415,7 @@ program.command("lint").description("Run anti-drift and wiki-health checks.").op
|
|
|
410
415
|
});
|
|
411
416
|
var graph = program.command("graph").description("Graph-related commands.");
|
|
412
417
|
graph.command("serve").description("Serve the local graph viewer.").option("--port <port>", "Port override").action(async (options) => {
|
|
413
|
-
const port = options.port ?
|
|
418
|
+
const port = options.port ? parsePositiveInt(options.port, 0) || void 0 : void 0;
|
|
414
419
|
const server = await startGraphServer(process2.cwd(), port);
|
|
415
420
|
if (isJson()) {
|
|
416
421
|
emitJson({ port: server.port, url: `http://localhost:${server.port}` });
|
|
@@ -418,7 +423,10 @@ graph.command("serve").description("Serve the local graph viewer.").option("--po
|
|
|
418
423
|
log(`Graph viewer running at http://localhost:${server.port}`);
|
|
419
424
|
}
|
|
420
425
|
process2.on("SIGINT", async () => {
|
|
421
|
-
|
|
426
|
+
try {
|
|
427
|
+
await server.close();
|
|
428
|
+
} catch {
|
|
429
|
+
}
|
|
422
430
|
process2.exit(0);
|
|
423
431
|
});
|
|
424
432
|
});
|
|
@@ -441,10 +449,10 @@ graph.command("export").description("Export the graph as HTML, SVG, GraphML, or
|
|
|
441
449
|
}
|
|
442
450
|
});
|
|
443
451
|
graph.command("query").description("Traverse the compiled graph deterministically from local search seeds.").argument("<question>", "Question or graph search seed").option("--dfs", "Prefer a depth-first traversal instead of breadth-first", false).option("--budget <n>", "Maximum number of graph nodes to summarize").action(async (question, options) => {
|
|
444
|
-
const budget = options.budget ?
|
|
452
|
+
const budget = options.budget ? parsePositiveInt(options.budget, 0) || void 0 : void 0;
|
|
445
453
|
const result = await queryGraphVault(process2.cwd(), question, {
|
|
446
454
|
traversal: options.dfs ? "dfs" : "bfs",
|
|
447
|
-
budget
|
|
455
|
+
budget
|
|
448
456
|
});
|
|
449
457
|
if (isJson()) {
|
|
450
458
|
emitJson(result);
|
|
@@ -469,8 +477,8 @@ graph.command("explain").description("Explain a graph node, its page, community,
|
|
|
469
477
|
log(result.summary);
|
|
470
478
|
});
|
|
471
479
|
graph.command("god-nodes").description("List the highest-connectivity non-source graph nodes.").option("--limit <n>", "Maximum number of nodes to return", "10").action(async (options) => {
|
|
472
|
-
const limit =
|
|
473
|
-
const result = await listGodNodes(process2.cwd(),
|
|
480
|
+
const limit = parsePositiveInt(options.limit, 10);
|
|
481
|
+
const result = await listGodNodes(process2.cwd(), limit);
|
|
474
482
|
if (isJson()) {
|
|
475
483
|
emitJson(result);
|
|
476
484
|
return;
|
|
@@ -555,12 +563,12 @@ candidate.command("archive").description("Archive a candidate by removing it fro
|
|
|
555
563
|
}
|
|
556
564
|
});
|
|
557
565
|
var watch = program.command("watch").description("Watch the inbox directory and optionally tracked repos, or run one refresh cycle immediately.").option("--lint", "Run lint after each compile cycle", false).option("--repo", "Also refresh tracked repo sources and watch their repo roots", false).option("--once", "Run one import/refresh cycle immediately instead of starting a watcher", false).option("--debounce <ms>", "Debounce window in milliseconds", "900").action(async (options) => {
|
|
558
|
-
const debounceMs =
|
|
566
|
+
const debounceMs = parsePositiveInt(options.debounce, 900);
|
|
559
567
|
if (options.once) {
|
|
560
568
|
const result = await runWatchCycle(process2.cwd(), {
|
|
561
569
|
lint: options.lint ?? false,
|
|
562
570
|
repo: options.repo ?? false,
|
|
563
|
-
debounceMs
|
|
571
|
+
debounceMs
|
|
564
572
|
});
|
|
565
573
|
if (isJson()) {
|
|
566
574
|
emitJson(result);
|
|
@@ -575,7 +583,7 @@ var watch = program.command("watch").description("Watch the inbox directory and
|
|
|
575
583
|
const controller = await watchVault(process2.cwd(), {
|
|
576
584
|
lint: options.lint ?? false,
|
|
577
585
|
repo: options.repo ?? false,
|
|
578
|
-
debounceMs
|
|
586
|
+
debounceMs
|
|
579
587
|
});
|
|
580
588
|
if (isJson()) {
|
|
581
589
|
emitJson({ status: "watching", inboxDir: paths.inboxDir, repo: options.repo ?? false });
|
|
@@ -583,11 +591,14 @@ var watch = program.command("watch").description("Watch the inbox directory and
|
|
|
583
591
|
log(`Watching inbox${options.repo ? " and tracked repos" : ""} for changes. Press Ctrl+C to stop.`);
|
|
584
592
|
}
|
|
585
593
|
process2.on("SIGINT", async () => {
|
|
586
|
-
|
|
594
|
+
try {
|
|
595
|
+
await controller.close();
|
|
596
|
+
} catch {
|
|
597
|
+
}
|
|
587
598
|
process2.exit(0);
|
|
588
599
|
});
|
|
589
600
|
});
|
|
590
|
-
|
|
601
|
+
async function showWatchStatus() {
|
|
591
602
|
const result = await getWatchStatus(process2.cwd());
|
|
592
603
|
if (isJson()) {
|
|
593
604
|
emitJson(result);
|
|
@@ -598,19 +609,9 @@ watch.command("status").description("Show the latest watch run plus pending sema
|
|
|
598
609
|
for (const entry of result.pendingSemanticRefresh.slice(0, 8)) {
|
|
599
610
|
log(`- ${entry.changeType} ${entry.path}`);
|
|
600
611
|
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
if (isJson()) {
|
|
605
|
-
emitJson(result);
|
|
606
|
-
return;
|
|
607
|
-
}
|
|
608
|
-
log(`Watched repo roots: ${result.watchedRepoRoots.length}`);
|
|
609
|
-
log(`Pending semantic refresh: ${result.pendingSemanticRefresh.length}`);
|
|
610
|
-
for (const entry of result.pendingSemanticRefresh.slice(0, 8)) {
|
|
611
|
-
log(`- ${entry.changeType} ${entry.path}`);
|
|
612
|
-
}
|
|
613
|
-
});
|
|
612
|
+
}
|
|
613
|
+
watch.command("status").description("Show the latest watch run plus pending semantic refresh entries.").action(showWatchStatus);
|
|
614
|
+
program.command("watch-status").description("Show the latest watch run plus pending semantic refresh entries.").action(showWatchStatus);
|
|
614
615
|
var hook = program.command("hook").description("Install local git hooks that keep tracked repos and the vault in sync.");
|
|
615
616
|
hook.command("install").description("Install post-commit and post-checkout hooks for the nearest git repository.").action(async () => {
|
|
616
617
|
const status = await installGitHooks(process2.cwd());
|
|
@@ -670,15 +671,18 @@ schedule.command("run").description("Run one configured schedule job immediately
|
|
|
670
671
|
);
|
|
671
672
|
});
|
|
672
673
|
schedule.command("serve").description("Run the local schedule loop.").option("--poll <ms>", "Polling interval in milliseconds", "30000").action(async (options) => {
|
|
673
|
-
const pollMs =
|
|
674
|
-
const controller = await serveSchedules(process2.cwd(),
|
|
674
|
+
const pollMs = parsePositiveInt(options.poll, 3e4);
|
|
675
|
+
const controller = await serveSchedules(process2.cwd(), pollMs);
|
|
675
676
|
if (isJson()) {
|
|
676
|
-
emitJson({ status: "serving", pollMs
|
|
677
|
+
emitJson({ status: "serving", pollMs });
|
|
677
678
|
} else {
|
|
678
679
|
log("Serving schedules. Press Ctrl+C to stop.");
|
|
679
680
|
}
|
|
680
681
|
process2.on("SIGINT", async () => {
|
|
681
|
-
|
|
682
|
+
try {
|
|
683
|
+
await controller.close();
|
|
684
|
+
} catch {
|
|
685
|
+
}
|
|
682
686
|
process2.exit(0);
|
|
683
687
|
});
|
|
684
688
|
});
|
|
@@ -689,21 +693,33 @@ program.command("mcp").description("Run SwarmVault as a local MCP server over st
|
|
|
689
693
|
}
|
|
690
694
|
const controller = await startMcpServer(process2.cwd());
|
|
691
695
|
process2.on("SIGINT", async () => {
|
|
692
|
-
|
|
696
|
+
try {
|
|
697
|
+
await controller.close();
|
|
698
|
+
} catch {
|
|
699
|
+
}
|
|
693
700
|
process2.exit(0);
|
|
694
701
|
});
|
|
695
702
|
});
|
|
696
|
-
program.command("install").description("Install SwarmVault instructions for an agent in the current project.").requiredOption("--agent <agent>", "codex, claude, cursor, goose, pi, gemini, or
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
703
|
+
program.command("install").description("Install SwarmVault instructions for an agent in the current project.").requiredOption("--agent <agent>", "codex, claude, cursor, goose, pi, gemini, opencode, aider, or copilot").option("--hook", "Also install hook/plugin guidance when the target agent supports it", false).action(
|
|
704
|
+
async (options) => {
|
|
705
|
+
const hookCapableAgents = /* @__PURE__ */ new Set(["claude", "opencode", "gemini", "copilot"]);
|
|
706
|
+
if (options.hook && !hookCapableAgents.has(options.agent)) {
|
|
707
|
+
throw new Error("--hook is only supported for --agent claude, opencode, gemini, or copilot");
|
|
708
|
+
}
|
|
709
|
+
const result = await installAgent(process2.cwd(), options.agent, { hook: options.hook ?? false });
|
|
710
|
+
if (isJson()) {
|
|
711
|
+
emitJson({ ...result, hook: options.hook ?? false });
|
|
712
|
+
} else {
|
|
713
|
+
log(`Installed rules into ${result.target}`);
|
|
714
|
+
if (result.targets.length > 1) {
|
|
715
|
+
log(`Also wrote: ${result.targets.filter((entry) => entry !== result.target).join(", ")}`);
|
|
716
|
+
}
|
|
717
|
+
for (const warning of result.warnings ?? []) {
|
|
718
|
+
emitNotice(warning);
|
|
719
|
+
}
|
|
720
|
+
}
|
|
705
721
|
}
|
|
706
|
-
|
|
722
|
+
);
|
|
707
723
|
program.parseAsync(process2.argv).catch((error) => {
|
|
708
724
|
const message = error instanceof Error ? error.message : String(error);
|
|
709
725
|
if (isJson()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmvaultai/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.29",
|
|
4
4
|
"description": "Global CLI for SwarmVault.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -37,18 +37,18 @@
|
|
|
37
37
|
"engines": {
|
|
38
38
|
"node": ">=24.0.0"
|
|
39
39
|
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"build": "tsup src/index.ts --format esm --dts",
|
|
42
|
+
"test": "vitest run",
|
|
43
|
+
"typecheck": "tsc --noEmit"
|
|
44
|
+
},
|
|
40
45
|
"dependencies": {
|
|
41
|
-
"@swarmvaultai/engine": "0.1.
|
|
46
|
+
"@swarmvaultai/engine": "0.1.29",
|
|
42
47
|
"commander": "^14.0.1"
|
|
43
48
|
},
|
|
44
49
|
"devDependencies": {
|
|
45
50
|
"@types/node": "^24.6.0",
|
|
46
51
|
"tsup": "^8.5.0",
|
|
47
52
|
"vitest": "^3.2.4"
|
|
48
|
-
},
|
|
49
|
-
"scripts": {
|
|
50
|
-
"build": "tsup src/index.ts --format esm --dts",
|
|
51
|
-
"test": "vitest run",
|
|
52
|
-
"typecheck": "tsc --noEmit"
|
|
53
53
|
}
|
|
54
|
-
}
|
|
54
|
+
}
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 SwarmVault
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|