@swarmvaultai/cli 3.17.0 → 3.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +32 -11
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -337,9 +337,9 @@ program.addHelpText(
337
337
  function readCliVersion() {
338
338
  try {
339
339
  const packageJson = JSON.parse(readFileSync(new URL("../package.json", import.meta.url), "utf8"));
340
- return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "3.17.0";
340
+ return typeof packageJson.version === "string" && packageJson.version.trim() ? packageJson.version : "3.18.0";
341
341
  } catch {
342
- return "3.17.0";
342
+ return "3.18.0";
343
343
  }
344
344
  }
345
345
  function parsePositiveInt(value, fallback) {
@@ -2490,24 +2490,24 @@ program.command("cluster-only", { hidden: true }).description("Compatibility ali
2490
2490
  program.command("tree", { hidden: true }).description("Compatibility alias for graph tree: write a collapsible source/module/symbol tree for the compiled graph.").option("--output <html>", "Output HTML path (default: wiki/graph/tree.html)").option("--root <path>", "Vault root to read instead of the current directory").option("--label <name>", "Tree title").option("--max-children <n>", "Maximum children to render per tree node", "250").action(runGraphTreeCommand);
2491
2491
  program.command("merge-graphs", { hidden: true }).description("Compatibility alias for graph merge: combine graph JSON files into one namespaced graph artifact.").argument("<graphs...>", "Graph JSON files to merge").requiredOption("--out <path>", "Output graph JSON path").option("--label <name>", "Label/prefix to use when merging one graph").action(runGraphMergeCommand);
2492
2492
  var hook = program.command("hook").description("Install local git hooks that keep tracked repos and the vault in sync.");
2493
- hook.command("install").description("Install post-commit and post-checkout hooks for the nearest git repository.").action(async () => {
2494
- const status = await installGitHooks(process2.cwd());
2493
+ hook.command("install").description("Install post-commit and post-checkout hooks for the nearest git repository, or an explicit repo below the vault root.").argument("[repo]", "Optional git repo path when the tracked repo lives below the vault root").action(async (repo) => {
2494
+ const status = await installGitHooks(process2.cwd(), { repoPath: repo });
2495
2495
  if (isJson()) {
2496
2496
  emitJson(status);
2497
2497
  return;
2498
2498
  }
2499
2499
  log(`Installed hooks in ${status.repoRoot}`);
2500
2500
  });
2501
- hook.command("uninstall").description("Remove the SwarmVault-managed git hook blocks from the nearest git repository.").action(async () => {
2502
- const status = await uninstallGitHooks(process2.cwd());
2501
+ hook.command("uninstall").description("Remove the SwarmVault-managed git hook blocks from the nearest git repository or an explicit repo path.").argument("[repo]", "Optional git repo path when the tracked repo lives below the vault root").action(async (repo) => {
2502
+ const status = await uninstallGitHooks(process2.cwd(), { repoPath: repo });
2503
2503
  if (isJson()) {
2504
2504
  emitJson(status);
2505
2505
  return;
2506
2506
  }
2507
2507
  log(`Removed SwarmVault hook blocks from ${status.repoRoot ?? "the current workspace"}`);
2508
2508
  });
2509
- hook.command("status").description("Show whether SwarmVault-managed git hooks are installed.").action(async () => {
2510
- const status = await getGitHookStatus(process2.cwd());
2509
+ hook.command("status").description("Show whether SwarmVault-managed git hooks are installed.").argument("[repo]", "Optional git repo path when the tracked repo lives below the vault root").action(async (repo) => {
2510
+ const status = await getGitHookStatus(process2.cwd(), { repoPath: repo });
2511
2511
  if (isJson()) {
2512
2512
  emitJson(status);
2513
2513
  return;
@@ -2614,7 +2614,10 @@ install.command("status").description("Show whether SwarmVault instructions are
2614
2614
  install.option(
2615
2615
  "--agent <agent>",
2616
2616
  "claude, codex, cursor, gemini, goose, opencode, copilot, aider, droid, pi, trae, claw, kiro, kilo, hermes, antigravity, vscode, amp, augment, adal, bob, cline, codebuddy, command-code, continue, cortex, crush, deepagents, devin, firebender, iflow, junie, kilo-code, kimi, kode, mcpjam, mistral-vibe, mux, neovate, openclaw, openhands, pochi, qoder, qwen-code, replit, roo-code, trae-cn, warp, windsurf, or zencoder"
2617
- ).option("--hook", "Also install hook/plugin guidance when the target agent supports it", false).option("--mcp", "Also register the SwarmVault MCP server in the agent's project MCP config", false).option("--scope <scope>", "Install scope: project or user", "project").action(async (options) => {
2617
+ ).option("--hook", "Also install hook/plugin guidance when the target agent supports it", false).option("--mcp", "Also register the SwarmVault MCP server in the agent's project MCP config", false).option(
2618
+ "--graph-first [mode]",
2619
+ "Opt in to graph-first search enforcement for installed hooks: deny (default when the flag is passed), context, or off; persisted as hooks.graphFirst"
2620
+ ).option("--scope <scope>", "Install scope: project or user", "project").action(async (options) => {
2618
2621
  if (!options.agent) {
2619
2622
  throw new Error("Specify --agent <agent>.");
2620
2623
  }
@@ -2625,12 +2628,30 @@ install.option(
2625
2628
  if (options.mcp && options.agent !== "claude") {
2626
2629
  throw new Error("--mcp is currently only supported for --agent claude (project-level .mcp.json)");
2627
2630
  }
2631
+ let graphFirst;
2632
+ if (options.graphFirst !== void 0) {
2633
+ const mode = options.graphFirst === true ? "deny" : String(options.graphFirst).toLowerCase();
2634
+ if (mode !== "deny" && mode !== "context" && mode !== "off") {
2635
+ throw new Error("--graph-first accepts deny, context, or off");
2636
+ }
2637
+ graphFirst = mode;
2638
+ }
2628
2639
  const scope = options.scope === "user" ? "user" : "project";
2629
- const result = await installAgent(process2.cwd(), options.agent, { hook: options.hook ?? false, mcp: options.mcp ?? false, scope });
2640
+ const result = await installAgent(process2.cwd(), options.agent, {
2641
+ hook: options.hook ?? false,
2642
+ mcp: options.mcp ?? false,
2643
+ graphFirst,
2644
+ scope
2645
+ });
2630
2646
  if (isJson()) {
2631
- emitJson({ ...result, hook: options.hook ?? false, mcp: options.mcp ?? false, scope });
2647
+ emitJson({ ...result, hook: options.hook ?? false, mcp: options.mcp ?? false, graphFirst: graphFirst ?? null, scope });
2632
2648
  } else {
2633
2649
  log(`Installed rules into ${result.target}`);
2650
+ if (graphFirst) {
2651
+ log(`Graph-first hook mode set to "${graphFirst}" in swarmvault.config.json.`);
2652
+ } else if (options.hook) {
2653
+ log('Hooks run in advisory mode by default; add --graph-first to opt in to search enforcement (hooks.graphFirst: "deny").');
2654
+ }
2634
2655
  if (result.targets.length > 1) {
2635
2656
  log(`Also wrote: ${result.targets.filter((entry) => entry !== result.target).join(", ")}`);
2636
2657
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@swarmvaultai/cli",
3
- "version": "3.17.0",
3
+ "version": "3.18.0",
4
4
  "description": "Global CLI for SwarmVault.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -44,7 +44,7 @@
44
44
  "prepublishOnly": "node ../../scripts/check-release-sync.mjs && node ../../scripts/check-published-manifests.mjs"
45
45
  },
46
46
  "dependencies": {
47
- "@swarmvaultai/engine": "3.17.0",
47
+ "@swarmvaultai/engine": "3.18.0",
48
48
  "commander": "^14.0.1"
49
49
  },
50
50
  "devDependencies": {