@mrciphersmith/keryx 0.2.54 → 0.2.56
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 +453 -80
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -19314,6 +19314,8 @@ async function runAgentTurnCore(io, deps, history, userLine, options = {}) {
|
|
|
19314
19314
|
const spawnConcurrencyCandidates = calls.filter((call) => call.name === "spawn_subagent" && reservationByCallId.get(call.id)?.ok === true);
|
|
19315
19315
|
const untrustedGateBlocksSpawns = untrustedContentSeen || batchContainsUntrustedWeb;
|
|
19316
19316
|
const concurrentSpawnResults = spawnConcurrencyCandidates.length >= 2 && !untrustedGateBlocksSpawns ? await runConcurrentSpawnBatch(spawnConcurrencyCandidates, toolByName, io, deps) : undefined;
|
|
19317
|
+
let anchorsToAnnounce;
|
|
19318
|
+
let repeatedFailureHint;
|
|
19317
19319
|
for (const call of calls) {
|
|
19318
19320
|
if (isAborted()) {
|
|
19319
19321
|
system(`
|
|
@@ -19375,8 +19377,7 @@ ${modelOutput}` : modelOutput,
|
|
|
19375
19377
|
runtime: { provider: deps.providerId, model: deps.modelId }
|
|
19376
19378
|
});
|
|
19377
19379
|
if (touch.changed) {
|
|
19378
|
-
|
|
19379
|
-
io.onHistoryChange?.("tool");
|
|
19380
|
+
anchorsToAnnounce = touch.slate.anchors;
|
|
19380
19381
|
}
|
|
19381
19382
|
} catch (err) {
|
|
19382
19383
|
io.onSystem?.(`slate touch update failed (ignored): ${err instanceof Error ? err.message : String(err)}
|
|
@@ -19397,14 +19398,21 @@ ${modelOutput}` : modelOutput,
|
|
|
19397
19398
|
system(`
|
|
19398
19399
|
${hint}
|
|
19399
19400
|
`);
|
|
19400
|
-
|
|
19401
|
-
io.onHistoryChange?.("tool");
|
|
19401
|
+
repeatedFailureHint = hint;
|
|
19402
19402
|
}
|
|
19403
19403
|
} else {
|
|
19404
19404
|
lastErrorByHash.delete(reservation.hash);
|
|
19405
19405
|
errorStreakByHash.delete(reservation.hash);
|
|
19406
19406
|
}
|
|
19407
19407
|
}
|
|
19408
|
+
if (anchorsToAnnounce !== undefined) {
|
|
19409
|
+
history.push({ role: "user", content: renderAnchorsBlock(anchorsToAnnounce), provenance: "project" });
|
|
19410
|
+
io.onHistoryChange?.("tool");
|
|
19411
|
+
}
|
|
19412
|
+
if (repeatedFailureHint !== undefined) {
|
|
19413
|
+
history.push({ role: "user", content: repeatedFailureHint, provenance: "project" });
|
|
19414
|
+
io.onHistoryChange?.("tool");
|
|
19415
|
+
}
|
|
19408
19416
|
const noProgress = !executedAny && calls.length > 0;
|
|
19409
19417
|
if (exhaustedBudget !== undefined || noProgress) {
|
|
19410
19418
|
const finishReason = exhaustedBudget !== undefined ? "budget" : "no-progress";
|
|
@@ -26103,6 +26111,10 @@ ${markdown.slice(4)}`;
|
|
|
26103
26111
|
}
|
|
26104
26112
|
return markdown;
|
|
26105
26113
|
}
|
|
26114
|
+
function extractFrontmatterStatus(markdown) {
|
|
26115
|
+
const bodyMatch = /\nStatus:\s*(\S+)/i.exec(markdown) ?? /^Status:\s*(\S+)/im.exec(markdown);
|
|
26116
|
+
return bodyMatch?.[1] ?? "draft";
|
|
26117
|
+
}
|
|
26106
26118
|
async function mapPool(items, concurrency, worker) {
|
|
26107
26119
|
const results = new Array(items.length);
|
|
26108
26120
|
let next = 0;
|
|
@@ -26129,7 +26141,6 @@ async function wikiEnrich(input2) {
|
|
|
26129
26141
|
const concurrency = Math.max(1, Math.min(MAX_CONCURRENCY, input2.concurrency ?? DEFAULT_CONCURRENCY));
|
|
26130
26142
|
const maxOutputTokens = input2.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS;
|
|
26131
26143
|
const validate = input2.validate !== false;
|
|
26132
|
-
const markAccepted = input2.keepStatus === true ? false : input2.markAccepted !== false;
|
|
26133
26144
|
const wikiConfig = await loadWikiConfig(input2.cwd);
|
|
26134
26145
|
const result = {
|
|
26135
26146
|
provider,
|
|
@@ -26223,9 +26234,7 @@ async function wikiEnrich(input2) {
|
|
|
26223
26234
|
return { path: page.relativePath, action: "failed", reason: `validation: ${structural}` };
|
|
26224
26235
|
}
|
|
26225
26236
|
}
|
|
26226
|
-
|
|
26227
|
-
enriched = setFrontmatterStatus(enriched, "accepted");
|
|
26228
|
-
}
|
|
26237
|
+
enriched = setFrontmatterStatus(enriched, extractFrontmatterStatus(original));
|
|
26229
26238
|
if (input2.dryRun) {
|
|
26230
26239
|
onPage({ index, total, path: page.relativePath, status, phase: "done" });
|
|
26231
26240
|
return {
|
|
@@ -26239,7 +26248,7 @@ async function wikiEnrich(input2) {
|
|
|
26239
26248
|
await writeFile36(page.absolutePath, `${enriched.endsWith(`
|
|
26240
26249
|
`) ? enriched : `${enriched}
|
|
26241
26250
|
`}`, "utf8");
|
|
26242
|
-
onPage({ index, total, path: page.relativePath, status
|
|
26251
|
+
onPage({ index, total, path: page.relativePath, status, phase: "done" });
|
|
26243
26252
|
return {
|
|
26244
26253
|
path: page.relativePath,
|
|
26245
26254
|
action: "enriched",
|
|
@@ -26265,7 +26274,6 @@ async function wikiEnrich(input2) {
|
|
|
26265
26274
|
model,
|
|
26266
26275
|
maxOutputTokens,
|
|
26267
26276
|
validate,
|
|
26268
|
-
markAccepted,
|
|
26269
26277
|
concurrency,
|
|
26270
26278
|
total,
|
|
26271
26279
|
onPage,
|
|
@@ -26348,8 +26356,8 @@ function finalizeEnrichedText(original, rawText, options) {
|
|
|
26348
26356
|
if (options.validate) {
|
|
26349
26357
|
structuralError = validateEnrichedMarkdown(original, content);
|
|
26350
26358
|
}
|
|
26351
|
-
if (structuralError === null
|
|
26352
|
-
content = setFrontmatterStatus(content,
|
|
26359
|
+
if (structuralError === null) {
|
|
26360
|
+
content = setFrontmatterStatus(content, extractFrontmatterStatus(original));
|
|
26353
26361
|
}
|
|
26354
26362
|
return { content, structuralError };
|
|
26355
26363
|
}
|
|
@@ -26376,7 +26384,7 @@ async function finishSuccess(ctx, page, originalRaw, content, keyFiles, extra) {
|
|
|
26376
26384
|
index,
|
|
26377
26385
|
total: ctx.total,
|
|
26378
26386
|
path: page.relativePath,
|
|
26379
|
-
status
|
|
26387
|
+
status,
|
|
26380
26388
|
phase: "done"
|
|
26381
26389
|
});
|
|
26382
26390
|
return {
|
|
@@ -26420,8 +26428,7 @@ async function runDeepSingle(ctx, item) {
|
|
|
26420
26428
|
let content = null;
|
|
26421
26429
|
if (rawText !== null) {
|
|
26422
26430
|
const finalized = finalizeEnrichedText(original, rawText, {
|
|
26423
|
-
validate: ctx.validate
|
|
26424
|
-
markAccepted: ctx.markAccepted
|
|
26431
|
+
validate: ctx.validate
|
|
26425
26432
|
});
|
|
26426
26433
|
if (finalized.structuralError === null) {
|
|
26427
26434
|
content = finalized.content;
|
|
@@ -26606,8 +26613,7 @@ async function runLightBatch(ctx, items) {
|
|
|
26606
26613
|
continue;
|
|
26607
26614
|
}
|
|
26608
26615
|
const finalized = finalizeEnrichedText(item.original, rawText, {
|
|
26609
|
-
validate: ctx.validate
|
|
26610
|
-
markAccepted: ctx.markAccepted
|
|
26616
|
+
validate: ctx.validate
|
|
26611
26617
|
});
|
|
26612
26618
|
if (finalized.structuralError !== null) {
|
|
26613
26619
|
ctx.onPage({ index, total: ctx.total, path: item.page.relativePath, status, phase: "failed" });
|
|
@@ -26658,7 +26664,6 @@ async function runRlmPipeline(ctxInput) {
|
|
|
26658
26664
|
model: ctxInput.model,
|
|
26659
26665
|
maxOutputTokens: ctxInput.maxOutputTokens,
|
|
26660
26666
|
validate: ctxInput.validate,
|
|
26661
|
-
markAccepted: ctxInput.markAccepted,
|
|
26662
26667
|
total: ctxInput.total,
|
|
26663
26668
|
onPage: ctxInput.onPage,
|
|
26664
26669
|
input: input2,
|
|
@@ -36667,7 +36672,6 @@ async function runEnrich(args2) {
|
|
|
36667
36672
|
const resume = args2.includes("--resume");
|
|
36668
36673
|
const refreshGraph = args2.includes("--refresh-graph");
|
|
36669
36674
|
const dryRun = args2.includes("--dry-run");
|
|
36670
|
-
const keepStatus = args2.includes("--keep-status");
|
|
36671
36675
|
const noValidate = args2.includes("--no-validate");
|
|
36672
36676
|
const valueFlags = new Set([
|
|
36673
36677
|
"--page",
|
|
@@ -36736,9 +36740,7 @@ async function runEnrich(args2) {
|
|
|
36736
36740
|
resume,
|
|
36737
36741
|
refreshGraph,
|
|
36738
36742
|
dryRun,
|
|
36739
|
-
keepStatus,
|
|
36740
36743
|
validate: !noValidate,
|
|
36741
|
-
markAccepted: !keepStatus,
|
|
36742
36744
|
...prompt ? { prompt } : {},
|
|
36743
36745
|
...provider ? { provider } : {},
|
|
36744
36746
|
...model ? { model } : {},
|
|
@@ -36815,10 +36817,12 @@ Usage:
|
|
|
36815
36817
|
keryx wiki validate
|
|
36816
36818
|
keryx wiki ask "<question>" [--k <n>] [--rerank]
|
|
36817
36819
|
keryx wiki enrich [<page>|--all] [--force] [--list] [--resume] [--limit N] [--concurrency N]
|
|
36818
|
-
[--refresh-graph] [--max-tokens N] [--
|
|
36820
|
+
[--refresh-graph] [--max-tokens N] [--no-validate]
|
|
36819
36821
|
[--prompt "<i>"] [--provider <p>] [--model <m>] [--dry-run] [--json]
|
|
36820
36822
|
# defaults: drafts only; provider/model from auth.json; validate on;
|
|
36821
|
-
#
|
|
36823
|
+
# concurrency 1 (raise for parallel page swarm)
|
|
36824
|
+
# rewrites prose only \u2014 Status is always left exactly as it was
|
|
36825
|
+
# before the run; enrich can never itself accept a page (issue #391)
|
|
36822
36826
|
keryx wiki context
|
|
36823
36827
|
keryx wiki backlinks <wiki-page-or-code-file>
|
|
36824
36828
|
|
|
@@ -49933,7 +49937,8 @@ var PREFIX_BANNED = new Set([
|
|
|
49933
49937
|
"osascript",
|
|
49934
49938
|
"open",
|
|
49935
49939
|
"tee",
|
|
49936
|
-
"cd"
|
|
49940
|
+
"cd",
|
|
49941
|
+
"keryx"
|
|
49937
49942
|
]);
|
|
49938
49943
|
var PREFIX_BANNED_READERS = new Set([
|
|
49939
49944
|
"cat",
|
|
@@ -50004,6 +50009,17 @@ function validateShellPattern(pattern) {
|
|
|
50004
50009
|
}
|
|
50005
50010
|
return { ok: true };
|
|
50006
50011
|
}
|
|
50012
|
+
function getRunningBinaryName() {
|
|
50013
|
+
try {
|
|
50014
|
+
const argv0 = process.argv0;
|
|
50015
|
+
if (!argv0)
|
|
50016
|
+
return "";
|
|
50017
|
+
const name = argv0.split(/[\\/]/).pop() ?? "";
|
|
50018
|
+
return name.toLowerCase();
|
|
50019
|
+
} catch {
|
|
50020
|
+
return "";
|
|
50021
|
+
}
|
|
50022
|
+
}
|
|
50007
50023
|
function bannedPrefixGrant(pattern, firstToken) {
|
|
50008
50024
|
const rest = pattern.slice(firstToken.length).trim();
|
|
50009
50025
|
const wildcardOnly = /^\*+$/.test(rest) || rest.length === 0 && /\*+$/.test(firstToken);
|
|
@@ -50016,6 +50032,13 @@ function bannedPrefixGrant(pattern, firstToken) {
|
|
|
50016
50032
|
reason: `\`${word} *\` grants arbitrary execution: ${word} is an interpreter or wrapper, so its first token does not constrain what runs`
|
|
50017
50033
|
};
|
|
50018
50034
|
}
|
|
50035
|
+
const runningBinary = getRunningBinaryName();
|
|
50036
|
+
if (runningBinary && word === runningBinary) {
|
|
50037
|
+
return {
|
|
50038
|
+
word,
|
|
50039
|
+
reason: `\`${word} *\` grants arbitrary execution: ${word} can run arbitrary subcommands, so remembering this grant would silently approve mutating/destructive operations forever`
|
|
50040
|
+
};
|
|
50041
|
+
}
|
|
50019
50042
|
if (PREFIX_BANNED_READERS.has(word)) {
|
|
50020
50043
|
return {
|
|
50021
50044
|
word,
|
|
@@ -53100,8 +53123,10 @@ init_slate();
|
|
|
53100
53123
|
init_workspace_service();
|
|
53101
53124
|
init_workspace_resolve();
|
|
53102
53125
|
init_service7();
|
|
53126
|
+
init_store2();
|
|
53103
53127
|
init_fs();
|
|
53104
53128
|
import path149 from "path";
|
|
53129
|
+
import { readFile as readFile79 } from "fs/promises";
|
|
53105
53130
|
var POSITIVE_INTEGER = /^[1-9][0-9]*$/;
|
|
53106
53131
|
var DEFAULT_AUTO_GOAL_ROUNDS = 8;
|
|
53107
53132
|
function parseGoalArgs(rest) {
|
|
@@ -53203,9 +53228,20 @@ async function autoProvisionFlow(cwd, goalText) {
|
|
|
53203
53228
|
await service5.start({ cwd, id: result.flow.id });
|
|
53204
53229
|
return result.flow.id;
|
|
53205
53230
|
}
|
|
53231
|
+
var ROUND_DONE_MARKER = "GOAL_ROUND_COMPLETE";
|
|
53232
|
+
function continuationRoundClaimsDone(history) {
|
|
53233
|
+
for (let i = history.length - 1;i >= 0; i--) {
|
|
53234
|
+
const message2 = history[i];
|
|
53235
|
+
if (message2?.role === "assistant") {
|
|
53236
|
+
return message2.content.includes(ROUND_DONE_MARKER);
|
|
53237
|
+
}
|
|
53238
|
+
}
|
|
53239
|
+
return false;
|
|
53240
|
+
}
|
|
53206
53241
|
async function buildContinuationMessage(cwd, slateSession, round4, roundsCap) {
|
|
53207
53242
|
const totalRounds = roundsCap + 1;
|
|
53208
|
-
const
|
|
53243
|
+
const doneInstruction = `If \u2014 and only if \u2014 the stated goal is now FULLY achieved and there is nothing further to do this round, ` + `end your reply with the exact line ${ROUND_DONE_MARKER} on its own, and nothing else on that line. ` + `Otherwise do not include that line at all.`;
|
|
53244
|
+
const generic = `Continue working toward the stated goal (round ${round4} of ${totalRounds}). ${doneInstruction}`;
|
|
53209
53245
|
const slate = await readSlate(slateSession.dir).catch(() => {
|
|
53210
53246
|
return;
|
|
53211
53247
|
});
|
|
@@ -53243,11 +53279,61 @@ function parseVerifierVerdict(output2) {
|
|
|
53243
53279
|
return;
|
|
53244
53280
|
}
|
|
53245
53281
|
}
|
|
53246
|
-
|
|
53282
|
+
var MAX_EVIDENCE_SEEDS = 10;
|
|
53283
|
+
function summarizeRecentSeeds(seeds) {
|
|
53284
|
+
return seeds.slice(-MAX_EVIDENCE_SEEDS).map((seed) => `- Seed${seed.kind !== undefined ? ` [${seed.kind}]` : ""}: ${seed.text}`);
|
|
53285
|
+
}
|
|
53286
|
+
function summarizeWorkspaceProposals(history) {
|
|
53287
|
+
const lines = [];
|
|
53288
|
+
for (const message2 of history) {
|
|
53289
|
+
if (message2.role !== "assistant" || message2.toolCalls === undefined) {
|
|
53290
|
+
continue;
|
|
53291
|
+
}
|
|
53292
|
+
for (const call of message2.toolCalls) {
|
|
53293
|
+
if (call.name !== "workspace_propose") {
|
|
53294
|
+
continue;
|
|
53295
|
+
}
|
|
53296
|
+
const resultMessage = history.find((candidate) => candidate.role === "tool" && candidate.toolCallId === call.id);
|
|
53297
|
+
let argSummary = call.arguments;
|
|
53298
|
+
try {
|
|
53299
|
+
const parsedArgs = JSON.parse(call.arguments);
|
|
53300
|
+
const kind2 = typeof parsedArgs.kind === "string" ? parsedArgs.kind : "?";
|
|
53301
|
+
const note2 = typeof parsedArgs.note === "string" ? parsedArgs.note : undefined;
|
|
53302
|
+
argSummary = `kind=${kind2}${note2 !== undefined ? `, note=${note2}` : ""}`;
|
|
53303
|
+
} catch {}
|
|
53304
|
+
const outcome = resultMessage !== undefined ? resultMessage.content : "(no result recorded this run)";
|
|
53305
|
+
lines.push(`- workspace_propose: ${argSummary} -> ${outcome}`);
|
|
53306
|
+
}
|
|
53307
|
+
}
|
|
53308
|
+
return lines;
|
|
53309
|
+
}
|
|
53310
|
+
async function flowDefersCompletionToVerifier(cwd, flowId) {
|
|
53311
|
+
try {
|
|
53312
|
+
const dir = await resolveFlowDir(cwd, flowId);
|
|
53313
|
+
const text = await readFile79(acPath(cwd, dir), "utf8");
|
|
53314
|
+
const normalized = text.replace(/\s+/g, " ");
|
|
53315
|
+
return normalized.includes("judged by the verifier subagent");
|
|
53316
|
+
} catch {
|
|
53317
|
+
return false;
|
|
53318
|
+
}
|
|
53319
|
+
}
|
|
53320
|
+
async function buildVerifierEvidence(cwd, slateSession, history) {
|
|
53321
|
+
const slate = await readSlate(slateSession.dir).catch(() => {
|
|
53322
|
+
return;
|
|
53323
|
+
});
|
|
53324
|
+
const lines = [...slate !== undefined ? summarizeRecentSeeds(slate.seeds) : [], ...summarizeWorkspaceProposals(history)];
|
|
53325
|
+
const evidenceText = lines.length > 0 ? lines.join(`
|
|
53326
|
+
`) : "(no Seeds or workspace_propose records were recorded this run)";
|
|
53327
|
+
const flowId = slate?.course.flowRef;
|
|
53328
|
+
const deferToVerifier = flowId !== undefined ? await flowDefersCompletionToVerifier(cwd, flowId) : false;
|
|
53329
|
+
return { evidenceText, deferToVerifier };
|
|
53330
|
+
}
|
|
53331
|
+
async function runGoalVerifier(deps, goalText, cwd, slateSession, history, io, mintCallId) {
|
|
53247
53332
|
const tool = deps.tools.find((candidate) => candidate.definition.name === "spawn_subagent");
|
|
53248
53333
|
if (tool === undefined) {
|
|
53249
53334
|
return;
|
|
53250
53335
|
}
|
|
53336
|
+
const { evidenceText, deferToVerifier } = await buildVerifierEvidence(cwd, slateSession, history);
|
|
53251
53337
|
const task = [
|
|
53252
53338
|
"Independently verify whether the following goal has ACTUALLY been achieved, based on the",
|
|
53253
53339
|
"current, real state of the repository (read the real files/tests \u2014 never trust a prior",
|
|
@@ -53255,17 +53341,49 @@ async function runGoalVerifier(deps, goalText) {
|
|
|
53255
53341
|
"",
|
|
53256
53342
|
`Goal: "${goalText}"`,
|
|
53257
53343
|
"",
|
|
53344
|
+
"Evidence this run already produced (recent Slate Seeds and workspace_propose records) \u2014",
|
|
53345
|
+
"weigh this as real evidence of what was actually done, not merely a claim:",
|
|
53346
|
+
evidenceText,
|
|
53347
|
+
"",
|
|
53348
|
+
...deferToVerifier ? [
|
|
53349
|
+
"This run's Task Manager flow acceptance criteria explicitly defer the completion",
|
|
53350
|
+
"judgment to THIS verifier check, not to the flow's own task checkboxes \u2014 its tasks",
|
|
53351
|
+
"are expected to remain unchecked even when the goal is genuinely achieved. Do NOT",
|
|
53352
|
+
"treat an incomplete/unchecked flow task list, by itself, as evidence the goal is NOT",
|
|
53353
|
+
"achieved; judge achievement from the real repository state and the evidence above.",
|
|
53354
|
+
""
|
|
53355
|
+
] : [],
|
|
53258
53356
|
"Reply with EXACTLY one JSON object and nothing else, no prose before or after it:",
|
|
53259
53357
|
'{"achieved": true or false, "gaps": ["specific reason it is not fully achieved", ...]}',
|
|
53260
53358
|
'"gaps" must be empty when "achieved" is true.'
|
|
53261
53359
|
].join(`
|
|
53262
53360
|
`);
|
|
53361
|
+
const input2 = { task, mode: "read_only", label: "goal-verifier" };
|
|
53362
|
+
const callId = mintCallId();
|
|
53363
|
+
io.onToolCall?.("spawn_subagent", JSON.stringify(input2));
|
|
53364
|
+
history.push({
|
|
53365
|
+
role: "assistant",
|
|
53366
|
+
content: "",
|
|
53367
|
+
provenance: "model",
|
|
53368
|
+
toolCalls: [{ id: callId, name: "spawn_subagent", arguments: JSON.stringify(input2) }]
|
|
53369
|
+
});
|
|
53370
|
+
io.onHistoryChange?.("tool");
|
|
53263
53371
|
let result;
|
|
53264
53372
|
try {
|
|
53265
|
-
result = await tool.invoke(
|
|
53266
|
-
} catch {
|
|
53373
|
+
result = await tool.invoke(input2);
|
|
53374
|
+
} catch (err) {
|
|
53375
|
+
const errorResult = {
|
|
53376
|
+
output: `spawn_subagent dispatch failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
53377
|
+
isError: true
|
|
53378
|
+
};
|
|
53379
|
+
io.onToolResult?.("spawn_subagent", errorResult);
|
|
53380
|
+
history.push({ role: "tool", content: errorResult.output, provenance: "tool", toolCallId: callId });
|
|
53381
|
+
io.onHistoryChange?.("tool");
|
|
53267
53382
|
return;
|
|
53268
53383
|
}
|
|
53384
|
+
io.onToolResult?.("spawn_subagent", result);
|
|
53385
|
+
history.push({ role: "tool", content: result.output, provenance: "tool", toolCallId: callId });
|
|
53386
|
+
io.onHistoryChange?.("tool");
|
|
53269
53387
|
if (result.isError) {
|
|
53270
53388
|
return;
|
|
53271
53389
|
}
|
|
@@ -53355,10 +53473,21 @@ async function runGoalCommand(params) {
|
|
|
53355
53473
|
systemLine(io, `/goal --auto: round ${round4}/${roundsCap + 1} \u2014 continuing toward the goal.
|
|
53356
53474
|
`);
|
|
53357
53475
|
await runAgentTurn(io, deps, history, continuationText, turnOptions);
|
|
53476
|
+
if (continuationRoundClaimsDone(history)) {
|
|
53477
|
+
systemLine(io, `/goal --auto: model signaled this round's work is complete (round ${round4}/${roundsCap + 1}) \u2014 ` + `ending the round budget early; the verifier will confirm.
|
|
53478
|
+
`);
|
|
53479
|
+
break;
|
|
53480
|
+
}
|
|
53358
53481
|
}
|
|
53359
53482
|
const wasOpenBeforeVerifier = slateSession.opened;
|
|
53360
|
-
const verdict = await runGoalVerifier(deps, parsed.text);
|
|
53361
|
-
if (verdict
|
|
53483
|
+
const verdict = await runGoalVerifier(deps, parsed.text, cwd, slateSession, history, io, mintAttemptId);
|
|
53484
|
+
if (verdict === undefined) {
|
|
53485
|
+
systemLine(io, `/goal --auto: verifier unavailable \u2014 outcome not independently checked.
|
|
53486
|
+
`);
|
|
53487
|
+
} else if (verdict.achieved) {
|
|
53488
|
+
systemLine(io, `/goal --auto: verifier confirmed the goal is achieved.
|
|
53489
|
+
`);
|
|
53490
|
+
} else {
|
|
53362
53491
|
systemLine(io, `/goal --auto: verifier found the goal not fully achieved${verdict.gaps.length > 0 ? ` \u2014 ${verdict.gaps.join("; ")}` : " (no specific gaps reported)"}
|
|
53363
53492
|
`);
|
|
53364
53493
|
if (roundsLeft > 0) {
|
|
@@ -53406,7 +53535,7 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
53406
53535
|
// package.json
|
|
53407
53536
|
var package_default = {
|
|
53408
53537
|
name: "@mrciphersmith/keryx",
|
|
53409
|
-
version: "0.2.
|
|
53538
|
+
version: "0.2.56",
|
|
53410
53539
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
53411
53540
|
private: false,
|
|
53412
53541
|
publishConfig: {
|
|
@@ -54666,7 +54795,7 @@ init_store3();
|
|
|
54666
54795
|
init_proposal_lifecycle();
|
|
54667
54796
|
init_workspace_service();
|
|
54668
54797
|
import { randomUUID as randomUUID24 } from "crypto";
|
|
54669
|
-
import { readdir as readdir26 } from "fs/promises";
|
|
54798
|
+
import { readdir as readdir26, stat as stat8 } from "fs/promises";
|
|
54670
54799
|
import path150 from "path";
|
|
54671
54800
|
|
|
54672
54801
|
// src/sac/lifecycle-flag.ts
|
|
@@ -54717,14 +54846,18 @@ async function computeLifecycleFlags(cwd, now = () => new Date) {
|
|
|
54717
54846
|
|
|
54718
54847
|
// src/sac/catch-up.ts
|
|
54719
54848
|
init_proposal_evidence();
|
|
54849
|
+
init_collect();
|
|
54850
|
+
init_store();
|
|
54720
54851
|
async function buildCatchUp(input2) {
|
|
54721
|
-
const [proposals, sessionCategories, lifecycleFlagsAll] = await Promise.all([
|
|
54852
|
+
const [proposals, sessionCategories, lifecycleFlagsAll, unreviewedPathsAll] = await Promise.all([
|
|
54722
54853
|
collectProposals(input2.cwd, input2.workspaceId),
|
|
54723
54854
|
collectSessionCategories(input2.cwd),
|
|
54724
|
-
computeLifecycleFlags(input2.cwd)
|
|
54855
|
+
computeLifecycleFlags(input2.cwd),
|
|
54856
|
+
detectUnreviewedSacPathChanges(input2.cwd, listSessions(input2.cwd))
|
|
54725
54857
|
]);
|
|
54726
54858
|
const lifecycleFlags = input2.workspaceId === undefined ? lifecycleFlagsAll : lifecycleFlagsAll.filter((flag) => flag.kind !== "workspace" || flag.ref === input2.workspaceId);
|
|
54727
|
-
|
|
54859
|
+
const unreviewedPaths = input2.workspaceId === undefined ? unreviewedPathsAll : unreviewedPathsAll.filter((item) => item.workspaceId === input2.workspaceId);
|
|
54860
|
+
return { proposals, ...sessionCategories, lifecycleFlags, unreviewedPaths };
|
|
54728
54861
|
}
|
|
54729
54862
|
async function collectProposals(cwd, workspaceId) {
|
|
54730
54863
|
const authorizationServer = localWorkspaceAuthorizationServer();
|
|
@@ -54787,8 +54920,82 @@ async function classifySession(session) {
|
|
|
54787
54920
|
const workspaceId = (await safeReadSlate(dir))?.workspaceId;
|
|
54788
54921
|
return { kind: "unknown", item: { type: "unknown", sessionId: session.id, ...workspaceId !== undefined ? { workspaceId } : {}, lastSeenAt: session.updatedAt } };
|
|
54789
54922
|
}
|
|
54923
|
+
async function readExternalUnboundCandidates(cwd) {
|
|
54924
|
+
const extDir = externalSlatesDir(cwd);
|
|
54925
|
+
let externalIds;
|
|
54926
|
+
try {
|
|
54927
|
+
const entries = await readdir26(extDir);
|
|
54928
|
+
externalIds = entries.filter((name) => name.endsWith(".json")).map((name) => name.slice(0, -".json".length));
|
|
54929
|
+
} catch {
|
|
54930
|
+
return [];
|
|
54931
|
+
}
|
|
54932
|
+
const candidates = [];
|
|
54933
|
+
for (const id of externalIds) {
|
|
54934
|
+
const slate = await readExternalSlate(cwd, id);
|
|
54935
|
+
if (!slate)
|
|
54936
|
+
continue;
|
|
54937
|
+
if (slate.closedAt === undefined || slate.workspaceId !== undefined)
|
|
54938
|
+
continue;
|
|
54939
|
+
const unbound2 = await readNewestUnboundCandidateForExternal(cwd, id);
|
|
54940
|
+
if (unbound2) {
|
|
54941
|
+
candidates.push({
|
|
54942
|
+
type: "unbound-candidate",
|
|
54943
|
+
externalSessionId: id,
|
|
54944
|
+
evidencePath: unbound2.evidencePath,
|
|
54945
|
+
summary: unbound2.summary
|
|
54946
|
+
});
|
|
54947
|
+
} else {
|
|
54948
|
+
const summary = summarizeUnboundCandidate((slate.seeds ?? []).reduce((groups, seed) => {
|
|
54949
|
+
const kind2 = seed.kind ?? "follow-up";
|
|
54950
|
+
const existing = groups.find((g) => g.kind === kind2);
|
|
54951
|
+
if (existing) {
|
|
54952
|
+
existing.seeds = (existing.seeds ?? []).concat([{ text: seed.text }]);
|
|
54953
|
+
} else {
|
|
54954
|
+
groups.push({ kind: kind2, seeds: [{ text: seed.text }] });
|
|
54955
|
+
}
|
|
54956
|
+
return groups;
|
|
54957
|
+
}, []));
|
|
54958
|
+
candidates.push({
|
|
54959
|
+
type: "unbound-candidate",
|
|
54960
|
+
externalSessionId: id,
|
|
54961
|
+
evidencePath: path150.join(extDir, `${id}.json`),
|
|
54962
|
+
summary
|
|
54963
|
+
});
|
|
54964
|
+
}
|
|
54965
|
+
}
|
|
54966
|
+
return candidates;
|
|
54967
|
+
}
|
|
54968
|
+
async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
|
|
54969
|
+
const evidenceDir = path150.join(externalSlatesDir(cwd), externalSessionId);
|
|
54970
|
+
let entries;
|
|
54971
|
+
try {
|
|
54972
|
+
entries = (await readdir26(evidenceDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
54973
|
+
} catch {
|
|
54974
|
+
return;
|
|
54975
|
+
}
|
|
54976
|
+
entries.sort();
|
|
54977
|
+
for (let i = entries.length - 1;i >= 0; i--) {
|
|
54978
|
+
const evidencePath = path150.join(evidenceDir, entries[i]);
|
|
54979
|
+
const result = readConfigFile(evidencePath);
|
|
54980
|
+
if (!result.ok) {
|
|
54981
|
+
continue;
|
|
54982
|
+
}
|
|
54983
|
+
try {
|
|
54984
|
+
const parsed = JSON.parse(result.text);
|
|
54985
|
+
if (parsed.recordType !== "unbound-candidate")
|
|
54986
|
+
continue;
|
|
54987
|
+
return { evidencePath, summary: summarizeUnboundCandidate(parsed.groups) };
|
|
54988
|
+
} catch {
|
|
54989
|
+
continue;
|
|
54990
|
+
}
|
|
54991
|
+
}
|
|
54992
|
+
return;
|
|
54993
|
+
}
|
|
54790
54994
|
async function collectSessionCategories(cwd) {
|
|
54791
|
-
const classified = await Promise.all(
|
|
54995
|
+
const [classified, externalUnboundCandidates] = await Promise.all([
|
|
54996
|
+
Promise.all(listSessions(cwd).map((session) => classifySession(session))),
|
|
54997
|
+
readExternalUnboundCandidates(cwd)
|
|
54998
|
+
]);
|
|
54792
54999
|
const blocked2 = [];
|
|
54793
55000
|
const unboundCandidates = [];
|
|
54794
55001
|
const unknown = [];
|
|
@@ -54802,8 +55009,151 @@ async function collectSessionCategories(cwd) {
|
|
|
54802
55009
|
else
|
|
54803
55010
|
unknown.push(category.item);
|
|
54804
55011
|
}
|
|
55012
|
+
unboundCandidates.push(...externalUnboundCandidates);
|
|
54805
55013
|
return { blocked: blocked2, unboundCandidates, unknown };
|
|
54806
55014
|
}
|
|
55015
|
+
var SESSION_ATTRIBUTION_SLACK_MS = 5 * 60000;
|
|
55016
|
+
var MTIME_CLOCK_SKEW_TOLERANCE_MS = 5000;
|
|
55017
|
+
async function collectReceiptTargets(cwd, owner) {
|
|
55018
|
+
const targets = new Set;
|
|
55019
|
+
const workspacesDir = path150.join(cwd, ".metaproject", "workspaces");
|
|
55020
|
+
let workspaceIds;
|
|
55021
|
+
try {
|
|
55022
|
+
workspaceIds = (await readdir26(workspacesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
|
|
55023
|
+
} catch {
|
|
55024
|
+
return targets;
|
|
55025
|
+
}
|
|
55026
|
+
for (const workspaceId of workspaceIds) {
|
|
55027
|
+
const receiptsDir = path150.join(workspacesDir, workspaceId, `${owner}-write-receipts`);
|
|
55028
|
+
let files;
|
|
55029
|
+
try {
|
|
55030
|
+
files = (await readdir26(receiptsDir)).filter((name) => name.endsWith(".json"));
|
|
55031
|
+
} catch {
|
|
55032
|
+
continue;
|
|
55033
|
+
}
|
|
55034
|
+
for (const file of files) {
|
|
55035
|
+
const result = readConfigFile(path150.join(receiptsDir, file));
|
|
55036
|
+
if (!result.ok)
|
|
55037
|
+
continue;
|
|
55038
|
+
try {
|
|
55039
|
+
const receipt = JSON.parse(result.text);
|
|
55040
|
+
if (typeof receipt.targetRef === "string") {
|
|
55041
|
+
targets.add(receipt.targetRef.replace(/^\.\//, ""));
|
|
55042
|
+
}
|
|
55043
|
+
} catch {
|
|
55044
|
+
continue;
|
|
55045
|
+
}
|
|
55046
|
+
}
|
|
55047
|
+
}
|
|
55048
|
+
return targets;
|
|
55049
|
+
}
|
|
55050
|
+
async function attributeToSession(absolutePath, sessionsNewestFirst) {
|
|
55051
|
+
let mtimeMs;
|
|
55052
|
+
try {
|
|
55053
|
+
mtimeMs = (await stat8(absolutePath)).mtimeMs;
|
|
55054
|
+
} catch {
|
|
55055
|
+
return;
|
|
55056
|
+
}
|
|
55057
|
+
for (const session of sessionsNewestFirst) {
|
|
55058
|
+
const start = Date.parse(session.createdAt);
|
|
55059
|
+
const end = Date.parse(session.updatedAt);
|
|
55060
|
+
if (Number.isNaN(start) || Number.isNaN(end))
|
|
55061
|
+
continue;
|
|
55062
|
+
if (mtimeMs >= start - MTIME_CLOCK_SKEW_TOLERANCE_MS && mtimeMs <= end + SESSION_ATTRIBUTION_SLACK_MS) {
|
|
55063
|
+
const workspaceId = (await safeReadSlate(sessionDir(session.projectPath, session.id)))?.workspaceId;
|
|
55064
|
+
return { sessionId: session.id, workspaceId, changedAt: new Date(mtimeMs).toISOString() };
|
|
55065
|
+
}
|
|
55066
|
+
}
|
|
55067
|
+
return;
|
|
55068
|
+
}
|
|
55069
|
+
async function findSkillFiles(dir) {
|
|
55070
|
+
let entries;
|
|
55071
|
+
try {
|
|
55072
|
+
entries = await readdir26(dir, { withFileTypes: true });
|
|
55073
|
+
} catch {
|
|
55074
|
+
return [];
|
|
55075
|
+
}
|
|
55076
|
+
const found = [];
|
|
55077
|
+
for (const entry of entries) {
|
|
55078
|
+
const full = path150.join(dir, entry.name);
|
|
55079
|
+
if (entry.isDirectory()) {
|
|
55080
|
+
found.push(...await findSkillFiles(full));
|
|
55081
|
+
} else if (entry.isFile() && entry.name === "SKILL.md") {
|
|
55082
|
+
found.push(full);
|
|
55083
|
+
}
|
|
55084
|
+
}
|
|
55085
|
+
return found;
|
|
55086
|
+
}
|
|
55087
|
+
async function detectUnreviewedSacPathChanges(cwd, sessions) {
|
|
55088
|
+
const scoped = [...sessions].filter((session) => session.projectPath === cwd).sort((a, b) => Date.parse(b.updatedAt) - Date.parse(a.updatedAt));
|
|
55089
|
+
if (scoped.length === 0)
|
|
55090
|
+
return [];
|
|
55091
|
+
const [wikiReceipts, memoryReceipts, skillReceipts] = await Promise.all([
|
|
55092
|
+
collectReceiptTargets(cwd, "wiki"),
|
|
55093
|
+
collectReceiptTargets(cwd, "memory"),
|
|
55094
|
+
collectReceiptTargets(cwd, "skill")
|
|
55095
|
+
]);
|
|
55096
|
+
const items = [];
|
|
55097
|
+
const wikiPages = await collectPages(cwd).catch(() => []);
|
|
55098
|
+
for (const page of wikiPages) {
|
|
55099
|
+
if ((page.status ?? "").toLowerCase() !== "accepted")
|
|
55100
|
+
continue;
|
|
55101
|
+
const targetRef = `wiki/${page.relativePath}`;
|
|
55102
|
+
if (wikiReceipts.has(targetRef))
|
|
55103
|
+
continue;
|
|
55104
|
+
const attribution = await attributeToSession(page.absolutePath, scoped);
|
|
55105
|
+
if (attribution === undefined)
|
|
55106
|
+
continue;
|
|
55107
|
+
items.push({
|
|
55108
|
+
type: "unreviewed-sac-path",
|
|
55109
|
+
sessionId: attribution.sessionId,
|
|
55110
|
+
...attribution.workspaceId !== undefined ? { workspaceId: attribution.workspaceId } : {},
|
|
55111
|
+
owner: "wiki",
|
|
55112
|
+
path: targetRef,
|
|
55113
|
+
...page.status !== null ? { status: page.status } : {},
|
|
55114
|
+
changedAt: attribution.changedAt
|
|
55115
|
+
});
|
|
55116
|
+
}
|
|
55117
|
+
const memoryEntries = await collectEntries(cwd).catch(() => []);
|
|
55118
|
+
for (const entry of memoryEntries) {
|
|
55119
|
+
if (entry.status !== "accepted")
|
|
55120
|
+
continue;
|
|
55121
|
+
const targetRef = `memory/${entry.relativePath}`;
|
|
55122
|
+
if (memoryReceipts.has(targetRef))
|
|
55123
|
+
continue;
|
|
55124
|
+
const attribution = await attributeToSession(entry.absolutePath, scoped);
|
|
55125
|
+
if (attribution === undefined)
|
|
55126
|
+
continue;
|
|
55127
|
+
items.push({
|
|
55128
|
+
type: "unreviewed-sac-path",
|
|
55129
|
+
sessionId: attribution.sessionId,
|
|
55130
|
+
...attribution.workspaceId !== undefined ? { workspaceId: attribution.workspaceId } : {},
|
|
55131
|
+
owner: "memory",
|
|
55132
|
+
path: targetRef,
|
|
55133
|
+
status: entry.status,
|
|
55134
|
+
changedAt: attribution.changedAt
|
|
55135
|
+
});
|
|
55136
|
+
}
|
|
55137
|
+
const sacSkillsDir = path150.join(cwd, ".metaproject", "project-skills", "sac");
|
|
55138
|
+
const skillFiles = await findSkillFiles(sacSkillsDir);
|
|
55139
|
+
for (const absolutePath of skillFiles) {
|
|
55140
|
+
const targetRef = path150.relative(path150.join(cwd, ".metaproject"), absolutePath).split(path150.sep).join("/");
|
|
55141
|
+
if (skillReceipts.has(targetRef))
|
|
55142
|
+
continue;
|
|
55143
|
+
const attribution = await attributeToSession(absolutePath, scoped);
|
|
55144
|
+
if (attribution === undefined)
|
|
55145
|
+
continue;
|
|
55146
|
+
items.push({
|
|
55147
|
+
type: "unreviewed-sac-path",
|
|
55148
|
+
sessionId: attribution.sessionId,
|
|
55149
|
+
...attribution.workspaceId !== undefined ? { workspaceId: attribution.workspaceId } : {},
|
|
55150
|
+
owner: "skill",
|
|
55151
|
+
path: targetRef,
|
|
55152
|
+
changedAt: attribution.changedAt
|
|
55153
|
+
});
|
|
55154
|
+
}
|
|
55155
|
+
return items;
|
|
55156
|
+
}
|
|
54807
55157
|
async function isSlateEngaged(dir) {
|
|
54808
55158
|
if (await pathExists(path150.join(dir, "slate.json")))
|
|
54809
55159
|
return true;
|
|
@@ -55070,7 +55420,7 @@ async function loadInspectorCatchUp(cwd) {
|
|
|
55070
55420
|
try {
|
|
55071
55421
|
return await buildCatchUp({ cwd });
|
|
55072
55422
|
} catch {
|
|
55073
|
-
return { proposals: [], blocked: [], unboundCandidates: [], unknown: [], lifecycleFlags: [] };
|
|
55423
|
+
return { proposals: [], blocked: [], unboundCandidates: [], unknown: [], lifecycleFlags: [], unreviewedPaths: [] };
|
|
55074
55424
|
}
|
|
55075
55425
|
}
|
|
55076
55426
|
function catchUpItems(report) {
|
|
@@ -65145,6 +65495,22 @@ New session ${shortSessionId(live.summary.id)}.
|
|
|
65145
65495
|
slateSession,
|
|
65146
65496
|
mintAttemptId: mintTimestampAttemptId
|
|
65147
65497
|
});
|
|
65498
|
+
} else if (command === "/theme") {
|
|
65499
|
+
const wanted = rest.trim();
|
|
65500
|
+
if (wanted.length === 0) {
|
|
65501
|
+
agentIo.onSystem?.(formatThemeList(getThemeId()));
|
|
65502
|
+
} else {
|
|
65503
|
+
const next = parseThemeId(wanted);
|
|
65504
|
+
if (next === undefined) {
|
|
65505
|
+
agentIo.onSystem?.(`Unknown theme '${wanted}'.
|
|
65506
|
+
${formatThemeList(getThemeId())}`);
|
|
65507
|
+
} else {
|
|
65508
|
+
applyThemeId(next);
|
|
65509
|
+
persistThemeId(next);
|
|
65510
|
+
agentIo.onSystem?.(`Theme: ${themeLabel(next)}
|
|
65511
|
+
`);
|
|
65512
|
+
}
|
|
65513
|
+
}
|
|
65148
65514
|
} else {
|
|
65149
65515
|
agentIo.onSystem?.(describeUnavailableCommand(command, "agent") ?? `Unknown command: ${command}. Type /help.
|
|
65150
65516
|
`);
|
|
@@ -65409,6 +65775,12 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
65409
65775
|
}
|
|
65410
65776
|
}
|
|
65411
65777
|
const rl = readline2.createInterface({ input: process.stdin });
|
|
65778
|
+
if (!process.stdin.isTTY) {
|
|
65779
|
+
process.on("SIGINT", () => {
|
|
65780
|
+
rl.close();
|
|
65781
|
+
process.exit(130);
|
|
65782
|
+
});
|
|
65783
|
+
}
|
|
65412
65784
|
const lineIterator = rl[Symbol.asyncIterator]();
|
|
65413
65785
|
const sharedLines = { [Symbol.asyncIterator]: () => lineIterator };
|
|
65414
65786
|
const { io, emitSystem, printHeader, printPrompt, destroy } = createRichIo(sharedLines, versionCheck);
|
|
@@ -65675,7 +66047,7 @@ Shell:
|
|
|
65675
66047
|
|
|
65676
66048
|
// src/commands/modules.ts
|
|
65677
66049
|
init_fs();
|
|
65678
|
-
import { readFile as
|
|
66050
|
+
import { readFile as readFile80 } from "fs/promises";
|
|
65679
66051
|
import { stdin } from "process";
|
|
65680
66052
|
import path153 from "path";
|
|
65681
66053
|
var MODULES = [
|
|
@@ -65737,7 +66109,7 @@ async function modulesCommand(args2 = []) {
|
|
|
65737
66109
|
}
|
|
65738
66110
|
let manifest = {};
|
|
65739
66111
|
try {
|
|
65740
|
-
manifest = JSON.parse(await
|
|
66112
|
+
manifest = JSON.parse(await readFile80(manifestPath, "utf8"));
|
|
65741
66113
|
} catch {}
|
|
65742
66114
|
const enabled = new Set(MODULES.filter((module) => manifest.modules?.[module.name]?.enabled === true).map((module) => module.name));
|
|
65743
66115
|
if (wantsJson) {
|
|
@@ -67711,7 +68083,7 @@ function printHelp17() {
|
|
|
67711
68083
|
|
|
67712
68084
|
// src/commands/update.ts
|
|
67713
68085
|
import { spawn as spawn5 } from "child_process";
|
|
67714
|
-
import { chmod as chmod4, mkdir as mkdir56, readFile as
|
|
68086
|
+
import { chmod as chmod4, mkdir as mkdir56, readFile as readFile81, readdir as readdir27, writeFile as writeFile49 } from "fs/promises";
|
|
67715
68087
|
import { access as access4, constants as constants2, existsSync as existsSync30 } from "fs";
|
|
67716
68088
|
import path158 from "path";
|
|
67717
68089
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
@@ -68022,7 +68394,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
|
|
|
68022
68394
|
if (!await pathExists(hookPath)) {
|
|
68023
68395
|
return false;
|
|
68024
68396
|
}
|
|
68025
|
-
return (await
|
|
68397
|
+
return (await readFile81(hookPath, "utf8")).includes("# keryx:");
|
|
68026
68398
|
}
|
|
68027
68399
|
async function collectDashboardData(metaprojectRoot) {
|
|
68028
68400
|
const data = {};
|
|
@@ -68074,12 +68446,12 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
68074
68446
|
continue;
|
|
68075
68447
|
}
|
|
68076
68448
|
try {
|
|
68077
|
-
const flow = JSON.parse(await
|
|
68449
|
+
const flow = JSON.parse(await readFile81(flowPath, "utf8"));
|
|
68078
68450
|
const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
|
|
68079
68451
|
let acTotal = 0;
|
|
68080
68452
|
const acPath2 = path158.join(flowsRoot2, dir, "acceptance-criteria.md");
|
|
68081
68453
|
if (await pathExists(acPath2)) {
|
|
68082
|
-
const acContent = await
|
|
68454
|
+
const acContent = await readFile81(acPath2, "utf8");
|
|
68083
68455
|
acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
|
|
68084
68456
|
}
|
|
68085
68457
|
flows.push({
|
|
@@ -68135,7 +68507,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
68135
68507
|
if (!await pathExists(filePath)) {
|
|
68136
68508
|
continue;
|
|
68137
68509
|
}
|
|
68138
|
-
const content = await
|
|
68510
|
+
const content = await readFile81(filePath, "utf8");
|
|
68139
68511
|
docs[href] = content.length > 40000 ? `${content.slice(0, 40000)}
|
|
68140
68512
|
|
|
68141
68513
|
\u2026truncated\u2026` : content;
|
|
@@ -68152,7 +68524,7 @@ async function collectHealthDashboardData(metaprojectRoot) {
|
|
|
68152
68524
|
if (!await pathExists(reportPath2)) {
|
|
68153
68525
|
return;
|
|
68154
68526
|
}
|
|
68155
|
-
const report = JSON.parse(await
|
|
68527
|
+
const report = JSON.parse(await readFile81(reportPath2, "utf8"));
|
|
68156
68528
|
const metrics = Array.isArray(report.metrics) ? report.metrics : [];
|
|
68157
68529
|
const findings = Array.isArray(report.findings) ? report.findings : [];
|
|
68158
68530
|
const project = metrics.find((metric) => metric.key === "project") ?? {};
|
|
@@ -68266,7 +68638,7 @@ async function collectGraphDashboardData(metaprojectRoot) {
|
|
|
68266
68638
|
let nodes = 0;
|
|
68267
68639
|
let files = 0;
|
|
68268
68640
|
let assets = 0;
|
|
68269
|
-
for (const node of parseJsonl2(await
|
|
68641
|
+
for (const node of parseJsonl2(await readFile81(nodesPath, "utf8"))) {
|
|
68270
68642
|
nodes += 1;
|
|
68271
68643
|
if (node.kind === "asset") {
|
|
68272
68644
|
assets += 1;
|
|
@@ -68282,7 +68654,7 @@ async function collectGraphDashboardData(metaprojectRoot) {
|
|
|
68282
68654
|
let imports = 0;
|
|
68283
68655
|
let assetEdges = 0;
|
|
68284
68656
|
let unresolved = 0;
|
|
68285
|
-
for (const edge of parseJsonl2(await
|
|
68657
|
+
for (const edge of parseJsonl2(await readFile81(edgesPath, "utf8"))) {
|
|
68286
68658
|
edges += 1;
|
|
68287
68659
|
if (edge.kind === "imports") {
|
|
68288
68660
|
imports += 1;
|
|
@@ -68312,7 +68684,7 @@ async function collectTestingDashboardData(metaprojectRoot) {
|
|
|
68312
68684
|
const reportPath2 = path158.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
|
|
68313
68685
|
const contextPath = path158.join(metaprojectRoot, "data", "testing", "context.md");
|
|
68314
68686
|
if (await pathExists(reportPath2)) {
|
|
68315
|
-
const report = JSON.parse(await
|
|
68687
|
+
const report = JSON.parse(await readFile81(reportPath2, "utf8"));
|
|
68316
68688
|
const totalTests = numberOrUndefined(report.total);
|
|
68317
68689
|
const failedTests = Array.isArray(report.failures) ? report.failures.length : numberOrUndefined(report.failed);
|
|
68318
68690
|
return {
|
|
@@ -68343,7 +68715,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
68343
68715
|
if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
|
|
68344
68716
|
continue;
|
|
68345
68717
|
}
|
|
68346
|
-
const content = await
|
|
68718
|
+
const content = await readFile81(filePath, "utf8");
|
|
68347
68719
|
const embedded = content.length > 24000 ? `${content.slice(0, 24000)}
|
|
68348
68720
|
|
|
68349
68721
|
\u2026truncated\u2026` : content;
|
|
@@ -68519,7 +68891,7 @@ async function enableTasksInManifest(metaprojectRoot) {
|
|
|
68519
68891
|
}
|
|
68520
68892
|
let raw;
|
|
68521
68893
|
try {
|
|
68522
|
-
raw = JSON.parse(await
|
|
68894
|
+
raw = JSON.parse(await readFile81(manifestPath, "utf8"));
|
|
68523
68895
|
} catch {
|
|
68524
68896
|
return;
|
|
68525
68897
|
}
|
|
@@ -68542,7 +68914,7 @@ async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
|
|
|
68542
68914
|
}
|
|
68543
68915
|
let raw;
|
|
68544
68916
|
try {
|
|
68545
|
-
raw = JSON.parse(await
|
|
68917
|
+
raw = JSON.parse(await readFile81(manifestPath, "utf8"));
|
|
68546
68918
|
} catch {
|
|
68547
68919
|
return;
|
|
68548
68920
|
}
|
|
@@ -68651,7 +69023,7 @@ async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
|
68651
69023
|
const managedBlock = `${blockStart}
|
|
68652
69024
|
${content.trim()}
|
|
68653
69025
|
${blockEnd}`;
|
|
68654
|
-
const existing = await pathExists(hookPath) ? await
|
|
69026
|
+
const existing = await pathExists(hookPath) ? await readFile81(hookPath, "utf8") : `#!/usr/bin/env sh
|
|
68655
69027
|
`;
|
|
68656
69028
|
const blockPattern = new RegExp(`${escapeRegExp6(blockStart)}[\\s\\S]*?${escapeRegExp6(blockEnd)}`);
|
|
68657
69029
|
const next = blockPattern.test(existing) ? existing.replace(blockPattern, managedBlock) : `${existing.trimEnd()}
|
|
@@ -68670,7 +69042,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
|
|
|
68670
69042
|
if (!await pathExists(hookPath)) {
|
|
68671
69043
|
return;
|
|
68672
69044
|
}
|
|
68673
|
-
const existing = await
|
|
69045
|
+
const existing = await readFile81(hookPath, "utf8");
|
|
68674
69046
|
const blockStart = `# keryx:${blockId}:begin`;
|
|
68675
69047
|
const blockEnd = `# keryx:${blockId}:end`;
|
|
68676
69048
|
const blockPattern = new RegExp(`\\n*${escapeRegExp6(blockStart)}[\\s\\S]*?${escapeRegExp6(blockEnd)}\\n*`);
|
|
@@ -68692,7 +69064,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
|
|
|
68692
69064
|
if (!await pathExists(hookPath)) {
|
|
68693
69065
|
return false;
|
|
68694
69066
|
}
|
|
68695
|
-
const hook = await
|
|
69067
|
+
const hook = await readFile81(hookPath, "utf8");
|
|
68696
69068
|
return hook.includes("# keryx:security-pre-push:begin");
|
|
68697
69069
|
}
|
|
68698
69070
|
async function agentSettingsHasSecuritySentinel2(projectRoot) {
|
|
@@ -68700,7 +69072,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
|
|
|
68700
69072
|
if (!await pathExists(file)) {
|
|
68701
69073
|
return false;
|
|
68702
69074
|
}
|
|
68703
|
-
return (await
|
|
69075
|
+
return (await readFile81(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
|
|
68704
69076
|
}
|
|
68705
69077
|
async function readManifest5(metaprojectRoot) {
|
|
68706
69078
|
const manifestPath = path158.join(metaprojectRoot, "metaproject.json");
|
|
@@ -68713,7 +69085,7 @@ async function readManifest5(metaprojectRoot) {
|
|
|
68713
69085
|
};
|
|
68714
69086
|
}
|
|
68715
69087
|
try {
|
|
68716
|
-
const manifest = JSON.parse(await
|
|
69088
|
+
const manifest = JSON.parse(await readFile81(manifestPath, "utf8"));
|
|
68717
69089
|
const normalized = normalizeManifest(manifest);
|
|
68718
69090
|
return {
|
|
68719
69091
|
exists: true,
|
|
@@ -68837,7 +69209,7 @@ async function run(command, args2, cwd) {
|
|
|
68837
69209
|
});
|
|
68838
69210
|
}
|
|
68839
69211
|
async function writeTextIfChanged4(filePath, content) {
|
|
68840
|
-
if (await pathExists(filePath) && await
|
|
69212
|
+
if (await pathExists(filePath) && await readFile81(filePath, "utf8") === content) {
|
|
68841
69213
|
return;
|
|
68842
69214
|
}
|
|
68843
69215
|
await mkdir56(path158.dirname(filePath), { recursive: true });
|
|
@@ -68851,8 +69223,8 @@ async function writeTextIfMissing4(filePath, content) {
|
|
|
68851
69223
|
await writeFile49(filePath, content, "utf8");
|
|
68852
69224
|
}
|
|
68853
69225
|
async function copyFileIfChanged2(from, to) {
|
|
68854
|
-
const next = await
|
|
68855
|
-
if (await pathExists(to) && await
|
|
69226
|
+
const next = await readFile81(from, "utf8");
|
|
69227
|
+
if (await pathExists(to) && await readFile81(to, "utf8") === next) {
|
|
68856
69228
|
return;
|
|
68857
69229
|
}
|
|
68858
69230
|
await mkdir56(path158.dirname(to), { recursive: true });
|
|
@@ -68962,7 +69334,7 @@ function printHelp19() {
|
|
|
68962
69334
|
import { readFileSync as readFileSync10 } from "fs";
|
|
68963
69335
|
|
|
68964
69336
|
// src/agents/bootstrap.ts
|
|
68965
|
-
import { mkdir as mkdir57, readFile as
|
|
69337
|
+
import { mkdir as mkdir57, readFile as readFile82, writeFile as writeFile50 } from "fs/promises";
|
|
68966
69338
|
import { homedir as homedir7 } from "os";
|
|
68967
69339
|
import path160 from "path";
|
|
68968
69340
|
init_fs();
|
|
@@ -69035,7 +69407,7 @@ function resolveAgentBootstrapRuntimes(ids) {
|
|
|
69035
69407
|
async function agentBootstrapStatus(runtime, homeRoot = homedir7()) {
|
|
69036
69408
|
const filePath = runtime.filePath(homeRoot);
|
|
69037
69409
|
const exists2 = await pathExists(filePath);
|
|
69038
|
-
const content = exists2 ? await
|
|
69410
|
+
const content = exists2 ? await readFile82(filePath, "utf8") : "";
|
|
69039
69411
|
const expected = renderAgentBootstrapBlock(runtime.fileName).trim();
|
|
69040
69412
|
const installed = content.includes(AGENT_BOOTSTRAP_START);
|
|
69041
69413
|
const current = installed && extractManagedBlock(content)?.trim() === expected;
|
|
@@ -69045,7 +69417,7 @@ async function installAgentBootstrap(runtime, options = {}) {
|
|
|
69045
69417
|
const homeRoot = options.homeRoot ?? homedir7();
|
|
69046
69418
|
const filePath = runtime.filePath(homeRoot);
|
|
69047
69419
|
const exists2 = await pathExists(filePath);
|
|
69048
|
-
const current = exists2 ? await
|
|
69420
|
+
const current = exists2 ? await readFile82(filePath, "utf8") : "";
|
|
69049
69421
|
const next = upsertManagedBlock(current || defaultAgentFile(runtime), renderAgentBootstrapBlock(runtime.fileName));
|
|
69050
69422
|
const dryRun = options.dryRun === true;
|
|
69051
69423
|
const wrote = next !== current;
|
|
@@ -69060,7 +69432,7 @@ async function uninstallAgentBootstrap(runtime, options = {}) {
|
|
|
69060
69432
|
const homeRoot = options.homeRoot ?? homedir7();
|
|
69061
69433
|
const filePath = runtime.filePath(homeRoot);
|
|
69062
69434
|
const exists2 = await pathExists(filePath);
|
|
69063
|
-
const current = exists2 ? await
|
|
69435
|
+
const current = exists2 ? await readFile82(filePath, "utf8") : "";
|
|
69064
69436
|
const next = removeManagedBlock(current);
|
|
69065
69437
|
const dryRun = options.dryRun === true;
|
|
69066
69438
|
const removed = next !== current;
|
|
@@ -69586,7 +69958,7 @@ function printBootstrapHelp() {
|
|
|
69586
69958
|
|
|
69587
69959
|
// src/commands/metrics.ts
|
|
69588
69960
|
init_args();
|
|
69589
|
-
import { readFile as
|
|
69961
|
+
import { readFile as readFile83 } from "fs/promises";
|
|
69590
69962
|
import path162 from "path";
|
|
69591
69963
|
|
|
69592
69964
|
// src/metrics/benchmark.ts
|
|
@@ -70862,7 +71234,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
70862
71234
|
process.exitCode = 1;
|
|
70863
71235
|
return;
|
|
70864
71236
|
}
|
|
70865
|
-
const record2 = JSON.parse(await
|
|
71237
|
+
const record2 = JSON.parse(await readFile83(path162.resolve(projectRoot, file), "utf8"));
|
|
70866
71238
|
const result = validateRunRecord(record2);
|
|
70867
71239
|
console.log(result.valid ? "valid: yes" : "valid: no");
|
|
70868
71240
|
for (const error2 of result.errors)
|
|
@@ -70893,7 +71265,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
70893
71265
|
process.exitCode = 1;
|
|
70894
71266
|
return;
|
|
70895
71267
|
}
|
|
70896
|
-
console.log(await
|
|
71268
|
+
console.log(await readFile83(file, "utf8"));
|
|
70897
71269
|
return;
|
|
70898
71270
|
}
|
|
70899
71271
|
if (subcommand === "compare") {
|
|
@@ -70904,8 +71276,8 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
70904
71276
|
process.exitCode = 1;
|
|
70905
71277
|
return;
|
|
70906
71278
|
}
|
|
70907
|
-
const a = JSON.parse(await
|
|
70908
|
-
const b = JSON.parse(await
|
|
71279
|
+
const a = JSON.parse(await readFile83(path162.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
|
|
71280
|
+
const b = JSON.parse(await readFile83(path162.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
|
|
70909
71281
|
const comparison = compareExecutionRuns(a, b);
|
|
70910
71282
|
console.log(stableJson(comparison));
|
|
70911
71283
|
return;
|
|
@@ -70954,7 +71326,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
70954
71326
|
process.exitCode = 1;
|
|
70955
71327
|
return;
|
|
70956
71328
|
}
|
|
70957
|
-
const raw = JSON.parse(await
|
|
71329
|
+
const raw = JSON.parse(await readFile83(path162.resolve(projectRoot, file), "utf8"));
|
|
70958
71330
|
const input2 = Array.isArray(raw) ? raw : raw.runs ?? [];
|
|
70959
71331
|
const result = validatePairedBenchmark(input2);
|
|
70960
71332
|
console.log(stableJson(result));
|
|
@@ -70966,7 +71338,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
70966
71338
|
process.exitCode = 1;
|
|
70967
71339
|
}
|
|
70968
71340
|
async function loadAffectedSets(projectRoot, file) {
|
|
70969
|
-
const raw = JSON.parse(await
|
|
71341
|
+
const raw = JSON.parse(await readFile83(path162.resolve(projectRoot, file), "utf8"));
|
|
70970
71342
|
const map = new Map;
|
|
70971
71343
|
for (const entry of raw.targets ?? []) {
|
|
70972
71344
|
if (typeof entry.target === "string")
|
|
@@ -71037,7 +71409,7 @@ async function runHarnessLayer(projectRoot, args2, ladder) {
|
|
|
71037
71409
|
let tasks;
|
|
71038
71410
|
let model;
|
|
71039
71411
|
try {
|
|
71040
|
-
const raw = JSON.parse(await
|
|
71412
|
+
const raw = JSON.parse(await readFile83(path162.resolve(projectRoot, resultsPath), "utf8"));
|
|
71041
71413
|
tasks = raw.tasks ?? [];
|
|
71042
71414
|
model = raw.model;
|
|
71043
71415
|
} catch (error2) {
|
|
@@ -71068,7 +71440,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args2, ladder) {
|
|
|
71068
71440
|
let cases;
|
|
71069
71441
|
let model;
|
|
71070
71442
|
try {
|
|
71071
|
-
const raw = JSON.parse(await
|
|
71443
|
+
const raw = JSON.parse(await readFile83(path162.resolve(projectRoot, resultsPath), "utf8"));
|
|
71072
71444
|
cases = raw.cases ?? [];
|
|
71073
71445
|
model = raw.model;
|
|
71074
71446
|
} catch (error2) {
|
|
@@ -71093,7 +71465,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args2, ladder) {
|
|
|
71093
71465
|
let cases;
|
|
71094
71466
|
let model;
|
|
71095
71467
|
try {
|
|
71096
|
-
const raw = JSON.parse(await
|
|
71468
|
+
const raw = JSON.parse(await readFile83(path162.resolve(projectRoot, resultsPath), "utf8"));
|
|
71097
71469
|
cases = raw.cases ?? [];
|
|
71098
71470
|
model = raw.model;
|
|
71099
71471
|
} catch (error2) {
|
|
@@ -71118,7 +71490,7 @@ async function runSafetyContainmentLayer(projectRoot, args2, ladder, caseClass)
|
|
|
71118
71490
|
let cases;
|
|
71119
71491
|
let model;
|
|
71120
71492
|
try {
|
|
71121
|
-
const raw = JSON.parse(await
|
|
71493
|
+
const raw = JSON.parse(await readFile83(path162.resolve(projectRoot, resultsPath), "utf8"));
|
|
71122
71494
|
cases = raw.cases ?? [];
|
|
71123
71495
|
model = raw.model;
|
|
71124
71496
|
} catch (error2) {
|
|
@@ -71209,7 +71581,7 @@ async function runGdgraphLayer(projectRoot, args2, ladder) {
|
|
|
71209
71581
|
return allValid;
|
|
71210
71582
|
}
|
|
71211
71583
|
async function loadCoverageMap2(projectRoot, file) {
|
|
71212
|
-
const raw = JSON.parse(await
|
|
71584
|
+
const raw = JSON.parse(await readFile83(path162.resolve(projectRoot, file), "utf8"));
|
|
71213
71585
|
return raw.coverageMap ?? {};
|
|
71214
71586
|
}
|
|
71215
71587
|
async function runTestingLayer(projectRoot, args2, ladder) {
|
|
@@ -71246,7 +71618,7 @@ async function runTestingLayer(projectRoot, args2, ladder) {
|
|
|
71246
71618
|
return result.valid;
|
|
71247
71619
|
}
|
|
71248
71620
|
async function loadMemoryGoldK(projectRoot, file) {
|
|
71249
|
-
const raw = JSON.parse(await
|
|
71621
|
+
const raw = JSON.parse(await readFile83(path162.resolve(projectRoot, file), "utf8"));
|
|
71250
71622
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
|
|
71251
71623
|
}
|
|
71252
71624
|
async function runMemoryLayer(projectRoot, args2, ladder) {
|
|
@@ -71283,11 +71655,11 @@ async function runMemoryLayer(projectRoot, args2, ladder) {
|
|
|
71283
71655
|
return result.valid;
|
|
71284
71656
|
}
|
|
71285
71657
|
async function loadWikiGoldK(projectRoot, file) {
|
|
71286
|
-
const raw = JSON.parse(await
|
|
71658
|
+
const raw = JSON.parse(await readFile83(path162.resolve(projectRoot, file), "utf8"));
|
|
71287
71659
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
|
|
71288
71660
|
}
|
|
71289
71661
|
async function loadWikiGroundedness(projectRoot, file) {
|
|
71290
|
-
const raw = JSON.parse(await
|
|
71662
|
+
const raw = JSON.parse(await readFile83(path162.resolve(projectRoot, file), "utf8"));
|
|
71291
71663
|
const map = new Map;
|
|
71292
71664
|
for (const entry of raw.targets ?? []) {
|
|
71293
71665
|
if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
|
|
@@ -71343,7 +71715,7 @@ async function runWikiLayer(projectRoot, args2, ladder) {
|
|
|
71343
71715
|
return result.valid;
|
|
71344
71716
|
}
|
|
71345
71717
|
async function loadGdctxFacts(projectRoot, file) {
|
|
71346
|
-
const raw = JSON.parse(await
|
|
71718
|
+
const raw = JSON.parse(await readFile83(path162.resolve(projectRoot, file), "utf8"));
|
|
71347
71719
|
const inputs = [];
|
|
71348
71720
|
for (const entry of raw.inputs ?? []) {
|
|
71349
71721
|
if (typeof entry.input === "string") {
|
|
@@ -71381,7 +71753,7 @@ async function collect(projectRoot, args2) {
|
|
|
71381
71753
|
process.exitCode = 1;
|
|
71382
71754
|
return;
|
|
71383
71755
|
}
|
|
71384
|
-
const raw = JSON.parse(await
|
|
71756
|
+
const raw = JSON.parse(await readFile83(path162.resolve(projectRoot, eventFile), "utf8"));
|
|
71385
71757
|
const events2 = Array.isArray(raw) ? raw : raw.events;
|
|
71386
71758
|
const startedAt = optionValue(args2, "--started-at") ?? events2[0]?.timestamp_utc ?? new Date().toISOString();
|
|
71387
71759
|
const finishedAt = optionValue(args2, "--finished-at") ?? events2.at(-1)?.timestamp_utc ?? startedAt;
|
|
@@ -71781,6 +72153,7 @@ function renderCatchUp(report, includeLifecycleFlags = true) {
|
|
|
71781
72153
|
sections.push(renderSection("Blocked sessions (stopped unattended)", report.blocked, (item) => `- Session ${item.sessionId} stopped unattended (${item.terminalState.reason}) at ${item.terminalState.occurredAt}. Resume it, or archive and move on? ` + `Recommendation: \`keryx shell -r ${item.sessionId}\` to resume and unblock it.`));
|
|
71782
72154
|
sections.push(renderSection("Unbound candidates (wrap-up ran, no workspace bound)", report.unboundCandidates, (item) => `- Session ${item.sessionId} produced untriaged seeds with no workspace bound (${item.summary}). Bind to a workspace and propose, or discard? ` + `Recommendation: pick a workspace, then \`keryx workspace propose <workspace-id> --kind <kind> --session ${item.sessionId}\` (evidence: ${item.evidencePath}).`));
|
|
71783
72155
|
sections.push(renderSection("Unknown (no resolution recorded)", report.unknown, (item) => `- Session ${item.sessionId} was last seen ${item.lastSeenAt} with no proposal, terminal state, or unbound-candidate artifact recorded. Investigate, or ignore? ` + `Recommendation: \`keryx sessions list\` / \`keryx shell -r ${item.sessionId}\` to see what happened.`));
|
|
72156
|
+
sections.push(renderSection("Unreviewed SAC-owned changes (no proposal on record)", report.unreviewedPaths, (item) => `- Session ${item.sessionId} changed ${item.owner} path \`${item.path}\`${item.status !== undefined ? ` (Status: ${item.status})` : ""} at ${item.changedAt} with NO SAC proposal/receipt behind it \u2014 this looks like it bypassed review. Was this reviewed some other way, or should it be? ` + `Recommendation: \`keryx shell -r ${item.sessionId}\` to see what happened; if the content is good, route it through a real proposal (\`keryx workspace propose ...\`) before trusting it as durable knowledge.`));
|
|
71784
72157
|
if (includeLifecycleFlags) {
|
|
71785
72158
|
sections.push(renderSection("Lifecycle flags (component no longer in the graph)", report.lifecycleFlags, (item) => `- ${item.kind} \`${item.ref}\` scopes to \`${item.missingComponent}\`, which is no longer in the code graph (flagged ${item.flaggedAt}). Still relevant, or safe to clean up? ` + `Recommendation: this is report-only \u2014 nothing was archived/edited/removed automatically; ${item.kind === "workspace" ? "`keryx workspace archive " + item.ref + "`" : item.kind === "memory-entry" ? "`keryx memory supersede` or edit the entry directly" : "edit or remove the wiki page directly"} if you decide it's actually stale.`));
|
|
71786
72159
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mrciphersmith/keryx",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.56",
|
|
4
4
|
"description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
5
5
|
"private": false,
|
|
6
6
|
"publishConfig": {
|