@cybedefend/vibedefend 1.4.4 → 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 (47) hide show
  1. package/README.md +39 -3
  2. package/dist/clients/claude-code.js +27 -0
  3. package/dist/clients/claude-code.js.map +1 -1
  4. package/dist/clients/codex.js +80 -8
  5. package/dist/clients/codex.js.map +1 -1
  6. package/dist/clients/cursor-guards-rules.js +26 -1
  7. package/dist/clients/cursor-guards-rules.js.map +1 -1
  8. package/dist/clients/cursor.js +16 -0
  9. package/dist/clients/cursor.js.map +1 -1
  10. package/dist/clients/unwire.js +57 -0
  11. package/dist/clients/unwire.js.map +1 -0
  12. package/dist/clients/vscode.js +29 -1
  13. package/dist/clients/vscode.js.map +1 -1
  14. package/dist/clients/windsurf.js +24 -1
  15. package/dist/clients/windsurf.js.map +1 -1
  16. package/dist/config.js +7 -0
  17. package/dist/config.js.map +1 -1
  18. package/dist/doctor.js +3 -1
  19. package/dist/doctor.js.map +1 -1
  20. package/dist/global-cli.js +138 -0
  21. package/dist/global-cli.js.map +1 -0
  22. package/dist/hook-runner.js +154 -45
  23. package/dist/hooks/install.js +1 -0
  24. package/dist/hooks/install.js.map +1 -1
  25. package/dist/hooks/runtime/pre-compact.js +9 -2
  26. package/dist/hooks/runtime/pre-compact.js.map +1 -1
  27. package/dist/hooks/runtime/proposal-mode.js +75 -0
  28. package/dist/hooks/runtime/proposal-mode.js.map +1 -0
  29. package/dist/hooks/runtime/session-review.js +40 -20
  30. package/dist/hooks/runtime/session-review.js.map +1 -1
  31. package/dist/hooks/runtime/session-start.js +7 -1
  32. package/dist/hooks/runtime/session-start.js.map +1 -1
  33. package/dist/hooks/runtime/shell-writes.js +3 -2
  34. package/dist/hooks/runtime/shell-writes.js.map +1 -1
  35. package/dist/hooks/runtime/user-prompt-submit.js +12 -2
  36. package/dist/hooks/runtime/user-prompt-submit.js.map +1 -1
  37. package/dist/index.js +12 -1
  38. package/dist/index.js.map +1 -1
  39. package/dist/install.js +23 -1
  40. package/dist/install.js.map +1 -1
  41. package/dist/prompts.js +18 -3
  42. package/dist/prompts.js.map +1 -1
  43. package/dist/self-update.js +5 -0
  44. package/dist/self-update.js.map +1 -1
  45. package/dist/uninstall.js +166 -0
  46. package/dist/uninstall.js.map +1 -0
  47. 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) {
@@ -6006,6 +6071,30 @@ function composeSecurityBody(markdown, total, filePath) {
6006
6071
  ].join("\n");
6007
6072
  }
6008
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
+
6009
6098
  // src/hooks/runtime/self-update-check.ts
6010
6099
  import { spawn } from "node:child_process";
6011
6100
  import { existsSync as existsSync8, readFileSync as readFileSync7, rmSync, writeFileSync as writeFileSync4 } from "node:fs";
@@ -6046,6 +6135,7 @@ var GLOBAL_MODES = /* @__PURE__ */ new Set([
6046
6135
  ]);
6047
6136
  function detectInstallMode(scriptPath) {
6048
6137
  if (/[\\/]_npx[\\/]/i.test(scriptPath)) return "npx-cache";
6138
+ if (/[\\/]dlx(?:-\d+)?[\\/]/i.test(scriptPath)) return "npx-cache";
6049
6139
  if (/[\\/]pnpm(?:[\\/]global|[\\/]nodejs|[\\/]node_modules)/i.test(scriptPath)) {
6050
6140
  return "pnpm-global";
6051
6141
  }
@@ -6283,7 +6373,7 @@ function readPackageMeta(opts = {}) {
6283
6373
  }
6284
6374
  }
6285
6375
  var pkg = readPackageMeta({
6286
- bakedVersion: true ? "1.4.4" : void 0
6376
+ bakedVersion: true ? "1.4.5" : void 0
6287
6377
  });
6288
6378
 
6289
6379
  // src/hooks/runtime/session-start.ts
@@ -6338,7 +6428,12 @@ async function sessionStartHook(deps = {}) {
6338
6428
  " if your client does not surface it, load the `cybe-init` MCP prompt manually.)"
6339
6429
  );
6340
6430
  lines.push("");
6341
- 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) {
6342
6437
  lines.push(
6343
6438
  "- **Step 0 \u2014 proposals inbox**: \u26A0 unable to verify (no MCP token reachable).",
6344
6439
  ' **YOU MUST call `cybe_rules_list_proposals(projectId, status="proposed")` as',
@@ -6410,6 +6505,11 @@ function transcriptHasReviewSentinel(transcript) {
6410
6505
  return false;
6411
6506
  }
6412
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
+ }
6413
6513
  async function sessionReviewHook(deps = {}) {
6414
6514
  const readStdin = deps.readStdin ?? readStdinJson;
6415
6515
  const loadConfig = deps.loadConfig ?? loadRuntimeConfig;
@@ -6430,20 +6530,21 @@ async function sessionReviewHook(deps = {}) {
6430
6530
  editCount = parseInt(process.env.CYBEDEFEND_DRY_RUN_EDITS ?? "5", 10);
6431
6531
  }
6432
6532
  if (editCount === 0) return;
6433
- if (editCount < config.hooks.reviewThreshold) return;
6533
+ if (editCount < resolveReviewThreshold(config)) return;
6434
6534
  const userPrompt = extractFirstUserPrompt(transcript);
6535
+ const proposalMode = resolveProposalMode(config);
6435
6536
  let body = composeReviewBody(
6436
6537
  editCount,
6437
6538
  event.client,
6438
6539
  userPrompt,
6439
- config.hooks.autoProposeMode
6540
+ proposalMode
6440
6541
  );
6441
6542
  const withDiffScan = config.hooks.enableDiffScan !== false;
6442
6543
  if (withDiffScan) {
6443
6544
  const cwd = typeof stdin.cwd === "string" && stdin.cwd ? stdin.cwd : process.cwd();
6444
6545
  body += "\n" + composeDiffScanBody(extractChangedFiles(transcript, void 0, cwd));
6445
6546
  }
6446
- emitFn(body, event.client, composeStopReason(withDiffScan));
6547
+ emitFn(body, event.client, composeStopReason(withDiffScan, proposalMode));
6447
6548
  }
6448
6549
  function loadTranscript(stdin, readFile) {
6449
6550
  if (Array.isArray(stdin.transcript)) return stdin.transcript;
@@ -6605,24 +6706,16 @@ function extractFirstUserPrompt(transcript) {
6605
6706
  }
6606
6707
  return "";
6607
6708
  }
6608
- function composeReviewBody(editCount, client, userPrompt, autoPropose) {
6709
+ function composeReviewBody(editCount, client, userPrompt, proposalMode) {
6609
6710
  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") : [
6711
+ const proposalLine = proposalMode === "ask" ? [
6619
6712
  " Ask the user via AskUserQuestion:",
6620
6713
  ' > "Your prompt implied <concern>, but no business rule covers it.',
6621
6714
  ' > Propose it as a standing rule for the project?" \u2014 Yes / No / Edit.',
6622
6715
  ' ONLY on an explicit "yes": call `cybe_rules_report_missing(projectId,',
6623
6716
  ' { rule_text, evidence_files, rationale: "<quote user prompt>",',
6624
6717
  " confirmed_by_user: true })`."
6625
- ].join("\n");
6718
+ ].join("\n") : ` ${DIRECT_PROPOSE_DIRECTIVES[proposalMode]}`;
6626
6719
  return [
6627
6720
  `## \u{1F6E1}\uFE0F CybeDefend gap analysis (Stop hook \u2014 ${editCount} edits this session, client=${client})`,
6628
6721
  "",
@@ -6694,9 +6787,15 @@ function composeDiffScanBody(changedFiles) {
6694
6787
  ""
6695
6788
  ].join("\n");
6696
6789
  }
6697
- function composeStopReason(withDiffScan) {
6790
+ function composeStopReason(withDiffScan, proposalMode) {
6698
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" : "";
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 + ".";
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 + ".";
6700
6799
  }
6701
6800
 
6702
6801
  // src/hooks/runtime/pre-compact.ts
@@ -6717,6 +6816,11 @@ async function preCompactHook(deps = {}) {
6717
6816
  );
6718
6817
  return;
6719
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]}`];
6720
6824
  const body = [
6721
6825
  "## \u{1F6E1}\uFE0F CybeDefend PreCompact gap analysis",
6722
6826
  "",
@@ -6724,8 +6828,7 @@ async function preCompactHook(deps = {}) {
6724
6828
  "Before the fine-grained tool history is summarised away, run a gap analysis:",
6725
6829
  "1. Re-read the original user request (still uncompacted now).",
6726
6830
  "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.',
6831
+ ...proposeStep,
6729
6832
  ""
6730
6833
  ].join("\n");
6731
6834
  emitFn(body, event.client);
@@ -6802,7 +6905,13 @@ async function userPromptSubmitHook(deps = {}) {
6802
6905
  "",
6803
6906
  "### Step 0 \u2014 Proposal inbox"
6804
6907
  ];
6805
- 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) {
6806
6915
  lines.push(
6807
6916
  "- CybeDefend: could not fetch proposal-inbox status (token / API unreachable).",
6808
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.'
@@ -6828,7 +6937,7 @@ async function userPromptSubmitHook(deps = {}) {
6828
6937
  "- The PreToolUse hook fires automatically as a backstop on Edit/Write/MultiEdit, but your explicit call with a narrative intent gets better matches.",
6829
6938
  "",
6830
6939
  "### Order",
6831
- "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).",
6832
6941
  "2. Satisfy step 1 (fetch rules with a narrative intent) once you know which files you will touch.",
6833
6942
  "3. THEN proceed with whatever skill / brainstorm / implementation flow fits the task.",
6834
6943
  ""
@@ -159,6 +159,7 @@ export function installHookRuntime(opts, deps = {}) {
159
159
  enableSessionReview: opts.hooks.enableSessionReview,
160
160
  reviewThreshold: opts.hooks.reviewThreshold,
161
161
  autoProposeMode: opts.hooks.autoProposeMode,
162
+ nonBlockingMode: opts.hooks.nonBlockingMode,
162
163
  autoUpdate: useShim ? opts.hooks.autoUpdate : false,
163
164
  enableDiffScan: opts.hooks.enableDiffScan,
164
165
  },
@@ -1 +1 @@
1
- {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/hooks/install.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"install.js","sourceRoot":"","sources":["../../src/hooks/install.ts"],"names":[],"mappings":"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"}
@@ -10,6 +10,7 @@
10
10
  import { sniff } from './sniff.js';
11
11
  import { emit } from './emit.js';
12
12
  import { loadRuntimeConfig, readStdinJson } from './config.js';
13
+ import { DIRECT_PROPOSE_DIRECTIVES, resolveProposalMode, } from './proposal-mode.js';
13
14
  export async function preCompactHook(deps = {}) {
14
15
  const readStdin = deps.readStdin ?? readStdinJson;
15
16
  const loadConfig = deps.loadConfig ?? loadRuntimeConfig;
@@ -25,6 +26,13 @@ export async function preCompactHook(deps = {}) {
25
26
  emitFn(`## 🛡️ CybeDefend PreCompact (dry-run, client=${event.client})\n`, event.client);
26
27
  return;
27
28
  }
29
+ const proposalMode = resolveProposalMode(config);
30
+ const proposeStep = proposalMode === 'ask'
31
+ ? [
32
+ '3. For uncovered concerns, ask the user via AskUserQuestion whether to propose;',
33
+ ' on "yes", call `cybe_rules_report_missing` with confirmed_by_user: true.',
34
+ ]
35
+ : [`3. ${DIRECT_PROPOSE_DIRECTIVES[proposalMode]}`];
28
36
  const body = [
29
37
  '## 🛡️ CybeDefend PreCompact gap analysis',
30
38
  '',
@@ -32,8 +40,7 @@ export async function preCompactHook(deps = {}) {
32
40
  'Before the fine-grained tool history is summarised away, run a gap analysis:',
33
41
  '1. Re-read the original user request (still uncompacted now).',
34
42
  '2. List the CybeDefend rules returned during the session.',
35
- '3. For uncovered concerns, ask the user via AskUserQuestion whether to propose;',
36
- ' on "yes", call `cybe_rules_report_missing` with confirmed_by_user: true.',
43
+ ...proposeStep,
37
44
  '',
38
45
  ].join('\n');
39
46
  emitFn(body, event.client);
@@ -1 +1 @@
1
- {"version":3,"file":"pre-compact.js","sourceRoot":"","sources":["../../../src/hooks/runtime/pre-compact.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAQ/D,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAuB,EAAE;IAC5D,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,aAAa,CAAC;IAClD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,iBAAiB,CAAC;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC;IAEnC,MAAM,KAAK,GAAG,MAAM,SAAS,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IAE3B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB;QAAE,OAAO;IAE9C,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,GAAG,EAAE,CAAC;QAC3C,MAAM,CACJ,iDAAiD,KAAK,CAAC,MAAM,KAAK,EAClE,KAAK,CAAC,MAAM,CACb,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,IAAI,GAAG;QACX,2CAA2C;QAC3C,EAAE;QACF,uEAAuE;QACvE,8EAA8E;QAC9E,+DAA+D;QAC/D,2DAA2D;QAC3D,iFAAiF;QACjF,6EAA6E;QAC7E,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC"}
1
+ {"version":3,"file":"pre-compact.js","sourceRoot":"","sources":["../../../src/hooks/runtime/pre-compact.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAC/D,OAAO,EACL,yBAAyB,EACzB,mBAAmB,GACpB,MAAM,oBAAoB,CAAC;AAQ5B,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,OAAuB,EAAE;IAC5D,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,IAAI,aAAa,CAAC;IAClD,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,iBAAiB,CAAC;IACxD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC;IAEnC,MAAM,KAAK,GAAG,MAAM,SAAS,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;IAE3B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;IAC5B,IAAI,CAAC,MAAM;QAAE,OAAO;IACpB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,mBAAmB;QAAE,OAAO;IAE9C,IAAI,OAAO,CAAC,GAAG,CAAC,kBAAkB,KAAK,GAAG,EAAE,CAAC;QAC3C,MAAM,CACJ,iDAAiD,KAAK,CAAC,MAAM,KAAK,EAClE,KAAK,CAAC,MAAM,CACb,CAAC;QACF,OAAO;IACT,CAAC;IAED,MAAM,YAAY,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACjD,MAAM,WAAW,GACf,YAAY,KAAK,KAAK;QACpB,CAAC,CAAC;YACE,iFAAiF;YACjF,6EAA6E;SAC9E;QACH,CAAC,CAAC,CAAC,MAAM,yBAAyB,CAAC,YAAY,CAAC,EAAE,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG;QACX,2CAA2C;QAC3C,EAAE;QACF,uEAAuE;QACvE,8EAA8E;QAC9E,+DAA+D;QAC/D,2DAA2D;QAC3D,GAAG,WAAW;QACd,EAAE;KACH,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,MAAM,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC"}
@@ -0,0 +1,75 @@
1
+ const TRUTHY = new Set(['1', 'true', 'yes', 'on']);
2
+ const FALSY = new Set(['0', 'false', 'no', 'off']);
3
+ /**
4
+ * Boolean env override: `true` / `false` for a recognised value, `undefined`
5
+ * otherwise (unset, empty, typo) so the caller falls back to the config.
6
+ */
7
+ function envFlag(env, name) {
8
+ const raw = env[name]?.trim().toLowerCase();
9
+ if (raw && TRUTHY.has(raw))
10
+ return true;
11
+ if (raw && FALSY.has(raw))
12
+ return false;
13
+ return undefined;
14
+ }
15
+ /**
16
+ * Whether the hooks run in non-blocking mode.
17
+ *
18
+ * `CYBEDEFEND_NON_BLOCKING` wins over the installed config in both
19
+ * directions, so one machine can serve a human (interactive) and an
20
+ * autonomous agent (env var set on its process only). `undefined` in the
21
+ * config (written before this feature shipped) means OFF.
22
+ */
23
+ export function isNonBlockingMode(config, env = process.env) {
24
+ return (envFlag(env, 'CYBEDEFEND_NON_BLOCKING') ??
25
+ config.hooks.nonBlockingMode === true);
26
+ }
27
+ /**
28
+ * Resolve the proposal mode. Non-blocking supersedes auto-propose (it already
29
+ * proposes without asking, and skips the inbox review on top). Auto-propose
30
+ * follows `CYBEDEFEND_AUTO_PROPOSE` first, in both directions, then the
31
+ * installed `autoProposeMode`.
32
+ */
33
+ export function resolveProposalMode(config, env = process.env) {
34
+ if (isNonBlockingMode(config, env))
35
+ return 'non-blocking';
36
+ const auto = envFlag(env, 'CYBEDEFEND_AUTO_PROPOSE') ??
37
+ config.hooks.autoProposeMode === true;
38
+ return auto ? 'auto' : 'ask';
39
+ }
40
+ /**
41
+ * Step-0 override shared by the SessionStart and UserPromptSubmit hooks in
42
+ * non-blocking mode. The agent must not triage the inbox itself either:
43
+ * accepting or rejecting a proposal stays a human decision, taken in the
44
+ * dashboard.
45
+ */
46
+ export const NON_BLOCKING_INBOX_DIRECTIVE = 'Non-blocking mode is ON (autonomous agent — nobody is in the chat to answer). ' +
47
+ 'This OVERRIDES step 0 of the CybeDefend doctrine: do NOT call ' +
48
+ '`cybe_rules_list_proposals`, do NOT ask the user about proposals (no ' +
49
+ 'AskUserQuestion), and NEVER call `cybe_rules_accept_proposal` / ' +
50
+ '`cybe_rules_reject_proposal` yourself. Pending proposals stay in the inbox ' +
51
+ 'for a human to review in the CybeDefend dashboard. Proceed directly with the task.';
52
+ const REPORT_MISSING_CALL = '`cybe_rules_report_missing(projectId, { rule_text, evidence_files, ' +
53
+ 'rationale: "<quote user prompt>", confirmed_by_user: true })`';
54
+ /**
55
+ * Gap-analysis proposal step for the Stop and PreCompact hooks when the agent
56
+ * proposes without asking (the `ask` flow keeps each hook's own wording).
57
+ *
58
+ * `confirmed_by_user: true` is still passed: the gateway requires it for
59
+ * agent-sourced proposals, and here it stands for the operator's opt-in to
60
+ * that mode. The human gate moves to the inbox review (next session start for
61
+ * `auto`, the dashboard for `non-blocking`).
62
+ */
63
+ export const DIRECT_PROPOSE_DIRECTIVES = {
64
+ auto: '**Auto-propose mode is ON** — this OVERRIDES the doctrine\'s "ask first" ' +
65
+ 'step: do NOT ask the user (no AskUserQuestion). If — and ONLY if — one ' +
66
+ `durable, reusable gap stands out, call ${REPORT_MISSING_CALL} directly for ` +
67
+ 'that single rule. The user validates it at the next session start via the ' +
68
+ 'Accept/Reject picker. Otherwise propose nothing.',
69
+ 'non-blocking': '**Non-blocking mode is ON** (autonomous agent — nobody is in the chat to ' +
70
+ 'answer). Do NOT ask the user (no AskUserQuestion). If — and ONLY if — one ' +
71
+ `durable, reusable gap stands out, call ${REPORT_MISSING_CALL} directly for ` +
72
+ 'that single rule. It lands as a pending proposal that a human accepts or ' +
73
+ 'rejects in the CybeDefend dashboard. Otherwise propose nothing.',
74
+ };
75
+ //# sourceMappingURL=proposal-mode.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"proposal-mode.js","sourceRoot":"","sources":["../../../src/hooks/runtime/proposal-mode.ts"],"names":[],"mappings":"AAoBA,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,CAAC,CAAC;AACnD,MAAM,KAAK,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;AAEnD;;;GAGG;AACH,SAAS,OAAO,CAAC,GAAsB,EAAE,IAAY;IACnD,MAAM,GAAG,GAAG,GAAG,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;IAC5C,IAAI,GAAG,IAAI,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,IAAI,GAAG,IAAI,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACxC,OAAO,SAAS,CAAC;AACnB,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAqB,EACrB,MAAyB,OAAO,CAAC,GAAG;IAEpC,OAAO,CACL,OAAO,CAAC,GAAG,EAAE,yBAAyB,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,eAAe,KAAK,IAAI,CACtC,CAAC;AACJ,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,mBAAmB,CACjC,MAAqB,EACrB,MAAyB,OAAO,CAAC,GAAG;IAEpC,IAAI,iBAAiB,CAAC,MAAM,EAAE,GAAG,CAAC;QAAE,OAAO,cAAc,CAAC;IAC1D,MAAM,IAAI,GACR,OAAO,CAAC,GAAG,EAAE,yBAAyB,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,eAAe,KAAK,IAAI,CAAC;IACxC,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC;AAC/B,CAAC;AAED;;;;;GAKG;AACH,MAAM,CAAC,MAAM,4BAA4B,GACvC,gFAAgF;IAChF,gEAAgE;IAChE,uEAAuE;IACvE,kEAAkE;IAClE,6EAA6E;IAC7E,oFAAoF,CAAC;AAEvF,MAAM,mBAAmB,GACvB,qEAAqE;IACrE,+DAA+D,CAAC;AAElE;;;;;;;;GAQG;AACH,MAAM,CAAC,MAAM,yBAAyB,GAGlC;IACF,IAAI,EACF,2EAA2E;QAC3E,yEAAyE;QACzE,0CAA0C,mBAAmB,gBAAgB;QAC7E,4EAA4E;QAC5E,kDAAkD;IACpD,cAAc,EACZ,2EAA2E;QAC3E,4EAA4E;QAC5E,0CAA0C,mBAAmB,gBAAgB;QAC7E,2EAA2E;QAC3E,iEAAiE;CACpE,CAAC"}