@swarmvaultai/cli 3.15.0 → 3.16.1
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 +29 -3
- package/dist/index.js +203 -24
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -672,7 +672,15 @@ Trace the reverse-import blast radius of changing a file or module.
|
|
|
672
672
|
- follows reverse `imports` edges through the compiled graph
|
|
673
673
|
- reports affected modules by depth so you can estimate downstream impact before editing
|
|
674
674
|
|
|
675
|
-
### `swarmvault graph
|
|
675
|
+
### `swarmvault graph cycles [--relation <name>] [--limit <n>] [--max-depth <n>]`
|
|
676
|
+
|
|
677
|
+
Find deterministic directed cycles in the compiled graph.
|
|
678
|
+
|
|
679
|
+
- defaults to `imports` edges for module cycle checks
|
|
680
|
+
- accepts repeated `--relation` flags to inspect other directed relationships
|
|
681
|
+
- supports global `--json` for automation
|
|
682
|
+
|
|
683
|
+
### `swarmvault graph export --html|--html-standalone|--report|--svg|--graphml|--cypher|--json|--callflow|--obsidian|--canvas <output>`
|
|
676
684
|
|
|
677
685
|
Export the current graph as one or more shareable formats:
|
|
678
686
|
|
|
@@ -683,6 +691,7 @@ Export the current graph as one or more shareable formats:
|
|
|
683
691
|
- `--graphml` for graph-tool interoperability
|
|
684
692
|
- `--cypher` for Neo4j-style import scripts
|
|
685
693
|
- `--json` for a deterministic machine-readable graph package
|
|
694
|
+
- `--callflow` for compact directed relationship HTML
|
|
686
695
|
- `--obsidian` for an Obsidian-friendly markdown vault that preserves wiki folders, appends graph connections, emits orphan-node stubs and community notes, copies assets, and writes a minimal `.obsidian/` config
|
|
687
696
|
- `--canvas` for an Obsidian canvas grouped by community
|
|
688
697
|
|
|
@@ -713,11 +722,11 @@ Defaults:
|
|
|
713
722
|
- namespaces every remote record by `vaultId` so multiple vaults can safely share one Neo4j database
|
|
714
723
|
- upserts current graph records and does not prune stale remote data yet
|
|
715
724
|
|
|
716
|
-
### `swarmvault install --agent <agent
|
|
725
|
+
### `swarmvault install --agent <agent> [--scope project|user]`
|
|
717
726
|
|
|
718
727
|
Install agent-specific rules into the current project so an agent understands the SwarmVault workspace contract and workflow.
|
|
719
728
|
|
|
720
|
-
`init`, `quickstart`, `scan`, and `clone` do not write project-local agent rule files by default. Run `swarmvault install --agent <agent
|
|
729
|
+
`init`, `quickstart`, `scan`, and `clone` do not write project-local agent rule files by default. Run `swarmvault install --agent <agent> --scope project` for one target at a time, use `--scope user` for supported user-scope skill installs, or list targets in `swarmvault.config.json` and pass `--install-agent-rules` to `init`, `quickstart`, `scan`, or `clone` when you intentionally want configured targets installed together.
|
|
721
730
|
|
|
722
731
|
Hook-capable installs:
|
|
723
732
|
|
|
@@ -727,6 +736,7 @@ swarmvault install --agent claude --hook
|
|
|
727
736
|
swarmvault install --agent gemini --hook
|
|
728
737
|
swarmvault install --agent opencode --hook
|
|
729
738
|
swarmvault install --agent copilot --hook
|
|
739
|
+
swarmvault install --agent kilo --hook
|
|
730
740
|
```
|
|
731
741
|
|
|
732
742
|
Agent target mapping:
|
|
@@ -741,9 +751,13 @@ Agent target mapping:
|
|
|
741
751
|
- `claw` writes `.claw/skills/swarmvault/SKILL.md`
|
|
742
752
|
- `droid` writes `.factory/rules/swarmvault.md`
|
|
743
753
|
- `kiro` writes `.kiro/skills/swarmvault/SKILL.md` and `.kiro/steering/swarmvault.md`
|
|
754
|
+
- `kilo` project-scope writes `AGENTS.md`; with `--hook`, it also writes `.kilo/plugins/swarmvault.js` and `.kilo/kilo.json` while preserving an existing `.kilo/kilo.jsonc`
|
|
744
755
|
- `hermes` writes `~/.hermes/skills/swarmvault/SKILL.md` plus `AGENTS.md`
|
|
745
756
|
- `antigravity` writes `.agents/rules/swarmvault.md` and `.agents/workflows/swarmvault.md`, and removes older fully managed `.agent/` files during reinstall
|
|
746
757
|
- `vscode` writes `.github/chatmodes/swarmvault.chatmode.md` plus `.github/copilot-instructions.md`
|
|
758
|
+
- `devin` writes `.devin/skills/swarmvault/SKILL.md` plus `.windsurf/rules/swarmvault.md`
|
|
759
|
+
|
|
760
|
+
`swarmvault install status --agent <agent> [--scope project|user] [--hook]` reports the expected install paths and whether they exist without writing files.
|
|
747
761
|
|
|
748
762
|
SwarmVault only owns the managed block inside shared markdown rule files. It keeps the SwarmVault block aligned across targets while preserving any user-owned text before or after the block, so `AGENTS.md` and `CLAUDE.md` do not need to be byte-identical.
|
|
749
763
|
|
|
@@ -754,6 +768,7 @@ Hook semantics:
|
|
|
754
768
|
- `gemini --hook` writes `.gemini/settings.json` plus `.gemini/hooks/swarmvault-graph-first.js` and stays advisory/model-visible
|
|
755
769
|
- `opencode --hook` writes `.opencode/plugins/swarmvault-graph-first.js` and stays advisory/log-only
|
|
756
770
|
- `copilot --hook` writes `.github/hooks/swarmvault-graph-first.json` plus `.github/hooks/swarmvault-graph-first.js` and remains decision-based rather than advisory
|
|
771
|
+
- `kilo --hook` writes `.kilo/plugins/swarmvault.js` and registers it in `.kilo/kilo.json`
|
|
757
772
|
|
|
758
773
|
`aider` is intentionally file/config-based in this release rather than hook-based.
|
|
759
774
|
|
|
@@ -775,6 +790,17 @@ npm install -g @swarmvaultai/cli
|
|
|
775
790
|
|
|
776
791
|
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.
|
|
777
792
|
|
|
793
|
+
CLI registry commands:
|
|
794
|
+
|
|
795
|
+
```bash
|
|
796
|
+
swarmvault provider add router --type openrouter --model openrouter/auto --api-key-env OPENROUTER_API_KEY --capability chat --capability structured --task queryProvider
|
|
797
|
+
swarmvault provider list
|
|
798
|
+
swarmvault provider show router
|
|
799
|
+
swarmvault provider remove router --fallback local
|
|
800
|
+
```
|
|
801
|
+
|
|
802
|
+
`provider add|remove` preserves unrelated config fields and stores secret references through `apiKeyEnv`, not literal API key values.
|
|
803
|
+
|
|
778
804
|
Example:
|
|
779
805
|
|
|
780
806
|
```json
|
package/dist/index.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
acceptApproval,
|
|
11
11
|
addInput,
|
|
12
12
|
addManagedSource,
|
|
13
|
+
addProviderConfig,
|
|
13
14
|
addWatchedRoot,
|
|
14
15
|
archiveCandidate,
|
|
15
16
|
askChatSession,
|
|
@@ -37,9 +38,12 @@ import {
|
|
|
37
38
|
exportGraphTree,
|
|
38
39
|
exportObsidianCanvas,
|
|
39
40
|
exportObsidianVault,
|
|
41
|
+
findGraphCycles,
|
|
40
42
|
finishMemoryTask,
|
|
43
|
+
getAgentInstallStatus,
|
|
41
44
|
getGitHookStatus,
|
|
42
45
|
getGraphStatus,
|
|
46
|
+
getProviderConfigEntry,
|
|
43
47
|
getRetrievalStatus,
|
|
44
48
|
getWatchStatus,
|
|
45
49
|
graphDiff,
|
|
@@ -61,6 +65,7 @@ import {
|
|
|
61
65
|
listManagedSourceRecords,
|
|
62
66
|
listManifests,
|
|
63
67
|
listMemoryTasks,
|
|
68
|
+
listProviderConfigEntries,
|
|
64
69
|
listSchedules,
|
|
65
70
|
listWatchedRoots,
|
|
66
71
|
loadVaultConfig,
|
|
@@ -80,6 +85,7 @@ import {
|
|
|
80
85
|
registerLocalWhisperProvider,
|
|
81
86
|
rejectApproval,
|
|
82
87
|
reloadManagedSources,
|
|
88
|
+
removeProviderConfig,
|
|
83
89
|
removeWatchedRoot,
|
|
84
90
|
renderContextPackLlms,
|
|
85
91
|
renderContextPackMarkdown,
|
|
@@ -330,9 +336,9 @@ program.addHelpText(
|
|
|
330
336
|
function readCliVersion() {
|
|
331
337
|
try {
|
|
332
338
|
const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
|
|
333
|
-
return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "3.
|
|
339
|
+
return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "3.16.1";
|
|
334
340
|
} catch {
|
|
335
|
-
return "3.
|
|
341
|
+
return "3.16.1";
|
|
336
342
|
}
|
|
337
343
|
}
|
|
338
344
|
function parsePositiveInt(value, fallback) {
|
|
@@ -340,6 +346,60 @@ function parsePositiveInt(value, fallback) {
|
|
|
340
346
|
const parsed = Number.parseInt(value, 10);
|
|
341
347
|
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
342
348
|
}
|
|
349
|
+
var providerTypes = [
|
|
350
|
+
"heuristic",
|
|
351
|
+
"openai",
|
|
352
|
+
"ollama",
|
|
353
|
+
"anthropic",
|
|
354
|
+
"gemini",
|
|
355
|
+
"openai-compatible",
|
|
356
|
+
"openrouter",
|
|
357
|
+
"groq",
|
|
358
|
+
"together",
|
|
359
|
+
"xai",
|
|
360
|
+
"cerebras",
|
|
361
|
+
"local-whisper",
|
|
362
|
+
"custom"
|
|
363
|
+
];
|
|
364
|
+
var providerCapabilities = [
|
|
365
|
+
"responses",
|
|
366
|
+
"chat",
|
|
367
|
+
"structured",
|
|
368
|
+
"tools",
|
|
369
|
+
"vision",
|
|
370
|
+
"embeddings",
|
|
371
|
+
"streaming",
|
|
372
|
+
"local",
|
|
373
|
+
"image_generation",
|
|
374
|
+
"audio"
|
|
375
|
+
];
|
|
376
|
+
var providerTaskKeys = [
|
|
377
|
+
"compileProvider",
|
|
378
|
+
"queryProvider",
|
|
379
|
+
"lintProvider",
|
|
380
|
+
"visionProvider",
|
|
381
|
+
"imageProvider",
|
|
382
|
+
"embeddingProvider",
|
|
383
|
+
"audioProvider"
|
|
384
|
+
];
|
|
385
|
+
function parseProviderType(value) {
|
|
386
|
+
if (providerTypes.includes(value)) {
|
|
387
|
+
return value;
|
|
388
|
+
}
|
|
389
|
+
throw new Error(`Unknown provider type "${value}". Use one of: ${providerTypes.join(", ")}.`);
|
|
390
|
+
}
|
|
391
|
+
function parseProviderCapability(value) {
|
|
392
|
+
if (providerCapabilities.includes(value)) {
|
|
393
|
+
return value;
|
|
394
|
+
}
|
|
395
|
+
throw new Error(`Unknown provider capability "${value}". Use one of: ${providerCapabilities.join(", ")}.`);
|
|
396
|
+
}
|
|
397
|
+
function parseProviderTask(value) {
|
|
398
|
+
if (providerTaskKeys.includes(value)) {
|
|
399
|
+
return value;
|
|
400
|
+
}
|
|
401
|
+
throw new Error(`Unknown provider task "${value}". Use one of: ${providerTaskKeys.join(", ")}.`);
|
|
402
|
+
}
|
|
343
403
|
function parsePositiveNumber(value) {
|
|
344
404
|
if (value === void 0) return void 0;
|
|
345
405
|
const parsed = Number.parseFloat(value);
|
|
@@ -1853,8 +1913,8 @@ graph.command("serve").description("Serve the local graph viewer.").option("--po
|
|
|
1853
1913
|
});
|
|
1854
1914
|
});
|
|
1855
1915
|
graph.command("export").description(
|
|
1856
|
-
"Export the graph as HTML, report, SVG, GraphML, Cypher, JSON, Obsidian vault, or Obsidian canvas. Combine flags to write multiple formats in one run."
|
|
1857
|
-
).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(
|
|
1916
|
+
"Export the graph as HTML, report, SVG, GraphML, Cypher, JSON, callflow HTML, Obsidian vault, or Obsidian canvas. Combine flags to write multiple formats in one run."
|
|
1917
|
+
).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("--callflow <output>", "Output directed callflow HTML 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(
|
|
1858
1918
|
async (options) => {
|
|
1859
1919
|
const useFullGraph = options.overview ? false : options.full ?? true;
|
|
1860
1920
|
const targets = [
|
|
@@ -1866,12 +1926,13 @@ graph.command("export").description(
|
|
|
1866
1926
|
options.cypher ? { format: "cypher", outputPath: options.cypher } : null,
|
|
1867
1927
|
options.neo4j ? { format: "cypher", outputPath: options.neo4j } : null,
|
|
1868
1928
|
options.json ? { format: "json", outputPath: options.json } : null,
|
|
1929
|
+
options.callflow ? { format: "callflow", outputPath: options.callflow } : null,
|
|
1869
1930
|
options.obsidian ? { format: "obsidian", outputPath: options.obsidian } : null,
|
|
1870
1931
|
options.canvas ? { format: "canvas", outputPath: options.canvas } : null
|
|
1871
1932
|
].filter((target) => Boolean(target));
|
|
1872
1933
|
if (targets.length === 0) {
|
|
1873
1934
|
throw new Error(
|
|
1874
|
-
"Pass at least one of --html, --html-standalone, --report, --svg, --graphml, --cypher, --neo4j, --json, --obsidian, or --canvas."
|
|
1935
|
+
"Pass at least one of --html, --html-standalone, --report, --svg, --graphml, --cypher, --neo4j, --json, --callflow, --obsidian, or --canvas."
|
|
1875
1936
|
);
|
|
1876
1937
|
}
|
|
1877
1938
|
const results = [];
|
|
@@ -2010,6 +2071,24 @@ graph.command("blast").description("Show the blast radius of changing a file or
|
|
|
2010
2071
|
log(` ${" ".repeat(mod.depth - 1)}${mod.label} (depth ${mod.depth})`);
|
|
2011
2072
|
}
|
|
2012
2073
|
});
|
|
2074
|
+
graph.command("cycles").description("Find directed cycles in the compiled graph, defaulting to import edges.").option("--relation <name>", "Relation name to follow (repeatable; default: imports)", collectRepeated, []).option("--limit <n>", "Maximum cycles to report", "25").option("--max-depth <n>", "Maximum cycle depth", "25").action(async (options) => {
|
|
2075
|
+
const { paths } = await loadVaultConfig(process2.cwd());
|
|
2076
|
+
const raw = await readFile2(paths.graphPath, "utf-8");
|
|
2077
|
+
const graphArtifact = JSON.parse(raw);
|
|
2078
|
+
const result = findGraphCycles(graphArtifact, {
|
|
2079
|
+
relations: options.relation?.length ? options.relation : ["imports"],
|
|
2080
|
+
limit: parsePositiveInt(options.limit, 25),
|
|
2081
|
+
maxDepth: parsePositiveInt(options.maxDepth, 25)
|
|
2082
|
+
});
|
|
2083
|
+
if (isJson()) {
|
|
2084
|
+
emitJson(result);
|
|
2085
|
+
return;
|
|
2086
|
+
}
|
|
2087
|
+
log(result.summary);
|
|
2088
|
+
for (const cycle of result.cycles) {
|
|
2089
|
+
log(`- ${cycle.labels.join(" -> ")} -> ${cycle.labels[0]} (${cycle.relations.join(", ")})`);
|
|
2090
|
+
}
|
|
2091
|
+
});
|
|
2013
2092
|
graph.command("supersession").description("Record that one page has been replaced by another (writes a superseded_by edge).").argument("<pageId>", "Page id or path of the older page").argument("<replacedById>", "Page id or path of the replacement page").action(async (pageId, replacedById) => {
|
|
2014
2093
|
const result = await createSupersessionEdge(process2.cwd(), pageId, replacedById);
|
|
2015
2094
|
if (isJson()) {
|
|
@@ -2193,6 +2272,91 @@ provider.command("setup").description("Interactive setup for a provider (current
|
|
|
2193
2272
|
log(`Left tasks.audioProvider = "${registration.previousAudioProvider}" untouched (use --set-audio-provider to override).`);
|
|
2194
2273
|
}
|
|
2195
2274
|
});
|
|
2275
|
+
provider.command("add").description("Add or update a named provider in swarmvault.config.json.").argument("<id>", "Provider id").requiredOption("--type <type>", `Provider type: ${providerTypes.join(", ")}`).requiredOption("--model <model>", "Provider model name").option("--base-url <url>", "OpenAI-compatible base URL").option("--api-key-env <name>", "Environment variable that holds the provider API key").option("--capability <capability>", `Provider capability (${providerCapabilities.join(", ")})`, collectRepeated, []).option("--task <task>", `Assign provider to task (${providerTaskKeys.join(", ")})`, collectRepeated, []).option("--api-style <style>", "OpenAI-compatible API style: responses or chat").option("--module <path>", "Custom provider module path").option("--binary-path <path>", "Local provider binary path").option("--model-path <path>", "Local model file path").option("--threads <n>", "Local provider thread count").action(
|
|
2276
|
+
async (id, options) => {
|
|
2277
|
+
const apiStyle = options.apiStyle;
|
|
2278
|
+
if (apiStyle && apiStyle !== "responses" && apiStyle !== "chat") {
|
|
2279
|
+
throw new Error("--api-style must be responses or chat.");
|
|
2280
|
+
}
|
|
2281
|
+
const threads = options.threads ? parsePositiveInt(options.threads, 0) || void 0 : void 0;
|
|
2282
|
+
const result = await addProviderConfig({
|
|
2283
|
+
rootDir: process2.cwd(),
|
|
2284
|
+
providerId: id,
|
|
2285
|
+
provider: {
|
|
2286
|
+
type: parseProviderType(options.type),
|
|
2287
|
+
model: options.model,
|
|
2288
|
+
baseUrl: options.baseUrl,
|
|
2289
|
+
apiKeyEnv: options.apiKeyEnv,
|
|
2290
|
+
capabilities: options.capability?.map(parseProviderCapability),
|
|
2291
|
+
apiStyle,
|
|
2292
|
+
module: options.module,
|
|
2293
|
+
binaryPath: options.binaryPath,
|
|
2294
|
+
modelPath: options.modelPath,
|
|
2295
|
+
threads
|
|
2296
|
+
},
|
|
2297
|
+
tasks: options.task?.map(parseProviderTask)
|
|
2298
|
+
});
|
|
2299
|
+
if (isJson()) {
|
|
2300
|
+
emitJson(result);
|
|
2301
|
+
return;
|
|
2302
|
+
}
|
|
2303
|
+
log(`${result.added ? "Added" : result.updated ? "Updated" : "Kept"} provider ${result.providerId} in ${result.configPath}.`);
|
|
2304
|
+
if (result.updatedTasks.length) {
|
|
2305
|
+
log(`Assigned tasks: ${result.updatedTasks.join(", ")}`);
|
|
2306
|
+
}
|
|
2307
|
+
}
|
|
2308
|
+
);
|
|
2309
|
+
provider.command("list").description("List configured providers and task assignments.").action(async () => {
|
|
2310
|
+
const entries = await listProviderConfigEntries(process2.cwd());
|
|
2311
|
+
if (isJson()) {
|
|
2312
|
+
emitJson(entries);
|
|
2313
|
+
return;
|
|
2314
|
+
}
|
|
2315
|
+
if (!entries.length) {
|
|
2316
|
+
log("No providers configured.");
|
|
2317
|
+
return;
|
|
2318
|
+
}
|
|
2319
|
+
for (const entry of entries) {
|
|
2320
|
+
const tasks = entry.assignedTasks.length ? ` tasks=${entry.assignedTasks.join(",")}` : "";
|
|
2321
|
+
const key = entry.apiKeyEnv ? ` key=${entry.apiKeyEnv}` : "";
|
|
2322
|
+
log(`${entry.id} type=${entry.type} model=${entry.model}${key}${tasks}`);
|
|
2323
|
+
}
|
|
2324
|
+
});
|
|
2325
|
+
provider.command("show").description("Show one configured provider.").argument("<id>", "Provider id").action(async (id) => {
|
|
2326
|
+
const entry = await getProviderConfigEntry(process2.cwd(), id);
|
|
2327
|
+
if (!entry) {
|
|
2328
|
+
throw new Error(`Provider ${id} is not configured.`);
|
|
2329
|
+
}
|
|
2330
|
+
if (isJson()) {
|
|
2331
|
+
emitJson(entry);
|
|
2332
|
+
return;
|
|
2333
|
+
}
|
|
2334
|
+
log(`${entry.id}`);
|
|
2335
|
+
log(`type=${entry.type}`);
|
|
2336
|
+
log(`model=${entry.model}`);
|
|
2337
|
+
if (entry.baseUrl) log(`baseUrl=${entry.baseUrl}`);
|
|
2338
|
+
if (entry.apiKeyEnv) log(`apiKeyEnv=${entry.apiKeyEnv}`);
|
|
2339
|
+
if (entry.capabilities.length) log(`capabilities=${entry.capabilities.join(",")}`);
|
|
2340
|
+
if (entry.assignedTasks.length) log(`tasks=${entry.assignedTasks.join(",")}`);
|
|
2341
|
+
});
|
|
2342
|
+
provider.command("remove").description("Remove a configured provider and reassign its tasks to a fallback provider.").argument("<id>", "Provider id").option("--fallback <id>", "Fallback provider for tasks currently assigned to the removed provider").action(async (id, options) => {
|
|
2343
|
+
const result = await removeProviderConfig({
|
|
2344
|
+
rootDir: process2.cwd(),
|
|
2345
|
+
providerId: id,
|
|
2346
|
+
fallbackProviderId: options.fallback
|
|
2347
|
+
});
|
|
2348
|
+
if (isJson()) {
|
|
2349
|
+
emitJson(result);
|
|
2350
|
+
return;
|
|
2351
|
+
}
|
|
2352
|
+
log(`${result.removed ? "Removed" : "No provider named"} ${id}.`);
|
|
2353
|
+
if (result.reassignedTasks.length) {
|
|
2354
|
+
log(`Reassigned tasks to ${result.fallbackProviderId}: ${result.reassignedTasks.join(", ")}`);
|
|
2355
|
+
}
|
|
2356
|
+
if (result.clearedTasks.length) {
|
|
2357
|
+
log(`Cleared task assignments: ${result.clearedTasks.join(", ")}`);
|
|
2358
|
+
}
|
|
2359
|
+
});
|
|
2196
2360
|
async function confirmInteractive(message) {
|
|
2197
2361
|
if (!process2.stdin.isTTY) return false;
|
|
2198
2362
|
const rl = createInterface({ input: process2.stdin, output: process2.stderr });
|
|
@@ -2406,29 +2570,44 @@ program.command("mcp").description("Run SwarmVault as a local MCP server over st
|
|
|
2406
2570
|
process2.exit(0);
|
|
2407
2571
|
});
|
|
2408
2572
|
});
|
|
2409
|
-
program.command("install").description("Install SwarmVault instructions for an agent in the current project.")
|
|
2573
|
+
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) => {
|
|
2575
|
+
const scope = options.scope === "user" ? "user" : "project";
|
|
2576
|
+
const result = await getAgentInstallStatus(process2.cwd(), options.agent, { hook: options.hook ?? false, scope });
|
|
2577
|
+
if (isJson()) {
|
|
2578
|
+
emitJson(result);
|
|
2579
|
+
return;
|
|
2580
|
+
}
|
|
2581
|
+
log(`${result.agent} ${result.installed ? "installed" : "not installed"} (${result.scope}${result.hook ? ", hook" : ""})`);
|
|
2582
|
+
for (const target of result.targets) {
|
|
2583
|
+
log(`${target.exists ? "ok" : "missing"} ${target.path}`);
|
|
2584
|
+
}
|
|
2585
|
+
});
|
|
2586
|
+
install.option(
|
|
2410
2587
|
"--agent <agent>",
|
|
2411
|
-
"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"
|
|
2412
|
-
).option("--hook", "Also install hook/plugin guidance when the target agent supports it", false).action(
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2588
|
+
"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("--scope <scope>", "Install scope: project or user", "project").action(async (options) => {
|
|
2590
|
+
if (!options.agent) {
|
|
2591
|
+
throw new Error("Specify --agent <agent>.");
|
|
2592
|
+
}
|
|
2593
|
+
const hookCapableAgents = /* @__PURE__ */ new Set(["codex", "claude", "opencode", "gemini", "copilot", "kilo"]);
|
|
2594
|
+
if (options.hook && !hookCapableAgents.has(options.agent)) {
|
|
2595
|
+
throw new Error("--hook is only supported for --agent codex, claude, opencode, gemini, copilot, or kilo");
|
|
2596
|
+
}
|
|
2597
|
+
const scope = options.scope === "user" ? "user" : "project";
|
|
2598
|
+
const result = await installAgent(process2.cwd(), options.agent, { hook: options.hook ?? false, scope });
|
|
2599
|
+
if (isJson()) {
|
|
2600
|
+
emitJson({ ...result, hook: options.hook ?? false, scope });
|
|
2601
|
+
} else {
|
|
2602
|
+
log(`Installed rules into ${result.target}`);
|
|
2603
|
+
if (result.targets.length > 1) {
|
|
2604
|
+
log(`Also wrote: ${result.targets.filter((entry) => entry !== result.target).join(", ")}`);
|
|
2417
2605
|
}
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
emitJson({ ...result, hook: options.hook ?? false });
|
|
2421
|
-
} else {
|
|
2422
|
-
log(`Installed rules into ${result.target}`);
|
|
2423
|
-
if (result.targets.length > 1) {
|
|
2424
|
-
log(`Also wrote: ${result.targets.filter((entry) => entry !== result.target).join(", ")}`);
|
|
2425
|
-
}
|
|
2426
|
-
for (const warning of result.warnings ?? []) {
|
|
2427
|
-
emitNotice(warning);
|
|
2428
|
-
}
|
|
2606
|
+
for (const warning of result.warnings ?? []) {
|
|
2607
|
+
emitNotice(warning);
|
|
2429
2608
|
}
|
|
2430
2609
|
}
|
|
2431
|
-
);
|
|
2610
|
+
});
|
|
2432
2611
|
program.command("demo").description("Try SwarmVault with a bundled sample vault \u2014 zero config, zero API keys.").option("--port <port>", "Port for the graph viewer").option("--no-serve", "Skip launching the graph viewer after compile").action(async (options) => {
|
|
2433
2612
|
const { mkdtemp, writeFile: writeFile3, mkdir: mkdir3 } = await import("fs/promises");
|
|
2434
2613
|
const { tmpdir } = await import("os");
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@swarmvaultai/cli",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.16.1",
|
|
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.16.1",
|
|
48
48
|
"commander": "^14.0.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|