@mrciphersmith/keryx 0.3.123 → 0.3.124
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/cli.js +1058 -516
- package/dist/core.js +26 -7
- package/package.json +1 -1
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.detail.md +3 -3
- package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +11 -11
- package/src/gdskills/bundled/skills/review/review-premise/SKILL.md +1 -1
package/dist/cli.js
CHANGED
|
@@ -23312,7 +23312,7 @@ var package_default;
|
|
|
23312
23312
|
var init_package = __esm(() => {
|
|
23313
23313
|
package_default = {
|
|
23314
23314
|
name: "@mrciphersmith/keryx",
|
|
23315
|
-
version: "0.3.
|
|
23315
|
+
version: "0.3.124",
|
|
23316
23316
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
23317
23317
|
private: false,
|
|
23318
23318
|
publishConfig: {
|
|
@@ -43746,6 +43746,12 @@ var init_scope = __esm(() => {
|
|
|
43746
43746
|
});
|
|
43747
43747
|
|
|
43748
43748
|
// src/review/types.ts
|
|
43749
|
+
function cardPressFields(source) {
|
|
43750
|
+
return {
|
|
43751
|
+
...source?.pressedBy === undefined ? {} : { pressedBy: source.pressedBy },
|
|
43752
|
+
...source?.pressedAt === undefined ? {} : { pressedAt: source.pressedAt }
|
|
43753
|
+
};
|
|
43754
|
+
}
|
|
43749
43755
|
var MANAGED_REVIEW_MODES, REVIEW_TARGET_KINDS, REVIEW_PACKAGE_STATUSES, REVIEW_COVERAGE_STATUSES, REVIEW_FINDING_SEVERITIES, REVIEW_FINDING_CONFIDENCES, FINDING_DISPOSITION_STATES, FINDING_DISMISSAL_STATES, EXTERNAL_TERMINAL_DISPOSITIONS, VERIFICATION_VERDICTS, VERIFICATION_METHODS, REASONING_CAPPED_VERDICT = "unverifiable", VERIFICATION_MODES, DEFAULT_VERIFICATION_MODE = "annotate", PREMISE_VERDICTS, HELD_PREMISE_VERDICTS;
|
|
43750
43756
|
var init_types4 = __esm(() => {
|
|
43751
43757
|
MANAGED_REVIEW_MODES = ["attach-review", "review-flow", "ingest"];
|
|
@@ -48993,6 +48999,7 @@ async function recordPremiseDecisionAfter(packageDir, input, applyFirst) {
|
|
|
48993
48999
|
at: input.at,
|
|
48994
49000
|
actor: PREMISE_DECISION_ACTOR,
|
|
48995
49001
|
confirmedVia: input.confirmedVia,
|
|
49002
|
+
...cardPressFields(input),
|
|
48996
49003
|
subject: input.subject,
|
|
48997
49004
|
...input.carriedFrom === undefined ? {} : { carriedFrom: input.carriedFrom }
|
|
48998
49005
|
};
|
|
@@ -49032,7 +49039,7 @@ async function recordOperatorDecision(packageDir, decision) {
|
|
|
49032
49039
|
if (!validGateDecision(decision))
|
|
49033
49040
|
throw new Error("an operator decision needs a kind, the operator's words, a reason, how it was confirmed and the time");
|
|
49034
49041
|
const subject = decision.subject === undefined ? undefined : { ...decision.subject.head === undefined ? {} : { head: decision.subject.head }, items: [...new Set(decision.subject.items)].sort() };
|
|
49035
|
-
const entry = { ...decision, quote: decision.quote.trim(), reason: decision.reason.trim(), ...subject === undefined ? {} : { subject } };
|
|
49042
|
+
const entry = { ...decision, quote: decision.quote.trim(), reason: decision.reason.trim(), ...cardPressFields(decision), ...subject === undefined ? {} : { subject } };
|
|
49036
49043
|
await withOperatorDecisionsLock(packageDir, () => appendDecision(packageDir, entry));
|
|
49037
49044
|
return decision;
|
|
49038
49045
|
}
|
|
@@ -49046,6 +49053,7 @@ var OPERATOR_DECISIONS_FILE = "operator-decisions.json", OPERATOR_DECISIONS_LOCK
|
|
|
49046
49053
|
var init_operator_decisions = __esm(() => {
|
|
49047
49054
|
init_fs();
|
|
49048
49055
|
init_json();
|
|
49056
|
+
init_types4();
|
|
49049
49057
|
OPERATOR_DECISION_KINDS = ["mutation-waiver", "reviewer-override"];
|
|
49050
49058
|
VIAS = ["tui-card", "remote-card", "terminal"];
|
|
49051
49059
|
});
|
|
@@ -51490,14 +51498,14 @@ function recordChunkEstimate(packageDir, estimate, mode) {
|
|
|
51490
51498
|
return record;
|
|
51491
51499
|
});
|
|
51492
51500
|
}
|
|
51493
|
-
async function answerChunkEstimate(packageDir, value, quote, now = new Date, confirmedVia) {
|
|
51501
|
+
async function answerChunkEstimate(packageDir, value, quote, now = new Date, confirmedVia, press) {
|
|
51494
51502
|
if (quote.trim() === "")
|
|
51495
51503
|
throw new Error("The answer needs the operator's own words (--quote).");
|
|
51496
51504
|
return withChunkStateLock(packageDir, async () => {
|
|
51497
51505
|
const previous = await readChunkEstimate(packageDir);
|
|
51498
51506
|
if (previous === undefined)
|
|
51499
51507
|
throw new Error("No chunk estimate is recorded; run `keryx review chunks estimate` first.");
|
|
51500
|
-
const record = { ...previous, asked: true, answer: { value, quote: quote.trim(), at: now.toISOString(), ...confirmedVia === undefined ? {} : { confirmedVia } } };
|
|
51508
|
+
const record = { ...previous, asked: true, answer: { value, quote: quote.trim(), at: now.toISOString(), ...confirmedVia === undefined ? {} : { confirmedVia }, ...cardPressFields(press) } };
|
|
51501
51509
|
await writeFileAtomic(path117.join(packageDir, CHUNK_ESTIMATE_FILE), `${JSON.stringify(record, null, 2)}
|
|
51502
51510
|
`);
|
|
51503
51511
|
return record;
|
|
@@ -51543,6 +51551,7 @@ var init_chunks_estimate = __esm(() => {
|
|
|
51543
51551
|
init_chunks();
|
|
51544
51552
|
init_blast_radius();
|
|
51545
51553
|
init_json();
|
|
51554
|
+
init_types4();
|
|
51546
51555
|
DEFAULT_CHUNK_LIMITS = { maxPasses: DEFAULT_MAX_PASSES, inputUsdPerMTok: 3, outputUsdPerMTok: 15, secondsPerPass: 150, source: "default" };
|
|
51547
51556
|
});
|
|
51548
51557
|
|
|
@@ -68822,7 +68831,7 @@ async function collectProjectRules(projectRoot, warnings) {
|
|
|
68822
68831
|
function selectRules(candidates, files, taskType, bundled) {
|
|
68823
68832
|
const selected = [];
|
|
68824
68833
|
for (const candidate of candidates) {
|
|
68825
|
-
const shadowsPath = candidate.refs.map((ref) => bundled.get(ref)).find((found) => found !== undefined);
|
|
68834
|
+
const shadowsPath = candidate.refs.map((ref) => bundled.get(ref) ?? bundled.get(withoutCorePrefix(ref))).find((found) => found !== undefined);
|
|
68826
68835
|
const matching = candidate.front.globs.filter((glob) => files.some((file) => matchesAnyGlobWithBraces(file, [glob])));
|
|
68827
68836
|
const base = { candidate, ...shadowsPath !== undefined ? { shadows: shadowsPath } : {} };
|
|
68828
68837
|
if (matching.length > 0) {
|
|
@@ -69095,7 +69104,7 @@ var init_context_resolver = __esm(() => {
|
|
|
69095
69104
|
service_api: ["code-style-patterns", "nestjs-dto", "api-contracts"]
|
|
69096
69105
|
};
|
|
69097
69106
|
TEST_RULES = ["tdd-workflow", "testing-conventions"];
|
|
69098
|
-
CITATION = /`([^`\s]+\.mdc?)
|
|
69107
|
+
CITATION = /`([^`\s#]+\.mdc?)(?:#[^`\s]*)?`/g;
|
|
69099
69108
|
});
|
|
69100
69109
|
|
|
69101
69110
|
// src/memory/relevant.ts
|
|
@@ -73358,7 +73367,9 @@ function readCoverageWaiver(dispatch) {
|
|
|
73358
73367
|
return;
|
|
73359
73368
|
const files = Array.isArray(waiver.files) ? waiver.files.filter(text2) : [];
|
|
73360
73369
|
const via = waiver.confirmedVia === "tui-card" || waiver.confirmedVia === "remote-card" ? waiver.confirmedVia : undefined;
|
|
73361
|
-
|
|
73370
|
+
const pressedBy = typeof waiver.pressedBy === "number" && Number.isSafeInteger(waiver.pressedBy) ? waiver.pressedBy : undefined;
|
|
73371
|
+
const pressedAt = text2(waiver.pressedAt) ? waiver.pressedAt : undefined;
|
|
73372
|
+
return { operatorQuote: waiver.operatorQuote, time: waiver.time, files, ...via === undefined ? {} : { confirmedVia: via }, ...cardPressFields({ pressedBy, pressedAt }) };
|
|
73362
73373
|
}
|
|
73363
73374
|
async function computeCoverage(packageDir, options = {}) {
|
|
73364
73375
|
const research = options.research ?? await readJson4(path172.join(packageDir, "research.json"));
|
|
@@ -73458,7 +73469,7 @@ function coverageLine(report) {
|
|
|
73458
73469
|
const { inScope, covered, omitted, uncovered } = report.counts;
|
|
73459
73470
|
return `coverage: in scope ${inScope}, covered ${covered}, omitted ${omitted}${reasons === "" ? "" : ` (${reasons})`}, uncovered ${uncovered}`;
|
|
73460
73471
|
}
|
|
73461
|
-
async function recordCoverageWaiver(packageDir, operatorQuote, now = new Date, confirmedVia) {
|
|
73472
|
+
async function recordCoverageWaiver(packageDir, operatorQuote, now = new Date, confirmedVia, press) {
|
|
73462
73473
|
const quote = operatorQuote.replace(/-{0,2}waive[- ]?coverage/gi, "").trim();
|
|
73463
73474
|
if (quote.length < MIN_WAIVER_QUOTE_CHARS || !/[\p{L}\p{N}]/u.test(quote)) {
|
|
73464
73475
|
throw new Error(`--waive-coverage needs the operator's own words as its value: at least ${MIN_WAIVER_QUOTE_CHARS} characters, with letters or digits in them.`);
|
|
@@ -73472,7 +73483,7 @@ async function recordCoverageWaiver(packageDir, operatorQuote, now = new Date, c
|
|
|
73472
73483
|
const uncovered = report.files.filter((entry) => entry.status === "uncovered").map((entry) => entry.file);
|
|
73473
73484
|
if (!report.tracked || uncovered.length === 0)
|
|
73474
73485
|
return;
|
|
73475
|
-
const waiver = { operatorQuote: quote, time: now.toISOString(), files: uncovered, ...confirmedVia === undefined ? {} : { confirmedVia } };
|
|
73486
|
+
const waiver = { operatorQuote: quote, time: now.toISOString(), files: uncovered, ...confirmedVia === undefined ? {} : { confirmedVia }, ...cardPressFields(press) };
|
|
73476
73487
|
await writeFileAtomic(file, `${JSON.stringify({ ...research, dispatch: { ...dispatch, coverageWaiver: waiver } }, null, 2)}
|
|
73477
73488
|
`);
|
|
73478
73489
|
return waiver;
|
|
@@ -73503,6 +73514,7 @@ var init_coverage_complete = __esm(() => {
|
|
|
73503
73514
|
init_chunks();
|
|
73504
73515
|
init_blast_radius();
|
|
73505
73516
|
init_coverage();
|
|
73517
|
+
init_types4();
|
|
73506
73518
|
init_json();
|
|
73507
73519
|
});
|
|
73508
73520
|
|
|
@@ -77566,7 +77578,9 @@ function flowStateSchema() {
|
|
|
77566
77578
|
description: "The completion target the token was minted for: `pr:<url>` or `merged`."
|
|
77567
77579
|
}
|
|
77568
77580
|
}
|
|
77569
|
-
}
|
|
77581
|
+
},
|
|
77582
|
+
pressedBy: { type: "integer", description: "Flow 464: on a remote-card token, the Telegram user id that pressed the card." },
|
|
77583
|
+
pressedAt: { type: "string", description: "Flow 464: on a remote-card token, the shell's clock when the press arrived." }
|
|
77570
77584
|
}
|
|
77571
77585
|
}
|
|
77572
77586
|
}
|
|
@@ -77828,7 +77842,9 @@ async function mintConfirmationToken(cwd, dir, binding, deps) {
|
|
|
77828
77842
|
mintedAt: mintedAt.toISOString(),
|
|
77829
77843
|
expiresAt: new Date(mintedAt.getTime() + CONFIRMATION_TOKEN_TTL_MS).toISOString(),
|
|
77830
77844
|
...binding.confirmedVia !== undefined ? { confirmedVia: binding.confirmedVia } : {},
|
|
77831
|
-
...binding.confirmationQuote !== undefined ? { confirmationQuote: binding.confirmationQuote } : {}
|
|
77845
|
+
...binding.confirmationQuote !== undefined ? { confirmationQuote: binding.confirmationQuote } : {},
|
|
77846
|
+
...binding.pressedBy !== undefined ? { pressedBy: binding.pressedBy } : {},
|
|
77847
|
+
...binding.pressedAt !== undefined ? { pressedAt: binding.pressedAt } : {}
|
|
77832
77848
|
};
|
|
77833
77849
|
await writeFileAtomic(confirmTokenPath2(cwd, dir), `${JSON.stringify(stored, null, 2)}
|
|
77834
77850
|
`);
|
|
@@ -77886,7 +77902,9 @@ async function consumeConfirmationToken(cwd, dir, stored, now) {
|
|
|
77886
77902
|
tokenRef: stored.tokenRef,
|
|
77887
77903
|
mintedAt: stored.mintedAt,
|
|
77888
77904
|
consumedAt,
|
|
77889
|
-
boundTo: { kind: stored.kind, acChecksum: stored.acChecksum, target: stored.target }
|
|
77905
|
+
boundTo: { kind: stored.kind, acChecksum: stored.acChecksum, target: stored.target },
|
|
77906
|
+
...stored.pressedBy !== undefined ? { pressedBy: stored.pressedBy } : {},
|
|
77907
|
+
...stored.pressedAt !== undefined ? { pressedAt: stored.pressedAt } : {}
|
|
77890
77908
|
};
|
|
77891
77909
|
}
|
|
77892
77910
|
function describeConfirmationFailure(reason, flowId) {
|
|
@@ -80494,7 +80512,12 @@ function createFlowService(deps) {
|
|
|
80494
80512
|
flowId: flow.id,
|
|
80495
80513
|
acChecksum: flow.acChecksum,
|
|
80496
80514
|
target: completionTarget({ merged: merged === true, prUrl: flow.pr.url }),
|
|
80497
|
-
...confirmation !== undefined ? {
|
|
80515
|
+
...confirmation !== undefined ? {
|
|
80516
|
+
confirmedVia: confirmation.confirmedVia,
|
|
80517
|
+
confirmationQuote: confirmation.quote,
|
|
80518
|
+
...confirmation.pressedBy !== undefined ? { pressedBy: confirmation.pressedBy } : {},
|
|
80519
|
+
...confirmation.pressedAt !== undefined ? { pressedAt: confirmation.pressedAt } : {}
|
|
80520
|
+
} : {}
|
|
80498
80521
|
}, { now: deps.now });
|
|
80499
80522
|
flow = await save(cwd, dir, flow, "confirmation-minted", `token ${stored.tokenRef}, expires ${stored.expiresAt}`);
|
|
80500
80523
|
return {
|
|
@@ -82187,6 +82210,7 @@ var init_unattended = __esm(() => {
|
|
|
82187
82210
|
"review_comments_reply",
|
|
82188
82211
|
"review_confirm_plan",
|
|
82189
82212
|
"review_decide_premise",
|
|
82213
|
+
"review_complete",
|
|
82190
82214
|
"flow_confirm",
|
|
82191
82215
|
"external_apply",
|
|
82192
82216
|
"learn_accept",
|
|
@@ -84680,6 +84704,10 @@ var init_schedule_verify = __esm(() => {
|
|
|
84680
84704
|
function cardSafe(text) {
|
|
84681
84705
|
return text.replace(/\u001b\[[0-?]*[ -/]*[@-~]/g, "").replace(/\u001b[\]P^_][\s\S]*?(?:\u0007|\u001b\\)/g, "").replace(/\r\n|\r|\n/g, " \u23CE ").replace(/[\u0000-\u0008\u000b-\u001f\u007f-\u009f\u200e\u200f\u202a-\u202e\u2066-\u2069]/g, "").replace(/\t/g, " ");
|
|
84682
84706
|
}
|
|
84707
|
+
function hiddenInCard(text) {
|
|
84708
|
+
return cardSafe(text) !== text || /[\p{Cf}\p{Zl}\p{Zp}]/u.test(text);
|
|
84709
|
+
}
|
|
84710
|
+
var HIDDEN_IN_CARD = "holds control, escape, newline, bidi or zero-width characters the card would hide or rewrite; send plain single-line text";
|
|
84683
84711
|
|
|
84684
84712
|
// src/trigger/schedules.ts
|
|
84685
84713
|
import { execFile as execFile4 } from "child_process";
|
|
@@ -85180,6 +85208,9 @@ function parseEvidenceLevel(report) {
|
|
|
85180
85208
|
}
|
|
85181
85209
|
return { ok: false, reason: "the evidence level must start with `static only` or `ran`" };
|
|
85182
85210
|
}
|
|
85211
|
+
function evidenceLevelLine(report) {
|
|
85212
|
+
return withoutFences(report).find((line) => LINE.test(line))?.trim();
|
|
85213
|
+
}
|
|
85183
85214
|
var REPORT_FIELD_EVIDENCE_LEVEL = "evidence-level", LINE, SEPARATORS, PLACEHOLDER;
|
|
85184
85215
|
var init_evidence_level = __esm(() => {
|
|
85185
85216
|
LINE = /^\s*(?:[-*+]\s+)?(?:\*\*|__)?evidence level\s*:?\s*(?:\*\*|__)?\s*:?\s*(.*)$/i;
|
|
@@ -87995,7 +88026,7 @@ async function waiveReadiness(packageDir, selectors, operatorQuote, options = {}
|
|
|
87995
88026
|
const manifest = JSON.parse(await readFile109(manifestPath, "utf8"));
|
|
87996
88027
|
const time = (options.now ?? new Date).toISOString();
|
|
87997
88028
|
const kept = (manifest.readiness_waivers ?? []).filter((waiver) => !ids.includes(waiver.item));
|
|
87998
|
-
const added = ids.map((item) => ({ item, operator_quote: quote, time, ...options.confirmedVia === undefined ? {} : { confirmedVia: options.confirmedVia } }));
|
|
88029
|
+
const added = ids.map((item) => ({ item, operator_quote: quote, time, ...options.confirmedVia === undefined ? {} : { confirmedVia: options.confirmedVia }, ...cardPressFields(options) }));
|
|
87999
88030
|
const all = [...kept, ...added].sort((a, b) => READINESS_ITEMS.findIndex((i) => i.id === a.item) - READINESS_ITEMS.findIndex((i) => i.id === b.item));
|
|
88000
88031
|
await writeFileAtomic(manifestPath, `${JSON.stringify({ ...manifest, readiness_waivers: all }, null, 2)}
|
|
88001
88032
|
`);
|
|
@@ -89927,6 +89958,18 @@ async function recordDispositions(packageDir, records) {
|
|
|
89927
89958
|
async function recordDispositionsLocked(packageDir, records) {
|
|
89928
89959
|
const findingsPath = path211.join(packageDir, "findings.json");
|
|
89929
89960
|
const findings = JSON.parse(await readFile110(findingsPath, "utf8"));
|
|
89961
|
+
const pending = await resolveDispositions(findings, records);
|
|
89962
|
+
for (const { finding, disposition } of pending) {
|
|
89963
|
+
finding.disposition = disposition;
|
|
89964
|
+
}
|
|
89965
|
+
await writeFileAtomic(findingsPath, `${JSON.stringify(findings, null, 2)}
|
|
89966
|
+
`);
|
|
89967
|
+
}
|
|
89968
|
+
async function checkDispositions(packageDir, records) {
|
|
89969
|
+
const findings = JSON.parse(await readFile110(path211.join(packageDir, "findings.json"), "utf8"));
|
|
89970
|
+
await resolveDispositions(findings, records);
|
|
89971
|
+
}
|
|
89972
|
+
async function resolveDispositions(findings, records) {
|
|
89930
89973
|
const schema = await loadSchema("review-finding");
|
|
89931
89974
|
const dispositionSchema = schema.properties?.["disposition"];
|
|
89932
89975
|
if (!dispositionSchema) {
|
|
@@ -89958,11 +90001,7 @@ async function recordDispositionsLocked(packageDir, records) {
|
|
|
89958
90001
|
}
|
|
89959
90002
|
pending.push({ finding, disposition });
|
|
89960
90003
|
}
|
|
89961
|
-
|
|
89962
|
-
finding.disposition = disposition;
|
|
89963
|
-
}
|
|
89964
|
-
await writeFileAtomic(findingsPath, `${JSON.stringify(findings, null, 2)}
|
|
89965
|
-
`);
|
|
90004
|
+
return pending;
|
|
89966
90005
|
}
|
|
89967
90006
|
function embeddedFence(tag) {
|
|
89968
90007
|
return new RegExp("^ {0,3}(`{3,}|~{3,})[^\\n]*\\bkeryx:" + tag + "\\b[^\\n]*$", "gm");
|
|
@@ -91073,22 +91112,25 @@ function sessionStartedAt(history) {
|
|
|
91073
91112
|
}
|
|
91074
91113
|
return Date.now();
|
|
91075
91114
|
}
|
|
91076
|
-
function
|
|
91115
|
+
function closeStep(card) {
|
|
91116
|
+
return card ? "call the `review_complete` card (the operator answers it; you never run `keryx review complete` yourself)" : "run `keryx review complete`";
|
|
91117
|
+
}
|
|
91118
|
+
function nextStep(open, card) {
|
|
91077
91119
|
if (open.packageDir === "") {
|
|
91078
|
-
return
|
|
91120
|
+
return `Next: to continue a review opened in an earlier session, name its package (\`keryx review status <review-id-or-path>\`); otherwise \`keryx review start --pr <n> --report <path>\` opens the package; then dispatch the reviewers per slice, save each result, \`keryx review ledger build <input-dir> --package <review-dir>\`, \`keryx review ingest ... --research <review-dir>/research.json\`, then ${closeStep(card)}.`;
|
|
91079
91121
|
}
|
|
91080
91122
|
if (open.blockers.some((b) => /research|dispatch/.test(b))) {
|
|
91081
|
-
return
|
|
91123
|
+
return `Next: dispatch the reviewers per slice, build the research ledger with \`keryx review ledger build <input-dir> --package <review-dir>\` (it names every gap), \`keryx review ingest ... --research <review-dir>/research.json\`, then ${closeStep(card)}.`;
|
|
91082
91124
|
}
|
|
91083
|
-
return
|
|
91125
|
+
return `Next: write or ingest the missing artifacts (\`keryx review ingest\`), then ${closeStep(card)}.`;
|
|
91084
91126
|
}
|
|
91085
91127
|
function nudgeLine(blocker) {
|
|
91086
91128
|
const flat = blocker.replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029]+/g, " ").trim();
|
|
91087
91129
|
return flat.length > MAX_NUDGE_BLOCKER_CHARS ? `${flat.slice(0, MAX_NUDGE_BLOCKER_CHARS)}\u2026` : flat;
|
|
91088
91130
|
}
|
|
91089
|
-
function describeBlockers(open) {
|
|
91131
|
+
function describeBlockers(open, card = true) {
|
|
91090
91132
|
if (open.blockers.length === 0)
|
|
91091
|
-
return
|
|
91133
|
+
return `- none listed; ${card ? "call the `review_complete` card" : "run `keryx review complete`"} to close the package`;
|
|
91092
91134
|
const shown = open.blockers.slice(0, MAX_NUDGE_BLOCKERS).map((b) => `- ${nudgeLine(b)}`);
|
|
91093
91135
|
const more = open.blockers.length - shown.length;
|
|
91094
91136
|
return [...shown, ...more > 0 ? [`- \u2026 and ${more} more (\`keryx review complete\` lists them all)`] : []].join(`
|
|
@@ -91098,22 +91140,23 @@ function decideReviewGate(input) {
|
|
|
91098
91140
|
const { open } = input;
|
|
91099
91141
|
if (open === null || !input.runSeen)
|
|
91100
91142
|
return { action: "accept" };
|
|
91143
|
+
const card = input.cardAvailable !== false;
|
|
91101
91144
|
const stalls = input.lastSignature === open.signature ? input.stalls + 1 : 0;
|
|
91102
91145
|
if (input.continues >= MAX_REVIEW_GATE_CONTINUES || stalls >= MAX_REVIEW_GATE_STALLS) {
|
|
91103
91146
|
return {
|
|
91104
91147
|
action: "stop",
|
|
91105
|
-
report: `Review ${open.reviewId} is not complete after ${input.continues} continue(s)` + `${stalls >= MAX_REVIEW_GATE_STALLS ? " and no new artifact in the last " + stalls : ""}. ` +
|
|
91106
|
-
${describeBlockers(open)}`
|
|
91148
|
+
report: `Review ${open.reviewId} is not complete after ${input.continues} continue(s)` + `${stalls >= MAX_REVIEW_GATE_STALLS ? " and no new artifact in the last " + stalls : ""}. ` + `${card ? "The review_complete card" : "`keryx review complete`"} would still be refused on:
|
|
91149
|
+
${describeBlockers(open, card)}`
|
|
91107
91150
|
};
|
|
91108
91151
|
}
|
|
91109
91152
|
return {
|
|
91110
91153
|
action: "continue",
|
|
91111
91154
|
stalls,
|
|
91112
91155
|
signature: open.signature,
|
|
91113
|
-
message: `The managed review ${open.reviewId} is not finished: it is finished when
|
|
91114
|
-
${describeBlockers(open)}
|
|
91115
|
-
` + `${nextStep(open)}
|
|
91116
|
-
` + "Do the next missing step now. Step 1 (Build Review Context Pack) has no CLI command; it is a prose step, so mark it done and move on. " + "A command that is refused is not a blocker: run `keryx review --help` for the accepted usage and retry with the right flags. " + "A plan item you marked blocked for such a reason is not blocked: mark it in_progress. " + "A tool loop guard or an empty child result is not a reason to ask the operator to resend the request or to continue: change the arguments, retry the failed pass on a smaller slice, and keep going. " +
|
|
91156
|
+
message: `The managed review ${open.reviewId} is not finished: it is finished when the package is closed (${card ? "the operator answers the `review_complete` card and its gates pass" : "`keryx review complete` accepts it"}), not when you stop writing. ` + `It still refuses on:
|
|
91157
|
+
${describeBlockers(open, card)}
|
|
91158
|
+
` + `${nextStep(open, card)}
|
|
91159
|
+
` + "Do the next missing step now. Step 1 (Build Review Context Pack) has no CLI command; it is a prose step, so mark it done and move on. " + "A command that is refused is not a blocker: run `keryx review --help` for the accepted usage and retry with the right flags. " + "A plan item you marked blocked for such a reason is not blocked: mark it in_progress. " + "A tool loop guard or an empty child result is not a reason to ask the operator to resend the request or to continue: change the arguments, retry the failed pass on a smaller slice, and keep going. " + `Stop only after ${card ? "the `review_complete` card has" : "`keryx review complete` has"} closed the package, or report the one real blocker with its evidence.`
|
|
91117
91160
|
};
|
|
91118
91161
|
}
|
|
91119
91162
|
var MAX_REVIEW_GATE_CONTINUES = 20, MAX_REVIEW_GATE_STALLS = 3, MAX_HASHED_FILES = 4096, RACY_WINDOW_MS = 2000, fileHashes, METAPROJECT = ".metaproject", REVIEWS = "reviews", PREFIX_DELIMITERS, MAX_PREFIX_CHARS = 1024, MAX_NAME_CHARS = 255, MAX_SESSION_REVIEW_ROOTS = 8, MAX_SESSION_REVIEW_DIRS = 32, MAX_SESSION_REVIEW_IDS = 32, MAX_PENDING_CALLS = 64, SAFE_REVIEW_ID, isPathDelimiter = (ch) => PREFIX_DELIMITERS.has(ch) || /\s/.test(ch), isSeparator = (ch) => ch === "/" || ch === "\\", OPENING_SUBCOMMANDS, PACKAGE_ARG_SUBCOMMANDS, VALUELESS_FLAGS, OWN_OUTPUT_LINE, KERYX_PACKAGE, PIPE_FILTERS, NO_VALUE_OPTIONS, HEAD_TAIL_VALUE_OPTIONS, GREP_VALUE_OPTIONS, READER_VALUE_OPTIONS, GREP_PATTERN_OPTIONS, GREP_TREE_OPTION, KERYX_PACKAGE_NAMES, nextIs = (line, at, chars) => at + 1 < line.length && chars.includes(line[at + 1]), baseName2 = (word) => path212.posix.basename(word.replace(/\\/g, "/")), trackers, SCAN_CONCURRENCY = 16, NO_PACKAGE_BLOCKER = "no review package this session opened or named: `keryx review start` has not opened one, and no `keryx review` command in this session names an open package by its path or review id", MAX_NUDGE_BLOCKERS = 20, MAX_NUDGE_BLOCKER_CHARS = 300;
|
|
@@ -105865,7 +105908,8 @@ ${line}`;
|
|
|
105865
105908
|
runSeen,
|
|
105866
105909
|
continues: reviewGateContinues,
|
|
105867
105910
|
stalls: reviewGateStalls,
|
|
105868
|
-
lastSignature: lastReviewGateSignature
|
|
105911
|
+
lastSignature: lastReviewGateSignature,
|
|
105912
|
+
cardAvailable: deps.tools.some((tool) => tool.definition.name === "review_complete")
|
|
105869
105913
|
});
|
|
105870
105914
|
if (gate.action === "continue") {
|
|
105871
105915
|
reviewGateContinues += 1;
|
|
@@ -107270,6 +107314,8 @@ async function executeCallUnbounded(call, toolByName, requestApproval, permissio
|
|
|
107270
107314
|
const hookInteractive = hooks?.runtime.interactive ?? true;
|
|
107271
107315
|
let confirmationToken;
|
|
107272
107316
|
let confirmationVia;
|
|
107317
|
+
let confirmationPressedBy;
|
|
107318
|
+
let confirmationPressedAt;
|
|
107273
107319
|
if (tool.confirmation !== undefined && risk !== "write") {
|
|
107274
107320
|
return { output: `tool "${call.name}" needs operator confirmation but is not a write tool; refused`, isError: true };
|
|
107275
107321
|
}
|
|
@@ -107458,8 +107504,12 @@ async function executeCallUnbounded(call, toolByName, requestApproval, permissio
|
|
|
107458
107504
|
isError: true
|
|
107459
107505
|
};
|
|
107460
107506
|
}
|
|
107461
|
-
if (card !== undefined)
|
|
107462
|
-
|
|
107507
|
+
if (card !== undefined) {
|
|
107508
|
+
const remote = typeof response === "object" && response.via === "remote" ? response : undefined;
|
|
107509
|
+
confirmationVia = remote !== undefined ? "remote" : "local";
|
|
107510
|
+
confirmationPressedBy = remote?.pressedBy;
|
|
107511
|
+
confirmationPressedAt = remote?.pressedAt;
|
|
107512
|
+
}
|
|
107463
107513
|
if (hookResult?.decisions.some((d) => d.hookId === IMPACT_EVIDENCE_HOOK_ID && d.decision === "ask")) {
|
|
107464
107514
|
hooks?.runtime.acknowledgeImpactEvidence?.(extractFilePathsFromToolInput(input));
|
|
107465
107515
|
}
|
|
@@ -107501,6 +107551,8 @@ async function executeCallUnbounded(call, toolByName, requestApproval, permissio
|
|
|
107501
107551
|
...signal !== undefined ? { signal } : {},
|
|
107502
107552
|
...confirmationToken !== undefined ? { confirmationToken } : {},
|
|
107503
107553
|
...confirmationToken !== undefined && confirmationVia !== undefined ? { confirmationVia } : {},
|
|
107554
|
+
...confirmationToken !== undefined && confirmationVia === "remote" && confirmationPressedBy !== undefined ? { confirmationPressedBy } : {},
|
|
107555
|
+
...confirmationToken !== undefined && confirmationVia === "remote" && confirmationPressedAt !== undefined ? { confirmationPressedAt } : {},
|
|
107504
107556
|
...untrustedContentSeen ? { untrustedContentSeen: true } : {}
|
|
107505
107557
|
});
|
|
107506
107558
|
if (review !== undefined && call.name === "shell_exec" && typeof input.command === "string") {
|
|
@@ -115600,7 +115652,7 @@ async function landWriteRun(input, deps = {}) {
|
|
|
115600
115652
|
decision: "landed",
|
|
115601
115653
|
branch,
|
|
115602
115654
|
commit,
|
|
115603
|
-
...input.confirmation !== undefined ? { confirmedVia: input.confirmation.confirmedVia, quote: input.confirmation.quote } : {}
|
|
115655
|
+
...input.confirmation !== undefined ? { confirmedVia: input.confirmation.confirmedVia, quote: input.confirmation.quote, ...input.confirmation.pressedBy !== undefined ? { pressedBy: input.confirmation.pressedBy } : {}, ...input.confirmation.pressedAt !== undefined ? { pressedAt: input.confirmation.pressedAt } : {} } : {}
|
|
115604
115656
|
}));
|
|
115605
115657
|
if (!won) {
|
|
115606
115658
|
await git(["branch", "-D", branch], repo).catch(() => {
|
|
@@ -122586,7 +122638,7 @@ class RemoteClient {
|
|
|
122586
122638
|
}
|
|
122587
122639
|
async askApproval(prompt, timeoutMs, options = {}) {
|
|
122588
122640
|
if (!this.connected || options.signal?.aborted === true) {
|
|
122589
|
-
return { decision: "deny" };
|
|
122641
|
+
return { decision: "deny", undelivered: true };
|
|
122590
122642
|
}
|
|
122591
122643
|
const body = {
|
|
122592
122644
|
sessionId: this.options.sessionId,
|
|
@@ -122598,15 +122650,15 @@ class RemoteClient {
|
|
|
122598
122650
|
try {
|
|
122599
122651
|
const response = await this.post("approval", body);
|
|
122600
122652
|
if (!response.ok) {
|
|
122601
|
-
return { decision: "deny" };
|
|
122653
|
+
return { decision: "deny", undelivered: true };
|
|
122602
122654
|
}
|
|
122603
122655
|
const id = (await response.json())?.approvalId;
|
|
122604
122656
|
if (typeof id !== "string" || id.length === 0) {
|
|
122605
|
-
return { decision: "deny" };
|
|
122657
|
+
return { decision: "deny", undelivered: true };
|
|
122606
122658
|
}
|
|
122607
122659
|
approvalId = id;
|
|
122608
122660
|
} catch {
|
|
122609
|
-
return { decision: "deny" };
|
|
122661
|
+
return { decision: "deny", undelivered: true };
|
|
122610
122662
|
}
|
|
122611
122663
|
const early = this.earlyDecisions.get(approvalId);
|
|
122612
122664
|
if (early !== undefined) {
|
|
@@ -124734,13 +124786,13 @@ function sha25610(value) {
|
|
|
124734
124786
|
function contentDigest(value) {
|
|
124735
124787
|
return sha25610(JSON.stringify(value ?? null));
|
|
124736
124788
|
}
|
|
124737
|
-
function limited(lines) {
|
|
124738
|
-
if (lines.length <= MAX_LISTED)
|
|
124739
|
-
return [...lines];
|
|
124740
|
-
return [...lines.slice(0, MAX_LISTED), ` ... and ${lines.length - MAX_LISTED} more`];
|
|
124741
|
-
}
|
|
124742
124789
|
function record4(confirmation) {
|
|
124743
|
-
return {
|
|
124790
|
+
return {
|
|
124791
|
+
confirmedVia: confirmation.confirmedVia,
|
|
124792
|
+
quote: confirmation.quote,
|
|
124793
|
+
...confirmation.pressedBy === undefined ? {} : { pressedBy: confirmation.pressedBy },
|
|
124794
|
+
...confirmation.pressedAt === undefined ? {} : { pressedAt: confirmation.pressedAt }
|
|
124795
|
+
};
|
|
124744
124796
|
}
|
|
124745
124797
|
async function captureConsole(fn) {
|
|
124746
124798
|
const lines = [];
|
|
@@ -124904,8 +124956,8 @@ function externalApplySpec(seams) {
|
|
|
124904
124956
|
`branch: ${branch} (new, local; cut from base commit ${cardSafe(run.baseCommit)})`,
|
|
124905
124957
|
`patch hash (sha256): ${hash}`,
|
|
124906
124958
|
`changed files: ${run.files.length}`,
|
|
124907
|
-
...
|
|
124908
|
-
...run.flaggedPaths.length > 0 ? [`FLAGGED PATHS (${run.flaggedPaths.length}) - repo plumbing, CI, hooks or agent config:`, ...
|
|
124959
|
+
...files,
|
|
124960
|
+
...run.flaggedPaths.length > 0 ? [`FLAGGED PATHS (${run.flaggedPaths.length}) - repo plumbing, CI, hooks or agent config:`, ...run.flaggedPaths.map((p) => ` ! ${cardSafe(p)}`)] : [],
|
|
124909
124961
|
"effect: your current branch, HEAD, index and working tree are never touched"
|
|
124910
124962
|
],
|
|
124911
124963
|
subject: `${branch} @ sha256:${hash}`,
|
|
@@ -125270,7 +125322,7 @@ function otherConfirmSpecs(seams = {}) {
|
|
|
125270
125322
|
scheduleSpec("remove", seams)
|
|
125271
125323
|
];
|
|
125272
125324
|
}
|
|
125273
|
-
var
|
|
125325
|
+
var SCOPES2, SCHEDULE_DESCRIPTIONS;
|
|
125274
125326
|
var init_operator_confirm_other = __esm(() => {
|
|
125275
125327
|
init_service11();
|
|
125276
125328
|
init_record2();
|
|
@@ -125287,9 +125339,101 @@ var init_operator_confirm_other = __esm(() => {
|
|
|
125287
125339
|
};
|
|
125288
125340
|
});
|
|
125289
125341
|
|
|
125342
|
+
// src/review/routing-check.ts
|
|
125343
|
+
import { readFileSync as readFileSync33 } from "fs";
|
|
125344
|
+
import path286 from "path";
|
|
125345
|
+
function narrowedSlices(manifest, readText, fix) {
|
|
125346
|
+
const originals = manifest.slices.filter((entry) => entry.retryOf === undefined && Array.isArray(entry.roles));
|
|
125347
|
+
const changedFiles = [...new Set(manifest.slices.filter((entry) => entry.retryOf === undefined).flatMap((entry) => entry.files))];
|
|
125348
|
+
const narrowed = [];
|
|
125349
|
+
const unreadable = [];
|
|
125350
|
+
for (const entry of originals) {
|
|
125351
|
+
const text = readText(entry);
|
|
125352
|
+
if (text === undefined) {
|
|
125353
|
+
unreadable.push(entry.id);
|
|
125354
|
+
continue;
|
|
125355
|
+
}
|
|
125356
|
+
const needed = routeSliceRegions(entry.id, parseScopedDiff(text), { changedFiles });
|
|
125357
|
+
const backedFix = fixClaimBacked(entry, fix, text);
|
|
125358
|
+
const backedSecondLook = secondLookClaimBacked(entry, fix, text);
|
|
125359
|
+
const roles = backedSecondLook ? [...SECOND_LOOK_ROLES] : backedFix ? expectedFixRoles(entry, fix, text) : needed.roles;
|
|
125360
|
+
const recorded = new Set(entry.roles ?? []);
|
|
125361
|
+
const missingRoles = roles.filter((role) => !recorded.has(role)).sort();
|
|
125362
|
+
const shallow = (backedFix || backedSecondLook || needed.depth === "deep") && entry.depth !== "deep";
|
|
125363
|
+
if (missingRoles.length > 0 || shallow) {
|
|
125364
|
+
narrowed.push({ id: entry.id, missingRoles, ...shallow ? { depth: { recorded: entry.depth ?? "unset", needed: "deep" } } : {} });
|
|
125365
|
+
}
|
|
125366
|
+
}
|
|
125367
|
+
return { narrowed, unreadable };
|
|
125368
|
+
}
|
|
125369
|
+
function sliceTextReader(manifestDir) {
|
|
125370
|
+
return (entry) => {
|
|
125371
|
+
try {
|
|
125372
|
+
return readFileSync33(path286.resolve(manifestDir, entry.path), "utf8");
|
|
125373
|
+
} catch {
|
|
125374
|
+
return;
|
|
125375
|
+
}
|
|
125376
|
+
};
|
|
125377
|
+
}
|
|
125378
|
+
function packageRoutingRefusal(packageDir, projectRoot, options = {}) {
|
|
125379
|
+
const shared = path286.join(projectRoot, ".metaproject", "data", "review", "slices");
|
|
125380
|
+
const own = path286.join(packageDir, "slices");
|
|
125381
|
+
for (const [file, dirs] of [[path286.join(own, "manifest.json"), [own]], [path286.join(packageDir, "slice-manifest.json"), [own, shared]]]) {
|
|
125382
|
+
let manifest;
|
|
125383
|
+
try {
|
|
125384
|
+
const parsed = JSON.parse(readFileSync33(file, "utf8"));
|
|
125385
|
+
manifest = Array.isArray(parsed.slices) ? { slices: parsed.slices } : undefined;
|
|
125386
|
+
} catch {
|
|
125387
|
+
manifest = undefined;
|
|
125388
|
+
}
|
|
125389
|
+
if (manifest === undefined)
|
|
125390
|
+
continue;
|
|
125391
|
+
const readers = dirs.map((dir) => sliceTextReader(dir));
|
|
125392
|
+
const { narrowed, unreadable } = narrowedSlices(manifest, (entry) => readers.map((read) => read(entry)).find((text) => text !== undefined), fixContextOf(packageDir));
|
|
125393
|
+
const label = path286.relative(projectRoot, file) || file;
|
|
125394
|
+
const refusal = routingRefusal(label, narrowed);
|
|
125395
|
+
if (refusal !== undefined || options.refuseUnreadable !== true || unreadable.length === 0)
|
|
125396
|
+
return refusal;
|
|
125397
|
+
return `${label} lists ${unreadable.length} slice(s) whose text is missing (${unreadable.slice(0, 8).join(", ")}${unreadable.length > 8 ? ", \u2026" : ""}), so their routing cannot be checked against their content. ` + "Restore the slice files next to the manifest, or re-run `keryx review slice` and the round";
|
|
125398
|
+
}
|
|
125399
|
+
return;
|
|
125400
|
+
}
|
|
125401
|
+
function routingRefusal(manifestLabel, narrowed) {
|
|
125402
|
+
if (narrowed.length === 0)
|
|
125403
|
+
return;
|
|
125404
|
+
const lines = narrowed.slice(0, 8).map((entry) => `${entry.id}: ${[
|
|
125405
|
+
entry.missingRoles.length > 0 ? `roles ${entry.missingRoles.join(", ")} are missing` : undefined,
|
|
125406
|
+
entry.depth !== undefined ? `recorded ${entry.depth.recorded}, its content needs deep` : undefined
|
|
125407
|
+
].filter(Boolean).join("; ")}`);
|
|
125408
|
+
return `${manifestLabel} routes ${narrowed.length} slice(s) narrower than their content needs (${lines.join(" | ")}${narrowed.length > 8 ? ` | \u2026 and ${narrowed.length - 8} more` : ""}). ` + "Routing is recomputed from each slice's text and the manifest may only be wider. Re-run `keryx review slice` to route the slices again, and do not edit roles or depth by hand.";
|
|
125409
|
+
}
|
|
125410
|
+
var init_routing_check = __esm(() => {
|
|
125411
|
+
init_slice();
|
|
125412
|
+
init_slice_routing();
|
|
125413
|
+
init_round_split();
|
|
125414
|
+
});
|
|
125415
|
+
|
|
125416
|
+
// src/review/complete-gated.ts
|
|
125417
|
+
async function completeReviewGated(cwd, ref, options) {
|
|
125418
|
+
const packageDir = await resolveReviewPackagePath(cwd, ref);
|
|
125419
|
+
const profileMissing = runProfileRefusal(packageDir);
|
|
125420
|
+
if (profileMissing !== undefined)
|
|
125421
|
+
throw new Error(`review complete refused: ${profileMissing}`);
|
|
125422
|
+
const narrowedRouting = packageRoutingRefusal(packageDir, resolveProjectRoot2(cwd), { refuseUnreadable: true });
|
|
125423
|
+
if (narrowedRouting !== undefined)
|
|
125424
|
+
throw new Error(`review complete refused: ${narrowedRouting}`);
|
|
125425
|
+
return completeManagedReview(cwd, ref, options);
|
|
125426
|
+
}
|
|
125427
|
+
var init_complete_gated = __esm(() => {
|
|
125428
|
+
init_contained_path();
|
|
125429
|
+
init_managed();
|
|
125430
|
+
init_run_profile();
|
|
125431
|
+
init_routing_check();
|
|
125432
|
+
});
|
|
125433
|
+
|
|
125290
125434
|
// src/review/publish.ts
|
|
125291
125435
|
import { readFile as readFile131 } from "fs/promises";
|
|
125292
|
-
import
|
|
125436
|
+
import path287 from "path";
|
|
125293
125437
|
function reviewEvent(value) {
|
|
125294
125438
|
const wanted = (value ?? "COMMENT").trim().toUpperCase();
|
|
125295
125439
|
const found = REVIEW_EVENTS.find((event) => event === wanted);
|
|
@@ -125385,7 +125529,7 @@ function buildRoundBlock(input) {
|
|
|
125385
125529
|
return { ...base, findings: kept, ...kept.length < listed.length ? { truncated: listed.length - kept.length } : {} };
|
|
125386
125530
|
}
|
|
125387
125531
|
async function publishReview(packageDir, options) {
|
|
125388
|
-
const manifest = await readJsonFileOr(
|
|
125532
|
+
const manifest = await readJsonFileOr(path287.join(packageDir, "manifest.json"), undefined);
|
|
125389
125533
|
if (manifest === undefined)
|
|
125390
125534
|
throw new Error(`Managed review package has no readable manifest.json: ${packageDir}`);
|
|
125391
125535
|
if (!isPrBound(manifest.target)) {
|
|
@@ -125400,7 +125544,7 @@ async function publishReview(packageDir, options) {
|
|
|
125400
125544
|
if (partial && event === "APPROVE") {
|
|
125401
125545
|
throw new Error(`A partial review cannot approve: ${failing.length} readiness item(s) are failing (${failing.map((item) => item.id).join(", ")}). Use --event COMMENT or REQUEST_CHANGES, or repair the items.`);
|
|
125402
125546
|
}
|
|
125403
|
-
const report = (await readFile131(
|
|
125547
|
+
const report = (await readFile131(path287.join(packageDir, "report.md"), "utf8").catch(() => "")).trim();
|
|
125404
125548
|
if (report === "")
|
|
125405
125549
|
throw new Error(`Review ${manifest.reviewId} has no report.md body to publish.`);
|
|
125406
125550
|
const head = `${partial ? `${partialPublishHeader(readiness).join(`
|
|
@@ -125411,7 +125555,7 @@ async function publishReview(packageDir, options) {
|
|
|
125411
125555
|
const minors = minorSection((await computeVerdict(packageDir)).minors, report, PUBLISHED_BODY_LIMIT - head.length - foot.length - ROUND_BLOCK_LIMIT);
|
|
125412
125556
|
const visible = `${head}${minors}${foot}`;
|
|
125413
125557
|
const reviewedHead = manifest.target.head ?? null;
|
|
125414
|
-
const findings = await readJsonFileOr(
|
|
125558
|
+
const findings = await readJsonFileOr(path287.join(packageDir, "findings.json"), []);
|
|
125415
125559
|
const roundBlock = buildRoundBlock({ body: visible, round: readRoundNumber(packageDir) ?? 1, reviewId: manifest.reviewId, reviewedHead, findings: Array.isArray(findings) ? findings : [] });
|
|
125416
125560
|
const body = `${visible}
|
|
125417
125561
|
${renderRoundBlock(roundBlock)}
|
|
@@ -125421,7 +125565,7 @@ ${renderRoundBlock(roundBlock)}
|
|
|
125421
125565
|
const result = await options.post(manifest.target.ref, event, body);
|
|
125422
125566
|
if (!result.ok)
|
|
125423
125567
|
throw new Error(`GitHub refused the review: ${result.detail}`);
|
|
125424
|
-
await writeFileAtomic(
|
|
125568
|
+
await writeFileAtomic(path287.join(packageDir, "published.json"), `${JSON.stringify({ pr: manifest.target.ref, reviewId: manifest.reviewId, head: reviewedHead, round: roundBlock.round, event, partial, failing: failing.map((item) => item.id), time: (options.now?.() ?? new Date).toISOString(), ...options.confirmedVia === undefined ? {} : { confirmedVia: options.confirmedVia }, ...cardPressFields(options) }, null, 2)}
|
|
125425
125569
|
`);
|
|
125426
125570
|
const registered = recordFinishedRound(packageDir, "publish");
|
|
125427
125571
|
return { readiness, body, event, partial, posted: true, ...registered.error === undefined ? {} : { registryWarning: `round not recorded in the local round registry: ${registered.error}` } };
|
|
@@ -125437,6 +125581,7 @@ var init_publish = __esm(() => {
|
|
|
125437
125581
|
init_round_registry();
|
|
125438
125582
|
init_verdict();
|
|
125439
125583
|
init_readiness();
|
|
125584
|
+
init_types4();
|
|
125440
125585
|
REVIEW_EVENTS = ["COMMENT", "REQUEST_CHANGES", "APPROVE"];
|
|
125441
125586
|
});
|
|
125442
125587
|
|
|
@@ -125444,16 +125589,16 @@ var init_publish = __esm(() => {
|
|
|
125444
125589
|
import { createHash as createHash81 } from "crypto";
|
|
125445
125590
|
import { existsSync as existsSync56, realpathSync as realpathSync19 } from "fs";
|
|
125446
125591
|
import { readFile as readFile132 } from "fs/promises";
|
|
125447
|
-
import
|
|
125592
|
+
import path288 from "path";
|
|
125448
125593
|
async function findHeldFinding(packageDir, ref) {
|
|
125449
125594
|
const wanted = ref.trim();
|
|
125450
125595
|
if (wanted === "")
|
|
125451
125596
|
return { ok: false, refusal: "a finding id is required" };
|
|
125452
|
-
const findings = await readJsonFileOr(
|
|
125597
|
+
const findings = await readJsonFileOr(path288.join(packageDir, FINDINGS_FILE), undefined);
|
|
125453
125598
|
const records = Array.isArray(findings) ? findings.map(obj).filter((entry) => entry !== undefined) : [];
|
|
125454
125599
|
const named = records.filter((record) => str3(record["global_id"]) === wanted || str3(record["id"]) === wanted);
|
|
125455
125600
|
if (named.length === 0)
|
|
125456
|
-
return { ok: false, refusal: `No finding "${wanted}" in ${
|
|
125601
|
+
return { ok: false, refusal: `No finding "${wanted}" in ${path288.basename(packageDir)}/${FINDINGS_FILE}. Name a finding by its global_id or id.` };
|
|
125457
125602
|
const keys = new Set(named.map((record) => str3(record["global_id"]) ?? str3(record["id"]) ?? wanted));
|
|
125458
125603
|
if (keys.size > 1)
|
|
125459
125604
|
return { ok: false, refusal: `"${wanted}" names ${keys.size} findings (${[...keys].join(", ")}); name one by its global_id.` };
|
|
@@ -125482,13 +125627,13 @@ async function carryPremiseDecisions(packageDir, previousDir, carriedIds, carrie
|
|
|
125482
125627
|
if (carriedIds.length === 0 || !isReviewPackageDir(previousDir, options.projectRoot))
|
|
125483
125628
|
return [];
|
|
125484
125629
|
const realPrevious = realpathSync19(previousDir);
|
|
125485
|
-
const realPackage = existsSync56(packageDir) ? realpathSync19(packageDir) :
|
|
125486
|
-
if (realPrevious === realPackage || realPackage.startsWith(`${realPrevious}${
|
|
125630
|
+
const realPackage = existsSync56(packageDir) ? realpathSync19(packageDir) : path288.resolve(packageDir);
|
|
125631
|
+
if (realPrevious === realPackage || realPackage.startsWith(`${realPrevious}${path288.sep}`))
|
|
125487
125632
|
return [];
|
|
125488
125633
|
const previous = await readPremiseDecisions(realPrevious);
|
|
125489
125634
|
if (previous.length === 0)
|
|
125490
125635
|
return [];
|
|
125491
|
-
const findings = await readJsonFileOr(
|
|
125636
|
+
const findings = await readJsonFileOr(path288.join(realPrevious, FINDINGS_FILE), undefined);
|
|
125492
125637
|
const records = Array.isArray(findings) ? findings.map(obj).filter((entry) => entry !== undefined) : [];
|
|
125493
125638
|
const have = await readPremiseDecisions(packageDir);
|
|
125494
125639
|
const carried = [];
|
|
@@ -125510,13 +125655,13 @@ async function carryPremiseDecisions(packageDir, previousDir, carriedIds, carrie
|
|
|
125510
125655
|
return carried;
|
|
125511
125656
|
}
|
|
125512
125657
|
async function answerPremiseQuestion(packageDir, input, registry = {}) {
|
|
125513
|
-
const { decision, applied: vouched } = await recordPremiseDecisionAfter(packageDir, { finding: input.finding.key, answer: input.answer, reason: input.reason, quote: input.quote, at: input.at, confirmedVia: input.confirmedVia, subject: input.finding.subject }, (kept) => applyAnswerToFindings(packageDir, kept, registry));
|
|
125658
|
+
const { decision, applied: vouched } = await recordPremiseDecisionAfter(packageDir, { finding: input.finding.key, answer: input.answer, reason: input.reason, quote: input.quote, at: input.at, confirmedVia: input.confirmedVia, ...cardPressFields(input), subject: input.finding.subject }, (kept) => applyAnswerToFindings(packageDir, kept, registry));
|
|
125514
125659
|
if (vouched === true)
|
|
125515
125660
|
reRecordRegistry(packageDir, registry);
|
|
125516
125661
|
return decision;
|
|
125517
125662
|
}
|
|
125518
125663
|
async function applyAnswerToFindings(packageDir, kept, registry) {
|
|
125519
|
-
const file =
|
|
125664
|
+
const file = path288.join(packageDir, FINDINGS_FILE);
|
|
125520
125665
|
const before = await readFile132(file, "utf8").catch(() => {
|
|
125521
125666
|
return;
|
|
125522
125667
|
});
|
|
@@ -125561,14 +125706,15 @@ var init_premise_question = __esm(() => {
|
|
|
125561
125706
|
init_json();
|
|
125562
125707
|
init_operator_decisions();
|
|
125563
125708
|
init_package_location();
|
|
125709
|
+
init_types4();
|
|
125564
125710
|
init_premise();
|
|
125565
125711
|
init_round_registry();
|
|
125566
125712
|
});
|
|
125567
125713
|
|
|
125568
125714
|
// src/review/estimate.ts
|
|
125569
125715
|
import { createHash as createHash82 } from "crypto";
|
|
125570
|
-
import { existsSync as existsSync57, readFileSync as
|
|
125571
|
-
import
|
|
125716
|
+
import { existsSync as existsSync57, readFileSync as readFileSync34 } from "fs";
|
|
125717
|
+
import path289 from "path";
|
|
125572
125718
|
function planKeyOf(estimate) {
|
|
125573
125719
|
const roles = [...estimate.perRole].sort((a, b) => a.role.localeCompare(b.role)).map((entry) => ({ role: entry.role, waves: entry.waves }));
|
|
125574
125720
|
const slices = [...estimate.slicePlan].sort((a, b) => a.id.localeCompare(b.id)).map((entry) => ({ id: entry.id, roles: [...entry.roles].sort(), depth: entry.depth ?? null }));
|
|
@@ -125648,7 +125794,7 @@ function estimateRound(roles, manifest, depth = "unrecorded", thresholds = { max
|
|
|
125648
125794
|
}
|
|
125649
125795
|
async function readPlanThresholds(projectRoot) {
|
|
125650
125796
|
const defaults = { maxDispatches: DEFAULT_MAX_DISPATCHES, maxMinutes: DEFAULT_MAX_MINUTES, source: "default" };
|
|
125651
|
-
const file =
|
|
125797
|
+
const file = path289.join(projectRoot, "routing.config.json");
|
|
125652
125798
|
if (!await pathExists(file))
|
|
125653
125799
|
return defaults;
|
|
125654
125800
|
const read = await readJsonObjectFile(file);
|
|
@@ -125672,7 +125818,7 @@ function isPlanConfirmation(value) {
|
|
|
125672
125818
|
}
|
|
125673
125819
|
function readPlanConfirmation(packageDir) {
|
|
125674
125820
|
try {
|
|
125675
|
-
const parsed = JSON.parse(
|
|
125821
|
+
const parsed = JSON.parse(readFileSync34(path289.join(packageDir, PLAN_CONFIRMATION_FILE), "utf8"));
|
|
125676
125822
|
return isPlanConfirmation(parsed) ? parsed : undefined;
|
|
125677
125823
|
} catch {
|
|
125678
125824
|
return;
|
|
@@ -125687,7 +125833,7 @@ function planConfirmationFor(estimate, quote, confirmedVia, now = new Date) {
|
|
|
125687
125833
|
return { planKey: planKeyOf(estimate), dispatches: estimate.dispatches, minutes: [estimate.minutes.min, estimate.minutes.max], quote: quote.trim(), confirmedVia, at: now.toISOString() };
|
|
125688
125834
|
}
|
|
125689
125835
|
async function writePlanConfirmation(packageDir, confirmation) {
|
|
125690
|
-
const file =
|
|
125836
|
+
const file = path289.join(packageDir, PLAN_CONFIRMATION_FILE);
|
|
125691
125837
|
await writeFileAtomic(file, `${JSON.stringify(confirmation, null, 2)}
|
|
125692
125838
|
`);
|
|
125693
125839
|
return file;
|
|
@@ -125712,9 +125858,9 @@ async function packagePlanGate(packageDir, options = {}) {
|
|
|
125712
125858
|
}
|
|
125713
125859
|
function manifestCandidates(packageDir, root) {
|
|
125714
125860
|
return [
|
|
125715
|
-
|
|
125716
|
-
|
|
125717
|
-
|
|
125861
|
+
path289.join(packageDir, "slices", "manifest.json"),
|
|
125862
|
+
path289.join(packageDir, "slice-manifest.json"),
|
|
125863
|
+
path289.join(root, ".metaproject", "data", "review", "slices", "manifest.json")
|
|
125718
125864
|
];
|
|
125719
125865
|
}
|
|
125720
125866
|
async function readManifest3(candidates) {
|
|
@@ -125737,15 +125883,15 @@ async function estimatePackage(packageDir, options = {}) {
|
|
|
125737
125883
|
depth: recorded?.depth
|
|
125738
125884
|
});
|
|
125739
125885
|
const read = await readManifest3(options.manifest === undefined ? manifestCandidates(packageDir, root) : [options.manifest]);
|
|
125740
|
-
const found = options.manifestValue === undefined ? read : { manifest: options.manifestValue, file: read?.file ?? options.manifest ??
|
|
125886
|
+
const found = options.manifestValue === undefined ? read : { manifest: options.manifestValue, file: read?.file ?? options.manifest ?? path289.join(packageDir, "slices", "manifest.json") };
|
|
125741
125887
|
if (options.manifest !== undefined && found === undefined)
|
|
125742
125888
|
throw new Error(`${options.manifest} is not a slice manifest (no readable JSON with a slices array); run \`keryx review slice\` first`);
|
|
125743
125889
|
const problem = found === undefined ? undefined : manifestSizeProblem(found.manifest);
|
|
125744
125890
|
if (problem !== undefined)
|
|
125745
|
-
throw new Error(`${
|
|
125891
|
+
throw new Error(`${path289.relative(root, found.file)}: ${problem}`);
|
|
125746
125892
|
const thresholds = await readPlanThresholds(root);
|
|
125747
125893
|
const roles = [...new Set([...set.required, ...found === undefined ? [] : manifestRosterRoles(found.manifest)])].sort();
|
|
125748
|
-
return { ...estimateRound(roles, found?.manifest, recorded?.depth ?? "unrecorded", thresholds), manifest: found === undefined ? undefined :
|
|
125894
|
+
return { ...estimateRound(roles, found?.manifest, recorded?.depth ?? "unrecorded", thresholds), manifest: found === undefined ? undefined : path289.relative(root, found.file) };
|
|
125749
125895
|
}
|
|
125750
125896
|
function renderEstimate(estimate) {
|
|
125751
125897
|
const lines = [
|
|
@@ -125781,7 +125927,7 @@ var init_estimate = __esm(() => {
|
|
|
125781
125927
|
});
|
|
125782
125928
|
|
|
125783
125929
|
// src/review/plan-confirmation.ts
|
|
125784
|
-
import
|
|
125930
|
+
import path290 from "path";
|
|
125785
125931
|
function planOverThreshold(estimate) {
|
|
125786
125932
|
return estimate.overThreshold;
|
|
125787
125933
|
}
|
|
@@ -125800,21 +125946,23 @@ async function recordPlanConfirmation(packageDir, estimate, confirmation) {
|
|
|
125800
125946
|
minutes: [estimate.minutes.min, estimate.minutes.max],
|
|
125801
125947
|
quote: confirmation.quote.trim(),
|
|
125802
125948
|
confirmedVia: confirmation.confirmedVia,
|
|
125949
|
+
...cardPressFields(confirmation),
|
|
125803
125950
|
at: confirmation.at
|
|
125804
125951
|
};
|
|
125805
|
-
await writeFileAtomic(
|
|
125952
|
+
await writeFileAtomic(path290.join(packageDir, PLAN_CONFIRMATION_FILE), `${JSON.stringify(record, null, 2)}
|
|
125806
125953
|
`);
|
|
125807
125954
|
return record;
|
|
125808
125955
|
}
|
|
125809
125956
|
var init_plan_confirmation = __esm(() => {
|
|
125810
125957
|
init_fs();
|
|
125811
125958
|
init_estimate();
|
|
125959
|
+
init_types4();
|
|
125812
125960
|
});
|
|
125813
125961
|
|
|
125814
125962
|
// src/review/severity-audit.ts
|
|
125815
125963
|
import { existsSync as existsSync58 } from "fs";
|
|
125816
125964
|
import { readFile as readFile133, readdir as readdir48, writeFile as writeFile43 } from "fs/promises";
|
|
125817
|
-
import
|
|
125965
|
+
import path291 from "path";
|
|
125818
125966
|
function cited(finding) {
|
|
125819
125967
|
const evidence = typeof finding.evidence === "string" ? finding.evidence : "";
|
|
125820
125968
|
return [...new Set([...evidence.matchAll(CITATION2)].map((m) => m[1]))];
|
|
@@ -125892,14 +126040,14 @@ function auditSeverity(findings, records, options = {}) {
|
|
|
125892
126040
|
return { findings: audited, changes };
|
|
125893
126041
|
}
|
|
125894
126042
|
async function loadMutationRecords(packageDir) {
|
|
125895
|
-
const dir =
|
|
126043
|
+
const dir = path291.join(packageDir, EVIDENCE_DIR);
|
|
125896
126044
|
if (!existsSync58(dir))
|
|
125897
126045
|
return [];
|
|
125898
126046
|
const names = (await readdir48(dir)).filter((n) => /^mutation-\d+\.json$/.test(n)).sort((a, b) => Number(/\d+/.exec(a)[0]) - Number(/\d+/.exec(b)[0]));
|
|
125899
126047
|
const out = [];
|
|
125900
126048
|
for (const name of names) {
|
|
125901
126049
|
try {
|
|
125902
|
-
out.push({ name, record: JSON.parse(await readFile133(
|
|
126050
|
+
out.push({ name, record: JSON.parse(await readFile133(path291.join(dir, name), "utf8")) });
|
|
125903
126051
|
} catch {
|
|
125904
126052
|
continue;
|
|
125905
126053
|
}
|
|
@@ -125907,7 +126055,7 @@ async function loadMutationRecords(packageDir) {
|
|
|
125907
126055
|
return out;
|
|
125908
126056
|
}
|
|
125909
126057
|
async function auditPackage(packageDir, options = {}) {
|
|
125910
|
-
const file =
|
|
126058
|
+
const file = path291.join(packageDir, "findings.json");
|
|
125911
126059
|
const raw = JSON.parse(await readFile133(file, "utf8"));
|
|
125912
126060
|
if (!Array.isArray(raw))
|
|
125913
126061
|
throw new Error(`${file} must hold an array of findings`);
|
|
@@ -125928,7 +126076,7 @@ var init_severity_audit = __esm(() => {
|
|
|
125928
126076
|
|
|
125929
126077
|
// src/review/context-collect.ts
|
|
125930
126078
|
import { mkdir as mkdir70, readFile as readFile134, writeFile as writeFile44 } from "fs/promises";
|
|
125931
|
-
import
|
|
126079
|
+
import path292 from "path";
|
|
125932
126080
|
function extractBackendPins(texts) {
|
|
125933
126081
|
const pins = [];
|
|
125934
126082
|
const seen = new Set;
|
|
@@ -126031,7 +126179,7 @@ function renderPrContext(ctx) {
|
|
|
126031
126179
|
}
|
|
126032
126180
|
async function readPrContext(packageDir) {
|
|
126033
126181
|
try {
|
|
126034
|
-
const parsed = JSON.parse(await readFile134(
|
|
126182
|
+
const parsed = JSON.parse(await readFile134(path292.join(packageDir, PR_CONTEXT_JSON), "utf8"));
|
|
126035
126183
|
return parsed?.schemaVersion === 1 ? parsed : undefined;
|
|
126036
126184
|
} catch {
|
|
126037
126185
|
return;
|
|
@@ -126053,8 +126201,8 @@ async function collectPrContext(input) {
|
|
|
126053
126201
|
]);
|
|
126054
126202
|
const context = buildPrContext({ repo: input.repo, number: input.number, pull: pullText(pull), comments: collected.comments, checks: input.checks, previous, now: input.now });
|
|
126055
126203
|
await mkdir70(input.packageDir, { recursive: true });
|
|
126056
|
-
const json =
|
|
126057
|
-
const md =
|
|
126204
|
+
const json = path292.join(input.packageDir, PR_CONTEXT_JSON);
|
|
126205
|
+
const md = path292.join(input.packageDir, PR_CONTEXT_MD);
|
|
126058
126206
|
await writeFile44(json, `${JSON.stringify(context, null, 2)}
|
|
126059
126207
|
`, "utf8");
|
|
126060
126208
|
await writeFile44(md, renderPrContext(context), "utf8");
|
|
@@ -126086,7 +126234,7 @@ var init_context_collect = __esm(() => {
|
|
|
126086
126234
|
// src/review/compare-reference.ts
|
|
126087
126235
|
import { existsSync as existsSync59 } from "fs";
|
|
126088
126236
|
import { readFile as readFile135, readdir as readdir49 } from "fs/promises";
|
|
126089
|
-
import
|
|
126237
|
+
import path293 from "path";
|
|
126090
126238
|
function locationsOf(text) {
|
|
126091
126239
|
const found = [];
|
|
126092
126240
|
const pattern = /`?([A-Za-z0-9_./-]+\.(?:tsx?|jsx?|css|scss|md|json|ya?ml))(?::(\d+)(?:-(\d+))?)?`?/g;
|
|
@@ -126164,17 +126312,17 @@ async function readJson11(file) {
|
|
|
126164
126312
|
}
|
|
126165
126313
|
}
|
|
126166
126314
|
async function rawReplies(inputDir) {
|
|
126167
|
-
const dir =
|
|
126315
|
+
const dir = path293.join(inputDir, "raw");
|
|
126168
126316
|
if (!existsSync59(dir))
|
|
126169
126317
|
return [];
|
|
126170
126318
|
const out = [];
|
|
126171
126319
|
for (const name of (await readdir49(dir)).filter((n) => n.endsWith(".txt")).sort()) {
|
|
126172
|
-
out.push({ id: `raw/${name}`, text: await readFile135(
|
|
126320
|
+
out.push({ id: `raw/${name}`, text: await readFile135(path293.join(dir, name), "utf8") });
|
|
126173
126321
|
}
|
|
126174
126322
|
return out;
|
|
126175
126323
|
}
|
|
126176
126324
|
async function loadPackageEvidence(packageDir, inputDir) {
|
|
126177
|
-
const findingsRaw = await readJson11(
|
|
126325
|
+
const findingsRaw = await readJson11(path293.join(packageDir, "findings.json"));
|
|
126178
126326
|
const findings = (Array.isArray(findingsRaw) ? findingsRaw : []).map((f) => ({
|
|
126179
126327
|
id: asText(f.id),
|
|
126180
126328
|
severity: asText(f.severity) || undefined,
|
|
@@ -126184,7 +126332,7 @@ async function loadPackageEvidence(packageDir, inputDir) {
|
|
|
126184
126332
|
`)
|
|
126185
126333
|
}));
|
|
126186
126334
|
const canonicalIds = new Set(findings.map((f) => f.id));
|
|
126187
|
-
const research = asRecord2(await readJson11(
|
|
126335
|
+
const research = asRecord2(await readJson11(path293.join(packageDir, "research.json")));
|
|
126188
126336
|
const dispatch = asRecord2(research?.dispatch);
|
|
126189
126337
|
const rows = (value) => Array.isArray(value) ? value.filter((r) => r !== null && typeof r === "object") : [];
|
|
126190
126338
|
const selected = Array.isArray(dispatch?.selected) ? dispatch.selected : [];
|
|
@@ -126202,7 +126350,7 @@ async function loadPackageEvidence(packageDir, inputDir) {
|
|
|
126202
126350
|
}
|
|
126203
126351
|
if (inputDir !== undefined)
|
|
126204
126352
|
lost.push(...await rawReplies(inputDir));
|
|
126205
|
-
const evidenceDir =
|
|
126353
|
+
const evidenceDir = path293.join(packageDir, "research-evidence");
|
|
126206
126354
|
const mutationPass = existsSync59(evidenceDir) && (await readdir49(evidenceDir)).some((n) => /^mutation-\d+\.(json|md)$/i.test(n));
|
|
126207
126355
|
return { ran: [...ran].sort(), findings, lost, mutationPass };
|
|
126208
126356
|
}
|
|
@@ -126213,14 +126361,14 @@ function score2(item, evidence) {
|
|
|
126213
126361
|
if (haystack.includes(identifier))
|
|
126214
126362
|
points += 1;
|
|
126215
126363
|
for (const loc of item.locations) {
|
|
126216
|
-
const base =
|
|
126364
|
+
const base = path293.basename(loc.file).toLowerCase();
|
|
126217
126365
|
if (!haystack.includes(base))
|
|
126218
126366
|
continue;
|
|
126219
126367
|
if (loc.line === undefined) {
|
|
126220
126368
|
points += 1;
|
|
126221
126369
|
continue;
|
|
126222
126370
|
}
|
|
126223
|
-
const near = [...haystack.matchAll(new RegExp(`${base.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:(\\d+)`, "g"))].some((m) => Math.abs(Number(m[1]) - loc.line) <= LINE_WINDOW2) || evidence.file !== undefined &&
|
|
126371
|
+
const near = [...haystack.matchAll(new RegExp(`${base.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}:(\\d+)`, "g"))].some((m) => Math.abs(Number(m[1]) - loc.line) <= LINE_WINDOW2) || evidence.file !== undefined && path293.basename(evidence.file).toLowerCase() === base && evidence.line !== undefined && Math.abs(evidence.line - loc.line) <= LINE_WINDOW2;
|
|
126224
126372
|
points += near ? 2 : 1;
|
|
126225
126373
|
}
|
|
126226
126374
|
return points;
|
|
@@ -126285,7 +126433,7 @@ var init_compare_reference = __esm(() => {
|
|
|
126285
126433
|
// src/review/driver-read.ts
|
|
126286
126434
|
import { existsSync as existsSync60 } from "fs";
|
|
126287
126435
|
import { readFile as readFile136, readdir as readdir50 } from "fs/promises";
|
|
126288
|
-
import
|
|
126436
|
+
import path294 from "path";
|
|
126289
126437
|
async function readJson12(file) {
|
|
126290
126438
|
try {
|
|
126291
126439
|
return JSON.parse(await readFile136(file, "utf8"));
|
|
@@ -126298,7 +126446,7 @@ function definitionCandidates(driver) {
|
|
|
126298
126446
|
return [driver.definition, driver.skill_path, payload.definition, payload.skill_path].filter((v) => typeof v === "string" && v.trim() !== "");
|
|
126299
126447
|
}
|
|
126300
126448
|
async function jsonFiles(dir) {
|
|
126301
|
-
return existsSync60(dir) ? (await readdir50(dir)).filter((n) => n.endsWith(".json")).sort().map((n) =>
|
|
126449
|
+
return existsSync60(dir) ? (await readdir50(dir)).filter((n) => n.endsWith(".json")).sort().map((n) => path294.join(dir, n)) : [];
|
|
126302
126450
|
}
|
|
126303
126451
|
async function readDrivers(inputDir, root) {
|
|
126304
126452
|
const payloadSlices = {};
|
|
@@ -126310,7 +126458,7 @@ async function readDrivers(inputDir, root) {
|
|
|
126310
126458
|
const replyFiles = new Set;
|
|
126311
126459
|
const replyReviewers = new Set;
|
|
126312
126460
|
const dropped = [];
|
|
126313
|
-
for (const file of await jsonFiles(
|
|
126461
|
+
for (const file of await jsonFiles(path294.join(inputDir, "drivers"))) {
|
|
126314
126462
|
const driver = await readJson12(file);
|
|
126315
126463
|
if (typeof driver?.reviewer !== "string")
|
|
126316
126464
|
continue;
|
|
@@ -126319,11 +126467,11 @@ async function readDrivers(inputDir, root) {
|
|
|
126319
126467
|
const slices = readSliceIds(driver.slices, "assigned");
|
|
126320
126468
|
const complete = Array.isArray(driver.slices) && slices.unusable === 0;
|
|
126321
126469
|
if (slices.unusable > 0)
|
|
126322
|
-
dropped.push({ file:
|
|
126470
|
+
dropped.push({ file: path294.basename(file), reviewer: driver.reviewer, unusable: slices.unusable });
|
|
126323
126471
|
if (complete)
|
|
126324
126472
|
(payloadSlices[driver.reviewer] ??= []).push(...slices.ids);
|
|
126325
126473
|
if (typeof driver.result === "string") {
|
|
126326
|
-
replyFiles.add(
|
|
126474
|
+
replyFiles.add(path294.basename(driver.result));
|
|
126327
126475
|
if (complete) {
|
|
126328
126476
|
const key = `${driver.reviewer}\x00${driver.result}`;
|
|
126329
126477
|
const matches = assignments.get(key) ?? [];
|
|
@@ -126336,9 +126484,9 @@ async function readDrivers(inputDir, root) {
|
|
|
126336
126484
|
if (typeof driver.model === "string" && driver.model.trim() !== "")
|
|
126337
126485
|
models[driver.reviewer] = driver.model.trim();
|
|
126338
126486
|
for (const candidate of definitionCandidates(driver)) {
|
|
126339
|
-
const definition =
|
|
126487
|
+
const definition = path294.resolve(root, candidate);
|
|
126340
126488
|
if (existsSync60(definition)) {
|
|
126341
|
-
ruleFiles[driver.reviewer] =
|
|
126489
|
+
ruleFiles[driver.reviewer] = path294.relative(root, definition);
|
|
126342
126490
|
break;
|
|
126343
126491
|
}
|
|
126344
126492
|
}
|
|
@@ -126352,7 +126500,7 @@ var init_driver_read = __esm(() => {
|
|
|
126352
126500
|
// src/review/wave-drivers.ts
|
|
126353
126501
|
import { existsSync as existsSync61 } from "fs";
|
|
126354
126502
|
import { mkdir as mkdir71, readFile as readFile137, readdir as readdir51, rm as rm23 } from "fs/promises";
|
|
126355
|
-
import
|
|
126503
|
+
import path295 from "path";
|
|
126356
126504
|
async function packageReviewers(packageDir, manifest, options = {}) {
|
|
126357
126505
|
const root = projectRootOfPackage(packageDir);
|
|
126358
126506
|
const names = new Set;
|
|
@@ -126376,21 +126524,21 @@ async function packageReviewers(packageDir, manifest, options = {}) {
|
|
|
126376
126524
|
async function writeWaveDrivers(input) {
|
|
126377
126525
|
const { packageDir, manifest, sliceDir } = input;
|
|
126378
126526
|
const root = projectRootOfPackage(packageDir);
|
|
126379
|
-
const dir =
|
|
126527
|
+
const dir = path295.join(packageDir, DRIVERS_DIR);
|
|
126380
126528
|
await mkdir71(dir, { recursive: true });
|
|
126381
126529
|
const kept = new Map;
|
|
126382
126530
|
for (const name of existsSync61(dir) ? await readdir51(dir) : []) {
|
|
126383
126531
|
if (!/-w\d+\.json$/.test(name))
|
|
126384
126532
|
continue;
|
|
126385
126533
|
try {
|
|
126386
|
-
kept.set(name, JSON.parse(await readFile137(
|
|
126534
|
+
kept.set(name, JSON.parse(await readFile137(path295.join(dir, name), "utf8")));
|
|
126387
126535
|
} catch {}
|
|
126388
|
-
await rm23(
|
|
126536
|
+
await rm23(path295.join(dir, name), { force: true });
|
|
126389
126537
|
}
|
|
126390
126538
|
const written = [];
|
|
126391
126539
|
for (const reviewer of [...input.reviewers].sort()) {
|
|
126392
126540
|
const waves = roleDispatchWaves(manifest, reviewer);
|
|
126393
|
-
const definition = DEFINITION_CANDIDATES(reviewer).find((candidate) => existsSync61(
|
|
126541
|
+
const definition = DEFINITION_CANDIDATES(reviewer).find((candidate) => existsSync61(path295.resolve(root, candidate)));
|
|
126394
126542
|
for (const [at, wave] of waves.entries()) {
|
|
126395
126543
|
const number = at + 1;
|
|
126396
126544
|
const name = `${reviewer}-w${number}.json`;
|
|
@@ -126400,15 +126548,15 @@ async function writeWaveDrivers(input) {
|
|
|
126400
126548
|
wave: number,
|
|
126401
126549
|
waves: waves.length,
|
|
126402
126550
|
slices: wave.map((entry) => entry.id),
|
|
126403
|
-
slicePaths: wave.map((entry) => ({ id: entry.id, path:
|
|
126551
|
+
slicePaths: wave.map((entry) => ({ id: entry.id, path: path295.resolve(sliceDir, entry.path), bytes: entry.bytes })),
|
|
126404
126552
|
result: `${RAW_DIR}/${reviewer}-w${number}.txt`,
|
|
126405
126553
|
...definition === undefined ? {} : { definition },
|
|
126406
126554
|
...typeof prior?.model === "string" && prior.model.trim() !== "" ? { model: prior.model } : {},
|
|
126407
126555
|
...typeof prior?.toolLog === "string" && prior.toolLog.trim() !== "" ? { toolLog: prior.toolLog } : {}
|
|
126408
126556
|
};
|
|
126409
|
-
await writeFileAtomic(
|
|
126557
|
+
await writeFileAtomic(path295.join(dir, name), `${JSON.stringify(driver, null, 2)}
|
|
126410
126558
|
`);
|
|
126411
|
-
written.push(
|
|
126559
|
+
written.push(path295.join(dir, name));
|
|
126412
126560
|
}
|
|
126413
126561
|
}
|
|
126414
126562
|
return written;
|
|
@@ -126428,7 +126576,7 @@ var init_wave_drivers = __esm(() => {
|
|
|
126428
126576
|
// src/commands/review-plan.ts
|
|
126429
126577
|
import { existsSync as existsSync62 } from "fs";
|
|
126430
126578
|
import { readFile as readFile138 } from "fs/promises";
|
|
126431
|
-
import
|
|
126579
|
+
import path296 from "path";
|
|
126432
126580
|
function printPlanHelp() {
|
|
126433
126581
|
console.log(`keryx review plan --target <pr-url|branch> [--ref <base>] [--head <sha>] [--host ${REVIEW_HOSTS.join("|")}]
|
|
126434
126582
|
[--memory-query "<query>"] [--unattended] [--json]
|
|
@@ -126461,7 +126609,7 @@ function packagePathOf(startOutput, cwd) {
|
|
|
126461
126609
|
if (line === undefined)
|
|
126462
126610
|
return;
|
|
126463
126611
|
const value = line.slice("path: ".length).trim();
|
|
126464
|
-
return value === "" ? undefined :
|
|
126612
|
+
return value === "" ? undefined : path296.resolve(cwd, value);
|
|
126465
126613
|
}
|
|
126466
126614
|
function slicesByKind(manifest) {
|
|
126467
126615
|
const originals = manifest.slices.filter((entry) => entry.retryOf === undefined);
|
|
@@ -126485,7 +126633,7 @@ function renderPlanSummary(plan) {
|
|
|
126485
126633
|
`estimate: ${plan.dispatches} dispatches, ${plan.minutes.min}-${plan.minutes.max} min${plan.overThreshold ? ` \u2014 over threshold (${plan.overReasons.join("; ")})` : ""}`,
|
|
126486
126634
|
...plan.warnings.map((warning) => `warning: ${warning}`),
|
|
126487
126635
|
`next: ${plan.next}`,
|
|
126488
|
-
`written: ${
|
|
126636
|
+
`written: ${path296.join(plan.package, PLAN_FILE)}`
|
|
126489
126637
|
];
|
|
126490
126638
|
return lines.join(`
|
|
126491
126639
|
`);
|
|
@@ -126532,7 +126680,7 @@ async function runPlan2(args, deps) {
|
|
|
126532
126680
|
const scoped = await deps.run(["scope", "--json", ...baseRef === undefined ? [] : ["--ref", baseRef, ...head === undefined ? [] : ["--head", head]]]);
|
|
126533
126681
|
if (scoped.exitCode !== 0)
|
|
126534
126682
|
fail2("scope", scoped);
|
|
126535
|
-
const scopeFile =
|
|
126683
|
+
const scopeFile = path296.join(packageDir, "scope.json");
|
|
126536
126684
|
await writeFileAtomic(scopeFile, scoped.stdout.endsWith(`
|
|
126537
126685
|
`) ? scoped.stdout : `${scoped.stdout}
|
|
126538
126686
|
`);
|
|
@@ -126543,19 +126691,19 @@ async function runPlan2(args, deps) {
|
|
|
126543
126691
|
throw new Error("review plan: `review scope --json` printed no JSON; the plan stops here.");
|
|
126544
126692
|
}
|
|
126545
126693
|
const scopeFiles = Array.isArray(scopeRecord?.files) ? scopeRecord.files.filter((file) => typeof file === "string") : [];
|
|
126546
|
-
if (!existsSync62(
|
|
126694
|
+
if (!existsSync62(path296.join(packageDir, "scope-files.json"))) {
|
|
126547
126695
|
const lines = scopeRecord?.counts?.changedLinesRetained;
|
|
126548
|
-
await writeFileAtomic(
|
|
126696
|
+
await writeFileAtomic(path296.join(packageDir, "scope-files.json"), `${JSON.stringify({ version: 1, files: scopeFiles, ...typeof lines === "number" ? { changedLines: lines } : {} }, null, 2)}
|
|
126549
126697
|
`);
|
|
126550
126698
|
}
|
|
126551
|
-
const radius = await deps.run(["blast-radius", "--package", packageDir, "--json", "--out",
|
|
126699
|
+
const radius = await deps.run(["blast-radius", "--package", packageDir, "--json", "--out", path296.join(packageDir, "blast-radius.json"), ...baseRef === undefined ? [] : ["--ref", baseRef]]);
|
|
126552
126700
|
if (radius.exitCode !== 0)
|
|
126553
126701
|
warnings.push(`blast radius not computed (${(radius.stderr.trim() || radius.stdout.trim()).split(`
|
|
126554
126702
|
`).pop() ?? `exit ${radius.exitCode}`})`);
|
|
126555
126703
|
const sliced = await deps.run(["slice", "--package", packageDir, ...baseRef === undefined || delta !== undefined ? [] : ["--ref", baseRef], ...head === undefined ? [] : ["--head", head], ...host === undefined ? [] : ["--host", host], "--json"]);
|
|
126556
126704
|
if (sliced.exitCode !== 0)
|
|
126557
126705
|
fail2("slice", sliced);
|
|
126558
|
-
const manifestPath =
|
|
126706
|
+
const manifestPath = path296.join(packageDir, "slices", "manifest.json");
|
|
126559
126707
|
const manifest = JSON.parse(await readFile138(manifestPath, "utf8"));
|
|
126560
126708
|
const required = await deps.run(["required", "--depth", "auto", "--scope", scopeFile, "--package", packageDir, "--json"]);
|
|
126561
126709
|
if (required.exitCode !== 0)
|
|
@@ -126568,7 +126716,7 @@ async function runPlan2(args, deps) {
|
|
|
126568
126716
|
throw new Error("review plan: `review required --json` printed no JSON; the plan stops here.");
|
|
126569
126717
|
}
|
|
126570
126718
|
const estimate = await estimatePackage(packageDir);
|
|
126571
|
-
const drivers = await writeWaveDrivers({ packageDir, manifest, sliceDir:
|
|
126719
|
+
const drivers = await writeWaveDrivers({ packageDir, manifest, sliceDir: path296.dirname(manifestPath), reviewers: await packageReviewers(packageDir, manifest) });
|
|
126572
126720
|
if (drivers.length !== estimate.dispatches)
|
|
126573
126721
|
warnings.push(`${drivers.length} driver(s) written for ${estimate.dispatches} planned dispatch(es); re-run \`keryx review slice --package ${packageDir}\``);
|
|
126574
126722
|
const confirmed = planConfirmed(packageDir, estimate);
|
|
@@ -126600,7 +126748,7 @@ async function runPlan2(args, deps) {
|
|
|
126600
126748
|
warnings,
|
|
126601
126749
|
next: nextStepOf(estimate, confirmed)
|
|
126602
126750
|
};
|
|
126603
|
-
await writeFileAtomic(
|
|
126751
|
+
await writeFileAtomic(path296.join(packageDir, PLAN_FILE), `${JSON.stringify(plan, null, 2)}
|
|
126604
126752
|
`);
|
|
126605
126753
|
console.log(args.includes("--json") ? JSON.stringify(plan, null, 2) : renderPlanSummary(plan));
|
|
126606
126754
|
}
|
|
@@ -126750,7 +126898,7 @@ var init_plan_eval = __esm(() => {
|
|
|
126750
126898
|
// src/commands/review-plan-eval.ts
|
|
126751
126899
|
import { existsSync as existsSync63 } from "fs";
|
|
126752
126900
|
import { readFile as readFile139 } from "fs/promises";
|
|
126753
|
-
import
|
|
126901
|
+
import path297 from "path";
|
|
126754
126902
|
function printPlanEvalHelp() {
|
|
126755
126903
|
console.log(`keryx review plan-eval \u2014 usage:
|
|
126756
126904
|
|
|
@@ -126810,7 +126958,7 @@ async function runPlanEval(args, deps) {
|
|
|
126810
126958
|
throw new Error("plan-eval needs --diff <file|base..head>. See `keryx review plan-eval --help`.");
|
|
126811
126959
|
const diff = await readDiff(source);
|
|
126812
126960
|
const findingsFile = optionValue(args, "--findings");
|
|
126813
|
-
const findings = findingsFile === undefined ? undefined : parseGroundTruth(await readFile139(
|
|
126961
|
+
const findings = findingsFile === undefined ? undefined : parseGroundTruth(await readFile139(path297.resolve(findingsFile), "utf8"));
|
|
126814
126962
|
const rolesFlag = optionValue(args, "--roles");
|
|
126815
126963
|
let roles = rolesFlag?.split(",").map((role) => role.trim()).filter(Boolean);
|
|
126816
126964
|
if (roles === undefined) {
|
|
@@ -126868,7 +127016,7 @@ var init_retired_rules = __esm(() => {
|
|
|
126868
127016
|
import { cp as cp2, lstat as lstat19, readFile as readFile140, readdir as readdir52, realpath as realpath26, unlink as unlink2 } from "fs/promises";
|
|
126869
127017
|
import { existsSync as existsSync64 } from "fs";
|
|
126870
127018
|
import { createHash as createHash83 } from "crypto";
|
|
126871
|
-
import
|
|
127019
|
+
import path298 from "path";
|
|
126872
127020
|
import { fileURLToPath as fileURLToPath5 } from "url";
|
|
126873
127021
|
function isErrnoException(error) {
|
|
126874
127022
|
return error instanceof Error && "code" in error;
|
|
@@ -126884,7 +127032,7 @@ function bundledSkillSourcePath(category, skillName) {
|
|
|
126884
127032
|
if (existsSync64(directPath)) {
|
|
126885
127033
|
return directPath;
|
|
126886
127034
|
}
|
|
126887
|
-
const packagedSourcePath =
|
|
127035
|
+
const packagedSourcePath = path298.join(path298.dirname(fileURLToPath5(import.meta.url)), "..", "src", "gdskills", "bundled", "skills", category, skillName);
|
|
126888
127036
|
if (existsSync64(packagedSourcePath)) {
|
|
126889
127037
|
return packagedSourcePath;
|
|
126890
127038
|
}
|
|
@@ -126905,7 +127053,7 @@ async function removeStaleRuntimeBuilds(skillsRoot, fsOps = { unlink: unlink2 })
|
|
|
126905
127053
|
}
|
|
126906
127054
|
const categories = new Map;
|
|
126907
127055
|
for (const skillEntry of BUNDLED_GDSKILLS) {
|
|
126908
|
-
const categoryDir =
|
|
127056
|
+
const categoryDir = path298.join(skillsRoot, skillEntry.category);
|
|
126909
127057
|
let category = categories.get(categoryDir);
|
|
126910
127058
|
if (!category) {
|
|
126911
127059
|
category = await resolveSweepableDir(categoryDir);
|
|
@@ -126916,7 +127064,7 @@ async function removeStaleRuntimeBuilds(skillsRoot, fsOps = { unlink: unlink2 })
|
|
|
126916
127064
|
}
|
|
126917
127065
|
if (category.kind !== "usable")
|
|
126918
127066
|
continue;
|
|
126919
|
-
const installedDir =
|
|
127067
|
+
const installedDir = path298.join(categoryDir, skillEntry.name);
|
|
126920
127068
|
const skillDir = await resolveSweepableDir(installedDir);
|
|
126921
127069
|
if (skillDir.kind === "missing") {
|
|
126922
127070
|
continue;
|
|
@@ -126925,7 +127073,7 @@ async function removeStaleRuntimeBuilds(skillsRoot, fsOps = { unlink: unlink2 })
|
|
|
126925
127073
|
outcomes.push(skillDir.outcome);
|
|
126926
127074
|
continue;
|
|
126927
127075
|
}
|
|
126928
|
-
if (skillDir.realPath !==
|
|
127076
|
+
if (skillDir.realPath !== path298.join(root.realPath, skillEntry.category, skillEntry.name)) {
|
|
126929
127077
|
outcomes.push({
|
|
126930
127078
|
path: installedDir,
|
|
126931
127079
|
blockedAt: installedDir,
|
|
@@ -126952,9 +127100,9 @@ async function removeStaleRuntimeBuilds(skillsRoot, fsOps = { unlink: unlink2 })
|
|
|
126952
127100
|
for (const entryName of entries) {
|
|
126953
127101
|
if (!RUNTIME_BUILD_FILE_NAMES.includes(entryName))
|
|
126954
127102
|
continue;
|
|
126955
|
-
if (existsSync64(
|
|
127103
|
+
if (existsSync64(path298.join(bundledDir, entryName)))
|
|
126956
127104
|
continue;
|
|
126957
|
-
const installedPath =
|
|
127105
|
+
const installedPath = path298.join(installedDir, entryName);
|
|
126958
127106
|
try {
|
|
126959
127107
|
const stats = await lstat19(installedPath);
|
|
126960
127108
|
if (!stats.isFile()) {
|
|
@@ -127042,12 +127190,12 @@ function unsweepableDirCause(outcome, projectRoot) {
|
|
|
127042
127190
|
}
|
|
127043
127191
|
}
|
|
127044
127192
|
function displayPath2(target, projectRoot) {
|
|
127045
|
-
return
|
|
127193
|
+
return path298.relative(projectRoot, target).split(path298.sep).join("/");
|
|
127046
127194
|
}
|
|
127047
127195
|
async function removeUnmodifiedRetiredRules(rulesTarget, fsOps = { unlink: unlink2 }) {
|
|
127048
127196
|
const outcomes = [];
|
|
127049
127197
|
for (const retired of RETIRED_RULES) {
|
|
127050
|
-
const installedPath =
|
|
127198
|
+
const installedPath = path298.join(rulesTarget, retired.fileName);
|
|
127051
127199
|
let stage = "lstat";
|
|
127052
127200
|
try {
|
|
127053
127201
|
const stats = await lstat19(installedPath);
|
|
@@ -127123,10 +127271,10 @@ var init_guarded_fs_ops = __esm(() => {
|
|
|
127123
127271
|
// src/gdskills/install.ts
|
|
127124
127272
|
import { lstat as lstat20, readFile as readFile141, readlink as readlink2 } from "fs/promises";
|
|
127125
127273
|
import { existsSync as existsSync65 } from "fs";
|
|
127126
|
-
import
|
|
127274
|
+
import path299 from "path";
|
|
127127
127275
|
import { fileURLToPath as fileURLToPath6 } from "url";
|
|
127128
127276
|
async function mkdirContainedOrWarn(metaprojectRoot, target) {
|
|
127129
|
-
const rel =
|
|
127277
|
+
const rel = path299.relative(metaprojectRoot, target);
|
|
127130
127278
|
try {
|
|
127131
127279
|
await mkdirContained(metaprojectRoot, rel);
|
|
127132
127280
|
return null;
|
|
@@ -127139,20 +127287,20 @@ async function mkdirContainedOrWarn(metaprojectRoot, target) {
|
|
|
127139
127287
|
}
|
|
127140
127288
|
async function installGdskills(metaprojectRoot, profile, options = {}) {
|
|
127141
127289
|
const skills = getBundledSkillsForProfile(profile);
|
|
127142
|
-
const skillsRoot =
|
|
127143
|
-
const dataRoot =
|
|
127144
|
-
const coreRoot =
|
|
127145
|
-
const contractsRoot =
|
|
127146
|
-
const projectSkillsRoot =
|
|
127290
|
+
const skillsRoot = path299.join(metaprojectRoot, "skills", "gdskills");
|
|
127291
|
+
const dataRoot = path299.join(metaprojectRoot, "data", "gdskills");
|
|
127292
|
+
const coreRoot = path299.join(metaprojectRoot, "core", "gdskills");
|
|
127293
|
+
const contractsRoot = path299.join(coreRoot, "contracts");
|
|
127294
|
+
const projectSkillsRoot = path299.join(metaprojectRoot, metaprojectRelative(PROJECT_SKILLS_DIR));
|
|
127147
127295
|
const skippedDestinationWarnings = [];
|
|
127148
127296
|
for (const dir of [
|
|
127149
|
-
|
|
127150
|
-
|
|
127297
|
+
path299.join(metaprojectRoot, "jobs"),
|
|
127298
|
+
path299.join(metaprojectRoot, "modules"),
|
|
127151
127299
|
projectSkillsRoot,
|
|
127152
127300
|
...options.createDataDirs === false ? [] : [
|
|
127153
|
-
|
|
127154
|
-
|
|
127155
|
-
|
|
127301
|
+
path299.join(dataRoot, "artifacts"),
|
|
127302
|
+
path299.join(metaprojectRoot, metaprojectRelative(PROJECT_SKILL_REPORTS_DIR)),
|
|
127303
|
+
path299.join(dataRoot, "proposals")
|
|
127156
127304
|
]
|
|
127157
127305
|
]) {
|
|
127158
127306
|
const warning = await mkdirContainedOrWarn(metaprojectRoot, dir);
|
|
@@ -127161,8 +127309,8 @@ async function installGdskills(metaprojectRoot, profile, options = {}) {
|
|
|
127161
127309
|
}
|
|
127162
127310
|
let installedSkills = 0;
|
|
127163
127311
|
for (const skillEntry of skills) {
|
|
127164
|
-
const skillDir =
|
|
127165
|
-
const relSkillDir =
|
|
127312
|
+
const skillDir = path299.join(skillsRoot, skillEntry.category, skillEntry.name);
|
|
127313
|
+
const relSkillDir = path299.relative(metaprojectRoot, skillDir);
|
|
127166
127314
|
const destination = await checkInstallDestination(skillDir, relSkillDir);
|
|
127167
127315
|
if (!destination.usable) {
|
|
127168
127316
|
skippedDestinationWarnings.push(destination.warning);
|
|
@@ -127177,7 +127325,7 @@ async function installGdskills(metaprojectRoot, profile, options = {}) {
|
|
|
127177
127325
|
if (existsSync65(bundledSkillPath)) {
|
|
127178
127326
|
await copyDirectoryContained(bundledSkillPath, skillDir);
|
|
127179
127327
|
} else {
|
|
127180
|
-
await writeContained(metaprojectRoot,
|
|
127328
|
+
await writeContained(metaprojectRoot, path299.join(relSkillDir, "SKILL.md"), renderBundledSkill(skillEntry));
|
|
127181
127329
|
}
|
|
127182
127330
|
installedSkills += 1;
|
|
127183
127331
|
}
|
|
@@ -127191,12 +127339,12 @@ async function installGdskills(metaprojectRoot, profile, options = {}) {
|
|
|
127191
127339
|
skippedDestinationWarnings.push(rulesInstall.warning);
|
|
127192
127340
|
}
|
|
127193
127341
|
const retiredRuleOutcomes = rulesInstall.outcomes;
|
|
127194
|
-
const catalogPath =
|
|
127195
|
-
await writeContained(metaprojectRoot,
|
|
127196
|
-
const manifestPath =
|
|
127197
|
-
await writeContained(metaprojectRoot,
|
|
127342
|
+
const catalogPath = path299.join(metaprojectRoot, metaprojectRelative(PROJECT_SKILLS_CATALOG_PATH));
|
|
127343
|
+
await writeContained(metaprojectRoot, path299.relative(metaprojectRoot, catalogPath), await preserveProjectSkillsSection(catalogPath, renderGdskillsCatalog(profile)));
|
|
127344
|
+
const manifestPath = path299.join(metaprojectRoot, "modules", "gdskills.md");
|
|
127345
|
+
await writeContained(metaprojectRoot, path299.relative(metaprojectRoot, manifestPath), renderGdskillsManifest(profile));
|
|
127198
127346
|
skippedDestinationWarnings.push(...await installContracts(contractsRoot, metaprojectRoot));
|
|
127199
|
-
const projectRoot =
|
|
127347
|
+
const projectRoot = path299.dirname(metaprojectRoot);
|
|
127200
127348
|
const warnings = [
|
|
127201
127349
|
...skippedDestinationWarnings,
|
|
127202
127350
|
...retiredRuleOutcomes.map(retiredRuleWarning).filter((warning) => warning !== null),
|
|
@@ -127218,8 +127366,8 @@ async function installBundledSharedSkills(skillsRoot, metaprojectRoot) {
|
|
|
127218
127366
|
if (!existsSync65(sharedSource)) {
|
|
127219
127367
|
return null;
|
|
127220
127368
|
}
|
|
127221
|
-
const sharedTarget =
|
|
127222
|
-
const relSharedTarget =
|
|
127369
|
+
const sharedTarget = path299.join(skillsRoot, "shared");
|
|
127370
|
+
const relSharedTarget = path299.relative(metaprojectRoot, sharedTarget);
|
|
127223
127371
|
const destination = await checkInstallDestination(sharedTarget, relSharedTarget);
|
|
127224
127372
|
if (!destination.usable) {
|
|
127225
127373
|
return destination.warning;
|
|
@@ -127262,11 +127410,11 @@ async function checkInstallDestination(destination, displayPath) {
|
|
|
127262
127410
|
}
|
|
127263
127411
|
async function installBundledRules(metaprojectRoot) {
|
|
127264
127412
|
const rulesSource = bundledRulesSourcePath();
|
|
127265
|
-
const rulesTarget =
|
|
127413
|
+
const rulesTarget = path299.join(metaprojectRoot, "rules", "core");
|
|
127266
127414
|
if (!existsSync65(rulesSource)) {
|
|
127267
127415
|
return { outcomes: [], warning: null };
|
|
127268
127416
|
}
|
|
127269
|
-
const relRulesTarget =
|
|
127417
|
+
const relRulesTarget = path299.relative(metaprojectRoot, rulesTarget);
|
|
127270
127418
|
const destination = await checkInstallDestination(rulesTarget, relRulesTarget);
|
|
127271
127419
|
if (!destination.usable) {
|
|
127272
127420
|
return { outcomes: [], warning: destination.warning };
|
|
@@ -127297,13 +127445,13 @@ ${section}
|
|
|
127297
127445
|
`;
|
|
127298
127446
|
}
|
|
127299
127447
|
async function installContracts(contractsRoot, metaprojectRoot) {
|
|
127300
|
-
const rootCheck = await checkInstallDestination(contractsRoot,
|
|
127448
|
+
const rootCheck = await checkInstallDestination(contractsRoot, path299.relative(metaprojectRoot, contractsRoot));
|
|
127301
127449
|
if (!rootCheck.usable) {
|
|
127302
127450
|
return [rootCheck.warning];
|
|
127303
127451
|
}
|
|
127304
127452
|
const results = await Promise.all(CONTRACTS.map(async (contract) => {
|
|
127305
|
-
const destination =
|
|
127306
|
-
const relDestination =
|
|
127453
|
+
const destination = path299.join(contractsRoot, contract.fileName);
|
|
127454
|
+
const relDestination = path299.relative(metaprojectRoot, destination);
|
|
127307
127455
|
const check = await checkInstallFile(destination, relDestination);
|
|
127308
127456
|
if (!check.usable) {
|
|
127309
127457
|
return check.warning;
|
|
@@ -127348,7 +127496,7 @@ function contractSourcePath(fileName) {
|
|
|
127348
127496
|
if (existsSync65(directPath)) {
|
|
127349
127497
|
return directPath;
|
|
127350
127498
|
}
|
|
127351
|
-
const packagedSourcePath =
|
|
127499
|
+
const packagedSourcePath = path299.join(path299.dirname(fileURLToPath6(import.meta.url)), "..", "src", "gdskills", "contracts", fileName);
|
|
127352
127500
|
if (existsSync65(packagedSourcePath)) {
|
|
127353
127501
|
return packagedSourcePath;
|
|
127354
127502
|
}
|
|
@@ -127359,7 +127507,7 @@ function bundledSharedSourcePath() {
|
|
|
127359
127507
|
if (existsSync65(directPath)) {
|
|
127360
127508
|
return directPath;
|
|
127361
127509
|
}
|
|
127362
|
-
const packagedSourcePath =
|
|
127510
|
+
const packagedSourcePath = path299.join(path299.dirname(fileURLToPath6(import.meta.url)), "..", "src", "gdskills", "bundled", "skills", "shared");
|
|
127363
127511
|
if (existsSync65(packagedSourcePath)) {
|
|
127364
127512
|
return packagedSourcePath;
|
|
127365
127513
|
}
|
|
@@ -127370,7 +127518,7 @@ function bundledRulesSourcePath() {
|
|
|
127370
127518
|
if (existsSync65(directPath)) {
|
|
127371
127519
|
return directPath;
|
|
127372
127520
|
}
|
|
127373
|
-
const packagedSourcePath =
|
|
127521
|
+
const packagedSourcePath = path299.join(path299.dirname(fileURLToPath6(import.meta.url)), "..", "src", "gdskills", "bundled", "rules", "core");
|
|
127374
127522
|
if (existsSync65(packagedSourcePath)) {
|
|
127375
127523
|
return packagedSourcePath;
|
|
127376
127524
|
}
|
|
@@ -127388,7 +127536,7 @@ var init_install2 = __esm(() => {
|
|
|
127388
127536
|
// src/gdskills/import-skills.ts
|
|
127389
127537
|
import { lstat as lstat21, readdir as readdir53, readFile as readFile142, stat as stat33 } from "fs/promises";
|
|
127390
127538
|
import { homedir as homedir11 } from "os";
|
|
127391
|
-
import
|
|
127539
|
+
import path300 from "path";
|
|
127392
127540
|
function emptyOnlyError(label) {
|
|
127393
127541
|
return new Error(`${label}: --only needs a glob; an empty value selects nothing. Pass a package directory name or a glob over them (\`*\`, \`?\`), or drop --only.`);
|
|
127394
127542
|
}
|
|
@@ -127777,17 +127925,17 @@ function refuseSharedDestinations(label, picked) {
|
|
|
127777
127925
|
}
|
|
127778
127926
|
}
|
|
127779
127927
|
function isInside6(parent, child) {
|
|
127780
|
-
const relative =
|
|
127781
|
-
return relative !== "" && relative !== ".." && !relative.startsWith(`..${
|
|
127928
|
+
const relative = path300.relative(parent, child);
|
|
127929
|
+
return relative !== "" && relative !== ".." && !relative.startsWith(`..${path300.sep}`) && !path300.isAbsolute(relative);
|
|
127782
127930
|
}
|
|
127783
127931
|
function portableOriginRef(absolute, projectRoot, home = homedir11()) {
|
|
127784
|
-
const resolved =
|
|
127785
|
-
const root =
|
|
127932
|
+
const resolved = path300.resolve(absolute);
|
|
127933
|
+
const root = path300.resolve(projectRoot);
|
|
127786
127934
|
if (isInside6(root, resolved)) {
|
|
127787
|
-
return toPosix(
|
|
127935
|
+
return toPosix(path300.relative(root, resolved));
|
|
127788
127936
|
}
|
|
127789
127937
|
if (isInside6(home, resolved)) {
|
|
127790
|
-
return `~/${toPosix(
|
|
127938
|
+
return `~/${toPosix(path300.relative(home, resolved))}`;
|
|
127791
127939
|
}
|
|
127792
127940
|
return resolved;
|
|
127793
127941
|
}
|
|
@@ -127850,8 +127998,8 @@ async function sourceFromDirectory(options, absolute) {
|
|
|
127850
127998
|
const { dirs, tree } = await skillPackageDirs(absolute);
|
|
127851
127999
|
const candidates = [];
|
|
127852
128000
|
for (const dir of dirs) {
|
|
127853
|
-
const skillMd =
|
|
127854
|
-
const name =
|
|
128001
|
+
const skillMd = path300.join(dir, "SKILL.md");
|
|
128002
|
+
const name = path300.basename(dir);
|
|
127855
128003
|
candidates.push({ name, slug: projectSkillSlug(name), skillMd, content: await readFile142(skillMd, "utf8") });
|
|
127856
128004
|
}
|
|
127857
128005
|
candidates.sort((a, b) => a.name.localeCompare(b.name));
|
|
@@ -127904,18 +128052,18 @@ ${candidateList(selected)}`);
|
|
|
127904
128052
|
return picked.map(({ source }) => source).sort((a, b) => projectSkillKey(a).localeCompare(projectSkillKey(b)));
|
|
127905
128053
|
}
|
|
127906
128054
|
async function skillPackageDirs(root) {
|
|
127907
|
-
if (await pathExists(
|
|
128055
|
+
if (await pathExists(path300.join(root, "SKILL.md"))) {
|
|
127908
128056
|
return { dirs: [root], tree: false };
|
|
127909
128057
|
}
|
|
127910
|
-
const nestedSkills =
|
|
128058
|
+
const nestedSkills = path300.join(root, "skills");
|
|
127911
128059
|
const scan = await pathExists(nestedSkills) ? nestedSkills : root;
|
|
127912
128060
|
const entries = await readdir53(scan, { withFileTypes: true });
|
|
127913
128061
|
const dirs = [];
|
|
127914
128062
|
for (const entry of entries) {
|
|
127915
128063
|
if (!entry.isDirectory())
|
|
127916
128064
|
continue;
|
|
127917
|
-
const dir =
|
|
127918
|
-
if (await pathExists(
|
|
128065
|
+
const dir = path300.join(scan, entry.name);
|
|
128066
|
+
if (await pathExists(path300.join(dir, "SKILL.md"))) {
|
|
127919
128067
|
dirs.push(dir);
|
|
127920
128068
|
}
|
|
127921
128069
|
}
|
|
@@ -128027,7 +128175,7 @@ function refusedSkillRow(base, verdict, dryRun) {
|
|
|
128027
128175
|
}
|
|
128028
128176
|
async function planOne(options, source) {
|
|
128029
128177
|
const dest = projectSkillPackagePath(source.module, source.name);
|
|
128030
|
-
const destAbs =
|
|
128178
|
+
const destAbs = path300.join(options.projectRoot, dest, "SKILL.md");
|
|
128031
128179
|
const exists = await pathExists(destAbs);
|
|
128032
128180
|
const bundled = BUNDLED_NAMES.has(source.name);
|
|
128033
128181
|
if (skippedAsDeprecated(source)) {
|
|
@@ -128067,7 +128215,7 @@ async function planOne(options, source) {
|
|
|
128067
128215
|
}
|
|
128068
128216
|
};
|
|
128069
128217
|
}
|
|
128070
|
-
const verdict = await gateText(options.projectRoot, plannedSkillText(source.content, { name: source.name, module: source.module, origin: source.origin }), toPosix(
|
|
128218
|
+
const verdict = await gateText(options.projectRoot, plannedSkillText(source.content, { name: source.name, module: source.module, origin: source.origin }), toPosix(path300.join(dest, "SKILL.md")), options.allowFlagged === true);
|
|
128071
128219
|
if (verdict.kind !== "write") {
|
|
128072
128220
|
return {
|
|
128073
128221
|
row: refusedSkillRow({ name: source.name, module: source.module, path: dest, origin: source.origin }, verdict, options.dryRun === true)
|
|
@@ -128120,7 +128268,7 @@ function mergeNotes(notes, parts) {
|
|
|
128120
128268
|
};
|
|
128121
128269
|
}
|
|
128122
128270
|
async function planUpdate(options, entry) {
|
|
128123
|
-
const skillMd =
|
|
128271
|
+
const skillMd = path300.join(options.projectRoot, entry.path, "SKILL.md");
|
|
128124
128272
|
const current = await readFile142(skillMd, "utf8");
|
|
128125
128273
|
let origin = options.from ?? metadataLine2(current, "Origin");
|
|
128126
128274
|
const skippedRow = (reason, recorded) => ({
|
|
@@ -128147,7 +128295,7 @@ async function planUpdate(options, entry) {
|
|
|
128147
128295
|
sourcePath = resolved;
|
|
128148
128296
|
origin = portableOriginRef(resolved, options.projectRoot);
|
|
128149
128297
|
}
|
|
128150
|
-
const verdict = await gateText(options.projectRoot, plannedSkillText(content, { name: entry.name, module: entry.module, origin }), toPosix(
|
|
128298
|
+
const verdict = await gateText(options.projectRoot, plannedSkillText(content, { name: entry.name, module: entry.module, origin }), toPosix(path300.join(entry.path, "SKILL.md")), options.allowFlagged === true);
|
|
128151
128299
|
if (verdict.kind !== "write") {
|
|
128152
128300
|
return {
|
|
128153
128301
|
row: refusedSkillRow({ name: entry.name, module: entry.module, path: entry.path, origin }, verdict, options.dryRun === true)
|
|
@@ -128191,7 +128339,7 @@ async function writeUpdate(options, { entry, origin, content, gate }) {
|
|
|
128191
128339
|
};
|
|
128192
128340
|
}
|
|
128193
128341
|
async function writeGatedSkill(projectRoot, created, gate) {
|
|
128194
|
-
await writeContained(projectRoot, toPosix(
|
|
128342
|
+
await writeContained(projectRoot, toPosix(path300.join(created.skillPath, "SKILL.md")), gate.content);
|
|
128195
128343
|
}
|
|
128196
128344
|
function stampImportHeader(source, header) {
|
|
128197
128345
|
if (!header)
|
|
@@ -128251,15 +128399,15 @@ function versionOption(content) {
|
|
|
128251
128399
|
return version ? { version } : {};
|
|
128252
128400
|
}
|
|
128253
128401
|
async function findOverlayRule(sourcePath, ref) {
|
|
128254
|
-
let dir =
|
|
128402
|
+
let dir = path300.dirname(sourcePath);
|
|
128255
128403
|
let notRegular;
|
|
128256
128404
|
for (let depth = 0;depth <= 3; depth += 1) {
|
|
128257
|
-
const candidate =
|
|
128405
|
+
const candidate = path300.join(dir, "rules", ref);
|
|
128258
128406
|
if (await isRegularFile(candidate))
|
|
128259
128407
|
return { found: candidate };
|
|
128260
128408
|
if (notRegular === undefined && await pathExists(candidate))
|
|
128261
128409
|
notRegular = candidate;
|
|
128262
|
-
const parent =
|
|
128410
|
+
const parent = path300.dirname(dir);
|
|
128263
128411
|
if (parent === dir)
|
|
128264
128412
|
break;
|
|
128265
128413
|
dir = parent;
|
|
@@ -128333,7 +128481,7 @@ async function decideRule(options, citation, bundledRules) {
|
|
|
128333
128481
|
const base = { ref: citation.ref, citedBy: citation.citedBy };
|
|
128334
128482
|
const resolution = await resolveRuleReference(options.projectRoot, citation.ref);
|
|
128335
128483
|
const existing = resolution.resolved;
|
|
128336
|
-
const shipsWithKeryx = citation.ref.startsWith(`${KERYX_RULES_DIR}/`) && await pathExists(
|
|
128484
|
+
const shipsWithKeryx = citation.ref.startsWith(`${KERYX_RULES_DIR}/`) && await pathExists(path300.join(bundledRules, path300.basename(citation.ref)));
|
|
128337
128485
|
const notRegularFiles = resolution.notRegularFiles ?? [];
|
|
128338
128486
|
const notRegularReason = (candidate) => `${candidate} is not a regular file; nothing was written there`;
|
|
128339
128487
|
let found;
|
|
@@ -128354,7 +128502,7 @@ async function decideRule(options, citation, bundledRules) {
|
|
|
128354
128502
|
}
|
|
128355
128503
|
const origin = portableOriginRef(found, options.projectRoot);
|
|
128356
128504
|
const content = await readFile142(found, "utf8");
|
|
128357
|
-
const existingContent = existing !== undefined ? await readFile142(
|
|
128505
|
+
const existingContent = existing !== undefined ? await readFile142(path300.join(options.projectRoot, existing), "utf8") : undefined;
|
|
128358
128506
|
if (existing !== undefined && existingContent === content) {
|
|
128359
128507
|
return { row: { ...base, status: "present", origin, existing } };
|
|
128360
128508
|
}
|
|
@@ -128410,7 +128558,7 @@ async function decideReferencedRules(options, sources, refusedPackages) {
|
|
|
128410
128558
|
return decisions;
|
|
128411
128559
|
}
|
|
128412
128560
|
async function listTemplateFiles(sourcePath) {
|
|
128413
|
-
const root =
|
|
128561
|
+
const root = path300.join(path300.dirname(sourcePath), TEMPLATES_DIR);
|
|
128414
128562
|
try {
|
|
128415
128563
|
if (!(await lstat21(root)).isDirectory())
|
|
128416
128564
|
return [];
|
|
@@ -128423,7 +128571,7 @@ async function listTemplateFiles(sourcePath) {
|
|
|
128423
128571
|
for (const entry of entries) {
|
|
128424
128572
|
const rel = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
128425
128573
|
if (entry.isDirectory())
|
|
128426
|
-
await walk(
|
|
128574
|
+
await walk(path300.join(dir, entry.name), rel);
|
|
128427
128575
|
else if (entry.isFile())
|
|
128428
128576
|
files.push(rel);
|
|
128429
128577
|
}
|
|
@@ -128435,7 +128583,7 @@ async function fileInTheWay(projectRoot, target) {
|
|
|
128435
128583
|
let current = projectRoot;
|
|
128436
128584
|
let relative = "";
|
|
128437
128585
|
for (const part of target.split("/").slice(0, -1)) {
|
|
128438
|
-
current =
|
|
128586
|
+
current = path300.join(current, part);
|
|
128439
128587
|
relative = relative === "" ? part : `${relative}/${part}`;
|
|
128440
128588
|
try {
|
|
128441
128589
|
if (!(await stat33(current)).isDirectory())
|
|
@@ -128472,13 +128620,13 @@ async function planTemplates(options, pkg, replace) {
|
|
|
128472
128620
|
for (const file of await listTemplateFiles(pkg.sourcePath)) {
|
|
128473
128621
|
const target = `${packagePath}/${TEMPLATES_DIR}/${file}`;
|
|
128474
128622
|
const base = { package: packageKey, file, path: target };
|
|
128475
|
-
const bytes = await readFile142(
|
|
128623
|
+
const bytes = await readFile142(path300.join(path300.dirname(pkg.sourcePath), TEMPLATES_DIR, file));
|
|
128476
128624
|
const text = bytes.toString("utf8");
|
|
128477
128625
|
if (!Buffer.from(text, "utf8").equals(bytes)) {
|
|
128478
128626
|
plans.push({ row: { ...base, status: "skipped", reason: "not UTF-8 text; the importer copies text templates only" } });
|
|
128479
128627
|
continue;
|
|
128480
128628
|
}
|
|
128481
|
-
const targetAbs =
|
|
128629
|
+
const targetAbs = path300.join(options.projectRoot, target);
|
|
128482
128630
|
const blocker = await fileInTheWay(options.projectRoot, target);
|
|
128483
128631
|
if (blocker !== undefined) {
|
|
128484
128632
|
plans.push({ row: { ...base, status: "skipped", reason: `${blocker} is a file, not a directory; nothing was written there` } });
|
|
@@ -128558,10 +128706,10 @@ function inferNameFromPath(filePath, content) {
|
|
|
128558
128706
|
const fromFrontmatter = frontmatterName(content);
|
|
128559
128707
|
if (fromFrontmatter)
|
|
128560
128708
|
return fromFrontmatter;
|
|
128561
|
-
const parent =
|
|
128709
|
+
const parent = path300.basename(path300.dirname(filePath));
|
|
128562
128710
|
if (parent && parent !== "skills" && parent !== ".")
|
|
128563
128711
|
return parent;
|
|
128564
|
-
return
|
|
128712
|
+
return path300.basename(filePath, path300.extname(filePath));
|
|
128565
128713
|
}
|
|
128566
128714
|
function inferNameFromUrl(url) {
|
|
128567
128715
|
const cleaned = url.replace(/\/SKILL\.md$/i, "");
|
|
@@ -128588,7 +128736,7 @@ function metadataLine2(content, label) {
|
|
|
128588
128736
|
}
|
|
128589
128737
|
async function readRegistry(projectRoot) {
|
|
128590
128738
|
await Promise.resolve().then(() => init_json());
|
|
128591
|
-
const manifestPath =
|
|
128739
|
+
const manifestPath = path300.join(projectRoot, PROJECT_SKILLS_MANIFEST_PATH);
|
|
128592
128740
|
const manifest = await readJsonFileOr(manifestPath, {});
|
|
128593
128741
|
return manifest.modules?.gdskills?.projectSkillRegistry ?? [];
|
|
128594
128742
|
}
|
|
@@ -128988,10 +129136,10 @@ var init_import_reviewers = __esm(() => {
|
|
|
128988
129136
|
|
|
128989
129137
|
// src/review/jev-rules-config.ts
|
|
128990
129138
|
import { readFile as readFile143 } from "fs/promises";
|
|
128991
|
-
import
|
|
129139
|
+
import path301 from "path";
|
|
128992
129140
|
async function readJevRulesEnabled(cwd) {
|
|
128993
129141
|
try {
|
|
128994
|
-
const parsed = JSON.parse(await readFile143(
|
|
129142
|
+
const parsed = JSON.parse(await readFile143(path301.join(cwd, REVIEW_GATE_CONFIG_PATH), "utf8"));
|
|
128995
129143
|
if (typeof parsed !== "object" || parsed === null)
|
|
128996
129144
|
return false;
|
|
128997
129145
|
const review = parsed["review"];
|
|
@@ -129011,7 +129159,7 @@ var init_jev_rules_config = __esm(() => {
|
|
|
129011
129159
|
|
|
129012
129160
|
// src/review/jev-rules-cache.ts
|
|
129013
129161
|
import { readFile as readFile144 } from "fs/promises";
|
|
129014
|
-
import
|
|
129162
|
+
import path302 from "path";
|
|
129015
129163
|
function hashCacheKey(input) {
|
|
129016
129164
|
let hash = 2166136261;
|
|
129017
129165
|
for (let i = 0;i < input.length; i += 1) {
|
|
@@ -129024,7 +129172,7 @@ function cacheKeyFor(ruleId, clauseId, regionPath, regionStartLine, regionEndLin
|
|
|
129024
129172
|
return hashCacheKey(`${ruleId}::${clauseId}::${regionPath}:${regionStartLine}-${regionEndLine}::${clauseText}`);
|
|
129025
129173
|
}
|
|
129026
129174
|
function cacheFilePath(cwd) {
|
|
129027
|
-
return
|
|
129175
|
+
return path302.join(cwd, JEV_RULES_CACHE_PATH);
|
|
129028
129176
|
}
|
|
129029
129177
|
async function readJevRulesCache(cwd) {
|
|
129030
129178
|
const file = cacheFilePath(cwd);
|
|
@@ -129854,14 +130002,14 @@ var init_jev_rules = __esm(() => {
|
|
|
129854
130002
|
});
|
|
129855
130003
|
|
|
129856
130004
|
// src/review/conform-tag-cache.ts
|
|
129857
|
-
import
|
|
130005
|
+
import path303 from "path";
|
|
129858
130006
|
import { readFile as readFile145 } from "fs/promises";
|
|
129859
130007
|
function hashConformDocContent(content) {
|
|
129860
130008
|
return hashOriginContent(`clause-tag-schema:v${CLAUSE_TAG_SCHEMA_VERSION}
|
|
129861
130009
|
${content}`);
|
|
129862
130010
|
}
|
|
129863
130011
|
function cacheFilePath2(cwd, cacheRelPath) {
|
|
129864
|
-
return
|
|
130012
|
+
return path303.join(cwd, cacheRelPath);
|
|
129865
130013
|
}
|
|
129866
130014
|
async function readClauseTagCache(cwd, cacheRelPath = CONFORM_TAG_CACHE_PATH) {
|
|
129867
130015
|
const file = cacheFilePath2(cwd, cacheRelPath);
|
|
@@ -130386,7 +130534,7 @@ function checkPullOpen(facts) {
|
|
|
130386
130534
|
|
|
130387
130535
|
// src/review/bot/packages.ts
|
|
130388
130536
|
import { readdir as readdir55, readFile as readFile147 } from "fs/promises";
|
|
130389
|
-
import
|
|
130537
|
+
import path304 from "path";
|
|
130390
130538
|
async function subdirectories(dir) {
|
|
130391
130539
|
try {
|
|
130392
130540
|
const entries = await readdir55(dir, { withFileTypes: true });
|
|
@@ -130409,12 +130557,12 @@ function pullFromTarget(target) {
|
|
|
130409
130557
|
}
|
|
130410
130558
|
async function readPackage(dir) {
|
|
130411
130559
|
try {
|
|
130412
|
-
const manifest = JSON.parse(await readFile147(
|
|
130560
|
+
const manifest = JSON.parse(await readFile147(path304.join(dir, "manifest.json"), "utf8"));
|
|
130413
130561
|
if (manifest.target?.kind !== "pr")
|
|
130414
130562
|
return null;
|
|
130415
130563
|
let findings = [];
|
|
130416
130564
|
try {
|
|
130417
|
-
const parsed = JSON.parse(await readFile147(
|
|
130565
|
+
const parsed = JSON.parse(await readFile147(path304.join(dir, "findings.json"), "utf8"));
|
|
130418
130566
|
if (Array.isArray(parsed))
|
|
130419
130567
|
findings = parsed;
|
|
130420
130568
|
} catch {
|
|
@@ -130429,16 +130577,16 @@ async function readPackage(dir) {
|
|
|
130429
130577
|
function candidateDirs2(cwd) {
|
|
130430
130578
|
return (async () => {
|
|
130431
130579
|
const dirs = [];
|
|
130432
|
-
const reviews =
|
|
130580
|
+
const reviews = path304.join(cwd, ".metaproject", "reviews");
|
|
130433
130581
|
for (const name of await subdirectories(reviews)) {
|
|
130434
130582
|
if (name !== "bot")
|
|
130435
|
-
dirs.push(
|
|
130583
|
+
dirs.push(path304.join(reviews, name));
|
|
130436
130584
|
}
|
|
130437
|
-
const flows =
|
|
130585
|
+
const flows = path304.join(cwd, ".metaproject", "flows");
|
|
130438
130586
|
for (const flow of await subdirectories(flows)) {
|
|
130439
|
-
const nested =
|
|
130587
|
+
const nested = path304.join(flows, flow, "reviews");
|
|
130440
130588
|
for (const name of await subdirectories(nested))
|
|
130441
|
-
dirs.push(
|
|
130589
|
+
dirs.push(path304.join(nested, name));
|
|
130442
130590
|
}
|
|
130443
130591
|
return dirs;
|
|
130444
130592
|
})();
|
|
@@ -130455,7 +130603,7 @@ async function listReviewPackages(cwd) {
|
|
|
130455
130603
|
}
|
|
130456
130604
|
async function loadReviewPackage(cwd, reviewId) {
|
|
130457
130605
|
for (const dir of await candidateDirs2(cwd)) {
|
|
130458
|
-
if (
|
|
130606
|
+
if (path304.basename(dir) !== reviewId)
|
|
130459
130607
|
continue;
|
|
130460
130608
|
const listed = await readPackage(dir);
|
|
130461
130609
|
if (listed !== null && listed.reviewId === reviewId)
|
|
@@ -130466,7 +130614,7 @@ async function loadReviewPackage(cwd, reviewId) {
|
|
|
130466
130614
|
var init_packages = () => {};
|
|
130467
130615
|
|
|
130468
130616
|
// src/review/bot/run.ts
|
|
130469
|
-
import
|
|
130617
|
+
import path305 from "path";
|
|
130470
130618
|
function capDiff(diff, maxBytes) {
|
|
130471
130619
|
const totalBytes = Buffer.byteLength(diff);
|
|
130472
130620
|
if (totalBytes <= maxBytes)
|
|
@@ -130511,7 +130659,7 @@ function parseVerifierVerdict(reply) {
|
|
|
130511
130659
|
return unverifiable;
|
|
130512
130660
|
}
|
|
130513
130661
|
function botStatePath(cwd, repo, number) {
|
|
130514
|
-
return
|
|
130662
|
+
return path305.join(cwd, ".metaproject", "reviews", "bot", `${repo.replace(/\//g, "__")}__${number}.json`);
|
|
130515
130663
|
}
|
|
130516
130664
|
function emptyBotState(repo, number) {
|
|
130517
130665
|
return { schemaVersion: 1, repo, number, reviews: [], pull: null };
|
|
@@ -131422,7 +131570,7 @@ var init_review_bot = __esm(() => {
|
|
|
131422
131570
|
});
|
|
131423
131571
|
|
|
131424
131572
|
// src/commands/review-run.ts
|
|
131425
|
-
import
|
|
131573
|
+
import path306 from "path";
|
|
131426
131574
|
function positive3(args, name, integer) {
|
|
131427
131575
|
const raw = optionValue(args, name);
|
|
131428
131576
|
if (raw === undefined) {
|
|
@@ -131527,7 +131675,7 @@ async function runReviewRunCommand(args, deps = {}) {
|
|
|
131527
131675
|
}
|
|
131528
131676
|
const cwd = deps.cwd ?? process.cwd();
|
|
131529
131677
|
const target = parsed.target;
|
|
131530
|
-
const notifier = deps.notifier ?? (parsed.notify ? createTelegramNotifier({ project:
|
|
131678
|
+
const notifier = deps.notifier ?? (parsed.notify ? createTelegramNotifier({ project: path306.basename(cwd), target }) : NO_NOTIFIER);
|
|
131531
131679
|
const run = deps.run ?? runAutonomousReview;
|
|
131532
131680
|
const heartbeat = createHeartbeat(error, deps.now, undefined, deps.keepAliveMs ?? HEARTBEAT_KEEPALIVE_MS);
|
|
131533
131681
|
const stop = new AbortController;
|
|
@@ -131550,14 +131698,14 @@ async function runReviewRunCommand(args, deps = {}) {
|
|
|
131550
131698
|
...parsed.model === undefined ? {} : { model: parsed.model }
|
|
131551
131699
|
});
|
|
131552
131700
|
await notifier.finished(endLine(result.summary));
|
|
131553
|
-
const escalated = arbiterEscalateLine(result.summary, readArbiterLog(
|
|
131701
|
+
const escalated = arbiterEscalateLine(result.summary, readArbiterLog(path306.join(result.runDir, ARBITER_LOG_NAME)));
|
|
131554
131702
|
if (escalated !== undefined)
|
|
131555
131703
|
await notifier.finished(escalated);
|
|
131556
131704
|
if (parsed.json)
|
|
131557
131705
|
log(JSON.stringify(result.summary, null, 2));
|
|
131558
131706
|
else {
|
|
131559
131707
|
log(`${result.reused ? "reused" : result.status}: ${result.summary.reason}`);
|
|
131560
|
-
log(`report: ${
|
|
131708
|
+
log(`report: ${path306.join(result.runDir, "report.md")}`);
|
|
131561
131709
|
}
|
|
131562
131710
|
return result.exitCode;
|
|
131563
131711
|
} catch (problem) {
|
|
@@ -131615,10 +131763,10 @@ A run that really stalls is still stopped by --stall-minutes (exit 5), and the l
|
|
|
131615
131763
|
|
|
131616
131764
|
// src/review/jev-risk-config.ts
|
|
131617
131765
|
import { readFile as readFile149 } from "fs/promises";
|
|
131618
|
-
import
|
|
131766
|
+
import path307 from "path";
|
|
131619
131767
|
async function readJevRiskEnabled(cwd) {
|
|
131620
131768
|
try {
|
|
131621
|
-
const parsed = JSON.parse(await readFile149(
|
|
131769
|
+
const parsed = JSON.parse(await readFile149(path307.join(cwd, REVIEW_GATE_CONFIG_PATH), "utf8"));
|
|
131622
131770
|
if (typeof parsed !== "object" || parsed === null)
|
|
131623
131771
|
return false;
|
|
131624
131772
|
const review = parsed["review"];
|
|
@@ -131846,80 +131994,6 @@ var init_review_jev_risk = __esm(() => {
|
|
|
131846
131994
|
JEV_RISK_FLAGS = ["--diff", "--pr", "--scope", "--max-calls", "--threshold", "--model", "--repo", "--fixtures", "--json"];
|
|
131847
131995
|
});
|
|
131848
131996
|
|
|
131849
|
-
// src/review/routing-check.ts
|
|
131850
|
-
import { readFileSync as readFileSync34 } from "fs";
|
|
131851
|
-
import path307 from "path";
|
|
131852
|
-
function narrowedSlices(manifest, readText, fix) {
|
|
131853
|
-
const originals = manifest.slices.filter((entry) => entry.retryOf === undefined && Array.isArray(entry.roles));
|
|
131854
|
-
const changedFiles = [...new Set(manifest.slices.filter((entry) => entry.retryOf === undefined).flatMap((entry) => entry.files))];
|
|
131855
|
-
const narrowed = [];
|
|
131856
|
-
const unreadable = [];
|
|
131857
|
-
for (const entry of originals) {
|
|
131858
|
-
const text = readText(entry);
|
|
131859
|
-
if (text === undefined) {
|
|
131860
|
-
unreadable.push(entry.id);
|
|
131861
|
-
continue;
|
|
131862
|
-
}
|
|
131863
|
-
const needed = routeSliceRegions(entry.id, parseScopedDiff(text), { changedFiles });
|
|
131864
|
-
const backedFix = fixClaimBacked(entry, fix, text);
|
|
131865
|
-
const backedSecondLook = secondLookClaimBacked(entry, fix, text);
|
|
131866
|
-
const roles = backedSecondLook ? [...SECOND_LOOK_ROLES] : backedFix ? expectedFixRoles(entry, fix, text) : needed.roles;
|
|
131867
|
-
const recorded = new Set(entry.roles ?? []);
|
|
131868
|
-
const missingRoles = roles.filter((role) => !recorded.has(role)).sort();
|
|
131869
|
-
const shallow = (backedFix || backedSecondLook || needed.depth === "deep") && entry.depth !== "deep";
|
|
131870
|
-
if (missingRoles.length > 0 || shallow) {
|
|
131871
|
-
narrowed.push({ id: entry.id, missingRoles, ...shallow ? { depth: { recorded: entry.depth ?? "unset", needed: "deep" } } : {} });
|
|
131872
|
-
}
|
|
131873
|
-
}
|
|
131874
|
-
return { narrowed, unreadable };
|
|
131875
|
-
}
|
|
131876
|
-
function sliceTextReader(manifestDir) {
|
|
131877
|
-
return (entry) => {
|
|
131878
|
-
try {
|
|
131879
|
-
return readFileSync34(path307.resolve(manifestDir, entry.path), "utf8");
|
|
131880
|
-
} catch {
|
|
131881
|
-
return;
|
|
131882
|
-
}
|
|
131883
|
-
};
|
|
131884
|
-
}
|
|
131885
|
-
function packageRoutingRefusal(packageDir, projectRoot, options = {}) {
|
|
131886
|
-
const shared = path307.join(projectRoot, ".metaproject", "data", "review", "slices");
|
|
131887
|
-
const own = path307.join(packageDir, "slices");
|
|
131888
|
-
for (const [file, dirs] of [[path307.join(own, "manifest.json"), [own]], [path307.join(packageDir, "slice-manifest.json"), [own, shared]]]) {
|
|
131889
|
-
let manifest;
|
|
131890
|
-
try {
|
|
131891
|
-
const parsed = JSON.parse(readFileSync34(file, "utf8"));
|
|
131892
|
-
manifest = Array.isArray(parsed.slices) ? { slices: parsed.slices } : undefined;
|
|
131893
|
-
} catch {
|
|
131894
|
-
manifest = undefined;
|
|
131895
|
-
}
|
|
131896
|
-
if (manifest === undefined)
|
|
131897
|
-
continue;
|
|
131898
|
-
const readers = dirs.map((dir) => sliceTextReader(dir));
|
|
131899
|
-
const { narrowed, unreadable } = narrowedSlices(manifest, (entry) => readers.map((read) => read(entry)).find((text) => text !== undefined), fixContextOf(packageDir));
|
|
131900
|
-
const label = path307.relative(projectRoot, file) || file;
|
|
131901
|
-
const refusal = routingRefusal(label, narrowed);
|
|
131902
|
-
if (refusal !== undefined || options.refuseUnreadable !== true || unreadable.length === 0)
|
|
131903
|
-
return refusal;
|
|
131904
|
-
return `${label} lists ${unreadable.length} slice(s) whose text is missing (${unreadable.slice(0, 8).join(", ")}${unreadable.length > 8 ? ", \u2026" : ""}), so their routing cannot be checked against their content. ` + "Restore the slice files next to the manifest, or re-run `keryx review slice` and the round";
|
|
131905
|
-
}
|
|
131906
|
-
return;
|
|
131907
|
-
}
|
|
131908
|
-
function routingRefusal(manifestLabel, narrowed) {
|
|
131909
|
-
if (narrowed.length === 0)
|
|
131910
|
-
return;
|
|
131911
|
-
const lines = narrowed.slice(0, 8).map((entry) => `${entry.id}: ${[
|
|
131912
|
-
entry.missingRoles.length > 0 ? `roles ${entry.missingRoles.join(", ")} are missing` : undefined,
|
|
131913
|
-
entry.depth !== undefined ? `recorded ${entry.depth.recorded}, its content needs deep` : undefined
|
|
131914
|
-
].filter(Boolean).join("; ")}`);
|
|
131915
|
-
return `${manifestLabel} routes ${narrowed.length} slice(s) narrower than their content needs (${lines.join(" | ")}${narrowed.length > 8 ? ` | \u2026 and ${narrowed.length - 8} more` : ""}). ` + "Routing is recomputed from each slice's text and the manifest may only be wider. Re-run `keryx review slice` to route the slices again, and do not edit roles or depth by hand.";
|
|
131916
|
-
}
|
|
131917
|
-
var init_routing_check = __esm(() => {
|
|
131918
|
-
init_slice();
|
|
131919
|
-
init_slice_routing();
|
|
131920
|
-
init_round_split();
|
|
131921
|
-
});
|
|
131922
|
-
|
|
131923
131997
|
// src/commands/review-ledger.ts
|
|
131924
131998
|
import { existsSync as existsSync66 } from "fs";
|
|
131925
131999
|
import { createHash as createHash84 } from "crypto";
|
|
@@ -146555,19 +146629,12 @@ async function runComplete2(args, seams) {
|
|
|
146555
146629
|
}
|
|
146556
146630
|
const isTerminal = seams?.isTerminal ?? (() => process.stdin.isTTY === true && process.stdout.isTTY === true);
|
|
146557
146631
|
if (waiver !== undefined && !isTerminal()) {
|
|
146558
|
-
throw new Error("--waive-coverage needs a terminal: the waiver is the operator's own statement and is refused when stdin or stdout is not a TTY. Inside keryx shell, call the review_waive_coverage tool: the operator answers it as a card, then
|
|
146632
|
+
throw new Error("--waive-coverage needs a terminal: the waiver is the operator's own statement and is refused when stdin or stdout is not a TTY. Inside keryx shell, call the review_waive_coverage tool: the operator answers it as a card, then call the review_complete card. Outside keryx shell, run this command in a terminal.");
|
|
146559
146633
|
}
|
|
146560
146634
|
const readinessWaivers = parseReadinessWaivers(args);
|
|
146561
146635
|
if (readinessWaivers.length > 0 && !isTerminal()) {
|
|
146562
|
-
throw new Error("--waive needs a terminal: the waiver is the operator's own statement and is refused when stdin or stdout is not a TTY. Inside keryx shell, call the review_waive_readiness tool: the operator answers it as a card, then
|
|
146636
|
+
throw new Error("--waive needs a terminal: the waiver is the operator's own statement and is refused when stdin or stdout is not a TTY. Inside keryx shell, call the review_waive_readiness tool: the operator answers it as a card, then call the review_complete card. Outside keryx shell, run this command in a terminal.");
|
|
146563
146637
|
}
|
|
146564
|
-
const completeDir = await resolveReviewPackagePath(process.cwd(), ref);
|
|
146565
|
-
const profileMissing = runProfileRefusal(completeDir);
|
|
146566
|
-
if (profileMissing !== undefined)
|
|
146567
|
-
throw new Error(`review complete refused: ${profileMissing}`);
|
|
146568
|
-
const narrowedRouting = packageRoutingRefusal(completeDir, resolveProjectRoot2(process.cwd()), { refuseUnreadable: true });
|
|
146569
|
-
if (narrowedRouting !== undefined)
|
|
146570
|
-
throw new Error(`review complete refused: ${narrowedRouting}`);
|
|
146571
146638
|
const {
|
|
146572
146639
|
manifest,
|
|
146573
146640
|
reviewNotes,
|
|
@@ -146575,7 +146642,7 @@ async function runComplete2(args, seams) {
|
|
|
146575
146642
|
coverageWaiver,
|
|
146576
146643
|
readiness,
|
|
146577
146644
|
warnings
|
|
146578
|
-
} = await
|
|
146645
|
+
} = await completeReviewGated(process.cwd(), ref, { dispositions, prLookup: seams?.prLookup ?? trackerPrLookup, prFiles: seams?.prFiles ?? githubPrFiles, ...readinessWaivers.length === 0 ? {} : { waiveReadiness: readinessWaivers }, ...waiver?.value === undefined ? {} : { waiveCoverage: waiver.value }, ...headFlag?.value === undefined ? {} : { head: headFlag.value } });
|
|
146579
146646
|
for (const warning of warnings)
|
|
146580
146647
|
console.error(`warning: ${warning}`);
|
|
146581
146648
|
if (coverageWaiver !== undefined) {
|
|
@@ -147431,7 +147498,7 @@ var init_review = __esm(() => {
|
|
|
147431
147498
|
init_host_profile();
|
|
147432
147499
|
init_skill_drift();
|
|
147433
147500
|
init_run_profile();
|
|
147434
|
-
|
|
147501
|
+
init_complete_gated();
|
|
147435
147502
|
init_review_operator();
|
|
147436
147503
|
init_review_chunks();
|
|
147437
147504
|
init_review_layers();
|
|
@@ -147688,8 +147755,22 @@ process; past the bound the package is still created, the command returns, and t
|
|
|
147688
147755
|
});
|
|
147689
147756
|
|
|
147690
147757
|
// src/commands/operator-confirm-review.ts
|
|
147691
|
-
import {
|
|
147758
|
+
import { createHash as createHash85 } from "crypto";
|
|
147759
|
+
import { existsSync as existsSync75, readFileSync as readFileSync39, statSync as statSync21 } from "fs";
|
|
147692
147760
|
import path337 from "path";
|
|
147761
|
+
async function defaultReplyPass(args, dryRun) {
|
|
147762
|
+
await Promise.resolve().then(() => init_review());
|
|
147763
|
+
return commentsReplyPass(args, dryRun);
|
|
147764
|
+
}
|
|
147765
|
+
function shownBody(text) {
|
|
147766
|
+
const lines = text.split(/\r?\n/).map((line) => line.replace(/\t/g, " "));
|
|
147767
|
+
while (lines.length > 1 && lines[lines.length - 1] === "")
|
|
147768
|
+
lines.pop();
|
|
147769
|
+
const bad = lines.findIndex((line) => hiddenInCard(line));
|
|
147770
|
+
if (bad !== -1)
|
|
147771
|
+
return { refusal: `holds control, escape, bidi or zero-width characters on line ${bad + 1} that the card would hide or rewrite; it is not shown, so it is not asked` };
|
|
147772
|
+
return { lines: lines.map((line) => ` ${line}`) };
|
|
147773
|
+
}
|
|
147693
147774
|
async function trackerPrLookup2(ref) {
|
|
147694
147775
|
if (!await githubAdapter.detect())
|
|
147695
147776
|
return null;
|
|
@@ -147708,13 +147789,12 @@ async function resolvePackage(ref, projectRoot) {
|
|
|
147708
147789
|
return resolveReviewPackagePath(projectRoot, ref);
|
|
147709
147790
|
}
|
|
147710
147791
|
function listFiles3(files) {
|
|
147711
|
-
|
|
147712
|
-
return files.length > LISTED_FILES ? [...shown, ` \u2026 and ${files.length - LISTED_FILES} more`] : shown;
|
|
147792
|
+
return files.map((file) => ` ${file}`);
|
|
147713
147793
|
}
|
|
147714
147794
|
function chunksAnswerSpec() {
|
|
147715
147795
|
return {
|
|
147716
147796
|
name: "review_chunks_answer",
|
|
147717
|
-
description: "Record the operator's answer to the chunk-run question: a blast-radius chunk run needs more review passes than the ceiling allows.
|
|
147797
|
+
description: "Record the operator's answer to the chunk-run question: a blast-radius chunk run needs more review passes than the ceiling allows. Pass the review package and the answer you are asking for (yes lifts the ceiling for this plan, no stops the run). Same effect as `keryx review chunks estimate --answer`.",
|
|
147718
147798
|
inputSchema: {
|
|
147719
147799
|
type: "object",
|
|
147720
147800
|
properties: { package: packageSchema, answer: { type: "string", enum: ["yes", "no"], description: "The answer to record if the operator confirms." } },
|
|
@@ -147764,7 +147844,7 @@ function chunksAnswerSpec() {
|
|
|
147764
147844
|
return { output: "The chunk plan changed after the card was shown; nothing was recorded. Ask again.", isError: true };
|
|
147765
147845
|
}
|
|
147766
147846
|
await recordChunkEstimate(prepared.dir, estimate, chunkModeOf(prepared.dir));
|
|
147767
|
-
const record = await answerChunkEstimate(prepared.dir, prepared.answer, confirmation.quote, new Date(confirmation.at), confirmation.confirmedVia);
|
|
147847
|
+
const record = await answerChunkEstimate(prepared.dir, prepared.answer, confirmation.quote, new Date(confirmation.at), confirmation.confirmedVia, cardPressFields(confirmation));
|
|
147768
147848
|
return { output: `chunk run answered ${record.answer?.value ?? prepared.answer} by the operator (${confirmation.confirmedVia}, ${confirmation.at}); ${estimate.chunks} pass(es) against a ceiling of ${estimate.maxPasses}` };
|
|
147769
147849
|
}
|
|
147770
147850
|
};
|
|
@@ -147774,7 +147854,7 @@ function publishSpec(seams) {
|
|
|
147774
147854
|
const post = () => seams.postReview ?? githubPostReview;
|
|
147775
147855
|
return {
|
|
147776
147856
|
name: "review_publish",
|
|
147777
|
-
description: "Post a finished review to its pull request on GitHub. Pass the review package, optionally the event (COMMENT, REQUEST_CHANGES, APPROVE; default COMMENT) and partial=true to post a review whose readiness checklist still has failing items (the page then says what is missing).
|
|
147857
|
+
description: "Post a finished review to its pull request on GitHub. Pass the review package, optionally the event (COMMENT, REQUEST_CHANGES, APPROVE; default COMMENT) and partial=true to post a review whose readiness checklist still has failing items (the page then says what is missing). Same effect as `keryx review publish`; the operator sees the target, the verdict and the whole body first (a body with hidden or control characters is refused; a card over 3000 characters is answered in the shell, not in the Telegram topic).",
|
|
147778
147858
|
inputSchema: {
|
|
147779
147859
|
type: "object",
|
|
147780
147860
|
properties: {
|
|
@@ -147804,8 +147884,10 @@ function publishSpec(seams) {
|
|
|
147804
147884
|
return false;
|
|
147805
147885
|
}
|
|
147806
147886
|
});
|
|
147807
|
-
const
|
|
147808
|
-
|
|
147887
|
+
const shownReview = shownBody(result.body);
|
|
147888
|
+
if ("refusal" in shownReview)
|
|
147889
|
+
return { refusal: `The review body ${shownReview.refusal}` };
|
|
147890
|
+
const bodyLines = shownReview.lines;
|
|
147809
147891
|
const failing = result.readiness.items.filter((item) => item.status === "fail").map((item) => item.id);
|
|
147810
147892
|
const name = label3(dir, ctx.projectRoot);
|
|
147811
147893
|
return {
|
|
@@ -147814,8 +147896,8 @@ function publishSpec(seams) {
|
|
|
147814
147896
|
`target: ${cardSafe(target)}`,
|
|
147815
147897
|
`verdict: ${result.event}`,
|
|
147816
147898
|
result.partial ? `partial: yes \u2014 readiness is failing: ${cardSafe(failing.join(", "))}` : "partial: no \u2014 the readiness checklist passes",
|
|
147817
|
-
`body: ${result.body.length} characters, ${bodyLines.length} lines
|
|
147818
|
-
...
|
|
147899
|
+
`body: ${result.body.length} characters, ${bodyLines.length} lines, shown whole:`,
|
|
147900
|
+
...bodyLines,
|
|
147819
147901
|
"yes: the review is posted to GitHub and cannot be taken back from here"
|
|
147820
147902
|
],
|
|
147821
147903
|
subject: `${cardSafe(target)} (package ${name})`,
|
|
@@ -147836,6 +147918,7 @@ function publishSpec(seams) {
|
|
|
147836
147918
|
post: post(),
|
|
147837
147919
|
now: () => new Date(confirmation.at),
|
|
147838
147920
|
confirmedVia: confirmation.confirmedVia,
|
|
147921
|
+
...cardPressFields(confirmation),
|
|
147839
147922
|
confirm: async (body, targetRef) => {
|
|
147840
147923
|
changed = body !== prepared.body || targetRef !== prepared.ref;
|
|
147841
147924
|
return !changed;
|
|
@@ -147851,7 +147934,7 @@ function publishSpec(seams) {
|
|
|
147851
147934
|
function waiveCoverageSpec() {
|
|
147852
147935
|
return {
|
|
147853
147936
|
name: "review_waive_coverage",
|
|
147854
|
-
description: "Accept that the in-scope files no review slice or chunk covered stay unreviewed, so `keryx review complete` passes its coverage gate. Pass the review package.
|
|
147937
|
+
description: "Accept that the in-scope files no review slice or chunk covered stay unreviewed, so `keryx review complete` passes its coverage gate. Pass the review package. Same effect as `keryx review complete --waive-coverage`: records the waiver (the operator's yes, the time, the files); call review_complete afterwards to close the review.",
|
|
147855
147938
|
inputSchema: { type: "object", properties: { package: packageSchema }, required: ["package"], additionalProperties: false },
|
|
147856
147939
|
async buildCard(input, ctx) {
|
|
147857
147940
|
const ref = packageRef(input);
|
|
@@ -147887,10 +147970,10 @@ function waiveCoverageSpec() {
|
|
|
147887
147970
|
if (now.length !== prepared.files.length || now.some((file, index) => file !== prepared.files[index])) {
|
|
147888
147971
|
return { output: "The uncovered files changed after the card was shown; nothing was recorded. Ask again.", isError: true };
|
|
147889
147972
|
}
|
|
147890
|
-
const waiver = await recordCoverageWaiver(prepared.dir, confirmation.quote, new Date(confirmation.at), confirmation.confirmedVia);
|
|
147973
|
+
const waiver = await recordCoverageWaiver(prepared.dir, confirmation.quote, new Date(confirmation.at), confirmation.confirmedVia, cardPressFields(confirmation));
|
|
147891
147974
|
if (waiver === undefined)
|
|
147892
147975
|
return { output: "No coverage waiver was recorded: the package holds no dispatch record or nothing is uncovered.", isError: true };
|
|
147893
|
-
return { output: `coverage waived by the operator (${waiver.confirmedVia ?? "unknown"}, ${waiver.time}): ${waiver.files.length} file(s) not reviewed.
|
|
147976
|
+
return { output: `coverage waived by the operator (${waiver.confirmedVia ?? "unknown"}, ${waiver.time}): ${waiver.files.length} file(s) not reviewed. Call review_complete to close the review.` };
|
|
147894
147977
|
}
|
|
147895
147978
|
};
|
|
147896
147979
|
}
|
|
@@ -147898,7 +147981,7 @@ function waiveReadinessSpec(seams) {
|
|
|
147898
147981
|
const readiness = () => ({ prLookup: seams.prLookup ?? trackerPrLookup2, prFiles: seams.prFiles ?? githubPrFiles });
|
|
147899
147982
|
return {
|
|
147900
147983
|
name: "review_waive_readiness",
|
|
147901
|
-
description: "Accept one failing item of the review readiness checklist as it is, so the review can complete or be published as a partial review. Pass the review package and the item (its number or id).
|
|
147984
|
+
description: "Accept one failing item of the review readiness checklist as it is, so the review can complete or be published as a partial review. Pass the review package and the item (its number or id). Same effect as `keryx review complete --waive <item>`: records the waiver (the operator's yes, the time) in the manifest and the report header; call review_complete afterwards to close the review.",
|
|
147902
147985
|
inputSchema: {
|
|
147903
147986
|
type: "object",
|
|
147904
147987
|
properties: { package: packageSchema, item: { type: "string", description: `The readiness item: its number (1-${READINESS_ITEMS.length}) or id, e.g. head, pr-files or quotes-located. Item ${READINESS_ITEMS.find((entry) => entry.id === "premise-decisions")?.number ?? "premise-decisions"} (premise-decisions) is never waived: each held finding is answered with review_decide_premise.` } },
|
|
@@ -147950,8 +148033,8 @@ function waiveReadinessSpec(seams) {
|
|
|
147950
148033
|
if (current === undefined || current.status !== "fail" || current.evidence !== prepared.evidence) {
|
|
147951
148034
|
return { output: "The readiness item changed after the card was shown; nothing was recorded. Ask again.", isError: true };
|
|
147952
148035
|
}
|
|
147953
|
-
const added = await waiveReadiness(prepared.dir, [prepared.item], confirmation.quote, { ...readiness(), now: new Date(confirmation.at), confirmedVia: confirmation.confirmedVia });
|
|
147954
|
-
return { output: `readiness item waived by the operator (${confirmation.confirmedVia}, ${confirmation.at}): ${added.map((waiver) => waiver.item).join(", ")}.
|
|
148036
|
+
const added = await waiveReadiness(prepared.dir, [prepared.item], confirmation.quote, { ...readiness(), now: new Date(confirmation.at), confirmedVia: confirmation.confirmedVia, ...cardPressFields(confirmation) });
|
|
148037
|
+
return { output: `readiness item waived by the operator (${confirmation.confirmedVia}, ${confirmation.at}): ${added.map((waiver) => waiver.item).join(", ")}. Call review_complete to close the review.` };
|
|
147955
148038
|
}
|
|
147956
148039
|
};
|
|
147957
148040
|
}
|
|
@@ -147959,7 +148042,7 @@ function decisionSpec(kind) {
|
|
|
147959
148042
|
const mutation = kind === "mutation-waiver";
|
|
147960
148043
|
return {
|
|
147961
148044
|
name: mutation ? "review_waive_mutation" : "review_override_reviewers",
|
|
147962
|
-
description: mutation ? "Record the operator's waiver of the mutation pass: the proposed mutants stay unrun and the review records `Not run: mutation pass`. Pass the review package and the reason.
|
|
148045
|
+
description: mutation ? "Record the operator's waiver of the mutation pass: the proposed mutants stay unrun and the review records `Not run: mutation pass`. Pass the review package and the reason. Same effect as `keryx review waive-mutation` at a terminal. A mutationWaiver quote you write into the dispatch is not accepted on its own." : "Record the operator's decision to run a smaller reviewer set than `keryx review required` names (an override, or mode diff). Pass the review package, the reason and, optionally, the reviewers that will run. Same effect as `keryx review override-reviewers` at a terminal. An override quote you write into the dispatch is not accepted on its own.",
|
|
147963
148046
|
inputSchema: {
|
|
147964
148047
|
type: "object",
|
|
147965
148048
|
properties: {
|
|
@@ -147972,11 +148055,12 @@ function decisionSpec(kind) {
|
|
|
147972
148055
|
},
|
|
147973
148056
|
async buildCard(input, ctx) {
|
|
147974
148057
|
const ref = packageRef(input);
|
|
147975
|
-
const
|
|
148058
|
+
const given = reasonOf2(input);
|
|
147976
148059
|
if (ref === undefined)
|
|
147977
148060
|
return { refusal: "package is required" };
|
|
147978
|
-
if (
|
|
147979
|
-
return
|
|
148061
|
+
if ("refusal" in given)
|
|
148062
|
+
return given;
|
|
148063
|
+
const { reason } = given;
|
|
147980
148064
|
try {
|
|
147981
148065
|
const dir = await resolvePackage(ref, ctx.projectRoot);
|
|
147982
148066
|
const name = label3(dir, ctx.projectRoot);
|
|
@@ -147988,7 +148072,7 @@ function decisionSpec(kind) {
|
|
|
147988
148072
|
return {
|
|
147989
148073
|
title: `Waive the mutation pass for ${name}: ${pending} proposed mutant(s) stay unrun?`,
|
|
147990
148074
|
lines: [
|
|
147991
|
-
`unrun mutants: ${pending} (research-evidence/mutants.json): ${cardSafe(subject.items.
|
|
148075
|
+
`unrun mutants: ${pending} (research-evidence/mutants.json): ${cardSafe(subject.items.join(", "))}`,
|
|
147992
148076
|
`head: ${cardSafe(subject.head ?? "not recorded")}`,
|
|
147993
148077
|
`reason: ${cardSafe(reason)}`,
|
|
147994
148078
|
"yes: the review records `Not run: mutation pass` with your words for these mutants at this head; a new head or a new mutant asks again"
|
|
@@ -148007,7 +148091,7 @@ function decisionSpec(kind) {
|
|
|
148007
148091
|
`required (mode all): ${required.length} reviewer(s)`,
|
|
148008
148092
|
`head: ${cardSafe(subject.head ?? "not recorded")}`,
|
|
148009
148093
|
selected === undefined ? "selected: not named" : `selected: ${cardSafe(selected.join(", "))}`,
|
|
148010
|
-
`not run: ${cardSafe(missing.
|
|
148094
|
+
`not run: ${cardSafe(missing.join(", ") || "none")}`,
|
|
148011
148095
|
`reason: ${cardSafe(reason)}`,
|
|
148012
148096
|
"yes: the gate accepts leaving out exactly these reviewers at this head; dropping another one or a new head asks again"
|
|
148013
148097
|
],
|
|
@@ -148026,7 +148110,7 @@ function decisionSpec(kind) {
|
|
|
148026
148110
|
if (JSON.stringify(again) !== JSON.stringify(prepared.subject)) {
|
|
148027
148111
|
return { output: "The head, the unrun mutants or the reviewer set changed after the card was shown; nothing was recorded. Ask again.", isError: true };
|
|
148028
148112
|
}
|
|
148029
|
-
await recordOperatorDecision(prepared.dir, { kind, quote: confirmation.quote, reason: prepared.reason, confirmedVia: confirmation.confirmedVia, at: confirmation.at, subject: prepared.subject });
|
|
148113
|
+
await recordOperatorDecision(prepared.dir, { kind, quote: confirmation.quote, reason: prepared.reason, confirmedVia: confirmation.confirmedVia, ...cardPressFields(confirmation), at: confirmation.at, subject: prepared.subject });
|
|
148030
148114
|
return { output: `${mutation ? "mutation pass waived" : "reviewer-set override recorded"} by the operator (${confirmation.confirmedVia}, ${confirmation.at}) in operator-decisions.json` };
|
|
148031
148115
|
}
|
|
148032
148116
|
};
|
|
@@ -148051,10 +148135,11 @@ function replyArgv(input) {
|
|
|
148051
148135
|
argv.push("--round", String(input["round"]));
|
|
148052
148136
|
return argv;
|
|
148053
148137
|
}
|
|
148054
|
-
function replySpec() {
|
|
148138
|
+
function replySpec(seams) {
|
|
148139
|
+
const pass = seams.replyPass ?? defaultReplyPass;
|
|
148055
148140
|
return {
|
|
148056
148141
|
name: "review_comments_reply",
|
|
148057
|
-
description: "Post the final review-comment replies on a pull request (`keryx review comments reply --final`). Pass the repo, the PR number, its head sha, the outcomes file and the review package or result.
|
|
148142
|
+
description: "Post the final review-comment replies on a pull request (`keryx review comments reply --final`). Pass the repo, the PR number, its head sha, the outcomes file and the review package or result. The operator sees every reply whole before anything is posted (a reply with hidden or control characters is refused; a card over 3000 characters is answered in the shell, not in the Telegram topic).",
|
|
148058
148143
|
inputSchema: {
|
|
148059
148144
|
type: "object",
|
|
148060
148145
|
properties: {
|
|
@@ -148075,15 +148160,20 @@ function replySpec() {
|
|
|
148075
148160
|
if (!Array.isArray(argv))
|
|
148076
148161
|
return argv;
|
|
148077
148162
|
try {
|
|
148078
|
-
await
|
|
148079
|
-
const dry = await commentsReplyPass([...argv], true);
|
|
148163
|
+
const dry = await pass([...argv], true);
|
|
148080
148164
|
const requests = dry.result.requests;
|
|
148081
148165
|
if (requests.length === 0)
|
|
148082
148166
|
return { refusal: "The reply pass has nothing to post." };
|
|
148083
|
-
const bodies =
|
|
148167
|
+
const bodies = [];
|
|
148168
|
+
for (const [index, request] of requests.entries()) {
|
|
148169
|
+
const shown = shownBody(request.body.body ?? "");
|
|
148170
|
+
if ("refusal" in shown)
|
|
148171
|
+
return { refusal: `Reply ${index + 1} ${shown.refusal}` };
|
|
148172
|
+
bodies.push(`reply ${index + 1}:`, ...shown.lines);
|
|
148173
|
+
}
|
|
148084
148174
|
return {
|
|
148085
148175
|
title: `Post ${requests.length} reply(ies) on ${cardSafe(dry.repo)}#${dry.number}?`,
|
|
148086
|
-
lines: [`replies: ${requests.length}
|
|
148176
|
+
lines: [`replies: ${requests.length}, each shown whole:`, ...bodies, "yes: the replies are posted on the pull request and cannot be taken back from here"],
|
|
148087
148177
|
subject: `${cardSafe(dry.repo)}#${dry.number}`,
|
|
148088
148178
|
prepared: { argv, requests: JSON.stringify(requests) }
|
|
148089
148179
|
};
|
|
@@ -148094,11 +148184,10 @@ function replySpec() {
|
|
|
148094
148184
|
async apply(_input, _ctx, confirmation, prepared) {
|
|
148095
148185
|
if (prepared === undefined)
|
|
148096
148186
|
return { output: "review_comments_reply: nothing prepared for this card", isError: true };
|
|
148097
|
-
await
|
|
148098
|
-
const again = await commentsReplyPass([...prepared.argv], true);
|
|
148187
|
+
const again = await pass([...prepared.argv], true);
|
|
148099
148188
|
if (JSON.stringify(again.result.requests) !== prepared.requests)
|
|
148100
148189
|
return { output: "The replies changed after the card was shown; nothing was posted. Ask again.", isError: true };
|
|
148101
|
-
const posted = await
|
|
148190
|
+
const posted = await pass([...prepared.argv], false);
|
|
148102
148191
|
return { output: `posted ${posted.result.posted.length} reply(ies) (${confirmation.confirmedVia}, ${confirmation.at}); skipped ${posted.result.skipped.length}` };
|
|
148103
148192
|
}
|
|
148104
148193
|
};
|
|
@@ -148125,7 +148214,7 @@ function confirmPlanSpec() {
|
|
|
148125
148214
|
`dispatches: ${estimate.dispatches} (${estimate.roles} roles, ${estimate.slices} slices, ${estimate.sequentialBatches} batch(es) one after another)`,
|
|
148126
148215
|
`expected duration: ${estimate.minutes.min}-${estimate.minutes.max} min`,
|
|
148127
148216
|
`threshold: ${estimate.thresholds.maxDispatches} dispatches or ${estimate.thresholds.maxMinutes} min (${estimate.overReasons.join("; ")})`,
|
|
148128
|
-
...estimate.perRole.
|
|
148217
|
+
...estimate.perRole.map((role) => ` ${cardSafe(role.role)}: ${role.slices} slice(s), ${role.waves} wave(s)`),
|
|
148129
148218
|
"yes: this plan may run; a re-cut plan asks again"
|
|
148130
148219
|
],
|
|
148131
148220
|
subject: `package ${name}`,
|
|
@@ -148141,7 +148230,7 @@ function confirmPlanSpec() {
|
|
|
148141
148230
|
const estimate = await estimatePlan(prepared.dir);
|
|
148142
148231
|
if (planKeyOf2(estimate) !== prepared.planKey)
|
|
148143
148232
|
return { output: "The plan changed after the card was shown; nothing was recorded. Ask again.", isError: true };
|
|
148144
|
-
const record = await recordPlanConfirmation(prepared.dir, estimate, { quote: confirmation.quote, confirmedVia: confirmation.confirmedVia, at: confirmation.at });
|
|
148233
|
+
const record = await recordPlanConfirmation(prepared.dir, estimate, { quote: confirmation.quote, confirmedVia: confirmation.confirmedVia, ...cardPressFields(confirmation), at: confirmation.at });
|
|
148145
148234
|
return { output: `plan confirmed by the operator (${record.confirmedVia}, ${record.at}): ${record.dispatches} dispatch(es), ${record.minutes[0]}-${record.minutes[1]} min` };
|
|
148146
148235
|
}
|
|
148147
148236
|
};
|
|
@@ -148163,7 +148252,7 @@ function decidePremiseSpec() {
|
|
|
148163
148252
|
},
|
|
148164
148253
|
async buildCard(input, ctx) {
|
|
148165
148254
|
const ref = packageRef(input);
|
|
148166
|
-
const
|
|
148255
|
+
const given = reasonOf2(input);
|
|
148167
148256
|
const finding = typeof input["finding"] === "string" ? input["finding"].trim() : "";
|
|
148168
148257
|
const answer = input["answer"];
|
|
148169
148258
|
if (ref === undefined)
|
|
@@ -148172,8 +148261,9 @@ function decidePremiseSpec() {
|
|
|
148172
148261
|
return { refusal: "finding is required" };
|
|
148173
148262
|
if (answer !== "uphold" && answer !== "dismiss")
|
|
148174
148263
|
return { refusal: 'answer must be "uphold" or "dismiss"' };
|
|
148175
|
-
if (
|
|
148176
|
-
return
|
|
148264
|
+
if ("refusal" in given)
|
|
148265
|
+
return given;
|
|
148266
|
+
const { reason } = given;
|
|
148177
148267
|
try {
|
|
148178
148268
|
const dir = await resolvePackage(ref, ctx.projectRoot);
|
|
148179
148269
|
const held = await findHeldFinding(dir, finding);
|
|
@@ -148205,12 +148295,196 @@ function decidePremiseSpec() {
|
|
|
148205
148295
|
return { output: "The finding changed after the card was shown; nothing was recorded. Ask again.", isError: true };
|
|
148206
148296
|
}
|
|
148207
148297
|
const warnings = [];
|
|
148208
|
-
await answerPremiseQuestion(prepared.dir, { finding: again.finding, answer: prepared.answer, reason: prepared.reason, quote: confirmation.quote, at: confirmation.at, confirmedVia: confirmation.confirmedVia }, { cwd: ctx.projectRoot, warn: (line) => warnings.push(`warning: ${line}`) });
|
|
148298
|
+
await answerPremiseQuestion(prepared.dir, { finding: again.finding, answer: prepared.answer, reason: prepared.reason, quote: confirmation.quote, at: confirmation.at, confirmedVia: confirmation.confirmedVia, ...cardPressFields(confirmation) }, { cwd: ctx.projectRoot, warn: (line) => warnings.push(`warning: ${line}`) });
|
|
148209
148299
|
return { output: [`premise question on ${prepared.finding} answered ${prepared.answer} by the operator (${confirmation.confirmedVia}, ${confirmation.at}) in operator-decisions.json and findings.json`, ...warnings].join(`
|
|
148210
148300
|
`) };
|
|
148211
148301
|
}
|
|
148212
148302
|
};
|
|
148213
148303
|
}
|
|
148304
|
+
function completionDispositions(input) {
|
|
148305
|
+
const raw = input["dispositions"];
|
|
148306
|
+
if (raw === undefined)
|
|
148307
|
+
return [];
|
|
148308
|
+
if (!Array.isArray(raw))
|
|
148309
|
+
return { refusal: "dispositions must be an array of { finding, disposition, evidence? }" };
|
|
148310
|
+
const records = [];
|
|
148311
|
+
for (const [index, entry] of raw.entries()) {
|
|
148312
|
+
const item = typeof entry === "object" && entry !== null ? entry : {};
|
|
148313
|
+
const finding = typeof item["finding"] === "string" ? item["finding"].trim() : "";
|
|
148314
|
+
const state = item["disposition"];
|
|
148315
|
+
const evidence = item["evidence"];
|
|
148316
|
+
if (finding === "")
|
|
148317
|
+
return { refusal: `dispositions[${index}]: finding is required` };
|
|
148318
|
+
if (typeof state !== "string" || !FINDING_DISPOSITION_STATES.includes(state)) {
|
|
148319
|
+
return { refusal: `dispositions[${index}]: disposition must be one of ${FINDING_DISPOSITION_STATES.join(", ")}` };
|
|
148320
|
+
}
|
|
148321
|
+
if (evidence !== undefined && typeof evidence !== "string")
|
|
148322
|
+
return { refusal: `dispositions[${index}]: evidence must be text` };
|
|
148323
|
+
const text = evidence?.trim();
|
|
148324
|
+
if (finding.length > MAX_DISPOSITION_FINDING_CHARS)
|
|
148325
|
+
return { refusal: `dispositions[${index}]: finding is longer than ${MAX_DISPOSITION_FINDING_CHARS} characters` };
|
|
148326
|
+
if (text !== undefined && text.length > MAX_DISPOSITION_EVIDENCE_CHARS) {
|
|
148327
|
+
return { refusal: `dispositions[${index}]: evidence is ${text.length} characters; the card shows it whole, so the limit is ${MAX_DISPOSITION_EVIDENCE_CHARS}` };
|
|
148328
|
+
}
|
|
148329
|
+
for (const [field, value] of [["finding", finding], ["evidence", text ?? ""]]) {
|
|
148330
|
+
if (hiddenInCard(value))
|
|
148331
|
+
return { refusal: `dispositions[${index}]: ${field} ${HIDDEN_IN_CARD}` };
|
|
148332
|
+
}
|
|
148333
|
+
records.push({ finding, state, ...text === undefined || text === "" ? {} : { evidence: text } });
|
|
148334
|
+
}
|
|
148335
|
+
return records;
|
|
148336
|
+
}
|
|
148337
|
+
async function completionFacts(dir) {
|
|
148338
|
+
const manifest = JSON.parse(readFileSync39(path337.join(dir, "manifest.json"), "utf8"));
|
|
148339
|
+
const recorded = typeof manifest.target?.head === "string" && manifest.target.head !== "" ? manifest.target.head : undefined;
|
|
148340
|
+
const report = readFileSync39(path337.join(dir, "report.md"), "utf8");
|
|
148341
|
+
const evidenceLine = evidenceLevelLine(report);
|
|
148342
|
+
const waivers = (manifest.readiness_waivers ?? []).map((waiver) => `waiver: readiness item ${cardSafe(waiver.item)}: "${quoteWhole(waiver.operator_quote)}"`);
|
|
148343
|
+
let coverage;
|
|
148344
|
+
try {
|
|
148345
|
+
coverage = readCoverageWaiver(JSON.parse(readFileSync39(path337.join(dir, "research.json"), "utf8")).dispatch);
|
|
148346
|
+
} catch (error) {
|
|
148347
|
+
if (error.code !== "ENOENT") {
|
|
148348
|
+
waivers.push(`waiver: coverage, could not read research.json (${cardSafe(error instanceof Error ? error.message : String(error))}); the research gate will refuse`);
|
|
148349
|
+
}
|
|
148350
|
+
coverage = undefined;
|
|
148351
|
+
}
|
|
148352
|
+
if (coverage !== undefined)
|
|
148353
|
+
waivers.push(`waiver: coverage, ${coverage.files.length} file(s) not reviewed: "${quoteWhole(coverage.operatorQuote)}"`);
|
|
148354
|
+
for (const decision of await readOperatorDecisions(dir)) {
|
|
148355
|
+
const items = decision.subject?.items ?? [];
|
|
148356
|
+
const what = decision.kind === "mutation-waiver" ? "mutation pass not run" : "reviewer override, left out";
|
|
148357
|
+
waivers.push(`waiver: ${what}${items.length === 0 ? "" : ` ${cardSafe(items.join(", "))}`}: "${quoteWhole(decision.quote)}" (${decision.confirmedVia})`);
|
|
148358
|
+
}
|
|
148359
|
+
return {
|
|
148360
|
+
reviewId: manifest.reviewId ?? path337.basename(dir),
|
|
148361
|
+
head: recorded === undefined ? undefined : cardSafe(recorded),
|
|
148362
|
+
evidence: evidenceLine === undefined ? "evidence level: the report has no `Evidence level:` line; the gate will refuse" : cardSafe(evidenceLine),
|
|
148363
|
+
waivers,
|
|
148364
|
+
status: manifest.status ?? ""
|
|
148365
|
+
};
|
|
148366
|
+
}
|
|
148367
|
+
function quoteWhole(text) {
|
|
148368
|
+
return hiddenInCard(text) ? `${cardSafe(text)} [the stored quote holds characters this card hides or rewrites]` : text;
|
|
148369
|
+
}
|
|
148370
|
+
function completeSpec(seams) {
|
|
148371
|
+
return {
|
|
148372
|
+
name: "review_complete",
|
|
148373
|
+
description: "Close a review round: the operator sees the reviewed head SHA, the report's evidence level and every waiver the package carries (readiness, coverage, mutation, reviewer override), and says yes before the round is closed. Pass the review package; the round closes against the head the package recorded. Same gates and same effect as `keryx review complete` at a terminal; record the waivers first with review_waive_readiness or review_waive_coverage. A fix round passes `dispositions` (finding, disposition, evidence) for the earlier findings; the card lists each one. A refused gate closes nothing.",
|
|
148374
|
+
inputSchema: {
|
|
148375
|
+
type: "object",
|
|
148376
|
+
properties: {
|
|
148377
|
+
package: packageSchema,
|
|
148378
|
+
dispositions: dispositionsSchema
|
|
148379
|
+
},
|
|
148380
|
+
required: ["package"],
|
|
148381
|
+
additionalProperties: false
|
|
148382
|
+
},
|
|
148383
|
+
async buildCard(input, ctx) {
|
|
148384
|
+
const ref = packageRef(input);
|
|
148385
|
+
if (ref === undefined)
|
|
148386
|
+
return { refusal: "package is required" };
|
|
148387
|
+
const dispositions = completionDispositions(input);
|
|
148388
|
+
if ("refusal" in dispositions)
|
|
148389
|
+
return dispositions;
|
|
148390
|
+
try {
|
|
148391
|
+
const dir = await resolvePackage(ref, ctx.projectRoot);
|
|
148392
|
+
const facts = await completionFacts(dir);
|
|
148393
|
+
if (facts.status === "closed")
|
|
148394
|
+
return { refusal: `${facts.reviewId} is already complete` };
|
|
148395
|
+
if (dispositions.length > 0)
|
|
148396
|
+
await checkDispositions(dir, dispositions);
|
|
148397
|
+
const name = label3(dir, ctx.projectRoot);
|
|
148398
|
+
const at = facts.head === undefined ? "" : ` at ${facts.head.slice(0, 12)}`;
|
|
148399
|
+
return {
|
|
148400
|
+
title: `Complete review ${cardSafe(facts.reviewId)}${at}?`,
|
|
148401
|
+
lines: [
|
|
148402
|
+
facts.head === undefined ? "reviewed head: not recorded (the package does not carry a commit)" : `reviewed head: ${facts.head}`,
|
|
148403
|
+
facts.evidence,
|
|
148404
|
+
...facts.waivers.length === 0 ? ["waivers: none"] : facts.waivers,
|
|
148405
|
+
...dispositions.map(dispositionLine),
|
|
148406
|
+
"yes: the head, evidence-level, research and readiness gates run now; a refusal closes nothing"
|
|
148407
|
+
],
|
|
148408
|
+
subject: `package ${name}${facts.head === undefined ? "" : `, head ${facts.head.slice(0, 12)}`}`,
|
|
148409
|
+
prepared: { dir, head: facts.head, digest: factsDigest(facts), dispositions }
|
|
148410
|
+
};
|
|
148411
|
+
} catch (error) {
|
|
148412
|
+
return failed(error);
|
|
148413
|
+
}
|
|
148414
|
+
},
|
|
148415
|
+
async apply(_input, ctx, confirmation, prepared) {
|
|
148416
|
+
if (prepared === undefined)
|
|
148417
|
+
return { output: "review_complete: nothing prepared for this card", isError: true };
|
|
148418
|
+
let facts;
|
|
148419
|
+
try {
|
|
148420
|
+
facts = await completionFacts(prepared.dir);
|
|
148421
|
+
} catch (error) {
|
|
148422
|
+
return { output: `review_complete refused: ${errorText4(error)} \u2014 nothing was recorded`, isError: true };
|
|
148423
|
+
}
|
|
148424
|
+
if (factsDigest(facts) !== prepared.digest) {
|
|
148425
|
+
return { output: "The head, the evidence level or the waivers changed after the card was shown; nothing was recorded. Ask again.", isError: true };
|
|
148426
|
+
}
|
|
148427
|
+
const run = await runCompletionGates(ctx.projectRoot, prepared, confirmation.at, seams);
|
|
148428
|
+
if (!run.closed)
|
|
148429
|
+
return { output: `review_complete refused: ${errorText4(run.gateError)} \u2014 nothing was recorded`, isError: true };
|
|
148430
|
+
const recordError = await writeCompletionRecord(prepared.dir, {
|
|
148431
|
+
quote: confirmation.quote,
|
|
148432
|
+
confirmedVia: confirmation.confirmedVia,
|
|
148433
|
+
...cardPressFields(confirmation),
|
|
148434
|
+
at: confirmation.at,
|
|
148435
|
+
head: prepared.head,
|
|
148436
|
+
evidence: facts.evidence,
|
|
148437
|
+
waivers: facts.waivers,
|
|
148438
|
+
dispositions: prepared.dispositions
|
|
148439
|
+
});
|
|
148440
|
+
return completionOutput(`review ${facts.reviewId} complete${prepared.head === undefined ? "" : ` at ${prepared.head.slice(0, 12)}`} (${confirmation.confirmedVia}, ${confirmation.at})`, run, recordError);
|
|
148441
|
+
}
|
|
148442
|
+
};
|
|
148443
|
+
}
|
|
148444
|
+
async function runCompletionGates(projectRoot, prepared, at, seams) {
|
|
148445
|
+
let done;
|
|
148446
|
+
let gateError;
|
|
148447
|
+
try {
|
|
148448
|
+
done = await completeReviewGated(projectRoot, prepared.dir, {
|
|
148449
|
+
dispositions: prepared.dispositions,
|
|
148450
|
+
prLookup: seams.prLookup ?? trackerPrLookup2,
|
|
148451
|
+
prFiles: seams.prFiles ?? githubPrFiles,
|
|
148452
|
+
now: new Date(at)
|
|
148453
|
+
});
|
|
148454
|
+
} catch (error) {
|
|
148455
|
+
gateError = error;
|
|
148456
|
+
}
|
|
148457
|
+
const closed = done !== undefined || (await completionFacts(prepared.dir).catch(() => {
|
|
148458
|
+
return;
|
|
148459
|
+
}))?.status === "closed";
|
|
148460
|
+
return { done, gateError, closed };
|
|
148461
|
+
}
|
|
148462
|
+
async function writeCompletionRecord(dir, record) {
|
|
148463
|
+
try {
|
|
148464
|
+
await writeFileAtomic(path337.join(dir, COMPLETION_FILE), `${JSON.stringify(record, null, 2)}
|
|
148465
|
+
`);
|
|
148466
|
+
return;
|
|
148467
|
+
} catch (error) {
|
|
148468
|
+
return error;
|
|
148469
|
+
}
|
|
148470
|
+
}
|
|
148471
|
+
function completionOutput(closedLine, run, recordError) {
|
|
148472
|
+
const problems = [];
|
|
148473
|
+
if (recordError !== undefined)
|
|
148474
|
+
problems.push(`the round is closed but the confirmation record could not be written: ${errorText4(recordError)}`);
|
|
148475
|
+
if (run.gateError !== undefined)
|
|
148476
|
+
problems.push(`the round is closed but its bookkeeping stopped: ${errorText4(run.gateError)}`);
|
|
148477
|
+
return {
|
|
148478
|
+
output: [
|
|
148479
|
+
closedLine,
|
|
148480
|
+
...problems,
|
|
148481
|
+
...run.done?.readiness === undefined ? [] : [readinessLine(run.done.readiness)],
|
|
148482
|
+
...(run.done?.warnings ?? []).map((warning) => `warning: ${warning}`)
|
|
148483
|
+
].join(`
|
|
148484
|
+
`),
|
|
148485
|
+
...problems.length === 0 ? {} : { isError: true }
|
|
148486
|
+
};
|
|
148487
|
+
}
|
|
148214
148488
|
function buildReviewConfirmSpecs(seams = {}) {
|
|
148215
148489
|
return [
|
|
148216
148490
|
chunksAnswerSpec(),
|
|
@@ -148219,25 +148493,33 @@ function buildReviewConfirmSpecs(seams = {}) {
|
|
|
148219
148493
|
waiveReadinessSpec(seams),
|
|
148220
148494
|
decisionSpec("mutation-waiver"),
|
|
148221
148495
|
decisionSpec("reviewer-override"),
|
|
148222
|
-
replySpec(),
|
|
148496
|
+
replySpec(seams),
|
|
148223
148497
|
confirmPlanSpec(),
|
|
148224
|
-
decidePremiseSpec()
|
|
148498
|
+
decidePremiseSpec(),
|
|
148499
|
+
completeSpec(seams)
|
|
148225
148500
|
];
|
|
148226
148501
|
}
|
|
148227
148502
|
function reviewConfirmSpecs() {
|
|
148228
148503
|
return buildReviewConfirmSpecs();
|
|
148229
148504
|
}
|
|
148230
|
-
var
|
|
148505
|
+
var packageSchema, label3 = (dir, projectRoot) => path337.relative(projectRoot, dir) || ".", failed = (error) => ({ refusal: error instanceof Error ? error.message : String(error) }), reasonOf2 = (input) => {
|
|
148231
148506
|
const value = input["reason"];
|
|
148232
|
-
|
|
148233
|
-
|
|
148507
|
+
const text = typeof value === "string" ? value.trim() : "";
|
|
148508
|
+
if (text === "")
|
|
148509
|
+
return { refusal: "reason is required" };
|
|
148510
|
+
return hiddenInCard(text) ? { refusal: `reason ${HIDDEN_IN_CARD}` } : { reason: text };
|
|
148511
|
+
}, COMPLETION_FILE = "completion-confirmation.json", MAX_DISPOSITION_FINDING_CHARS = 200, MAX_DISPOSITION_EVIDENCE_CHARS = 300, dispositionsSchema, dispositionLine = (record) => `disposition: ${cardSafe(record.finding)} \u2192 ${record.state}${record.evidence === undefined ? "" : `: "${cardSafe(record.evidence)}"`}`, factsDigest = (facts) => createHash85("sha256").update(JSON.stringify(facts)).digest("hex"), errorText4 = (error) => error instanceof Error ? error.message : String(error);
|
|
148234
148512
|
var init_operator_confirm_review = __esm(() => {
|
|
148513
|
+
init_types4();
|
|
148235
148514
|
init_service11();
|
|
148236
148515
|
init_review_path_flags();
|
|
148237
148516
|
init_blast_radius();
|
|
148238
148517
|
init_chunks();
|
|
148239
148518
|
init_chunks_estimate();
|
|
148519
|
+
init_complete_gated();
|
|
148240
148520
|
init_coverage_complete();
|
|
148521
|
+
init_evidence_level();
|
|
148522
|
+
init_fs();
|
|
148241
148523
|
init_managed();
|
|
148242
148524
|
init_publish();
|
|
148243
148525
|
init_readiness();
|
|
@@ -148246,6 +148528,24 @@ var init_operator_confirm_review = __esm(() => {
|
|
|
148246
148528
|
init_premise_question();
|
|
148247
148529
|
init_plan_confirmation();
|
|
148248
148530
|
packageSchema = { type: "string", description: "The review package: its directory or its review id." };
|
|
148531
|
+
dispositionsSchema = {
|
|
148532
|
+
type: "array",
|
|
148533
|
+
description: "Optional. How each earlier finding was handled in this fix round, recorded when the round closes (the same as `review complete --finding \u2026 --disposition \u2026 --evidence \u2026`). The operator sees every entry on the card, whole, so a long list makes a long card (a card over the Telegram limit is answered in the shell). A finding the package does not hold, and any state but `unknown` without evidence, refuse the card.",
|
|
148534
|
+
items: {
|
|
148535
|
+
type: "object",
|
|
148536
|
+
properties: {
|
|
148537
|
+
finding: { type: "string", maxLength: MAX_DISPOSITION_FINDING_CHARS, description: "The finding's global id or display id; it must be a finding this package holds." },
|
|
148538
|
+
disposition: { type: "string", enum: [...FINDING_DISPOSITION_STATES] },
|
|
148539
|
+
evidence: {
|
|
148540
|
+
type: "string",
|
|
148541
|
+
maxLength: MAX_DISPOSITION_EVIDENCE_CHARS,
|
|
148542
|
+
description: `The commit or reasoning behind the disposition; required for every state except \`unknown\`. At most ${MAX_DISPOSITION_EVIDENCE_CHARS} characters of plain single-line text; the card shows it whole.`
|
|
148543
|
+
}
|
|
148544
|
+
},
|
|
148545
|
+
required: ["finding", "disposition"],
|
|
148546
|
+
additionalProperties: false
|
|
148547
|
+
}
|
|
148548
|
+
};
|
|
148249
148549
|
});
|
|
148250
148550
|
|
|
148251
148551
|
// src/commands/operator-confirm-tools.ts
|
|
@@ -148297,7 +148597,7 @@ function operatorConfirmTool(spec, binding) {
|
|
|
148297
148597
|
const card = { title: built.title, lines: [...built.lines], subject: built.subject };
|
|
148298
148598
|
const shown = renderOperatorCard(card);
|
|
148299
148599
|
const token = randomUUID32();
|
|
148300
|
-
pending.set(token, { input: key(input), card, shown, prepared: built.prepared });
|
|
148600
|
+
pending.set(token, { input: key(input), card, shown, prepared: built.prepared, issuedAt: now().getTime() });
|
|
148301
148601
|
return { card: shown, title: card.title, token };
|
|
148302
148602
|
},
|
|
148303
148603
|
confirmationDeclined: (token) => {
|
|
@@ -148306,16 +148606,35 @@ function operatorConfirmTool(spec, binding) {
|
|
|
148306
148606
|
invoke: async (input, ctx) => {
|
|
148307
148607
|
const token = ctx?.confirmationToken;
|
|
148308
148608
|
const entry = token === undefined ? undefined : pending.get(token);
|
|
148309
|
-
|
|
148310
|
-
pending.delete(token);
|
|
148609
|
+
const refuse = (why) => ({ output: `${spec.name}: ${why} \u2014 nothing was changed`, isError: true });
|
|
148311
148610
|
if (entry === undefined || entry.input !== key(input)) {
|
|
148312
|
-
|
|
148611
|
+
if (token !== undefined)
|
|
148612
|
+
pending.delete(token);
|
|
148613
|
+
return refuse("no operator-confirmed card for this request");
|
|
148614
|
+
}
|
|
148615
|
+
pending.delete(token);
|
|
148616
|
+
const remote = ctx?.confirmationVia === "remote";
|
|
148617
|
+
let pressedBy;
|
|
148618
|
+
let pressedAt;
|
|
148619
|
+
if (remote) {
|
|
148620
|
+
pressedBy = ctx?.confirmationPressedBy;
|
|
148621
|
+
pressedAt = ctx?.confirmationPressedAt;
|
|
148622
|
+
const allowed = binding.allowedUserIds?.();
|
|
148623
|
+
if (pressedBy === undefined || pressedAt === undefined)
|
|
148624
|
+
return refuse("the remote answer did not say who pressed it");
|
|
148625
|
+
if (allowed === undefined || !allowed.includes(pressedBy)) {
|
|
148626
|
+
return refuse(`Telegram user ${pressedBy} is not on the allowed list`);
|
|
148627
|
+
}
|
|
148628
|
+
if (now().getTime() - entry.issuedAt > REMOTE_CARD_TTL_MS) {
|
|
148629
|
+
return refuse("the card is older than 1 hour; ask again");
|
|
148630
|
+
}
|
|
148313
148631
|
}
|
|
148314
148632
|
const confirmation = {
|
|
148315
148633
|
quote: `${entry.card.title}: yes`,
|
|
148316
148634
|
answer: "yes",
|
|
148317
148635
|
at: now().toISOString(),
|
|
148318
|
-
confirmedVia:
|
|
148636
|
+
confirmedVia: remote ? "remote-card" : "tui-card",
|
|
148637
|
+
...remote ? { pressedBy, pressedAt } : {},
|
|
148319
148638
|
subject: entry.card.subject,
|
|
148320
148639
|
card: entry.shown
|
|
148321
148640
|
};
|
|
@@ -148334,10 +148653,11 @@ function operatorConfirmSpecs() {
|
|
|
148334
148653
|
function operatorConfirmTools(binding, specs = operatorConfirmSpecs()) {
|
|
148335
148654
|
return specs.map((spec) => operatorConfirmTool(spec, binding));
|
|
148336
148655
|
}
|
|
148337
|
-
var OPERATOR_CONFIRM_NOTE;
|
|
148656
|
+
var REMOTE_CARD_TTL_MS, OPERATOR_CONFIRM_NOTE;
|
|
148338
148657
|
var init_operator_confirm_tools = __esm(() => {
|
|
148339
148658
|
init_operator_confirm_other();
|
|
148340
148659
|
init_operator_confirm_review();
|
|
148660
|
+
REMOTE_CARD_TTL_MS = 60 * 60 * 1000;
|
|
148341
148661
|
OPERATOR_CONFIRM_NOTE = " The operator ALWAYS sees a yes/no card in the shell and must answer it \u2014 you cannot confirm it, and you " + "must not ask them to run a command in a terminal instead.";
|
|
148342
148662
|
});
|
|
148343
148663
|
|
|
@@ -149520,7 +149840,7 @@ var init_workspace_lifecycle_tool = __esm(() => {
|
|
|
149520
149840
|
});
|
|
149521
149841
|
|
|
149522
149842
|
// src/mcp-servers/catalog.ts
|
|
149523
|
-
import { createHash as
|
|
149843
|
+
import { createHash as createHash86 } from "crypto";
|
|
149524
149844
|
function canonicalJson4(value) {
|
|
149525
149845
|
if (value === undefined)
|
|
149526
149846
|
return "null";
|
|
@@ -149538,7 +149858,7 @@ function toolDefinitionFingerprint(entry) {
|
|
|
149538
149858
|
description: entry.description ?? null,
|
|
149539
149859
|
inputSchema: entry.inputSchema ?? null
|
|
149540
149860
|
});
|
|
149541
|
-
return
|
|
149861
|
+
return createHash86("sha256").update(canonical).digest("hex");
|
|
149542
149862
|
}
|
|
149543
149863
|
function buildFqn(server, rawName) {
|
|
149544
149864
|
return `${server}${FQN_DELIMITER}${rawName}`;
|
|
@@ -149786,11 +150106,11 @@ var init_tools2 = __esm(() => {
|
|
|
149786
150106
|
});
|
|
149787
150107
|
|
|
149788
150108
|
// src/commands/review-arbiter-config.ts
|
|
149789
|
-
import { readFileSync as
|
|
150109
|
+
import { readFileSync as readFileSync40 } from "fs";
|
|
149790
150110
|
import path339 from "path";
|
|
149791
150111
|
function reviewArbiterBlock(cwd) {
|
|
149792
150112
|
try {
|
|
149793
|
-
const parsed = JSON.parse(
|
|
150113
|
+
const parsed = JSON.parse(readFileSync40(path339.join(cwd, REVIEW_GATE_CONFIG_PATH), "utf8"));
|
|
149794
150114
|
if (typeof parsed !== "object" || parsed === null)
|
|
149795
150115
|
return;
|
|
149796
150116
|
const review = parsed["review"];
|
|
@@ -150275,8 +150595,8 @@ var init_arbiter_rules = __esm(() => {
|
|
|
150275
150595
|
});
|
|
150276
150596
|
|
|
150277
150597
|
// src/review/arbiter-rules.ts
|
|
150278
|
-
import { createHash as
|
|
150279
|
-
import { existsSync as existsSync77, readFileSync as
|
|
150598
|
+
import { createHash as createHash87 } from "crypto";
|
|
150599
|
+
import { existsSync as existsSync77, readFileSync as readFileSync41 } from "fs";
|
|
150280
150600
|
import path340 from "path";
|
|
150281
150601
|
function isArbiterKind(value) {
|
|
150282
150602
|
return typeof value === "string" && ARBITER_KINDS.includes(value);
|
|
@@ -150347,7 +150667,7 @@ function parseArbiterRules(raw, source) {
|
|
|
150347
150667
|
});
|
|
150348
150668
|
if (errors.length > 0)
|
|
150349
150669
|
return { ok: false, errors };
|
|
150350
|
-
const hash =
|
|
150670
|
+
const hash = createHash87("sha256").update(JSON.stringify({ version, rules })).digest("hex").slice(0, 12);
|
|
150351
150671
|
return { ok: true, rules: { version, rules, source, hash } };
|
|
150352
150672
|
}
|
|
150353
150673
|
function parseWhen2(raw, where, errors) {
|
|
@@ -150426,7 +150746,7 @@ function loadArbiterRules(cwd) {
|
|
|
150426
150746
|
return { rules: bundledArbiterRules() };
|
|
150427
150747
|
let raw;
|
|
150428
150748
|
try {
|
|
150429
|
-
raw = JSON.parse(
|
|
150749
|
+
raw = JSON.parse(readFileSync41(file, "utf8"));
|
|
150430
150750
|
} catch (error) {
|
|
150431
150751
|
return { error: `${PROJECT_RULES_PATH} cannot be read as JSON: ${error instanceof Error ? error.message : String(error)}` };
|
|
150432
150752
|
}
|
|
@@ -150471,7 +150791,7 @@ var init_arbiter_rules2 = __esm(() => {
|
|
|
150471
150791
|
});
|
|
150472
150792
|
|
|
150473
150793
|
// src/commands/review-arbiter.ts
|
|
150474
|
-
import { createHash as
|
|
150794
|
+
import { createHash as createHash88 } from "crypto";
|
|
150475
150795
|
function parseQuestion(request) {
|
|
150476
150796
|
const policy = request.policy;
|
|
150477
150797
|
if (policy === undefined)
|
|
@@ -150592,7 +150912,7 @@ function checkModelAnswer(answer, question, rules) {
|
|
|
150592
150912
|
}
|
|
150593
150913
|
function cacheKey(question) {
|
|
150594
150914
|
const facts = Object.keys(question.facts).sort().map((name) => [name, question.facts[name]]);
|
|
150595
|
-
return
|
|
150915
|
+
return createHash88("sha256").update(JSON.stringify([question.kind, facts, question.optionCount, question.recommended])).digest("hex");
|
|
150596
150916
|
}
|
|
150597
150917
|
function costOf2(prices, modelId, inputTokens, outputTokens) {
|
|
150598
150918
|
const price = prices[modelId];
|
|
@@ -151068,7 +151388,7 @@ var init_shell_events = __esm(() => {
|
|
|
151068
151388
|
// src/lib/shell-permissions.ts
|
|
151069
151389
|
import { existsSync as existsSync78 } from "fs";
|
|
151070
151390
|
import path341 from "path";
|
|
151071
|
-
import { createHash as
|
|
151391
|
+
import { createHash as createHash89 } from "crypto";
|
|
151072
151392
|
function validateShellPattern(pattern) {
|
|
151073
151393
|
const trimmed = pattern.trim();
|
|
151074
151394
|
if (trimmed.length === 0) {
|
|
@@ -151303,7 +151623,7 @@ function shellPermissionsFingerprint(dir) {
|
|
|
151303
151623
|
if (!read.ok) {
|
|
151304
151624
|
return "";
|
|
151305
151625
|
}
|
|
151306
|
-
return
|
|
151626
|
+
return createHash89("sha256").update(read.text, "utf8").digest("hex");
|
|
151307
151627
|
} catch {
|
|
151308
151628
|
return "";
|
|
151309
151629
|
}
|
|
@@ -153394,7 +153714,7 @@ var init_child_model_request = __esm(() => {
|
|
|
153394
153714
|
});
|
|
153395
153715
|
|
|
153396
153716
|
// src/harness/tool/builtin/spawn-subagent-tool.ts
|
|
153397
|
-
import { createHash as
|
|
153717
|
+
import { createHash as createHash90, randomUUID as randomUUID37 } from "crypto";
|
|
153398
153718
|
import { execFile as execFile14 } from "child_process";
|
|
153399
153719
|
import { lstat as lstat22, mkdir as mkdir78, mkdtemp as mkdtemp10, readFile as readFile182, realpath as realpath29, rm as rm26, stat as stat42, utimes as utimes2, writeFile as writeFile49 } from "fs/promises";
|
|
153400
153720
|
import { tmpdir as tmpdir13 } from "os";
|
|
@@ -153700,7 +154020,7 @@ function readExternalRuntimeRequest(input) {
|
|
|
153700
154020
|
return agent === block.agent ? block : { ...block, agent };
|
|
153701
154021
|
}
|
|
153702
154022
|
function sha25611(text) {
|
|
153703
|
-
return
|
|
154023
|
+
return createHash90("sha256").update(text).digest("hex");
|
|
153704
154024
|
}
|
|
153705
154025
|
async function resolveSubagentCwd(rawCwd, parentCwd) {
|
|
153706
154026
|
if (rawCwd === undefined) {
|
|
@@ -155121,7 +155441,7 @@ var init_http_headers = __esm(() => {
|
|
|
155121
155441
|
});
|
|
155122
155442
|
|
|
155123
155443
|
// src/mcp-servers/trust.ts
|
|
155124
|
-
import { createHash as
|
|
155444
|
+
import { createHash as createHash91 } from "crypto";
|
|
155125
155445
|
import path348 from "path";
|
|
155126
155446
|
function trustFile(configDir) {
|
|
155127
155447
|
return path348.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers-trust.json");
|
|
@@ -155144,7 +155464,7 @@ function serverFingerprint(server) {
|
|
|
155144
155464
|
}
|
|
155145
155465
|
}
|
|
155146
155466
|
});
|
|
155147
|
-
return
|
|
155467
|
+
return createHash91("sha256").update(material).digest("hex").slice(0, 32);
|
|
155148
155468
|
}
|
|
155149
155469
|
function sortedKeys(values) {
|
|
155150
155470
|
return Object.entries(values ?? {}).sort(([a], [b]) => a.localeCompare(b));
|
|
@@ -158382,7 +158702,7 @@ var init_references_check = __esm(() => {
|
|
|
158382
158702
|
init_project_skills();
|
|
158383
158703
|
init_references_config();
|
|
158384
158704
|
init_references_section();
|
|
158385
|
-
DOCUMENT_CITATION = /`([^`\s]+\.md)
|
|
158705
|
+
DOCUMENT_CITATION = /`([^`\s#]+\.md)(?:#[^`\s]*)?`/g;
|
|
158386
158706
|
});
|
|
158387
158707
|
|
|
158388
158708
|
// src/gdskills/project-context-status.ts
|
|
@@ -160475,7 +160795,7 @@ var init_help_groups = __esm(() => {
|
|
|
160475
160795
|
kind: "slash",
|
|
160476
160796
|
name: "/decisions",
|
|
160477
160797
|
group: "Automation",
|
|
160478
|
-
summary: "Recommendation journal report: match share by mode and stage, the deviations with their reasons, and your own answers and typed reasons in full (marked \u270D in the sidebar row)."
|
|
160798
|
+
summary: "Recommendation journal report: match share by mode and stage, the deviations with their reasons, and your own answers and typed reasons in full (marked \u270D in the sidebar row), and the Telegram review cards waiting or answered (review_confirm_plan, review_decide_premise, review_waive_readiness, review_waive_coverage, review_complete)."
|
|
160479
160799
|
},
|
|
160480
160800
|
{
|
|
160481
160801
|
kind: "slash",
|
|
@@ -160493,7 +160813,7 @@ var init_help_groups = __esm(() => {
|
|
|
160493
160813
|
kind: "slash",
|
|
160494
160814
|
name: "/remote-control",
|
|
160495
160815
|
group: "Automation",
|
|
160496
|
-
summary: 'Drive this session from a Telegram topic: send lines, get replies, answer approvals and questions (an option button, or "\u270D \u0421\u0432\u043E\u0439 \u043E\u0442\u0432\u0435\u0442" and a reply with your own text). Off by default; [name|off|status].'
|
|
160816
|
+
summary: 'Drive this session from a Telegram topic: send lines, get replies, answer approvals and questions (an option button, or "\u270D \u0421\u0432\u043E\u0439 \u043E\u0442\u0432\u0435\u0442" and a reply with your own text). Off by default; [name|off|status]. Also takes the review cards (review_confirm_plan, review_decide_premise, review_waive_readiness, review_waive_coverage, review_complete): each pending one shows with its age and the time left of its 1-hour token, and who pressed.'
|
|
160497
160817
|
},
|
|
160498
160818
|
{
|
|
160499
160819
|
kind: "slash",
|
|
@@ -164653,7 +164973,7 @@ var init_extract3 = __esm(() => {
|
|
|
164653
164973
|
});
|
|
164654
164974
|
|
|
164655
164975
|
// src/product/corpus.ts
|
|
164656
|
-
import { createHash as
|
|
164976
|
+
import { createHash as createHash92 } from "crypto";
|
|
164657
164977
|
import { readdir as readdir67, readFile as readFile191 } from "fs/promises";
|
|
164658
164978
|
import path366 from "path";
|
|
164659
164979
|
function flowsDir(cwd) {
|
|
@@ -164698,7 +165018,7 @@ function digestOf4(content) {
|
|
|
164698
165018
|
return "absent";
|
|
164699
165019
|
if (content === "unreadable")
|
|
164700
165020
|
return "unreadable";
|
|
164701
|
-
return
|
|
165021
|
+
return createHash92("sha256").update(content).digest("hex");
|
|
164702
165022
|
}
|
|
164703
165023
|
async function readBytes(file) {
|
|
164704
165024
|
try {
|
|
@@ -164732,7 +165052,7 @@ async function corpusFingerprint(cwd) {
|
|
|
164732
165052
|
const specification = await readBytes(path366.join(dir, "specification.md"));
|
|
164733
165053
|
lines.push(`file\x00docs/requirements/${name}/specification.md\x00${digestOf4(specification)}`);
|
|
164734
165054
|
}
|
|
164735
|
-
return
|
|
165055
|
+
return createHash92("sha256").update([...lines].sort().join(`
|
|
164736
165056
|
`)).digest("hex");
|
|
164737
165057
|
}
|
|
164738
165058
|
function idOrder(intent) {
|
|
@@ -166909,7 +167229,8 @@ function mapApprovalAnswer(got, offered) {
|
|
|
166909
167229
|
decision: yes ? "allow" : "deny",
|
|
166910
167230
|
always: o.decision === "always" && offered,
|
|
166911
167231
|
...approvalId === undefined ? {} : { approvalId },
|
|
166912
|
-
...fromId === undefined ? {} : { fromId }
|
|
167232
|
+
...fromId === undefined ? {} : { fromId },
|
|
167233
|
+
...!yes && o.undelivered === true ? { undelivered: true } : {}
|
|
166913
167234
|
};
|
|
166914
167235
|
}
|
|
166915
167236
|
function composeApprovalPrompt(prompt) {
|
|
@@ -166966,6 +167287,13 @@ function operatorCardTooLongForTopic(tool, inputJson, meta) {
|
|
|
166966
167287
|
return false;
|
|
166967
167288
|
return redactSensitiveText(describeApprovalForTopic(tool, inputJson, meta)).trim().length > MAX_APPROVAL_PROMPT_CHARS;
|
|
166968
167289
|
}
|
|
167290
|
+
function mirrorUndeliveredNote(bridge) {
|
|
167291
|
+
if (bridge === undefined || !bridge.active)
|
|
167292
|
+
return `${MIRROR_NOT_DELIVERED}: remote control is not on in this shell. Answer it here.`;
|
|
167293
|
+
if (!bridge.connected)
|
|
167294
|
+
return `${MIRROR_NOT_DELIVERED}: the remote channel is not connected. Answer it here.`;
|
|
167295
|
+
return;
|
|
167296
|
+
}
|
|
166969
167297
|
|
|
166970
167298
|
class RemoteBridge {
|
|
166971
167299
|
options;
|
|
@@ -167070,6 +167398,9 @@ class RemoteBridge {
|
|
|
167070
167398
|
get active() {
|
|
167071
167399
|
return this.client !== undefined;
|
|
167072
167400
|
}
|
|
167401
|
+
get connected() {
|
|
167402
|
+
return this.client?.connected === true;
|
|
167403
|
+
}
|
|
167073
167404
|
get telegramTurnActive() {
|
|
167074
167405
|
return this.tgTurn;
|
|
167075
167406
|
}
|
|
@@ -167484,7 +167815,7 @@ class RemoteBridge {
|
|
|
167484
167815
|
const client = this.client;
|
|
167485
167816
|
if (client === undefined || !client.connected) {
|
|
167486
167817
|
this.push("approval", "denied: not connected");
|
|
167487
|
-
return { decision: "deny", always: false };
|
|
167818
|
+
return { decision: "deny", always: false, undelivered: true };
|
|
167488
167819
|
}
|
|
167489
167820
|
this.push("approval", "asked in the topic");
|
|
167490
167821
|
let answer;
|
|
@@ -167563,7 +167894,7 @@ class RemoteBridge {
|
|
|
167563
167894
|
await this.client?.reportApprovalResult?.(approvalId, remembered);
|
|
167564
167895
|
}
|
|
167565
167896
|
}
|
|
167566
|
-
var TG_SOURCE = "tg", REMOTE_EVENT_LIMIT = 20, REMOTE_REPLY_MAX_CHARS = 16000, EVENT_PREVIEW_CHARS = 80, HISTORY_PACE_MS = 2000;
|
|
167897
|
+
var TG_SOURCE = "tg", REMOTE_EVENT_LIMIT = 20, REMOTE_REPLY_MAX_CHARS = 16000, EVENT_PREVIEW_CHARS = 80, HISTORY_PACE_MS = 2000, MIRROR_NOT_DELIVERED = "Telegram mirror not delivered";
|
|
167567
167898
|
var init_shell_bridge = __esm(() => {
|
|
167568
167899
|
init_approval_cause();
|
|
167569
167900
|
init_service2();
|
|
@@ -167574,45 +167905,6 @@ var init_shell_bridge = __esm(() => {
|
|
|
167574
167905
|
init_history2();
|
|
167575
167906
|
});
|
|
167576
167907
|
|
|
167577
|
-
// src/tui/approval-mirror.ts
|
|
167578
|
-
function isTopicPress(answer) {
|
|
167579
|
-
return answer.decision === "allow" || answer.fromId !== undefined;
|
|
167580
|
-
}
|
|
167581
|
-
async function raceApproval(options) {
|
|
167582
|
-
const dockAbort = new AbortController;
|
|
167583
|
-
const topicAbort = new AbortController;
|
|
167584
|
-
let topicAnswer;
|
|
167585
|
-
let settled = false;
|
|
167586
|
-
let started = false;
|
|
167587
|
-
const race = {
|
|
167588
|
-
signal: AbortSignal.any([options.turnSignal, dockAbort.signal]),
|
|
167589
|
-
opened: () => {
|
|
167590
|
-
if (started || settled)
|
|
167591
|
-
return;
|
|
167592
|
-
started = true;
|
|
167593
|
-
options.topic(topicAbort.signal).then((answer) => {
|
|
167594
|
-
if (settled || !isTopicPress(answer))
|
|
167595
|
-
return;
|
|
167596
|
-
topicAnswer = answer;
|
|
167597
|
-
dockAbort.abort("topic");
|
|
167598
|
-
}).catch(() => {
|
|
167599
|
-
return;
|
|
167600
|
-
});
|
|
167601
|
-
},
|
|
167602
|
-
topicWon: () => topicAnswer !== undefined
|
|
167603
|
-
};
|
|
167604
|
-
try {
|
|
167605
|
-
const result = await options.dock(race);
|
|
167606
|
-
if (topicAnswer !== undefined)
|
|
167607
|
-
return { from: "topic", answer: topicAnswer };
|
|
167608
|
-
return { from: "dock", result };
|
|
167609
|
-
} finally {
|
|
167610
|
-
settled = true;
|
|
167611
|
-
if (topicAnswer === undefined)
|
|
167612
|
-
topicAbort.abort(options.turnSignal.aborted ? "cancelled" : "shell");
|
|
167613
|
-
}
|
|
167614
|
-
}
|
|
167615
|
-
|
|
167616
167908
|
// src/remote/history-host.ts
|
|
167617
167909
|
function sessionEntryOf(resumed) {
|
|
167618
167910
|
return { resumed, kind: resumed ? "resumed" : "new" };
|
|
@@ -168974,6 +169266,212 @@ var init_channels_surface = __esm(() => {
|
|
|
168974
169266
|
TOKEN_CHAR = /^[A-Za-z0-9_:-]$/;
|
|
168975
169267
|
});
|
|
168976
169268
|
|
|
169269
|
+
// src/tui/approval-mirror.ts
|
|
169270
|
+
function isTopicPress(answer) {
|
|
169271
|
+
return answer.decision === "allow" || answer.fromId !== undefined;
|
|
169272
|
+
}
|
|
169273
|
+
async function raceApproval(options) {
|
|
169274
|
+
const dockAbort = new AbortController;
|
|
169275
|
+
const topicAbort = new AbortController;
|
|
169276
|
+
let topicAnswer;
|
|
169277
|
+
let settled = false;
|
|
169278
|
+
let started = false;
|
|
169279
|
+
const race = {
|
|
169280
|
+
signal: AbortSignal.any([options.turnSignal, dockAbort.signal]),
|
|
169281
|
+
opened: () => {
|
|
169282
|
+
if (started || settled)
|
|
169283
|
+
return;
|
|
169284
|
+
started = true;
|
|
169285
|
+
options.topic(topicAbort.signal).then((answer) => {
|
|
169286
|
+
if (settled)
|
|
169287
|
+
return;
|
|
169288
|
+
if (answer.undelivered === true)
|
|
169289
|
+
options.onUndelivered?.();
|
|
169290
|
+
else if (!isTopicPress(answer) && !topicAbort.signal.aborted)
|
|
169291
|
+
options.onTopicEnded?.();
|
|
169292
|
+
if (!isTopicPress(answer))
|
|
169293
|
+
return;
|
|
169294
|
+
topicAnswer = answer;
|
|
169295
|
+
dockAbort.abort("topic");
|
|
169296
|
+
}).catch(() => {
|
|
169297
|
+
return;
|
|
169298
|
+
});
|
|
169299
|
+
},
|
|
169300
|
+
topicWon: () => topicAnswer !== undefined
|
|
169301
|
+
};
|
|
169302
|
+
try {
|
|
169303
|
+
const result = await options.dock(race);
|
|
169304
|
+
if (topicAnswer !== undefined)
|
|
169305
|
+
return { from: "topic", answer: topicAnswer };
|
|
169306
|
+
return { from: "dock", result };
|
|
169307
|
+
} finally {
|
|
169308
|
+
settled = true;
|
|
169309
|
+
if (topicAnswer === undefined)
|
|
169310
|
+
topicAbort.abort(options.turnSignal.aborted ? "cancelled" : "shell");
|
|
169311
|
+
}
|
|
169312
|
+
}
|
|
169313
|
+
|
|
169314
|
+
// src/tui/operator-confirmations.ts
|
|
169315
|
+
class OperatorConfirmationLog {
|
|
169316
|
+
now;
|
|
169317
|
+
items = [];
|
|
169318
|
+
nextId = 1;
|
|
169319
|
+
listeners = new Set;
|
|
169320
|
+
constructor(now = () => Date.now()) {
|
|
169321
|
+
this.now = now;
|
|
169322
|
+
}
|
|
169323
|
+
ask(question, mirrored) {
|
|
169324
|
+
const id = this.nextId++;
|
|
169325
|
+
this.items.push({ id, question, askedAt: this.now(), mirrored });
|
|
169326
|
+
this.changed();
|
|
169327
|
+
return id;
|
|
169328
|
+
}
|
|
169329
|
+
answer(id, result) {
|
|
169330
|
+
const at = this.items.findIndex((item) => item.id === id);
|
|
169331
|
+
const entry = this.items[at];
|
|
169332
|
+
if (entry === undefined || entry.answered !== undefined)
|
|
169333
|
+
return;
|
|
169334
|
+
this.items[at] = {
|
|
169335
|
+
...entry,
|
|
169336
|
+
answered: { place: result.place, yes: result.yes, at: this.now(), ...result.pressedBy === undefined ? {} : { pressedBy: result.pressedBy } }
|
|
169337
|
+
};
|
|
169338
|
+
const answered = this.items.filter((item) => item.answered !== undefined);
|
|
169339
|
+
for (const old of answered.slice(0, Math.max(0, answered.length - ANSWERED_CARDS_KEPT))) {
|
|
169340
|
+
this.items.splice(this.items.indexOf(old), 1);
|
|
169341
|
+
}
|
|
169342
|
+
this.changed();
|
|
169343
|
+
}
|
|
169344
|
+
settle(id, outcome, turnAborted) {
|
|
169345
|
+
if (outcome === undefined)
|
|
169346
|
+
this.answer(id, { place: "withdrawn", yes: false });
|
|
169347
|
+
else if (outcome.from === "dock")
|
|
169348
|
+
this.answer(id, { place: turnAborted ? "withdrawn" : "shell", yes: outcome.result !== false });
|
|
169349
|
+
else
|
|
169350
|
+
this.answer(id, { place: "telegram", yes: outcome.answer.decision === "allow", ...outcome.answer.fromId === undefined ? {} : { pressedBy: outcome.answer.fromId } });
|
|
169351
|
+
}
|
|
169352
|
+
markUndelivered(id, reason = "undelivered") {
|
|
169353
|
+
const at = this.items.findIndex((item) => item.id === id);
|
|
169354
|
+
const entry = this.items[at];
|
|
169355
|
+
if (entry === undefined || entry.answered !== undefined || !entry.mirrored)
|
|
169356
|
+
return;
|
|
169357
|
+
this.items[at] = { ...entry, mirrored: false, ...reason === "ended" ? { topicEnded: true } : {} };
|
|
169358
|
+
this.changed();
|
|
169359
|
+
}
|
|
169360
|
+
waiting() {
|
|
169361
|
+
const now = this.now();
|
|
169362
|
+
return this.items.filter((item) => item.answered === undefined && item.mirrored && now - item.askedAt <= REMOTE_CARD_TTL_MS);
|
|
169363
|
+
}
|
|
169364
|
+
entries() {
|
|
169365
|
+
const open = this.items.filter((item) => item.answered === undefined);
|
|
169366
|
+
const done = this.items.filter((item) => item.answered !== undefined).reverse();
|
|
169367
|
+
return [...open, ...done];
|
|
169368
|
+
}
|
|
169369
|
+
subscribe(listener) {
|
|
169370
|
+
this.listeners.add(listener);
|
|
169371
|
+
return () => {
|
|
169372
|
+
this.listeners.delete(listener);
|
|
169373
|
+
};
|
|
169374
|
+
}
|
|
169375
|
+
changed() {
|
|
169376
|
+
for (const listener of [...this.listeners])
|
|
169377
|
+
listener();
|
|
169378
|
+
}
|
|
169379
|
+
}
|
|
169380
|
+
function age(ms) {
|
|
169381
|
+
const seconds = Math.max(0, Math.round(ms / 1000));
|
|
169382
|
+
if (seconds < 60)
|
|
169383
|
+
return `${seconds}s`;
|
|
169384
|
+
const minutes = Math.floor(seconds / 60);
|
|
169385
|
+
if (minutes < 60)
|
|
169386
|
+
return `${minutes}m`;
|
|
169387
|
+
return `${Math.floor(minutes / 60)}h${String(minutes % 60).padStart(2, "0")}m`;
|
|
169388
|
+
}
|
|
169389
|
+
function clock2(at) {
|
|
169390
|
+
return new Date(at).toISOString().slice(11, 19);
|
|
169391
|
+
}
|
|
169392
|
+
function clip7(text, max) {
|
|
169393
|
+
const flat = text.replace(/\s+/g, " ").trim();
|
|
169394
|
+
return flat.length <= max ? flat : `${flat.slice(0, Math.max(0, max - 1))}\u2026`;
|
|
169395
|
+
}
|
|
169396
|
+
function pendingConfirmationLine(entry, now) {
|
|
169397
|
+
const elapsed = now - entry.askedAt;
|
|
169398
|
+
const left = REMOTE_CARD_TTL_MS - elapsed;
|
|
169399
|
+
const token = left > 0 ? `${age(left)} left of the 1-hour token` : "Telegram token expired, answer in the shell";
|
|
169400
|
+
const where = entry.mirrored ? "" : entry.topicEnded === true ? " \xB7 Telegram is no longer waiting for this card, answer in the shell" : " \xB7 Telegram mirror not delivered, answer in the shell";
|
|
169401
|
+
return `${clip7(entry.question, 80)} \xB7 asked ${age(elapsed)} ago \xB7 ${token}${where}`;
|
|
169402
|
+
}
|
|
169403
|
+
function answeredConfirmationLine(entry) {
|
|
169404
|
+
const done = entry.answered;
|
|
169405
|
+
if (done === undefined)
|
|
169406
|
+
return clip7(entry.question, 80);
|
|
169407
|
+
const verdict = done.yes ? "yes" : "no";
|
|
169408
|
+
if (done.place === "telegram") {
|
|
169409
|
+
const who = done.pressedBy === undefined ? "a Telegram user" : `Telegram user ${done.pressedBy}`;
|
|
169410
|
+
return `${clip7(entry.question, 80)} \xB7 ${verdict} \xB7 pressed by ${who} at ${clock2(done.at)}`;
|
|
169411
|
+
}
|
|
169412
|
+
if (done.place === "withdrawn")
|
|
169413
|
+
return `${clip7(entry.question, 80)} \xB7 withdrawn (the turn stopped) at ${clock2(done.at)}`;
|
|
169414
|
+
return `${clip7(entry.question, 80)} \xB7 ${verdict} \xB7 answered in the shell at ${clock2(done.at)}`;
|
|
169415
|
+
}
|
|
169416
|
+
function confirmationLines(entries, now, heading = "Operator confirmations (Telegram cards)") {
|
|
169417
|
+
if (entries.length === 0)
|
|
169418
|
+
return [];
|
|
169419
|
+
const waiting = entries.filter((entry) => entry.answered === undefined);
|
|
169420
|
+
const answered = entries.filter((entry) => entry.answered !== undefined);
|
|
169421
|
+
const lines = [heading];
|
|
169422
|
+
for (const entry of waiting)
|
|
169423
|
+
lines.push(` waiting ${pendingConfirmationLine(entry, now)}`);
|
|
169424
|
+
for (const entry of answered)
|
|
169425
|
+
lines.push(` answered ${answeredConfirmationLine(entry)}`);
|
|
169426
|
+
return lines;
|
|
169427
|
+
}
|
|
169428
|
+
function waitingCardsText(waiting) {
|
|
169429
|
+
if (waiting <= 0)
|
|
169430
|
+
return;
|
|
169431
|
+
return `${waiting} card${waiting === 1 ? "" : "s"} waiting`;
|
|
169432
|
+
}
|
|
169433
|
+
function remoteApproval(fingerprint, fromId, now = () => new Date) {
|
|
169434
|
+
return { approved: true, fingerprint, via: "remote", ...fromId !== undefined ? { pressedBy: fromId } : {}, pressedAt: now().toISOString() };
|
|
169435
|
+
}
|
|
169436
|
+
async function askUnmirroredCard(log, question, turnSignal, ask) {
|
|
169437
|
+
const id = log.ask(question, false);
|
|
169438
|
+
let verdict;
|
|
169439
|
+
let answered = false;
|
|
169440
|
+
try {
|
|
169441
|
+
verdict = await ask();
|
|
169442
|
+
answered = true;
|
|
169443
|
+
return verdict;
|
|
169444
|
+
} finally {
|
|
169445
|
+
log.answer(id, { place: turnSignal.aborted || !answered ? "withdrawn" : "shell", yes: answered && verdict !== false });
|
|
169446
|
+
}
|
|
169447
|
+
}
|
|
169448
|
+
async function raceMirroredCard(options) {
|
|
169449
|
+
const id = options.question === undefined ? undefined : options.log.ask(options.question, true);
|
|
169450
|
+
let outcome;
|
|
169451
|
+
try {
|
|
169452
|
+
outcome = await raceApproval({
|
|
169453
|
+
turnSignal: options.turnSignal,
|
|
169454
|
+
...id === undefined ? {} : {
|
|
169455
|
+
onUndelivered: () => {
|
|
169456
|
+
options.log.markUndelivered(id);
|
|
169457
|
+
options.onUndelivered?.();
|
|
169458
|
+
},
|
|
169459
|
+
onTopicEnded: () => options.log.markUndelivered(id, "ended")
|
|
169460
|
+
},
|
|
169461
|
+
dock: options.dock,
|
|
169462
|
+
topic: options.topic
|
|
169463
|
+
});
|
|
169464
|
+
return outcome;
|
|
169465
|
+
} finally {
|
|
169466
|
+
if (id !== undefined)
|
|
169467
|
+
options.log.settle(id, outcome, options.turnSignal.aborted);
|
|
169468
|
+
}
|
|
169469
|
+
}
|
|
169470
|
+
var ANSWERED_CARDS_KEPT = 5;
|
|
169471
|
+
var init_operator_confirmations = __esm(() => {
|
|
169472
|
+
init_operator_confirm_tools();
|
|
169473
|
+
});
|
|
169474
|
+
|
|
168977
169475
|
// src/tui/remote-control-surface.ts
|
|
168978
169476
|
function commandEchoText(line, source) {
|
|
168979
169477
|
return source === TG_SOURCE ? `${TG_ECHO_MARKER} ${line}` : `\u276F ${line}`;
|
|
@@ -169023,9 +169521,9 @@ function formatHistoryStatus(status) {
|
|
|
169023
169521
|
const history = status.history;
|
|
169024
169522
|
if (history === undefined)
|
|
169025
169523
|
return `History: not posted yet. Use ${HISTORY_COMMAND} [N], or press h here.`;
|
|
169026
|
-
return `History: ${history.count} message${history.count === 1 ? "" : "s"} posted at ${
|
|
169524
|
+
return `History: ${history.count} message${history.count === 1 ? "" : "s"} posted at ${clock3(history.at)} (${history.auto ? "restored automatically" : HISTORY_COMMAND}). Again: ${HISTORY_COMMAND} [N], or press h here.`;
|
|
169027
169525
|
}
|
|
169028
|
-
function formatRemoteStatusLines(status, posture) {
|
|
169526
|
+
function formatRemoteStatusLines(status, posture, confirmations) {
|
|
169029
169527
|
const lines = [`Remote control: ${STATE_WORDS[status.state]}`];
|
|
169030
169528
|
if (status.state === "off") {
|
|
169031
169529
|
lines.push("Topic: none");
|
|
@@ -169041,25 +169539,27 @@ function formatRemoteStatusLines(status, posture) {
|
|
|
169041
169539
|
const n = status.unconfirmedApprovals;
|
|
169042
169540
|
lines.push(`Approvals not confirmed: ${n}. The topic shows ${n === 1 ? "it" : "them"} as "not confirmed" until serve takes the shell's ack.`);
|
|
169043
169541
|
}
|
|
169542
|
+
if (confirmations !== undefined && confirmations.length > 0)
|
|
169543
|
+
lines.push("", ...confirmations);
|
|
169044
169544
|
lines.push(`Turn it off with ${REMOTE_CONTROL_COMMAND} off, or press o here. Rename: ${REMOTE_CONTROL_COMMAND} off, then ${REMOTE_CONTROL_COMMAND} <name>.`);
|
|
169045
169545
|
if (posture !== undefined && posture.length > 0) {
|
|
169046
169546
|
lines.push("", "Telegram permissions (change with /remote-policy):", ...posture.map((line) => ` ${line}`));
|
|
169047
169547
|
}
|
|
169048
169548
|
return lines;
|
|
169049
169549
|
}
|
|
169050
|
-
function
|
|
169550
|
+
function clock3(at) {
|
|
169051
169551
|
return new Date(at).toISOString().slice(11, 19);
|
|
169052
169552
|
}
|
|
169053
169553
|
function formatRemoteEventLines(status) {
|
|
169054
169554
|
if (status.events.length === 0)
|
|
169055
169555
|
return ["No remote events yet."];
|
|
169056
|
-
return [...status.events].reverse().slice(0, REMOTE_EVENT_LIMIT).map((event) => `${
|
|
169556
|
+
return [...status.events].reverse().slice(0, REMOTE_EVENT_LIMIT).map((event) => `${clock3(event.at)} ${event.kind.padEnd(8)} ${event.text}`);
|
|
169057
169557
|
}
|
|
169058
169558
|
function formatRemoteCommandLines(status) {
|
|
169059
169559
|
const commands = status.events.filter((event) => event.kind === "command");
|
|
169060
169560
|
if (commands.length === 0)
|
|
169061
169561
|
return ["No remote commands yet."];
|
|
169062
|
-
return [...commands].reverse().slice(0, REMOTE_EVENT_LIMIT).map((event) => `${
|
|
169562
|
+
return [...commands].reverse().slice(0, REMOTE_EVENT_LIMIT).map((event) => `${clock3(event.at)} ${event.text}`);
|
|
169063
169563
|
}
|
|
169064
169564
|
function renderRemoteControlText(status) {
|
|
169065
169565
|
return `${[...formatRemoteStatusLines(status), "", "Recent events:", ...formatRemoteEventLines(status)].join(`
|
|
@@ -169105,7 +169605,7 @@ function presentRemoteControl(open, otui, chrome, options) {
|
|
|
169105
169605
|
return formatRemoteEventLines(status);
|
|
169106
169606
|
if (tab === "commands")
|
|
169107
169607
|
return formatRemoteCommandLines(status);
|
|
169108
|
-
const lines = formatRemoteStatusLines(status, options.getPostureLines?.());
|
|
169608
|
+
const lines = formatRemoteStatusLines(status, options.getPostureLines?.(), options.getConfirmationLines?.());
|
|
169109
169609
|
if (historyNote !== undefined) {
|
|
169110
169610
|
const at = lines.findIndex((line) => line.startsWith("History:"));
|
|
169111
169611
|
lines.splice(at < 0 ? lines.length : at + 1, 0, historyNote);
|
|
@@ -169196,14 +169696,17 @@ function fit3(text, width) {
|
|
|
169196
169696
|
return "";
|
|
169197
169697
|
return text.length <= width ? text : `${text.slice(0, Math.max(0, width - 1))}\u2026`;
|
|
169198
169698
|
}
|
|
169199
|
-
function projectRemoteRow(status, width) {
|
|
169699
|
+
function projectRemoteRow(status, width, waitingCards = 0) {
|
|
169200
169700
|
if (status === undefined || status.state === "off")
|
|
169201
169701
|
return { text: fit3("off", width), role: "muted", action: "open" };
|
|
169202
|
-
const detail = status.history === undefined ? {} : { detail: fit3(`history ${status.history.count} \xB7 ${
|
|
169702
|
+
const detail = status.history === undefined ? {} : { detail: fit3(`history ${status.history.count} \xB7 ${clock3(status.history.at).slice(0, 5)}`, width) };
|
|
169203
169703
|
if (status.state === "offline")
|
|
169204
169704
|
return { text: fit3("offline", width), role: "attention", action: "open", ...detail };
|
|
169205
169705
|
const topic = status.name ?? "connecting";
|
|
169206
169706
|
const unconfirmed = status.unconfirmedApprovals ?? 0;
|
|
169707
|
+
const cards = waitingCardsText(waitingCards);
|
|
169708
|
+
if (cards !== undefined)
|
|
169709
|
+
return { text: fit3(`${topic} \xB7 ${cards}`, width), role: "attention", action: "open", ...detail };
|
|
169207
169710
|
if (unconfirmed > 0)
|
|
169208
169711
|
return { text: fit3(`${topic} \xB7 ${unconfirmed} unconfirmed`, width), role: "attention", action: "open", ...detail };
|
|
169209
169712
|
return { text: fit3(topic, width), role: "ok", action: "open", ...detail };
|
|
@@ -169232,7 +169735,7 @@ function mountRemotePanel(otui, renderer, parent, options) {
|
|
|
169232
169735
|
box.add(detail);
|
|
169233
169736
|
const posture = new core.TextRenderable(renderer, { id: "sb-remote-p", content: "" });
|
|
169234
169737
|
box.add(posture);
|
|
169235
|
-
const currentRow = () => projectRemoteRow(options.getStatus(), options.width);
|
|
169738
|
+
const currentRow = () => projectRemoteRow(options.getStatus(), options.width, options.getWaitingCards?.() ?? 0);
|
|
169236
169739
|
const draw = () => {
|
|
169237
169740
|
if (disposed)
|
|
169238
169741
|
return;
|
|
@@ -169277,6 +169780,7 @@ var init_remote_control_surface = __esm(() => {
|
|
|
169277
169780
|
init_history2();
|
|
169278
169781
|
init_history_host();
|
|
169279
169782
|
init_shell_bridge();
|
|
169783
|
+
init_operator_confirmations();
|
|
169280
169784
|
init_flow_inspector();
|
|
169281
169785
|
init_modal_host();
|
|
169282
169786
|
init_theme_repaint();
|
|
@@ -169513,7 +170017,7 @@ var init_agent_commands = __esm(() => {
|
|
|
169513
170017
|
},
|
|
169514
170018
|
{
|
|
169515
170019
|
name: "/remote-control",
|
|
169516
|
-
description: "Drive this session from a Telegram topic: /remote-control [name|off|status]. Off by default",
|
|
170020
|
+
description: "Drive this session from a Telegram topic: /remote-control [name|off|status]. Off by default. Shows the review cards (review_confirm_plan, review_decide_premise, review_waive_readiness, review_waive_coverage, review_complete) waiting for a press",
|
|
169517
170021
|
modes: AGENT_ONLY
|
|
169518
170022
|
},
|
|
169519
170023
|
{
|
|
@@ -169598,7 +170102,7 @@ var init_agent_commands = __esm(() => {
|
|
|
169598
170102
|
},
|
|
169599
170103
|
{
|
|
169600
170104
|
name: "/decisions",
|
|
169601
|
-
description: "Recommendation journal: how often you follow the agent's recommendation, by mode and stage, and the deviations; /decisions arms: how often it was followed in each arm; /decisions reason <why> and /decisions change <option> follow up on the last answer",
|
|
170105
|
+
description: "Recommendation journal: how often you follow the agent's recommendation, by mode and stage, and the deviations; /decisions arms: how often it was followed in each arm; /decisions reason <why> and /decisions change <option> follow up on the last answer; also lists the Telegram review cards waiting or answered (review_confirm_plan, review_decide_premise, review_waive_readiness, review_waive_coverage, review_complete)",
|
|
169602
170106
|
modes: AGENT_ONLY
|
|
169603
170107
|
},
|
|
169604
170108
|
{
|
|
@@ -172314,7 +172818,7 @@ function approvalsText(views, now, options) {
|
|
|
172314
172818
|
var RECENT_RESOLVED_LIMIT = 10;
|
|
172315
172819
|
|
|
172316
172820
|
// src/lib/serve-turn-store.ts
|
|
172317
|
-
import { createHash as
|
|
172821
|
+
import { createHash as createHash93 } from "crypto";
|
|
172318
172822
|
import { existsSync as existsSync84, readdirSync as readdirSync16, rmSync as rmSync6 } from "fs";
|
|
172319
172823
|
import path376 from "path";
|
|
172320
172824
|
function turnsRoot(dir) {
|
|
@@ -172325,11 +172829,11 @@ function turnDir(turnId, dir) {
|
|
|
172325
172829
|
}
|
|
172326
172830
|
function keyPath(project, idempotencyKey, dir) {
|
|
172327
172831
|
const projectBytes = Buffer.byteLength(project, "utf8");
|
|
172328
|
-
const digest =
|
|
172832
|
+
const digest = createHash93("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
|
|
172329
172833
|
return path376.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
172330
172834
|
}
|
|
172331
172835
|
function legacyKeyPath(idempotencyKey, dir) {
|
|
172332
|
-
const digest =
|
|
172836
|
+
const digest = createHash93("sha256").update(idempotencyKey, "utf8").digest("hex");
|
|
172333
172837
|
return path376.join(turnsRoot(dir), "keys", `${digest}.json`);
|
|
172334
172838
|
}
|
|
172335
172839
|
function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
@@ -173583,7 +174087,7 @@ var init_types10 = __esm(() => {
|
|
|
173583
174087
|
});
|
|
173584
174088
|
|
|
173585
174089
|
// src/intake/store.ts
|
|
173586
|
-
import { createHash as
|
|
174090
|
+
import { createHash as createHash94 } from "crypto";
|
|
173587
174091
|
import { appendFile as appendFile17, mkdir as mkdir81, readFile as readFile199, writeFile as writeFile50 } from "fs/promises";
|
|
173588
174092
|
import path380 from "path";
|
|
173589
174093
|
async function ensureIntakeDataIgnored(root) {
|
|
@@ -173606,11 +174110,11 @@ async function locked2(root, lock, fn) {
|
|
|
173606
174110
|
return withFileLock(lock, fn, { timeoutMs: 5000 });
|
|
173607
174111
|
}
|
|
173608
174112
|
function cardIdFor(eventKey, stamp) {
|
|
173609
|
-
return `c${
|
|
174113
|
+
return `c${createHash94("sha256").update(`${eventKey}
|
|
173610
174114
|
${stamp}`).digest("hex").slice(0, 11)}`;
|
|
173611
174115
|
}
|
|
173612
174116
|
function overflowCardId(seed) {
|
|
173613
|
-
return `o${
|
|
174117
|
+
return `o${createHash94("sha256").update(seed).digest("hex").slice(0, 11)}`;
|
|
173614
174118
|
}
|
|
173615
174119
|
function emptyIntakeState() {
|
|
173616
174120
|
return { version: 1, paused: false, seen: {}, baselined: [], delivery: {}, recent: [], pendingEdits: {}, pendingEditTries: {} };
|
|
@@ -174993,7 +175497,7 @@ function summary(c) {
|
|
|
174993
175497
|
...c.remindAt !== undefined ? { remindAt: c.remindAt } : {}
|
|
174994
175498
|
};
|
|
174995
175499
|
}
|
|
174996
|
-
function
|
|
175500
|
+
function clock4(iso) {
|
|
174997
175501
|
if (iso === null)
|
|
174998
175502
|
return "\u0435\u0449\u0451 \u043D\u0435 \u043E\u043F\u0440\u0430\u0448\u0438\u0432\u0430\u043B\u0441\u044F";
|
|
174999
175503
|
const d = new Date(iso);
|
|
@@ -175013,7 +175517,7 @@ async function buildIntakeStatus(root, deps = {}) {
|
|
|
175013
175517
|
const nextPollAt = nextIntakePollAt(config, state);
|
|
175014
175518
|
const ghAccount = ghEnvForProject(root, env)["GH_ACCOUNT"] === "work" ? "work" : "personal";
|
|
175015
175519
|
const disabledReason = intakeDisabledReason(file);
|
|
175016
|
-
const line = !file.present ? "Intake: \u043D\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043D" : !config.enabled ? "Intake: \u0432\u044B\u043A\u043B" : state.paused ? `Intake: ${waiting.length} \u0436\u0434\u0443\u0442 | \u043F\u0430\u0443\u0437\u0430` : `Intake: ${waiting.length} \u0436\u0434\u0443\u0442 | \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u043E\u043F\u0440\u043E\u0441 ${
|
|
175520
|
+
const line = !file.present ? "Intake: \u043D\u0435 \u043D\u0430\u0441\u0442\u0440\u043E\u0435\u043D" : !config.enabled ? "Intake: \u0432\u044B\u043A\u043B" : state.paused ? `Intake: ${waiting.length} \u0436\u0434\u0443\u0442 | \u043F\u0430\u0443\u0437\u0430` : `Intake: ${waiting.length} \u0436\u0434\u0443\u0442 | \u0441\u043B\u0435\u0434\u0443\u044E\u0449\u0438\u0439 \u043E\u043F\u0440\u043E\u0441 ${clock4(nextPollAt)}`;
|
|
175017
175521
|
return {
|
|
175018
175522
|
configured: file.present,
|
|
175019
175523
|
enabled: config.enabled,
|
|
@@ -175811,12 +176315,19 @@ function presentDecisions(open, otui, chrome, options) {
|
|
|
175811
176315
|
return handle;
|
|
175812
176316
|
}
|
|
175813
176317
|
async function openDecisions(otui, chrome, options) {
|
|
175814
|
-
const { cwd, ...rest } = options;
|
|
175815
|
-
const
|
|
176318
|
+
const { cwd, confirmationLines, ...rest } = options;
|
|
176319
|
+
const report = await reportText(cwd).catch(() => UNREADABLE);
|
|
176320
|
+
const text = withConfirmationLines(report, confirmationLines?.() ?? []);
|
|
175816
176321
|
return presentDecisions((hostOtui, hostChrome, input) => openModal(hostOtui, hostChrome, input), otui, chrome, { ...rest, text });
|
|
175817
176322
|
}
|
|
176323
|
+
function withConfirmationLines(report, cards) {
|
|
176324
|
+
return cards.length === 0 ? report : `${report}
|
|
176325
|
+
|
|
176326
|
+
${cards.join(`
|
|
176327
|
+
`)}`;
|
|
176328
|
+
}
|
|
175818
176329
|
async function openDecisionsArms(otui, chrome, options) {
|
|
175819
|
-
const { cwd, ...rest } = options;
|
|
176330
|
+
const { cwd, confirmationLines: _cards, ...rest } = options;
|
|
175820
176331
|
const text = await armsText(cwd);
|
|
175821
176332
|
return presentDecisions((hostOtui, hostChrome, input) => openModal(hostOtui, hostChrome, input), otui, chrome, { ...rest, text, tabLabel: "Arms" });
|
|
175822
176333
|
}
|
|
@@ -175909,6 +176420,7 @@ function mountDecisionsSidebar(options) {
|
|
|
175909
176420
|
modal?.close({ restoreFocus: false });
|
|
175910
176421
|
modal = await open(core, chrome, {
|
|
175911
176422
|
cwd: options.cwd,
|
|
176423
|
+
...options.confirmationLines !== undefined ? { confirmationLines: options.confirmationLines } : {},
|
|
175912
176424
|
...options.onKeypress !== undefined ? { onKeypress: options.onKeypress } : {}
|
|
175913
176425
|
});
|
|
175914
176426
|
return modal;
|
|
@@ -176766,7 +177278,7 @@ function discardResultText(result, runId) {
|
|
|
176766
177278
|
return `Discarded run ${shortId(runId)}. Its stored patch was deleted.`;
|
|
176767
177279
|
return `Not discarded (${result.reason}): ${clean3(result.detail)}`;
|
|
176768
177280
|
}
|
|
176769
|
-
function
|
|
177281
|
+
function errorText5(error) {
|
|
176770
177282
|
return clean3(error instanceof Error ? error.message : String(error));
|
|
176771
177283
|
}
|
|
176772
177284
|
function orderedFiles(record) {
|
|
@@ -176798,7 +177310,7 @@ function externalDiffSlashText(deps) {
|
|
|
176798
177310
|
}
|
|
176799
177311
|
}
|
|
176800
177312
|
} catch (error) {
|
|
176801
|
-
lines.push(`Could not read the write runs: ${
|
|
177313
|
+
lines.push(`Could not read the write runs: ${errorText5(error)}`);
|
|
176802
177314
|
}
|
|
176803
177315
|
lines.push("Use keryx agents external review <run-id> and keryx agents external apply <run-id> in a terminal.");
|
|
176804
177316
|
return `${lines.join(`
|
|
@@ -176823,7 +177335,7 @@ function createExternalDiffController(options) {
|
|
|
176823
177335
|
runs = deps.list();
|
|
176824
177336
|
} catch (error) {
|
|
176825
177337
|
runs = [];
|
|
176826
|
-
notice = `Could not read the write runs: ${
|
|
177338
|
+
notice = `Could not read the write runs: ${errorText5(error)}`;
|
|
176827
177339
|
}
|
|
176828
177340
|
const ids = new Set(runs.map((run) => run.runId));
|
|
176829
177341
|
for (const id of [...views.keys()]) {
|
|
@@ -176853,7 +177365,7 @@ function createExternalDiffController(options) {
|
|
|
176853
177365
|
const view = deps.view(run.runId);
|
|
176854
177366
|
entry = view === undefined ? {} : { view };
|
|
176855
177367
|
} catch (error) {
|
|
176856
|
-
entry = { error:
|
|
177368
|
+
entry = { error: errorText5(error) };
|
|
176857
177369
|
}
|
|
176858
177370
|
views.set(run.runId, entry);
|
|
176859
177371
|
return entry;
|
|
@@ -176951,7 +177463,7 @@ function createExternalDiffController(options) {
|
|
|
176951
177463
|
try {
|
|
176952
177464
|
text = landResultText(await deps.land({ runId, confirmedPatchHash: hash, allowFlagged }));
|
|
176953
177465
|
} catch (error) {
|
|
176954
|
-
text = `Apply failed: ${
|
|
177466
|
+
text = `Apply failed: ${errorText5(error)}. ${NOTHING_APPLIED}`;
|
|
176955
177467
|
}
|
|
176956
177468
|
allowFlaggedRun = undefined;
|
|
176957
177469
|
views.clear();
|
|
@@ -176972,7 +177484,7 @@ function createExternalDiffController(options) {
|
|
|
176972
177484
|
try {
|
|
176973
177485
|
notice = discardResultText(deps.discard(run.runId), run.runId);
|
|
176974
177486
|
} catch (error) {
|
|
176975
|
-
notice = `Discard failed: ${
|
|
177487
|
+
notice = `Discard failed: ${errorText5(error)}`;
|
|
176976
177488
|
}
|
|
176977
177489
|
allowFlaggedRun = undefined;
|
|
176978
177490
|
views.clear();
|
|
@@ -177257,7 +177769,7 @@ var init_external_diff_sidebar = __esm(() => {
|
|
|
177257
177769
|
|
|
177258
177770
|
// src/tui/autoreview-surface.ts
|
|
177259
177771
|
import { spawn as nodeSpawn4 } from "child_process";
|
|
177260
|
-
import { existsSync as existsSync87, readdirSync as readdirSync18, readFileSync as
|
|
177772
|
+
import { existsSync as existsSync87, readdirSync as readdirSync18, readFileSync as readFileSync42 } from "fs";
|
|
177261
177773
|
import path386 from "path";
|
|
177262
177774
|
function parseAutoReviewArgs(line) {
|
|
177263
177775
|
const words = line.trim().split(/\s+/).slice(1);
|
|
@@ -177306,7 +177818,7 @@ function parseAutoReviewArgs(line) {
|
|
|
177306
177818
|
}
|
|
177307
177819
|
function readJson15(file) {
|
|
177308
177820
|
try {
|
|
177309
|
-
return JSON.parse(
|
|
177821
|
+
return JSON.parse(readFileSync42(file, "utf8"));
|
|
177310
177822
|
} catch {
|
|
177311
177823
|
return;
|
|
177312
177824
|
}
|
|
@@ -178707,7 +179219,7 @@ var init_stdin_guard = __esm(() => {
|
|
|
178707
179219
|
|
|
178708
179220
|
// src/tui/renderer-debug.ts
|
|
178709
179221
|
import { spawnSync as spawnSync7 } from "child_process";
|
|
178710
|
-
import { readFileSync as
|
|
179222
|
+
import { readFileSync as readFileSync43, readlinkSync as readlinkSync2 } from "fs";
|
|
178711
179223
|
function routeProcessWarnings(write = (text) => {
|
|
178712
179224
|
process.stderr.write(text);
|
|
178713
179225
|
}) {
|
|
@@ -178744,7 +179256,7 @@ function eofDiagnostics() {
|
|
|
178744
179256
|
}
|
|
178745
179257
|
let stat;
|
|
178746
179258
|
try {
|
|
178747
|
-
stat =
|
|
179259
|
+
stat = readFileSync43("/proc/self/stat", "utf8").trim();
|
|
178748
179260
|
} catch {
|
|
178749
179261
|
stat = "";
|
|
178750
179262
|
}
|
|
@@ -188619,12 +189131,12 @@ function maxOverFiles(files, values) {
|
|
|
188619
189131
|
}
|
|
188620
189132
|
|
|
188621
189133
|
// src/wiki/deep-enrich.ts
|
|
188622
|
-
import { randomUUID as randomUUID45, createHash as
|
|
189134
|
+
import { randomUUID as randomUUID45, createHash as createHash95 } from "crypto";
|
|
188623
189135
|
function errorMessage5(cause) {
|
|
188624
189136
|
return cause instanceof Error ? cause.message : String(cause);
|
|
188625
189137
|
}
|
|
188626
189138
|
function sha256Hex10(text) {
|
|
188627
|
-
return
|
|
189139
|
+
return createHash95("sha256").update(text).digest("hex");
|
|
188628
189140
|
}
|
|
188629
189141
|
function buildDeepEnrichTools(port) {
|
|
188630
189142
|
const deepOps = METAPROJECT_OPERATIONS.filter((op) => DEEP_ENRICH_OPS.includes(op.name));
|
|
@@ -188863,7 +189375,7 @@ var init_deep_enrich = __esm(() => {
|
|
|
188863
189375
|
});
|
|
188864
189376
|
|
|
188865
189377
|
// src/wiki/enrich.ts
|
|
188866
|
-
import { existsSync as existsSync88, mkdirSync as mkdirSync14, readFileSync as
|
|
189378
|
+
import { existsSync as existsSync88, mkdirSync as mkdirSync14, readFileSync as readFileSync44, writeFileSync as writeFileSync12 } from "fs";
|
|
188867
189379
|
import { readFile as readFile207 } from "fs/promises";
|
|
188868
189380
|
import path393 from "path";
|
|
188869
189381
|
function resolveEnrichProviderModel(input) {
|
|
@@ -188881,7 +189393,7 @@ function loadResumeState(cwd) {
|
|
|
188881
189393
|
if (!existsSync88(file)) {
|
|
188882
189394
|
return { updatedAt: new Date().toISOString(), completed: [], failed: [] };
|
|
188883
189395
|
}
|
|
188884
|
-
const raw = JSON.parse(
|
|
189396
|
+
const raw = JSON.parse(readFileSync44(file, "utf8"));
|
|
188885
189397
|
if (raw === null || typeof raw !== "object") {
|
|
188886
189398
|
return { updatedAt: new Date().toISOString(), completed: [], failed: [] };
|
|
188887
189399
|
}
|
|
@@ -191485,7 +191997,7 @@ var init_turn_loop = __esm(() => {
|
|
|
191485
191997
|
});
|
|
191486
191998
|
|
|
191487
191999
|
// src/tui/worker-fleet.ts
|
|
191488
|
-
function
|
|
192000
|
+
function clip8(s, max) {
|
|
191489
192001
|
if (s.length <= max) {
|
|
191490
192002
|
return s;
|
|
191491
192003
|
}
|
|
@@ -191576,7 +192088,7 @@ function formatFleetSidebar(workers, maxLines = 14) {
|
|
|
191576
192088
|
if (main !== undefined) {
|
|
191577
192089
|
lines.push(mainHeadline(main.status));
|
|
191578
192090
|
const phase = humanFleetPhase(main.status, main.detail);
|
|
191579
|
-
lines.push(` ${
|
|
192091
|
+
lines.push(` ${clip8(phase, 26)}`);
|
|
191580
192092
|
if (main.status === "blocked") {
|
|
191581
192093
|
lines.push(" \u2191 pick menu above input");
|
|
191582
192094
|
}
|
|
@@ -191611,8 +192123,8 @@ function formatFleetSidebar(workers, maxLines = 14) {
|
|
|
191611
192123
|
for (const w of shown) {
|
|
191612
192124
|
const g = STATUS_GLYPH2[w.status];
|
|
191613
192125
|
const phase = humanFleetPhase(w.status, w.detail);
|
|
191614
|
-
const label =
|
|
191615
|
-
const det = phase.length > 0 ? ` ${
|
|
192126
|
+
const label = clip8(w.label, 12);
|
|
192127
|
+
const det = phase.length > 0 ? ` ${clip8(phase, 12)}` : "";
|
|
191616
192128
|
lines.push(`${g} ${label}${det}`);
|
|
191617
192129
|
}
|
|
191618
192130
|
if (sorted.length > shown.length) {
|
|
@@ -191642,8 +192154,8 @@ function formatPeerLine(peer) {
|
|
|
191642
192154
|
const statusGlyph = PEER_STATUS_GLYPH[peer.status] ?? "?";
|
|
191643
192155
|
const name = displaySafe(peer.name);
|
|
191644
192156
|
const activity = displaySafe(peer.activity).trim();
|
|
191645
|
-
const detail = activity.length > 0 ? ` ${
|
|
191646
|
-
return `${marker}${statusGlyph} @${
|
|
192157
|
+
const detail = activity.length > 0 ? ` ${clip8(activity, 14)}` : "";
|
|
192158
|
+
return `${marker}${statusGlyph} @${clip8(name, 10)}${detail}`;
|
|
191647
192159
|
}
|
|
191648
192160
|
function formatFleetSidebarWithPeers(workers, peers = [], maxLines = 14) {
|
|
191649
192161
|
const base = formatFleetSidebar(workers, maxLines);
|
|
@@ -191746,7 +192258,7 @@ var init_worker_fleet = __esm(() => {
|
|
|
191746
192258
|
});
|
|
191747
192259
|
|
|
191748
192260
|
// src/tui/subagent-session.ts
|
|
191749
|
-
function
|
|
192261
|
+
function clip9(s, max) {
|
|
191750
192262
|
if (s.length <= max) {
|
|
191751
192263
|
return s;
|
|
191752
192264
|
}
|
|
@@ -191778,11 +192290,11 @@ function formatSubagentRow(session, width = SIDEBAR_TEXT_WIDTH) {
|
|
|
191778
192290
|
const mark = session.runtime === "external" ? EXTERNAL_SUBAGENT_MARK : "";
|
|
191779
192291
|
const budget = Math.max(4, width - glyph.length - 1);
|
|
191780
192292
|
const labelBudget = Math.max(1, Math.min(12, budget) - mark.length);
|
|
191781
|
-
const label = `${mark}${
|
|
192293
|
+
const label = `${mark}${clip9(session.label, labelBudget)}`;
|
|
191782
192294
|
const used = glyph.length + 1 + label.length;
|
|
191783
192295
|
const detBudget = width - used - 1;
|
|
191784
|
-
const det = detBudget >= 3 && phase.length > 0 ? ` ${
|
|
191785
|
-
return
|
|
192296
|
+
const det = detBudget >= 3 && phase.length > 0 ? ` ${clip9(phase, detBudget)}` : "";
|
|
192297
|
+
return clip9(`${glyph} ${label}${det}`, width);
|
|
191786
192298
|
}
|
|
191787
192299
|
function formatSubagentListHeader(count) {
|
|
191788
192300
|
return `Subagents ${count}`;
|
|
@@ -192877,7 +193389,7 @@ function renderExternalTranscript(events, options = {}) {
|
|
|
192877
193389
|
if (options.summary !== false && events.length > 0) {
|
|
192878
193390
|
lines.push("", formatTranscriptFooter(foldExternalTranscript(events)));
|
|
192879
193391
|
}
|
|
192880
|
-
return options.width === undefined ? lines : lines.map((line) =>
|
|
193392
|
+
return options.width === undefined ? lines : lines.map((line) => clip10(line, options.width ?? 0));
|
|
192881
193393
|
}
|
|
192882
193394
|
function formatExternalWork(events, options = {}) {
|
|
192883
193395
|
return renderExternalTranscript(events, options).join(`
|
|
@@ -193042,7 +193554,7 @@ function describeSkips(skippedLines) {
|
|
|
193042
193554
|
function plural5(count, singular, pluralForm) {
|
|
193043
193555
|
return `${count} ${count === 1 ? singular : pluralForm ?? `${singular}s`}`;
|
|
193044
193556
|
}
|
|
193045
|
-
function
|
|
193557
|
+
function clip10(value, width) {
|
|
193046
193558
|
if (width <= 0 || value.length <= width)
|
|
193047
193559
|
return value;
|
|
193048
193560
|
return `${value.slice(0, Math.max(1, width - 1))}\u2026`;
|
|
@@ -193185,7 +193697,7 @@ function appendBounded(output, chunk) {
|
|
|
193185
193697
|
const combined = output + chunk;
|
|
193186
193698
|
return combined.length > MAX_BACKGROUND_JOB_OUTPUT_CHARS ? combined.slice(-MAX_BACKGROUND_JOB_OUTPUT_CHARS) : combined;
|
|
193187
193699
|
}
|
|
193188
|
-
function
|
|
193700
|
+
function clip11(s, max) {
|
|
193189
193701
|
if (max <= 0) {
|
|
193190
193702
|
return "";
|
|
193191
193703
|
}
|
|
@@ -193200,8 +193712,8 @@ function formatJobListHeader(count) {
|
|
|
193200
193712
|
function formatJobRow(entry, width = SIDEBAR_TEXT_WIDTH) {
|
|
193201
193713
|
const glyph = STATUS_GLYPH4[entry.status];
|
|
193202
193714
|
const budget = Math.max(1, width - glyph.length - 1);
|
|
193203
|
-
const cmd =
|
|
193204
|
-
return
|
|
193715
|
+
const cmd = clip11(entry.command, budget);
|
|
193716
|
+
return clip11(`${glyph} ${cmd}`, width);
|
|
193205
193717
|
}
|
|
193206
193718
|
function formatJobMeta(entry, now = Date.now()) {
|
|
193207
193719
|
const rows = [
|
|
@@ -194981,6 +195493,9 @@ function shouldAutoApproveReadOnlySpawn(mode, hookAsk, untrustedOrigin) {
|
|
|
194981
195493
|
function isUserPromptApprovalCard(tool) {
|
|
194982
195494
|
return tool === "user_prompt";
|
|
194983
195495
|
}
|
|
195496
|
+
function withMirrorNote(card, note) {
|
|
195497
|
+
return card.length === 0 ? [note] : [...card.slice(0, -1), note, card[card.length - 1]];
|
|
195498
|
+
}
|
|
194984
195499
|
function operatorCardDialog(cardTitle) {
|
|
194985
195500
|
if (cardTitle === undefined) {
|
|
194986
195501
|
return {
|
|
@@ -196431,6 +196946,8 @@ async function launchTuiAgentShell(opts) {
|
|
|
196431
196946
|
let liveReviewsPanel;
|
|
196432
196947
|
let remoteBridge;
|
|
196433
196948
|
let liveRemotePanel;
|
|
196949
|
+
const operatorCards = new OperatorConfirmationLog;
|
|
196950
|
+
const operatorCardLines = () => confirmationLines(operatorCards.entries(), Date.now());
|
|
196434
196951
|
let liveChannelsPanel;
|
|
196435
196952
|
const channelsClient = new ChannelsClient;
|
|
196436
196953
|
let liveApprovals;
|
|
@@ -196785,6 +197302,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
196785
197302
|
liveRemotePanel = mountRemotePanel(otui, r, sidebar, {
|
|
196786
197303
|
width: SIDEBAR_TEXT_WIDTH,
|
|
196787
197304
|
getStatus: remoteStatus,
|
|
197305
|
+
getWaitingCards: () => operatorCards.waiting().length,
|
|
196788
197306
|
onOpen: () => showRemoteControl(),
|
|
196789
197307
|
onOpenPermissions: () => showPermissions(),
|
|
196790
197308
|
getPosture: () => {
|
|
@@ -196826,7 +197344,8 @@ async function launchTuiAgentShell(opts) {
|
|
|
196826
197344
|
notice: (text) => io.onSystem?.(`\u25C7 ${text}
|
|
196827
197345
|
`),
|
|
196828
197346
|
lastDecisionId: lastAskUserDecisionId,
|
|
196829
|
-
session: askUserSessionId()
|
|
197347
|
+
session: askUserSessionId(),
|
|
197348
|
+
confirmationLines: operatorCardLines
|
|
196830
197349
|
});
|
|
196831
197350
|
liveDecisions = decisionsPanel;
|
|
196832
197351
|
liveResearchSync = mountResearchSyncPanel(otui, r, sidebar, {
|
|
@@ -197824,10 +198343,16 @@ async function launchTuiAgentShell(opts) {
|
|
|
197824
198343
|
`);
|
|
197825
198344
|
if (decision !== "allow")
|
|
197826
198345
|
return false;
|
|
197827
|
-
return meta?.fingerprint !== undefined ?
|
|
198346
|
+
return meta?.fingerprint !== undefined ? remoteApproval(meta.fingerprint, answer.fromId) : true;
|
|
197828
198347
|
};
|
|
197829
198348
|
const mirrorApprovalToTopic = (inner) => async (tool, inputJson, meta) => {
|
|
197830
198349
|
const bridge = remoteBridge;
|
|
198350
|
+
const isCard = meta?.alwaysAsk === true && meta.card !== undefined && meta.card.length > 0 && !isUserPromptApprovalCard(tool);
|
|
198351
|
+
const unmirrored = isCard ? mirrorUndeliveredNote(bridge) : undefined;
|
|
198352
|
+
if (unmirrored !== undefined && meta?.card !== undefined) {
|
|
198353
|
+
const card = withMirrorNote(meta.card, unmirrored);
|
|
198354
|
+
return askUnmirroredCard(operatorCards, meta.card[0] ?? tool, foregroundOperation.signal, () => inner(tool, inputJson, { ...meta, card }));
|
|
198355
|
+
}
|
|
197831
198356
|
if (bridge === undefined || !bridge.active) {
|
|
197832
198357
|
return inner(tool, inputJson, meta);
|
|
197833
198358
|
}
|
|
@@ -197836,8 +198361,12 @@ async function launchTuiAgentShell(opts) {
|
|
|
197836
198361
|
return inner(tool, inputJson, meta);
|
|
197837
198362
|
}
|
|
197838
198363
|
const brief = tool.length > 60 ? `${tool.slice(0, 59)}\u2026` : tool;
|
|
197839
|
-
const outcome = await
|
|
198364
|
+
const outcome = await raceMirroredCard({
|
|
198365
|
+
log: operatorCards,
|
|
198366
|
+
question: isCard && meta?.card !== undefined ? meta.card[0] ?? tool : undefined,
|
|
197840
198367
|
turnSignal: foregroundOperation.signal,
|
|
198368
|
+
onUndelivered: () => io.onSystem?.(`\u25C7 Telegram mirror not delivered: the topic did not take the card. Answer it here.
|
|
198369
|
+
`),
|
|
197841
198370
|
dock: (race) => inner(tool, inputJson, meta, race),
|
|
197842
198371
|
topic: (signal) => bridge.askApproval(describeApprovalForTopic(tool, inputJson, meta), { signal, timeoutMs: MAX_APPROVAL_TIMEOUT_MS })
|
|
197843
198372
|
});
|
|
@@ -197851,7 +198380,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
197851
198380
|
`);
|
|
197852
198381
|
if (!allowed)
|
|
197853
198382
|
return false;
|
|
197854
|
-
return meta?.fingerprint !== undefined ?
|
|
198383
|
+
return meta?.fingerprint !== undefined ? remoteApproval(meta.fingerprint, outcome.answer.fromId) : true;
|
|
197855
198384
|
};
|
|
197856
198385
|
io.requestApproval = trackApproval(mirrorApprovalToTopic(requestApprovalLocally));
|
|
197857
198386
|
const askUserInteractive = async (req) => {
|
|
@@ -198698,9 +199227,14 @@ Staying in the current session.
|
|
|
198698
199227
|
});
|
|
198699
199228
|
};
|
|
198700
199229
|
const remoteModalListeners = new Set;
|
|
199230
|
+
operatorCards.subscribe(() => {
|
|
199231
|
+
liveRemotePanel?.refresh();
|
|
199232
|
+
remoteModalListeners.forEach((listener) => listener());
|
|
199233
|
+
});
|
|
198701
199234
|
const showRemoteControl = () => {
|
|
198702
199235
|
openRemoteControl(otui, chrome, {
|
|
198703
199236
|
getStatus: remoteStatus,
|
|
199237
|
+
getConfirmationLines: operatorCardLines,
|
|
198704
199238
|
subscribeChange: (listener) => {
|
|
198705
199239
|
remoteModalListeners.add(listener);
|
|
198706
199240
|
return () => {
|
|
@@ -201276,6 +201810,7 @@ var init_tui_shell = __esm(() => {
|
|
|
201276
201810
|
init_approvals_sidebar();
|
|
201277
201811
|
init_intake_surface();
|
|
201278
201812
|
init_decisions_surface();
|
|
201813
|
+
init_operator_confirmations();
|
|
201279
201814
|
init_research_sync_panel();
|
|
201280
201815
|
init_external_diff_sidebar();
|
|
201281
201816
|
init_autoreview_surface();
|
|
@@ -201836,7 +202371,7 @@ var init_chat_shell = __esm(() => {
|
|
|
201836
202371
|
|
|
201837
202372
|
// src/tui/debug-watcher.ts
|
|
201838
202373
|
import { spawn as spawn12, spawnSync as spawnSync9 } from "child_process";
|
|
201839
|
-
import { closeSync as closeSync12, constants as constants7, openSync as openSync13, readdirSync as readdirSync19, readFileSync as
|
|
202374
|
+
import { closeSync as closeSync12, constants as constants7, openSync as openSync13, readdirSync as readdirSync19, readFileSync as readFileSync45, readlinkSync as readlinkSync3, statSync as statSync24 } from "fs";
|
|
201840
202375
|
import path396 from "path";
|
|
201841
202376
|
function parseEpollFdinfo(text) {
|
|
201842
202377
|
const out = [];
|
|
@@ -201881,7 +202416,7 @@ function isAlive(pid) {
|
|
|
201881
202416
|
}
|
|
201882
202417
|
function readText7(file) {
|
|
201883
202418
|
try {
|
|
201884
|
-
return
|
|
202419
|
+
return readFileSync45(file, "utf8");
|
|
201885
202420
|
} catch {
|
|
201886
202421
|
return;
|
|
201887
202422
|
}
|
|
@@ -204928,7 +205463,13 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
|
|
|
204928
205463
|
...flags.denyTools !== undefined ? { denyTools: flags.denyTools } : {},
|
|
204929
205464
|
...bus !== undefined ? { bus } : {},
|
|
204930
205465
|
schedules: { projectRoot: cwd, defaults: () => ({ provider: sel.provider, model: sel.model }) },
|
|
204931
|
-
operatorConfirm: {
|
|
205466
|
+
operatorConfirm: {
|
|
205467
|
+
projectRoot: cwd,
|
|
205468
|
+
allowedUserIds: () => {
|
|
205469
|
+
const loaded = loadRemoteConfig();
|
|
205470
|
+
return loaded.ok ? loaded.value.allowedUserIds : undefined;
|
|
205471
|
+
}
|
|
205472
|
+
}
|
|
204932
205473
|
}),
|
|
204933
205474
|
mcpRuntime: () => mcpRuntime,
|
|
204934
205475
|
maxOutputTokens: resolveAgentMaxOutputTokens({
|
|
@@ -205379,6 +205920,7 @@ var init_shell = __esm(() => {
|
|
|
205379
205920
|
init_reviews_inspector();
|
|
205380
205921
|
init_remote_control_surface();
|
|
205381
205922
|
init_channels_client();
|
|
205923
|
+
init_config11();
|
|
205382
205924
|
init_channels_surface();
|
|
205383
205925
|
init_intake_surface();
|
|
205384
205926
|
init_session_info();
|
|
@@ -211940,7 +212482,7 @@ var init_judge_recordings = __esm(() => {
|
|
|
211940
212482
|
});
|
|
211941
212483
|
|
|
211942
212484
|
// src/gdskills/governance/authoring-lint.ts
|
|
211943
|
-
import { existsSync as existsSync90, readdirSync as readdirSync20, readFileSync as
|
|
212485
|
+
import { existsSync as existsSync90, readdirSync as readdirSync20, readFileSync as readFileSync46 } from "fs";
|
|
211944
212486
|
import path414 from "path";
|
|
211945
212487
|
function skillDirectoryName(skillMdPath) {
|
|
211946
212488
|
return path414.basename(path414.dirname(skillMdPath));
|
|
@@ -211965,7 +212507,7 @@ function deepReferenceFindings(skillMdPath) {
|
|
|
211965
212507
|
if (!entry.isFile() || !entry.name.endsWith(".md"))
|
|
211966
212508
|
continue;
|
|
211967
212509
|
const refPath = path414.join(referencesDir, entry.name);
|
|
211968
|
-
const content =
|
|
212510
|
+
const content = readFileSync46(refPath, "utf8");
|
|
211969
212511
|
const linksAnotherReference = /\]\([^)]*\.md\)/.test(content) || /\breferences\/[\w.-]+\.md\b/.test(content);
|
|
211970
212512
|
if (linksAnotherReference) {
|
|
211971
212513
|
findings.push({
|
|
@@ -212086,15 +212628,15 @@ var init_authoring_lint = __esm(() => {
|
|
|
212086
212628
|
});
|
|
212087
212629
|
|
|
212088
212630
|
// src/gdskills/governance/stocktake.ts
|
|
212089
|
-
import { createHash as
|
|
212090
|
-
import { existsSync as existsSync91, readdirSync as readdirSync21, readFileSync as
|
|
212631
|
+
import { createHash as createHash96 } from "crypto";
|
|
212632
|
+
import { existsSync as existsSync91, readdirSync as readdirSync21, readFileSync as readFileSync47, statSync as statSync25 } from "fs";
|
|
212091
212633
|
import path415 from "path";
|
|
212092
212634
|
function cacheKeyMatches(cached, inputs, quick) {
|
|
212093
212635
|
return cached.algorithmVersion === ALGORITHM_VERSION && cached.quick === quick && cached.sha256 === inputs.sha256 && cached.catalogFingerprint === inputs.catalogFingerprint && cached.verificationReportHash === inputs.verificationReportHash && cached.referencesHash === inputs.referencesHash;
|
|
212094
212636
|
}
|
|
212095
212637
|
function catalogFingerprint(catalog) {
|
|
212096
212638
|
const parts = [...catalog].map((entry) => `${entry.id}:${entry.sha256}`).sort();
|
|
212097
|
-
return
|
|
212639
|
+
return createHash96("sha256").update(parts.join(`
|
|
212098
212640
|
`)).digest("hex");
|
|
212099
212641
|
}
|
|
212100
212642
|
function verificationReportHash(root, entry) {
|
|
@@ -212102,7 +212644,7 @@ function verificationReportHash(root, entry) {
|
|
|
212102
212644
|
if (!existsSync91(reportPath))
|
|
212103
212645
|
return null;
|
|
212104
212646
|
try {
|
|
212105
|
-
return
|
|
212647
|
+
return createHash96("sha256").update(readFileSync47(reportPath, "utf8")).digest("hex");
|
|
212106
212648
|
} catch {
|
|
212107
212649
|
return null;
|
|
212108
212650
|
}
|
|
@@ -212124,11 +212666,11 @@ function referencesHash(entry) {
|
|
|
212124
212666
|
}
|
|
212125
212667
|
};
|
|
212126
212668
|
walk(referencesDir, "");
|
|
212127
|
-
const hash =
|
|
212669
|
+
const hash = createHash96("sha256");
|
|
212128
212670
|
for (const rel of files) {
|
|
212129
212671
|
hash.update(rel);
|
|
212130
212672
|
hash.update("\x00");
|
|
212131
|
-
hash.update(
|
|
212673
|
+
hash.update(readFileSync47(path415.join(referencesDir, rel)));
|
|
212132
212674
|
hash.update("\x00");
|
|
212133
212675
|
}
|
|
212134
212676
|
return hash.digest("hex");
|
|
@@ -212137,7 +212679,7 @@ function loadCache2(cachePath) {
|
|
|
212137
212679
|
if (!existsSync91(cachePath))
|
|
212138
212680
|
return {};
|
|
212139
212681
|
try {
|
|
212140
|
-
return JSON.parse(
|
|
212682
|
+
return JSON.parse(readFileSync47(cachePath, "utf8"));
|
|
212141
212683
|
} catch {
|
|
212142
212684
|
return {};
|
|
212143
212685
|
}
|
|
@@ -212154,7 +212696,7 @@ function checkFreshness(root, entry) {
|
|
|
212154
212696
|
if (!existsSync91(reportPath))
|
|
212155
212697
|
return;
|
|
212156
212698
|
try {
|
|
212157
|
-
const report = JSON.parse(
|
|
212699
|
+
const report = JSON.parse(readFileSync47(reportPath, "utf8"));
|
|
212158
212700
|
if (report.status === "stale" || report.status === "needs-review" || report.status === "blocked") {
|
|
212159
212701
|
return {
|
|
212160
212702
|
skillId: entry.id,
|
|
@@ -213436,7 +213978,7 @@ var init_install_manifest_schema = __esm(() => {
|
|
|
213436
213978
|
});
|
|
213437
213979
|
|
|
213438
213980
|
// src/gdskills/manifest/manifest.ts
|
|
213439
|
-
import { existsSync as existsSync92, readFileSync as
|
|
213981
|
+
import { existsSync as existsSync92, readFileSync as readFileSync48 } from "fs";
|
|
213440
213982
|
import path417 from "path";
|
|
213441
213983
|
import { fileURLToPath as fileURLToPath9 } from "url";
|
|
213442
213984
|
function thisModuleDir() {
|
|
@@ -213468,7 +214010,7 @@ function parseInstallManifest(fileLabel, raw) {
|
|
|
213468
214010
|
}
|
|
213469
214011
|
function loadBundledManifest() {
|
|
213470
214012
|
const file = bundledManifestPath();
|
|
213471
|
-
const parsed = JSON.parse(
|
|
214013
|
+
const parsed = JSON.parse(readFileSync48(file, "utf8"));
|
|
213472
214014
|
return parseInstallManifest(file, parsed);
|
|
213473
214015
|
}
|
|
213474
214016
|
var HARNESS_IDS, BUNDLED_TREE, InvalidInstallManifestError;
|
|
@@ -213572,7 +214114,7 @@ var init_glob = __esm(() => {
|
|
|
213572
214114
|
});
|
|
213573
214115
|
|
|
213574
214116
|
// src/gdskills/manifest/plan.ts
|
|
213575
|
-
import { createHash as
|
|
214117
|
+
import { createHash as createHash97 } from "crypto";
|
|
213576
214118
|
import path419 from "path";
|
|
213577
214119
|
function destinationRootsForTarget(target) {
|
|
213578
214120
|
if (target === "claude")
|
|
@@ -213584,7 +214126,7 @@ function destinationRootsForTarget(target) {
|
|
|
213584
214126
|
}
|
|
213585
214127
|
function stackInputsSha256(stack) {
|
|
213586
214128
|
const { detectedAt: _detectedAt, ...rest } = stack;
|
|
213587
|
-
return
|
|
214129
|
+
return createHash97("sha256").update(stableStringify3(rest)).digest("hex");
|
|
213588
214130
|
}
|
|
213589
214131
|
function stableStringify3(value) {
|
|
213590
214132
|
if (Array.isArray(value)) {
|
|
@@ -226040,7 +226582,7 @@ var init_detect3 = __esm(() => {
|
|
|
226040
226582
|
});
|
|
226041
226583
|
|
|
226042
226584
|
// src/stack/service.ts
|
|
226043
|
-
import { createHash as
|
|
226585
|
+
import { createHash as createHash98 } from "crypto";
|
|
226044
226586
|
import { readFile as readFile233 } from "fs/promises";
|
|
226045
226587
|
import path446 from "path";
|
|
226046
226588
|
function stackJsonPath(root) {
|
|
@@ -226066,7 +226608,7 @@ function serializeStackDetection(doc) {
|
|
|
226066
226608
|
}
|
|
226067
226609
|
function fingerprintOf(core) {
|
|
226068
226610
|
const canonical = JSON.stringify(canonicalize2(core));
|
|
226069
|
-
return
|
|
226611
|
+
return createHash98("sha256").update(canonical, "utf8").digest("hex");
|
|
226070
226612
|
}
|
|
226071
226613
|
async function readStackDetection(root) {
|
|
226072
226614
|
try {
|
|
@@ -226340,9 +226882,9 @@ function evaluateCompletion(input, deps) {
|
|
|
226340
226882
|
var SCHEMA_VERSION3 = 1;
|
|
226341
226883
|
|
|
226342
226884
|
// src/harness/evidence/redaction.ts
|
|
226343
|
-
import { createHash as
|
|
226885
|
+
import { createHash as createHash99 } from "crypto";
|
|
226344
226886
|
function hashContent(content) {
|
|
226345
|
-
return
|
|
226887
|
+
return createHash99("sha256").update(content, "utf8").digest("hex");
|
|
226346
226888
|
}
|
|
226347
226889
|
function redactForPersistence(content, deps) {
|
|
226348
226890
|
const scan = deps.scan(content);
|
|
@@ -226392,7 +226934,7 @@ async function metaprojectBlastRadius(port, target) {
|
|
|
226392
226934
|
}
|
|
226393
226935
|
|
|
226394
226936
|
// src/harness/session/session.ts
|
|
226395
|
-
import { createHash as
|
|
226937
|
+
import { createHash as createHash100 } from "crypto";
|
|
226396
226938
|
function canonicalStringify(value) {
|
|
226397
226939
|
if (Array.isArray(value)) {
|
|
226398
226940
|
return `[${value.map(canonicalStringify).join(",")}]`;
|
|
@@ -226405,7 +226947,7 @@ function canonicalStringify(value) {
|
|
|
226405
226947
|
return JSON.stringify(value) ?? "null";
|
|
226406
226948
|
}
|
|
226407
226949
|
function sha25612(input) {
|
|
226408
|
-
return
|
|
226950
|
+
return createHash100("sha256").update(input, "utf8").digest("hex");
|
|
226409
226951
|
}
|
|
226410
226952
|
function deepFreeze(value) {
|
|
226411
226953
|
if (value !== null && typeof value === "object") {
|
|
@@ -226549,7 +227091,7 @@ var init_startup = __esm(() => {
|
|
|
226549
227091
|
});
|
|
226550
227092
|
|
|
226551
227093
|
// src/harness/run/run.ts
|
|
226552
|
-
import { createHash as
|
|
227094
|
+
import { createHash as createHash101 } from "crypto";
|
|
226553
227095
|
function aliasRunHookToolName(keryxToolName) {
|
|
226554
227096
|
return RUN_HOOK_TOOL_NAME_ALIASES[keryxToolName] ?? keryxToolName;
|
|
226555
227097
|
}
|
|
@@ -226565,7 +227107,7 @@ function canonicalize3(value) {
|
|
|
226565
227107
|
return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalize3(record[key])}`).join(",")}}`;
|
|
226566
227108
|
}
|
|
226567
227109
|
function sha25613(input) {
|
|
226568
|
-
return
|
|
227110
|
+
return createHash101("sha256").update(input, "utf8").digest("hex");
|
|
226569
227111
|
}
|
|
226570
227112
|
function uniqueInOrder2(values) {
|
|
226571
227113
|
const seen = new Set;
|
|
@@ -227158,7 +227700,7 @@ var init_replay = __esm(() => {
|
|
|
227158
227700
|
});
|
|
227159
227701
|
|
|
227160
227702
|
// src/harness/tool/registry.ts
|
|
227161
|
-
import { createHash as
|
|
227703
|
+
import { createHash as createHash102 } from "crypto";
|
|
227162
227704
|
function canonicalize4(value) {
|
|
227163
227705
|
if (value === null || typeof value !== "object") {
|
|
227164
227706
|
return JSON.stringify(value) ?? "null";
|
|
@@ -227172,7 +227714,7 @@ function canonicalize4(value) {
|
|
|
227172
227714
|
return `{${entries.join(",")}}`;
|
|
227173
227715
|
}
|
|
227174
227716
|
function sha25614(input) {
|
|
227175
|
-
return
|
|
227717
|
+
return createHash102("sha256").update(input, "utf8").digest("hex");
|
|
227176
227718
|
}
|
|
227177
227719
|
function definitionHash(definition) {
|
|
227178
227720
|
return sha25614(canonicalize4(definition));
|
|
@@ -227325,9 +227867,9 @@ var init_executor = __esm(() => {
|
|
|
227325
227867
|
|
|
227326
227868
|
// src/harness/process/real-process-adapter.ts
|
|
227327
227869
|
import { spawnSync as spawnSync10 } from "child_process";
|
|
227328
|
-
import { createHash as
|
|
227870
|
+
import { createHash as createHash103 } from "crypto";
|
|
227329
227871
|
function sha256Hex11(input) {
|
|
227330
|
-
return
|
|
227872
|
+
return createHash103("sha256").update(input, "utf8").digest("hex");
|
|
227331
227873
|
}
|
|
227332
227874
|
function byteLength3(chunk) {
|
|
227333
227875
|
if (chunk === null || chunk === undefined)
|
|
@@ -227641,7 +228183,7 @@ function checkApproval(input) {
|
|
|
227641
228183
|
}
|
|
227642
228184
|
|
|
227643
228185
|
// src/commands/harness.ts
|
|
227644
|
-
import { readFileSync as
|
|
228186
|
+
import { readFileSync as readFileSync49, writeFileSync as writeFileSync13 } from "fs";
|
|
227645
228187
|
import path448 from "path";
|
|
227646
228188
|
import { randomUUID as randomUUID49 } from "crypto";
|
|
227647
228189
|
import { realpathSync as realpathSync21 } from "fs";
|
|
@@ -227884,7 +228426,7 @@ function parseReplayArgs(args) {
|
|
|
227884
228426
|
function readJsonFile3(file) {
|
|
227885
228427
|
let text;
|
|
227886
228428
|
try {
|
|
227887
|
-
text =
|
|
228429
|
+
text = readFileSync49(file, "utf8");
|
|
227888
228430
|
} catch (error) {
|
|
227889
228431
|
return { ok: false, reason: error instanceof Error ? error.message : String(error) };
|
|
227890
228432
|
}
|
|
@@ -228248,7 +228790,7 @@ function readSpecArg(args) {
|
|
|
228248
228790
|
if (specPath === undefined) {
|
|
228249
228791
|
throw new Error("keryx harness: a --spec <path> argument is required when no spec is injected.");
|
|
228250
228792
|
}
|
|
228251
|
-
return JSON.parse(
|
|
228793
|
+
return JSON.parse(readFileSync49(specPath, "utf8"));
|
|
228252
228794
|
}
|
|
228253
228795
|
function harnessExtension(args, deps) {
|
|
228254
228796
|
const { clock, idSeq } = resolveRuntime(deps);
|
|
@@ -228431,7 +228973,7 @@ async function sessionsCommand(args) {
|
|
|
228431
228973
|
console.log(pad2("ID", 10) + pad2("UPDATED", 22) + pad2("MSGS", 6) + pad2("LIVE", 7) + pad2("MODEL", 24) + "TITLE");
|
|
228432
228974
|
for (const { summary: s, lease } of listed) {
|
|
228433
228975
|
const model = s.provider !== undefined && s.model !== undefined ? `${s.provider}/${s.model}` : s.model ?? "-";
|
|
228434
|
-
console.log(pad2(shortSessionId(s.id), 10) + pad2(s.updatedAt.slice(0, 19).replace("T", " "), 22) + pad2(String(s.messageCount), 6) + pad2(lease ?? "", 7) + pad2(
|
|
228976
|
+
console.log(pad2(shortSessionId(s.id), 10) + pad2(s.updatedAt.slice(0, 19).replace("T", " "), 22) + pad2(String(s.messageCount), 6) + pad2(lease ?? "", 7) + pad2(clip12(model, 22), 24) + (s.parentSessionId !== undefined ? `\u21B3 ${s.title}` : s.title));
|
|
228435
228977
|
if (s.remote !== undefined) {
|
|
228436
228978
|
console.log(`${" ".repeat(10)}\u21C4 ${describeRemote(s.remote)}`);
|
|
228437
228979
|
}
|
|
@@ -228537,7 +229079,7 @@ function leaseColumn(cwd, sessionId) {
|
|
|
228537
229079
|
function pad2(s, n) {
|
|
228538
229080
|
return s.length >= n ? `${s.slice(0, n - 1)} ` : s + " ".repeat(n - s.length);
|
|
228539
229081
|
}
|
|
228540
|
-
function
|
|
229082
|
+
function clip12(s, n) {
|
|
228541
229083
|
return s.length > n ? `${s.slice(0, n - 1)}\u2026` : s;
|
|
228542
229084
|
}
|
|
228543
229085
|
function printHelp20() {
|
|
@@ -229107,7 +229649,7 @@ var init_session3 = __esm(() => {
|
|
|
229107
229649
|
});
|
|
229108
229650
|
|
|
229109
229651
|
// src/acp/session-mcp.ts
|
|
229110
|
-
import { createHash as
|
|
229652
|
+
import { createHash as createHash104 } from "crypto";
|
|
229111
229653
|
function isRecord17(value) {
|
|
229112
229654
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
229113
229655
|
}
|
|
@@ -229234,7 +229776,7 @@ function acpMcpSetKey(parsed) {
|
|
|
229234
229776
|
})),
|
|
229235
229777
|
refused: parsed.refused
|
|
229236
229778
|
};
|
|
229237
|
-
return
|
|
229779
|
+
return createHash104("sha256").update(JSON.stringify(canonical)).digest("hex");
|
|
229238
229780
|
}
|
|
229239
229781
|
function startAcpSessionMcp(parsed, options = {}) {
|
|
229240
229782
|
const refused = parsed.refused.map((problem) => ({ name: problem.name, reason: scrub(problem.reason, parsed.secrets) }));
|
|
@@ -230193,9 +230735,9 @@ var init_server2 = __esm(() => {
|
|
|
230193
230735
|
});
|
|
230194
230736
|
|
|
230195
230737
|
// src/acp/fixture-provider.ts
|
|
230196
|
-
import { readFileSync as
|
|
230738
|
+
import { readFileSync as readFileSync50 } from "fs";
|
|
230197
230739
|
function loadAcpFixture(path) {
|
|
230198
|
-
const raw =
|
|
230740
|
+
const raw = readFileSync50(path, "utf-8");
|
|
230199
230741
|
const parsed = JSON.parse(raw);
|
|
230200
230742
|
const turns = parsed.turns;
|
|
230201
230743
|
const models = parsed.models !== undefined && parsed.models.length > 0 ? [...parsed.models] : ["fixture-model"];
|
|
@@ -232278,7 +232820,7 @@ var init_serve_runner = __esm(() => {
|
|
|
232278
232820
|
});
|
|
232279
232821
|
|
|
232280
232822
|
// src/remote/naming.ts
|
|
232281
|
-
import { createHash as
|
|
232823
|
+
import { createHash as createHash105 } from "crypto";
|
|
232282
232824
|
import { hostname as hostname5 } from "os";
|
|
232283
232825
|
import path451 from "path";
|
|
232284
232826
|
function nameKey(name) {
|
|
@@ -232308,7 +232850,7 @@ function idChars(sessionId) {
|
|
|
232308
232850
|
}
|
|
232309
232851
|
function machineSlug(machine) {
|
|
232310
232852
|
const slug = machine.normalize("NFKD").replace(/[^A-Za-z0-9._-]+/g, "-").replace(/-+/g, "-").replace(/^[-.]+|[-.]+$/g, "").slice(0, MAX_MACHINE_SLUG);
|
|
232311
|
-
return slug.length > 0 ? slug : `host-${
|
|
232853
|
+
return slug.length > 0 ? slug : `host-${createHash105("sha256").update(machine.normalize("NFC")).digest("hex").slice(0, 6)}`;
|
|
232312
232854
|
}
|
|
232313
232855
|
function localMachineName(read = hostname5) {
|
|
232314
232856
|
let name = "";
|
|
@@ -233710,7 +234252,7 @@ var init_durable_log = __esm(() => {
|
|
|
233710
234252
|
});
|
|
233711
234253
|
|
|
233712
234254
|
// src/remote/inbound.ts
|
|
233713
|
-
import { createHash as
|
|
234255
|
+
import { createHash as createHash106 } from "crypto";
|
|
233714
234256
|
import { unlinkSync as unlinkSync11 } from "fs";
|
|
233715
234257
|
import path452 from "path";
|
|
233716
234258
|
function parseEntry6(value) {
|
|
@@ -233743,7 +234285,7 @@ class InboundQueues {
|
|
|
233743
234285
|
this.directory = ensureRemoteDir(this.dir, INBOUND_DIRNAME);
|
|
233744
234286
|
}
|
|
233745
234287
|
fileFor(bindingKey) {
|
|
233746
|
-
const name =
|
|
234288
|
+
const name = createHash106("sha256").update(bindingKey).digest("hex").slice(0, 32);
|
|
233747
234289
|
return path452.join(this.directory, `${name}.jsonl`);
|
|
233748
234290
|
}
|
|
233749
234291
|
log(bindingKey) {
|
|
@@ -238214,7 +238756,7 @@ var init_serve_research_sync = __esm(() => {
|
|
|
238214
238756
|
|
|
238215
238757
|
// src/intake/assess.ts
|
|
238216
238758
|
import { randomUUID as randomUUID58 } from "crypto";
|
|
238217
|
-
function
|
|
238759
|
+
function clip13(text, max) {
|
|
238218
238760
|
const flat = redactSensitiveText(text).replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, " ").trim();
|
|
238219
238761
|
return flat.length > max ? `${flat.slice(0, max)}\u2026` : flat;
|
|
238220
238762
|
}
|
|
@@ -238222,10 +238764,10 @@ function promptFor(input) {
|
|
|
238222
238764
|
const { event } = input;
|
|
238223
238765
|
return [
|
|
238224
238766
|
`Kind: ${event.kind}`,
|
|
238225
|
-
`Title: ${
|
|
238767
|
+
`Title: ${clip13(event.title, TITLE_MAX).replace(/\s+/g, " ")}`,
|
|
238226
238768
|
`Allowed actions: ${input.allowed.join(", ")}`,
|
|
238227
238769
|
"Text:",
|
|
238228
|
-
|
|
238770
|
+
clip13(event.body ?? "", BODY_MAX2)
|
|
238229
238771
|
].join(`
|
|
238230
238772
|
`);
|
|
238231
238773
|
}
|
|
@@ -241288,7 +241830,7 @@ var init_agents_external = __esm(() => {
|
|
|
241288
241830
|
});
|
|
241289
241831
|
|
|
241290
241832
|
// src/commands/agents-catalog.ts
|
|
241291
|
-
import { existsSync as existsSync94, lstatSync as lstatSync10, readFileSync as
|
|
241833
|
+
import { existsSync as existsSync94, lstatSync as lstatSync10, readFileSync as readFileSync51, writeFileSync as writeFileSync14 } from "fs";
|
|
241292
241834
|
import path464 from "path";
|
|
241293
241835
|
function resolveDeps2(deps) {
|
|
241294
241836
|
return {
|
|
@@ -241569,7 +242111,7 @@ function printVerifyHelp2() {
|
|
|
241569
242111
|
function readStackPackForGeneration(packJsonPath, stackId) {
|
|
241570
242112
|
let raw;
|
|
241571
242113
|
try {
|
|
241572
|
-
raw = JSON.parse(
|
|
242114
|
+
raw = JSON.parse(readFileSync51(packJsonPath, "utf8"));
|
|
241573
242115
|
} catch {
|
|
241574
242116
|
return;
|
|
241575
242117
|
}
|
|
@@ -241694,7 +242236,7 @@ function generateCommand(args, depsIn) {
|
|
|
241694
242236
|
}
|
|
241695
242237
|
const outcomes = [];
|
|
241696
242238
|
for (const { file, filePath, existed } of prepared) {
|
|
241697
|
-
const previous = existed ?
|
|
242239
|
+
const previous = existed ? readFileSync51(filePath, "utf8") : undefined;
|
|
241698
242240
|
const changed = previous !== file.content;
|
|
241699
242241
|
if (!check && changed) {
|
|
241700
242242
|
writeFileSync14(filePath, file.content, "utf8");
|
|
@@ -241818,7 +242360,7 @@ var init_reduce = __esm(() => {
|
|
|
241818
242360
|
});
|
|
241819
242361
|
|
|
241820
242362
|
// src/commands/agents.ts
|
|
241821
|
-
import { readFileSync as
|
|
242363
|
+
import { readFileSync as readFileSync52 } from "fs";
|
|
241822
242364
|
async function agentsCommand(args = []) {
|
|
241823
242365
|
const subcommand = args[0];
|
|
241824
242366
|
if (!subcommand || subcommand === "--help" || subcommand === "-h") {
|
|
@@ -241870,7 +242412,7 @@ function monitorCommand(args) {
|
|
|
241870
242412
|
}
|
|
241871
242413
|
let events;
|
|
241872
242414
|
try {
|
|
241873
|
-
events = parseAgentEvents(
|
|
242415
|
+
events = parseAgentEvents(readFileSync52(source, "utf8"));
|
|
241874
242416
|
} catch (error) {
|
|
241875
242417
|
console.error(`Failed to read/parse agent-event source "${source}": ${error.message}`);
|
|
241876
242418
|
process.exitCode = 1;
|
|
@@ -246190,7 +246732,7 @@ var init_product = __esm(() => {
|
|
|
246190
246732
|
});
|
|
246191
246733
|
|
|
246192
246734
|
// src/commands/hooks.ts
|
|
246193
|
-
import { existsSync as existsSync95, readFileSync as
|
|
246735
|
+
import { existsSync as existsSync95, readFileSync as readFileSync53 } from "fs";
|
|
246194
246736
|
import path469 from "path";
|
|
246195
246737
|
function resolveHomeDir(deps) {
|
|
246196
246738
|
return resolveHooksHomeDir(process.env, deps.homeDir);
|
|
@@ -246615,7 +247157,7 @@ async function runTest(args, deps) {
|
|
|
246615
247157
|
if (payloadFileArg !== undefined) {
|
|
246616
247158
|
let raw;
|
|
246617
247159
|
try {
|
|
246618
|
-
raw =
|
|
247160
|
+
raw = readFileSync53(payloadFileArg, "utf8");
|
|
246619
247161
|
} catch (err) {
|
|
246620
247162
|
fail8(`Could not read --payload-file "${payloadFileArg}": ${err instanceof Error ? err.message : String(err)}`);
|
|
246621
247163
|
}
|
|
@@ -246755,7 +247297,7 @@ function emptyDoc() {
|
|
|
246755
247297
|
function readDoc(filePath) {
|
|
246756
247298
|
let raw;
|
|
246757
247299
|
try {
|
|
246758
|
-
raw =
|
|
247300
|
+
raw = readFileSync53(filePath, "utf8");
|
|
246759
247301
|
} catch (err) {
|
|
246760
247302
|
if (err instanceof Error && "code" in err && err.code === "ENOENT") {
|
|
246761
247303
|
return emptyDoc();
|