@cybedefend/vibedefend 1.4.4 → 1.4.6

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 (67) hide show
  1. package/README.md +39 -3
  2. package/dist/auth/auth.js +15 -2
  3. package/dist/auth/auth.js.map +1 -1
  4. package/dist/clients/claude-code.js +108 -30
  5. package/dist/clients/claude-code.js.map +1 -1
  6. package/dist/clients/codex.js +117 -18
  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 +24 -14
  11. package/dist/clients/cursor.js.map +1 -1
  12. package/dist/clients/mcp-url.js +42 -0
  13. package/dist/clients/mcp-url.js.map +1 -0
  14. package/dist/clients/unwire.js +57 -0
  15. package/dist/clients/unwire.js.map +1 -0
  16. package/dist/clients/vscode.js +38 -16
  17. package/dist/clients/vscode.js.map +1 -1
  18. package/dist/clients/windsurf.js +32 -14
  19. package/dist/clients/windsurf.js.map +1 -1
  20. package/dist/config.js +7 -0
  21. package/dist/config.js.map +1 -1
  22. package/dist/diagnostics.js +4 -2
  23. package/dist/diagnostics.js.map +1 -1
  24. package/dist/doctor.js +15 -3
  25. package/dist/doctor.js.map +1 -1
  26. package/dist/global-cli.js +138 -0
  27. package/dist/global-cli.js.map +1 -0
  28. package/dist/hook-runner.js +196 -85
  29. package/dist/hooks/install.js +3 -1
  30. package/dist/hooks/install.js.map +1 -1
  31. package/dist/hooks/runtime/pre-compact.js +9 -2
  32. package/dist/hooks/runtime/pre-compact.js.map +1 -1
  33. package/dist/hooks/runtime/proposal-mode.js +75 -0
  34. package/dist/hooks/runtime/proposal-mode.js.map +1 -0
  35. package/dist/hooks/runtime/resolve.js +12 -5
  36. package/dist/hooks/runtime/resolve.js.map +1 -1
  37. package/dist/hooks/runtime/session-review.js +40 -20
  38. package/dist/hooks/runtime/session-review.js.map +1 -1
  39. package/dist/hooks/runtime/session-start.js +7 -1
  40. package/dist/hooks/runtime/session-start.js.map +1 -1
  41. package/dist/hooks/runtime/shell-writes.js +3 -2
  42. package/dist/hooks/runtime/shell-writes.js.map +1 -1
  43. package/dist/hooks/runtime/user-prompt-submit.js +12 -2
  44. package/dist/hooks/runtime/user-prompt-submit.js.map +1 -1
  45. package/dist/index.js +12 -1
  46. package/dist/index.js.map +1 -1
  47. package/dist/install.js +118 -23
  48. package/dist/install.js.map +1 -1
  49. package/dist/json-file.js +115 -0
  50. package/dist/json-file.js.map +1 -0
  51. package/dist/link.js +5 -2
  52. package/dist/link.js.map +1 -1
  53. package/dist/login.js +12 -4
  54. package/dist/login.js.map +1 -1
  55. package/dist/prompts.js +18 -3
  56. package/dist/prompts.js.map +1 -1
  57. package/dist/refresh-hooks.js +1 -1
  58. package/dist/refresh-hooks.js.map +1 -1
  59. package/dist/self-update.js +5 -0
  60. package/dist/self-update.js.map +1 -1
  61. package/dist/status.js +5 -1
  62. package/dist/status.js.map +1 -1
  63. package/dist/uninstall.js +166 -0
  64. package/dist/uninstall.js.map +1 -0
  65. package/dist/utils.js +1 -20
  66. package/dist/utils.js.map +1 -1
  67. package/package.json +6 -4
@@ -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) {
@@ -4897,7 +4962,7 @@ function extractToolInputShape(input, client) {
4897
4962
  // src/hooks/runtime/resolve.ts
4898
4963
  import { execFileSync as execFileSync2 } from "node:child_process";
4899
4964
  import { createHash } from "node:crypto";
4900
- import { existsSync as existsSync6, readFileSync as readFileSync6 } from "node:fs";
4965
+ import { existsSync as existsSync6, readFileSync as readFileSync5 } from "node:fs";
4901
4966
 
4902
4967
  // src/auth/auth-store.ts
4903
4968
  import { execFileSync } from "node:child_process";
@@ -5242,11 +5307,11 @@ async function resolveProjectByRepository(apiBase, token, q, deps = {}) {
5242
5307
  }
5243
5308
 
5244
5309
  // src/hooks/runtime/repo-link-cache.ts
5245
- import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync3 } from "node:fs";
5310
+ import { existsSync as existsSync3, mkdirSync as mkdirSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
5246
5311
  import { dirname as dirname2 } from "node:path";
5247
5312
 
5248
5313
  // src/utils.ts
5249
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
5314
+ import { existsSync as existsSync2, mkdirSync as mkdirSync2 } from "node:fs";
5250
5315
  import { dirname, join as join2 } from "node:path";
5251
5316
  import { homedir as homedir3, platform } from "node:os";
5252
5317
 
@@ -5371,7 +5436,7 @@ function readRepoLinkCache(deps = {}) {
5371
5436
  const p = pathOf(deps);
5372
5437
  if (!existsSync3(p)) return {};
5373
5438
  try {
5374
- const raw = readFileSync3(p, "utf8");
5439
+ const raw = readFileSync2(p, "utf8");
5375
5440
  if (!raw.trim()) return {};
5376
5441
  const parsed = JSON.parse(raw);
5377
5442
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
@@ -5419,13 +5484,13 @@ function writeCachedRepoLink(key, entry, deps = {}) {
5419
5484
  const p = pathOf(deps);
5420
5485
  const dir = dirname2(p);
5421
5486
  if (!existsSync3(dir)) mkdirSync3(dir, { recursive: true });
5422
- writeFileSync3(p, JSON.stringify(map, null, 2) + "\n");
5487
+ writeFileSync2(p, JSON.stringify(map, null, 2) + "\n");
5423
5488
  } catch {
5424
5489
  }
5425
5490
  }
5426
5491
 
5427
5492
  // src/hooks/runtime/config.ts
5428
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
5493
+ import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
5429
5494
  import { homedir as homedir4 } from "node:os";
5430
5495
  import { join as join3 } from "node:path";
5431
5496
  function runtimeConfigPath() {
@@ -5435,7 +5500,7 @@ function loadRuntimeConfig(path) {
5435
5500
  const p = path ?? runtimeConfigPath();
5436
5501
  if (!existsSync4(p)) return null;
5437
5502
  try {
5438
- const raw = readFileSync4(p, "utf8");
5503
+ const raw = readFileSync3(p, "utf8");
5439
5504
  return JSON.parse(raw);
5440
5505
  } catch {
5441
5506
  return null;
@@ -5457,7 +5522,7 @@ async function readStdinJson() {
5457
5522
  }
5458
5523
 
5459
5524
  // src/hooks/runtime/trusted-api-base.ts
5460
- import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
5525
+ import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
5461
5526
  import { homedir as homedir5 } from "node:os";
5462
5527
  import { join as join4 } from "node:path";
5463
5528
 
@@ -5506,7 +5571,7 @@ function normalizeOrigin(candidate) {
5506
5571
  function readUserRegionApiBases(path) {
5507
5572
  try {
5508
5573
  if (!existsSync5(path)) return [];
5509
- const parsed = JSON.parse(readFileSync5(path, "utf8"));
5574
+ const parsed = JSON.parse(readFileSync4(path, "utf8"));
5510
5575
  if (!Array.isArray(parsed)) return [];
5511
5576
  return parsed.map(
5512
5577
  (entry) => entry && typeof entry === "object" ? entry.apiBase : null
@@ -5575,7 +5640,7 @@ function resolveProjectId(cwd = process.cwd(), deps = {}) {
5575
5640
  const configPath = `${cwd}/.cybedefend/config.json`;
5576
5641
  if (existsSync6(configPath)) {
5577
5642
  try {
5578
- const raw = readFileSync6(configPath, "utf8");
5643
+ const raw = readFileSync5(configPath, "utf8");
5579
5644
  const cfg = JSON.parse(raw);
5580
5645
  return {
5581
5646
  projectId: cfg.projectId ?? null,
@@ -5596,7 +5661,7 @@ function resolveToken(mcpName, opts = {}) {
5596
5661
  const platform2 = opts.platform ?? process.platform;
5597
5662
  const readKeychain = opts.readKeychain ?? readMacOSKeychain2;
5598
5663
  if (platform2 === "darwin") {
5599
- return resolveTokenMacOS(mcpName, readKeychain) ?? resolveTokenCodexMacOS(mcpName, opts.mcpUrl, readKeychain);
5664
+ return resolveTokenMacOS(mcpName, opts.mcpUrl, readKeychain) ?? resolveTokenCodexMacOS(mcpName, opts.mcpUrl, readKeychain);
5600
5665
  }
5601
5666
  if (platform2 === "linux") return resolveTokenLinux(mcpName);
5602
5667
  return null;
@@ -5627,16 +5692,18 @@ function readMacOSKeychain2(service, account) {
5627
5692
  return null;
5628
5693
  }
5629
5694
  }
5630
- function resolveTokenMacOS(mcpName, readKeychain) {
5695
+ function resolveTokenMacOS(mcpName, mcpUrl, readKeychain) {
5631
5696
  const blob = readKeychain("Claude Code-credentials");
5632
5697
  if (!blob) return null;
5633
5698
  try {
5634
5699
  const data = JSON.parse(blob);
5635
5700
  const oauth = data.mcpOAuth ?? {};
5636
5701
  for (const entry of Object.values(oauth)) {
5637
- if (entry?.serverName === mcpName && entry.accessToken) {
5638
- return entry.accessToken;
5702
+ if (entry?.serverName !== mcpName || !entry.accessToken) continue;
5703
+ if (mcpUrl && typeof entry.serverUrl === "string" && entry.serverUrl !== mcpUrl) {
5704
+ continue;
5639
5705
  }
5706
+ return entry.accessToken;
5640
5707
  }
5641
5708
  } catch {
5642
5709
  return blob || null;
@@ -6006,9 +6073,33 @@ function composeSecurityBody(markdown, total, filePath) {
6006
6073
  ].join("\n");
6007
6074
  }
6008
6075
 
6076
+ // src/hooks/runtime/proposal-mode.ts
6077
+ var TRUTHY = /* @__PURE__ */ new Set(["1", "true", "yes", "on"]);
6078
+ var FALSY = /* @__PURE__ */ new Set(["0", "false", "no", "off"]);
6079
+ function envFlag(env, name) {
6080
+ const raw = env[name]?.trim().toLowerCase();
6081
+ if (raw && TRUTHY.has(raw)) return true;
6082
+ if (raw && FALSY.has(raw)) return false;
6083
+ return void 0;
6084
+ }
6085
+ function isNonBlockingMode(config, env = process.env) {
6086
+ return envFlag(env, "CYBEDEFEND_NON_BLOCKING") ?? config.hooks.nonBlockingMode === true;
6087
+ }
6088
+ function resolveProposalMode(config, env = process.env) {
6089
+ if (isNonBlockingMode(config, env)) return "non-blocking";
6090
+ const auto = envFlag(env, "CYBEDEFEND_AUTO_PROPOSE") ?? config.hooks.autoProposeMode === true;
6091
+ return auto ? "auto" : "ask";
6092
+ }
6093
+ 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.";
6094
+ var REPORT_MISSING_CALL = '`cybe_rules_report_missing(projectId, { rule_text, evidence_files, rationale: "<quote user prompt>", confirmed_by_user: true })`';
6095
+ var DIRECT_PROPOSE_DIRECTIVES = {
6096
+ 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.`,
6097
+ "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.`
6098
+ };
6099
+
6009
6100
  // src/hooks/runtime/self-update-check.ts
6010
6101
  import { spawn } from "node:child_process";
6011
- import { existsSync as existsSync8, readFileSync as readFileSync7, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
6102
+ import { existsSync as existsSync8, readFileSync as readFileSync6, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
6012
6103
  import { homedir as homedir6 } from "node:os";
6013
6104
  import { join as join5 } from "node:path";
6014
6105
 
@@ -6046,6 +6137,7 @@ var GLOBAL_MODES = /* @__PURE__ */ new Set([
6046
6137
  ]);
6047
6138
  function detectInstallMode(scriptPath) {
6048
6139
  if (/[\\/]_npx[\\/]/i.test(scriptPath)) return "npx-cache";
6140
+ if (/[\\/]dlx(?:-\d+)?[\\/]/i.test(scriptPath)) return "npx-cache";
6049
6141
  if (/[\\/]pnpm(?:[\\/]global|[\\/]nodejs|[\\/]node_modules)/i.test(scriptPath)) {
6050
6142
  return "pnpm-global";
6051
6143
  }
@@ -6123,7 +6215,7 @@ function applyOrNotify(deps, installMode, latest, logPath) {
6123
6215
  function readThrottle(path) {
6124
6216
  try {
6125
6217
  if (!existsSync8(path)) return null;
6126
- const data = JSON.parse(readFileSync7(path, "utf8"));
6218
+ const data = JSON.parse(readFileSync6(path, "utf8"));
6127
6219
  if (typeof data.lastCheckedAt !== "number") return null;
6128
6220
  return { lastCheckedAt: data.lastCheckedAt };
6129
6221
  } catch {
@@ -6133,7 +6225,7 @@ function readThrottle(path) {
6133
6225
  function writeThrottle(path, throttle) {
6134
6226
  try {
6135
6227
  ensureDirFor(path);
6136
- writeFileSync4(path, JSON.stringify(throttle) + "\n");
6228
+ writeFileSync3(path, JSON.stringify(throttle) + "\n");
6137
6229
  } catch {
6138
6230
  }
6139
6231
  }
@@ -6142,7 +6234,7 @@ function takeUpdateLogNotice(logPath) {
6142
6234
  if (!existsSync8(logPath)) return null;
6143
6235
  let parsed = {};
6144
6236
  try {
6145
- parsed = JSON.parse(readFileSync7(logPath, "utf8"));
6237
+ parsed = JSON.parse(readFileSync6(logPath, "utf8"));
6146
6238
  } catch {
6147
6239
  parsed = {};
6148
6240
  }
@@ -6217,7 +6309,7 @@ function pmCommand(mode, spec) {
6217
6309
 
6218
6310
  // src/hooks/runtime/hook-refresh-trigger.ts
6219
6311
  import { spawn as spawn2 } from "node:child_process";
6220
- import { existsSync as existsSync9, readFileSync as readFileSync8, writeFileSync as writeFileSync5 } from "node:fs";
6312
+ import { existsSync as existsSync9, readFileSync as readFileSync7, writeFileSync as writeFileSync4 } from "node:fs";
6221
6313
  import { homedir as homedir7 } from "node:os";
6222
6314
  import { dirname as dirname3, join as join6 } from "node:path";
6223
6315
  function maybeRefreshStaleWiring(deps) {
@@ -6240,7 +6332,7 @@ function maybeRefreshStaleWiring(deps) {
6240
6332
  function readMarkerVersion(path) {
6241
6333
  try {
6242
6334
  if (!existsSync9(path)) return null;
6243
- const data = JSON.parse(readFileSync8(path, "utf8"));
6335
+ const data = JSON.parse(readFileSync7(path, "utf8"));
6244
6336
  return typeof data.version === "string" ? data.version : null;
6245
6337
  } catch {
6246
6338
  return null;
@@ -6249,7 +6341,7 @@ function readMarkerVersion(path) {
6249
6341
  function writeMarker(path, version) {
6250
6342
  try {
6251
6343
  ensureDirFor(path);
6252
- writeFileSync5(path, JSON.stringify({ version, spawnedAt: Date.now() }) + "\n");
6344
+ writeFileSync4(path, JSON.stringify({ version, spawnedAt: Date.now() }) + "\n");
6253
6345
  } catch {
6254
6346
  }
6255
6347
  }
@@ -6263,7 +6355,7 @@ function defaultSpawnRefresh(cliEntry) {
6263
6355
  }
6264
6356
 
6265
6357
  // src/version.ts
6266
- import { readFileSync as readFileSync9 } from "node:fs";
6358
+ import { readFileSync as readFileSync8 } from "node:fs";
6267
6359
  import { fileURLToPath as fileURLToPath2 } from "node:url";
6268
6360
  import { dirname as dirname4, join as join7 } from "node:path";
6269
6361
  var PKG_NAME = "@cybedefend/vibedefend";
@@ -6274,7 +6366,7 @@ function readPackageMeta(opts = {}) {
6274
6366
  if (typeof opts.bakedVersion === "string" && opts.bakedVersion.length > 0) {
6275
6367
  return { name: PKG_NAME, version: opts.bakedVersion };
6276
6368
  }
6277
- const readFile = opts.readFile ?? ((p) => readFileSync9(p, "utf8"));
6369
+ const readFile = opts.readFile ?? ((p) => readFileSync8(p, "utf8"));
6278
6370
  const pkgPath = opts.pkgPath ?? defaultPkgPath();
6279
6371
  try {
6280
6372
  return JSON.parse(readFile(pkgPath));
@@ -6283,7 +6375,7 @@ function readPackageMeta(opts = {}) {
6283
6375
  }
6284
6376
  }
6285
6377
  var pkg = readPackageMeta({
6286
- bakedVersion: true ? "1.4.4" : void 0
6378
+ bakedVersion: true ? "1.4.6" : void 0
6287
6379
  });
6288
6380
 
6289
6381
  // src/hooks/runtime/session-start.ts
@@ -6338,7 +6430,12 @@ async function sessionStartHook(deps = {}) {
6338
6430
  " if your client does not surface it, load the `cybe-init` MCP prompt manually.)"
6339
6431
  );
6340
6432
  lines.push("");
6341
- if (total === null) {
6433
+ if (isNonBlockingMode(config)) {
6434
+ lines.push(
6435
+ `- **Step 0 \u2014 proposals inbox**: ${total === null ? "not checked" : `${total} pending`} \u2014 no action required.`,
6436
+ ` ${NON_BLOCKING_INBOX_DIRECTIVE}`
6437
+ );
6438
+ } else if (total === null) {
6342
6439
  lines.push(
6343
6440
  "- **Step 0 \u2014 proposals inbox**: \u26A0 unable to verify (no MCP token reachable).",
6344
6441
  ' **YOU MUST call `cybe_rules_list_proposals(projectId, status="proposed")` as',
@@ -6394,7 +6491,7 @@ async function sessionStartHook(deps = {}) {
6394
6491
  }
6395
6492
 
6396
6493
  // src/hooks/runtime/session-review.ts
6397
- import { existsSync as existsSync10, readFileSync as readFileSync10 } from "node:fs";
6494
+ import { existsSync as existsSync10, readFileSync as readFileSync9 } from "node:fs";
6398
6495
  var COUNTED_TOOLS = /* @__PURE__ */ new Set([
6399
6496
  "Edit",
6400
6497
  "Write",
@@ -6410,6 +6507,11 @@ function transcriptHasReviewSentinel(transcript) {
6410
6507
  return false;
6411
6508
  }
6412
6509
  }
6510
+ function resolveReviewThreshold(config, env = process.env) {
6511
+ const raw = env.CYBEDEFEND_REVIEW_THRESHOLD?.trim();
6512
+ if (raw && /^\d+$/.test(raw) && Number(raw) <= 100) return Number(raw);
6513
+ return config.hooks.reviewThreshold;
6514
+ }
6413
6515
  async function sessionReviewHook(deps = {}) {
6414
6516
  const readStdin = deps.readStdin ?? readStdinJson;
6415
6517
  const loadConfig = deps.loadConfig ?? loadRuntimeConfig;
@@ -6430,20 +6532,21 @@ async function sessionReviewHook(deps = {}) {
6430
6532
  editCount = parseInt(process.env.CYBEDEFEND_DRY_RUN_EDITS ?? "5", 10);
6431
6533
  }
6432
6534
  if (editCount === 0) return;
6433
- if (editCount < config.hooks.reviewThreshold) return;
6535
+ if (editCount < resolveReviewThreshold(config)) return;
6434
6536
  const userPrompt = extractFirstUserPrompt(transcript);
6537
+ const proposalMode = resolveProposalMode(config);
6435
6538
  let body = composeReviewBody(
6436
6539
  editCount,
6437
6540
  event.client,
6438
6541
  userPrompt,
6439
- config.hooks.autoProposeMode
6542
+ proposalMode
6440
6543
  );
6441
6544
  const withDiffScan = config.hooks.enableDiffScan !== false;
6442
6545
  if (withDiffScan) {
6443
6546
  const cwd = typeof stdin.cwd === "string" && stdin.cwd ? stdin.cwd : process.cwd();
6444
6547
  body += "\n" + composeDiffScanBody(extractChangedFiles(transcript, void 0, cwd));
6445
6548
  }
6446
- emitFn(body, event.client, composeStopReason(withDiffScan));
6549
+ emitFn(body, event.client, composeStopReason(withDiffScan, proposalMode));
6447
6550
  }
6448
6551
  function loadTranscript(stdin, readFile) {
6449
6552
  if (Array.isArray(stdin.transcript)) return stdin.transcript;
@@ -6474,7 +6577,7 @@ function loadTranscript(stdin, readFile) {
6474
6577
  function defaultReadTranscript(path) {
6475
6578
  if (!existsSync10(path)) return null;
6476
6579
  try {
6477
- return readFileSync10(path, "utf8");
6580
+ return readFileSync9(path, "utf8");
6478
6581
  } catch {
6479
6582
  return null;
6480
6583
  }
@@ -6605,24 +6708,16 @@ function extractFirstUserPrompt(transcript) {
6605
6708
  }
6606
6709
  return "";
6607
6710
  }
6608
- function composeReviewBody(editCount, client, userPrompt, autoPropose) {
6711
+ function composeReviewBody(editCount, client, userPrompt, proposalMode) {
6609
6712
  const safePrompt = userPrompt || "(could not extract \u2014 re-read transcript)";
6610
- const proposalLine = autoPropose ? [
6611
- " **Auto-propose mode is ON** (configured at install). If \u2014 and ONLY",
6612
- " if \u2014 one gap clears the bar above, call WITHOUT asking the user:",
6613
- " `cybe_rules_report_missing(projectId, { rule_text, evidence_files,",
6614
- ' rationale: "<quote user prompt>", confirmed_by_user: true })` for',
6615
- " that single rule. The user validates it at the next session start",
6616
- " via the Accept/Reject picker. If nothing clears the bar, propose",
6617
- " nothing."
6618
- ].join("\n") : [
6713
+ const proposalLine = proposalMode === "ask" ? [
6619
6714
  " Ask the user via AskUserQuestion:",
6620
6715
  ' > "Your prompt implied <concern>, but no business rule covers it.',
6621
6716
  ' > Propose it as a standing rule for the project?" \u2014 Yes / No / Edit.',
6622
6717
  ' ONLY on an explicit "yes": call `cybe_rules_report_missing(projectId,',
6623
6718
  ' { rule_text, evidence_files, rationale: "<quote user prompt>",',
6624
6719
  " confirmed_by_user: true })`."
6625
- ].join("\n");
6720
+ ].join("\n") : ` ${DIRECT_PROPOSE_DIRECTIVES[proposalMode]}`;
6626
6721
  return [
6627
6722
  `## \u{1F6E1}\uFE0F CybeDefend gap analysis (Stop hook \u2014 ${editCount} edits this session, client=${client})`,
6628
6723
  "",
@@ -6694,9 +6789,15 @@ function composeDiffScanBody(changedFiles) {
6694
6789
  ""
6695
6790
  ].join("\n");
6696
6791
  }
6697
- function composeStopReason(withDiffScan) {
6792
+ function composeStopReason(withDiffScan, proposalMode) {
6698
6793
  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" : "";
6699
- 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 + ".";
6794
+ 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`). ";
6795
+ const propose = {
6796
+ ask: "for any uncovered security/data concern, offer to propose a rule via AskUserQuestion (`cybe_rules_report_missing` only on an explicit yes)",
6797
+ auto: directPropose + "Auto-propose mode: do NOT ask the user (no AskUserQuestion) \u2014 they review it at the next session start",
6798
+ "non-blocking": directPropose + "Non-blocking mode: do NOT ask the user (no AskUserQuestion) \u2014 a human reviews the proposal in the CybeDefend dashboard"
6799
+ };
6800
+ 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 + ".";
6700
6801
  }
6701
6802
 
6702
6803
  // src/hooks/runtime/pre-compact.ts
@@ -6717,6 +6818,11 @@ async function preCompactHook(deps = {}) {
6717
6818
  );
6718
6819
  return;
6719
6820
  }
6821
+ const proposalMode = resolveProposalMode(config);
6822
+ const proposeStep = proposalMode === "ask" ? [
6823
+ "3. For uncovered concerns, ask the user via AskUserQuestion whether to propose;",
6824
+ ' on "yes", call `cybe_rules_report_missing` with confirmed_by_user: true.'
6825
+ ] : [`3. ${DIRECT_PROPOSE_DIRECTIVES[proposalMode]}`];
6720
6826
  const body = [
6721
6827
  "## \u{1F6E1}\uFE0F CybeDefend PreCompact gap analysis",
6722
6828
  "",
@@ -6724,15 +6830,14 @@ async function preCompactHook(deps = {}) {
6724
6830
  "Before the fine-grained tool history is summarised away, run a gap analysis:",
6725
6831
  "1. Re-read the original user request (still uncompacted now).",
6726
6832
  "2. List the CybeDefend rules returned during the session.",
6727
- "3. For uncovered concerns, ask the user via AskUserQuestion whether to propose;",
6728
- ' on "yes", call `cybe_rules_report_missing` with confirmed_by_user: true.',
6833
+ ...proposeStep,
6729
6834
  ""
6730
6835
  ].join("\n");
6731
6836
  emitFn(body, event.client);
6732
6837
  }
6733
6838
 
6734
6839
  // src/hooks/runtime/user-prompt-submit.ts
6735
- import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync11, writeFileSync as writeFileSync6 } from "node:fs";
6840
+ import { existsSync as existsSync11, mkdirSync as mkdirSync4, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
6736
6841
  import { homedir as homedir8 } from "node:os";
6737
6842
  import { join as join8 } from "node:path";
6738
6843
  var SESSIONS_CAP = 100;
@@ -6744,7 +6849,7 @@ function defaultSessionTracker(file = defaultSessionsFile()) {
6744
6849
  function load() {
6745
6850
  if (!existsSync11(file)) return [];
6746
6851
  try {
6747
- const raw = readFileSync11(file, "utf8");
6852
+ const raw = readFileSync10(file, "utf8");
6748
6853
  const data = JSON.parse(raw);
6749
6854
  if (!Array.isArray(data.sessions)) return [];
6750
6855
  return data.sessions.filter((s) => typeof s === "string");
@@ -6762,7 +6867,7 @@ function defaultSessionTracker(file = defaultSessionsFile()) {
6762
6867
  const next = [...existing, sessionId].slice(-SESSIONS_CAP);
6763
6868
  try {
6764
6869
  mkdirSync4(join8(file, ".."), { recursive: true });
6765
- writeFileSync6(file, JSON.stringify({ sessions: next }), "utf8");
6870
+ writeFileSync5(file, JSON.stringify({ sessions: next }), "utf8");
6766
6871
  } catch {
6767
6872
  }
6768
6873
  }
@@ -6802,7 +6907,13 @@ async function userPromptSubmitHook(deps = {}) {
6802
6907
  "",
6803
6908
  "### Step 0 \u2014 Proposal inbox"
6804
6909
  ];
6805
- if (total === null) {
6910
+ const nonBlocking = isNonBlockingMode(config);
6911
+ if (nonBlocking) {
6912
+ lines.push(
6913
+ `- CybeDefend: ${total === null ? "proposal inbox not checked" : `**${total}** pending proposals`} \u2014 no action required.`,
6914
+ `- ${NON_BLOCKING_INBOX_DIRECTIVE}`
6915
+ );
6916
+ } else if (total === null) {
6806
6917
  lines.push(
6807
6918
  "- CybeDefend: could not fetch proposal-inbox status (token / API unreachable).",
6808
6919
  '- 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.'
@@ -6828,7 +6939,7 @@ async function userPromptSubmitHook(deps = {}) {
6828
6939
  "- The PreToolUse hook fires automatically as a backstop on Edit/Write/MultiEdit, but your explicit call with a narrative intent gets better matches.",
6829
6940
  "",
6830
6941
  "### Order",
6831
- "1. Satisfy step 0 (acknowledge / handle the inbox).",
6942
+ nonBlocking ? "1. Step 0 needs no action (non-blocking mode)." : "1. Satisfy step 0 (acknowledge / handle the inbox).",
6832
6943
  "2. Satisfy step 1 (fetch rules with a narrative intent) once you know which files you will touch.",
6833
6944
  "3. THEN proceed with whatever skill / brainstorm / implementation flow fits the task.",
6834
6945
  ""
@@ -7827,7 +7938,7 @@ function nearMiss(req, rule) {
7827
7938
  }
7828
7939
 
7829
7940
  // src/hooks/runtime/guard-rules-cache.ts
7830
- import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync12, mkdirSync as mkdirSync5 } from "node:fs";
7941
+ import { readFileSync as readFileSync11, writeFileSync as writeFileSync6, existsSync as existsSync12, mkdirSync as mkdirSync5 } from "node:fs";
7831
7942
  import { dirname as dirname5 } from "node:path";
7832
7943
  import { homedir as homedir9 } from "node:os";
7833
7944
  import { join as join9 } from "node:path";
@@ -12069,7 +12180,7 @@ function makeGuardRulesCache(opts) {
12069
12180
  function readCache(projectId) {
12070
12181
  if (!existsSync12(cacheFile)) return null;
12071
12182
  try {
12072
- const raw = readFileSync12(cacheFile, "utf8");
12183
+ const raw = readFileSync11(cacheFile, "utf8");
12073
12184
  const data = JSON.parse(raw);
12074
12185
  if (data.projectId !== projectId) return null;
12075
12186
  return data;
@@ -12080,7 +12191,7 @@ function makeGuardRulesCache(opts) {
12080
12191
  function writeCache(entry) {
12081
12192
  try {
12082
12193
  ensureDirFor2(cacheFile);
12083
- writeFileSync7(cacheFile, JSON.stringify(entry), "utf8");
12194
+ writeFileSync6(cacheFile, JSON.stringify(entry), "utf8");
12084
12195
  } catch {
12085
12196
  }
12086
12197
  }
@@ -12210,7 +12321,7 @@ function detectPromptInjection(text) {
12210
12321
  }
12211
12322
 
12212
12323
  // src/hooks/runtime/judge-cache.ts
12213
- import { readFileSync as readFileSync13, writeFileSync as writeFileSync8, existsSync as existsSync13, mkdirSync as mkdirSync6 } from "node:fs";
12324
+ import { readFileSync as readFileSync12, writeFileSync as writeFileSync7, existsSync as existsSync13, mkdirSync as mkdirSync6 } from "node:fs";
12214
12325
  import { dirname as dirname6, join as join10 } from "node:path";
12215
12326
  import { homedir as homedir10 } from "node:os";
12216
12327
  var DEFAULT_JUDGE_CACHE_FILE = join10(
@@ -12226,7 +12337,7 @@ function makeJudgeCache(opts = {}) {
12226
12337
  function readAll() {
12227
12338
  if (!existsSync13(cacheFile)) return {};
12228
12339
  try {
12229
- return JSON.parse(readFileSync13(cacheFile, "utf8"));
12340
+ return JSON.parse(readFileSync12(cacheFile, "utf8"));
12230
12341
  } catch {
12231
12342
  return {};
12232
12343
  }
@@ -12246,7 +12357,7 @@ function makeJudgeCache(opts = {}) {
12246
12357
  }
12247
12358
  all[key] = { res, at: t };
12248
12359
  ensureDirFor3(cacheFile);
12249
- writeFileSync8(cacheFile, JSON.stringify(all), "utf8");
12360
+ writeFileSync7(cacheFile, JSON.stringify(all), "utf8");
12250
12361
  } catch {
12251
12362
  }
12252
12363
  }
@@ -12267,8 +12378,8 @@ import { join as join12 } from "node:path";
12267
12378
  import {
12268
12379
  appendFileSync,
12269
12380
  existsSync as existsSync14,
12270
- readFileSync as readFileSync14,
12271
- writeFileSync as writeFileSync9,
12381
+ readFileSync as readFileSync13,
12382
+ writeFileSync as writeFileSync8,
12272
12383
  mkdirSync as mkdirSync7
12273
12384
  } from "node:fs";
12274
12385
  import { dirname as dirname7 } from "node:path";
@@ -12297,7 +12408,7 @@ function makeViolationsBuffer(opts = {}) {
12297
12408
  if (!existsSync14(bufferFile)) return;
12298
12409
  let content;
12299
12410
  try {
12300
- content = readFileSync14(bufferFile, "utf8").trim();
12411
+ content = readFileSync13(bufferFile, "utf8").trim();
12301
12412
  } catch {
12302
12413
  return;
12303
12414
  }
@@ -12316,7 +12427,7 @@ function makeViolationsBuffer(opts = {}) {
12316
12427
  }
12317
12428
  if (records.length === 0) {
12318
12429
  try {
12319
- writeFileSync9(bufferFile, "", "utf8");
12430
+ writeFileSync8(bufferFile, "", "utf8");
12320
12431
  } catch {
12321
12432
  }
12322
12433
  return;
@@ -12346,10 +12457,10 @@ function makeViolationsBuffer(opts = {}) {
12346
12457
  }
12347
12458
  try {
12348
12459
  if (failed.length === 0) {
12349
- writeFileSync9(bufferFile, "", "utf8");
12460
+ writeFileSync8(bufferFile, "", "utf8");
12350
12461
  } else {
12351
12462
  const serialised = failed.map((r) => JSON.stringify(r)).join("\n") + "\n";
12352
- writeFileSync9(bufferFile, serialised, "utf8");
12463
+ writeFileSync8(bufferFile, serialised, "utf8");
12353
12464
  }
12354
12465
  } catch {
12355
12466
  }