caveat-cli 0.17.7 → 0.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.
package/dist/index.js CHANGED
@@ -76,7 +76,7 @@ import {
76
76
  userPromptSubmitReminderText,
77
77
  writeDigestMarker,
78
78
  writeUserConfigPatch
79
- } from "./chunk-ZBHCOA6R.js";
79
+ } from "./chunk-KAULUIC4.js";
80
80
 
81
81
  // ../../node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/codegen/code.js
82
82
  var require_code = __commonJS({
@@ -455,11 +455,11 @@ var require_codegen = __commonJS({
455
455
  const rhs = this.rhs === void 0 ? "" : ` = ${this.rhs}`;
456
456
  return `${varKind} ${this.name}${rhs};` + _n;
457
457
  }
458
- optimizeNames(names, constants4) {
458
+ optimizeNames(names, constants5) {
459
459
  if (!names[this.name.str])
460
460
  return;
461
461
  if (this.rhs)
462
- this.rhs = optimizeExpr(this.rhs, names, constants4);
462
+ this.rhs = optimizeExpr(this.rhs, names, constants5);
463
463
  return this;
464
464
  }
465
465
  get names() {
@@ -476,10 +476,10 @@ var require_codegen = __commonJS({
476
476
  render({ _n }) {
477
477
  return `${this.lhs} = ${this.rhs};` + _n;
478
478
  }
479
- optimizeNames(names, constants4) {
479
+ optimizeNames(names, constants5) {
480
480
  if (this.lhs instanceof code_1.Name && !names[this.lhs.str] && !this.sideEffects)
481
481
  return;
482
- this.rhs = optimizeExpr(this.rhs, names, constants4);
482
+ this.rhs = optimizeExpr(this.rhs, names, constants5);
483
483
  return this;
484
484
  }
485
485
  get names() {
@@ -540,8 +540,8 @@ var require_codegen = __commonJS({
540
540
  optimizeNodes() {
541
541
  return `${this.code}` ? this : void 0;
542
542
  }
543
- optimizeNames(names, constants4) {
544
- this.code = optimizeExpr(this.code, names, constants4);
543
+ optimizeNames(names, constants5) {
544
+ this.code = optimizeExpr(this.code, names, constants5);
545
545
  return this;
546
546
  }
547
547
  get names() {
@@ -570,12 +570,12 @@ var require_codegen = __commonJS({
570
570
  }
571
571
  return nodes.length > 0 ? this : void 0;
572
572
  }
573
- optimizeNames(names, constants4) {
573
+ optimizeNames(names, constants5) {
574
574
  const { nodes } = this;
575
575
  let i = nodes.length;
576
576
  while (i--) {
577
577
  const n = nodes[i];
578
- if (n.optimizeNames(names, constants4))
578
+ if (n.optimizeNames(names, constants5))
579
579
  continue;
580
580
  subtractNames(names, n.names);
581
581
  nodes.splice(i, 1);
@@ -628,12 +628,12 @@ var require_codegen = __commonJS({
628
628
  return void 0;
629
629
  return this;
630
630
  }
631
- optimizeNames(names, constants4) {
631
+ optimizeNames(names, constants5) {
632
632
  var _a3;
633
- this.else = (_a3 = this.else) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names, constants4);
634
- if (!(super.optimizeNames(names, constants4) || this.else))
633
+ this.else = (_a3 = this.else) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names, constants5);
634
+ if (!(super.optimizeNames(names, constants5) || this.else))
635
635
  return;
636
- this.condition = optimizeExpr(this.condition, names, constants4);
636
+ this.condition = optimizeExpr(this.condition, names, constants5);
637
637
  return this;
638
638
  }
639
639
  get names() {
@@ -656,10 +656,10 @@ var require_codegen = __commonJS({
656
656
  render(opts) {
657
657
  return `for(${this.iteration})` + super.render(opts);
658
658
  }
659
- optimizeNames(names, constants4) {
660
- if (!super.optimizeNames(names, constants4))
659
+ optimizeNames(names, constants5) {
660
+ if (!super.optimizeNames(names, constants5))
661
661
  return;
662
- this.iteration = optimizeExpr(this.iteration, names, constants4);
662
+ this.iteration = optimizeExpr(this.iteration, names, constants5);
663
663
  return this;
664
664
  }
665
665
  get names() {
@@ -695,10 +695,10 @@ var require_codegen = __commonJS({
695
695
  render(opts) {
696
696
  return `for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})` + super.render(opts);
697
697
  }
698
- optimizeNames(names, constants4) {
699
- if (!super.optimizeNames(names, constants4))
698
+ optimizeNames(names, constants5) {
699
+ if (!super.optimizeNames(names, constants5))
700
700
  return;
701
- this.iterable = optimizeExpr(this.iterable, names, constants4);
701
+ this.iterable = optimizeExpr(this.iterable, names, constants5);
702
702
  return this;
703
703
  }
704
704
  get names() {
@@ -740,11 +740,11 @@ var require_codegen = __commonJS({
740
740
  (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNodes();
741
741
  return this;
742
742
  }
743
- optimizeNames(names, constants4) {
743
+ optimizeNames(names, constants5) {
744
744
  var _a3, _b;
745
- super.optimizeNames(names, constants4);
746
- (_a3 = this.catch) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names, constants4);
747
- (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants4);
745
+ super.optimizeNames(names, constants5);
746
+ (_a3 = this.catch) === null || _a3 === void 0 ? void 0 : _a3.optimizeNames(names, constants5);
747
+ (_b = this.finally) === null || _b === void 0 ? void 0 : _b.optimizeNames(names, constants5);
748
748
  return this;
749
749
  }
750
750
  get names() {
@@ -1045,7 +1045,7 @@ var require_codegen = __commonJS({
1045
1045
  function addExprNames(names, from) {
1046
1046
  return from instanceof code_1._CodeOrName ? addNames(names, from.names) : names;
1047
1047
  }
1048
- function optimizeExpr(expr, names, constants4) {
1048
+ function optimizeExpr(expr, names, constants5) {
1049
1049
  if (expr instanceof code_1.Name)
1050
1050
  return replaceName(expr);
1051
1051
  if (!canOptimize(expr))
@@ -1060,14 +1060,14 @@ var require_codegen = __commonJS({
1060
1060
  return items;
1061
1061
  }, []));
1062
1062
  function replaceName(n) {
1063
- const c = constants4[n.str];
1063
+ const c = constants5[n.str];
1064
1064
  if (c === void 0 || names[n.str] !== 1)
1065
1065
  return n;
1066
1066
  delete names[n.str];
1067
1067
  return c;
1068
1068
  }
1069
1069
  function canOptimize(e) {
1070
- return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants4[c.str] !== void 0);
1070
+ return e instanceof code_1._Code && e._items.some((c) => c instanceof code_1.Name && names[c.str] === 1 && constants5[c.str] !== void 0);
1071
1071
  }
1072
1072
  }
1073
1073
  function subtractNames(names, from) {
@@ -6889,8 +6889,8 @@ var stdoutLogger = {
6889
6889
 
6890
6890
  // src/commands/init.ts
6891
6891
  import { spawnSync as spawnSync4 } from "node:child_process";
6892
- import { existsSync as existsSync6, mkdirSync as mkdirSync2, readdirSync, renameSync, rmdirSync, writeFileSync as writeFileSync3 } from "node:fs";
6893
- import { dirname as dirname3, join as join8 } from "node:path";
6892
+ import { existsSync as existsSync7, mkdirSync as mkdirSync3, readdirSync, renameSync, rmdirSync, writeFileSync as writeFileSync3 } from "node:fs";
6893
+ import { dirname as dirname3, join as join9 } from "node:path";
6894
6894
 
6895
6895
  // src/claudeInstall.ts
6896
6896
  import { spawnSync } from "node:child_process";
@@ -6998,9 +6998,10 @@ function isCaveatClaudeHookCommand(actual, event) {
6998
6998
  return lower.includes("caveat") && actual.includes(`hook ${event}`);
6999
6999
  }
7000
7000
  function isCanonicalCaveatClaudeHookCommand(actual, event, nodePath, cliScriptPath) {
7001
- if (actual !== hookCommand(nodePath, cliScriptPath, event)) return false;
7002
7001
  const tokens = commandTokens(actual);
7003
- return tokens?.length === 4 && isCanonicalAsset(tokens[0], nodePath, constants.X_OK) && isCanonicalAsset(tokens[1], cliScriptPath, constants.R_OK) && tokens[2] === "hook" && tokens[3] === event;
7002
+ if (tokens?.length !== 4 || tokens[2] !== "hook" || tokens[3] !== event) return false;
7003
+ if (actual !== hookCommand(tokens[0], tokens[1], event)) return false;
7004
+ return isCanonicalAsset(tokens[0], nodePath, constants.X_OK) && isCanonicalAsset(tokens[1], cliScriptPath, constants.R_OK);
7004
7005
  }
7005
7006
  function isCaveatClaudeMcpRegistration(value, nodePath, cliScriptPath) {
7006
7007
  if (!value || typeof value !== "object" || Array.isArray(value)) return false;
@@ -7511,6 +7512,150 @@ function detectCodexHookInstallation(codexHome) {
7511
7512
  };
7512
7513
  }
7513
7514
 
7515
+ // src/cursorInstall.ts
7516
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4 } from "node:fs";
7517
+ import { join as join5 } from "node:path";
7518
+ import { constants as constants3 } from "node:fs";
7519
+ var TIMEOUT_SEC = 10;
7520
+ function hookCommand3(nodePath, cliScriptPath, event) {
7521
+ return `${quoteCommandPath(nodePath)} ${quoteCommandPath(cliScriptPath)} cursor-hook ${event}`;
7522
+ }
7523
+ function eventCommandFragment2(event) {
7524
+ return `cursor-hook ${event}`;
7525
+ }
7526
+ function isCaveatCursorHookCommand(actual, event) {
7527
+ const lower = actual.toLowerCase();
7528
+ return lower.includes("caveat") && actual.includes(eventCommandFragment2(event));
7529
+ }
7530
+ function readHooks2(path) {
7531
+ if (!existsSync4(path)) return { version: 1, hooks: {} };
7532
+ const parsed = JSON.parse(readFileSync4(path, "utf-8"));
7533
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
7534
+ throw new Error(`${path} \u306F object \u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`);
7535
+ }
7536
+ const file2 = parsed;
7537
+ if (file2.hooks == null) file2.hooks = {};
7538
+ if (typeof file2.hooks !== "object" || Array.isArray(file2.hooks)) {
7539
+ throw new Error(`${path} \u306E hooks \u306F object \u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059`);
7540
+ }
7541
+ return file2;
7542
+ }
7543
+ function listFor(file2, event) {
7544
+ const current = file2.hooks?.[event];
7545
+ if (Array.isArray(current)) return current;
7546
+ return [];
7547
+ }
7548
+ function isSameCommand(actual, expected) {
7549
+ const left = commandTokens(actual);
7550
+ const right = commandTokens(expected);
7551
+ return left !== null && right !== null && left.length === right.length && left.every((token, index) => token === right[index]);
7552
+ }
7553
+ function upsertHook3(file2, event, command, subcommand) {
7554
+ file2.hooks ??= {};
7555
+ const list = listFor(file2, event);
7556
+ for (const entry of list) {
7557
+ if (typeof entry.command !== "string") continue;
7558
+ if (isSameCommand(entry.command, command) && entry.timeout === TIMEOUT_SEC) {
7559
+ return "unchanged";
7560
+ }
7561
+ if (isSameCommand(entry.command, command) || isCaveatCursorHookCommand(entry.command, subcommand)) {
7562
+ entry.command = command;
7563
+ entry.timeout = TIMEOUT_SEC;
7564
+ file2.hooks[event] = list;
7565
+ return "added";
7566
+ }
7567
+ }
7568
+ list.push({ command, timeout: TIMEOUT_SEC });
7569
+ file2.hooks[event] = list;
7570
+ return "added";
7571
+ }
7572
+ function removeHook3(file2, event, command, subcommand) {
7573
+ const list = listFor(file2, event);
7574
+ if (list.length === 0) return false;
7575
+ const kept = list.filter((entry) => {
7576
+ if (typeof entry.command !== "string") return true;
7577
+ return !(isSameCommand(entry.command, command) || isCaveatCursorHookCommand(entry.command, subcommand));
7578
+ });
7579
+ if (kept.length === list.length) return false;
7580
+ if (!file2.hooks) return true;
7581
+ if (kept.length > 0) file2.hooks[event] = kept;
7582
+ else delete file2.hooks[event];
7583
+ return true;
7584
+ }
7585
+ function hasCaveatHook2(file2, event, fragment) {
7586
+ return listFor(file2, event).some(
7587
+ (entry) => typeof entry.command === "string" && entry.command.includes(fragment)
7588
+ );
7589
+ }
7590
+ function persist(opts, file2, changed) {
7591
+ const hooksPath = join5(opts.cursorDir, "hooks.json");
7592
+ if (opts.dryRun) {
7593
+ opts.logger.info(`[dry-run] would write Cursor hooks to ${hooksPath}`);
7594
+ return void 0;
7595
+ }
7596
+ if (!changed) return void 0;
7597
+ mkdirSync2(opts.cursorDir, { recursive: true });
7598
+ file2.version = 1;
7599
+ const backup = writeJsonWithBackup(hooksPath, file2);
7600
+ return backup || void 0;
7601
+ }
7602
+ function installCursorHooks(opts) {
7603
+ const hooksPath = join5(opts.cursorDir, "hooks.json");
7604
+ const file2 = readHooks2(hooksPath);
7605
+ const promptCmd = hookCommand3(opts.nodePath, opts.cliScriptPath, "user-prompt-submit");
7606
+ const toolCmd = hookCommand3(opts.nodePath, opts.cliScriptPath, "post-tool-use");
7607
+ const stopCmd = hookCommand3(opts.nodePath, opts.cliScriptPath, "stop");
7608
+ const hooks = {
7609
+ beforeSubmitPrompt: upsertHook3(file2, "beforeSubmitPrompt", promptCmd, "user-prompt-submit"),
7610
+ postToolUse: upsertHook3(file2, "postToolUse", toolCmd, "post-tool-use"),
7611
+ postToolUseFailure: upsertHook3(file2, "postToolUseFailure", toolCmd, "post-tool-use"),
7612
+ stop: upsertHook3(file2, "stop", stopCmd, "stop")
7613
+ };
7614
+ const changed = Object.values(hooks).some((status2) => status2 === "added");
7615
+ const backupPath = persist(opts, file2, changed);
7616
+ return { hooks, backupPath };
7617
+ }
7618
+ function uninstallCursorHooks(opts) {
7619
+ const hooksPath = join5(opts.cursorDir, "hooks.json");
7620
+ const file2 = readHooks2(hooksPath);
7621
+ const promptCmd = hookCommand3(opts.nodePath, opts.cliScriptPath, "user-prompt-submit");
7622
+ const toolCmd = hookCommand3(opts.nodePath, opts.cliScriptPath, "post-tool-use");
7623
+ const stopCmd = hookCommand3(opts.nodePath, opts.cliScriptPath, "stop");
7624
+ const removed = {
7625
+ beforeSubmitPrompt: removeHook3(file2, "beforeSubmitPrompt", promptCmd, "user-prompt-submit"),
7626
+ postToolUse: removeHook3(file2, "postToolUse", toolCmd, "post-tool-use"),
7627
+ postToolUseFailure: removeHook3(file2, "postToolUseFailure", toolCmd, "post-tool-use"),
7628
+ stop: removeHook3(file2, "stop", stopCmd, "stop")
7629
+ };
7630
+ const changed = Object.values(removed).some(Boolean);
7631
+ const backupPath = persist(opts, file2, changed);
7632
+ return {
7633
+ hooks: {
7634
+ beforeSubmitPrompt: removed.beforeSubmitPrompt ? "added" : "unchanged",
7635
+ postToolUse: removed.postToolUse ? "added" : "unchanged",
7636
+ postToolUseFailure: removed.postToolUseFailure ? "added" : "unchanged",
7637
+ stop: removed.stop ? "added" : "unchanged"
7638
+ },
7639
+ backupPath
7640
+ };
7641
+ }
7642
+ function detectCursorHookInstallation(cursorDir) {
7643
+ const hooksPath = join5(cursorDir, "hooks.json");
7644
+ const file2 = existsSync4(hooksPath) ? readHooks2(hooksPath) : { version: 1, hooks: {} };
7645
+ const hooks = {
7646
+ beforeSubmitPrompt: hasCaveatHook2(file2, "beforeSubmitPrompt", eventCommandFragment2("user-prompt-submit")),
7647
+ postToolUse: hasCaveatHook2(file2, "postToolUse", eventCommandFragment2("post-tool-use")),
7648
+ postToolUseFailure: hasCaveatHook2(file2, "postToolUseFailure", eventCommandFragment2("post-tool-use")),
7649
+ stop: hasCaveatHook2(file2, "stop", eventCommandFragment2("stop"))
7650
+ };
7651
+ const count = Object.values(hooks).filter(Boolean).length;
7652
+ return {
7653
+ installation: count === 0 ? "not-installed" : count === 4 ? "installed" : "partial",
7654
+ hooksPath,
7655
+ hooks
7656
+ };
7657
+ }
7658
+
7514
7659
  // src/ghSetup.ts
7515
7660
  import { spawnSync as spawnSync2 } from "node:child_process";
7516
7661
  import { readSync } from "node:fs";
@@ -7558,8 +7703,8 @@ function askOnce(question) {
7558
7703
  }
7559
7704
 
7560
7705
  // src/nodePath.ts
7561
- import { existsSync as existsSync4, realpathSync as realpathSync2 } from "node:fs";
7562
- import { delimiter, join as join5 } from "node:path";
7706
+ import { existsSync as existsSync5, realpathSync as realpathSync2 } from "node:fs";
7707
+ import { delimiter, join as join6 } from "node:path";
7563
7708
  var defaultRealpath = (path) => realpathSync2.native(path);
7564
7709
  function safeRealpath(path, realpath = defaultRealpath) {
7565
7710
  try {
@@ -7572,7 +7717,7 @@ function resolveHookNodePath({
7572
7717
  env = process.env,
7573
7718
  execPath = process.execPath,
7574
7719
  platform = process.platform,
7575
- exists = existsSync4,
7720
+ exists = existsSync5,
7576
7721
  realpath = defaultRealpath
7577
7722
  } = {}) {
7578
7723
  const execRealpath = safeRealpath(execPath, realpath);
@@ -7580,7 +7725,7 @@ function resolveHookNodePath({
7580
7725
  const names = nodeExecutableNames(platform);
7581
7726
  for (const dir of pathEnv.split(delimiter).filter(Boolean)) {
7582
7727
  for (const name of names) {
7583
- const candidate = join5(dir, name);
7728
+ const candidate = join6(dir, name);
7584
7729
  if (!exists(candidate)) continue;
7585
7730
  const candidateRealpath = safeRealpath(candidate, realpath);
7586
7731
  if (execRealpath && candidateRealpath && candidateRealpath === execRealpath) {
@@ -7592,14 +7737,14 @@ function resolveHookNodePath({
7592
7737
  }
7593
7738
 
7594
7739
  // src/commands/publish.ts
7595
- import { existsSync as existsSync5 } from "node:fs";
7596
- import { join as join7 } from "node:path";
7740
+ import { existsSync as existsSync6 } from "node:fs";
7741
+ import { join as join8 } from "node:path";
7597
7742
 
7598
7743
  // src/commands/codexSidecarAdvisory.ts
7599
7744
  import { spawnSync as spawnSync3 } from "node:child_process";
7600
- import { mkdtempSync, readFileSync as readFileSync4, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
7745
+ import { mkdtempSync, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync2 } from "node:fs";
7601
7746
  import { tmpdir } from "node:os";
7602
- import { join as join6 } from "node:path";
7747
+ import { join as join7 } from "node:path";
7603
7748
  var DEFAULT_HOOK_SIDECAR_TIMEOUT_MS = 12e4;
7604
7749
  var MAX_HOOK_SIDECAR_TIMEOUT_MS = 24e4;
7605
7750
  function resolveHookSidecarTimeoutMs(raw) {
@@ -7655,11 +7800,11 @@ function runCodexSidecarAdvisory(input) {
7655
7800
  let resultDir;
7656
7801
  let advisory;
7657
7802
  try {
7658
- resultDir = mkdtempSync(join6(tmpdir(), "caveat-codex-advisory-"));
7659
- const resultFile = join6(resultDir, "result.json");
7803
+ resultDir = mkdtempSync(join7(tmpdir(), "caveat-codex-advisory-"));
7804
+ const resultFile = join7(resultDir, "result.json");
7660
7805
  args.push("--save-result", resultFile);
7661
7806
  if (input.additionalContext) {
7662
- const additionalContextFile = join6(resultDir, "hook-signal.json");
7807
+ const additionalContextFile = join7(resultDir, "hook-signal.json");
7663
7808
  writeFileSync2(additionalContextFile, JSON.stringify({ context: [input.additionalContext] }) + "\n", { encoding: "utf-8", mode: 384, flag: "wx" });
7664
7809
  args.push("--additional-context-file", additionalContextFile);
7665
7810
  }
@@ -7687,7 +7832,7 @@ function runCodexSidecarAdvisory(input) {
7687
7832
  message: `sidecar command failed: ${sidecarFailureDetail(detail)}`
7688
7833
  };
7689
7834
  } else {
7690
- const parsed = JSON.parse(readFileSync4(resultFile, "utf-8"));
7835
+ const parsed = JSON.parse(readFileSync5(resultFile, "utf-8"));
7691
7836
  if (parsed.status !== "ok" || typeof parsed.summary !== "string") {
7692
7837
  advisory = {
7693
7838
  status: "failed",
@@ -7755,7 +7900,7 @@ async function runPublish(ctx, opts, dependencies = {}) {
7755
7900
  const isTty = dependencies.isTty ?? (() => Boolean(process.stdin.isTTY));
7756
7901
  const confirm = dependencies.confirm ?? askOnce;
7757
7902
  const projectRoot = process.cwd();
7758
- const hasCodexSidecarConfig = dependencies.hasCodexSidecarConfig ?? ((root) => existsSync5(join7(root, ".codex-sidecar.yml")));
7903
+ const hasCodexSidecarConfig = dependencies.hasCodexSidecarConfig ?? ((root) => existsSync6(join8(root, ".codex-sidecar.yml")));
7759
7904
  const publishAdvisory = hasCodexSidecarConfig(projectRoot) ? (changes) => {
7760
7905
  try {
7761
7906
  return dependencies.runCodexSidecarAdvisory?.(changes, projectRoot) ?? formatCodexSidecarAdvisory(runCodexSidecarAdvisory({
@@ -7812,24 +7957,25 @@ async function runInit(ctx, opts = { skipClaude: false, dryRun: false }, depende
7812
7957
  const ghRunner = dependencies.ghRunner ?? runGh;
7813
7958
  let publishTarget = ctx.config.publishTarget;
7814
7959
  let codexHookState = "not-installed";
7960
+ let cursorHookState = "not-installed";
7815
7961
  ensureUserConfig(ctx.userConfigPath);
7816
7962
  ctx.logger.info(`user config: ${ctx.userConfigPath}`);
7817
- if (!existsSync6(ctx.paths.knowledgeRepo)) {
7818
- mkdirSync2(ctx.paths.knowledgeRepo, { recursive: true });
7819
- mkdirSync2(ctx.paths.entriesDir, { recursive: true });
7963
+ if (!existsSync7(ctx.paths.knowledgeRepo)) {
7964
+ mkdirSync3(ctx.paths.knowledgeRepo, { recursive: true });
7965
+ mkdirSync3(ctx.paths.entriesDir, { recursive: true });
7820
7966
  ctx.logger.info(`knowledge repo scaffolded: ${ctx.paths.knowledgeRepo}`);
7821
7967
  } else {
7822
7968
  ctx.logger.info(`knowledge repo: ${ctx.paths.knowledgeRepo}`);
7823
7969
  }
7824
7970
  migrateLegacyCommunityDir(ctx);
7825
- const gitignorePath = join8(ctx.paths.knowledgeRepo, ".gitignore");
7826
- if (!existsSync6(gitignorePath)) {
7971
+ const gitignorePath = join9(ctx.paths.knowledgeRepo, ".gitignore");
7972
+ if (!existsSync7(gitignorePath)) {
7827
7973
  writeFileSync3(gitignorePath, KNOWLEDGE_GITIGNORE, "utf-8");
7828
7974
  ctx.logger.info(`.gitignore created: ${gitignorePath}`);
7829
7975
  }
7830
7976
  if (!opts.dryRun) {
7831
7977
  const dbDir = dirname3(ctx.paths.dbPath);
7832
- if (!existsSync6(dbDir)) mkdirSync2(dbDir, { recursive: true });
7978
+ if (!existsSync7(dbDir)) mkdirSync3(dbDir, { recursive: true });
7833
7979
  const keyProvider = createKeyserverKeyProvider({ caveatHome: ctx.caveatHome });
7834
7980
  const failures = await prewarmSealedKeys({ paths: ctx.paths, keyProvider });
7835
7981
  for (const failure of failures) {
@@ -7943,7 +8089,7 @@ async function runInit(ctx, opts = { skipClaude: false, dryRun: false }, depende
7943
8089
  ctx.logger.warn("cannot determine CLI script path; skipping Claude integration");
7944
8090
  } else {
7945
8091
  const result = installClaudeIntegration({
7946
- claudeDir: join8(ctx.userHome, ".claude"),
8092
+ claudeDir: join9(ctx.userHome, ".claude"),
7947
8093
  cliScriptPath,
7948
8094
  nodePath: resolveHookNodePath(),
7949
8095
  dryRun: opts.dryRun,
@@ -7961,7 +8107,7 @@ async function runInit(ctx, opts = { skipClaude: false, dryRun: false }, depende
7961
8107
  codexHookState = "skipped";
7962
8108
  ctx.logger.warn("cannot determine CLI script path; skipping Codex hook integration");
7963
8109
  } else {
7964
- const codexHome = join8(ctx.userHome, ".codex");
8110
+ const codexHome = join9(ctx.userHome, ".codex");
7965
8111
  const result = installCodexHooks({
7966
8112
  codexHome,
7967
8113
  cliScriptPath,
@@ -7972,7 +8118,7 @@ async function runInit(ctx, opts = { skipClaude: false, dryRun: false }, depende
7972
8118
  if (result.feature === "blocked") {
7973
8119
  codexHookState = "skipped";
7974
8120
  ctx.logger.warn(
7975
- `${result.blockedReason}; preserving explicit consent. Set \`hooks = true\` in ${join8(codexHome, "config.toml")}, then rerun \`caveat init\`.`
8121
+ `${result.blockedReason}; preserving explicit consent. Set \`hooks = true\` in ${join9(codexHome, "config.toml")}, then rerun \`caveat init\`.`
7976
8122
  );
7977
8123
  } else if (opts.dryRun) {
7978
8124
  codexHookState = "skipped";
@@ -7982,7 +8128,35 @@ async function runInit(ctx, opts = { skipClaude: false, dryRun: false }, depende
7982
8128
  }
7983
8129
  }
7984
8130
  }
7985
- reportEnvironmentSummary(ctx, publishTarget, codexHookState, opts.dryRun);
8131
+ if (opts.skipCursorHook) {
8132
+ cursorHookState = "skipped";
8133
+ ctx.logger.info("Cursor hook integration skipped (--skip-cursor-hook)");
8134
+ } else if (existsSync7(join9(ctx.userHome, ".cursor"))) {
8135
+ const cliScriptPath = process.argv[1];
8136
+ if (!cliScriptPath) {
8137
+ cursorHookState = "skipped";
8138
+ ctx.logger.warn("cannot determine CLI script path; skipping Cursor hook integration");
8139
+ } else {
8140
+ const cursorDir = join9(ctx.userHome, ".cursor");
8141
+ const result = installCursorHooks({
8142
+ cursorDir,
8143
+ cliScriptPath,
8144
+ nodePath: resolveHookNodePath(),
8145
+ dryRun: opts.dryRun,
8146
+ logger: ctx.logger
8147
+ });
8148
+ if (opts.dryRun) {
8149
+ cursorHookState = "skipped";
8150
+ ctx.logger.info("[dry-run] would install Cursor hooks");
8151
+ } else {
8152
+ cursorHookState = detectCursorHookInstallation(cursorDir).installation;
8153
+ ctx.logger.info(`Cursor beforeSubmitPrompt hook: ${result.hooks.beforeSubmitPrompt}`);
8154
+ ctx.logger.info(`Cursor postToolUse hook: ${result.hooks.postToolUse}`);
8155
+ ctx.logger.info(`Cursor stop hook: ${result.hooks.stop}`);
8156
+ }
8157
+ }
8158
+ }
8159
+ reportEnvironmentSummary(ctx, publishTarget, codexHookState, cursorHookState, opts.dryRun);
7986
8160
  }
7987
8161
  function detectCodexAvailability() {
7988
8162
  const result = spawnSync4("codex --version", {
@@ -8003,36 +8177,37 @@ function gitOutput(args) {
8003
8177
  const result = spawnSync4("git", args, { encoding: "utf-8" });
8004
8178
  return result.status === 0 && typeof result.stdout === "string" ? result.stdout.trim() : null;
8005
8179
  }
8006
- function reportEnvironmentSummary(ctx, publishTarget, codexHookState, dryRun) {
8180
+ function reportEnvironmentSummary(ctx, publishTarget, codexHookState, cursorHookState, dryRun) {
8007
8181
  const prefix = dryRun ? "[dry-run] would have " : "";
8008
8182
  const isRepo = gitOutput(["-C", ctx.paths.knowledgeRepo, "rev-parse", "--is-inside-work-tree"]) === "true";
8009
8183
  const remote = isRepo ? gitOutput(["-C", ctx.paths.knowledgeRepo, "config", "--get", "remote.origin.url"]) : null;
8010
- const communityCount = existsSync6(ctx.paths.communityDir) ? readdirSync(ctx.paths.communityDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).length : 0;
8184
+ const communityCount = existsSync7(ctx.paths.communityDir) ? readdirSync(ctx.paths.communityDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).length : 0;
8011
8185
  ctx.logger.info(`${prefix}environment summary:`);
8012
8186
  ctx.logger.info(` own git: ${isRepo ? "repository" : "not initialized"}`);
8013
8187
  ctx.logger.info(` private remote: ${remote || "not configured"}`);
8014
8188
  ctx.logger.info(` publish target: ${publishTarget || "not configured"}`);
8015
8189
  ctx.logger.info(` community sources: ${communityCount}`);
8016
8190
  ctx.logger.info(` codex hook: ${codexHookState}`);
8191
+ ctx.logger.info(` cursor hook: ${cursorHookState}`);
8017
8192
  }
8018
8193
  function errorMessage(err) {
8019
8194
  return err instanceof Error ? err.message : String(err);
8020
8195
  }
8021
8196
  function migrateLegacyCommunityDir(ctx) {
8022
- const legacy = join8(ctx.paths.knowledgeRepo, "community");
8197
+ const legacy = join9(ctx.paths.knowledgeRepo, "community");
8023
8198
  const current = ctx.paths.communityDir;
8024
8199
  if (legacy === current) return;
8025
- if (!existsSync6(legacy)) return;
8026
- if (existsSync6(current)) {
8200
+ if (!existsSync7(legacy)) return;
8201
+ if (existsSync7(current)) {
8027
8202
  ctx.logger.warn(
8028
8203
  `legacy community dir still exists at ${legacy} \u2014 remove manually (new location in use)`
8029
8204
  );
8030
8205
  return;
8031
8206
  }
8032
- mkdirSync2(current, { recursive: true });
8207
+ mkdirSync3(current, { recursive: true });
8033
8208
  for (const entry of readdirSync(legacy, { withFileTypes: true })) {
8034
8209
  if (!entry.isDirectory()) continue;
8035
- renameSync(join8(legacy, entry.name), join8(current, entry.name));
8210
+ renameSync(join9(legacy, entry.name), join9(current, entry.name));
8036
8211
  }
8037
8212
  try {
8038
8213
  rmdirSync(legacy);
@@ -8047,7 +8222,7 @@ function runUninstall(ctx, opts) {
8047
8222
  process.exit(1);
8048
8223
  }
8049
8224
  const result = uninstallClaudeIntegration({
8050
- claudeDir: join8(ctx.userHome, ".claude"),
8225
+ claudeDir: join9(ctx.userHome, ".claude"),
8051
8226
  cliScriptPath,
8052
8227
  nodePath: resolveHookNodePath(),
8053
8228
  dryRun: opts.dryRun,
@@ -8094,11 +8269,11 @@ function reportInstallResult(ctx, result, dryRun) {
8094
8269
  }
8095
8270
 
8096
8271
  // src/commands/indexCmd.ts
8097
- import { existsSync as existsSync7, mkdirSync as mkdirSync3 } from "node:fs";
8272
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4 } from "node:fs";
8098
8273
  import { dirname as dirname4 } from "node:path";
8099
8274
  async function runIndex(ctx, opts) {
8100
8275
  const dbDir = dirname4(ctx.paths.dbPath);
8101
- if (!existsSync7(dbDir)) mkdirSync3(dbDir, { recursive: true });
8276
+ if (!existsSync8(dbDir)) mkdirSync4(dbDir, { recursive: true });
8102
8277
  const keyProvider = createKeyserverKeyProvider({ caveatHome: ctx.caveatHome });
8103
8278
  const failures = await prewarmSealedKeys({ paths: ctx.paths, keyProvider });
8104
8279
  for (const failure of failures) {
@@ -8282,7 +8457,7 @@ function runStats(ctx) {
8282
8457
 
8283
8458
  // src/commands/serve.ts
8284
8459
  async function runServe(opts) {
8285
- const { startServer } = await import("./server-GBVHOF67.js");
8460
+ const { startServer } = await import("./server-HKXLHXNN.js");
8286
8461
  const { port, host } = startServer({ port: opts.port });
8287
8462
  process.stdout.write(`[caveat] web portal: http://${host}:${port}/
8288
8463
  `);
@@ -32354,7 +32529,7 @@ var StdioServerTransport = class {
32354
32529
 
32355
32530
  // ../mcp/dist/context.js
32356
32531
  import { homedir as homedir2 } from "node:os";
32357
- import { join as join9 } from "node:path";
32532
+ import { join as join10 } from "node:path";
32358
32533
 
32359
32534
  // ../mcp/dist/autoSyncTrigger.js
32360
32535
  function triggerAutoSyncAfterWrite(caveatHome, logger) {
@@ -32373,7 +32548,7 @@ function triggerAutoSyncAfterWrite(caveatHome, logger) {
32373
32548
  function buildMcpContext(overrides = {}) {
32374
32549
  const userHome = overrides.userHome ?? homedir2();
32375
32550
  const caveatHome = overrides.caveatHome ?? findCaveatHome(userHome);
32376
- const userConfigPath = join9(userHome, ".caveatrc.json");
32551
+ const userConfigPath = join10(userHome, ".caveatrc.json");
32377
32552
  const logger = overrides.logger ?? stderrLogger;
32378
32553
  const config2 = loadConfig(userConfigPath);
32379
32554
  const paths = resolvePaths(caveatHome, config2.knowledgeRepo, userHome);
@@ -32522,12 +32697,12 @@ function handleListRecent(ctx, args) {
32522
32697
  }
32523
32698
 
32524
32699
  // ../mcp/dist/tools/pull.js
32525
- import { existsSync as existsSync8 } from "node:fs";
32700
+ import { existsSync as existsSync9 } from "node:fs";
32526
32701
  var pullInputShape = {};
32527
32702
  async function handlePull(ctx, _args = {}) {
32528
32703
  const pulled = [];
32529
32704
  const indexed = [];
32530
- if (existsSync8(ctx.paths.communityDir)) {
32705
+ if (existsSync9(ctx.paths.communityDir)) {
32531
32706
  const results = await communityPull({
32532
32707
  communityDir: ctx.paths.communityDir,
32533
32708
  logger: ctx.logger
@@ -32683,28 +32858,28 @@ async function runMcpServer() {
32683
32858
  import { spawn as spawn2 } from "node:child_process";
32684
32859
  import {
32685
32860
  chmodSync,
32686
- existsSync as existsSync11,
32861
+ existsSync as existsSync12,
32687
32862
  lstatSync,
32688
32863
  mkdtempSync as mkdtempSync2,
32689
- mkdirSync as mkdirSync5,
32864
+ mkdirSync as mkdirSync6,
32690
32865
  readdirSync as readdirSync2,
32691
- readFileSync as readFileSync6,
32866
+ readFileSync as readFileSync7,
32692
32867
  realpathSync as realpathSync3,
32693
32868
  rmSync as rmSync2,
32694
32869
  writeFileSync as writeFileSync5
32695
32870
  } from "node:fs";
32696
32871
  import { tmpdir as tmpdir2 } from "node:os";
32697
- import { basename, dirname as dirname5, join as join12 } from "node:path";
32872
+ import { basename, dirname as dirname5, join as join13 } from "node:path";
32698
32873
  import { randomBytes } from "node:crypto";
32699
32874
 
32700
32875
  // src/autoReindexTrigger.ts
32701
32876
  import { spawn } from "node:child_process";
32702
- import { existsSync as existsSync9 } from "node:fs";
32703
- import { join as join10 } from "node:path";
32877
+ import { existsSync as existsSync10 } from "node:fs";
32878
+ import { join as join11 } from "node:path";
32704
32879
  function maybeTriggerAutoReindex(ctx) {
32705
32880
  if (process.env.CAVEAT_INDEX_AUTOSYNC === "off") return;
32706
- if (!existsSync9(ctx.paths.dbPath)) return;
32707
- if (existsSync9(join10(ctx.caveatHome, "index", ".reindex-lock"))) return;
32881
+ if (!existsSync10(ctx.paths.dbPath)) return;
32882
+ if (existsSync10(join11(ctx.caveatHome, "index", ".reindex-lock"))) return;
32708
32883
  const current = computeEntriesDigest(ctx.paths);
32709
32884
  const marker = readDigestMarker(ctx.caveatHome);
32710
32885
  if (marker?.digest === current.digest && marker.fileCount === current.fileCount) return;
@@ -32730,8 +32905,8 @@ function maybeTriggerAutoSync(ctx, debounceMs = AUTO_SYNC_DEBOUNCE_MS) {
32730
32905
  }
32731
32906
 
32732
32907
  // src/hookShared.ts
32733
- import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
32734
- import { join as join11 } from "node:path";
32908
+ import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
32909
+ import { join as join12 } from "node:path";
32735
32910
  import { createHash } from "node:crypto";
32736
32911
  var MAX_CONTEXT_BLOCKS = 3;
32737
32912
  var STOP_REMINDER_PREFIX = "[caveat] \u3053\u306E\u30BB\u30C3\u30B7\u30E7\u30F3\u3067\u5916\u90E8\u4ED5\u69D8\u306E\u7F60\u306B\u5F53\u305F\u3063\u305F\u53EF\u80FD\u6027\u3092\u793A\u3059\u30B7\u30B0\u30CA\u30EB:";
@@ -32786,7 +32961,7 @@ function searchCaveatsSafely(host, input) {
32786
32961
  let hits;
32787
32962
  try {
32788
32963
  const ctx = buildContextSafely(host);
32789
- if (!ctx || !existsSync10(ctx.paths.dbPath)) return [];
32964
+ if (!ctx || !existsSync11(ctx.paths.dbPath)) return [];
32790
32965
  caveatHome = ctx.caveatHome;
32791
32966
  db = openDb({ path: ctx.paths.dbPath });
32792
32967
  const searchOptions = {
@@ -32872,19 +33047,19 @@ function stopSignalKey(signals, related) {
32872
33047
  return createHash("sha256").update(body).digest("hex");
32873
33048
  }
32874
33049
  function stopStatePath(host, caveatHome, sessionId) {
32875
- return join11(caveatHome, host.stopStateDir, `${sanitizeStateId(sessionId)}.txt`);
33050
+ return join12(caveatHome, host.stopStateDir, `${sanitizeStateId(sessionId)}.txt`);
32876
33051
  }
32877
33052
  function wasStopReminderQueued(host, caveatHome, sessionId, key) {
32878
33053
  const path = stopStatePath(host, caveatHome, sessionId);
32879
33054
  try {
32880
- return readFileSync5(path, "utf-8") === key;
33055
+ return readFileSync6(path, "utf-8") === key;
32881
33056
  } catch {
32882
33057
  return false;
32883
33058
  }
32884
33059
  }
32885
33060
  function markStopReminderQueued(host, caveatHome, sessionId, key) {
32886
33061
  const path = stopStatePath(host, caveatHome, sessionId);
32887
- mkdirSync4(join11(caveatHome, host.stopStateDir), { recursive: true });
33062
+ mkdirSync5(join12(caveatHome, host.stopStateDir), { recursive: true });
32888
33063
  writeFileSync4(path, key, "utf-8");
32889
33064
  }
32890
33065
  function queueStopForSession(host, sessionId, signals, related, buildText) {
@@ -32996,9 +33171,9 @@ function spawnWorker(job) {
32996
33171
  try {
32997
33172
  root = workerRoot();
32998
33173
  sweepStaleWorkerDirs(Date.now(), root);
32999
- workDir = mkdtempSync2(join12(root, "job-"));
33174
+ workDir = mkdtempSync2(join13(root, "job-"));
33000
33175
  chmodSync(workDir, 448);
33001
- workFile = join12(workDir, `${randomBytes(4).toString("hex")}.json`);
33176
+ workFile = join13(workDir, `${randomBytes(4).toString("hex")}.json`);
33002
33177
  writeFileSync5(workFile, JSON.stringify({ ...job, schemaVersion: "caveat-worker-job/v2" }), { encoding: "utf-8", mode: 384, flag: "wx" });
33003
33178
  } catch (err) {
33004
33179
  process.stderr.write(`[caveat:hook] worker writefile error: ${errorMessage4(err)}
@@ -33030,7 +33205,7 @@ function spawnWorker(job) {
33030
33205
  async function runWorker(workFile) {
33031
33206
  let raw;
33032
33207
  try {
33033
- raw = readFileSync6(workFile, "utf-8");
33208
+ raw = readFileSync7(workFile, "utf-8");
33034
33209
  } catch {
33035
33210
  process.exit(0);
33036
33211
  }
@@ -33094,7 +33269,7 @@ function sweepStaleWorkerDirs(now = Date.now(), root = workerRoot()) {
33094
33269
  }
33095
33270
  for (const entry of entries) {
33096
33271
  if (!entry.startsWith("job-")) continue;
33097
- const path = join12(root, entry);
33272
+ const path = join13(root, entry);
33098
33273
  try {
33099
33274
  if (!isOwnedWorkerDir(path, root)) continue;
33100
33275
  const stat = lstatSync(path);
@@ -33106,29 +33281,29 @@ function sweepStaleWorkerDirs(now = Date.now(), root = workerRoot()) {
33106
33281
  }
33107
33282
  }
33108
33283
  function workerRoot(base = tmpdir2()) {
33109
- const root = join12(base, WORKER_ROOT);
33110
- mkdirSync5(root, { recursive: true, mode: 448 });
33284
+ const root = join13(base, WORKER_ROOT);
33285
+ mkdirSync6(root, { recursive: true, mode: 448 });
33111
33286
  const stat = lstatSync(root);
33112
33287
  const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
33113
33288
  if (!stat.isDirectory() || stat.isSymbolicLink() || !hasPrivateOwnership(stat, uid)) throw new Error("worker root is unsafe");
33114
- const marker = join12(root, WORKER_MARKER);
33289
+ const marker = join13(root, WORKER_MARKER);
33115
33290
  try {
33116
33291
  writeFileSync5(marker, "caveat-worker/v1\n", { mode: 384, flag: "wx" });
33117
33292
  } catch (error51) {
33118
33293
  if (!(error51 && typeof error51 === "object" && "code" in error51 && error51.code === "EEXIST")) throw error51;
33119
33294
  }
33120
33295
  const markerStat = lstatSync(marker);
33121
- if (!markerStat.isFile() || markerStat.isSymbolicLink() || !hasPrivateOwnership(markerStat, uid) || readFileSync6(marker, "utf-8") !== "caveat-worker/v1\n") throw new Error("worker root marker is invalid");
33296
+ if (!markerStat.isFile() || markerStat.isSymbolicLink() || !hasPrivateOwnership(markerStat, uid) || readFileSync7(marker, "utf-8") !== "caveat-worker/v1\n") throw new Error("worker root marker is invalid");
33122
33297
  return root;
33123
33298
  }
33124
33299
  function isStaleWorkerJobDir(path) {
33125
33300
  const entries = readdirSync2(path);
33126
33301
  if (entries.length !== 1 || !entries[0].endsWith(".json")) return false;
33127
- const file2 = join12(path, entries[0]);
33302
+ const file2 = join13(path, entries[0]);
33128
33303
  const stat = lstatSync(file2);
33129
33304
  const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
33130
33305
  if (!stat.isFile() || stat.isSymbolicLink() || !hasPrivateOwnership(stat, uid)) return false;
33131
- return isKnownStaleWorkerJob(JSON.parse(readFileSync6(file2, "utf-8")));
33306
+ return isKnownStaleWorkerJob(JSON.parse(readFileSync7(file2, "utf-8")));
33132
33307
  }
33133
33308
  function hasPrivateOwnership(stat, uid) {
33134
33309
  return isPrivateOwnerStat(stat, uid);
@@ -33150,7 +33325,7 @@ function isKnownStaleWorkerJob(value) {
33150
33325
  }
33151
33326
  function writeLastReindex(caveatHome, value) {
33152
33327
  try {
33153
- writeFileSync5(join12(caveatHome, "index", ".last-reindex.json"), JSON.stringify(value), "utf-8");
33328
+ writeFileSync5(join13(caveatHome, "index", ".last-reindex.json"), JSON.stringify(value), "utf-8");
33154
33329
  } catch (err) {
33155
33330
  process.stderr.write(`[caveat:hook] reindex status write error: ${errorMessage4(err)}
33156
33331
  `);
@@ -33162,7 +33337,7 @@ async function runReindexWorker() {
33162
33337
  return;
33163
33338
  }
33164
33339
  const ctx = buildContextSafely(CLAUDE_HOST);
33165
- if (!ctx || !existsSync11(ctx.paths.dbPath)) {
33340
+ if (!ctx || !existsSync12(ctx.paths.dbPath)) {
33166
33341
  process.stderr.write("[caveat:hook] auto reindex skipped: index database does not exist\n");
33167
33342
  return;
33168
33343
  }
@@ -33229,7 +33404,7 @@ function buildToolErrorReminder(job, hits) {
33229
33404
  const mode = hookCodexSidecarMode();
33230
33405
  if (mode === "off") return base;
33231
33406
  const projectRoot = process.cwd();
33232
- const hasSidecarConfig = existsSync11(join12(projectRoot, ".codex-sidecar.yml"));
33407
+ const hasSidecarConfig = existsSync12(join13(projectRoot, ".codex-sidecar.yml"));
33233
33408
  if (mode === "auto" && !hasSidecarConfig) return base;
33234
33409
  const advisory = runCodexSidecarAdvisory({
33235
33410
  searchText: job.failureText,
@@ -33265,7 +33440,7 @@ function buildStopReminder(signals, related) {
33265
33440
  const mode = hookCodexSidecarMode();
33266
33441
  if (mode === "off") return base;
33267
33442
  const projectRoot = process.cwd();
33268
- const hasSidecarConfig = existsSync11(join12(projectRoot, ".codex-sidecar.yml"));
33443
+ const hasSidecarConfig = existsSync12(join13(projectRoot, ".codex-sidecar.yml"));
33269
33444
  if (mode === "auto" && !hasSidecarConfig) return base;
33270
33445
  const advisory = runCodexSidecarAdvisory({
33271
33446
  searchText: struggleSearchText(signals),
@@ -33405,9 +33580,9 @@ async function runHook(name, arg) {
33405
33580
 
33406
33581
  // src/commands/codexHookCmd.ts
33407
33582
  import { spawn as spawn3, spawnSync as spawnSync5 } from "node:child_process";
33408
- import { existsSync as existsSync12, readFileSync as readFileSync7, unlinkSync, writeFileSync as writeFileSync6 } from "node:fs";
33583
+ import { existsSync as existsSync13, readFileSync as readFileSync8, unlinkSync, writeFileSync as writeFileSync6 } from "node:fs";
33409
33584
  import { homedir as homedir3, tmpdir as tmpdir3 } from "node:os";
33410
- import { join as join13 } from "node:path";
33585
+ import { join as join14 } from "node:path";
33411
33586
  import { randomBytes as randomBytes2 } from "node:crypto";
33412
33587
  var CODEX_HOST = {
33413
33588
  agent: "codex",
@@ -33437,10 +33612,10 @@ function processExitCodeFromText(text) {
33437
33612
  return m ? Number(m[1]) : null;
33438
33613
  }
33439
33614
  function transcriptToolOutput(transcriptPath, toolUseId) {
33440
- if (!transcriptPath || !toolUseId || !existsSync12(transcriptPath)) return null;
33615
+ if (!transcriptPath || !toolUseId || !existsSync13(transcriptPath)) return null;
33441
33616
  let raw = "";
33442
33617
  try {
33443
- raw = readFileSync7(transcriptPath, "utf-8");
33618
+ raw = readFileSync8(transcriptPath, "utf-8");
33444
33619
  } catch {
33445
33620
  return null;
33446
33621
  }
@@ -33584,7 +33759,7 @@ async function processCodexWorkerJob(job, opts = { waitForTranscript: true }) {
33584
33759
  async function runCodexWorker(workFile) {
33585
33760
  let raw;
33586
33761
  try {
33587
- raw = readFileSync7(workFile, "utf-8");
33762
+ raw = readFileSync8(workFile, "utf-8");
33588
33763
  } catch {
33589
33764
  process.exit(0);
33590
33765
  }
@@ -33601,7 +33776,7 @@ async function runCodexWorker(workFile) {
33601
33776
  await processCodexWorkerJob(job);
33602
33777
  process.exit(0);
33603
33778
  }
33604
- function runDiagnostics(codexHome = process.env.CODEX_HOME ?? join13(homedir3(), ".codex")) {
33779
+ function runDiagnostics(codexHome = process.env.CODEX_HOME ?? join14(homedir3(), ".codex")) {
33605
33780
  const features = spawnSync5("codex", ["features", "list"], {
33606
33781
  encoding: "utf-8",
33607
33782
  maxBuffer: 1024 * 1024,
@@ -33706,10 +33881,151 @@ async function runCodexHook(name, arg) {
33706
33881
  process.exit(0);
33707
33882
  }
33708
33883
 
33884
+ // src/commands/cursorHookCmd.ts
33885
+ import { homedir as homedir4 } from "node:os";
33886
+ import { join as join15 } from "node:path";
33887
+ var CURSOR_HOST = {
33888
+ agent: "cursor",
33889
+ stderrTag: "caveat:cursor-hook",
33890
+ errorCode: "CAVEAT.CURSOR_HOOK_FAILED",
33891
+ stopStateDir: "cursor-stop-state",
33892
+ stopDedupeKey: "cursor-stop-reminder"
33893
+ };
33894
+ function cursorContextOutput(text) {
33895
+ return JSON.stringify({ additional_context: text });
33896
+ }
33897
+ function cursorSessionId(payload) {
33898
+ for (const key of ["conversation_id", "session_id", "sessionId"]) {
33899
+ const value = payload[key];
33900
+ if (typeof value === "string" && value.length > 0) return value;
33901
+ }
33902
+ return "_unknown";
33903
+ }
33904
+ function cursorPrompt(payload) {
33905
+ return typeof payload.prompt === "string" ? payload.prompt : "";
33906
+ }
33907
+ function isCursorToolError(payload) {
33908
+ if (payload.hook_event_name === "postToolUseFailure") return true;
33909
+ if (payload.is_error === true) return true;
33910
+ if (typeof payload.error === "string" && payload.error.length > 0) return true;
33911
+ const resp = payload.tool_response ?? payload.toolResponse;
33912
+ if (resp !== null && typeof resp === "object" && !Array.isArray(resp)) {
33913
+ if (resp.is_error === true) return true;
33914
+ }
33915
+ return false;
33916
+ }
33917
+ function loadSignalsSafely3(path) {
33918
+ try {
33919
+ return readSessionSignals(path);
33920
+ } catch (err) {
33921
+ process.stderr.write(`[caveat:cursor-hook] transcript read error: ${errorMessage4(err)}
33922
+ `);
33923
+ return null;
33924
+ }
33925
+ }
33926
+ function runDiagnostics2(cursorDir = join15(homedir4(), ".cursor")) {
33927
+ const installation = detectCursorHookInstallation(cursorDir);
33928
+ process.stdout.write(`${JSON.stringify({
33929
+ installation: installation.installation,
33930
+ cursorDir,
33931
+ hooksPath: installation.hooksPath,
33932
+ installedHooks: installation.hooks
33933
+ }, null, 2)}
33934
+ `);
33935
+ }
33936
+ async function runCursorHook(name, arg) {
33937
+ if (name === "diagnostics") {
33938
+ runDiagnostics2(arg);
33939
+ return;
33940
+ }
33941
+ let raw = "";
33942
+ try {
33943
+ raw = await readStdin();
33944
+ } catch (err) {
33945
+ process.stderr.write(`[caveat:cursor-hook] stdin read error: ${errorMessage4(err)}
33946
+ `);
33947
+ process.exit(0);
33948
+ }
33949
+ const payload = parsePayload(CURSOR_HOST, raw);
33950
+ const sessionId = cursorSessionId(payload);
33951
+ const contexts = name === "stop" ? [] : drainForSession(CURSOR_HOST, sessionId);
33952
+ if (name === "user-prompt-submit") {
33953
+ const prompt = cursorPrompt(payload);
33954
+ const hits = searchCaveatsSafely(CURSOR_HOST, {
33955
+ topicText: prompt,
33956
+ failureText: prompt,
33957
+ surface: "user_prompt"
33958
+ });
33959
+ if (hits.length > 0) contexts.push(userPromptSubmitReminderText(hits));
33960
+ const compacted = compactContexts(CURSOR_HOST, contexts);
33961
+ if (compacted.length > 0) {
33962
+ process.stdout.write(`${cursorContextOutput(compacted.join("\n\n"))}
33963
+ `);
33964
+ }
33965
+ process.exit(0);
33966
+ }
33967
+ if (name === "post-tool-use") {
33968
+ if (isCursorToolError(payload)) {
33969
+ const errText = extractToolResponseText(
33970
+ payload.tool_response ?? payload.toolResponse ?? payload.error ?? payload
33971
+ );
33972
+ const hits = searchCaveatsSafely(CURSOR_HOST, {
33973
+ topicText: errText,
33974
+ failureText: errText,
33975
+ surface: "tool_error"
33976
+ });
33977
+ if (hits.length > 0) contexts.push(toolErrorReminderText(hits));
33978
+ }
33979
+ const compacted = compactContexts(CURSOR_HOST, contexts);
33980
+ if (compacted.length > 0) {
33981
+ process.stdout.write(`${cursorContextOutput(compacted.join("\n\n"))}
33982
+ `);
33983
+ }
33984
+ process.exit(0);
33985
+ }
33986
+ if (name === "stop") {
33987
+ const ctx = buildContextSafely(CURSOR_HOST);
33988
+ if (ctx) {
33989
+ try {
33990
+ maybeSweepPendingDirs(ctx.caveatHome);
33991
+ } catch (err) {
33992
+ process.stderr.write(`[caveat:cursor-hook] pending sweep error: ${errorMessage4(err)}
33993
+ `);
33994
+ }
33995
+ try {
33996
+ maybeTriggerAutoReindex(ctx);
33997
+ } catch (err) {
33998
+ process.stderr.write(`[caveat:cursor-hook] auto reindex trigger error: ${errorMessage4(err)}
33999
+ `);
34000
+ }
34001
+ try {
34002
+ maybeTriggerAutoSync(ctx);
34003
+ } catch (err) {
34004
+ process.stderr.write(`[caveat:cursor-hook] auto sync trigger error: ${errorMessage4(err)}
34005
+ `);
34006
+ }
34007
+ }
34008
+ if (payload.stop_hook_active === true) process.exit(0);
34009
+ const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : "";
34010
+ const signals = transcriptPath ? loadSignalsSafely3(transcriptPath) : null;
34011
+ if (!signals || !hasAnyStruggleSignal(signals)) process.exit(0);
34012
+ const related = searchCaveatsSafely(CURSOR_HOST, signals.errorSnippets.map((failureText) => ({
34013
+ topicText: "",
34014
+ failureText,
34015
+ surface: "stop"
34016
+ })));
34017
+ queueStopForSession(CURSOR_HOST, sessionId, signals, related, () => stopReminderText(signals, related));
34018
+ process.exit(0);
34019
+ }
34020
+ process.stderr.write(`[caveat:cursor-hook] unknown hook name: ${name}
34021
+ `);
34022
+ process.exit(0);
34023
+ }
34024
+
33709
34025
  // src/commands/pull.ts
33710
- import { existsSync as existsSync13 } from "node:fs";
34026
+ import { existsSync as existsSync14 } from "node:fs";
33711
34027
  async function runPull(ctx) {
33712
- const hasCommunityDir = existsSync13(ctx.paths.communityDir);
34028
+ const hasCommunityDir = existsSync14(ctx.paths.communityDir);
33713
34029
  if (!hasCommunityDir) {
33714
34030
  ctx.logger.info(
33715
34031
  "no community repos yet \u2014 add one with `caveat community add <github-url>`."
@@ -33809,9 +34125,9 @@ async function runSync(ctx, opts, dependencies = {}) {
33809
34125
 
33810
34126
  // src/commands/codexSidecar.ts
33811
34127
  import { spawnSync as spawnSync6 } from "node:child_process";
33812
- import { closeSync, constants as constants3, fstatSync, lstatSync as lstatSync2, mkdirSync as mkdirSync6, mkdtempSync as mkdtempSync3, openSync, readSync as readSync2, realpathSync as realpathSync4, rmSync as rmSync3, writeFileSync as writeFileSync7 } from "node:fs";
34128
+ import { closeSync, constants as constants4, fstatSync, lstatSync as lstatSync2, mkdirSync as mkdirSync7, mkdtempSync as mkdtempSync3, openSync, readSync as readSync2, realpathSync as realpathSync4, rmSync as rmSync3, writeFileSync as writeFileSync7 } from "node:fs";
33813
34129
  import { tmpdir as tmpdir4 } from "node:os";
33814
- import { basename as basename2, dirname as dirname6, join as join14 } from "node:path";
34130
+ import { basename as basename2, dirname as dirname6, join as join16 } from "node:path";
33815
34131
  import { cwd, exit } from "node:process";
33816
34132
  function runCodexSidecarDiagnostics(logger, opts) {
33817
34133
  const plan = buildCodexSidecarDiagnosticsCommand({
@@ -33843,8 +34159,8 @@ function runCodexSidecarWithCaveats(ctx, workflow, prompt, opts) {
33843
34159
  process.stdout.write(JSON.stringify({ status: "skipped", decision }, null, 2) + "\n");
33844
34160
  exit(0);
33845
34161
  }
33846
- const contextDir = mkdtempSync3(join14(tmpdir4(), "caveat-sidecar-context-"));
33847
- const contextFile = join14(contextDir, "context.json");
34162
+ const contextDir = mkdtempSync3(join16(tmpdir4(), "caveat-sidecar-context-"));
34163
+ const contextFile = join16(contextDir, "context.json");
33848
34164
  let status2 = 1;
33849
34165
  try {
33850
34166
  const blocks = collectCaveatContextBlocks(ctx, {
@@ -33877,7 +34193,7 @@ function readHookSignalAdditionalContextFile(path, testProbe) {
33877
34193
  const uid = typeof process.getuid === "function" ? process.getuid() : void 0;
33878
34194
  assertPrivateRegular(before, uid, platform);
33879
34195
  testProbe?.afterLstat?.();
33880
- const fd = openSync(path, constants3.O_RDONLY | constants3.O_NONBLOCK | (constants3.O_NOFOLLOW ?? 0));
34196
+ const fd = openSync(path, constants4.O_RDONLY | constants4.O_NONBLOCK | (constants4.O_NOFOLLOW ?? 0));
33881
34197
  let raw;
33882
34198
  try {
33883
34199
  const opened = fstatSync(fd);
@@ -34007,7 +34323,7 @@ function executePlan(logger, command, args, options = {}) {
34007
34323
  }
34008
34324
  function saveStructuredResult(path, stdout) {
34009
34325
  const parsed = JSON.parse(stdout);
34010
- mkdirSync6(dirname6(path), { recursive: true });
34326
+ mkdirSync7(dirname6(path), { recursive: true });
34011
34327
  writeFileSync7(path, JSON.stringify(parsed, null, 2) + "\n", "utf-8");
34012
34328
  }
34013
34329
  function shellDisplayQuote(value) {
@@ -34096,8 +34412,8 @@ function runCommunityRemove(ctx, handle, opts) {
34096
34412
 
34097
34413
  // src/commands/factoryDiagnostics.ts
34098
34414
  import { execFileSync } from "node:child_process";
34099
- import { existsSync as existsSync14, readFileSync as readFileSync8 } from "node:fs";
34100
- import { join as join15 } from "node:path";
34415
+ import { existsSync as existsSync15, readFileSync as readFileSync9 } from "node:fs";
34416
+ import { join as join17 } from "node:path";
34101
34417
  import { DatabaseSync } from "node:sqlite";
34102
34418
  import { parse as parseToml2 } from "smol-toml";
34103
34419
  var status = (ok, reason) => ({ status: ok ? "ready" : "not_ready", reason_code: ok ? "ready" : reason });
@@ -34109,10 +34425,10 @@ function isRecord2(value) {
34109
34425
  return typeof value === "object" && value !== null && !Array.isArray(value);
34110
34426
  }
34111
34427
  function claudeRegistration(home, nodePath, cliScriptPath) {
34112
- const path = join15(home, ".claude.json");
34113
- if (!existsSync14(path)) return status(false, "not_registered");
34428
+ const path = join17(home, ".claude.json");
34429
+ if (!existsSync15(path)) return status(false, "not_registered");
34114
34430
  try {
34115
- const value = JSON.parse(readFileSync8(path, "utf8"));
34431
+ const value = JSON.parse(readFileSync9(path, "utf8"));
34116
34432
  if (!isRecord2(value) || !isRecord2(value.mcpServers)) return status(false, "not_registered");
34117
34433
  return status(isCaveatClaudeMcpRegistration(value.mcpServers.caveat, nodePath, cliScriptPath), "not_registered");
34118
34434
  } catch {
@@ -34121,7 +34437,7 @@ function claudeRegistration(home, nodePath, cliScriptPath) {
34121
34437
  }
34122
34438
  function claudeHooks(home, nodePath, cliScriptPath) {
34123
34439
  try {
34124
- const settings = JSON.parse(readFileSync8(join15(home, ".claude", "settings.json"), "utf8"));
34440
+ const settings = JSON.parse(readFileSync9(join17(home, ".claude", "settings.json"), "utf8"));
34125
34441
  const present = (event, subcommand) => settings.hooks?.[event]?.some((entry) => entry.hooks?.some((item) => typeof item.command === "string" && isCanonicalCaveatClaudeHookCommand(item.command, subcommand, nodePath, cliScriptPath))) ?? false;
34126
34442
  return { user_prompt_submit: hook(present("UserPromptSubmit", "user-prompt-submit")), post_tool_use: hook(present("PostToolUse", "post-tool-use")), post_tool_use_failure: hook(present("PostToolUseFailure", "post-tool-use")), stop: hook(present("Stop", "stop")) };
34127
34443
  } catch {
@@ -34143,7 +34459,7 @@ function strictSearchSchema(db) {
34143
34459
  return columns.map((column) => `${column.name}:${column.hidden}`).join(",") === "id:0,title:0,body:0,tags:0,entries_fts:1,rank:1";
34144
34460
  }
34145
34461
  function database(path) {
34146
- if (!existsSync14(path)) return { status: "not_ready", reason_code: "missing", schema_version: null, supported_schema_version: 3, migration_status: "unverified" };
34462
+ if (!existsSync15(path)) return { status: "not_ready", reason_code: "missing", schema_version: null, supported_schema_version: 3, migration_status: "unverified" };
34147
34463
  try {
34148
34464
  const db = new DatabaseSync(path, { readOnly: true });
34149
34465
  try {
@@ -34168,10 +34484,10 @@ function database(path) {
34168
34484
  }
34169
34485
  }
34170
34486
  function codexFeature(codexHome) {
34171
- const path = join15(codexHome, "config.toml");
34172
- if (!existsSync14(path)) return status(false, "feature_disabled");
34487
+ const path = join17(codexHome, "config.toml");
34488
+ if (!existsSync15(path)) return status(false, "feature_disabled");
34173
34489
  try {
34174
- const config2 = parseToml2(readFileSync8(path, "utf8"));
34490
+ const config2 = parseToml2(readFileSync9(path, "utf8"));
34175
34491
  if (!isRecord2(config2.features)) return status(false, "feature_disabled");
34176
34492
  const features = config2.features;
34177
34493
  return status(features.hooks === true && features.codex_hooks === void 0, "feature_disabled");
@@ -34180,7 +34496,7 @@ function codexFeature(codexHome) {
34180
34496
  }
34181
34497
  }
34182
34498
  function codexHooks(codexHome, nodePath, cliScriptPath) {
34183
- const value = JSON.parse(readFileSync8(join15(codexHome, "hooks.json"), "utf8"));
34499
+ const value = JSON.parse(readFileSync9(join17(codexHome, "hooks.json"), "utf8"));
34184
34500
  if (!isRecord2(value) || !isRecord2(value.hooks)) throw Error("config_unreadable");
34185
34501
  const hooks = value.hooks;
34186
34502
  const present = (event, subcommand) => Array.isArray(hooks[event]) && hooks[event].some((entry) => isRecord2(entry) && Array.isArray(entry.hooks) && entry.hooks.some((item) => isCanonicalCaveatCodexHookEntry(item, subcommand, nodePath, cliScriptPath)));
@@ -34215,7 +34531,7 @@ function sync(own) {
34215
34531
  return unverified("upstream_unavailable");
34216
34532
  }
34217
34533
  }
34218
- function factoryDiagnostics(ctx, codexHome = process.env.CODEX_HOME ?? join15(ctx.userHome, ".codex")) {
34534
+ function factoryDiagnostics(ctx, codexHome = process.env.CODEX_HOME ?? join17(ctx.userHome, ".codex")) {
34219
34535
  const nodePath = process.execPath;
34220
34536
  const cliScriptPath = process.argv[1] ?? "";
34221
34537
  const db = database(ctx.paths.dbPath);
@@ -34290,7 +34606,7 @@ for (const action of ["resolve", "reopen"]) runtimeErrors.command(`${action} <fi
34290
34606
  });
34291
34607
  program.command("init").description(
34292
34608
  "Initialize ~/.caveatrc.json, ~/.caveat/, and register Claude Code integration. Add knowledge sources later with `caveat community add <github-url>`."
34293
- ).option("--skip-claude", "skip Claude Code MCP + hook registration", false).option("--sync [url]", "initialize private sync (optionally with its URL)").option("--publish-target [url]", "configure the sealed public mirror target").option("--yes", "approve creating conventional GitHub repositories", false).option("--skip-codex-hook", "skip Codex hook installation", false).option("--dry-run", "show planned changes without writing", false).option(
34609
+ ).option("--skip-claude", "skip Claude Code MCP + hook registration", false).option("--sync [url]", "initialize private sync (optionally with its URL)").option("--publish-target [url]", "configure the sealed public mirror target").option("--yes", "approve creating conventional GitHub repositories", false).option("--skip-codex-hook", "skip Codex hook installation", false).option("--skip-cursor-hook", "skip Cursor hook installation", false).option("--dry-run", "show planned changes without writing", false).option(
34294
34610
  "--pending-stale-days <days>",
34295
34611
  "sweep `<caveatHome>/pending/<sessionId>/` whose newest entry is older than this (default 7)",
34296
34612
  (raw) => {
@@ -34310,7 +34626,8 @@ program.command("init").description(
34310
34626
  sync: opts.sync,
34311
34627
  publishTarget: opts.publishTarget,
34312
34628
  yes: opts.yes,
34313
- skipCodexHook: opts.skipCodexHook
34629
+ skipCodexHook: opts.skipCodexHook,
34630
+ skipCursorHook: opts.skipCursorHook
34314
34631
  });
34315
34632
  }
34316
34633
  );
@@ -34438,6 +34755,57 @@ codexHook.command("stop").description("Run the Codex Stop hook").action(async ()
34438
34755
  codexHook.command("worker <workFile>").description("Run the detached Codex hook worker").action(async (workFile) => {
34439
34756
  await runCodexHook("worker", workFile);
34440
34757
  });
34758
+ var cursorHook = program.command("cursor-hook").description("Install or run Cursor hooks for Caveat");
34759
+ cursorHook.command("install").description("Install Caveat hooks into ~/.cursor/hooks.json (factory hooks are kept)").option("--dry-run", "show planned changes without writing", false).option("--cursor-dir <path>", "Cursor home directory", `${process.env.HOME}/.cursor`).action((opts) => {
34760
+ const cliScriptPath = process.argv[1];
34761
+ if (!cliScriptPath) {
34762
+ process.stderr.write("[caveat:error] cannot determine CLI script path\n");
34763
+ process.exit(1);
34764
+ }
34765
+ const result = installCursorHooks({
34766
+ cursorDir: opts.cursorDir,
34767
+ cliScriptPath,
34768
+ nodePath: resolveHookNodePath(),
34769
+ dryRun: opts.dryRun,
34770
+ logger: stdoutLogger
34771
+ });
34772
+ stdoutLogger.info(`beforeSubmitPrompt hook: ${result.hooks.beforeSubmitPrompt}`);
34773
+ stdoutLogger.info(`postToolUse hook: ${result.hooks.postToolUse}`);
34774
+ stdoutLogger.info(`postToolUseFailure hook: ${result.hooks.postToolUseFailure}`);
34775
+ stdoutLogger.info(`stop hook: ${result.hooks.stop}`);
34776
+ if (result.backupPath) stdoutLogger.info(`hooks.json backed up: ${result.backupPath}`);
34777
+ });
34778
+ cursorHook.command("uninstall").description("Remove Caveat-owned Cursor hooks from ~/.cursor/hooks.json").option("--dry-run", "show planned changes without writing", false).option("--cursor-dir <path>", "Cursor home directory", `${process.env.HOME}/.cursor`).action((opts) => {
34779
+ const cliScriptPath = process.argv[1];
34780
+ if (!cliScriptPath) {
34781
+ process.stderr.write("[caveat:error] cannot determine CLI script path\n");
34782
+ process.exit(1);
34783
+ }
34784
+ const result = uninstallCursorHooks({
34785
+ cursorDir: opts.cursorDir,
34786
+ cliScriptPath,
34787
+ nodePath: resolveHookNodePath(),
34788
+ dryRun: opts.dryRun,
34789
+ logger: stdoutLogger
34790
+ });
34791
+ stdoutLogger.info(`beforeSubmitPrompt hook: ${result.hooks.beforeSubmitPrompt === "added" ? "removed" : "not present"}`);
34792
+ stdoutLogger.info(`postToolUse hook: ${result.hooks.postToolUse === "added" ? "removed" : "not present"}`);
34793
+ stdoutLogger.info(`postToolUseFailure hook: ${result.hooks.postToolUseFailure === "added" ? "removed" : "not present"}`);
34794
+ stdoutLogger.info(`stop hook: ${result.hooks.stop === "added" ? "removed" : "not present"}`);
34795
+ if (result.backupPath) stdoutLogger.info(`hooks.json backed up: ${result.backupPath}`);
34796
+ });
34797
+ cursorHook.command("diagnostics").description("Check local Cursor hook installation").option("--cursor-dir <path>", "Cursor home directory", `${process.env.HOME}/.cursor`).action(async (opts) => {
34798
+ await runCursorHook("diagnostics", opts.cursorDir);
34799
+ });
34800
+ cursorHook.command("user-prompt-submit").description("Run the Cursor beforeSubmitPrompt hook").action(async () => {
34801
+ await runCursorHook("user-prompt-submit");
34802
+ });
34803
+ cursorHook.command("post-tool-use").description("Run the Cursor postToolUse / postToolUseFailure hook").action(async () => {
34804
+ await runCursorHook("post-tool-use");
34805
+ });
34806
+ cursorHook.command("stop").description("Run the Cursor stop hook").action(async () => {
34807
+ await runCursorHook("stop");
34808
+ });
34441
34809
  var codexSidecar = program.command("codex-sidecar").description("Check Codex sidecar availability for the current repository");
34442
34810
  codexSidecar.command("diagnostics").description("Run codex-sidecar diagnostics for this repository").option("--project <path>", "repository root to check").option("--preset <preset>", "codex-sidecar preset", "review").option("--command <command>", "codex-sidecar executable", "codex-sidecar").option("--node-cli <path>", "development path to codex-sidecar CLI JS").option("--save-result <path>", "write structured SidecarResult JSON to this path").action((opts) => {
34443
34811
  runCodexSidecarDiagnostics(stdoutLogger, opts);