@swarmvaultai/cli 1.4.0 → 3.1.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.
Files changed (3) hide show
  1. package/README.md +68 -10
  2. package/dist/index.js +297 -9
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -44,6 +44,9 @@ swarmvault graph share --svg ./share-card.svg
44
44
  swarmvault graph share --bundle ./share-kit
45
45
  swarmvault benchmark
46
46
  swarmvault query "What keeps recurring?" --commit
47
+ swarmvault context build "Ship this feature safely" --target ./src --budget 8000
48
+ swarmvault task start "Ship this feature safely" --target ./src --agent codex
49
+ swarmvault retrieval status
47
50
  swarmvault query "Turn this into slides" --format slides
48
51
  swarmvault explore "What should I research next?" --steps 3
49
52
  swarmvault lint --deep
@@ -211,7 +214,7 @@ Compile the current manifests into:
211
214
 
212
215
  - generated markdown in `wiki/`
213
216
  - structured graph data in `state/graph.json`
214
- - local search data in `state/search.sqlite`
217
+ - local retrieval data in `state/retrieval/`
215
218
  - share cards at `wiki/graph/share-card.md` and `wiki/graph/share-card.svg`, plus a portable share kit at `wiki/graph/share-kit/`
216
219
 
217
220
  The compiler also reads `swarmvault.schema.md` and records a `schema_hash` plus lifecycle metadata such as `status`, `created_at`, `updated_at`, `compiled_from`, and `managed_by` in generated pages so schema edits can mark pages stale without losing lifecycle state.
@@ -270,13 +273,47 @@ By default, the answer is written into `wiki/outputs/` and immediately registere
270
273
  - `wiki/index.md`
271
274
  - `wiki/outputs/index.md`
272
275
  - `state/graph.json`
273
- - `state/search.sqlite`
276
+ - `state/retrieval/`
274
277
 
275
278
  Saved outputs also carry related page, node, and source metadata so SwarmVault can refresh related source, concept, and entity pages immediately.
276
279
 
277
280
  Human-authored pages in `wiki/insights/` are also indexed into search and query context, but SwarmVault does not rewrite them after initialization.
278
281
 
279
- By default, query uses the local SQLite search index. When an embedding-capable provider is available and `search.hybrid` is not disabled, semantic page matches are fused into the same candidate set before answer generation. `tasks.embeddingProvider` is the explicit way to choose that backend, but SwarmVault can also fall back to a `queryProvider` with embeddings support. Set `search.rerank: true` when you want the configured `queryProvider` to rerank the merged top hits. `--commit` immediately commits saved `wiki/` and `state/` changes when the vault root is inside a git repo.
282
+ By default, query uses the local SQLite retrieval index. When an embedding-capable provider is available and `retrieval.hybrid` is not disabled, semantic page matches are fused into the same candidate set before answer generation. `tasks.embeddingProvider` is the explicit way to choose that backend, but SwarmVault can also fall back to a `queryProvider` with embeddings support. Set `retrieval.rerank: true` when you want the configured `queryProvider` to rerank the merged top hits. `--commit` immediately commits saved `wiki/` and `state/` changes when the vault root is inside a git repo.
283
+
284
+ ### `swarmvault context build|list|show|delete`
285
+
286
+ Build and manage agent-ready context packs from the compiled vault.
287
+
288
+ - `context build "<goal>"` assembles relevant pages, graph nodes, edges, hyperedges, citations, and explicit omitted entries into a bounded bundle
289
+ - `--target <path-or-node>` anchors the pack around a file, page id, node id, or graph label
290
+ - `--task <id>` links the newly built context pack to an active task
291
+ - `--memory <id>` remains a compatibility alias for `--task`
292
+ - `--budget <tokens>` caps the estimated token budget; over-budget candidates are listed in `omittedItems`
293
+ - `--format markdown|json|llms` controls the printed output shape, while every pack is still saved as JSON
294
+ - saved artifacts live under `state/context-packs/`, with companion markdown pages under `wiki/context/`
295
+ - `context list`, `context show <id>`, and `context delete <id>` manage saved packs
296
+
297
+ Use this before handing work to an agent, starting a PR review, or preserving the evidence bundle behind a design/debugging decision.
298
+
299
+ ### `swarmvault task start|update|finish|list|show|resume`
300
+
301
+ Record a durable local task ledger for agent work.
302
+
303
+ - `task start "<goal>" --target <path-or-node>` creates `state/memory/tasks/<id>.json`, `wiki/memory/tasks/<id>.md`, updates `wiki/memory/index.md`, and builds an initial context pack
304
+ - `task update <id>` records notes, decisions, changed paths, context packs, sessions, sources, pages, nodes, git refs, or status changes
305
+ - `task finish <id> --outcome <text>` marks the task completed and can add one or more `--follow-up` entries
306
+ - `task list`, `task show <id>`, and `task resume <id> --format markdown|json|llms` expose the task history for the next agent
307
+
308
+ `query`, `explore`, and `context build` accept `--task <id>` so saved outputs and context packs can attach to an active task. The 2.0 `memory` command group and `--memory <id>` flag remain compatibility aliases.
309
+
310
+ ### `swarmvault retrieval status|rebuild|doctor`
311
+
312
+ Inspect and maintain the local retrieval index under `state/retrieval/`.
313
+
314
+ - `retrieval status` reports backend, configured hybrid/rerank behavior, manifest freshness, page count, and the current SQLite shard path
315
+ - `retrieval rebuild` rebuilds the local shard from current wiki pages and refreshes `state/retrieval/manifest.json`
316
+ - `retrieval doctor` checks for stale or missing retrieval artifacts; add `--repair` to rebuild missing or stale artifacts immediately
280
317
 
281
318
  ### `swarmvault explore "<question>" [--steps <n>] [--format markdown|report|slides|chart|image]`
282
319
 
@@ -345,10 +382,28 @@ Run SwarmVault as a local MCP server over stdio. This exposes the vault to compa
345
382
  - `compile_vault`
346
383
  - `lint_vault`
347
384
  - `blast_radius`
348
-
349
- `compile_vault` also accepts `maxTokens` for bounded wiki output, and `blast_radius` traces reverse import impact for a file or module target.
350
-
351
- The MCP surface also exposes `swarmvault://schema`, `swarmvault://sessions`, `swarmvault://sessions/{path}`, and includes `schemaPath` in `workspace_info`.
385
+ - `build_context_pack`
386
+ - `list_context_packs`
387
+ - `read_context_pack`
388
+ - `start_task`
389
+ - `update_task`
390
+ - `finish_task`
391
+ - `list_tasks`
392
+ - `read_task`
393
+ - `resume_task`
394
+ - `start_memory_task`
395
+ - `update_memory_task`
396
+ - `finish_memory_task`
397
+ - `list_memory_tasks`
398
+ - `read_memory_task`
399
+ - `resume_memory_task`
400
+ - `retrieval_status`
401
+ - `rebuild_retrieval`
402
+ - `doctor_retrieval`
403
+
404
+ `compile_vault` also accepts `maxTokens` for bounded wiki output, `blast_radius` traces reverse import impact for a file or module target, `build_context_pack` creates the same bounded agent evidence bundles as `swarmvault context build`, the task tools mirror `swarmvault task`, the memory tools mirror the compatibility command group, and retrieval tools inspect or repair the local index.
405
+
406
+ The MCP surface also exposes `swarmvault://schema`, `swarmvault://sessions`, `swarmvault://sessions/{path}`, `swarmvault://context-packs`, `swarmvault://tasks`, `swarmvault://memory-tasks`, and includes `schemaPath` in `workspace_info`.
352
407
 
353
408
  ### `swarmvault graph serve`
354
409
 
@@ -539,15 +594,18 @@ Search behavior is configurable separately from provider routing:
539
594
 
540
595
  ```json
541
596
  {
542
- "search": {
597
+ "retrieval": {
598
+ "backend": "sqlite",
599
+ "shardSize": 25000,
543
600
  "hybrid": true,
544
601
  "rerank": false
545
602
  }
546
603
  }
547
604
  ```
548
605
 
549
- - `search.hybrid` defaults to enabled and merges full-text hits with semantic page matches when an embedding-capable provider is available
550
- - `search.rerank` optionally asks the current `queryProvider` to rerank the merged top hits before query answers are generated
606
+ - `retrieval.hybrid` defaults to enabled and merges full-text hits with semantic page matches when an embedding-capable provider is available
607
+ - `retrieval.rerank` optionally asks the current `queryProvider` to rerank the merged top hits before query answers are generated
608
+ - `retrieval.backend` currently supports the local SQLite backend
551
609
 
552
610
  ## Troubleshooting
553
611
 
package/dist/index.js CHANGED
@@ -15,11 +15,14 @@ import {
15
15
  autoCommitWikiChanges,
16
16
  benchmarkVault,
17
17
  blastRadiusVault,
18
+ buildContextPack,
18
19
  buildGraphShareArtifact,
19
20
  compileVault,
20
21
  consolidateVault,
21
22
  createSupersessionEdge,
23
+ deleteContextPack,
22
24
  deleteManagedSource,
25
+ doctorRetrieval,
23
26
  downloadWhisperModel,
24
27
  explainGraphVault,
25
28
  exploreVault,
@@ -28,7 +31,9 @@ import {
28
31
  exportGraphReportHtml,
29
32
  exportObsidianCanvas,
30
33
  exportObsidianVault,
34
+ finishMemoryTask,
31
35
  getGitHookStatus,
36
+ getRetrievalStatus,
32
37
  getWatchStatus,
33
38
  graphDiff,
34
39
  guideManagedSource,
@@ -42,9 +47,11 @@ import {
42
47
  lintVault,
43
48
  listApprovals,
44
49
  listCandidates,
50
+ listContextPacks,
45
51
  listGodNodes,
46
52
  listManagedSourceRecords,
47
53
  listManifests,
54
+ listMemoryTasks,
48
55
  listSchedules,
49
56
  listWatchedRoots,
50
57
  loadVaultConfig,
@@ -55,14 +62,20 @@ import {
55
62
  queryGraphVault,
56
63
  queryVault,
57
64
  readApproval,
65
+ readContextPack,
58
66
  readGraphReport,
67
+ readMemoryTask,
68
+ rebuildRetrievalIndex,
59
69
  registerLocalWhisperProvider,
60
70
  rejectApproval,
61
71
  reloadManagedSources,
62
72
  removeWatchedRoot,
73
+ renderContextPackLlms,
74
+ renderContextPackMarkdown,
63
75
  renderGraphShareBundleFiles,
64
76
  renderGraphShareMarkdown,
65
77
  renderGraphShareSvg,
78
+ resumeMemoryTask,
66
79
  resumeSourceSession,
67
80
  reviewManagedSource,
68
81
  reviewSourceScope,
@@ -73,8 +86,10 @@ import {
73
86
  serveSchedules,
74
87
  startGraphServer,
75
88
  startMcpServer,
89
+ startMemoryTask,
76
90
  summarizeLocalWhisperSetup,
77
91
  uninstallGitHooks,
92
+ updateMemoryTask,
78
93
  watchVault
79
94
  } from "@swarmvaultai/engine";
80
95
  import { Command, Option } from "commander";
@@ -293,9 +308,9 @@ program.name("swarmvault").description("SwarmVault is a local-first knowledge co
293
308
  function readCliVersion() {
294
309
  try {
295
310
  const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
296
- return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "1.4.0";
311
+ return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "3.1.0";
297
312
  } catch {
298
- return "1.4.0";
313
+ return "3.1.0";
299
314
  }
300
315
  }
301
316
  function parsePositiveInt(value, fallback) {
@@ -374,8 +389,8 @@ async function maybeEmitHeuristicNotice(commandPath) {
374
389
  try {
375
390
  const { config } = await loadVaultConfig(process2.cwd());
376
391
  const analysisTaskKeys = ["compileProvider", "queryProvider", "lintProvider"];
377
- const usingHeuristic = analysisTaskKeys.every((task) => {
378
- const providerId = config.tasks[task];
392
+ const usingHeuristic = analysisTaskKeys.every((task2) => {
393
+ const providerId = config.tasks[task2];
379
394
  const providerConfig = config.providers[providerId];
380
395
  return !providerConfig || providerConfig.type === "heuristic";
381
396
  });
@@ -801,7 +816,7 @@ program.command("consolidate").description("Roll working-tier insights up into e
801
816
  log(decision);
802
817
  }
803
818
  });
804
- program.command("query").description("Query the compiled SwarmVault wiki.").argument("<question>", "Question to ask SwarmVault").option("--no-save", "Do not persist the answer to wiki/outputs").option("--commit", "Auto-commit wiki changes after query").option("--gap-fill", "Pull external web-search evidence when the local wiki has gaps (requires webSearch.tasks.queryProvider).").addOption(
819
+ program.command("query").description("Query the compiled SwarmVault wiki.").argument("<question>", "Question to ask SwarmVault").option("--no-save", "Do not persist the answer to wiki/outputs").option("--commit", "Auto-commit wiki changes after query").option("--gap-fill", "Pull external web-search evidence when the local wiki has gaps (requires webSearch.tasks.queryProvider).").option("--task <id>", "Attach this query output to an agent task").option("--memory <id>", "Compatibility alias for --task").addOption(
805
820
  new Option("--format <format>", "Output format").choices(["markdown", "report", "slides", "chart", "image"]).default("markdown")
806
821
  ).action(
807
822
  async (question, options) => {
@@ -809,7 +824,8 @@ program.command("query").description("Query the compiled SwarmVault wiki.").argu
809
824
  question,
810
825
  save: options.save ?? true,
811
826
  format: options.format,
812
- gapFill: options.gapFill ?? false
827
+ gapFill: options.gapFill ?? false,
828
+ memoryTaskId: options.task ?? options.memory
813
829
  });
814
830
  if (isJson()) {
815
831
  emitJson(result);
@@ -826,7 +842,235 @@ program.command("query").description("Query the compiled SwarmVault wiki.").argu
826
842
  await maybeEmitHeuristicNotice(["query"]);
827
843
  }
828
844
  );
829
- 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").option("--gap-fill", "Pull external web-search evidence when the local wiki has gaps (requires webSearch.tasks.exploreProvider).").addOption(
845
+ var context = program.command("context").description("Build and manage token-bounded agent context packs.");
846
+ context.command("build").description("Build a cited, token-bounded context pack for an agent task.").argument("<goal>", "Task, question, or goal the agent needs context for").option("--target <target>", "Optional page, node, path, project, or label to anchor the pack").option("--budget <tokens>", "Approximate token budget for included context", String(8e3)).option("--task <id>", "Attach the context pack to an agent task").option("--memory <id>", "Compatibility alias for --task").addOption(new Option("--format <format>", "Output format").choices(["markdown", "json", "llms"]).default("markdown")).action(
847
+ async (goal, options) => {
848
+ const budgetTokens = parsePositiveInt(options.budget, 8e3);
849
+ const result = await buildContextPack(process2.cwd(), {
850
+ goal,
851
+ target: options.target,
852
+ budgetTokens,
853
+ format: options.format,
854
+ memoryTaskId: options.task ?? options.memory
855
+ });
856
+ if (isJson()) {
857
+ emitJson(result);
858
+ return;
859
+ }
860
+ log(result.rendered);
861
+ log(`Saved context pack to ${result.markdownPath}`);
862
+ log(`Saved context artifact to ${result.artifactPath}`);
863
+ }
864
+ );
865
+ context.command("list").description("List saved context packs.").action(async () => {
866
+ const packs = await listContextPacks(process2.cwd());
867
+ if (isJson()) {
868
+ emitJson(packs);
869
+ return;
870
+ }
871
+ if (!packs.length) {
872
+ log("No context packs.");
873
+ return;
874
+ }
875
+ for (const pack of packs) {
876
+ log(`${pack.id} \u2014 ${pack.goal} (${pack.itemCount} item(s), ${pack.omittedCount} omitted)`);
877
+ }
878
+ });
879
+ context.command("show").description("Print a saved context pack.").argument("<id>", "Context pack id").addOption(new Option("--format <format>", "Output format").choices(["markdown", "json", "llms"]).default("markdown")).action(async (id, options) => {
880
+ const pack = await readContextPack(process2.cwd(), id);
881
+ if (!pack) {
882
+ throw new Error(`Context pack not found: ${id}`);
883
+ }
884
+ if (isJson() || options.format === "json") {
885
+ emitJson(pack);
886
+ return;
887
+ }
888
+ log(options.format === "llms" ? renderContextPackLlms(pack) : renderContextPackMarkdown(pack));
889
+ });
890
+ context.command("delete").description("Delete a saved context pack artifact and markdown page.").argument("<id>", "Context pack id").action(async (id) => {
891
+ const deleted = await deleteContextPack(process2.cwd(), id);
892
+ if (!deleted) {
893
+ throw new Error(`Context pack not found: ${id}`);
894
+ }
895
+ if (isJson()) {
896
+ emitJson(deleted);
897
+ return;
898
+ }
899
+ log(`Deleted context pack ${deleted.id}.`);
900
+ });
901
+ var memory = program.command("memory").description("Manage git-backed agent memory task ledger entries.");
902
+ memory.command("start").description("Start a durable agent memory task and build its initial context pack.").argument("<goal>", "Task goal to preserve in agent memory").option("--target <target>", "Optional page, node, path, project, or label to anchor the initial context pack").option("--budget <tokens>", "Approximate token budget for the initial context pack", String(8e3)).option("--agent <name>", "Agent name to record on the task").option("--context-pack <id>", "Attach an existing context pack instead of building a new one").action(async (goal, options) => {
903
+ const result = await startMemoryTask(process2.cwd(), {
904
+ goal,
905
+ target: options.target,
906
+ budgetTokens: parsePositiveInt(options.budget, 8e3),
907
+ agent: options.agent,
908
+ contextPackId: options.contextPack
909
+ });
910
+ if (isJson()) {
911
+ emitJson(result);
912
+ return;
913
+ }
914
+ log(result.task.id);
915
+ log(`Saved memory task to ${result.markdownPath}`);
916
+ });
917
+ memory.command("update").description("Append a note, decision, path, context pack, or status change to a memory task.").argument("<id>", "Memory task id").option("--note <text>", "Append a task note").option("--decision <text>", "Append a decision").option("--changed-path <path>", "Record a changed file or wiki path").option("--context-pack <id>", "Attach a context pack").option("--session <id>", "Attach a session id").option("--source <id>", "Attach a source id").option("--page <id>", "Attach a page id").option("--node <id>", "Attach a graph node id").option("--git-ref <ref>", "Attach a git ref").addOption(new Option("--status <status>", "Task status").choices(["active", "blocked", "completed", "archived"])).action(
918
+ async (id, options) => {
919
+ const result = await updateMemoryTask(process2.cwd(), id, {
920
+ note: options.note,
921
+ decision: options.decision,
922
+ changedPath: options.changedPath,
923
+ contextPackId: options.contextPack,
924
+ sessionId: options.session,
925
+ sourceId: options.source,
926
+ pageId: options.page,
927
+ nodeId: options.node,
928
+ gitRef: options.gitRef,
929
+ status: options.status
930
+ });
931
+ if (isJson()) {
932
+ emitJson(result);
933
+ return;
934
+ }
935
+ log(`Updated memory task ${result.task.id}.`);
936
+ }
937
+ );
938
+ memory.command("finish").description("Finish a memory task with an outcome and optional follow-up.").argument("<id>", "Memory task id").requiredOption("--outcome <text>", "Outcome to record").option("--follow-up <text>", "Follow-up to preserve for the next agent").action(async (id, options) => {
939
+ const result = await finishMemoryTask(process2.cwd(), id, {
940
+ outcome: options.outcome,
941
+ followUp: options.followUp
942
+ });
943
+ if (isJson()) {
944
+ emitJson(result);
945
+ return;
946
+ }
947
+ log(`Finished memory task ${result.task.id}.`);
948
+ });
949
+ memory.command("list").description("List saved agent memory tasks.").action(async () => {
950
+ const tasks = await listMemoryTasks(process2.cwd());
951
+ if (isJson()) {
952
+ emitJson(tasks);
953
+ return;
954
+ }
955
+ if (!tasks.length) {
956
+ log("No memory tasks.");
957
+ return;
958
+ }
959
+ for (const task2 of tasks) {
960
+ log(`${task2.id} \u2014 ${task2.status} \u2014 ${task2.goal}`);
961
+ }
962
+ });
963
+ memory.command("show").description("Print a saved agent memory task.").argument("<id>", "Memory task id").action(async (id) => {
964
+ const task2 = await readMemoryTask(process2.cwd(), id);
965
+ if (!task2) {
966
+ throw new Error(`Memory task not found: ${id}`);
967
+ }
968
+ if (isJson()) {
969
+ emitJson(task2);
970
+ return;
971
+ }
972
+ log(`Task: ${task2.title}`);
973
+ log(`Status: ${task2.status}`);
974
+ log(`Goal: ${task2.goal}`);
975
+ if (task2.outcome) log(`Outcome: ${task2.outcome}`);
976
+ if (task2.followUps.length) log(`Follow-ups: ${task2.followUps.join("; ")}`);
977
+ log(`Markdown: ${task2.markdownPath}`);
978
+ });
979
+ memory.command("resume").description("Render a memory task handoff for the next agent.").argument("<id>", "Memory task id").addOption(new Option("--format <format>", "Output format").choices(["markdown", "json", "llms"]).default("markdown")).action(async (id, options) => {
980
+ const result = await resumeMemoryTask(process2.cwd(), id, { format: options.format });
981
+ if (isJson() || options.format === "json") {
982
+ emitJson(result);
983
+ return;
984
+ }
985
+ log(result.rendered);
986
+ });
987
+ var task = program.command("task").description("Manage git-backed agent task ledger entries.");
988
+ task.command("start").description("Start a durable agent task and build its initial context pack.").argument("<goal>", "Task goal to preserve").option("--target <target>", "Optional page, node, path, project, or label to anchor the initial context pack").option("--budget <tokens>", "Approximate token budget for the initial context pack", String(8e3)).option("--agent <name>", "Agent name to record on the task").option("--context-pack <id>", "Attach an existing context pack instead of building a new one").action(async (goal, options) => {
989
+ const result = await startMemoryTask(process2.cwd(), {
990
+ goal,
991
+ target: options.target,
992
+ budgetTokens: parsePositiveInt(options.budget, 8e3),
993
+ agent: options.agent,
994
+ contextPackId: options.contextPack
995
+ });
996
+ if (isJson()) {
997
+ emitJson(result);
998
+ return;
999
+ }
1000
+ log(result.task.id);
1001
+ log(`Saved task to ${result.markdownPath}`);
1002
+ });
1003
+ task.command("update").description("Append a note, decision, path, context pack, or status change to a task.").argument("<id>", "Task id").option("--note <text>", "Append a task note").option("--decision <text>", "Append a decision").option("--changed-path <path>", "Record a changed file or wiki path").option("--context-pack <id>", "Attach a context pack").option("--session <id>", "Attach a session id").option("--source <id>", "Attach a source id").option("--page <id>", "Attach a page id").option("--node <id>", "Attach a graph node id").option("--git-ref <ref>", "Attach a git ref").addOption(new Option("--status <status>", "Task status").choices(["active", "blocked", "completed", "archived"])).action(
1004
+ async (id, options) => {
1005
+ const result = await updateMemoryTask(process2.cwd(), id, {
1006
+ note: options.note,
1007
+ decision: options.decision,
1008
+ changedPath: options.changedPath,
1009
+ contextPackId: options.contextPack,
1010
+ sessionId: options.session,
1011
+ sourceId: options.source,
1012
+ pageId: options.page,
1013
+ nodeId: options.node,
1014
+ gitRef: options.gitRef,
1015
+ status: options.status
1016
+ });
1017
+ if (isJson()) {
1018
+ emitJson(result);
1019
+ return;
1020
+ }
1021
+ log(`Updated task ${result.task.id}.`);
1022
+ }
1023
+ );
1024
+ task.command("finish").description("Finish a task with an outcome and optional follow-up.").argument("<id>", "Task id").requiredOption("--outcome <text>", "Outcome to record").option("--follow-up <text>", "Follow-up to preserve for the next agent").action(async (id, options) => {
1025
+ const result = await finishMemoryTask(process2.cwd(), id, {
1026
+ outcome: options.outcome,
1027
+ followUp: options.followUp
1028
+ });
1029
+ if (isJson()) {
1030
+ emitJson(result);
1031
+ return;
1032
+ }
1033
+ log(`Finished task ${result.task.id}.`);
1034
+ });
1035
+ task.command("list").description("List saved agent tasks.").action(async () => {
1036
+ const tasks = await listMemoryTasks(process2.cwd());
1037
+ if (isJson()) {
1038
+ emitJson(tasks);
1039
+ return;
1040
+ }
1041
+ if (!tasks.length) {
1042
+ log("No tasks.");
1043
+ return;
1044
+ }
1045
+ for (const entry of tasks) {
1046
+ log(`${entry.id} \u2014 ${entry.status} \u2014 ${entry.goal}`);
1047
+ }
1048
+ });
1049
+ task.command("show").description("Print a saved agent task.").argument("<id>", "Task id").action(async (id) => {
1050
+ const entry = await readMemoryTask(process2.cwd(), id);
1051
+ if (!entry) {
1052
+ throw new Error(`Task not found: ${id}`);
1053
+ }
1054
+ if (isJson()) {
1055
+ emitJson(entry);
1056
+ return;
1057
+ }
1058
+ log(`Task: ${entry.title}`);
1059
+ log(`Status: ${entry.status}`);
1060
+ log(`Goal: ${entry.goal}`);
1061
+ if (entry.outcome) log(`Outcome: ${entry.outcome}`);
1062
+ if (entry.followUps.length) log(`Follow-ups: ${entry.followUps.join("; ")}`);
1063
+ log(`Markdown: ${entry.markdownPath}`);
1064
+ });
1065
+ task.command("resume").description("Render a task handoff for the next agent.").argument("<id>", "Task id").addOption(new Option("--format <format>", "Output format").choices(["markdown", "json", "llms"]).default("markdown")).action(async (id, options) => {
1066
+ const result = await resumeMemoryTask(process2.cwd(), id, { format: options.format });
1067
+ if (isJson() || options.format === "json") {
1068
+ emitJson(result);
1069
+ return;
1070
+ }
1071
+ log(result.rendered);
1072
+ });
1073
+ 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").option("--gap-fill", "Pull external web-search evidence when the local wiki has gaps (requires webSearch.tasks.exploreProvider).").option("--task <id>", "Attach this exploration to an agent task").option("--memory <id>", "Compatibility alias for --task").addOption(
830
1074
  new Option("--format <format>", "Output format for step pages").choices(["markdown", "report", "slides", "chart", "image"]).default("markdown")
831
1075
  ).action(
832
1076
  async (question, options) => {
@@ -835,7 +1079,8 @@ program.command("explore").description("Run a save-first multi-step exploration
835
1079
  question,
836
1080
  steps: stepCount,
837
1081
  format: options.format,
838
- gapFill: options.gapFill ?? false
1082
+ gapFill: options.gapFill ?? false,
1083
+ memoryTaskId: options.task ?? options.memory
839
1084
  });
840
1085
  if (isJson()) {
841
1086
  emitJson(result);
@@ -1468,7 +1713,7 @@ program.command("mcp").description("Run SwarmVault as a local MCP server over st
1468
1713
  });
1469
1714
  program.command("install").description("Install SwarmVault instructions for an agent in the current project.").requiredOption(
1470
1715
  "--agent <agent>",
1471
- "codex, claude, cursor, goose, pi, gemini, opencode, aider, copilot, trae, claw, droid, kiro, hermes, antigravity, or vscode"
1716
+ "claude, codex, cursor, gemini, goose, opencode, copilot, aider, droid, pi, trae, claw, kiro, hermes, antigravity, vscode, amp, augment, adal, bob, cline, codebuddy, command-code, continue, cortex, crush, deepagents, 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"
1472
1717
  ).option("--hook", "Also install hook/plugin guidance when the target agent supports it", false).action(
1473
1718
  async (options) => {
1474
1719
  const hookCapableAgents = /* @__PURE__ */ new Set(["claude", "opencode", "gemini", "copilot"]);
@@ -1768,6 +2013,49 @@ program.command("diff").description("Show what changed in the knowledge graph si
1768
2013
  }
1769
2014
  }
1770
2015
  });
2016
+ var retrieval = program.command("retrieval").description("Inspect and repair the local retrieval index.");
2017
+ retrieval.command("status").description("Show retrieval index health and configuration.").action(async () => {
2018
+ const status = await getRetrievalStatus(process2.cwd());
2019
+ if (isJson()) {
2020
+ emitJson(status);
2021
+ return;
2022
+ }
2023
+ log(`Retrieval backend: ${status.configured.backend}`);
2024
+ log(`Index: ${status.indexExists ? "present" : "missing"} (${status.indexPath})`);
2025
+ log(`Manifest: ${status.manifestExists ? "present" : "missing"} (${status.manifestPath})`);
2026
+ log(`Graph: ${status.graphExists ? "present" : "missing"}`);
2027
+ log(`Pages indexed: ${status.pageCount}`);
2028
+ log(`State: ${status.stale ? "stale" : "fresh"}`);
2029
+ for (const warning of status.warnings) {
2030
+ log(`Warning: ${warning}`);
2031
+ }
2032
+ });
2033
+ retrieval.command("rebuild").description("Rebuild the local retrieval index from the current graph.").action(async () => {
2034
+ const status = await rebuildRetrievalIndex(process2.cwd());
2035
+ if (isJson()) {
2036
+ emitJson(status);
2037
+ return;
2038
+ }
2039
+ log(`Rebuilt retrieval index at ${status.indexPath}`);
2040
+ log(`Pages indexed: ${status.pageCount}`);
2041
+ });
2042
+ retrieval.command("doctor").description("Diagnose retrieval index problems and optionally repair them.").option("--repair", "Rebuild stale or missing retrieval artifacts", false).action(async (options) => {
2043
+ const result = await doctorRetrieval(process2.cwd(), { repair: options.repair });
2044
+ if (isJson()) {
2045
+ emitJson(result);
2046
+ return;
2047
+ }
2048
+ log(`Retrieval health: ${result.ok ? "ok" : "needs attention"}`);
2049
+ if (result.repaired) {
2050
+ log("Repaired retrieval index.");
2051
+ }
2052
+ if (result.actions.length) {
2053
+ log(`Suggested action(s): ${result.actions.join(", ")}`);
2054
+ }
2055
+ for (const warning of result.status.warnings) {
2056
+ log(`Warning: ${warning}`);
2057
+ }
2058
+ });
1771
2059
  program.command("scan").description("Quick-start: initialize, ingest, compile, and serve a graph viewer in one command.").argument("<directory>", "Directory to scan").option("--port <port>", "Port for the graph viewer").option("--no-serve", "Skip launching the graph viewer after compile").action(async (directory, options) => {
1772
2060
  const rootDir = process2.cwd();
1773
2061
  await initVault(rootDir, {});
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarmvaultai/cli",
3
- "version": "1.4.0",
3
+ "version": "3.1.0",
4
4
  "description": "Global CLI for SwarmVault.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -38,7 +38,7 @@
38
38
  "node": ">=24.0.0"
39
39
  },
40
40
  "dependencies": {
41
- "@swarmvaultai/engine": "1.4.0",
41
+ "@swarmvaultai/engine": "3.1.0",
42
42
  "commander": "^14.0.1"
43
43
  },
44
44
  "devDependencies": {