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