@cybedefend/vibedefend 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3654,10 +3654,10 @@ var require_re2 = __commonJS({
3654
3654
  } });
3655
3655
  var calledRun;
3656
3656
  dependenciesFulfilled = function runCaller() {
3657
- if (!calledRun) run();
3657
+ if (!calledRun) run3();
3658
3658
  if (!calledRun) dependenciesFulfilled = runCaller;
3659
3659
  };
3660
- function run(args) {
3660
+ function run3(args) {
3661
3661
  args = args || arguments_;
3662
3662
  if (runDependencies > 0) {
3663
3663
  return;
@@ -3690,7 +3690,7 @@ var require_re2 = __commonJS({
3690
3690
  }
3691
3691
  checkStackCookie();
3692
3692
  }
3693
- Module["run"] = run;
3693
+ Module["run"] = run3;
3694
3694
  if (Module["preInit"]) {
3695
3695
  if (typeof Module["preInit"] == "function") Module["preInit"] = [Module["preInit"]];
3696
3696
  while (Module["preInit"].length > 0) {
@@ -3698,7 +3698,7 @@ var require_re2 = __commonJS({
3698
3698
  }
3699
3699
  }
3700
3700
  noExitRuntime = true;
3701
- run();
3701
+ run3();
3702
3702
  }
3703
3703
  });
3704
3704
 
@@ -5135,6 +5135,323 @@ function composeSecurityBody(markdown, total, filePath) {
5135
5135
  ].join("\n");
5136
5136
  }
5137
5137
 
5138
+ // src/hooks/runtime/self-update-check.ts
5139
+ import { spawn } from "node:child_process";
5140
+ import { existsSync as existsSync6, readFileSync as readFileSync5, rmSync, writeFileSync as writeFileSync3 } from "node:fs";
5141
+ import { homedir as homedir3 } from "node:os";
5142
+ import { join as join4 } from "node:path";
5143
+
5144
+ // src/utils.ts
5145
+ import { existsSync as existsSync4, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
5146
+ import { dirname, join as join3 } from "node:path";
5147
+
5148
+ // node_modules/.pnpm/kleur@4.1.5/node_modules/kleur/index.mjs
5149
+ var FORCE_COLOR;
5150
+ var NODE_DISABLE_COLORS;
5151
+ var NO_COLOR;
5152
+ var TERM;
5153
+ var isTTY = true;
5154
+ if (typeof process !== "undefined") {
5155
+ ({ FORCE_COLOR, NODE_DISABLE_COLORS, NO_COLOR, TERM } = process.env || {});
5156
+ isTTY = process.stdout && process.stdout.isTTY;
5157
+ }
5158
+ var $ = {
5159
+ enabled: !NODE_DISABLE_COLORS && NO_COLOR == null && TERM !== "dumb" && (FORCE_COLOR != null && FORCE_COLOR !== "0" || isTTY),
5160
+ // modifiers
5161
+ reset: init(0, 0),
5162
+ bold: init(1, 22),
5163
+ dim: init(2, 22),
5164
+ italic: init(3, 23),
5165
+ underline: init(4, 24),
5166
+ inverse: init(7, 27),
5167
+ hidden: init(8, 28),
5168
+ strikethrough: init(9, 29),
5169
+ // colors
5170
+ black: init(30, 39),
5171
+ red: init(31, 39),
5172
+ green: init(32, 39),
5173
+ yellow: init(33, 39),
5174
+ blue: init(34, 39),
5175
+ magenta: init(35, 39),
5176
+ cyan: init(36, 39),
5177
+ white: init(37, 39),
5178
+ gray: init(90, 39),
5179
+ grey: init(90, 39),
5180
+ // background colors
5181
+ bgBlack: init(40, 49),
5182
+ bgRed: init(41, 49),
5183
+ bgGreen: init(42, 49),
5184
+ bgYellow: init(43, 49),
5185
+ bgBlue: init(44, 49),
5186
+ bgMagenta: init(45, 49),
5187
+ bgCyan: init(46, 49),
5188
+ bgWhite: init(47, 49)
5189
+ };
5190
+ function run(arr, str) {
5191
+ let i = 0, tmp, beg = "", end = "";
5192
+ for (; i < arr.length; i++) {
5193
+ tmp = arr[i];
5194
+ beg += tmp.open;
5195
+ end += tmp.close;
5196
+ if (!!~str.indexOf(tmp.close)) {
5197
+ str = str.replace(tmp.rgx, tmp.close + tmp.open);
5198
+ }
5199
+ }
5200
+ return beg + str + end;
5201
+ }
5202
+ function chain(has, keys) {
5203
+ let ctx = { has, keys };
5204
+ ctx.reset = $.reset.bind(ctx);
5205
+ ctx.bold = $.bold.bind(ctx);
5206
+ ctx.dim = $.dim.bind(ctx);
5207
+ ctx.italic = $.italic.bind(ctx);
5208
+ ctx.underline = $.underline.bind(ctx);
5209
+ ctx.inverse = $.inverse.bind(ctx);
5210
+ ctx.hidden = $.hidden.bind(ctx);
5211
+ ctx.strikethrough = $.strikethrough.bind(ctx);
5212
+ ctx.black = $.black.bind(ctx);
5213
+ ctx.red = $.red.bind(ctx);
5214
+ ctx.green = $.green.bind(ctx);
5215
+ ctx.yellow = $.yellow.bind(ctx);
5216
+ ctx.blue = $.blue.bind(ctx);
5217
+ ctx.magenta = $.magenta.bind(ctx);
5218
+ ctx.cyan = $.cyan.bind(ctx);
5219
+ ctx.white = $.white.bind(ctx);
5220
+ ctx.gray = $.gray.bind(ctx);
5221
+ ctx.grey = $.grey.bind(ctx);
5222
+ ctx.bgBlack = $.bgBlack.bind(ctx);
5223
+ ctx.bgRed = $.bgRed.bind(ctx);
5224
+ ctx.bgGreen = $.bgGreen.bind(ctx);
5225
+ ctx.bgYellow = $.bgYellow.bind(ctx);
5226
+ ctx.bgBlue = $.bgBlue.bind(ctx);
5227
+ ctx.bgMagenta = $.bgMagenta.bind(ctx);
5228
+ ctx.bgCyan = $.bgCyan.bind(ctx);
5229
+ ctx.bgWhite = $.bgWhite.bind(ctx);
5230
+ return ctx;
5231
+ }
5232
+ function init(open, close) {
5233
+ let blk = {
5234
+ open: `\x1B[${open}m`,
5235
+ close: `\x1B[${close}m`,
5236
+ rgx: new RegExp(`\\x1b\\[${close}m`, "g")
5237
+ };
5238
+ return function(txt) {
5239
+ if (this !== void 0 && this.has !== void 0) {
5240
+ !!~this.has.indexOf(open) || (this.has.push(open), this.keys.push(blk));
5241
+ return txt === void 0 ? this : $.enabled ? run(this.keys, txt + "") : txt + "";
5242
+ }
5243
+ return txt === void 0 ? chain([open], [blk]) : $.enabled ? run([blk], txt + "") : txt + "";
5244
+ };
5245
+ }
5246
+
5247
+ // src/utils.ts
5248
+ function ensureDirFor(filePath) {
5249
+ const dir = dirname(filePath);
5250
+ if (!existsSync4(dir)) mkdirSync2(dir, { recursive: true });
5251
+ }
5252
+
5253
+ // src/hooks/runtime/semver.ts
5254
+ function parseVersion(input) {
5255
+ const cleaned = input.trim().replace(/^v/i, "");
5256
+ const core = cleaned.split("-")[0].split("+")[0];
5257
+ const parts = core.split(".");
5258
+ const seg = (i) => {
5259
+ const v = Number.parseInt(parts[i] ?? "0", 10);
5260
+ return Number.isFinite(v) && v >= 0 ? v : 0;
5261
+ };
5262
+ return [seg(0), seg(1), seg(2)];
5263
+ }
5264
+ function compareVersions(a, b) {
5265
+ const pa = parseVersion(a);
5266
+ const pb = parseVersion(b);
5267
+ for (let i = 0; i < 3; i++) {
5268
+ if (pa[i] < pb[i]) return -1;
5269
+ if (pa[i] > pb[i]) return 1;
5270
+ }
5271
+ return 0;
5272
+ }
5273
+ function isNewer(latest, current) {
5274
+ return compareVersions(latest, current) > 0;
5275
+ }
5276
+
5277
+ // src/self-update.ts
5278
+ import { fileURLToPath } from "node:url";
5279
+ import { existsSync as existsSync5 } from "node:fs";
5280
+ var GLOBAL_MODES = /* @__PURE__ */ new Set([
5281
+ "npm-global",
5282
+ "pnpm-global",
5283
+ "yarn-global"
5284
+ ]);
5285
+ function detectInstallMode(scriptPath) {
5286
+ if (/[\\/]_npx[\\/]/i.test(scriptPath)) return "npx-cache";
5287
+ if (/[\\/]pnpm(?:[\\/]global|[\\/]nodejs|[\\/]node_modules)/i.test(scriptPath)) {
5288
+ return "pnpm-global";
5289
+ }
5290
+ if (/[\\/]\.pnpm-global[\\/]/i.test(scriptPath)) return "pnpm-global";
5291
+ if (/[\\/]yarn[\\/](?:[^\\/]+[\\/])?global[\\/]/i.test(scriptPath)) {
5292
+ return "yarn-global";
5293
+ }
5294
+ if (/[\\/]lib[\\/]node_modules[\\/]/i.test(scriptPath)) return "npm-global";
5295
+ if (/[\\/](?:npm|nodejs)[\\/]node_modules[\\/]/i.test(scriptPath)) {
5296
+ return "npm-global";
5297
+ }
5298
+ if (/[\\/]nvm[\\/][^\\/]+[\\/]node_modules[\\/]/i.test(scriptPath)) {
5299
+ return "npm-global";
5300
+ }
5301
+ if (/[\\/]node_modules[\\/]/i.test(scriptPath)) return "project-local";
5302
+ return "unknown";
5303
+ }
5304
+ function resolveScriptPath() {
5305
+ const fromArgv = process.argv[1];
5306
+ if (fromArgv && existsSync5(fromArgv)) return fromArgv;
5307
+ return fileURLToPath(import.meta.url);
5308
+ }
5309
+
5310
+ // src/hooks/runtime/self-update-check.ts
5311
+ var PACKAGE_NAME = "@cybedefend/vibedefend";
5312
+ var REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
5313
+ var THROTTLE_MS = 24 * 60 * 60 * 1e3;
5314
+ var FETCH_TIMEOUT_MS = 3e3;
5315
+ async function selfUpdateCheck(deps) {
5316
+ const now = deps.now ?? (() => Date.now());
5317
+ const dir = deps.cybeDir ?? join4(process.env.CYBEDEFEND_HOME ?? homedir3(), ".cybedefend");
5318
+ const throttlePath = join4(dir, "update-check.json");
5319
+ const logPath = join4(dir, "last-update.log");
5320
+ const installMode = deps.installMode ?? (deps.runnerSource ? detectInstallMode(deps.runnerSource) : detectInstallMode(resolveScriptPath()));
5321
+ const priorFailure = takeUpdateLogNotice(logPath);
5322
+ const throttle = readThrottle(throttlePath);
5323
+ if (throttle && now() - throttle.lastCheckedAt < THROTTLE_MS) {
5324
+ return priorFailure;
5325
+ }
5326
+ const fetchLatest = deps.fetchLatest ?? defaultFetchLatest;
5327
+ const latest = await fetchLatest();
5328
+ writeThrottle(throttlePath, { lastCheckedAt: now() });
5329
+ if (!latest || !isNewer(latest, deps.installedVersion)) {
5330
+ return priorFailure;
5331
+ }
5332
+ const updateNotice = applyOrNotify(deps, installMode, latest, logPath);
5333
+ return [priorFailure, updateNotice].filter(Boolean).join("\n") || null;
5334
+ }
5335
+ function applyOrNotify(deps, installMode, latest, logPath) {
5336
+ const from = deps.installedVersion;
5337
+ if (!deps.autoUpdate || !GLOBAL_MODES.has(installMode)) {
5338
+ return `\u{1F6E1}\uFE0F vibedefend ${from} \u2192 ${latest} available \u2014 run \`vibedefend update --self\` to upgrade.`;
5339
+ }
5340
+ const spawnUpdate = deps.spawnUpdate ?? defaultSpawnUpdate;
5341
+ spawnUpdate({
5342
+ mode: installMode,
5343
+ spec: `${PACKAGE_NAME}@${latest}`,
5344
+ logPath,
5345
+ from,
5346
+ to: latest
5347
+ });
5348
+ return `\u{1F6E1}\uFE0F vibedefend: updating in background ${from} \u2192 ${latest} (active next session).`;
5349
+ }
5350
+ function readThrottle(path) {
5351
+ try {
5352
+ if (!existsSync6(path)) return null;
5353
+ const data = JSON.parse(readFileSync5(path, "utf8"));
5354
+ if (typeof data.lastCheckedAt !== "number") return null;
5355
+ return { lastCheckedAt: data.lastCheckedAt };
5356
+ } catch {
5357
+ return null;
5358
+ }
5359
+ }
5360
+ function writeThrottle(path, throttle) {
5361
+ try {
5362
+ ensureDirFor(path);
5363
+ writeFileSync3(path, JSON.stringify(throttle) + "\n");
5364
+ } catch {
5365
+ }
5366
+ }
5367
+ function takeUpdateLogNotice(logPath) {
5368
+ try {
5369
+ if (!existsSync6(logPath)) return null;
5370
+ let parsed = {};
5371
+ try {
5372
+ parsed = JSON.parse(readFileSync5(logPath, "utf8"));
5373
+ } catch {
5374
+ parsed = {};
5375
+ }
5376
+ rmSync(logPath, { force: true });
5377
+ if (typeof parsed.exitCode === "number" && parsed.exitCode !== 0) {
5378
+ return `\u26A0 vibedefend: last auto-update to ${parsed.to ?? "latest"} failed (exit ${parsed.exitCode}) \u2014 run \`vibedefend update --self\`.`;
5379
+ }
5380
+ return null;
5381
+ } catch {
5382
+ return null;
5383
+ }
5384
+ }
5385
+ async function defaultFetchLatest() {
5386
+ try {
5387
+ const res = await fetch(REGISTRY_URL, {
5388
+ signal: AbortSignal.timeout(FETCH_TIMEOUT_MS)
5389
+ });
5390
+ if (!res.ok) return null;
5391
+ const body = await res.json();
5392
+ return typeof body.version === "string" ? body.version : null;
5393
+ } catch {
5394
+ return null;
5395
+ }
5396
+ }
5397
+ function buildUpdateWrapperScript(args) {
5398
+ return `
5399
+ const { spawnSync } = require('node:child_process');
5400
+ const { writeFileSync } = require('node:fs');
5401
+ const r = spawnSync(${JSON.stringify(args.pm)}, ${JSON.stringify(args.pmArgs)}, {
5402
+ stdio: 'ignore',
5403
+ shell: process.platform === 'win32',
5404
+ });
5405
+ try {
5406
+ writeFileSync(${JSON.stringify(args.logPath)}, JSON.stringify({
5407
+ from: ${JSON.stringify(args.from)},
5408
+ to: ${JSON.stringify(args.to)},
5409
+ exitCode: r.status == null ? 1 : r.status,
5410
+ finishedAt: Date.now(),
5411
+ }) + "\\n");
5412
+ } catch {}
5413
+ `;
5414
+ }
5415
+ function defaultSpawnUpdate(args) {
5416
+ const { pm, pmArgs } = pmCommand(args.mode, args.spec);
5417
+ const script = buildUpdateWrapperScript({
5418
+ pm,
5419
+ pmArgs,
5420
+ logPath: args.logPath,
5421
+ from: args.from,
5422
+ to: args.to
5423
+ });
5424
+ try {
5425
+ const child = spawn(process.execPath, ["-e", script], {
5426
+ detached: true,
5427
+ stdio: "ignore",
5428
+ windowsHide: true
5429
+ });
5430
+ child.unref();
5431
+ } catch {
5432
+ }
5433
+ }
5434
+ function pmCommand(mode, spec) {
5435
+ switch (mode) {
5436
+ case "pnpm-global":
5437
+ return { pm: "pnpm", pmArgs: ["add", "-g", spec] };
5438
+ case "yarn-global":
5439
+ return { pm: "yarn", pmArgs: ["global", "add", spec] };
5440
+ default:
5441
+ return { pm: "npm", pmArgs: ["install", "-g", spec] };
5442
+ }
5443
+ }
5444
+
5445
+ // src/version.ts
5446
+ import { readFileSync as readFileSync6 } from "node:fs";
5447
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
5448
+ import { dirname as dirname2, join as join5 } from "node:path";
5449
+ var here = dirname2(fileURLToPath2(import.meta.url));
5450
+ var pkgPath = join5(here, "..", "package.json");
5451
+ var pkg = JSON.parse(
5452
+ readFileSync6(pkgPath, "utf8")
5453
+ );
5454
+
5138
5455
  // src/hooks/runtime/session-start.ts
5139
5456
  async function sessionStartHook(deps = {}) {
5140
5457
  const readStdin = deps.readStdin ?? readStdinJson;
@@ -5142,6 +5459,7 @@ async function sessionStartHook(deps = {}) {
5142
5459
  const resolveProject = deps.resolveProject ?? resolveProjectId;
5143
5460
  const resolveTok = deps.resolveTokenFn ?? resolveToken;
5144
5461
  const fetchProposals = deps.fetchProposalsFn ?? fetchProposalsCount;
5462
+ const selfUpdateCheckFn = deps.selfUpdateCheckFn ?? selfUpdateCheck;
5145
5463
  const emitFn = deps.emitFn ?? emitContext;
5146
5464
  const stdin = await readStdin();
5147
5465
  const event = sniff(stdin);
@@ -5213,11 +5531,27 @@ async function sessionStartHook(deps = {}) {
5213
5531
  " and respect a deny verdict."
5214
5532
  );
5215
5533
  }
5534
+ try {
5535
+ const notice = await selfUpdateCheckFn({
5536
+ // The version actually loaded right now — NOT config.installedVersion,
5537
+ // which is the install-time stamp and goes stale after a background
5538
+ // update (causing perpetual re-spawn / re-notify). pkg.version tracks
5539
+ // the running bundle, so a completed update self-resolves to a no-op.
5540
+ installedVersion: pkg.version,
5541
+ autoUpdate: config.hooks.autoUpdate,
5542
+ // Stable global package path so the check derives the REAL install mode
5543
+ // (the runtime script path is the shim → 'unknown'). Undefined on
5544
+ // non-global installs (which fall back to notify-only).
5545
+ runnerSource: config.runnerSource
5546
+ });
5547
+ if (notice) lines.push("", notice);
5548
+ } catch {
5549
+ }
5216
5550
  emitFn(lines.join("\n") + "\n", event.client);
5217
5551
  }
5218
5552
 
5219
5553
  // src/hooks/runtime/session-review.ts
5220
- import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
5554
+ import { existsSync as existsSync7, readFileSync as readFileSync7 } from "node:fs";
5221
5555
  var COUNTED_TOOLS = /* @__PURE__ */ new Set([
5222
5556
  "Edit",
5223
5557
  "Write",
@@ -5278,42 +5612,87 @@ function loadTranscript(stdin, readFile) {
5278
5612
  return out;
5279
5613
  }
5280
5614
  function defaultReadTranscript(path) {
5281
- if (!existsSync4(path)) return null;
5615
+ if (!existsSync7(path)) return null;
5282
5616
  try {
5283
- return readFileSync4(path, "utf8");
5617
+ return readFileSync7(path, "utf8");
5284
5618
  } catch {
5285
5619
  return null;
5286
5620
  }
5287
5621
  }
5622
+ function toolNameOf(obj) {
5623
+ return typeof obj.name === "string" && obj.name || typeof obj.tool_name === "string" && obj.tool_name || typeof obj.toolName === "string" && obj.toolName || "";
5624
+ }
5625
+ function countToolUsesDeep(node, depth) {
5626
+ if (depth > 8 || node === null || typeof node !== "object") return 0;
5627
+ if (Array.isArray(node)) {
5628
+ let n2 = 0;
5629
+ for (const item of node) n2 += countToolUsesDeep(item, depth + 1);
5630
+ return n2;
5631
+ }
5632
+ const obj = node;
5633
+ if (COUNTED_TOOLS.has(toolNameOf(obj))) return 1;
5634
+ let n = 0;
5635
+ for (const value of Object.values(obj)) {
5636
+ if (value && typeof value === "object") {
5637
+ n += countToolUsesDeep(value, depth + 1);
5638
+ }
5639
+ }
5640
+ return n;
5641
+ }
5288
5642
  function countEdits(transcript) {
5289
5643
  let n = 0;
5290
5644
  for (const event of transcript) {
5291
- if (!event || typeof event !== "object") continue;
5292
- const rec = event;
5293
- const candidates = [];
5294
- for (const key of ["tool_uses", "toolCalls", "tools", "message"]) {
5295
- const v = rec[key];
5296
- if (Array.isArray(v)) candidates.push(...v);
5297
- else if (v && typeof v === "object") candidates.push(v);
5298
- }
5299
- candidates.push(rec);
5300
- for (const c of candidates) {
5301
- if (!c || typeof c !== "object") continue;
5302
- const obj = c;
5303
- const name = typeof obj.name === "string" && obj.name || typeof obj.tool_name === "string" && obj.tool_name || typeof obj.toolName === "string" && obj.toolName || "";
5304
- if (COUNTED_TOOLS.has(name)) n += 1;
5305
- }
5645
+ n += countToolUsesDeep(event, 0);
5306
5646
  }
5307
5647
  return n;
5308
5648
  }
5649
+ var COMMAND_WRAPPER_PREFIXES = [
5650
+ "<local-command-",
5651
+ "<command-name>",
5652
+ "<command-message>",
5653
+ "<command-args>",
5654
+ "<command-stdout>",
5655
+ "<bash-input>",
5656
+ "<bash-stdout>",
5657
+ "<bash-stderr>"
5658
+ ];
5659
+ function isCommandWrapper(text) {
5660
+ const t = text.trimStart();
5661
+ return COMMAND_WRAPPER_PREFIXES.some((p) => t.startsWith(p));
5662
+ }
5663
+ function userText(rec) {
5664
+ if (typeof rec.content === "string" && rec.content) return rec.content;
5665
+ if (typeof rec.text === "string" && rec.text) return rec.text;
5666
+ if (typeof rec.message === "string" && rec.message) return rec.message;
5667
+ const msg = rec.message;
5668
+ if (msg && typeof msg === "object") {
5669
+ const content = msg.content;
5670
+ if (typeof content === "string" && content) return content;
5671
+ if (Array.isArray(content)) {
5672
+ const parts = [];
5673
+ for (const block of content) {
5674
+ if (!block || typeof block !== "object") continue;
5675
+ const b = block;
5676
+ if (typeof b.text === "string" && (b.type === "text" || b.type === void 0)) {
5677
+ parts.push(b.text);
5678
+ }
5679
+ }
5680
+ const joined = parts.join("\n").trim();
5681
+ if (joined) return joined;
5682
+ }
5683
+ }
5684
+ return "";
5685
+ }
5309
5686
  function extractFirstUserPrompt(transcript) {
5310
5687
  for (const event of transcript) {
5311
5688
  if (!event || typeof event !== "object") continue;
5312
5689
  const rec = event;
5690
+ if (rec.isSidechain === true || rec.isMeta === true) continue;
5313
5691
  const isUser = rec.role === "user" || rec.type === "user" || rec.from === "user";
5314
5692
  if (!isUser) continue;
5315
- const text = typeof rec.content === "string" && rec.content || typeof rec.text === "string" && rec.text || typeof rec.message === "string" && rec.message || "";
5316
- if (text) return text.slice(0, 600);
5693
+ const text = userText(rec);
5694
+ if (!text || isCommandWrapper(text)) continue;
5695
+ return text.slice(0, 600);
5317
5696
  }
5318
5697
  return "";
5319
5698
  }
@@ -5323,8 +5702,8 @@ function composeReviewBody(editCount, client, userPrompt, autoPropose) {
5323
5702
  " **Auto-propose mode is ON** (configured at install). Without asking the user,",
5324
5703
  " call `cybe_rules_report_missing(projectId, { rule_text, evidence_files,",
5325
5704
  ' rationale: "<quote user prompt>", confirmed_by_user: true })` for each',
5326
- " uncovered concern. The user will validate them at the next session start",
5327
- " via the Accept/Reject picker."
5705
+ " selected concern (1\u20132 max). The user will validate them at the next",
5706
+ " session start via the Accept/Reject picker."
5328
5707
  ].join("\n") : [
5329
5708
  " Ask the user via AskUserQuestion:",
5330
5709
  ' > "Your prompt mentioned <concern>, but no business rule covers it.',
@@ -5345,9 +5724,13 @@ function composeReviewBody(editCount, client, userPrompt, autoPropose) {
5345
5724
  "### Required next-turn action",
5346
5725
  "1. Extract security/business concerns the prompt mentions.",
5347
5726
  "2. List CybeDefend rules returned during the session.",
5348
- "3. For each concern WITHOUT a matching rule:",
5727
+ "3. Identify concerns WITHOUT a matching rule, then select **AT MOST the",
5728
+ " 1\u20132 most important** (prefer security-critical / data-handling). Ignore",
5729
+ " the rest \u2014 over-proposing causes proposal fatigue and users end up",
5730
+ " rejecting everything. Propose nothing if no gap is worth a standing rule.",
5731
+ "4. For each selected concern (1\u20132 max):",
5349
5732
  proposalLine,
5350
- "4. If every concern is covered, say so:",
5733
+ "5. If every concern is covered (or none warrants a rule), say so:",
5351
5734
  ' "\u{1F6E1}\uFE0F CybeDefend gap analysis: every concern in your request was covered',
5352
5735
  ' by an existing rule. No new proposals."',
5353
5736
  ""
@@ -5387,19 +5770,19 @@ async function preCompactHook(deps = {}) {
5387
5770
  }
5388
5771
 
5389
5772
  // src/hooks/runtime/user-prompt-submit.ts
5390
- import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync2 } from "node:fs";
5391
- import { homedir as homedir3 } from "node:os";
5392
- import { join as join3 } from "node:path";
5773
+ import { existsSync as existsSync8, mkdirSync as mkdirSync3, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "node:fs";
5774
+ import { homedir as homedir4 } from "node:os";
5775
+ import { join as join6 } from "node:path";
5393
5776
  var SESSIONS_CAP = 100;
5394
5777
  function defaultSessionsFile() {
5395
- const dir = process.env.CYBEDEFEND_BASELINE_DIR ?? join3(homedir3(), ".cybedefend");
5396
- return join3(dir, "user-prompt-seen-sessions.json");
5778
+ const dir = process.env.CYBEDEFEND_BASELINE_DIR ?? join6(homedir4(), ".cybedefend");
5779
+ return join6(dir, "user-prompt-seen-sessions.json");
5397
5780
  }
5398
5781
  function defaultSessionTracker(file = defaultSessionsFile()) {
5399
5782
  function load() {
5400
- if (!existsSync5(file)) return [];
5783
+ if (!existsSync8(file)) return [];
5401
5784
  try {
5402
- const raw = readFileSync5(file, "utf8");
5785
+ const raw = readFileSync8(file, "utf8");
5403
5786
  const data = JSON.parse(raw);
5404
5787
  if (!Array.isArray(data.sessions)) return [];
5405
5788
  return data.sessions.filter((s) => typeof s === "string");
@@ -5416,8 +5799,8 @@ function defaultSessionTracker(file = defaultSessionsFile()) {
5416
5799
  if (existing.includes(sessionId)) return;
5417
5800
  const next = [...existing, sessionId].slice(-SESSIONS_CAP);
5418
5801
  try {
5419
- mkdirSync2(join3(file, ".."), { recursive: true });
5420
- writeFileSync2(file, JSON.stringify({ sessions: next }), "utf8");
5802
+ mkdirSync3(join6(file, ".."), { recursive: true });
5803
+ writeFileSync4(file, JSON.stringify({ sessions: next }), "utf8");
5421
5804
  } catch {
5422
5805
  }
5423
5806
  }
@@ -6037,12 +6420,12 @@ function hashTarget(text) {
6037
6420
  }
6038
6421
 
6039
6422
  // src/hooks/runtime/guard-rules-cache.ts
6040
- import { readFileSync as readFileSync6, writeFileSync as writeFileSync3, existsSync as existsSync6, mkdirSync as mkdirSync3 } from "node:fs";
6041
- import { dirname } from "node:path";
6042
- import { homedir as homedir4 } from "node:os";
6043
- import { join as join4 } from "node:path";
6044
- var DEFAULT_CACHE_FILE = join4(
6045
- homedir4(),
6423
+ import { readFileSync as readFileSync9, writeFileSync as writeFileSync5, existsSync as existsSync9, mkdirSync as mkdirSync4 } from "node:fs";
6424
+ import { dirname as dirname3 } from "node:path";
6425
+ import { homedir as homedir5 } from "node:os";
6426
+ import { join as join7 } from "node:path";
6427
+ var DEFAULT_CACHE_FILE = join7(
6428
+ homedir5(),
6046
6429
  ".cybedefend",
6047
6430
  "guards-cache.json"
6048
6431
  );
@@ -6139,9 +6522,9 @@ function makeGuardRulesCache(opts) {
6139
6522
  }
6140
6523
  }
6141
6524
  function readCache(projectId) {
6142
- if (!existsSync6(cacheFile)) return null;
6525
+ if (!existsSync9(cacheFile)) return null;
6143
6526
  try {
6144
- const raw = readFileSync6(cacheFile, "utf8");
6527
+ const raw = readFileSync9(cacheFile, "utf8");
6145
6528
  const data = JSON.parse(raw);
6146
6529
  if (data.projectId !== projectId) return null;
6147
6530
  return data;
@@ -6151,17 +6534,17 @@ function makeGuardRulesCache(opts) {
6151
6534
  }
6152
6535
  function writeCache(entry) {
6153
6536
  try {
6154
- ensureDirFor(cacheFile);
6155
- writeFileSync3(cacheFile, JSON.stringify(entry), "utf8");
6537
+ ensureDirFor2(cacheFile);
6538
+ writeFileSync5(cacheFile, JSON.stringify(entry), "utf8");
6156
6539
  } catch {
6157
6540
  }
6158
6541
  }
6159
6542
  return { refreshIfStale };
6160
6543
  }
6161
- function ensureDirFor(filePath) {
6162
- const dir = dirname(filePath);
6163
- if (!existsSync6(dir)) {
6164
- mkdirSync3(dir, { recursive: true });
6544
+ function ensureDirFor2(filePath) {
6545
+ const dir = dirname3(filePath);
6546
+ if (!existsSync9(dir)) {
6547
+ mkdirSync4(dir, { recursive: true });
6165
6548
  }
6166
6549
  }
6167
6550
  var DEFAULT_TIMEOUT_MS2 = 8e3;
@@ -6196,16 +6579,16 @@ async function fetchEffectiveRulesFromGateway(ctx, opts = {}) {
6196
6579
  // src/hooks/runtime/guard-violations-buffer.ts
6197
6580
  import {
6198
6581
  appendFileSync,
6199
- existsSync as existsSync7,
6200
- readFileSync as readFileSync7,
6201
- writeFileSync as writeFileSync4,
6202
- mkdirSync as mkdirSync4
6582
+ existsSync as existsSync10,
6583
+ readFileSync as readFileSync10,
6584
+ writeFileSync as writeFileSync6,
6585
+ mkdirSync as mkdirSync5
6203
6586
  } from "node:fs";
6204
- import { dirname as dirname2 } from "node:path";
6205
- import { homedir as homedir5 } from "node:os";
6206
- import { join as join5 } from "node:path";
6207
- var DEFAULT_BUFFER_FILE = join5(
6208
- homedir5(),
6587
+ import { dirname as dirname4 } from "node:path";
6588
+ import { homedir as homedir6 } from "node:os";
6589
+ import { join as join8 } from "node:path";
6590
+ var DEFAULT_BUFFER_FILE = join8(
6591
+ homedir6(),
6209
6592
  ".cybedefend",
6210
6593
  "guards-violations-buffer.jsonl"
6211
6594
  );
@@ -6218,16 +6601,16 @@ function makeViolationsBuffer(opts = {}) {
6218
6601
  timestamp: (/* @__PURE__ */ new Date()).toISOString()
6219
6602
  };
6220
6603
  try {
6221
- ensureDirFor2(bufferFile);
6604
+ ensureDirFor3(bufferFile);
6222
6605
  appendFileSync(bufferFile, JSON.stringify(record) + "\n", "utf8");
6223
6606
  } catch {
6224
6607
  }
6225
6608
  }
6226
6609
  async function flushViolations2(ctx, postFn) {
6227
- if (!existsSync7(bufferFile)) return;
6610
+ if (!existsSync10(bufferFile)) return;
6228
6611
  let content;
6229
6612
  try {
6230
- content = readFileSync7(bufferFile, "utf8").trim();
6613
+ content = readFileSync10(bufferFile, "utf8").trim();
6231
6614
  } catch {
6232
6615
  return;
6233
6616
  }
@@ -6246,7 +6629,7 @@ function makeViolationsBuffer(opts = {}) {
6246
6629
  }
6247
6630
  if (records.length === 0) {
6248
6631
  try {
6249
- writeFileSync4(bufferFile, "", "utf8");
6632
+ writeFileSync6(bufferFile, "", "utf8");
6250
6633
  } catch {
6251
6634
  }
6252
6635
  return;
@@ -6276,20 +6659,20 @@ function makeViolationsBuffer(opts = {}) {
6276
6659
  }
6277
6660
  try {
6278
6661
  if (failed.length === 0) {
6279
- writeFileSync4(bufferFile, "", "utf8");
6662
+ writeFileSync6(bufferFile, "", "utf8");
6280
6663
  } else {
6281
6664
  const serialised = failed.map((r) => JSON.stringify(r)).join("\n") + "\n";
6282
- writeFileSync4(bufferFile, serialised, "utf8");
6665
+ writeFileSync6(bufferFile, serialised, "utf8");
6283
6666
  }
6284
6667
  } catch {
6285
6668
  }
6286
6669
  }
6287
6670
  return { bufferViolation: bufferViolation2, flushViolations: flushViolations2 };
6288
6671
  }
6289
- function ensureDirFor2(filePath) {
6290
- const dir = dirname2(filePath);
6291
- if (!existsSync7(dir)) {
6292
- mkdirSync4(dir, { recursive: true });
6672
+ function ensureDirFor3(filePath) {
6673
+ const dir = dirname4(filePath);
6674
+ if (!existsSync10(dir)) {
6675
+ mkdirSync5(dir, { recursive: true });
6293
6676
  }
6294
6677
  }
6295
6678
  var _defaultBuffer = makeViolationsBuffer();
@@ -6315,11 +6698,11 @@ function appendDebugLog(entry) {
6315
6698
  try {
6316
6699
  const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "";
6317
6700
  if (!homeDir) return;
6318
- const { mkdirSync: mkdirSync5, appendFileSync: appendFileSync2 } = __require("node:fs");
6319
- const { join: join6 } = __require("node:path");
6320
- const dir = join6(homeDir, ".cybedefend");
6321
- mkdirSync5(dir, { recursive: true });
6322
- appendFileSync2(join6(dir, "hook-debug.log"), entry + "\n", "utf8");
6701
+ const { mkdirSync: mkdirSync6, appendFileSync: appendFileSync2 } = __require("node:fs");
6702
+ const { join: join9 } = __require("node:path");
6703
+ const dir = join9(homeDir, ".cybedefend");
6704
+ mkdirSync6(dir, { recursive: true });
6705
+ appendFileSync2(join9(dir, "hook-debug.log"), entry + "\n", "utf8");
6323
6706
  } catch {
6324
6707
  }
6325
6708
  }