@cybedefend/vibedefend 1.4.3 → 1.4.5

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 (61) hide show
  1. package/README.md +39 -3
  2. package/dist/auth/auth-store.js +5 -6
  3. package/dist/auth/auth-store.js.map +1 -1
  4. package/dist/clients/claude-code.js +27 -0
  5. package/dist/clients/claude-code.js.map +1 -1
  6. package/dist/clients/codex.js +80 -8
  7. package/dist/clients/codex.js.map +1 -1
  8. package/dist/clients/cursor-guards-rules.js +26 -1
  9. package/dist/clients/cursor-guards-rules.js.map +1 -1
  10. package/dist/clients/cursor.js +16 -0
  11. package/dist/clients/cursor.js.map +1 -1
  12. package/dist/clients/unwire.js +57 -0
  13. package/dist/clients/unwire.js.map +1 -0
  14. package/dist/clients/vscode.js +29 -1
  15. package/dist/clients/vscode.js.map +1 -1
  16. package/dist/clients/windsurf.js +24 -1
  17. package/dist/clients/windsurf.js.map +1 -1
  18. package/dist/config.js +7 -0
  19. package/dist/config.js.map +1 -1
  20. package/dist/diagnostics.js +3 -1
  21. package/dist/diagnostics.js.map +1 -1
  22. package/dist/doctor.js +3 -1
  23. package/dist/doctor.js.map +1 -1
  24. package/dist/global-cli.js +138 -0
  25. package/dist/global-cli.js.map +1 -0
  26. package/dist/hook-runner.js +323 -151
  27. package/dist/hooks/install.js +1 -0
  28. package/dist/hooks/install.js.map +1 -1
  29. package/dist/hooks/runtime/fetch-rules.js +3 -1
  30. package/dist/hooks/runtime/fetch-rules.js.map +1 -1
  31. package/dist/hooks/runtime/hook-refresh-trigger.js +76 -0
  32. package/dist/hooks/runtime/hook-refresh-trigger.js.map +1 -0
  33. package/dist/hooks/runtime/pre-compact.js +9 -2
  34. package/dist/hooks/runtime/pre-compact.js.map +1 -1
  35. package/dist/hooks/runtime/proposal-mode.js +75 -0
  36. package/dist/hooks/runtime/proposal-mode.js.map +1 -0
  37. package/dist/hooks/runtime/resolve.js +89 -64
  38. package/dist/hooks/runtime/resolve.js.map +1 -1
  39. package/dist/hooks/runtime/session-review.js +40 -20
  40. package/dist/hooks/runtime/session-review.js.map +1 -1
  41. package/dist/hooks/runtime/session-start.js +19 -7
  42. package/dist/hooks/runtime/session-start.js.map +1 -1
  43. package/dist/hooks/runtime/shell-writes.js +3 -2
  44. package/dist/hooks/runtime/shell-writes.js.map +1 -1
  45. package/dist/hooks/runtime/user-prompt-submit.js +15 -3
  46. package/dist/hooks/runtime/user-prompt-submit.js.map +1 -1
  47. package/dist/index.js +23 -1
  48. package/dist/index.js.map +1 -1
  49. package/dist/install.js +23 -1
  50. package/dist/install.js.map +1 -1
  51. package/dist/prompts.js +18 -3
  52. package/dist/prompts.js.map +1 -1
  53. package/dist/refresh-hooks.js +74 -0
  54. package/dist/refresh-hooks.js.map +1 -0
  55. package/dist/self-update.js +5 -0
  56. package/dist/self-update.js.map +1 -1
  57. package/dist/uninstall.js +166 -0
  58. package/dist/uninstall.js.map +1 -0
  59. package/dist/update.js +4 -0
  60. package/dist/update.js.map +1 -1
  61. package/package.json +3 -3
@@ -39,22 +39,31 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
39
39
  mod
40
40
  ));
41
41
 
42
- // node_modules/.pnpm/shell-quote@1.10.0/node_modules/shell-quote/quote.js
42
+ // node_modules/.pnpm/shell-quote@1.12.0/node_modules/shell-quote/quote.js
43
43
  var require_quote = __commonJS({
44
- "node_modules/.pnpm/shell-quote@1.10.0/node_modules/shell-quote/quote.js"(exports, module) {
44
+ "node_modules/.pnpm/shell-quote@1.12.0/node_modules/shell-quote/quote.js"(exports, module) {
45
45
  "use strict";
46
46
  var OPS = (
47
47
  /** @type {const} */
48
48
  [
49
49
  "||",
50
50
  "&&",
51
+ ";;&",
51
52
  ";;",
53
+ ";&",
52
54
  "|&",
53
55
  "<(",
56
+ ">(",
54
57
  "<<<",
58
+ "<<-",
59
+ "<<",
55
60
  ">>",
56
61
  ">&",
62
+ ">|",
63
+ "&>>",
64
+ "&>",
57
65
  "<&",
66
+ "<>",
58
67
  "&",
59
68
  ";",
60
69
  "(",
@@ -65,9 +74,13 @@ var require_quote = __commonJS({
65
74
  ]
66
75
  );
67
76
  var LINE_TERMINATORS = /[\n\r\u2028\u2029]/;
68
- var GLOB_SHELL_SPECIAL = /[\s#!"$&'():;<=>@\\^`|]/g;
77
+ var GLOB_SHELL_SPECIAL = /[\s#!"$&'():;<=>@\\^`|~]/g;
69
78
  module.exports = function quote(xs) {
79
+ var sawComment = false;
70
80
  return xs.map(function(s) {
81
+ if (sawComment && typeof s === "string" && LINE_TERMINATORS.test(s)) {
82
+ throw new TypeError("a token after a `comment` must not contain line terminators");
83
+ }
71
84
  if (s === "") {
72
85
  return (
73
86
  /** @type {const} */
@@ -82,6 +95,12 @@ var require_quote = __commonJS({
82
95
  if (LINE_TERMINATORS.test(s.pattern)) {
83
96
  throw new TypeError("glob `pattern` must not contain line terminators");
84
97
  }
98
+ if (s.pattern === "") {
99
+ return (
100
+ /** @type {const} */
101
+ "''"
102
+ );
103
+ }
85
104
  return s.pattern.replace(GLOB_SHELL_SPECIAL, "\\$&");
86
105
  }
87
106
  if ("op" in s && typeof s.op === "string") {
@@ -94,15 +113,19 @@ var require_quote = __commonJS({
94
113
  if (LINE_TERMINATORS.test(s.comment)) {
95
114
  throw new TypeError("`comment` must not contain line terminators");
96
115
  }
116
+ sawComment = true;
97
117
  return "#" + s.comment;
98
118
  }
99
119
  throw new TypeError("unrecognized object token shape");
100
120
  }
121
+ if (/'/.test(s) && /!/.test(s)) {
122
+ return "'" + s.replace(/'/g, `'"'"'`) + "'";
123
+ }
101
124
  if (/["\s\\]/.test(s) && !/'/.test(s)) {
102
- return "'" + s.replace(/(['])/g, "\\$1") + "'";
125
+ return "'" + s + "'";
103
126
  }
104
127
  if (/["'\s]/.test(s)) {
105
- return '"' + s.replace(/(["\\$`!])/g, "\\$1") + '"';
128
+ return '"' + s.replace(/(["\\$`])/g, "\\$1") + '"';
106
129
  }
107
130
  return String(s).replace(/([A-Za-z]:)?([#!"$&'()*,:;<=>?@[\\\]^`{|}~])/g, "$1\\$2");
108
131
  }).join(" ");
@@ -110,23 +133,31 @@ var require_quote = __commonJS({
110
133
  }
111
134
  });
112
135
 
113
- // node_modules/.pnpm/shell-quote@1.10.0/node_modules/shell-quote/parse.js
136
+ // node_modules/.pnpm/shell-quote@1.12.0/node_modules/shell-quote/parse.js
114
137
  var require_parse = __commonJS({
115
- "node_modules/.pnpm/shell-quote@1.10.0/node_modules/shell-quote/parse.js"(exports, module) {
138
+ "node_modules/.pnpm/shell-quote@1.12.0/node_modules/shell-quote/parse.js"(exports, module) {
116
139
  "use strict";
117
140
  var CONTROL = (
118
141
  /** @type {const} */
119
142
  "(?:" + /** @type {const} */
120
143
  [
121
144
  "\\|\\|",
122
- "\\&\\&",
123
- ";;",
124
- "\\|\\&",
145
+ "\\&(?:\\&|>>?)",
146
+ // `&&`, `&>`, and `&>>`
147
+ ";;\\&?",
148
+ // `;;` and `;;&`
149
+ "[;|]\\&",
150
+ // `;&` and `|&`
125
151
  "\\<\\(",
126
152
  "\\<\\<\\<",
153
+ "\\<\\<-",
154
+ "\\<\\<(?!\\()",
155
+ // `<<(` stays `<` and `<(`, as zsh reads it; other shells reject it
127
156
  ">>",
128
- ">\\&",
129
- "<\\&",
157
+ ">[&|(]",
158
+ // `>&`, `>|`, and `>(`
159
+ "<[&>]",
160
+ // `<&` and `<>`
130
161
  "[&;()|<>]"
131
162
  ].join(
132
163
  /** @type {const} */
@@ -143,10 +174,11 @@ var require_parse = __commonJS({
143
174
  /** @type {const} */
144
175
  "'([^']*?)'"
145
176
  );
146
- var DOUBLE_QUOTE = (
147
- /** @type {const} */
148
- '"((\\\\"|[^"])*?)"'
149
- );
177
+ var ANSI_C_BODY = "(?:\\\\[\\s\\S]|[^\\\\'])*?";
178
+ var ANSI_C_QUOTE = "\\$'" + ANSI_C_BODY + "'";
179
+ var ansiCAt = new RegExp("\\$'" + ANSI_C_BODY + "(?:(')|\\\\?$)", "g");
180
+ var ANSI_C_LETTERS = "abeEfnrtv";
181
+ var ANSI_C_CHARS = "\x07\b\x1B\x1B\f\n\r \v";
150
182
  var hash = /^#$/;
151
183
  var SQ = (
152
184
  /** @type {const} */
@@ -195,17 +227,46 @@ var require_parse = __commonJS({
195
227
  }
196
228
  return pre + r;
197
229
  }
230
+ var ansiCEscape = /\\([0-7]{1,3}|x[\dA-Fa-f]{1,2}|u[\dA-Fa-f]{1,4}|U[\dA-Fa-f]{1,8}|c(?:\\\\|[\s\S])|[abeEfnrtv\\'"?])/g;
231
+ function expandAnsiCEscape(m, escape) {
232
+ var kind = escape.charAt(0);
233
+ if (kind === "c") {
234
+ var ctrl = escape.charAt(1);
235
+ return ctrl === "?" ? "\x7F" : String.fromCharCode(ctrl.charCodeAt(0) & 31);
236
+ }
237
+ if (kind === "x" || kind === "u" || kind === "U") {
238
+ var cp = parseInt(escape.slice(1), 16);
239
+ if (cp > 1114111) {
240
+ return m;
241
+ }
242
+ return String.fromCharCode.apply(null, cp > 65535 ? [55232 + (cp >> 10), 56320 + (cp & 1023)] : [cp]);
243
+ }
244
+ if (kind >= "0" && kind <= "7") {
245
+ return String.fromCharCode(parseInt(escape, 8) & 255);
246
+ }
247
+ var letter = ANSI_C_LETTERS.indexOf(escape);
248
+ return letter < 0 ? escape : ANSI_C_CHARS.charAt(letter);
249
+ }
250
+ function expandAnsiC(body) {
251
+ return body.replace(ansiCEscape, expandAnsiCEscape).split("\0")[0];
252
+ }
253
+ function closesAnsiC(s, i2) {
254
+ ansiCAt.lastIndex = i2;
255
+ return !!/** @type {RegExpExecArray} */
256
+ ansiCAt.exec(s)[1];
257
+ }
198
258
  function parseInternal(string, env, opts) {
199
259
  if (!opts) {
200
260
  opts = {};
201
261
  }
202
262
  var BS = opts.escape || "\\";
203
263
  var ifs = opts.splitUnquoted === true ? " \n" : typeof opts.splitUnquoted === "string" ? opts.splitUnquoted : "";
204
- var BAREWORD = "(\\" + BS + `['"` + META + `]|[^\\s'"` + META + "])+";
264
+ var BAREWORD = "(\\" + BS + `['"$\\` + BS + META + "]|\\$\\$|\\$(?!" + ANSI_C_QUOTE.slice(2) + `)|[^\\s'"$` + META + "])+";
265
+ var DOUBLE_QUOTE = '"(?:\\' + BS + '[\\s\\S]|[^"\\' + BS + '])*"';
205
266
  var chunker = new RegExp([
206
267
  "(" + CONTROL + ")",
207
268
  // control chars
208
- "(" + BAREWORD + "|" + DOUBLE_QUOTE + "|" + SINGLE_QUOTE + ")+"
269
+ "(" + ANSI_C_QUOTE + "|" + BAREWORD + "|" + DOUBLE_QUOTE + "|" + SINGLE_QUOTE + ")+"
209
270
  ].join("|"), "g");
210
271
  var matches = matchAll(string, chunker);
211
272
  if (matches.length === 0) {
@@ -260,9 +321,8 @@ var require_parse = __commonJS({
260
321
  varend -= 1;
261
322
  varname = s.slice(i2, varend);
262
323
  i2 = varend;
263
- } else if (/[*@#?$!_-]/.test(char)) {
324
+ } else if (/[*@#?$!-]/.test(char)) {
264
325
  varname = char;
265
- i2 += 1;
266
326
  } else {
267
327
  var slicedFromI = s.slice(i2);
268
328
  varend = slicedFromI.match(/[^\w\d_]/);
@@ -352,6 +412,11 @@ var require_parse = __commonJS({
352
412
  );
353
413
  } else if (c === BS) {
354
414
  esc = true;
415
+ } else if (c === DS && s.charAt(i2 + 1) === SQ && closesAnsiC(s, i2)) {
416
+ flushRun();
417
+ sawQuote = true;
418
+ out += expandAnsiC(s.slice(i2 + 2, ansiCAt.lastIndex - 1));
419
+ i2 = ansiCAt.lastIndex - 1;
355
420
  } else if (c === DS) {
356
421
  var value = parseEnvVar();
357
422
  if (!ifs) {
@@ -435,9 +500,9 @@ var require_parse = __commonJS({
435
500
  }
436
501
  });
437
502
 
438
- // node_modules/.pnpm/shell-quote@1.10.0/node_modules/shell-quote/index.js
503
+ // node_modules/.pnpm/shell-quote@1.12.0/node_modules/shell-quote/index.js
439
504
  var require_shell_quote = __commonJS({
440
- "node_modules/.pnpm/shell-quote@1.10.0/node_modules/shell-quote/index.js"(exports) {
505
+ "node_modules/.pnpm/shell-quote@1.12.0/node_modules/shell-quote/index.js"(exports) {
441
506
  "use strict";
442
507
  exports.quote = require_quote();
443
508
  exports.parse = require_parse();
@@ -4742,7 +4807,7 @@ function extractShellWriteTargetsAt(command, depth) {
4742
4807
  const tok = tokens[i];
4743
4808
  if (isOp(tok)) {
4744
4809
  if (tok.op === "&" && isOp(tokens[i + 1], ">")) continue;
4745
- if (tok.op === ">" || tok.op === ">>") {
4810
+ if (tok.op === ">" || tok.op === ">>" || tok.op === "&>" || tok.op === "&>>") {
4746
4811
  const next = tokens[i + 1];
4747
4812
  const text2 = next === void 0 ? null : tokenText(next);
4748
4813
  if (text2 !== null) {
@@ -4896,6 +4961,7 @@ function extractToolInputShape(input, client) {
4896
4961
 
4897
4962
  // src/hooks/runtime/resolve.ts
4898
4963
  import { execFileSync as execFileSync2 } from "node:child_process";
4964
+ import { createHash } from "node:crypto";
4899
4965
  import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
4900
4966
 
4901
4967
  // src/auth/auth-store.ts
@@ -5585,30 +5651,50 @@ function resolveProjectId(cwd = process.cwd(), deps = {}) {
5585
5651
  }
5586
5652
  return { projectId: null, apiBase: null };
5587
5653
  }
5588
- function resolveToken(mcpName) {
5654
+ function resolveToken(mcpName, opts = {}) {
5589
5655
  const envToken = process.env.CYBEDEFEND_TOKEN;
5590
5656
  if (envToken && envToken.length > 0) return envToken;
5591
- if (process.platform === "darwin") {
5592
- return resolveTokenMacOS(mcpName) ?? resolveTokenCodexMacOS(mcpName);
5657
+ const stored = (opts.loadStoredAuth ?? loadAuth)(mcpName);
5658
+ if (stored && (opts.now ?? Date.now)() < stored.expiresAt) {
5659
+ return stored.accessToken;
5660
+ }
5661
+ const platform2 = opts.platform ?? process.platform;
5662
+ const readKeychain = opts.readKeychain ?? readMacOSKeychain2;
5663
+ if (platform2 === "darwin") {
5664
+ return resolveTokenMacOS(mcpName, readKeychain) ?? resolveTokenCodexMacOS(mcpName, opts.mcpUrl, readKeychain);
5593
5665
  }
5594
- if (process.platform === "linux") return resolveTokenLinux(mcpName);
5666
+ if (platform2 === "linux") return resolveTokenLinux(mcpName);
5595
5667
  return null;
5596
5668
  }
5597
- function resolveTokenMacOS(mcpName) {
5598
- let blob;
5669
+ function codexCredentialAccount(mcpName, mcpUrl) {
5670
+ const digest = createHash("sha256").update(JSON.stringify({ type: "http", url: mcpUrl, headers: {} })).digest("hex");
5671
+ return `${mcpName}|${digest.slice(0, 16)}`;
5672
+ }
5673
+ function readMacOSKeychain2(service, account) {
5599
5674
  try {
5600
- blob = execFileSync2(
5675
+ const blob = execFileSync2(
5601
5676
  "security",
5602
- ["find-generic-password", "-s", "Claude Code-credentials", "-w"],
5677
+ [
5678
+ "find-generic-password",
5679
+ "-s",
5680
+ service,
5681
+ ...account === void 0 ? [] : ["-a", account],
5682
+ "-w"
5683
+ ],
5603
5684
  {
5604
5685
  encoding: "utf8",
5605
5686
  stdio: ["ignore", "pipe", "ignore"],
5606
5687
  timeout: 2e3
5607
5688
  }
5608
5689
  ).trim();
5690
+ return blob.length > 0 ? blob : null;
5609
5691
  } catch {
5610
5692
  return null;
5611
5693
  }
5694
+ }
5695
+ function resolveTokenMacOS(mcpName, readKeychain) {
5696
+ const blob = readKeychain("Claude Code-credentials");
5697
+ if (!blob) return null;
5612
5698
  try {
5613
5699
  const data = JSON.parse(blob);
5614
5700
  const oauth = data.mcpOAuth ?? {};
@@ -5622,37 +5708,20 @@ function resolveTokenMacOS(mcpName) {
5622
5708
  }
5623
5709
  return null;
5624
5710
  }
5625
- function resolveTokenCodexMacOS(mcpName) {
5626
- let blob;
5627
- try {
5628
- blob = execFileSync2(
5629
- "security",
5630
- ["find-generic-password", "-s", "Codex MCP Credentials", "-a", mcpName, "-w"],
5631
- {
5632
- encoding: "utf8",
5633
- stdio: ["ignore", "pipe", "ignore"],
5634
- timeout: 2e3
5635
- }
5636
- ).trim();
5637
- } catch {
5638
- try {
5639
- blob = execFileSync2(
5640
- "security",
5641
- ["find-generic-password", "-s", "Codex MCP Credentials", "-w"],
5642
- {
5643
- encoding: "utf8",
5644
- stdio: ["ignore", "pipe", "ignore"],
5645
- timeout: 2e3
5646
- }
5647
- ).trim();
5648
- } catch {
5649
- return null;
5650
- }
5711
+ function resolveTokenCodexMacOS(mcpName, mcpUrl, readKeychain) {
5712
+ const accounts = mcpUrl ? [codexCredentialAccount(mcpName, mcpUrl), mcpName] : [mcpName];
5713
+ for (const account of accounts) {
5714
+ const blob = readKeychain("Codex MCP Credentials", account);
5715
+ if (!blob) continue;
5716
+ const token = parseCodexCredential(blob);
5717
+ if (token) return token;
5651
5718
  }
5652
- if (!blob) return null;
5719
+ return null;
5720
+ }
5721
+ function parseCodexCredential(blob) {
5653
5722
  try {
5654
5723
  const data = JSON.parse(blob);
5655
- return data.access_token ?? data.accessToken ?? data.token ?? null;
5724
+ return data.token_response?.access_token ?? data.access_token ?? data.accessToken ?? data.token ?? null;
5656
5725
  } catch {
5657
5726
  return blob || null;
5658
5727
  }
@@ -5673,7 +5742,7 @@ function resolveTokenLinux(mcpName) {
5673
5742
  return null;
5674
5743
  }
5675
5744
  }
5676
- async function resolveTokenWithRefresh(mcpName, deps = {}) {
5745
+ async function resolveTokenWithRefresh(mcpName, deps = {}, opts = {}) {
5677
5746
  const getValidAccessTokenFn = deps.getValidAccessToken ?? getValidAccessToken;
5678
5747
  const resolveTokenFn = deps.resolveTokenFn ?? resolveToken;
5679
5748
  const envToken = process.env.CYBEDEFEND_TOKEN;
@@ -5683,10 +5752,10 @@ async function resolveTokenWithRefresh(mcpName, deps = {}) {
5683
5752
  if (token) return token;
5684
5753
  } catch (e) {
5685
5754
  if (e instanceof LoginRequiredError) {
5686
- return resolveTokenFn(mcpName);
5755
+ return resolveTokenFn(mcpName, opts);
5687
5756
  }
5688
5757
  }
5689
- return resolveTokenFn(mcpName);
5758
+ return resolveTokenFn(mcpName, opts);
5690
5759
  }
5691
5760
  var RESOLVE_API_TIMEOUT_MS = 4e3;
5692
5761
  async function resolveProjectIdWithApi(cwd = process.cwd(), deps = {}) {
@@ -5909,7 +5978,9 @@ async function fetchRulesHook(deps = {}) {
5909
5978
  const { projectId, apiBase: configApiBase } = await resolveProject();
5910
5979
  if (!projectId) return;
5911
5980
  const apiBase = process.env.CYBEDEFEND_API_BASE ?? configApiBase ?? config.region.apiBase;
5912
- const token = resolveTok(config.region.mcpName);
5981
+ const token = resolveTok(config.region.mcpName, {
5982
+ mcpUrl: config.region.mcpUrl
5983
+ });
5913
5984
  if (!token) return;
5914
5985
  const intent = buildIntent(event);
5915
5986
  const files = event.filePaths.length > 0 ? event.filePaths : [event.filePath];
@@ -6000,6 +6071,30 @@ function composeSecurityBody(markdown, total, filePath) {
6000
6071
  ].join("\n");
6001
6072
  }
6002
6073
 
6074
+ // src/hooks/runtime/proposal-mode.ts
6075
+ var TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
6076
+ var FALSY = /* @__PURE__ */ new Set(["0", "false", "no", "off"]);
6077
+ function envFlag(env, name) {
6078
+ const raw = env[name]?.trim().toLowerCase();
6079
+ if (raw && TRUTHY.has(raw)) return true;
6080
+ if (raw && FALSY.has(raw)) return false;
6081
+ return void 0;
6082
+ }
6083
+ function isNonBlockingMode(config, env = process.env) {
6084
+ return envFlag(env, "CYBEDEFEND_NON_BLOCKING") ?? config.hooks.nonBlockingMode === true;
6085
+ }
6086
+ function resolveProposalMode(config, env = process.env) {
6087
+ if (isNonBlockingMode(config, env)) return "non-blocking";
6088
+ const auto = envFlag(env, "CYBEDEFEND_AUTO_PROPOSE") ?? config.hooks.autoProposeMode === true;
6089
+ return auto ? "auto" : "ask";
6090
+ }
6091
+ var NON_BLOCKING_INBOX_DIRECTIVE = "Non-blocking mode is ON (autonomous agent \u2014 nobody is in the chat to answer). This OVERRIDES step 0 of the CybeDefend doctrine: do NOT call `cybe_rules_list_proposals`, do NOT ask the user about proposals (no AskUserQuestion), and NEVER call `cybe_rules_accept_proposal` / `cybe_rules_reject_proposal` yourself. Pending proposals stay in the inbox for a human to review in the CybeDefend dashboard. Proceed directly with the task.";
6092
+ var REPORT_MISSING_CALL = '`cybe_rules_report_missing(projectId, { rule_text, evidence_files, rationale: "<quote user prompt>", confirmed_by_user: true })`';
6093
+ var DIRECT_PROPOSE_DIRECTIVES = {
6094
+ auto: `**Auto-propose mode is ON** \u2014 this OVERRIDES the doctrine's "ask first" step: do NOT ask the user (no AskUserQuestion). If \u2014 and ONLY if \u2014 one durable, reusable gap stands out, call ${REPORT_MISSING_CALL} directly for that single rule. The user validates it at the next session start via the Accept/Reject picker. Otherwise propose nothing.`,
6095
+ "non-blocking": `**Non-blocking mode is ON** (autonomous agent \u2014 nobody is in the chat to answer). Do NOT ask the user (no AskUserQuestion). If \u2014 and ONLY if \u2014 one durable, reusable gap stands out, call ${REPORT_MISSING_CALL} directly for that single rule. It lands as a pending proposal that a human accepts or rejects in the CybeDefend dashboard. Otherwise propose nothing.`
6096
+ };
6097
+
6003
6098
  // src/hooks/runtime/self-update-check.ts
6004
6099
  import { spawn } from "node:child_process";
6005
6100
  import { existsSync as existsSync8, readFileSync as readFileSync7, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
@@ -6040,6 +6135,7 @@ var GLOBAL_MODES = /* @__PURE__ */ new Set([
6040
6135
  ]);
6041
6136
  function detectInstallMode(scriptPath) {
6042
6137
  if (/[\\/]_npx[\\/]/i.test(scriptPath)) return "npx-cache";
6138
+ if (/[\\/]dlx(?:-\d+)?[\\/]/i.test(scriptPath)) return "npx-cache";
6043
6139
  if (/[\\/]pnpm(?:[\\/]global|[\\/]nodejs|[\\/]node_modules)/i.test(scriptPath)) {
6044
6140
  return "pnpm-global";
6045
6141
  }
@@ -6209,19 +6305,66 @@ function pmCommand(mode, spec) {
6209
6305
  }
6210
6306
  }
6211
6307
 
6308
+ // src/hooks/runtime/hook-refresh-trigger.ts
6309
+ import { spawn as spawn2 } from "node:child_process";
6310
+ import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
6311
+ import { homedir as homedir7 } from "node:os";
6312
+ import { dirname as dirname3, join as join6 } from "node:path";
6313
+ function maybeRefreshStaleWiring(deps) {
6314
+ try {
6315
+ const { config, runnerVersion } = deps;
6316
+ if (!config.runnerSource || config.installedVersion === runnerVersion) {
6317
+ return false;
6318
+ }
6319
+ const cliEntry = join6(dirname3(dirname3(config.runnerSource)), "bin", "vibedefend.js");
6320
+ if (!(deps.exists ?? existsSync9)(cliEntry)) return false;
6321
+ const markerPath = deps.markerPath ?? join6(process.env.CYBEDEFEND_HOME ?? homedir7(), ".cybedefend", "hook-refresh.json");
6322
+ if (readMarkerVersion(markerPath) === runnerVersion) return false;
6323
+ (deps.spawnRefresh ?? defaultSpawnRefresh)(cliEntry);
6324
+ writeMarker(markerPath, runnerVersion);
6325
+ return true;
6326
+ } catch {
6327
+ return false;
6328
+ }
6329
+ }
6330
+ function readMarkerVersion(path) {
6331
+ try {
6332
+ if (!existsSync9(path)) return null;
6333
+ const data = JSON.parse(readFileSync8(path, "utf8"));
6334
+ return typeof data.version === "string" ? data.version : null;
6335
+ } catch {
6336
+ return null;
6337
+ }
6338
+ }
6339
+ function writeMarker(path, version) {
6340
+ try {
6341
+ ensureDirFor(path);
6342
+ writeFileSync5(path, JSON.stringify({ version, spawnedAt: Date.now() }) + "\n");
6343
+ } catch {
6344
+ }
6345
+ }
6346
+ function defaultSpawnRefresh(cliEntry) {
6347
+ const child = spawn2(process.execPath, [cliEntry, "update", "--refresh-hooks"], {
6348
+ detached: true,
6349
+ stdio: "ignore",
6350
+ windowsHide: true
6351
+ });
6352
+ child.unref();
6353
+ }
6354
+
6212
6355
  // src/version.ts
6213
- import { readFileSync as readFileSync8 } from "node:fs";
6356
+ import { readFileSync as readFileSync9 } from "node:fs";
6214
6357
  import { fileURLToPath as fileURLToPath2 } from "node:url";
6215
- import { dirname as dirname3, join as join6 } from "node:path";
6358
+ import { dirname as dirname4, join as join7 } from "node:path";
6216
6359
  var PKG_NAME = "@cybedefend/vibedefend";
6217
6360
  function defaultPkgPath() {
6218
- return join6(dirname3(fileURLToPath2(import.meta.url)), "..", "package.json");
6361
+ return join7(dirname4(fileURLToPath2(import.meta.url)), "..", "package.json");
6219
6362
  }
6220
6363
  function readPackageMeta(opts = {}) {
6221
6364
  if (typeof opts.bakedVersion === "string" && opts.bakedVersion.length > 0) {
6222
6365
  return { name: PKG_NAME, version: opts.bakedVersion };
6223
6366
  }
6224
- const readFile = opts.readFile ?? ((p) => readFileSync8(p, "utf8"));
6367
+ const readFile = opts.readFile ?? ((p) => readFileSync9(p, "utf8"));
6225
6368
  const pkgPath = opts.pkgPath ?? defaultPkgPath();
6226
6369
  try {
6227
6370
  return JSON.parse(readFile(pkgPath));
@@ -6230,7 +6373,7 @@ function readPackageMeta(opts = {}) {
6230
6373
  }
6231
6374
  }
6232
6375
  var pkg = readPackageMeta({
6233
- bakedVersion: true ? "1.4.3" : void 0
6376
+ bakedVersion: true ? "1.4.5" : void 0
6234
6377
  });
6235
6378
 
6236
6379
  // src/hooks/runtime/session-start.ts
@@ -6249,6 +6392,10 @@ async function sessionStartHook(deps = {}) {
6249
6392
  const event = sniff(stdin);
6250
6393
  const config = loadConfig();
6251
6394
  if (!config) return;
6395
+ (deps.refreshStaleWiringFn ?? maybeRefreshStaleWiring)({
6396
+ config,
6397
+ runnerVersion: pkg.version
6398
+ });
6252
6399
  const { projectId, apiBase: configApiBase } = await resolveProject();
6253
6400
  if (!projectId) return;
6254
6401
  const apiBase = process.env.CYBEDEFEND_API_BASE ?? configApiBase ?? config.region.apiBase;
@@ -6261,7 +6408,9 @@ async function sessionStartHook(deps = {}) {
6261
6408
  );
6262
6409
  return;
6263
6410
  }
6264
- const token = resolveTok(config.region.mcpName);
6411
+ const token = resolveTok(config.region.mcpName, {
6412
+ mcpUrl: config.region.mcpUrl
6413
+ });
6265
6414
  let total = null;
6266
6415
  if (token) {
6267
6416
  const result = await fetchProposals({ apiBase, projectId, token });
@@ -6279,7 +6428,12 @@ async function sessionStartHook(deps = {}) {
6279
6428
  " if your client does not surface it, load the `cybe-init` MCP prompt manually.)"
6280
6429
  );
6281
6430
  lines.push("");
6282
- if (total === null) {
6431
+ if (isNonBlockingMode(config)) {
6432
+ lines.push(
6433
+ `- **Step 0 \u2014 proposals inbox**: ${total === null ? "not checked" : `${total} pending`} \u2014 no action required.`,
6434
+ ` ${NON_BLOCKING_INBOX_DIRECTIVE}`
6435
+ );
6436
+ } else if (total === null) {
6283
6437
  lines.push(
6284
6438
  "- **Step 0 \u2014 proposals inbox**: \u26A0 unable to verify (no MCP token reachable).",
6285
6439
  ' **YOU MUST call `cybe_rules_list_proposals(projectId, status="proposed")` as',
@@ -6335,7 +6489,7 @@ async function sessionStartHook(deps = {}) {
6335
6489
  }
6336
6490
 
6337
6491
  // src/hooks/runtime/session-review.ts
6338
- import { existsSync as existsSync9, readFileSync as readFileSync9 } from "node:fs";
6492
+ import { existsSync as existsSync10, readFileSync as readFileSync10 } from "node:fs";
6339
6493
  var COUNTED_TOOLS = /* @__PURE__ */ new Set([
6340
6494
  "Edit",
6341
6495
  "Write",
@@ -6351,6 +6505,11 @@ function transcriptHasReviewSentinel(transcript) {
6351
6505
  return false;
6352
6506
  }
6353
6507
  }
6508
+ function resolveReviewThreshold(config, env = process.env) {
6509
+ const raw = env.CYBEDEFEND_REVIEW_THRESHOLD?.trim();
6510
+ if (raw && /^\d+$/.test(raw) && Number(raw) <= 100) return Number(raw);
6511
+ return config.hooks.reviewThreshold;
6512
+ }
6354
6513
  async function sessionReviewHook(deps = {}) {
6355
6514
  const readStdin = deps.readStdin ?? readStdinJson;
6356
6515
  const loadConfig = deps.loadConfig ?? loadRuntimeConfig;
@@ -6371,20 +6530,21 @@ async function sessionReviewHook(deps = {}) {
6371
6530
  editCount = parseInt(process.env.CYBEDEFEND_DRY_RUN_EDITS ?? "5", 10);
6372
6531
  }
6373
6532
  if (editCount === 0) return;
6374
- if (editCount < config.hooks.reviewThreshold) return;
6533
+ if (editCount < resolveReviewThreshold(config)) return;
6375
6534
  const userPrompt = extractFirstUserPrompt(transcript);
6535
+ const proposalMode = resolveProposalMode(config);
6376
6536
  let body = composeReviewBody(
6377
6537
  editCount,
6378
6538
  event.client,
6379
6539
  userPrompt,
6380
- config.hooks.autoProposeMode
6540
+ proposalMode
6381
6541
  );
6382
6542
  const withDiffScan = config.hooks.enableDiffScan !== false;
6383
6543
  if (withDiffScan) {
6384
6544
  const cwd = typeof stdin.cwd === "string" && stdin.cwd ? stdin.cwd : process.cwd();
6385
6545
  body += "\n" + composeDiffScanBody(extractChangedFiles(transcript, void 0, cwd));
6386
6546
  }
6387
- emitFn(body, event.client, composeStopReason(withDiffScan));
6547
+ emitFn(body, event.client, composeStopReason(withDiffScan, proposalMode));
6388
6548
  }
6389
6549
  function loadTranscript(stdin, readFile) {
6390
6550
  if (Array.isArray(stdin.transcript)) return stdin.transcript;
@@ -6413,9 +6573,9 @@ function loadTranscript(stdin, readFile) {
6413
6573
  return out;
6414
6574
  }
6415
6575
  function defaultReadTranscript(path) {
6416
- if (!existsSync9(path)) return null;
6576
+ if (!existsSync10(path)) return null;
6417
6577
  try {
6418
- return readFileSync9(path, "utf8");
6578
+ return readFileSync10(path, "utf8");
6419
6579
  } catch {
6420
6580
  return null;
6421
6581
  }
@@ -6546,24 +6706,16 @@ function extractFirstUserPrompt(transcript) {
6546
6706
  }
6547
6707
  return "";
6548
6708
  }
6549
- function composeReviewBody(editCount, client, userPrompt, autoPropose) {
6709
+ function composeReviewBody(editCount, client, userPrompt, proposalMode) {
6550
6710
  const safePrompt = userPrompt || "(could not extract \u2014 re-read transcript)";
6551
- const proposalLine = autoPropose ? [
6552
- " **Auto-propose mode is ON** (configured at install). If \u2014 and ONLY",
6553
- " if \u2014 one gap clears the bar above, call WITHOUT asking the user:",
6554
- " `cybe_rules_report_missing(projectId, { rule_text, evidence_files,",
6555
- ' rationale: "<quote user prompt>", confirmed_by_user: true })` for',
6556
- " that single rule. The user validates it at the next session start",
6557
- " via the Accept/Reject picker. If nothing clears the bar, propose",
6558
- " nothing."
6559
- ].join("\n") : [
6711
+ const proposalLine = proposalMode === "ask" ? [
6560
6712
  " Ask the user via AskUserQuestion:",
6561
6713
  ' > "Your prompt implied <concern>, but no business rule covers it.',
6562
6714
  ' > Propose it as a standing rule for the project?" \u2014 Yes / No / Edit.',
6563
6715
  ' ONLY on an explicit "yes": call `cybe_rules_report_missing(projectId,',
6564
6716
  ' { rule_text, evidence_files, rationale: "<quote user prompt>",',
6565
6717
  " confirmed_by_user: true })`."
6566
- ].join("\n");
6718
+ ].join("\n") : ` ${DIRECT_PROPOSE_DIRECTIVES[proposalMode]}`;
6567
6719
  return [
6568
6720
  `## \u{1F6E1}\uFE0F CybeDefend gap analysis (Stop hook \u2014 ${editCount} edits this session, client=${client})`,
6569
6721
  "",
@@ -6635,9 +6787,15 @@ function composeDiffScanBody(changedFiles) {
6635
6787
  ""
6636
6788
  ].join("\n");
6637
6789
  }
6638
- function composeStopReason(withDiffScan) {
6790
+ function composeStopReason(withDiffScan, proposalMode) {
6639
6791
  const scan = withDiffScan ? ", then run a pre-commit `cybe_scan_diff` on the files you changed (poll `cybe_scan_status`, read `cybe_scan_findings`) and fix or justify each finding" : "";
6640
- return "\u{1F6E1}\uFE0F CybeDefend \u2014 before wrapping up, run the end-of-session review: gap-analyse the request against this session's business rules and, for any uncovered security/data concern, offer to propose a rule via AskUserQuestion (`cybe_rules_report_missing` only on an explicit yes)" + scan + ".";
6792
+ const directPropose = "if \u2014 and only if \u2014 one durable gap stands out, propose that single rule directly with `cybe_rules_report_missing` (`confirmed_by_user: true`). ";
6793
+ const propose = {
6794
+ ask: "for any uncovered security/data concern, offer to propose a rule via AskUserQuestion (`cybe_rules_report_missing` only on an explicit yes)",
6795
+ auto: directPropose + "Auto-propose mode: do NOT ask the user (no AskUserQuestion) \u2014 they review it at the next session start",
6796
+ "non-blocking": directPropose + "Non-blocking mode: do NOT ask the user (no AskUserQuestion) \u2014 a human reviews the proposal in the CybeDefend dashboard"
6797
+ };
6798
+ return "\u{1F6E1}\uFE0F CybeDefend \u2014 before wrapping up, run the end-of-session review: gap-analyse the request against this session's business rules and, " + propose[proposalMode] + scan + ".";
6641
6799
  }
6642
6800
 
6643
6801
  // src/hooks/runtime/pre-compact.ts
@@ -6658,6 +6816,11 @@ async function preCompactHook(deps = {}) {
6658
6816
  );
6659
6817
  return;
6660
6818
  }
6819
+ const proposalMode = resolveProposalMode(config);
6820
+ const proposeStep = proposalMode === "ask" ? [
6821
+ "3. For uncovered concerns, ask the user via AskUserQuestion whether to propose;",
6822
+ ' on "yes", call `cybe_rules_report_missing` with confirmed_by_user: true.'
6823
+ ] : [`3. ${DIRECT_PROPOSE_DIRECTIVES[proposalMode]}`];
6661
6824
  const body = [
6662
6825
  "## \u{1F6E1}\uFE0F CybeDefend PreCompact gap analysis",
6663
6826
  "",
@@ -6665,27 +6828,26 @@ async function preCompactHook(deps = {}) {
6665
6828
  "Before the fine-grained tool history is summarised away, run a gap analysis:",
6666
6829
  "1. Re-read the original user request (still uncompacted now).",
6667
6830
  "2. List the CybeDefend rules returned during the session.",
6668
- "3. For uncovered concerns, ask the user via AskUserQuestion whether to propose;",
6669
- ' on "yes", call `cybe_rules_report_missing` with confirmed_by_user: true.',
6831
+ ...proposeStep,
6670
6832
  ""
6671
6833
  ].join("\n");
6672
6834
  emitFn(body, event.client);
6673
6835
  }
6674
6836
 
6675
6837
  // src/hooks/runtime/user-prompt-submit.ts
6676
- import { existsSync as existsSync10, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
6677
- import { homedir as homedir7 } from "node:os";
6678
- import { join as join7 } from "node:path";
6838
+ import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
6839
+ import { homedir as homedir8 } from "node:os";
6840
+ import { join as join8 } from "node:path";
6679
6841
  var SESSIONS_CAP = 100;
6680
6842
  function defaultSessionsFile() {
6681
- const dir = process.env.CYBEDEFEND_BASELINE_DIR ?? join7(homedir7(), ".cybedefend");
6682
- return join7(dir, "user-prompt-seen-sessions.json");
6843
+ const dir = process.env.CYBEDEFEND_BASELINE_DIR ?? join8(homedir8(), ".cybedefend");
6844
+ return join8(dir, "user-prompt-seen-sessions.json");
6683
6845
  }
6684
6846
  function defaultSessionTracker(file = defaultSessionsFile()) {
6685
6847
  function load() {
6686
- if (!existsSync10(file)) return [];
6848
+ if (!existsSync11(file)) return [];
6687
6849
  try {
6688
- const raw = readFileSync10(file, "utf8");
6850
+ const raw = readFileSync11(file, "utf8");
6689
6851
  const data = JSON.parse(raw);
6690
6852
  if (!Array.isArray(data.sessions)) return [];
6691
6853
  return data.sessions.filter((s) => typeof s === "string");
@@ -6702,8 +6864,8 @@ function defaultSessionTracker(file = defaultSessionsFile()) {
6702
6864
  if (existing.includes(sessionId)) return;
6703
6865
  const next = [...existing, sessionId].slice(-SESSIONS_CAP);
6704
6866
  try {
6705
- mkdirSync4(join7(file, ".."), { recursive: true });
6706
- writeFileSync5(file, JSON.stringify({ sessions: next }), "utf8");
6867
+ mkdirSync4(join8(file, ".."), { recursive: true });
6868
+ writeFileSync6(file, JSON.stringify({ sessions: next }), "utf8");
6707
6869
  } catch {
6708
6870
  }
6709
6871
  }
@@ -6728,7 +6890,9 @@ async function userPromptSubmitHook(deps = {}) {
6728
6890
  const { projectId, apiBase: configApiBase } = await resolveProject();
6729
6891
  if (!projectId) return;
6730
6892
  const apiBase = process.env.CYBEDEFEND_API_BASE ?? configApiBase ?? config.region.apiBase;
6731
- const token = resolveTok(config.region.mcpName);
6893
+ const token = resolveTok(config.region.mcpName, {
6894
+ mcpUrl: config.region.mcpUrl
6895
+ });
6732
6896
  let total = null;
6733
6897
  if (token) {
6734
6898
  const result = await fetchProposals({ apiBase, projectId, token });
@@ -6741,7 +6905,13 @@ async function userPromptSubmitHook(deps = {}) {
6741
6905
  "",
6742
6906
  "### Step 0 \u2014 Proposal inbox"
6743
6907
  ];
6744
- if (total === null) {
6908
+ const nonBlocking = isNonBlockingMode(config);
6909
+ if (nonBlocking) {
6910
+ lines.push(
6911
+ `- CybeDefend: ${total === null ? "proposal inbox not checked" : `**${total}** pending proposals`} \u2014 no action required.`,
6912
+ `- ${NON_BLOCKING_INBOX_DIRECTIVE}`
6913
+ );
6914
+ } else if (total === null) {
6745
6915
  lines.push(
6746
6916
  "- CybeDefend: could not fetch proposal-inbox status (token / API unreachable).",
6747
6917
  '- Call `cybe_rules_list_proposals(projectId, status="proposed")` yourself to verify, then handle each surfaced proposal via AskUserQuestion (Accept / Reject / Skip / Tell me more) before continuing.'
@@ -6767,7 +6937,7 @@ async function userPromptSubmitHook(deps = {}) {
6767
6937
  "- The PreToolUse hook fires automatically as a backstop on Edit/Write/MultiEdit, but your explicit call with a narrative intent gets better matches.",
6768
6938
  "",
6769
6939
  "### Order",
6770
- "1. Satisfy step 0 (acknowledge / handle the inbox).",
6940
+ nonBlocking ? "1. Step 0 needs no action (non-blocking mode)." : "1. Satisfy step 0 (acknowledge / handle the inbox).",
6771
6941
  "2. Satisfy step 1 (fetch rules with a narrative intent) once you know which files you will touch.",
6772
6942
  "3. THEN proceed with whatever skill / brainstorm / implementation flow fits the task.",
6773
6943
  ""
@@ -7386,7 +7556,7 @@ function validateRule(rule) {
7386
7556
  }
7387
7557
 
7388
7558
  // src/guards-evaluator/redact.ts
7389
- import { createHash } from "node:crypto";
7559
+ import { createHash as createHash2 } from "node:crypto";
7390
7560
  var REDACTED = "[REDACTED]";
7391
7561
  var PATTERNS = [
7392
7562
  // 1. JWT — three base64url segments separated by dots
@@ -7488,7 +7658,7 @@ function redactTarget(text, maxLen = 256) {
7488
7658
  return result;
7489
7659
  }
7490
7660
  function hashTarget(text) {
7491
- return createHash("sha256").update(text, "utf8").digest("hex");
7661
+ return createHash2("sha256").update(text, "utf8").digest("hex");
7492
7662
  }
7493
7663
 
7494
7664
  // src/hooks/runtime/near-miss.ts
@@ -7766,10 +7936,10 @@ function nearMiss(req, rule) {
7766
7936
  }
7767
7937
 
7768
7938
  // src/hooks/runtime/guard-rules-cache.ts
7769
- import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as existsSync11, mkdirSync as mkdirSync5 } from "node:fs";
7770
- import { dirname as dirname4 } from "node:path";
7771
- import { homedir as homedir8 } from "node:os";
7772
- import { join as join8 } from "node:path";
7939
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync12, mkdirSync as mkdirSync5 } from "node:fs";
7940
+ import { dirname as dirname5 } from "node:path";
7941
+ import { homedir as homedir9 } from "node:os";
7942
+ import { join as join9 } from "node:path";
7773
7943
 
7774
7944
  // node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/external.js
7775
7945
  var external_exports = {};
@@ -11881,8 +12051,8 @@ function parseRuleFields(raw) {
11881
12051
  }
11882
12052
 
11883
12053
  // src/hooks/runtime/guard-rules-cache.ts
11884
- var DEFAULT_CACHE_FILE = join8(
11885
- homedir8(),
12054
+ var DEFAULT_CACHE_FILE = join9(
12055
+ homedir9(),
11886
12056
  ".cybedefend",
11887
12057
  "guards-cache.json"
11888
12058
  );
@@ -12006,9 +12176,9 @@ function makeGuardRulesCache(opts) {
12006
12176
  }
12007
12177
  }
12008
12178
  function readCache(projectId) {
12009
- if (!existsSync11(cacheFile)) return null;
12179
+ if (!existsSync12(cacheFile)) return null;
12010
12180
  try {
12011
- const raw = readFileSync11(cacheFile, "utf8");
12181
+ const raw = readFileSync12(cacheFile, "utf8");
12012
12182
  const data = JSON.parse(raw);
12013
12183
  if (data.projectId !== projectId) return null;
12014
12184
  return data;
@@ -12019,15 +12189,15 @@ function makeGuardRulesCache(opts) {
12019
12189
  function writeCache(entry) {
12020
12190
  try {
12021
12191
  ensureDirFor2(cacheFile);
12022
- writeFileSync6(cacheFile, JSON.stringify(entry), "utf8");
12192
+ writeFileSync7(cacheFile, JSON.stringify(entry), "utf8");
12023
12193
  } catch {
12024
12194
  }
12025
12195
  }
12026
12196
  return { refreshIfStale };
12027
12197
  }
12028
12198
  function ensureDirFor2(filePath) {
12029
- const dir = dirname4(filePath);
12030
- if (!existsSync11(dir)) {
12199
+ const dir = dirname5(filePath);
12200
+ if (!existsSync12(dir)) {
12031
12201
  mkdirSync5(dir, { recursive: true });
12032
12202
  }
12033
12203
  }
@@ -12149,11 +12319,11 @@ function detectPromptInjection(text) {
12149
12319
  }
12150
12320
 
12151
12321
  // src/hooks/runtime/judge-cache.ts
12152
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync12, mkdirSync as mkdirSync6 } from "node:fs";
12153
- import { dirname as dirname5, join as join9 } from "node:path";
12154
- import { homedir as homedir9 } from "node:os";
12155
- var DEFAULT_JUDGE_CACHE_FILE = join9(
12156
- homedir9(),
12322
+ import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, existsSync as existsSync13, mkdirSync as mkdirSync6 } from "node:fs";
12323
+ import { dirname as dirname6, join as join10 } from "node:path";
12324
+ import { homedir as homedir10 } from "node:os";
12325
+ var DEFAULT_JUDGE_CACHE_FILE = join10(
12326
+ homedir10(),
12157
12327
  ".cybedefend",
12158
12328
  "guards-judge-cache.json"
12159
12329
  );
@@ -12163,9 +12333,9 @@ function makeJudgeCache(opts = {}) {
12163
12333
  const ttlMs = opts.ttlMs ?? DEFAULT_JUDGE_TTL_MS;
12164
12334
  const now = opts.now ?? (() => Date.now());
12165
12335
  function readAll() {
12166
- if (!existsSync12(cacheFile)) return {};
12336
+ if (!existsSync13(cacheFile)) return {};
12167
12337
  try {
12168
- return JSON.parse(readFileSync12(cacheFile, "utf8"));
12338
+ return JSON.parse(readFileSync13(cacheFile, "utf8"));
12169
12339
  } catch {
12170
12340
  return {};
12171
12341
  }
@@ -12185,36 +12355,36 @@ function makeJudgeCache(opts = {}) {
12185
12355
  }
12186
12356
  all[key] = { res, at: t };
12187
12357
  ensureDirFor3(cacheFile);
12188
- writeFileSync7(cacheFile, JSON.stringify(all), "utf8");
12358
+ writeFileSync8(cacheFile, JSON.stringify(all), "utf8");
12189
12359
  } catch {
12190
12360
  }
12191
12361
  }
12192
12362
  return { get, set };
12193
12363
  }
12194
12364
  function ensureDirFor3(filePath) {
12195
- const dir = dirname5(filePath);
12196
- if (!existsSync12(dir)) {
12365
+ const dir = dirname6(filePath);
12366
+ if (!existsSync13(dir)) {
12197
12367
  mkdirSync6(dir, { recursive: true });
12198
12368
  }
12199
12369
  }
12200
12370
 
12201
12371
  // src/hooks/runtime/guard-check.ts
12202
- import { homedir as homedir11 } from "node:os";
12203
- import { join as join11 } from "node:path";
12372
+ import { homedir as homedir12 } from "node:os";
12373
+ import { join as join12 } from "node:path";
12204
12374
 
12205
12375
  // src/hooks/runtime/guard-violations-buffer.ts
12206
12376
  import {
12207
12377
  appendFileSync,
12208
- existsSync as existsSync13,
12209
- readFileSync as readFileSync13,
12210
- writeFileSync as writeFileSync8,
12378
+ existsSync as existsSync14,
12379
+ readFileSync as readFileSync14,
12380
+ writeFileSync as writeFileSync9,
12211
12381
  mkdirSync as mkdirSync7
12212
12382
  } from "node:fs";
12213
- import { dirname as dirname6 } from "node:path";
12214
- import { homedir as homedir10 } from "node:os";
12215
- import { join as join10 } from "node:path";
12216
- var DEFAULT_BUFFER_FILE = join10(
12217
- homedir10(),
12383
+ import { dirname as dirname7 } from "node:path";
12384
+ import { homedir as homedir11 } from "node:os";
12385
+ import { join as join11 } from "node:path";
12386
+ var DEFAULT_BUFFER_FILE = join11(
12387
+ homedir11(),
12218
12388
  ".cybedefend",
12219
12389
  "guards-violations-buffer.jsonl"
12220
12390
  );
@@ -12233,10 +12403,10 @@ function makeViolationsBuffer(opts = {}) {
12233
12403
  }
12234
12404
  }
12235
12405
  async function flushViolations2(ctx, postFn) {
12236
- if (!existsSync13(bufferFile)) return;
12406
+ if (!existsSync14(bufferFile)) return;
12237
12407
  let content;
12238
12408
  try {
12239
- content = readFileSync13(bufferFile, "utf8").trim();
12409
+ content = readFileSync14(bufferFile, "utf8").trim();
12240
12410
  } catch {
12241
12411
  return;
12242
12412
  }
@@ -12255,7 +12425,7 @@ function makeViolationsBuffer(opts = {}) {
12255
12425
  }
12256
12426
  if (records.length === 0) {
12257
12427
  try {
12258
- writeFileSync8(bufferFile, "", "utf8");
12428
+ writeFileSync9(bufferFile, "", "utf8");
12259
12429
  } catch {
12260
12430
  }
12261
12431
  return;
@@ -12285,10 +12455,10 @@ function makeViolationsBuffer(opts = {}) {
12285
12455
  }
12286
12456
  try {
12287
12457
  if (failed.length === 0) {
12288
- writeFileSync8(bufferFile, "", "utf8");
12458
+ writeFileSync9(bufferFile, "", "utf8");
12289
12459
  } else {
12290
12460
  const serialised = failed.map((r) => JSON.stringify(r)).join("\n") + "\n";
12291
- writeFileSync8(bufferFile, serialised, "utf8");
12461
+ writeFileSync9(bufferFile, serialised, "utf8");
12292
12462
  }
12293
12463
  } catch {
12294
12464
  }
@@ -12296,8 +12466,8 @@ function makeViolationsBuffer(opts = {}) {
12296
12466
  return { bufferViolation: bufferViolation2, flushViolations: flushViolations2 };
12297
12467
  }
12298
12468
  function ensureDirFor4(filePath) {
12299
- const dir = dirname6(filePath);
12300
- if (!existsSync13(dir)) {
12469
+ const dir = dirname7(filePath);
12470
+ if (!existsSync14(dir)) {
12301
12471
  mkdirSync7(dir, { recursive: true });
12302
12472
  }
12303
12473
  }
@@ -12325,10 +12495,10 @@ function appendDebugLog(entry) {
12325
12495
  const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "";
12326
12496
  if (!homeDir) return;
12327
12497
  const { mkdirSync: mkdirSync8, appendFileSync: appendFileSync2 } = __require("node:fs");
12328
- const { join: join12 } = __require("node:path");
12329
- const dir = join12(homeDir, ".cybedefend");
12498
+ const { join: join13 } = __require("node:path");
12499
+ const dir = join13(homeDir, ".cybedefend");
12330
12500
  mkdirSync8(dir, { recursive: true });
12331
- appendFileSync2(join12(dir, "hook-debug.log"), entry + "\n", "utf8");
12501
+ appendFileSync2(join13(dir, "hook-debug.log"), entry + "\n", "utf8");
12332
12502
  } catch {
12333
12503
  }
12334
12504
  }
@@ -12814,8 +12984,6 @@ function emitAgentMessage(verdict, message) {
12814
12984
  // Claude Code legacy
12815
12985
  decision: "block",
12816
12986
  reason: fullMessage,
12817
- continue: false,
12818
- stopReason: fullMessage,
12819
12987
  // Claude Code modern
12820
12988
  hookSpecificOutput: {
12821
12989
  hookEventName: "PreToolUse",
@@ -12844,9 +13012,9 @@ function emitAgentMessage(verdict, message) {
12844
13012
  }
12845
13013
  function makeProductionJudgeDeps() {
12846
13014
  const cache = makeJudgeCache();
12847
- const warnDir = process.env.CYBEDEFEND_BASELINE_DIR ?? join11(homedir11(), ".cybedefend");
13015
+ const warnDir = process.env.CYBEDEFEND_BASELINE_DIR ?? join12(homedir12(), ".cybedefend");
12848
13016
  const warnTracker = defaultSessionTracker(
12849
- join11(warnDir, "guards-judge-warned-sessions.json")
13017
+ join12(warnDir, "guards-judge-warned-sessions.json")
12850
13018
  );
12851
13019
  return {
12852
13020
  callJudge,
@@ -12871,7 +13039,11 @@ async function runPreToolUseGuard() {
12871
13039
  const { projectId, apiBase: configApiBase } = await resolveProjectIdWithApi();
12872
13040
  if (!projectId) return 0;
12873
13041
  const apiBaseResolved = process.env.CYBEDEFEND_API_BASE ?? configApiBase ?? config.region.apiBase;
12874
- const token = await resolveTokenWithRefresh(config.region.mcpName);
13042
+ const token = await resolveTokenWithRefresh(
13043
+ config.region.mcpName,
13044
+ {},
13045
+ { mcpUrl: config.region.mcpUrl }
13046
+ );
12875
13047
  if (!token) {
12876
13048
  emitUnverifiedBanner(
12877
13049
  "Not authenticated \u2014 run `vibedefend login` to restore Action Guards."