caveat-cli 0.16.0 → 0.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/dist/index.js CHANGED
@@ -63,7 +63,7 @@ import {
63
63
  userPromptSubmitReminderText,
64
64
  writeDigestMarker,
65
65
  writeUserConfigPatch
66
- } from "./chunk-ZNAFNCPW.js";
66
+ } from "./chunk-HCLEIAGO.js";
67
67
 
68
68
  // ../../node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/codegen/code.js
69
69
  var require_code = __commonJS({
@@ -7075,6 +7075,7 @@ function uninstallClaudeIntegration(opts) {
7075
7075
  // src/codexHookInstall.ts
7076
7076
  import { copyFileSync as copyFileSync2, existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
7077
7077
  import { dirname as dirname3, join as join4 } from "node:path";
7078
+ import { parse as parseToml } from "smol-toml";
7078
7079
  function quote2(p) {
7079
7080
  return p.includes(" ") ? `"${p}"` : p;
7080
7081
  }
@@ -7154,74 +7155,183 @@ function removeHook2(hooksJson, event, command, subcommand) {
7154
7155
  return true;
7155
7156
  }
7156
7157
  function enableCodexHooksFeature(raw) {
7157
- const lines = raw.split(/\r?\n/);
7158
- const unsupportedFeatureShape = lines.find(
7159
- (line) => /^\s*(?:["']features["']|features)\s*(?:\.|=)/.test(line) || /^\s*\[\[?\s*(?:["']features["']|features\.)/.test(line)
7160
- );
7161
- if (unsupportedFeatureShape) {
7158
+ let parsed;
7159
+ try {
7160
+ parsed = parseToml(raw);
7161
+ } catch {
7162
7162
  return {
7163
7163
  status: "blocked",
7164
7164
  text: raw,
7165
7165
  changed: false,
7166
- reason: "config.toml uses a non-canonical features table shape that Caveat will not rewrite safely"
7166
+ reason: "config.toml is invalid TOML; fix it before installing Caveat hooks"
7167
7167
  };
7168
7168
  }
7169
- const featureHeaders = lines.map((line, index) => /^\s*\[features]\s*(?:#.*)?$/.test(line) ? index : -1).filter((index) => index >= 0);
7170
- if (featureHeaders.length > 1) {
7169
+ const featureValue = parsed.features;
7170
+ if (featureValue !== void 0 && !isPlainRecord(featureValue)) {
7171
+ return {
7172
+ status: "blocked",
7173
+ text: raw,
7174
+ changed: false,
7175
+ reason: "config.toml features value is not a table"
7176
+ };
7177
+ }
7178
+ const features = featureValue;
7179
+ const canonicalValue = features?.hooks;
7180
+ const legacyValue = features?.codex_hooks;
7181
+ if (canonicalValue !== void 0 && typeof canonicalValue !== "boolean") {
7182
+ return { status: "blocked", text: raw, changed: false, reason: "[features].hooks must be boolean" };
7183
+ }
7184
+ if (legacyValue !== void 0 && typeof legacyValue !== "boolean") {
7185
+ return { status: "blocked", text: raw, changed: false, reason: "[features].codex_hooks must be boolean" };
7186
+ }
7187
+ if (canonicalValue === false) {
7188
+ return { status: "blocked", text: raw, changed: false, reason: "[features].hooks is explicitly false" };
7189
+ }
7190
+ if (legacyValue === false) {
7171
7191
  return {
7172
7192
  status: "blocked",
7173
7193
  text: raw,
7174
7194
  changed: false,
7175
- reason: "config.toml contains more than one [features] table"
7195
+ reason: canonicalValue === true ? "[features].hooks conflicts with the deprecated codex_hooks alias" : "[features].codex_hooks is explicitly false"
7176
7196
  };
7177
7197
  }
7178
- const featuresStart = featureHeaders[0] ?? -1;
7179
- if (featuresStart === -1) {
7198
+ if (canonicalValue === true && legacyValue === void 0) {
7199
+ return { status: "enabled", text: raw, changed: false };
7200
+ }
7201
+ const lines = raw.split(/\r?\n/);
7202
+ const codeLines = maskTomlStringsAndComments(raw).split(/\r?\n/);
7203
+ const featureHeaders = codeLines.map((line, index) => /^\s*\[\s*features\s*]\s*$/.test(line) ? index : -1).filter((index) => index >= 0);
7204
+ if (featureValue === void 0) {
7180
7205
  const prefix = raw.trimEnd();
7181
7206
  const text = `${prefix}${prefix ? "\n\n" : ""}[features]
7182
- codex_hooks = true
7207
+ hooks = true
7183
7208
  `;
7184
7209
  return { status: "enabled", text, changed: true };
7185
7210
  }
7211
+ if (featureHeaders.length !== 1) {
7212
+ return {
7213
+ status: "blocked",
7214
+ text: raw,
7215
+ changed: false,
7216
+ reason: "config.toml uses a features table shape that Caveat will not rewrite safely"
7217
+ };
7218
+ }
7219
+ const featuresStart = featureHeaders[0];
7186
7220
  const assignments = [];
7187
7221
  for (let i = featuresStart + 1; i < lines.length; i += 1) {
7188
- if (/^\s*\[\[?.+?\]?]\s*(?:#.*)?$/.test(lines[i])) {
7222
+ if (/^\s*\[\[?/.test(codeLines[i])) {
7189
7223
  break;
7190
7224
  }
7191
- const assignment = /^\s*codex_hooks\s*=\s*([^#]*?)\s*(?:#.*)?$/.exec(lines[i]);
7225
+ const assignment = /^\s*(hooks|codex_hooks)\s*=/.exec(codeLines[i]);
7192
7226
  if (assignment) {
7193
- assignments.push({ index: i, value: assignment[1].trim() });
7194
- } else if (/^\s*["']codex_hooks["']\s*=/.test(lines[i])) {
7195
- return {
7196
- status: "blocked",
7197
- text: raw,
7198
- changed: false,
7199
- reason: "[features].codex_hooks uses a quoted key that Caveat will not rewrite safely"
7200
- };
7227
+ assignments.push({ index: i, key: assignment[1] });
7201
7228
  }
7202
7229
  }
7203
- if (assignments.length > 1) {
7230
+ const canonical = assignments.filter((assignment) => assignment.key === "hooks");
7231
+ const legacy = assignments.filter((assignment) => assignment.key === "codex_hooks");
7232
+ if (canonicalValue !== void 0 && canonical.length !== 1 || legacyValue !== void 0 && legacy.length !== 1) {
7204
7233
  return {
7205
7234
  status: "blocked",
7206
7235
  text: raw,
7207
7236
  changed: false,
7208
- reason: "[features].codex_hooks is defined more than once"
7237
+ reason: "[features].hooks uses a key shape that Caveat will not rewrite safely"
7209
7238
  };
7210
7239
  }
7211
- const existing = assignments[0];
7212
- if (existing) {
7213
- if (existing.value === "true") return { status: "enabled", text: raw, changed: false };
7214
- return {
7215
- status: "blocked",
7216
- text: raw,
7217
- changed: false,
7218
- reason: `[features].codex_hooks is explicitly ${existing.value || "set to a non-true value"}`
7219
- };
7240
+ const current = canonical[0];
7241
+ const deprecated = legacy[0];
7242
+ if (canonicalValue === true && legacyValue === true && current && deprecated) {
7243
+ const inlineComment = /(#.*)$/.exec(lines[deprecated.index])?.[1];
7244
+ lines.splice(deprecated.index, 1);
7245
+ if (inlineComment) {
7246
+ const canonicalIndex = current.index > deprecated.index ? current.index - 1 : current.index;
7247
+ lines.splice(canonicalIndex + 1, 0, inlineComment);
7248
+ }
7249
+ return { status: "enabled", text: `${lines.join("\n").trimEnd()}
7250
+ `, changed: true };
7220
7251
  }
7221
- lines.splice(featuresStart + 1, 0, "codex_hooks = true");
7252
+ if (legacyValue === true && canonicalValue === void 0 && deprecated) {
7253
+ lines[deprecated.index] = lines[deprecated.index].replace(/\bcodex_hooks\b/, "hooks");
7254
+ return { status: "enabled", text: `${lines.join("\n").trimEnd()}
7255
+ `, changed: true };
7256
+ }
7257
+ lines.splice(featuresStart + 1, 0, "hooks = true");
7222
7258
  return { status: "enabled", text: `${lines.join("\n").trimEnd()}
7223
7259
  `, changed: true };
7224
7260
  }
7261
+ function isPlainRecord(value) {
7262
+ return value !== null && typeof value === "object" && !Array.isArray(value);
7263
+ }
7264
+ function maskTomlStringsAndComments(raw) {
7265
+ let state = "code";
7266
+ let output = "";
7267
+ for (let i = 0; i < raw.length; i += 1) {
7268
+ const char = raw[i];
7269
+ const triple = raw.slice(i, i + 3);
7270
+ if (char === "\n") {
7271
+ output += "\n";
7272
+ if (state === "comment") state = "code";
7273
+ continue;
7274
+ }
7275
+ if (state === "comment") {
7276
+ output += " ";
7277
+ continue;
7278
+ }
7279
+ if (state === "basic") {
7280
+ output += " ";
7281
+ if (char === "\\" && i + 1 < raw.length) {
7282
+ output += raw[i + 1] === "\n" ? "\n" : " ";
7283
+ i += 1;
7284
+ } else if (char === '"') state = "code";
7285
+ continue;
7286
+ }
7287
+ if (state === "literal") {
7288
+ output += " ";
7289
+ if (char === "'") state = "code";
7290
+ continue;
7291
+ }
7292
+ if (state === "multi-basic") {
7293
+ if (triple === '"""') {
7294
+ output += " ";
7295
+ i += 2;
7296
+ state = "code";
7297
+ } else {
7298
+ output += " ";
7299
+ if (char === "\\" && i + 1 < raw.length) {
7300
+ output += raw[i + 1] === "\n" ? "\n" : " ";
7301
+ i += 1;
7302
+ }
7303
+ }
7304
+ continue;
7305
+ }
7306
+ if (state === "multi-literal") {
7307
+ if (triple === "'''") {
7308
+ output += " ";
7309
+ i += 2;
7310
+ state = "code";
7311
+ } else output += " ";
7312
+ continue;
7313
+ }
7314
+ if (char === "#") {
7315
+ output += " ";
7316
+ state = "comment";
7317
+ } else if (triple === '"""') {
7318
+ output += " ";
7319
+ i += 2;
7320
+ state = "multi-basic";
7321
+ } else if (triple === "'''") {
7322
+ output += " ";
7323
+ i += 2;
7324
+ state = "multi-literal";
7325
+ } else if (char === '"') {
7326
+ output += " ";
7327
+ state = "basic";
7328
+ } else if (char === "'") {
7329
+ output += " ";
7330
+ state = "literal";
7331
+ } else output += char;
7332
+ }
7333
+ return output;
7334
+ }
7225
7335
  function writeConfigWithBackup(path, text) {
7226
7336
  const dir = dirname3(path);
7227
7337
  if (!existsSync2(dir)) mkdirSync2(dir, { recursive: true });
@@ -7269,7 +7379,7 @@ function installCodexHooks(opts) {
7269
7379
  let configBackupPath;
7270
7380
  if (opts.dryRun) {
7271
7381
  opts.logger.info(`[dry-run] would update ${hooksPath}`);
7272
- opts.logger.info(`[dry-run] would ensure [features].codex_hooks = true in ${configPath}`);
7382
+ opts.logger.info(`[dry-run] would ensure [features].hooks = true in ${configPath}`);
7273
7383
  } else {
7274
7384
  if (anyHookAdded) {
7275
7385
  const backup = writeJsonWithBackup(hooksPath, hooksJson);
@@ -7794,7 +7904,7 @@ async function runInit(ctx, opts = { skipClaude: false, dryRun: false }, depende
7794
7904
  if (result.feature === "blocked") {
7795
7905
  codexHookState = "skipped";
7796
7906
  ctx.logger.warn(
7797
- `${result.blockedReason}; preserving explicit consent. Set \`codex_hooks = true\` in ${join8(codexHome, "config.toml")}, then rerun \`caveat init\`.`
7907
+ `${result.blockedReason}; preserving explicit consent. Set \`hooks = true\` in ${join8(codexHome, "config.toml")}, then rerun \`caveat init\`.`
7798
7908
  );
7799
7909
  } else if (opts.dryRun) {
7800
7910
  codexHookState = "skipped";
@@ -8095,7 +8205,7 @@ function runStats(ctx) {
8095
8205
 
8096
8206
  // src/commands/serve.ts
8097
8207
  async function runServe(opts) {
8098
- const { startServer } = await import("./server-SZAVLE56.js");
8208
+ const { startServer } = await import("./server-EASKBCNK.js");
8099
8209
  const { port, host } = startServer({ port: opts.port });
8100
8210
  process.stdout.write(`[caveat] web portal: http://${host}:${port}/
8101
8211
  `);
@@ -33492,10 +33602,11 @@ async function runCodexWorker(workFile) {
33492
33602
  function runDiagnostics(codexHome = process.env.CODEX_HOME ?? join13(homedir3(), ".codex")) {
33493
33603
  const features = spawnSync5("codex", ["features", "list"], {
33494
33604
  encoding: "utf-8",
33495
- maxBuffer: 1024 * 1024
33605
+ maxBuffer: 1024 * 1024,
33606
+ env: codexFeatureListEnv(codexHome)
33496
33607
  });
33497
33608
  const featureOutput = [features.stdout, features.stderr].filter(Boolean).join("\n");
33498
- const hasHooks = /^codex_hooks\s+\S+\s+true\b/m.test(featureOutput);
33609
+ const hasHooks = /^hooks\s+\S+\s+true\b/m.test(featureOutput);
33499
33610
  const installation = detectCodexHookInstallation(codexHome);
33500
33611
  const result = {
33501
33612
  availability: features.error || features.status !== 0 || !hasHooks ? "unavailable" : "available",
@@ -33505,11 +33616,14 @@ function runDiagnostics(codexHome = process.env.CODEX_HOME ?? join13(homedir3(),
33505
33616
  codexHome,
33506
33617
  hooksPath: installation.hooksPath,
33507
33618
  installedHooks: installation.hooks,
33508
- evidence: featureOutput.split("\n").find((line) => line.trim().startsWith("codex_hooks")) ?? null
33619
+ evidence: featureOutput.split("\n").find((line) => line.trim().startsWith("hooks")) ?? null
33509
33620
  };
33510
33621
  process.stdout.write(`${JSON.stringify(result, null, 2)}
33511
33622
  `);
33512
33623
  }
33624
+ function codexFeatureListEnv(codexHome, inherited = process.env) {
33625
+ return { ...inherited, CODEX_HOME: codexHome };
33626
+ }
33513
33627
  async function runCodexHook(name, arg) {
33514
33628
  if (name === "diagnostics") {
33515
33629
  runDiagnostics(arg);
@@ -34068,7 +34182,7 @@ program.command("hook <name> [arg]").description(
34068
34182
  await runHook(name, arg);
34069
34183
  });
34070
34184
  var codexHook = program.command("codex-hook").description("Install or run Codex hooks for Caveat");
34071
- codexHook.command("install").description("Install Caveat hooks into ~/.codex/hooks.json and enable codex_hooks").option("--dry-run", "show planned changes without writing", false).option("--codex-home <path>", "Codex home directory", process.env.CODEX_HOME ?? `${process.env.HOME}/.codex`).action((opts) => {
34185
+ codexHook.command("install").description("Install Caveat hooks into ~/.codex/hooks.json and enable hooks").option("--dry-run", "show planned changes without writing", false).option("--codex-home <path>", "Codex home directory", process.env.CODEX_HOME ?? `${process.env.HOME}/.codex`).action((opts) => {
34072
34186
  const cliScriptPath = process.argv[1];
34073
34187
  if (!cliScriptPath) {
34074
34188
  process.stderr.write("[caveat:error] cannot determine CLI script path\n");
@@ -34084,10 +34198,10 @@ codexHook.command("install").description("Install Caveat hooks into ~/.codex/hoo
34084
34198
  stdoutLogger.info(`UserPromptSubmit hook: ${result.hooks.userPromptSubmit}`);
34085
34199
  stdoutLogger.info(`PostToolUse hook: ${result.hooks.postToolUse}`);
34086
34200
  stdoutLogger.info(`Stop hook: ${result.hooks.stop}`);
34087
- stdoutLogger.info(`codex_hooks feature: ${result.feature}`);
34201
+ stdoutLogger.info(`hooks feature: ${result.feature}`);
34088
34202
  if (result.feature === "blocked") {
34089
34203
  stdoutLogger.warn(
34090
- `${result.blockedReason}; preserving explicit consent. Set \`codex_hooks = true\` in ${opts.codexHome}/config.toml, then rerun \`caveat codex-hook install\`.`
34204
+ `${result.blockedReason}; preserving explicit consent. Set \`hooks = true\` in ${opts.codexHome}/config.toml, then rerun \`caveat codex-hook install\`.`
34091
34205
  );
34092
34206
  }
34093
34207
  if (result.backupPath) stdoutLogger.info(`hooks.json backed up: ${result.backupPath}`);