balladeer 1.0.16 → 1.0.18
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/dist/anchor-budget.d.ts +40 -0
- package/dist/anchor-budget.js +110 -0
- package/dist/anchoring.d.ts +241 -0
- package/dist/anchoring.js +766 -0
- package/dist/anchors-client.d.ts +62 -0
- package/dist/anchors-client.js +197 -0
- package/dist/anchors-schema.d.ts +85 -0
- package/dist/anchors-schema.js +246 -0
- package/dist/cli.d.ts +1 -1
- package/dist/cli.js +24 -7
- package/dist/commands/anchor-usage.d.ts +6 -0
- package/dist/commands/anchor-usage.js +19 -0
- package/dist/commands/anchor.d.ts +127 -0
- package/dist/commands/anchor.js +303 -0
- package/dist/commands/guidance.d.ts +2 -1
- package/dist/commands/guidance.js +55 -0
- package/dist/commands/judge.d.ts +219 -12
- package/dist/commands/judge.js +911 -103
- package/dist/commands/map.d.ts +46 -2
- package/dist/commands/map.js +244 -5
- package/dist/commands/mcp.js +2 -2
- package/dist/guidance-hook.mjs +282 -95
- package/dist/guidance.d.ts +7 -0
- package/dist/guidance.js +10 -1
- package/dist/headless-agent.d.ts +19 -1
- package/dist/headless-agent.js +22 -5
- package/dist/hook-trust.d.ts +41 -9
- package/dist/hook-trust.js +98 -16
- package/dist/judge-brief.d.ts +33 -3
- package/dist/judge-brief.js +39 -2
- package/dist/judge-hook.d.ts +188 -14
- package/dist/judge-hook.js +919 -61
- package/dist/judge-said.d.ts +52 -0
- package/dist/judge-said.js +181 -0
- package/dist/promise-meaning.d.ts +25 -0
- package/dist/promise-meaning.js +24 -7
- package/dist/relay.d.ts +71 -0
- package/dist/relay.js +193 -0
- package/dist/remove-earlier.js +4 -2
- package/dist/risk/git.d.ts +3 -1
- package/dist/risk/git.js +3 -3
- package/dist/risk/graph-cache.d.ts +83 -0
- package/dist/risk/graph-cache.js +291 -0
- package/dist/risk/import-graph.d.ts +43 -0
- package/dist/risk/import-graph.js +88 -34
- package/dist/risk/index.d.ts +1 -1
- package/dist/risk/index.js +1 -1
- package/dist/risk/pipeline.d.ts +13 -0
- package/dist/risk/pipeline.js +15 -4
- package/dist/risk/score.d.ts +10 -0
- package/dist/risk/score.js +11 -2
- package/dist/scratch-worktree.d.ts +5 -1
- package/dist/scratch-worktree.js +9 -2
- package/dist/user-scope.d.ts +74 -3
- package/dist/user-scope.js +270 -9
- package/dist/wire.d.ts +19 -3
- package/dist/wire.js +2 -2
- package/package.json +7 -2
package/dist/guidance-hook.mjs
CHANGED
|
@@ -16,9 +16,9 @@ var __commonJS = (cb, mod) => function __require2() {
|
|
|
16
16
|
};
|
|
17
17
|
var __copyProps = (to, from, except, desc) => {
|
|
18
18
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
-
for (let
|
|
20
|
-
if (!__hasOwnProp.call(to,
|
|
21
|
-
__defProp(to,
|
|
19
|
+
for (let key2 of __getOwnPropNames(from))
|
|
20
|
+
if (!__hasOwnProp.call(to, key2) && key2 !== except)
|
|
21
|
+
__defProp(to, key2, { get: () => from[key2], enumerable: !(desc = __getOwnPropDesc(from, key2)) || desc.enumerable });
|
|
22
22
|
}
|
|
23
23
|
return to;
|
|
24
24
|
};
|
|
@@ -366,9 +366,9 @@ var require_toml_parser = __commonJS({
|
|
|
366
366
|
var hasOwnProperty = Object.prototype.hasOwnProperty;
|
|
367
367
|
var defineProperty = Object.defineProperty;
|
|
368
368
|
var descriptor = { configurable: true, enumerable: true, writable: true, value: void 0 };
|
|
369
|
-
function hasKey(obj,
|
|
370
|
-
if (hasOwnProperty.call(obj,
|
|
371
|
-
if (
|
|
369
|
+
function hasKey(obj, key2) {
|
|
370
|
+
if (hasOwnProperty.call(obj, key2)) return true;
|
|
371
|
+
if (key2 === "__proto__") defineProperty(obj, "__proto__", descriptor);
|
|
372
372
|
return false;
|
|
373
373
|
}
|
|
374
374
|
var INLINE_TABLE = Symbol("inline-table");
|
|
@@ -570,11 +570,11 @@ var require_toml_parser = __commonJS({
|
|
|
570
570
|
parseAssign() {
|
|
571
571
|
return this.callNow(this.parseKeyword, this.recordAssignKeyword);
|
|
572
572
|
}
|
|
573
|
-
recordAssignKeyword(
|
|
573
|
+
recordAssignKeyword(key2) {
|
|
574
574
|
if (this.state.resultTable) {
|
|
575
|
-
this.state.resultTable.push(
|
|
575
|
+
this.state.resultTable.push(key2);
|
|
576
576
|
} else {
|
|
577
|
-
this.state.resultTable = [
|
|
577
|
+
this.state.resultTable = [key2];
|
|
578
578
|
}
|
|
579
579
|
return this.goto(this.parseAssignKeywordPreDot);
|
|
580
580
|
}
|
|
@@ -1779,10 +1779,10 @@ var require_stringify = __commonJS({
|
|
|
1779
1779
|
return new Error("Array values can't have mixed types");
|
|
1780
1780
|
}
|
|
1781
1781
|
function getInlineKeys(obj) {
|
|
1782
|
-
return Object.keys(obj).filter((
|
|
1782
|
+
return Object.keys(obj).filter((key2) => isInline(obj[key2]));
|
|
1783
1783
|
}
|
|
1784
1784
|
function getComplexKeys(obj) {
|
|
1785
|
-
return Object.keys(obj).filter((
|
|
1785
|
+
return Object.keys(obj).filter((key2) => !isInline(obj[key2]));
|
|
1786
1786
|
}
|
|
1787
1787
|
function toJSON(obj) {
|
|
1788
1788
|
let nobj = Array.isArray(obj) ? [] : Object.prototype.hasOwnProperty.call(obj, "__proto__") ? { ["__proto__"]: void 0 } : {};
|
|
@@ -1803,16 +1803,16 @@ var require_stringify = __commonJS({
|
|
|
1803
1803
|
complexKeys = getComplexKeys(obj);
|
|
1804
1804
|
var result = [];
|
|
1805
1805
|
var inlineIndent = indent || "";
|
|
1806
|
-
inlineKeys.forEach((
|
|
1807
|
-
var type = tomlType2(obj[
|
|
1806
|
+
inlineKeys.forEach((key2) => {
|
|
1807
|
+
var type = tomlType2(obj[key2]);
|
|
1808
1808
|
if (type !== "undefined" && type !== "null") {
|
|
1809
|
-
result.push(inlineIndent + stringifyKey(
|
|
1809
|
+
result.push(inlineIndent + stringifyKey(key2) + " = " + stringifyAnyInline(obj[key2], true));
|
|
1810
1810
|
}
|
|
1811
1811
|
});
|
|
1812
1812
|
if (result.length > 0) result.push("");
|
|
1813
1813
|
var complexIndent = prefix && inlineKeys.length > 0 ? indent + " " : "";
|
|
1814
|
-
complexKeys.forEach((
|
|
1815
|
-
result.push(stringifyComplex(prefix, complexIndent,
|
|
1814
|
+
complexKeys.forEach((key2) => {
|
|
1815
|
+
result.push(stringifyComplex(prefix, complexIndent, key2, obj[key2]));
|
|
1816
1816
|
});
|
|
1817
1817
|
return result.join("\n");
|
|
1818
1818
|
}
|
|
@@ -1857,8 +1857,8 @@ var require_stringify = __commonJS({
|
|
|
1857
1857
|
return "table";
|
|
1858
1858
|
}
|
|
1859
1859
|
}
|
|
1860
|
-
function stringifyKey(
|
|
1861
|
-
var keyStr = String(
|
|
1860
|
+
function stringifyKey(key2) {
|
|
1861
|
+
var keyStr = String(key2);
|
|
1862
1862
|
if (/^[-A-Za-z0-9_]+$/.test(keyStr)) {
|
|
1863
1863
|
return keyStr;
|
|
1864
1864
|
} else {
|
|
@@ -1977,27 +1977,27 @@ var require_stringify = __commonJS({
|
|
|
1977
1977
|
function stringifyInlineTable(value) {
|
|
1978
1978
|
value = toJSON(value);
|
|
1979
1979
|
var result = [];
|
|
1980
|
-
Object.keys(value).forEach((
|
|
1981
|
-
result.push(stringifyKey(
|
|
1980
|
+
Object.keys(value).forEach((key2) => {
|
|
1981
|
+
result.push(stringifyKey(key2) + " = " + stringifyAnyInline(value[key2], false));
|
|
1982
1982
|
});
|
|
1983
1983
|
return "{ " + result.join(", ") + (result.length > 0 ? " " : "") + "}";
|
|
1984
1984
|
}
|
|
1985
|
-
function stringifyComplex(prefix, indent,
|
|
1985
|
+
function stringifyComplex(prefix, indent, key2, value) {
|
|
1986
1986
|
var valueType = tomlType2(value);
|
|
1987
1987
|
if (valueType === "array") {
|
|
1988
|
-
return stringifyArrayOfTables(prefix, indent,
|
|
1988
|
+
return stringifyArrayOfTables(prefix, indent, key2, value);
|
|
1989
1989
|
} else if (valueType === "table") {
|
|
1990
|
-
return stringifyComplexTable(prefix, indent,
|
|
1990
|
+
return stringifyComplexTable(prefix, indent, key2, value);
|
|
1991
1991
|
} else {
|
|
1992
1992
|
throw typeError(valueType);
|
|
1993
1993
|
}
|
|
1994
1994
|
}
|
|
1995
|
-
function stringifyArrayOfTables(prefix, indent,
|
|
1995
|
+
function stringifyArrayOfTables(prefix, indent, key2, values) {
|
|
1996
1996
|
values = toJSON(values);
|
|
1997
1997
|
validateArray(values);
|
|
1998
1998
|
var firstValueType = tomlType2(values[0]);
|
|
1999
1999
|
if (firstValueType !== "table") throw typeError(firstValueType);
|
|
2000
|
-
var fullKey = prefix + stringifyKey(
|
|
2000
|
+
var fullKey = prefix + stringifyKey(key2);
|
|
2001
2001
|
var result = "";
|
|
2002
2002
|
values.forEach((table) => {
|
|
2003
2003
|
if (result.length > 0) result += "\n";
|
|
@@ -2006,8 +2006,8 @@ var require_stringify = __commonJS({
|
|
|
2006
2006
|
});
|
|
2007
2007
|
return result;
|
|
2008
2008
|
}
|
|
2009
|
-
function stringifyComplexTable(prefix, indent,
|
|
2010
|
-
var fullKey = prefix + stringifyKey(
|
|
2009
|
+
function stringifyComplexTable(prefix, indent, key2, value) {
|
|
2010
|
+
var fullKey = prefix + stringifyKey(key2);
|
|
2011
2011
|
var result = "";
|
|
2012
2012
|
if (getInlineKeys(value).length > 0) {
|
|
2013
2013
|
result += indent + "[" + fullKey + "]\n";
|
|
@@ -2026,8 +2026,133 @@ var require_toml = __commonJS({
|
|
|
2026
2026
|
}
|
|
2027
2027
|
});
|
|
2028
2028
|
|
|
2029
|
+
// src/relay.ts
|
|
2030
|
+
import { createHash } from "node:crypto";
|
|
2031
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
2032
|
+
import { join } from "node:path";
|
|
2033
|
+
var RELAY_FIELD = "forThePerson";
|
|
2034
|
+
var MAX_SENTENCE = 400;
|
|
2035
|
+
var MAX_SUBJECT = 200;
|
|
2036
|
+
var MAX_PENDING = 8;
|
|
2037
|
+
function clean(value, limit) {
|
|
2038
|
+
if (typeof value !== "string") return void 0;
|
|
2039
|
+
const text = value.replace(/[\u0000-\u001f\u007f]/g, " ").trim();
|
|
2040
|
+
return text ? text.slice(0, limit) : void 0;
|
|
2041
|
+
}
|
|
2042
|
+
function noticeFrom(value) {
|
|
2043
|
+
if (!value || typeof value !== "object") return void 0;
|
|
2044
|
+
const record = value;
|
|
2045
|
+
const sentence = clean(record.sentence, MAX_SENTENCE);
|
|
2046
|
+
if (!sentence) return void 0;
|
|
2047
|
+
const subjects = (Array.isArray(record.subjects) ? record.subjects : []).map((s) => clean(s, MAX_SUBJECT)).filter((s) => s !== void 0).slice(0, 4);
|
|
2048
|
+
return { sentence, subjects };
|
|
2049
|
+
}
|
|
2050
|
+
function noticeFromToolResponse(response) {
|
|
2051
|
+
const visit = (value, depth) => {
|
|
2052
|
+
if (depth > 4 || value === null || value === void 0) return void 0;
|
|
2053
|
+
if (typeof value === "string") {
|
|
2054
|
+
if (!value.includes(RELAY_FIELD)) return void 0;
|
|
2055
|
+
try {
|
|
2056
|
+
return visit(JSON.parse(value), depth + 1);
|
|
2057
|
+
} catch {
|
|
2058
|
+
return void 0;
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
if (Array.isArray(value)) {
|
|
2062
|
+
for (const item of value) {
|
|
2063
|
+
const found = visit(item, depth + 1);
|
|
2064
|
+
if (found) return found;
|
|
2065
|
+
}
|
|
2066
|
+
return void 0;
|
|
2067
|
+
}
|
|
2068
|
+
if (typeof value !== "object") return void 0;
|
|
2069
|
+
const record = value;
|
|
2070
|
+
if (RELAY_FIELD in record) return noticeFrom(record[RELAY_FIELD]);
|
|
2071
|
+
for (const key2 of ["structuredContent", "content", "text", "result", "tool_response"]) {
|
|
2072
|
+
const found = visit(record[key2], depth + 1);
|
|
2073
|
+
if (found) return found;
|
|
2074
|
+
}
|
|
2075
|
+
return void 0;
|
|
2076
|
+
};
|
|
2077
|
+
return visit(response, 0);
|
|
2078
|
+
}
|
|
2079
|
+
function relayNote(notice2) {
|
|
2080
|
+
return `Balladeer just acted on the person's behalf: ${notice2.sentence} Make sure your reply tells them this, in your own words and in about one line.`;
|
|
2081
|
+
}
|
|
2082
|
+
var WORD = /[a-z0-9]+/g;
|
|
2083
|
+
var QUIET = new Set(
|
|
2084
|
+
"a an the and or of to in on for with is are be by as at from that this it its not no".split(" ")
|
|
2085
|
+
);
|
|
2086
|
+
function words(text) {
|
|
2087
|
+
return (text.toLowerCase().match(WORD) ?? []).filter((w) => w.length > 2 && !QUIET.has(w));
|
|
2088
|
+
}
|
|
2089
|
+
function mentions(reply, subject) {
|
|
2090
|
+
const wanted = [...new Set(words(subject))];
|
|
2091
|
+
if (!wanted.length) return true;
|
|
2092
|
+
const said = new Set(words(reply));
|
|
2093
|
+
const found = wanted.filter((w) => said.has(w) || said.has(w.replace(/s$/, ""))).length;
|
|
2094
|
+
return found / wanted.length >= 0.5;
|
|
2095
|
+
}
|
|
2096
|
+
function relayed(reply, notice2) {
|
|
2097
|
+
return notice2.subjects.length === 0 || notice2.subjects.some((s) => mentions(reply, s));
|
|
2098
|
+
}
|
|
2099
|
+
function reminder(notices) {
|
|
2100
|
+
const told = notices.map((n) => n.sentence).join(" ");
|
|
2101
|
+
return `Your reply does not yet tell the person what Balladeer did: ${told} Add it in your own words, in about one line.`;
|
|
2102
|
+
}
|
|
2103
|
+
function pendingPath(directory, sessionId) {
|
|
2104
|
+
return join(directory, `${createHash("sha256").update(sessionId).digest("hex")}.relay.json`);
|
|
2105
|
+
}
|
|
2106
|
+
function readPending(directory, sessionId) {
|
|
2107
|
+
try {
|
|
2108
|
+
const parsed = JSON.parse(readFileSync(pendingPath(directory, sessionId), "utf8"));
|
|
2109
|
+
return parsed.schemaVersion === 1 && Array.isArray(parsed.notices) ? parsed.notices.map(noticeFrom).filter((n) => n !== void 0) : [];
|
|
2110
|
+
} catch {
|
|
2111
|
+
return [];
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
function writePending(directory, sessionId, notices) {
|
|
2115
|
+
mkdirSync(directory, { recursive: true, mode: 448 });
|
|
2116
|
+
const path = pendingPath(directory, sessionId);
|
|
2117
|
+
const temporary = `${path}.${process.pid}.tmp`;
|
|
2118
|
+
const body = { schemaVersion: 1, notices: notices.slice(-MAX_PENDING) };
|
|
2119
|
+
writeFileSync(temporary, JSON.stringify(body), { mode: 384 });
|
|
2120
|
+
renameSync(temporary, path);
|
|
2121
|
+
}
|
|
2122
|
+
function isBalladeerTool(name) {
|
|
2123
|
+
return typeof name === "string" && /^mcp__balladeer[a-z0-9_-]*__/i.test(name);
|
|
2124
|
+
}
|
|
2125
|
+
function relayHook(input, directory) {
|
|
2126
|
+
try {
|
|
2127
|
+
const sessionId = typeof input.session_id === "string" ? input.session_id : void 0;
|
|
2128
|
+
if (input.hook_event_name === "PostToolUse") {
|
|
2129
|
+
if (!isBalladeerTool(input.tool_name)) return void 0;
|
|
2130
|
+
const notice2 = noticeFromToolResponse(input.tool_response ?? input.tool_output);
|
|
2131
|
+
if (!notice2) return void 0;
|
|
2132
|
+
if (sessionId && directory)
|
|
2133
|
+
writePending(directory, sessionId, [...readPending(directory, sessionId), notice2]);
|
|
2134
|
+
return JSON.stringify({
|
|
2135
|
+
hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: relayNote(notice2) }
|
|
2136
|
+
});
|
|
2137
|
+
}
|
|
2138
|
+
if (input.hook_event_name === "Stop") {
|
|
2139
|
+
if (!sessionId || !directory) return void 0;
|
|
2140
|
+
const pending = readPending(directory, sessionId);
|
|
2141
|
+
if (!pending.length) return void 0;
|
|
2142
|
+
const reply = typeof input.last_assistant_message === "string" ? input.last_assistant_message : "";
|
|
2143
|
+
const missing = pending.filter((n) => !relayed(reply, n));
|
|
2144
|
+
writePending(directory, sessionId, []);
|
|
2145
|
+
if (!missing.length || input.stop_hook_active === true) return void 0;
|
|
2146
|
+
return JSON.stringify({ decision: "block", reason: reminder(missing) });
|
|
2147
|
+
}
|
|
2148
|
+
return void 0;
|
|
2149
|
+
} catch {
|
|
2150
|
+
return void 0;
|
|
2151
|
+
}
|
|
2152
|
+
}
|
|
2153
|
+
|
|
2029
2154
|
// src/wire.ts
|
|
2030
|
-
var CLI_VERSION = "1.0.
|
|
2155
|
+
var CLI_VERSION = "1.0.18";
|
|
2031
2156
|
var CLIENT_HEADER = "x-balladeer-client";
|
|
2032
2157
|
var UPDATE_STATE_HEADER = "x-balladeer-update-state";
|
|
2033
2158
|
var HOOK_HOST_HEADER = "x-balladeer-hook";
|
|
@@ -2040,15 +2165,15 @@ import {
|
|
|
2040
2165
|
fchmodSync,
|
|
2041
2166
|
fsyncSync,
|
|
2042
2167
|
lstatSync,
|
|
2043
|
-
mkdirSync,
|
|
2168
|
+
mkdirSync as mkdirSync2,
|
|
2044
2169
|
openSync,
|
|
2045
|
-
readFileSync,
|
|
2046
|
-
renameSync,
|
|
2170
|
+
readFileSync as readFileSync2,
|
|
2171
|
+
renameSync as renameSync2,
|
|
2047
2172
|
unlinkSync,
|
|
2048
2173
|
writeSync
|
|
2049
2174
|
} from "node:fs";
|
|
2050
2175
|
import { homedir } from "node:os";
|
|
2051
|
-
import { dirname, join, parse, resolve } from "node:path";
|
|
2176
|
+
import { dirname, join as join2, parse, resolve } from "node:path";
|
|
2052
2177
|
var StoreError = class extends Error {
|
|
2053
2178
|
code;
|
|
2054
2179
|
constructor(code, message, options) {
|
|
@@ -2062,11 +2187,11 @@ function configHome(environment = process.env) {
|
|
|
2062
2187
|
const explicit = environment.BALLADEER_CONFIG_HOME?.trim();
|
|
2063
2188
|
if (explicit) return resolve(explicit);
|
|
2064
2189
|
const xdg = environment.XDG_CONFIG_HOME?.trim();
|
|
2065
|
-
if (xdg) return resolve(
|
|
2066
|
-
return resolve(
|
|
2190
|
+
if (xdg) return resolve(join2(xdg, "balladeer"));
|
|
2191
|
+
return resolve(join2(homedir(), ".config", "balladeer"));
|
|
2067
2192
|
}
|
|
2068
2193
|
function credentialsPath(environment = process.env) {
|
|
2069
|
-
return
|
|
2194
|
+
return join2(configHome(environment), "credentials.json");
|
|
2070
2195
|
}
|
|
2071
2196
|
function assertPrivate(path, kind) {
|
|
2072
2197
|
const stats = lstatSync(path);
|
|
@@ -2098,7 +2223,7 @@ function readCredentials(environment = process.env) {
|
|
|
2098
2223
|
let raw;
|
|
2099
2224
|
try {
|
|
2100
2225
|
assertPrivate(path, "file");
|
|
2101
|
-
raw =
|
|
2226
|
+
raw = readFileSync2(path, "utf8");
|
|
2102
2227
|
} catch (error) {
|
|
2103
2228
|
if (error?.code === "ENOENT") {
|
|
2104
2229
|
return { ...EMPTY, pendingPairings: [], sessions: [], agents: [] };
|
|
@@ -2154,8 +2279,8 @@ function newerVersionPublished() {
|
|
|
2154
2279
|
|
|
2155
2280
|
// src/self-update.ts
|
|
2156
2281
|
import { spawn } from "node:child_process";
|
|
2157
|
-
import { existsSync, mkdirSync as
|
|
2158
|
-
import { delimiter, dirname as dirname2, join as
|
|
2282
|
+
import { existsSync, mkdirSync as mkdirSync3, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2283
|
+
import { delimiter, dirname as dirname2, join as join3 } from "node:path";
|
|
2159
2284
|
|
|
2160
2285
|
// src/release.ts
|
|
2161
2286
|
var HOOK_SPECIFIER = "balladeer@1";
|
|
@@ -2167,24 +2292,24 @@ var DAY_MS = 24 * 60 * 60 * 1e3;
|
|
|
2167
2292
|
var SELF_UPDATE_ENVIRONMENT = "BALLADEER_SELF_UPDATE";
|
|
2168
2293
|
var STATE_PATTERN = /^(?:started|unstartable|installed|failed):[A-Za-z0-9._-]{1,40}$/;
|
|
2169
2294
|
function stampPath(environment) {
|
|
2170
|
-
return
|
|
2295
|
+
return join3(configHome(environment), STAMP);
|
|
2171
2296
|
}
|
|
2172
2297
|
function statePath(environment) {
|
|
2173
|
-
return
|
|
2298
|
+
return join3(configHome(environment), STATE);
|
|
2174
2299
|
}
|
|
2175
2300
|
function lastStarted(environment) {
|
|
2176
2301
|
const path = stampPath(environment);
|
|
2177
2302
|
if (!existsSync(path)) return 0;
|
|
2178
2303
|
try {
|
|
2179
|
-
const parsed = JSON.parse(
|
|
2304
|
+
const parsed = JSON.parse(readFileSync3(path, "utf8"));
|
|
2180
2305
|
return typeof parsed.startedAt === "number" ? parsed.startedAt : 0;
|
|
2181
2306
|
} catch {
|
|
2182
2307
|
return 0;
|
|
2183
2308
|
}
|
|
2184
2309
|
}
|
|
2185
2310
|
function writePrivate(path, value, environment) {
|
|
2186
|
-
|
|
2187
|
-
|
|
2311
|
+
mkdirSync3(configHome(environment), { recursive: true, mode: 448 });
|
|
2312
|
+
writeFileSync2(path, JSON.stringify(value) + "\n", { mode: 384 });
|
|
2188
2313
|
}
|
|
2189
2314
|
function recordSelfUpdateState(environment, state, at) {
|
|
2190
2315
|
if (!STATE_PATTERN.test(state)) return;
|
|
@@ -2195,7 +2320,7 @@ function recordSelfUpdateState(environment, state, at) {
|
|
|
2195
2320
|
}
|
|
2196
2321
|
function selfUpdateState(environment) {
|
|
2197
2322
|
try {
|
|
2198
|
-
const parsed = JSON.parse(
|
|
2323
|
+
const parsed = JSON.parse(readFileSync3(statePath(environment), "utf8"));
|
|
2199
2324
|
return typeof parsed.state === "string" && STATE_PATTERN.test(parsed.state) ? parsed.state : void 0;
|
|
2200
2325
|
} catch {
|
|
2201
2326
|
return void 0;
|
|
@@ -2209,9 +2334,9 @@ function resolveLaunch(specifier, environment, execPath = process.execPath, exis
|
|
|
2209
2334
|
PATH: [bin, environment.PATH ?? ""].filter((part) => part.length > 0).join(delimiter),
|
|
2210
2335
|
[SELF_UPDATE_ENVIRONMENT]: "1"
|
|
2211
2336
|
};
|
|
2212
|
-
const script =
|
|
2337
|
+
const script = join3(bin, "..", "lib", "node_modules", "npm", "bin", "npx-cli.js");
|
|
2213
2338
|
if (exists(script)) return { command: execPath, args: [script, ...tail], environment: child };
|
|
2214
|
-
const shim =
|
|
2339
|
+
const shim = join3(bin, process.platform === "win32" ? "npx.cmd" : "npx");
|
|
2215
2340
|
if (exists(shim)) return { command: shim, args: tail, environment: child };
|
|
2216
2341
|
return { command: "npx", args: tail, environment: child };
|
|
2217
2342
|
}
|
|
@@ -2249,25 +2374,25 @@ function startSelfUpdateIfDue(newer, deps) {
|
|
|
2249
2374
|
}
|
|
2250
2375
|
|
|
2251
2376
|
// src/guidance.ts
|
|
2252
|
-
import { createHash, randomBytes } from "node:crypto";
|
|
2377
|
+
import { createHash as createHash2, randomBytes } from "node:crypto";
|
|
2253
2378
|
import {
|
|
2254
2379
|
constants,
|
|
2255
2380
|
closeSync as closeSync2,
|
|
2256
2381
|
fstatSync,
|
|
2257
2382
|
lstatSync as lstatSync2,
|
|
2258
|
-
mkdirSync as
|
|
2383
|
+
mkdirSync as mkdirSync4,
|
|
2259
2384
|
openSync as openSync2,
|
|
2260
|
-
readFileSync as
|
|
2261
|
-
renameSync as
|
|
2385
|
+
readFileSync as readFileSync4,
|
|
2386
|
+
renameSync as renameSync3,
|
|
2262
2387
|
unlinkSync as unlinkSync2,
|
|
2263
|
-
writeFileSync as
|
|
2388
|
+
writeFileSync as writeFileSync3
|
|
2264
2389
|
} from "node:fs";
|
|
2265
|
-
import { join as
|
|
2390
|
+
import { join as join4 } from "node:path";
|
|
2266
2391
|
var GUIDANCE_UNAVAILABLE = "Balladeer could not verify the current workspace guidance and capture mode. Continue the user's authorized work, but do not make unsolicited capture offers or file inferred promises. Do not reuse earlier Quiet/Thorough permission or cached instructions as current. An explicit request to record still requires current Balladeer tool checks and named-human agreement to meaning; never invent approval. Retry current guidance at the next hook boundary.";
|
|
2267
2392
|
var HEX = /^[a-f0-9]{64}$/;
|
|
2268
2393
|
var UUID = /^[a-f0-9]{8}-[a-f0-9]{4}-[1-5][a-f0-9]{3}-[89ab][a-f0-9]{3}-[a-f0-9]{12}$/i;
|
|
2269
2394
|
var MAX_BYTES = 4e4;
|
|
2270
|
-
var sha = (value) =>
|
|
2395
|
+
var sha = (value) => createHash2("sha256").update(value).digest("hex");
|
|
2271
2396
|
function object(value) {
|
|
2272
2397
|
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
2273
2398
|
}
|
|
@@ -2278,19 +2403,22 @@ function parseGuidance(value, agent) {
|
|
|
2278
2403
|
return value;
|
|
2279
2404
|
}
|
|
2280
2405
|
function guidanceEndpoint(agent) {
|
|
2406
|
+
return agentEndpoint(agent, "/api/agent-guidance");
|
|
2407
|
+
}
|
|
2408
|
+
function agentEndpoint(agent, pathname) {
|
|
2281
2409
|
const url = new URL(agent.mcpUrl);
|
|
2282
2410
|
const expected = new URL(agent.controlPlane);
|
|
2283
2411
|
if (url.origin !== expected.origin || url.protocol !== "https:" || url.pathname !== "/api/mcp" || url.username || url.password || url.hash || !UUID.test(agent.workspaceId) || !UUID.test(agent.repositoryId))
|
|
2284
2412
|
throw new Error("invalid_guidance_scope");
|
|
2285
|
-
for (const [
|
|
2413
|
+
for (const [key2, value] of [
|
|
2286
2414
|
["workspaceId", agent.workspaceId],
|
|
2287
2415
|
["repositoryId", agent.repositoryId]
|
|
2288
2416
|
]) {
|
|
2289
|
-
if (url.searchParams.getAll(
|
|
2417
|
+
if (url.searchParams.getAll(key2).length > 1 || url.searchParams.has(key2) && url.searchParams.get(key2) !== value)
|
|
2290
2418
|
throw new Error("invalid_guidance_scope");
|
|
2291
|
-
url.searchParams.set(
|
|
2419
|
+
url.searchParams.set(key2, value);
|
|
2292
2420
|
}
|
|
2293
|
-
url.pathname =
|
|
2421
|
+
url.pathname = pathname;
|
|
2294
2422
|
return url;
|
|
2295
2423
|
}
|
|
2296
2424
|
function readJson(path) {
|
|
@@ -2299,7 +2427,7 @@ function readJson(path) {
|
|
|
2299
2427
|
fd = openSync2(path, constants.O_RDONLY | constants.O_NOFOLLOW);
|
|
2300
2428
|
const stat = fstatSync(fd);
|
|
2301
2429
|
if (!stat.isFile() || stat.size > MAX_BYTES || (stat.mode & 63) !== 0) return void 0;
|
|
2302
|
-
return JSON.parse(
|
|
2430
|
+
return JSON.parse(readFileSync4(fd, "utf8"));
|
|
2303
2431
|
} catch {
|
|
2304
2432
|
return void 0;
|
|
2305
2433
|
} finally {
|
|
@@ -2308,7 +2436,7 @@ function readJson(path) {
|
|
|
2308
2436
|
}
|
|
2309
2437
|
function privateDirectory(path) {
|
|
2310
2438
|
try {
|
|
2311
|
-
|
|
2439
|
+
mkdirSync4(path, { mode: 448 });
|
|
2312
2440
|
} catch {
|
|
2313
2441
|
}
|
|
2314
2442
|
const stat = lstatSync2(path);
|
|
@@ -2318,9 +2446,9 @@ function privateDirectory(path) {
|
|
|
2318
2446
|
function atomicJson(path, value) {
|
|
2319
2447
|
const temporary = `${path}.${randomBytes(8).toString("hex")}.tmp`;
|
|
2320
2448
|
try {
|
|
2321
|
-
|
|
2449
|
+
writeFileSync3(temporary, `${JSON.stringify(value)}
|
|
2322
2450
|
`, { flag: "wx", mode: 384 });
|
|
2323
|
-
|
|
2451
|
+
renameSync3(temporary, path);
|
|
2324
2452
|
} finally {
|
|
2325
2453
|
try {
|
|
2326
2454
|
unlinkSync2(temporary);
|
|
@@ -2342,16 +2470,16 @@ function guidanceScopeKey(agent) {
|
|
|
2342
2470
|
function cacheDirectory(environment) {
|
|
2343
2471
|
const home = configHome(environment);
|
|
2344
2472
|
privateDirectory(home);
|
|
2345
|
-
const directory =
|
|
2473
|
+
const directory = join4(home, "guidance-v1");
|
|
2346
2474
|
privateDirectory(directory);
|
|
2347
2475
|
return directory;
|
|
2348
2476
|
}
|
|
2349
2477
|
function cached(directory, scopeKey, agent) {
|
|
2350
2478
|
try {
|
|
2351
|
-
const pointer = readJson(
|
|
2479
|
+
const pointer = readJson(join4(directory, `${scopeKey}.current.json`));
|
|
2352
2480
|
if (!object(pointer) || typeof pointer.key !== "string" || !HEX.test(pointer.key))
|
|
2353
2481
|
return void 0;
|
|
2354
|
-
const body = readJson(
|
|
2482
|
+
const body = readJson(join4(directory, `${pointer.key}.body.json`));
|
|
2355
2483
|
if (!object(body) || typeof body.etag !== "string" || !/^"[a-f0-9]{64}"$/.test(body.etag))
|
|
2356
2484
|
return void 0;
|
|
2357
2485
|
const document = parseGuidance(body.document, agent);
|
|
@@ -2427,12 +2555,12 @@ async function loadGuidance(input) {
|
|
|
2427
2555
|
if (abort.signal.aborted) throw new Error("guidance_deadline");
|
|
2428
2556
|
if (directory) {
|
|
2429
2557
|
try {
|
|
2430
|
-
const
|
|
2431
|
-
atomicJson(
|
|
2432
|
-
atomicJson(
|
|
2433
|
-
if (previous && previous.key !==
|
|
2558
|
+
const key2 = sha(JSON.stringify([scopeKey, document.revision]));
|
|
2559
|
+
atomicJson(join4(directory, `${key2}.body.json`), { document, etag });
|
|
2560
|
+
atomicJson(join4(directory, `${scopeKey}.current.json`), { key: key2 });
|
|
2561
|
+
if (previous && previous.key !== key2) {
|
|
2434
2562
|
try {
|
|
2435
|
-
unlinkSync2(
|
|
2563
|
+
unlinkSync2(join4(directory, `${previous.key}.body.json`));
|
|
2436
2564
|
} catch {
|
|
2437
2565
|
}
|
|
2438
2566
|
}
|
|
@@ -2474,7 +2602,7 @@ function recordGuidanceContext(input, write2) {
|
|
|
2474
2602
|
let path;
|
|
2475
2603
|
let emitted = true;
|
|
2476
2604
|
try {
|
|
2477
|
-
path =
|
|
2605
|
+
path = join4(cacheDirectory(input.environment), `${contextKey}.context.json`);
|
|
2478
2606
|
const previous = readJson(path);
|
|
2479
2607
|
emitted = always || !object(previous) || previous.status === "unavailable" || previous.digest !== document?.digest || previous.revision !== document?.revision || (previous.runtimeGeneration ?? null) !== (input.runtimeGeneration ?? null);
|
|
2480
2608
|
} catch {
|
|
@@ -2510,14 +2638,14 @@ import {
|
|
|
2510
2638
|
existsSync as existsSync3,
|
|
2511
2639
|
lstatSync as lstatSync4,
|
|
2512
2640
|
linkSync,
|
|
2513
|
-
mkdirSync as
|
|
2514
|
-
readFileSync as
|
|
2515
|
-
renameSync as
|
|
2641
|
+
mkdirSync as mkdirSync6,
|
|
2642
|
+
readFileSync as readFileSync7,
|
|
2643
|
+
renameSync as renameSync6,
|
|
2516
2644
|
unlinkSync as unlinkSync5,
|
|
2517
|
-
writeFileSync as
|
|
2645
|
+
writeFileSync as writeFileSync4
|
|
2518
2646
|
} from "node:fs";
|
|
2519
2647
|
var import_toml2 = __toESM(require_toml(), 1);
|
|
2520
|
-
import { dirname as dirname4, join as
|
|
2648
|
+
import { dirname as dirname4, join as join7, relative } from "node:path";
|
|
2521
2649
|
|
|
2522
2650
|
// src/codex-config.ts
|
|
2523
2651
|
var import_toml = __toESM(require_toml(), 1);
|
|
@@ -2526,26 +2654,26 @@ import {
|
|
|
2526
2654
|
existsSync as existsSync2,
|
|
2527
2655
|
fsyncSync as fsyncSync3,
|
|
2528
2656
|
lstatSync as lstatSync3,
|
|
2529
|
-
mkdirSync as
|
|
2657
|
+
mkdirSync as mkdirSync5,
|
|
2530
2658
|
openSync as openSync4,
|
|
2531
|
-
readFileSync as
|
|
2532
|
-
renameSync as
|
|
2659
|
+
readFileSync as readFileSync6,
|
|
2660
|
+
renameSync as renameSync5,
|
|
2533
2661
|
unlinkSync as unlinkSync4,
|
|
2534
2662
|
writeSync as writeSync3
|
|
2535
2663
|
} from "node:fs";
|
|
2536
|
-
import { join as
|
|
2664
|
+
import { join as join6 } from "node:path";
|
|
2537
2665
|
|
|
2538
2666
|
// src/mcp-config.ts
|
|
2539
2667
|
import {
|
|
2540
2668
|
closeSync as closeSync3,
|
|
2541
2669
|
fsyncSync as fsyncSync2,
|
|
2542
2670
|
openSync as openSync3,
|
|
2543
|
-
readFileSync as
|
|
2544
|
-
renameSync as
|
|
2671
|
+
readFileSync as readFileSync5,
|
|
2672
|
+
renameSync as renameSync4,
|
|
2545
2673
|
unlinkSync as unlinkSync3,
|
|
2546
2674
|
writeSync as writeSync2
|
|
2547
2675
|
} from "node:fs";
|
|
2548
|
-
import { basename, dirname as dirname3, isAbsolute, join as
|
|
2676
|
+
import { basename, dirname as dirname3, isAbsolute, join as join5 } from "node:path";
|
|
2549
2677
|
var MCP_CONFIG_FILE = ".mcp.json";
|
|
2550
2678
|
function sameOrigin(left, right) {
|
|
2551
2679
|
try {
|
|
@@ -2588,7 +2716,7 @@ function currentEntry(parsed) {
|
|
|
2588
2716
|
}
|
|
2589
2717
|
function readMcpConfig(repositoryRoot) {
|
|
2590
2718
|
try {
|
|
2591
|
-
return JSON.parse(
|
|
2719
|
+
return JSON.parse(readFileSync5(join5(repositoryRoot, MCP_CONFIG_FILE), "utf8"));
|
|
2592
2720
|
} catch {
|
|
2593
2721
|
return void 0;
|
|
2594
2722
|
}
|
|
@@ -2598,7 +2726,7 @@ function readMcpConfig(repositoryRoot) {
|
|
|
2598
2726
|
var CODEX_CONFIG_FILE = ".codex/config.toml";
|
|
2599
2727
|
function readCodexEntry(root) {
|
|
2600
2728
|
try {
|
|
2601
|
-
const config = (0, import_toml.parse)(
|
|
2729
|
+
const config = (0, import_toml.parse)(readFileSync6(join6(root, CODEX_CONFIG_FILE), "utf8"));
|
|
2602
2730
|
const servers = config.mcp_servers;
|
|
2603
2731
|
return servers && typeof servers === "object" && !Array.isArray(servers) && !(servers instanceof Date) ? servers.balladeer : void 0;
|
|
2604
2732
|
} catch {
|
|
@@ -2607,7 +2735,7 @@ function readCodexEntry(root) {
|
|
|
2607
2735
|
}
|
|
2608
2736
|
function hasManagedCodexEntry(root, controlPlane) {
|
|
2609
2737
|
try {
|
|
2610
|
-
const text =
|
|
2738
|
+
const text = readFileSync6(join6(root, CODEX_CONFIG_FILE), "utf8");
|
|
2611
2739
|
const starts = [...text.matchAll(/^# balladeer:mcp:start\r?$/gm)];
|
|
2612
2740
|
const ends = [...text.matchAll(/^# balladeer:mcp:end\r?$/gm)];
|
|
2613
2741
|
if (starts.length !== 1 || ends.length !== 1 || !starts[0] || !ends[0] || starts[0].index >= ends[0].index)
|
|
@@ -3032,7 +3160,7 @@ function readRegular(path, allowMissing = false, writable = true) {
|
|
|
3032
3160
|
const stat = lstatSync4(path);
|
|
3033
3161
|
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > MAX_FILE || writable && (stat.mode & 146) === 0)
|
|
3034
3162
|
throw new Error("file_not_safely_writable");
|
|
3035
|
-
return
|
|
3163
|
+
return readFileSync7(path, "utf8");
|
|
3036
3164
|
}
|
|
3037
3165
|
function safeParent(root, path) {
|
|
3038
3166
|
let at = dirname4(path);
|
|
@@ -3054,7 +3182,7 @@ function validateProjectGuidanceScope(options) {
|
|
|
3054
3182
|
stdio: ["ignore", "pipe", "ignore"],
|
|
3055
3183
|
timeout: 1e3
|
|
3056
3184
|
}).trim();
|
|
3057
|
-
const path =
|
|
3185
|
+
const path = join7(root, options.hook === "codex" ? ".codex/config.toml" : ".mcp.json");
|
|
3058
3186
|
safeParent(root, path);
|
|
3059
3187
|
readRegular(path, false, false);
|
|
3060
3188
|
const entry = options.hook === "codex" ? readCodexEntry(root) : currentEntry(readMcpConfig(root));
|
|
@@ -3168,31 +3296,52 @@ function repositoryHints(cwd = process.cwd()) {
|
|
|
3168
3296
|
}
|
|
3169
3297
|
|
|
3170
3298
|
// src/hook-trust.ts
|
|
3171
|
-
import { mkdirSync as
|
|
3172
|
-
import { join as
|
|
3299
|
+
import { mkdirSync as mkdirSync7, readFileSync as readFileSync8, rmSync, writeFileSync as writeFileSync5 } from "node:fs";
|
|
3300
|
+
import { join as join8 } from "node:path";
|
|
3301
|
+
var HOSTS_REQUIRING_TRUST = ["codex"];
|
|
3173
3302
|
var FILE = "hook-trust.json";
|
|
3303
|
+
var key = (host, set) => set === "guidance" ? host : `${host}:${set}`;
|
|
3174
3304
|
function read(environment) {
|
|
3175
3305
|
try {
|
|
3176
|
-
const parsed = JSON.parse(
|
|
3306
|
+
const parsed = JSON.parse(readFileSync8(join8(configHome(environment), FILE), "utf8"));
|
|
3177
3307
|
return parsed !== null && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
3178
3308
|
} catch {
|
|
3179
3309
|
return {};
|
|
3180
3310
|
}
|
|
3181
3311
|
}
|
|
3312
|
+
function pushCheckAwaitingFile(environment, host) {
|
|
3313
|
+
return join8(configHome(environment), `${host}-push-check-awaiting-first-run`);
|
|
3314
|
+
}
|
|
3315
|
+
function forget(path) {
|
|
3316
|
+
try {
|
|
3317
|
+
rmSync(path, { force: true });
|
|
3318
|
+
} catch {
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3182
3321
|
function write(environment, marks) {
|
|
3183
3322
|
try {
|
|
3184
|
-
|
|
3185
|
-
|
|
3323
|
+
mkdirSync7(configHome(environment), { recursive: true, mode: 448 });
|
|
3324
|
+
writeFileSync5(join8(configHome(environment), FILE), JSON.stringify(marks) + "\n", {
|
|
3186
3325
|
mode: 384
|
|
3187
3326
|
});
|
|
3327
|
+
for (const host of HOSTS_REQUIRING_TRUST) {
|
|
3328
|
+
const file = pushCheckAwaitingFile(environment, host);
|
|
3329
|
+
if (awaiting(marks[key(host, "push-check")])) writeFileSync5(file, "", { mode: 384 });
|
|
3330
|
+
else forget(file);
|
|
3331
|
+
}
|
|
3188
3332
|
} catch {
|
|
3189
3333
|
}
|
|
3190
3334
|
}
|
|
3191
|
-
function noteHookFired(environment, host, now = Date.now()) {
|
|
3335
|
+
function noteHookFired(environment, host, now = Date.now(), set = "guidance") {
|
|
3336
|
+
if (set === "push-check") forget(pushCheckAwaitingFile(environment, host));
|
|
3192
3337
|
const marks = read(environment);
|
|
3193
|
-
const mark = marks[host];
|
|
3338
|
+
const mark = marks[key(host, set)];
|
|
3194
3339
|
if (mark?.firedAt !== void 0 && mark.firedAt >= (mark.writtenAt ?? 0)) return;
|
|
3195
|
-
write(environment, { ...marks, [host]: { ...mark, firedAt: now } });
|
|
3340
|
+
write(environment, { ...marks, [key(host, set)]: { ...mark, firedAt: now } });
|
|
3341
|
+
}
|
|
3342
|
+
function awaiting(mark) {
|
|
3343
|
+
if (mark?.writtenAt === void 0) return false;
|
|
3344
|
+
return mark.firedAt === void 0 || mark.firedAt < mark.writtenAt;
|
|
3196
3345
|
}
|
|
3197
3346
|
|
|
3198
3347
|
// src/commands/guidance.ts
|
|
@@ -3237,7 +3386,45 @@ function eventFrom(input) {
|
|
|
3237
3386
|
function identifier(input) {
|
|
3238
3387
|
return typeof input === "string" && input.length > 0 && input.length <= 256 ? input : void 0;
|
|
3239
3388
|
}
|
|
3389
|
+
async function runRelayNote(options) {
|
|
3390
|
+
const input = await new Promise((resolve2) => {
|
|
3391
|
+
const chunks = [];
|
|
3392
|
+
let bytes = 0;
|
|
3393
|
+
let settled = false;
|
|
3394
|
+
const done = (value) => {
|
|
3395
|
+
if (settled) return;
|
|
3396
|
+
settled = true;
|
|
3397
|
+
clearTimeout(timer);
|
|
3398
|
+
options.stdin.removeListener("data", data);
|
|
3399
|
+
resolve2(value);
|
|
3400
|
+
};
|
|
3401
|
+
const data = (chunk) => {
|
|
3402
|
+
const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
|
|
3403
|
+
bytes += buffer.length;
|
|
3404
|
+
if (bytes > 1048576) done(void 0);
|
|
3405
|
+
else chunks.push(buffer);
|
|
3406
|
+
};
|
|
3407
|
+
const timer = setTimeout(() => done(void 0), 1e3);
|
|
3408
|
+
options.stdin.on("data", data);
|
|
3409
|
+
options.stdin.once("end", () => {
|
|
3410
|
+
try {
|
|
3411
|
+
done(JSON.parse(Buffer.concat(chunks).toString("utf8")));
|
|
3412
|
+
} catch {
|
|
3413
|
+
done(void 0);
|
|
3414
|
+
}
|
|
3415
|
+
});
|
|
3416
|
+
options.stdin.once("error", () => done(void 0));
|
|
3417
|
+
});
|
|
3418
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) return 0;
|
|
3419
|
+
const record = input;
|
|
3420
|
+
if (record.hook_event_name !== "PostToolUse") return 0;
|
|
3421
|
+
const out = relayHook(record);
|
|
3422
|
+
if (out) options.write(`${out}
|
|
3423
|
+
`);
|
|
3424
|
+
return 0;
|
|
3425
|
+
}
|
|
3240
3426
|
async function runGuidance(options) {
|
|
3427
|
+
if (options.event === "PostToolUse") return runRelayNote(options);
|
|
3241
3428
|
const projectScoped = options.repositoryId !== void 0;
|
|
3242
3429
|
if (projectScoped && !validateProjectGuidanceScope({
|
|
3243
3430
|
cwd: options.cwd,
|