@mrciphersmith/keryx 0.2.42 → 0.2.44
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 +1384 -405
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -12095,6 +12095,10 @@ function touchesAgentCredentials(command) {
|
|
|
12095
12095
|
const text = command.toLowerCase();
|
|
12096
12096
|
return CREDENTIAL_MARKERS.some((marker2) => text.includes(marker2));
|
|
12097
12097
|
}
|
|
12098
|
+
function touchesSacConfirmReview(command) {
|
|
12099
|
+
const text = command.toLowerCase();
|
|
12100
|
+
return SAC_REVIEW_MARKERS.some((marker2) => text.includes(marker2));
|
|
12101
|
+
}
|
|
12098
12102
|
var CATASTROPHIC_TARGETS, RECURSIVE_FLAG, PRIVILEGE, INTERPRETERS, DOWNLOADERS, CONTAINER_RUNTIMES, PROTECTED_BRANCHES, BLOCK_DEVICE, rulePrivilege = (v) => PRIVILEGE.has(v.cmd), ruleBlockDeviceRedirect = (v) => BLOCK_DEVICE.test(v.raw) && /(?:^|\s)(?:>|>>)\s*\/dev\//.test(v.raw), ruleRm = (v) => v.cmd === "rm" && v.positionals.some(isCatastrophicTarget), ruleDd = (v) => v.cmd === "dd" && v.args.some((x) => /^of=/i.test(x) && BLOCK_DEVICE.test(x)), rulePermissionSweep = (v) => (v.cmd === "chmod" || v.cmd === "chown" || v.cmd === "chgrp") && hasRecursive(v.words) && v.positionals.some(isCatastrophicTarget), ruleHostState = (v) => {
|
|
12099
12103
|
if (v.cmd === "shutdown" || v.cmd === "reboot" || v.cmd === "halt" || v.cmd === "poweroff")
|
|
12100
12104
|
return true;
|
|
@@ -12115,7 +12119,7 @@ var CATASTROPHIC_TARGETS, RECURSIVE_FLAG, PRIVILEGE, INTERPRETERS, DOWNLOADERS,
|
|
|
12115
12119
|
return true;
|
|
12116
12120
|
const named = after.map((x) => x.split("/").pop()?.toLowerCase() ?? "");
|
|
12117
12121
|
return named.some((n) => PROTECTED_BRANCHES.includes(n));
|
|
12118
|
-
}, RULES3, CREDENTIAL_MARKERS;
|
|
12122
|
+
}, RULES3, CREDENTIAL_MARKERS, SAC_REVIEW_MARKERS;
|
|
12119
12123
|
var init_command_risk = __esm(() => {
|
|
12120
12124
|
init_shell_syntax();
|
|
12121
12125
|
CATASTROPHIC_TARGETS = new Set([
|
|
@@ -12180,6 +12184,7 @@ var init_command_risk = __esm(() => {
|
|
|
12180
12184
|
".local/share/keryx",
|
|
12181
12185
|
".config/keryx"
|
|
12182
12186
|
];
|
|
12187
|
+
SAC_REVIEW_MARKERS = ["confirm-review", "workspace review"];
|
|
12183
12188
|
});
|
|
12184
12189
|
|
|
12185
12190
|
// src/commands/permission-mode.ts
|
|
@@ -12187,11 +12192,11 @@ function isPermissionMode(value) {
|
|
|
12187
12192
|
return PERMISSION_MODES.includes(value);
|
|
12188
12193
|
}
|
|
12189
12194
|
function resolveApprovalDecision(input2) {
|
|
12190
|
-
const { mode, risk, destructive, credentials } = input2;
|
|
12195
|
+
const { mode, risk, destructive, credentials, sacReviewConfirmation } = input2;
|
|
12191
12196
|
if (risk === "read") {
|
|
12192
12197
|
return "auto";
|
|
12193
12198
|
}
|
|
12194
|
-
if (credentials) {
|
|
12199
|
+
if (credentials || sacReviewConfirmation) {
|
|
12195
12200
|
return "ask";
|
|
12196
12201
|
}
|
|
12197
12202
|
if (mode === "auto") {
|
|
@@ -17667,7 +17672,8 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, on
|
|
|
17667
17672
|
const command = typeof input2.command === "string" ? input2.command : "";
|
|
17668
17673
|
const destructive = risk === "destructive" || isDestructiveCommand(command);
|
|
17669
17674
|
const credentials = touchesAgentCredentials(command);
|
|
17670
|
-
const
|
|
17675
|
+
const sacReviewConfirmation = touchesSacConfirmReview(command);
|
|
17676
|
+
const decision = resolveApprovalDecision({ mode, risk, destructive, credentials, sacReviewConfirmation });
|
|
17671
17677
|
if (decision === "auto") {
|
|
17672
17678
|
onAutoApproved?.(call.name, call.input, { destructive, credentials });
|
|
17673
17679
|
} else {
|
|
@@ -17682,7 +17688,7 @@ async function executeCall(call, toolByName, requestApproval, permissionMode, on
|
|
|
17682
17688
|
}
|
|
17683
17689
|
}
|
|
17684
17690
|
} else if (risk === "delegate") {
|
|
17685
|
-
const decision = resolveApprovalDecision({ mode, risk, destructive: false, credentials: false });
|
|
17691
|
+
const decision = resolveApprovalDecision({ mode, risk, destructive: false, credentials: false, sacReviewConfirmation: false });
|
|
17686
17692
|
if (decision === "auto") {
|
|
17687
17693
|
onAutoApproved?.(call.name, call.input, { destructive: false, credentials: false });
|
|
17688
17694
|
} else {
|
|
@@ -45817,7 +45823,7 @@ function harnessWave(args2, deps) {
|
|
|
45817
45823
|
init_make_provider();
|
|
45818
45824
|
init_orient();
|
|
45819
45825
|
init_metaproject_adapter();
|
|
45820
|
-
import { randomUUID as
|
|
45826
|
+
import { randomUUID as randomUUID25 } from "crypto";
|
|
45821
45827
|
import * as readline2 from "readline";
|
|
45822
45828
|
|
|
45823
45829
|
// src/commands/agent-approval-context.ts
|
|
@@ -47104,6 +47110,9 @@ function buildInteractiveAgentTools(input2) {
|
|
|
47104
47110
|
];
|
|
47105
47111
|
}
|
|
47106
47112
|
|
|
47113
|
+
// src/commands/shell-approval.ts
|
|
47114
|
+
init_command_risk();
|
|
47115
|
+
|
|
47107
47116
|
// src/lib/shell-permissions.ts
|
|
47108
47117
|
init_config_dir();
|
|
47109
47118
|
init_shell_config();
|
|
@@ -47254,6 +47263,12 @@ function validateShellPattern(pattern) {
|
|
|
47254
47263
|
reason: "touches the agent's own permission/credential files; remembering it would let one approved command disable the approval gate for every future session"
|
|
47255
47264
|
};
|
|
47256
47265
|
}
|
|
47266
|
+
if (touchesSacConfirmReview(trimmed)) {
|
|
47267
|
+
return {
|
|
47268
|
+
ok: false,
|
|
47269
|
+
reason: "touches SAC's proposal-review/confirm-token family; that guarantee depends on a human answering a real approval prompt, so it is never remembered"
|
|
47270
|
+
};
|
|
47271
|
+
}
|
|
47257
47272
|
const banned = bannedPrefixGrant(trimmed, firstToken);
|
|
47258
47273
|
if (banned !== undefined) {
|
|
47259
47274
|
return { ok: false, reason: banned.reason };
|
|
@@ -47391,6 +47406,9 @@ function isShellCommandAllowed(command, allow) {
|
|
|
47391
47406
|
if (touchesAgentCredentials(cmd)) {
|
|
47392
47407
|
return false;
|
|
47393
47408
|
}
|
|
47409
|
+
if (touchesSacConfirmReview(cmd)) {
|
|
47410
|
+
return false;
|
|
47411
|
+
}
|
|
47394
47412
|
return allow.some((pat) => matchShellPattern(pat, cmd));
|
|
47395
47413
|
}
|
|
47396
47414
|
function shellPermissionsFingerprint(dir) {
|
|
@@ -47416,12 +47434,12 @@ function suggestShellPatterns(command) {
|
|
|
47416
47434
|
const collapsed = firstLine2.replace(/\s+/g, " ").trim();
|
|
47417
47435
|
const first = collapsed.split(" ")[0] ?? collapsed;
|
|
47418
47436
|
const prefix = first.length > 0 ? `${first} *` : exact;
|
|
47419
|
-
const
|
|
47437
|
+
const neverRemember = trimmed.length > 0 && (isDestructiveCommand(trimmed) || touchesSacConfirmReview(trimmed));
|
|
47420
47438
|
return {
|
|
47421
47439
|
exact,
|
|
47422
47440
|
prefix,
|
|
47423
|
-
offerExact: !
|
|
47424
|
-
offerPrefix: !
|
|
47441
|
+
offerExact: !neverRemember && validateShellPattern(exact).ok,
|
|
47442
|
+
offerPrefix: !neverRemember && validateShellPattern(prefix).ok
|
|
47425
47443
|
};
|
|
47426
47444
|
}
|
|
47427
47445
|
function parseShellExecCommand(inputJson) {
|
|
@@ -47444,16 +47462,18 @@ function evaluateShellApproval(input2) {
|
|
|
47444
47462
|
const command = parseShellExecCommand(input2.inputJson);
|
|
47445
47463
|
const destructive = input2.meta?.destructive === true;
|
|
47446
47464
|
const credentials = input2.meta?.credentials === true;
|
|
47465
|
+
const sacReviewConfirmation = touchesSacConfirmReview(command);
|
|
47447
47466
|
const audit = io.loadAudit();
|
|
47448
47467
|
for (const pattern of audit.permissions.allow) {
|
|
47449
47468
|
input2.sessionAllow.add(pattern);
|
|
47450
47469
|
}
|
|
47451
47470
|
const tampered = io.fingerprint() !== input2.fingerprintAtStart;
|
|
47452
|
-
const autoApprove = !destructive && !credentials && isShellCommandAllowed(command, [...input2.sessionAllow]);
|
|
47471
|
+
const autoApprove = !destructive && !credentials && !sacReviewConfirmation && isShellCommandAllowed(command, [...input2.sessionAllow]);
|
|
47453
47472
|
return {
|
|
47454
47473
|
command,
|
|
47455
47474
|
destructive,
|
|
47456
47475
|
credentials,
|
|
47476
|
+
sacReviewConfirmation,
|
|
47457
47477
|
autoApprove,
|
|
47458
47478
|
rejected: audit.rejected,
|
|
47459
47479
|
tampered
|
|
@@ -47478,6 +47498,9 @@ function formatShellApprovalHints(evaled) {
|
|
|
47478
47498
|
if (evaled.credentials) {
|
|
47479
47499
|
lines.push("touches agent credentials \u2014 will not be remembered");
|
|
47480
47500
|
}
|
|
47501
|
+
if (evaled.sacReviewConfirmation) {
|
|
47502
|
+
lines.push("SAC proposal review/confirm-token \u2014 will not be remembered");
|
|
47503
|
+
}
|
|
47481
47504
|
return lines;
|
|
47482
47505
|
}
|
|
47483
47506
|
|
|
@@ -48582,7 +48605,7 @@ import { spawnSync as spawnSync2 } from "child_process";
|
|
|
48582
48605
|
// package.json
|
|
48583
48606
|
var package_default = {
|
|
48584
48607
|
name: "@mrciphersmith/keryx",
|
|
48585
|
-
version: "0.2.
|
|
48608
|
+
version: "0.2.44",
|
|
48586
48609
|
description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
|
|
48587
48610
|
private: false,
|
|
48588
48611
|
publishConfig: {
|
|
@@ -49460,6 +49483,12 @@ function onKeypress(r, handler) {
|
|
|
49460
49483
|
}
|
|
49461
49484
|
var BACKDROP_ID = "modal-backdrop";
|
|
49462
49485
|
var PANEL_ID = "modal-panel";
|
|
49486
|
+
var BACKDROP_ALPHA = 0.85;
|
|
49487
|
+
function backdropFillColor(otui) {
|
|
49488
|
+
const color = otui.RGBA.fromHex(getTheme().bg);
|
|
49489
|
+
color.a = BACKDROP_ALPHA;
|
|
49490
|
+
return color;
|
|
49491
|
+
}
|
|
49463
49492
|
var MODAL_PANEL_MARGIN = 1;
|
|
49464
49493
|
var MODAL_PANEL_CHROME_X = 4;
|
|
49465
49494
|
var MODAL_PANEL_MIN_WIDTH = 72;
|
|
@@ -49600,7 +49629,7 @@ function ensureHost(otui, chrome) {
|
|
|
49600
49629
|
left: 0,
|
|
49601
49630
|
width: "100%",
|
|
49602
49631
|
height: "100%",
|
|
49603
|
-
backgroundColor:
|
|
49632
|
+
backgroundColor: backdropFillColor(otui),
|
|
49604
49633
|
zIndex: 100,
|
|
49605
49634
|
flexDirection: "column",
|
|
49606
49635
|
justifyContent: "center",
|
|
@@ -49826,7 +49855,201 @@ function openModal(otui, chrome, input2) {
|
|
|
49826
49855
|
// src/tui/inspector-sources.ts
|
|
49827
49856
|
init_store2();
|
|
49828
49857
|
init_workspace_service();
|
|
49858
|
+
import { randomUUID as randomUUID24 } from "crypto";
|
|
49859
|
+
|
|
49860
|
+
// src/sac/catch-up.ts
|
|
49861
|
+
init_fs();
|
|
49862
|
+
init_config_dir();
|
|
49863
|
+
init_paths();
|
|
49864
|
+
init_slate();
|
|
49865
|
+
init_store3();
|
|
49866
|
+
init_proposal_lifecycle();
|
|
49867
|
+
init_workspace_service();
|
|
49868
|
+
import { randomUUID as randomUUID23 } from "crypto";
|
|
49869
|
+
import { readdir as readdir24 } from "fs/promises";
|
|
49870
|
+
import path145 from "path";
|
|
49871
|
+
|
|
49872
|
+
// src/sac/lifecycle-flag.ts
|
|
49873
|
+
init_store();
|
|
49874
|
+
init_service3();
|
|
49875
|
+
init_decision_dedup();
|
|
49876
|
+
init_workspace_service();
|
|
49829
49877
|
import { randomUUID as randomUUID22 } from "crypto";
|
|
49878
|
+
function normalize3(raw) {
|
|
49879
|
+
return raw.replace(/^\.\//, "");
|
|
49880
|
+
}
|
|
49881
|
+
function isStillPresent(recorded, valid) {
|
|
49882
|
+
const normalized = normalize3(recorded);
|
|
49883
|
+
return valid.has(normalized) || valid.has(moduleNameFromProjectPath(normalized));
|
|
49884
|
+
}
|
|
49885
|
+
async function computeLifecycleFlags(cwd, now = () => new Date) {
|
|
49886
|
+
const valid = await validModuleNames(cwd);
|
|
49887
|
+
if (valid === undefined)
|
|
49888
|
+
return [];
|
|
49889
|
+
const flaggedAt = now().toISOString();
|
|
49890
|
+
const flags = [];
|
|
49891
|
+
const workspaceService = new WorkspaceService({
|
|
49892
|
+
workspaceRoot: cwd,
|
|
49893
|
+
authorizationServer: localWorkspaceAuthorizationServer(),
|
|
49894
|
+
strictGuard: { mode: "strict", availability: "available", decision: "pass", policyRevision: "local-offline-v1" }
|
|
49895
|
+
});
|
|
49896
|
+
const workspaces = await workspaceService.list({ request: undefined, requestCorrelationId: randomUUID22(), includeArchived: true });
|
|
49897
|
+
for (const workspace of workspaces) {
|
|
49898
|
+
const component = workspace.resources.find((r) => r.kind === "component")?.uri;
|
|
49899
|
+
if (component !== undefined && !isStillPresent(component, valid)) {
|
|
49900
|
+
flags.push({ kind: "workspace", ref: workspace.id, missingComponent: normalize3(component), flaggedAt });
|
|
49901
|
+
}
|
|
49902
|
+
}
|
|
49903
|
+
for (const entry of await collectEntries(cwd)) {
|
|
49904
|
+
const module = entry.scopes.module;
|
|
49905
|
+
if (module !== null && module.length > 0 && !isStillPresent(module, valid)) {
|
|
49906
|
+
flags.push({ kind: "memory-entry", ref: entry.relativePath, missingComponent: normalize3(module), flaggedAt });
|
|
49907
|
+
}
|
|
49908
|
+
}
|
|
49909
|
+
for (const decision of await collectWikiDecisionEntries(cwd)) {
|
|
49910
|
+
const module = decision.scopes.module;
|
|
49911
|
+
if (module !== null && module.length > 0 && !isStillPresent(module, valid)) {
|
|
49912
|
+
flags.push({ kind: "wiki-decision", ref: decision.relativePath, missingComponent: normalize3(module), flaggedAt });
|
|
49913
|
+
}
|
|
49914
|
+
}
|
|
49915
|
+
return flags;
|
|
49916
|
+
}
|
|
49917
|
+
|
|
49918
|
+
// src/sac/catch-up.ts
|
|
49919
|
+
async function buildCatchUp(input2) {
|
|
49920
|
+
const [proposals, sessionCategories, lifecycleFlagsAll] = await Promise.all([
|
|
49921
|
+
collectProposals(input2.cwd, input2.workspaceId),
|
|
49922
|
+
collectSessionCategories(input2.cwd),
|
|
49923
|
+
computeLifecycleFlags(input2.cwd)
|
|
49924
|
+
]);
|
|
49925
|
+
const lifecycleFlags = input2.workspaceId === undefined ? lifecycleFlagsAll : lifecycleFlagsAll.filter((flag) => flag.kind !== "workspace" || flag.ref === input2.workspaceId);
|
|
49926
|
+
return { proposals, ...sessionCategories, lifecycleFlags };
|
|
49927
|
+
}
|
|
49928
|
+
async function collectProposals(cwd, workspaceId) {
|
|
49929
|
+
const authorizationServer = localWorkspaceAuthorizationServer();
|
|
49930
|
+
const actor = await authorizationServer.actorContextFor(undefined, randomUUID23());
|
|
49931
|
+
if (!actor)
|
|
49932
|
+
throw new Error("trusted ActorContext is required for catch-up");
|
|
49933
|
+
const proposalService = createLocalProposalLifecycleService(cwd);
|
|
49934
|
+
const groups = await proposalService.listVisibleProposedProposals(actor);
|
|
49935
|
+
const scoped = workspaceId === undefined ? groups : groups.filter((group) => group.workspace.id === workspaceId);
|
|
49936
|
+
const flattened = scoped.flatMap((group) => group.proposals.map((proposal) => ({ group, proposal })));
|
|
49937
|
+
return Promise.all(flattened.map(async ({ group, proposal }) => {
|
|
49938
|
+
const fresh = await proposalService.isEvidenceFresh(proposal, actor);
|
|
49939
|
+
return { type: "proposal", workspaceId: group.workspace.id, proposalId: proposal.id, fresh };
|
|
49940
|
+
}));
|
|
49941
|
+
}
|
|
49942
|
+
async function classifySession(session) {
|
|
49943
|
+
const dir = sessionDir(session.projectPath, session.id);
|
|
49944
|
+
if (await isLockHeld(slateLockPath(dir)))
|
|
49945
|
+
return;
|
|
49946
|
+
const terminalState = await readTerminalState(dir);
|
|
49947
|
+
if (terminalState !== undefined) {
|
|
49948
|
+
const workspaceId2 = (await safeReadSlate(dir))?.workspaceId;
|
|
49949
|
+
return { kind: "blocked", item: { type: "blocked", sessionId: session.id, ...workspaceId2 !== undefined ? { workspaceId: workspaceId2 } : {}, terminalState } };
|
|
49950
|
+
}
|
|
49951
|
+
const unboundCandidate = await readNewestUnboundCandidate(dir);
|
|
49952
|
+
if (unboundCandidate !== undefined) {
|
|
49953
|
+
return {
|
|
49954
|
+
kind: "unbound-candidate",
|
|
49955
|
+
item: { type: "unbound-candidate", sessionId: session.id, evidencePath: unboundCandidate.evidencePath, summary: unboundCandidate.summary }
|
|
49956
|
+
};
|
|
49957
|
+
}
|
|
49958
|
+
if (!await isSlateEngaged(dir))
|
|
49959
|
+
return;
|
|
49960
|
+
const workspaceId = (await safeReadSlate(dir))?.workspaceId;
|
|
49961
|
+
return { kind: "unknown", item: { type: "unknown", sessionId: session.id, ...workspaceId !== undefined ? { workspaceId } : {}, lastSeenAt: session.updatedAt } };
|
|
49962
|
+
}
|
|
49963
|
+
async function collectSessionCategories(cwd) {
|
|
49964
|
+
const classified = await Promise.all(listSessions(cwd).map((session) => classifySession(session)));
|
|
49965
|
+
const blocked2 = [];
|
|
49966
|
+
const unboundCandidates = [];
|
|
49967
|
+
const unknown = [];
|
|
49968
|
+
for (const category of classified) {
|
|
49969
|
+
if (category === undefined)
|
|
49970
|
+
continue;
|
|
49971
|
+
if (category.kind === "blocked")
|
|
49972
|
+
blocked2.push(category.item);
|
|
49973
|
+
else if (category.kind === "unbound-candidate")
|
|
49974
|
+
unboundCandidates.push(category.item);
|
|
49975
|
+
else
|
|
49976
|
+
unknown.push(category.item);
|
|
49977
|
+
}
|
|
49978
|
+
return { blocked: blocked2, unboundCandidates, unknown };
|
|
49979
|
+
}
|
|
49980
|
+
async function isSlateEngaged(dir) {
|
|
49981
|
+
if (await pathExists(path145.join(dir, "slate.json")))
|
|
49982
|
+
return true;
|
|
49983
|
+
if (await pathExists(path145.join(dir, "terminal-state.json")))
|
|
49984
|
+
return true;
|
|
49985
|
+
try {
|
|
49986
|
+
const entries = await readdir24(path145.join(dir, "slate-archive"));
|
|
49987
|
+
return entries.length > 0;
|
|
49988
|
+
} catch {
|
|
49989
|
+
return false;
|
|
49990
|
+
}
|
|
49991
|
+
}
|
|
49992
|
+
async function safeReadSlate(dir) {
|
|
49993
|
+
try {
|
|
49994
|
+
return await readSlate(dir);
|
|
49995
|
+
} catch {
|
|
49996
|
+
return;
|
|
49997
|
+
}
|
|
49998
|
+
}
|
|
49999
|
+
async function readTerminalState(dir) {
|
|
50000
|
+
const result = readConfigFile(path145.join(dir, "terminal-state.json"));
|
|
50001
|
+
if (!result.ok) {
|
|
50002
|
+
return;
|
|
50003
|
+
}
|
|
50004
|
+
try {
|
|
50005
|
+
return JSON.parse(result.text);
|
|
50006
|
+
} catch {
|
|
50007
|
+
return;
|
|
50008
|
+
}
|
|
50009
|
+
}
|
|
50010
|
+
async function readNewestUnboundCandidate(dir) {
|
|
50011
|
+
const archiveDir = path145.join(dir, "slate-archive");
|
|
50012
|
+
let entries;
|
|
50013
|
+
try {
|
|
50014
|
+
entries = (await readdir24(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
50015
|
+
} catch {
|
|
50016
|
+
return;
|
|
50017
|
+
}
|
|
50018
|
+
entries.sort();
|
|
50019
|
+
for (let i = entries.length - 1;i >= 0; i--) {
|
|
50020
|
+
const evidencePath = path145.join(archiveDir, entries[i]);
|
|
50021
|
+
const result = readConfigFile(evidencePath);
|
|
50022
|
+
if (!result.ok) {
|
|
50023
|
+
continue;
|
|
50024
|
+
}
|
|
50025
|
+
try {
|
|
50026
|
+
const parsed = JSON.parse(result.text);
|
|
50027
|
+
if (parsed.recordType !== "unbound-candidate")
|
|
50028
|
+
continue;
|
|
50029
|
+
return { evidencePath, summary: summarizeUnboundCandidate(parsed.groups) };
|
|
50030
|
+
} catch {
|
|
50031
|
+
continue;
|
|
50032
|
+
}
|
|
50033
|
+
}
|
|
50034
|
+
return;
|
|
50035
|
+
}
|
|
50036
|
+
function summarizeUnboundCandidate(groups) {
|
|
50037
|
+
const safeGroups = groups ?? [];
|
|
50038
|
+
if (safeGroups.length === 0)
|
|
50039
|
+
return "no seeds captured";
|
|
50040
|
+
const seedCount = safeGroups.reduce((sum, group) => sum + (group.seeds?.length ?? 0), 0);
|
|
50041
|
+
const kinds = safeGroups.map((group) => typeof group.kind === "string" ? group.kind : "unknown").join(", ");
|
|
50042
|
+
return `${seedCount} untriaged seed(s) across ${safeGroups.length} kind(s) (${kinds})`;
|
|
50043
|
+
}
|
|
50044
|
+
|
|
50045
|
+
// src/tui/inspector-sources.ts
|
|
50046
|
+
init_slate();
|
|
50047
|
+
|
|
50048
|
+
// src/session/index.ts
|
|
50049
|
+
init_paths();
|
|
50050
|
+
init_store3();
|
|
50051
|
+
|
|
50052
|
+
// src/tui/inspector-sources.ts
|
|
49830
50053
|
function workspaceFromManifest(manifest) {
|
|
49831
50054
|
return {
|
|
49832
50055
|
id: manifest.id,
|
|
@@ -49884,24 +50107,40 @@ function flowItemFromState(flow, dir) {
|
|
|
49884
50107
|
tasks: flow.tasks.map((task) => ({ id: task.id, title: task.title, status: task.status }))
|
|
49885
50108
|
};
|
|
49886
50109
|
}
|
|
50110
|
+
function localSacGuard() {
|
|
50111
|
+
return {
|
|
50112
|
+
mode: "strict",
|
|
50113
|
+
availability: "available",
|
|
50114
|
+
decision: "pass",
|
|
50115
|
+
policyRevision: "local-offline-v1"
|
|
50116
|
+
};
|
|
50117
|
+
}
|
|
49887
50118
|
async function loadInspectorWorkspaces(cwd) {
|
|
49888
50119
|
try {
|
|
49889
50120
|
const service5 = new WorkspaceService({
|
|
49890
50121
|
workspaceRoot: cwd,
|
|
49891
50122
|
authorizationServer: localWorkspaceAuthorizationServer(),
|
|
49892
|
-
strictGuard:
|
|
49893
|
-
mode: "strict",
|
|
49894
|
-
availability: "available",
|
|
49895
|
-
decision: "pass",
|
|
49896
|
-
policyRevision: "local-offline-v1"
|
|
49897
|
-
}
|
|
50123
|
+
strictGuard: localSacGuard()
|
|
49898
50124
|
});
|
|
49899
|
-
const listed = await service5.list({ request: undefined, requestCorrelationId:
|
|
50125
|
+
const listed = await service5.list({ request: undefined, requestCorrelationId: randomUUID24() });
|
|
49900
50126
|
return listed.map(workspaceFromManifest);
|
|
49901
50127
|
} catch {
|
|
49902
50128
|
return [];
|
|
49903
50129
|
}
|
|
49904
50130
|
}
|
|
50131
|
+
async function loadInspectorWorkspace(cwd, workspaceId) {
|
|
50132
|
+
try {
|
|
50133
|
+
const service5 = new WorkspaceService({
|
|
50134
|
+
workspaceRoot: cwd,
|
|
50135
|
+
authorizationServer: localWorkspaceAuthorizationServer(),
|
|
50136
|
+
strictGuard: localSacGuard()
|
|
50137
|
+
});
|
|
50138
|
+
const manifest = await service5.show({ request: undefined, requestCorrelationId: randomUUID24(), workspaceId });
|
|
50139
|
+
return workspaceFromManifest(manifest);
|
|
50140
|
+
} catch {
|
|
50141
|
+
return;
|
|
50142
|
+
}
|
|
50143
|
+
}
|
|
49905
50144
|
function sortFlowsNewestFirst(items) {
|
|
49906
50145
|
return [...items].sort((left, right) => {
|
|
49907
50146
|
const byId = Number(right.id) - Number(left.id);
|
|
@@ -49928,6 +50167,40 @@ async function loadInspectorFlows(cwd) {
|
|
|
49928
50167
|
return [];
|
|
49929
50168
|
}
|
|
49930
50169
|
}
|
|
50170
|
+
function sortSlatesNewestFirst(items) {
|
|
50171
|
+
return [...items].sort((left, right) => left.updatedAt < right.updatedAt ? 1 : left.updatedAt > right.updatedAt ? -1 : 0);
|
|
50172
|
+
}
|
|
50173
|
+
async function loadInspectorSlates(cwd, workspaceId) {
|
|
50174
|
+
try {
|
|
50175
|
+
const sessions = listSessions(cwd);
|
|
50176
|
+
const items = [];
|
|
50177
|
+
for (const session of sessions) {
|
|
50178
|
+
const dir = sessionDir(session.projectPath, session.id);
|
|
50179
|
+
let slate;
|
|
50180
|
+
try {
|
|
50181
|
+
slate = await readSlate(dir);
|
|
50182
|
+
} catch {
|
|
50183
|
+
continue;
|
|
50184
|
+
}
|
|
50185
|
+
if (slate === undefined || slate.workspaceId !== workspaceId) {
|
|
50186
|
+
continue;
|
|
50187
|
+
}
|
|
50188
|
+
items.push({
|
|
50189
|
+
sessionId: session.id,
|
|
50190
|
+
sessionTitle: session.title,
|
|
50191
|
+
updatedAt: session.updatedAt,
|
|
50192
|
+
courseStatus: session.courseStatus ?? "unbound",
|
|
50193
|
+
...slate.course.flowRef !== undefined ? { flowRef: slate.course.flowRef } : {},
|
|
50194
|
+
seedCount: slate.seeds.length,
|
|
50195
|
+
touchedFiles: slate.anchors.touched,
|
|
50196
|
+
seeds: slate.seeds
|
|
50197
|
+
});
|
|
50198
|
+
}
|
|
50199
|
+
return sortSlatesNewestFirst(items);
|
|
50200
|
+
} catch {
|
|
50201
|
+
return [];
|
|
50202
|
+
}
|
|
50203
|
+
}
|
|
49931
50204
|
function formatWorkspaceLines(workspaces) {
|
|
49932
50205
|
if (workspaces.length === 0) {
|
|
49933
50206
|
return ["No workspaces recorded in this session."];
|
|
@@ -49935,6 +50208,16 @@ function formatWorkspaceLines(workspaces) {
|
|
|
49935
50208
|
const width = workspaces.reduce((max, workspace) => Math.max(max, workspace.id.length), 0);
|
|
49936
50209
|
return workspaces.map((workspace) => `${workspace.id.padEnd(width)} ${workspace.status} ${workspace.title}`);
|
|
49937
50210
|
}
|
|
50211
|
+
async function loadInspectorCatchUp(cwd) {
|
|
50212
|
+
try {
|
|
50213
|
+
return await buildCatchUp({ cwd });
|
|
50214
|
+
} catch {
|
|
50215
|
+
return { proposals: [], blocked: [], unboundCandidates: [], unknown: [], lifecycleFlags: [] };
|
|
50216
|
+
}
|
|
50217
|
+
}
|
|
50218
|
+
function catchUpItems(report) {
|
|
50219
|
+
return [...report.proposals, ...report.blocked, ...report.unboundCandidates, ...report.unknown];
|
|
50220
|
+
}
|
|
49938
50221
|
function formatSessionFlowLines(flows) {
|
|
49939
50222
|
if (flows.length === 0) {
|
|
49940
50223
|
return ["No flows recorded in this session."];
|
|
@@ -50177,6 +50460,585 @@ function openFlows(otui, chrome, options) {
|
|
|
50177
50460
|
return presentFlows((hostOtui, hostChrome, input2) => openModal(hostOtui, hostChrome, input2), otui, chrome, options);
|
|
50178
50461
|
}
|
|
50179
50462
|
|
|
50463
|
+
// src/tui/workspace-inspector.ts
|
|
50464
|
+
var WORKSPACE_COMMAND = "/workspace";
|
|
50465
|
+
var WORKSPACE_FOOTER = [
|
|
50466
|
+
{ key: "[/]", label: "slate" },
|
|
50467
|
+
{ key: "\u2191/\u2193", label: "scroll" },
|
|
50468
|
+
{ key: "\u2190/\u2192", label: "tabs" },
|
|
50469
|
+
{ key: "esc", label: "close" }
|
|
50470
|
+
];
|
|
50471
|
+
function isWorkspaceCommand(line) {
|
|
50472
|
+
const token = line.trim().split(/\s+/)[0] ?? "";
|
|
50473
|
+
return token === WORKSPACE_COMMAND;
|
|
50474
|
+
}
|
|
50475
|
+
function formatWorkspaceOverviewLines(workspace, slateCount) {
|
|
50476
|
+
const resourceLines = workspace.resources.length === 0 ? [" (no resources)"] : workspace.resources.map((resource) => ` ${resource.kind.padEnd(10)} ${resource.uri}`);
|
|
50477
|
+
return [
|
|
50478
|
+
`${workspace.id} ${workspace.title}`,
|
|
50479
|
+
`Status ${workspace.status}`,
|
|
50480
|
+
`Slates ${slateCount}`,
|
|
50481
|
+
"",
|
|
50482
|
+
"Resources",
|
|
50483
|
+
...resourceLines
|
|
50484
|
+
];
|
|
50485
|
+
}
|
|
50486
|
+
function formatSlateListLines(items, selected) {
|
|
50487
|
+
if (items.length === 0) {
|
|
50488
|
+
return ["No slates bound to this workspace yet."];
|
|
50489
|
+
}
|
|
50490
|
+
return items.map((item, index) => {
|
|
50491
|
+
const mark = index === selected ? ">" : " ";
|
|
50492
|
+
const flow = item.flowRef !== undefined ? `flow ${item.flowRef}` : "no flow";
|
|
50493
|
+
return `${mark} ${item.sessionId.slice(0, 8)} ${item.courseStatus.padEnd(8)} ${item.seedCount} seeds ${flow} ${item.sessionTitle}`;
|
|
50494
|
+
});
|
|
50495
|
+
}
|
|
50496
|
+
function formatSlateDetailLines(item) {
|
|
50497
|
+
if (item === undefined) {
|
|
50498
|
+
return ["No slate selected.", "", "Press Enter (or click a row) on the Slates tab to view one."];
|
|
50499
|
+
}
|
|
50500
|
+
const touchedLines = item.touchedFiles.length === 0 ? [" (none)"] : item.touchedFiles.map((file) => ` ${file}`);
|
|
50501
|
+
const seedLines = item.seeds.length === 0 ? [" (no seeds)"] : item.seeds.map((seed) => ` ${seed.ts} ${(seed.kind ?? "note").padEnd(14)} ${seed.text}`);
|
|
50502
|
+
return [
|
|
50503
|
+
`${item.sessionId} ${item.sessionTitle}`,
|
|
50504
|
+
`Course ${item.courseStatus}`,
|
|
50505
|
+
`Flow ${item.flowRef ?? "\u2014"}`,
|
|
50506
|
+
`Updated ${item.updatedAt}`,
|
|
50507
|
+
"",
|
|
50508
|
+
"Touched files",
|
|
50509
|
+
...touchedLines,
|
|
50510
|
+
"",
|
|
50511
|
+
"Seeds",
|
|
50512
|
+
...seedLines
|
|
50513
|
+
];
|
|
50514
|
+
}
|
|
50515
|
+
function clampScroll2(offset, lineCount, height) {
|
|
50516
|
+
const max = Math.max(0, lineCount - height);
|
|
50517
|
+
return Math.min(max, Math.max(0, offset));
|
|
50518
|
+
}
|
|
50519
|
+
function windowLines2(lines, offset, height) {
|
|
50520
|
+
if (height < 1) {
|
|
50521
|
+
return [];
|
|
50522
|
+
}
|
|
50523
|
+
const start = clampScroll2(offset, lines.length, height);
|
|
50524
|
+
return lines.slice(start, start + height);
|
|
50525
|
+
}
|
|
50526
|
+
function scrollToReveal2(index, offset, height) {
|
|
50527
|
+
if (index < offset) {
|
|
50528
|
+
return index;
|
|
50529
|
+
}
|
|
50530
|
+
if (index >= offset + height) {
|
|
50531
|
+
return index - height + 1;
|
|
50532
|
+
}
|
|
50533
|
+
return offset;
|
|
50534
|
+
}
|
|
50535
|
+
function wrapLines2(text, width) {
|
|
50536
|
+
if (width === undefined || width < 8) {
|
|
50537
|
+
return text;
|
|
50538
|
+
}
|
|
50539
|
+
return text.split(`
|
|
50540
|
+
`).flatMap((line) => {
|
|
50541
|
+
if (line.length <= width) {
|
|
50542
|
+
return [line];
|
|
50543
|
+
}
|
|
50544
|
+
const chunks = [];
|
|
50545
|
+
for (let i = 0;i < line.length; i += width) {
|
|
50546
|
+
chunks.push(line.slice(i, i + width));
|
|
50547
|
+
}
|
|
50548
|
+
return chunks;
|
|
50549
|
+
}).join(`
|
|
50550
|
+
`);
|
|
50551
|
+
}
|
|
50552
|
+
function paintLines2(otui, renderer, body, lines, width, idPrefix = "workspace") {
|
|
50553
|
+
if (otui === undefined || otui === null || body === undefined || body === null) {
|
|
50554
|
+
return;
|
|
50555
|
+
}
|
|
50556
|
+
const parent = body;
|
|
50557
|
+
const ctor = otui.TextRenderable;
|
|
50558
|
+
if (parent.add === undefined || ctor === undefined) {
|
|
50559
|
+
return;
|
|
50560
|
+
}
|
|
50561
|
+
const node = new ctor(renderer, { id: `${idPrefix}-body`, content: wrapLines2(lines.join(`
|
|
50562
|
+
`), width) });
|
|
50563
|
+
parent.add(node);
|
|
50564
|
+
return node;
|
|
50565
|
+
}
|
|
50566
|
+
function presentWorkspace(openModal2, otui, chrome, options) {
|
|
50567
|
+
const items = sortSlatesNewestFirst(options.slates);
|
|
50568
|
+
let selected = 0;
|
|
50569
|
+
let openIndex;
|
|
50570
|
+
let overviewScroll = 0;
|
|
50571
|
+
let listScroll = 0;
|
|
50572
|
+
let detailScroll = 0;
|
|
50573
|
+
let overviewNode;
|
|
50574
|
+
let listNode;
|
|
50575
|
+
let detailNode;
|
|
50576
|
+
let unsubscribeKey;
|
|
50577
|
+
const rendererHint = options.renderer ?? chrome?.renderer;
|
|
50578
|
+
const bodyRows = options.visibleRows ?? (typeof rendererHint?.width === "number" && typeof rendererHint.height === "number" ? modalBodyRows(resolveModalPanelSize(rendererHint.width, rendererHint.height).height) : 13);
|
|
50579
|
+
let tabWidth;
|
|
50580
|
+
const overviewLines = () => wrapLines2(formatWorkspaceOverviewLines(options.workspace, items.length).join(`
|
|
50581
|
+
`), tabWidth).split(`
|
|
50582
|
+
`);
|
|
50583
|
+
const listLines = () => formatSlateListLines(items, selected);
|
|
50584
|
+
const detailLines = () => {
|
|
50585
|
+
const item = openIndex !== undefined ? items[openIndex] : undefined;
|
|
50586
|
+
return wrapLines2(formatSlateDetailLines(item).join(`
|
|
50587
|
+
`), tabWidth).split(`
|
|
50588
|
+
`);
|
|
50589
|
+
};
|
|
50590
|
+
const paintSelection = () => {
|
|
50591
|
+
listScroll = scrollToReveal2(selected, listScroll, bodyRows);
|
|
50592
|
+
listScroll = clampScroll2(listScroll, items.length, bodyRows);
|
|
50593
|
+
detailScroll = clampScroll2(detailScroll, detailLines().length, bodyRows);
|
|
50594
|
+
overviewScroll = clampScroll2(overviewScroll, overviewLines().length, bodyRows);
|
|
50595
|
+
if (overviewNode !== undefined) {
|
|
50596
|
+
overviewNode.content = windowLines2(overviewLines(), overviewScroll, bodyRows).join(`
|
|
50597
|
+
`);
|
|
50598
|
+
}
|
|
50599
|
+
if (listNode !== undefined) {
|
|
50600
|
+
listNode.content = windowLines2(listLines(), listScroll, bodyRows).join(`
|
|
50601
|
+
`);
|
|
50602
|
+
}
|
|
50603
|
+
if (detailNode !== undefined) {
|
|
50604
|
+
detailNode.content = windowLines2(detailLines(), detailScroll, bodyRows).join(`
|
|
50605
|
+
`);
|
|
50606
|
+
}
|
|
50607
|
+
};
|
|
50608
|
+
const moveSelection = (next) => {
|
|
50609
|
+
if (items.length === 0) {
|
|
50610
|
+
return;
|
|
50611
|
+
}
|
|
50612
|
+
const clamped = Math.min(items.length - 1, Math.max(0, next));
|
|
50613
|
+
if (clamped === selected) {
|
|
50614
|
+
return;
|
|
50615
|
+
}
|
|
50616
|
+
selected = clamped;
|
|
50617
|
+
paintSelection();
|
|
50618
|
+
};
|
|
50619
|
+
const openSelected = () => {
|
|
50620
|
+
if (items.length === 0) {
|
|
50621
|
+
return;
|
|
50622
|
+
}
|
|
50623
|
+
openIndex = selected;
|
|
50624
|
+
detailScroll = 0;
|
|
50625
|
+
handle?.setTab("detail");
|
|
50626
|
+
paintSelection();
|
|
50627
|
+
};
|
|
50628
|
+
const handle = openModal2(otui, chrome, {
|
|
50629
|
+
title: WORKSPACE_COMMAND,
|
|
50630
|
+
tabs: [
|
|
50631
|
+
{ id: "overview", label: "Workspace" },
|
|
50632
|
+
{ id: "slates", label: "Slates" },
|
|
50633
|
+
{ id: "detail", label: "Slate" }
|
|
50634
|
+
],
|
|
50635
|
+
initialTab: "overview",
|
|
50636
|
+
footer: WORKSPACE_FOOTER,
|
|
50637
|
+
renderTab: (tabId, body, ctx) => {
|
|
50638
|
+
const renderer = options.renderer ?? chrome?.renderer;
|
|
50639
|
+
tabWidth = ctx?.width;
|
|
50640
|
+
if (tabId === "overview") {
|
|
50641
|
+
overviewScroll = clampScroll2(overviewScroll, overviewLines().length, bodyRows);
|
|
50642
|
+
overviewNode = paintLines2(otui, renderer, body, windowLines2(overviewLines(), overviewScroll, bodyRows), undefined, "workspace-overview");
|
|
50643
|
+
return;
|
|
50644
|
+
}
|
|
50645
|
+
if (tabId === "slates") {
|
|
50646
|
+
listScroll = scrollToReveal2(selected, listScroll, bodyRows);
|
|
50647
|
+
listNode = paintLines2(otui, renderer, body, windowLines2(listLines(), listScroll, bodyRows), undefined, "workspace-slates");
|
|
50648
|
+
return;
|
|
50649
|
+
}
|
|
50650
|
+
detailScroll = clampScroll2(detailScroll, detailLines().length, bodyRows);
|
|
50651
|
+
detailNode = paintLines2(otui, renderer, body, windowLines2(detailLines(), detailScroll, bodyRows), tabWidth, "workspace-detail");
|
|
50652
|
+
return () => {
|
|
50653
|
+
openIndex = undefined;
|
|
50654
|
+
};
|
|
50655
|
+
},
|
|
50656
|
+
onClose: () => {
|
|
50657
|
+
unsubscribeKey?.();
|
|
50658
|
+
}
|
|
50659
|
+
});
|
|
50660
|
+
if (handle === undefined) {
|
|
50661
|
+
return;
|
|
50662
|
+
}
|
|
50663
|
+
if (options.onKeypress !== undefined) {
|
|
50664
|
+
unsubscribeKey = options.onKeypress((key) => {
|
|
50665
|
+
const token = key.name || key.sequence;
|
|
50666
|
+
const active = handle.activeTab();
|
|
50667
|
+
if (active === "slates") {
|
|
50668
|
+
if (token === "[" || token === "p" || token === "up" || token === "k") {
|
|
50669
|
+
moveSelection(selected - 1);
|
|
50670
|
+
return;
|
|
50671
|
+
}
|
|
50672
|
+
if (token === "]" || token === "n" || token === "down" || token === "j") {
|
|
50673
|
+
moveSelection(selected + 1);
|
|
50674
|
+
return;
|
|
50675
|
+
}
|
|
50676
|
+
if (token === "return" || token === "enter") {
|
|
50677
|
+
openSelected();
|
|
50678
|
+
}
|
|
50679
|
+
return;
|
|
50680
|
+
}
|
|
50681
|
+
const scrollable = active === "detail" ? detailLines : overviewLines;
|
|
50682
|
+
const setScroll = (value) => {
|
|
50683
|
+
if (active === "detail") {
|
|
50684
|
+
detailScroll = value;
|
|
50685
|
+
} else {
|
|
50686
|
+
overviewScroll = value;
|
|
50687
|
+
}
|
|
50688
|
+
};
|
|
50689
|
+
const current = active === "detail" ? detailScroll : overviewScroll;
|
|
50690
|
+
if (token === "up" || token === "k") {
|
|
50691
|
+
setScroll(clampScroll2(current - 1, scrollable().length, bodyRows));
|
|
50692
|
+
paintSelection();
|
|
50693
|
+
return;
|
|
50694
|
+
}
|
|
50695
|
+
if (token === "down" || token === "j") {
|
|
50696
|
+
setScroll(clampScroll2(current + 1, scrollable().length, bodyRows));
|
|
50697
|
+
paintSelection();
|
|
50698
|
+
return;
|
|
50699
|
+
}
|
|
50700
|
+
if (token === "pageup" || token === "pagedown") {
|
|
50701
|
+
const step = token === "pageup" ? -bodyRows : bodyRows;
|
|
50702
|
+
setScroll(clampScroll2(current + step, scrollable().length, bodyRows));
|
|
50703
|
+
paintSelection();
|
|
50704
|
+
}
|
|
50705
|
+
});
|
|
50706
|
+
}
|
|
50707
|
+
return handle;
|
|
50708
|
+
}
|
|
50709
|
+
function openWorkspace(otui, chrome, options) {
|
|
50710
|
+
return presentWorkspace((hostOtui, hostChrome, input2) => openModal(hostOtui, hostChrome, input2), otui, chrome, options);
|
|
50711
|
+
}
|
|
50712
|
+
|
|
50713
|
+
// src/tui/review-inspector.ts
|
|
50714
|
+
var REVIEW_COMMAND = "/review";
|
|
50715
|
+
var REVIEW_FOOTER = [
|
|
50716
|
+
{ key: "[/]", label: "item" },
|
|
50717
|
+
{ key: "a y", label: "accept" },
|
|
50718
|
+
{ key: "\u2191/\u2193", label: "scroll" },
|
|
50719
|
+
{ key: "\u2190/\u2192", label: "tabs" },
|
|
50720
|
+
{ key: "esc", label: "close" }
|
|
50721
|
+
];
|
|
50722
|
+
function isReviewCommand(line) {
|
|
50723
|
+
const token = line.trim().split(/\s+/)[0] ?? "";
|
|
50724
|
+
return token === REVIEW_COMMAND;
|
|
50725
|
+
}
|
|
50726
|
+
var TYPE_LABEL = {
|
|
50727
|
+
proposal: "PROPOSAL",
|
|
50728
|
+
blocked: "BLOCKED",
|
|
50729
|
+
"unbound-candidate": "UNBOUND",
|
|
50730
|
+
unknown: "UNKNOWN"
|
|
50731
|
+
};
|
|
50732
|
+
function summarizeReviewItem(item) {
|
|
50733
|
+
switch (item.type) {
|
|
50734
|
+
case "proposal":
|
|
50735
|
+
return `${item.proposalId} in ${item.workspaceId}${item.fresh ? "" : " (stale)"}`;
|
|
50736
|
+
case "blocked":
|
|
50737
|
+
return `${item.sessionId} \u2014 ${item.terminalState.reason}`;
|
|
50738
|
+
case "unbound-candidate":
|
|
50739
|
+
return `${item.sessionId} \u2014 ${item.summary}`;
|
|
50740
|
+
case "unknown":
|
|
50741
|
+
return `${item.sessionId} \u2014 last seen ${item.lastSeenAt}`;
|
|
50742
|
+
}
|
|
50743
|
+
}
|
|
50744
|
+
function formatReviewListLines(items, selected) {
|
|
50745
|
+
if (items.length === 0) {
|
|
50746
|
+
return ["Nothing needs review right now."];
|
|
50747
|
+
}
|
|
50748
|
+
return items.map((item, index) => {
|
|
50749
|
+
const mark = index === selected ? ">" : " ";
|
|
50750
|
+
return `${mark} ${TYPE_LABEL[item.type].padEnd(8)} ${summarizeReviewItem(item)}`;
|
|
50751
|
+
});
|
|
50752
|
+
}
|
|
50753
|
+
function describeReviewItem(item) {
|
|
50754
|
+
switch (item.type) {
|
|
50755
|
+
case "proposal":
|
|
50756
|
+
return [
|
|
50757
|
+
`Proposal ${item.proposalId}`,
|
|
50758
|
+
`Workspace ${item.workspaceId}`,
|
|
50759
|
+
`Evidence ${item.fresh ? "fresh" : "stale \u2014 evidence has drifted since this proposal was created; re-run wrap-up before deciding"}`,
|
|
50760
|
+
"",
|
|
50761
|
+
`Reject/dismiss from a terminal: keryx workspace review ${item.workspaceId} ${item.proposalId} --decision <rejected|dismissed>`
|
|
50762
|
+
];
|
|
50763
|
+
case "blocked":
|
|
50764
|
+
return [
|
|
50765
|
+
`Session ${item.sessionId}${item.workspaceId !== undefined ? ` (workspace ${item.workspaceId})` : ""}`,
|
|
50766
|
+
`Stopped unattended: ${item.terminalState.reason}`,
|
|
50767
|
+
`Occurred ${item.terminalState.occurredAt}`,
|
|
50768
|
+
"",
|
|
50769
|
+
`Resume: keryx shell -r ${item.sessionId}`
|
|
50770
|
+
];
|
|
50771
|
+
case "unbound-candidate":
|
|
50772
|
+
return [
|
|
50773
|
+
`Session ${item.sessionId}`,
|
|
50774
|
+
`Untriaged seeds: ${item.summary}`,
|
|
50775
|
+
`Evidence ${item.evidencePath}`,
|
|
50776
|
+
"",
|
|
50777
|
+
`Bind: keryx workspace propose <workspace-id> --kind <kind> --session ${item.sessionId}`
|
|
50778
|
+
];
|
|
50779
|
+
case "unknown":
|
|
50780
|
+
return [
|
|
50781
|
+
`Session ${item.sessionId}${item.workspaceId !== undefined ? ` (workspace ${item.workspaceId})` : ""}`,
|
|
50782
|
+
`Last seen ${item.lastSeenAt}`,
|
|
50783
|
+
"No proposal, terminal state, or unbound-candidate artifact recorded.",
|
|
50784
|
+
"",
|
|
50785
|
+
`Investigate: keryx sessions list / keryx shell -r ${item.sessionId}`
|
|
50786
|
+
];
|
|
50787
|
+
}
|
|
50788
|
+
}
|
|
50789
|
+
function formatReviewDetailLines(item, status) {
|
|
50790
|
+
if (item === undefined) {
|
|
50791
|
+
return ["No item selected.", "", "Press Enter (or click a row) on the Review tab to view one."];
|
|
50792
|
+
}
|
|
50793
|
+
const lines = describeReviewItem(item);
|
|
50794
|
+
if (item.type !== "proposal") {
|
|
50795
|
+
return lines;
|
|
50796
|
+
}
|
|
50797
|
+
const withAction = [...lines, ""];
|
|
50798
|
+
if (status.kind === "armed") {
|
|
50799
|
+
withAction.push("Press [y] to CONFIRM accept, any other key cancels.");
|
|
50800
|
+
} else if (status.kind === "running") {
|
|
50801
|
+
withAction.push("Accepting\u2026 running `keryx workspace confirm-review` then `keryx workspace review`.");
|
|
50802
|
+
} else if (status.kind === "done" && status.outcome.ok) {
|
|
50803
|
+
withAction.push("\u2713 Accepted.");
|
|
50804
|
+
} else if (status.kind === "done" && !status.outcome.ok) {
|
|
50805
|
+
withAction.push(`\u2717 Accept failed: ${status.outcome.message}`);
|
|
50806
|
+
} else {
|
|
50807
|
+
withAction.push("[a] Accept this proposal");
|
|
50808
|
+
}
|
|
50809
|
+
return withAction;
|
|
50810
|
+
}
|
|
50811
|
+
function clampScroll3(offset, lineCount, height) {
|
|
50812
|
+
const max = Math.max(0, lineCount - height);
|
|
50813
|
+
return Math.min(max, Math.max(0, offset));
|
|
50814
|
+
}
|
|
50815
|
+
function windowLines3(lines, offset, height) {
|
|
50816
|
+
if (height < 1) {
|
|
50817
|
+
return [];
|
|
50818
|
+
}
|
|
50819
|
+
const start = clampScroll3(offset, lines.length, height);
|
|
50820
|
+
return lines.slice(start, start + height);
|
|
50821
|
+
}
|
|
50822
|
+
function scrollToReveal3(index, offset, height) {
|
|
50823
|
+
if (index < offset) {
|
|
50824
|
+
return index;
|
|
50825
|
+
}
|
|
50826
|
+
if (index >= offset + height) {
|
|
50827
|
+
return index - height + 1;
|
|
50828
|
+
}
|
|
50829
|
+
return offset;
|
|
50830
|
+
}
|
|
50831
|
+
function wrapLines3(text, width) {
|
|
50832
|
+
if (width === undefined || width < 8) {
|
|
50833
|
+
return text;
|
|
50834
|
+
}
|
|
50835
|
+
return text.split(`
|
|
50836
|
+
`).flatMap((line) => {
|
|
50837
|
+
if (line.length <= width) {
|
|
50838
|
+
return [line];
|
|
50839
|
+
}
|
|
50840
|
+
const chunks = [];
|
|
50841
|
+
for (let i = 0;i < line.length; i += width) {
|
|
50842
|
+
chunks.push(line.slice(i, i + width));
|
|
50843
|
+
}
|
|
50844
|
+
return chunks;
|
|
50845
|
+
}).join(`
|
|
50846
|
+
`);
|
|
50847
|
+
}
|
|
50848
|
+
function paintLines3(otui, renderer, body, lines, width) {
|
|
50849
|
+
if (otui === undefined || otui === null || body === undefined || body === null) {
|
|
50850
|
+
return;
|
|
50851
|
+
}
|
|
50852
|
+
const parent = body;
|
|
50853
|
+
const ctor = otui.TextRenderable;
|
|
50854
|
+
if (parent.add === undefined || ctor === undefined) {
|
|
50855
|
+
return;
|
|
50856
|
+
}
|
|
50857
|
+
const node = new ctor(renderer, { id: "review-body", content: wrapLines3(lines.join(`
|
|
50858
|
+
`), width) });
|
|
50859
|
+
parent.add(node);
|
|
50860
|
+
return node;
|
|
50861
|
+
}
|
|
50862
|
+
function presentReview(openModal2, otui, chrome, options) {
|
|
50863
|
+
const items = [...options.items];
|
|
50864
|
+
let selected = 0;
|
|
50865
|
+
let listScroll = 0;
|
|
50866
|
+
let detailScroll = 0;
|
|
50867
|
+
let status = { kind: "idle" };
|
|
50868
|
+
let listNode;
|
|
50869
|
+
let detailNode;
|
|
50870
|
+
let unsubscribeKey;
|
|
50871
|
+
const rendererHint = options.renderer ?? chrome?.renderer;
|
|
50872
|
+
const bodyRows = options.visibleRows ?? (typeof rendererHint?.width === "number" && typeof rendererHint.height === "number" ? modalBodyRows(resolveModalPanelSize(rendererHint.width, rendererHint.height).height) : 13);
|
|
50873
|
+
let tabWidth;
|
|
50874
|
+
const listLines = () => formatReviewListLines(items, selected);
|
|
50875
|
+
const detailLines = () => wrapLines3(formatReviewDetailLines(items[selected], status).join(`
|
|
50876
|
+
`), tabWidth).split(`
|
|
50877
|
+
`);
|
|
50878
|
+
const paintSelection = () => {
|
|
50879
|
+
listScroll = scrollToReveal3(selected, listScroll, bodyRows);
|
|
50880
|
+
listScroll = clampScroll3(listScroll, items.length, bodyRows);
|
|
50881
|
+
detailScroll = clampScroll3(detailScroll, detailLines().length, bodyRows);
|
|
50882
|
+
if (listNode !== undefined) {
|
|
50883
|
+
listNode.content = windowLines3(listLines(), listScroll, bodyRows).join(`
|
|
50884
|
+
`);
|
|
50885
|
+
}
|
|
50886
|
+
if (detailNode !== undefined) {
|
|
50887
|
+
detailNode.content = windowLines3(detailLines(), detailScroll, bodyRows).join(`
|
|
50888
|
+
`);
|
|
50889
|
+
}
|
|
50890
|
+
};
|
|
50891
|
+
const moveSelection = (next) => {
|
|
50892
|
+
if (items.length === 0) {
|
|
50893
|
+
return;
|
|
50894
|
+
}
|
|
50895
|
+
const clamped = Math.min(items.length - 1, Math.max(0, next));
|
|
50896
|
+
if (clamped === selected) {
|
|
50897
|
+
return;
|
|
50898
|
+
}
|
|
50899
|
+
selected = clamped;
|
|
50900
|
+
detailScroll = 0;
|
|
50901
|
+
status = { kind: "idle" };
|
|
50902
|
+
paintSelection();
|
|
50903
|
+
};
|
|
50904
|
+
const runAccept = () => {
|
|
50905
|
+
const item = items[selected];
|
|
50906
|
+
if (item === undefined || item.type !== "proposal" || options.acceptProposal === undefined) {
|
|
50907
|
+
return;
|
|
50908
|
+
}
|
|
50909
|
+
status = { kind: "running" };
|
|
50910
|
+
paintSelection();
|
|
50911
|
+
options.acceptProposal(item).then((outcome) => {
|
|
50912
|
+
status = { kind: "done", outcome };
|
|
50913
|
+
if (outcome.ok) {
|
|
50914
|
+
options.onAccepted?.(item);
|
|
50915
|
+
}
|
|
50916
|
+
paintSelection();
|
|
50917
|
+
});
|
|
50918
|
+
};
|
|
50919
|
+
const handle = openModal2(otui, chrome, {
|
|
50920
|
+
title: REVIEW_COMMAND,
|
|
50921
|
+
tabs: [
|
|
50922
|
+
{ id: "list", label: "Review" },
|
|
50923
|
+
{ id: "detail", label: "Detail" }
|
|
50924
|
+
],
|
|
50925
|
+
initialTab: "list",
|
|
50926
|
+
footer: REVIEW_FOOTER,
|
|
50927
|
+
renderTab: (tabId, body, ctx) => {
|
|
50928
|
+
const renderer = options.renderer ?? chrome?.renderer;
|
|
50929
|
+
tabWidth = ctx?.width;
|
|
50930
|
+
if (tabId === "list") {
|
|
50931
|
+
listScroll = scrollToReveal3(selected, listScroll, bodyRows);
|
|
50932
|
+
listNode = paintLines3(otui, renderer, body, windowLines3(listLines(), listScroll, bodyRows));
|
|
50933
|
+
return;
|
|
50934
|
+
}
|
|
50935
|
+
detailScroll = clampScroll3(detailScroll, detailLines().length, bodyRows);
|
|
50936
|
+
detailNode = paintLines3(otui, renderer, body, windowLines3(detailLines(), detailScroll, bodyRows), tabWidth);
|
|
50937
|
+
},
|
|
50938
|
+
onClose: () => {
|
|
50939
|
+
unsubscribeKey?.();
|
|
50940
|
+
}
|
|
50941
|
+
});
|
|
50942
|
+
if (handle === undefined) {
|
|
50943
|
+
return;
|
|
50944
|
+
}
|
|
50945
|
+
if (options.onKeypress !== undefined) {
|
|
50946
|
+
unsubscribeKey = options.onKeypress((key) => {
|
|
50947
|
+
const token = key.name || key.sequence;
|
|
50948
|
+
if (items.length === 0) {
|
|
50949
|
+
return;
|
|
50950
|
+
}
|
|
50951
|
+
const onDetail = handle.activeTab() === "detail";
|
|
50952
|
+
if (onDetail && status.kind === "armed") {
|
|
50953
|
+
if (token === "y") {
|
|
50954
|
+
runAccept();
|
|
50955
|
+
} else {
|
|
50956
|
+
status = { kind: "idle" };
|
|
50957
|
+
paintSelection();
|
|
50958
|
+
}
|
|
50959
|
+
return;
|
|
50960
|
+
}
|
|
50961
|
+
if (token === "[" || token === "p") {
|
|
50962
|
+
moveSelection(selected - 1);
|
|
50963
|
+
return;
|
|
50964
|
+
}
|
|
50965
|
+
if (token === "]" || token === "n") {
|
|
50966
|
+
moveSelection(selected + 1);
|
|
50967
|
+
return;
|
|
50968
|
+
}
|
|
50969
|
+
if (token === "return" || token === "enter") {
|
|
50970
|
+
handle.setTab("detail");
|
|
50971
|
+
return;
|
|
50972
|
+
}
|
|
50973
|
+
if (onDetail && token === "a" && items[selected]?.type === "proposal" && options.acceptProposal !== undefined && status.kind !== "running") {
|
|
50974
|
+
status = { kind: "armed" };
|
|
50975
|
+
paintSelection();
|
|
50976
|
+
return;
|
|
50977
|
+
}
|
|
50978
|
+
if (token === "up" || token === "k") {
|
|
50979
|
+
if (onDetail) {
|
|
50980
|
+
detailScroll = clampScroll3(detailScroll - 1, detailLines().length, bodyRows);
|
|
50981
|
+
paintSelection();
|
|
50982
|
+
} else {
|
|
50983
|
+
moveSelection(selected - 1);
|
|
50984
|
+
}
|
|
50985
|
+
return;
|
|
50986
|
+
}
|
|
50987
|
+
if (token === "down" || token === "j") {
|
|
50988
|
+
if (onDetail) {
|
|
50989
|
+
detailScroll = clampScroll3(detailScroll + 1, detailLines().length, bodyRows);
|
|
50990
|
+
paintSelection();
|
|
50991
|
+
} else {
|
|
50992
|
+
moveSelection(selected + 1);
|
|
50993
|
+
}
|
|
50994
|
+
return;
|
|
50995
|
+
}
|
|
50996
|
+
if (onDetail && (token === "pageup" || token === "pagedown")) {
|
|
50997
|
+
const step = token === "pageup" ? -bodyRows : bodyRows;
|
|
50998
|
+
detailScroll = clampScroll3(detailScroll + step, detailLines().length, bodyRows);
|
|
50999
|
+
paintSelection();
|
|
51000
|
+
}
|
|
51001
|
+
});
|
|
51002
|
+
}
|
|
51003
|
+
return handle;
|
|
51004
|
+
}
|
|
51005
|
+
function openReview(otui, chrome, options) {
|
|
51006
|
+
return presentReview((hostOtui, hostChrome, input2) => openModal(hostOtui, hostChrome, input2), otui, chrome, options);
|
|
51007
|
+
}
|
|
51008
|
+
|
|
51009
|
+
// src/tui/review-accept.ts
|
|
51010
|
+
function shQuote(value) {
|
|
51011
|
+
return `'${value.replace(/'/g, `'\\''`)}'`;
|
|
51012
|
+
}
|
|
51013
|
+
async function acceptProposalViaShell(run, workspaceId, proposalId) {
|
|
51014
|
+
const mint = await run(`keryx workspace confirm-review ${shQuote(workspaceId)} ${shQuote(proposalId)}`);
|
|
51015
|
+
if (mint.isError) {
|
|
51016
|
+
return { ok: false, message: mint.output };
|
|
51017
|
+
}
|
|
51018
|
+
let token;
|
|
51019
|
+
try {
|
|
51020
|
+
const parsed = JSON.parse(mint.output);
|
|
51021
|
+
const candidate = parsed?.token;
|
|
51022
|
+
if (typeof candidate !== "string" || candidate.length === 0) {
|
|
51023
|
+
throw new Error("no token in confirm-review output");
|
|
51024
|
+
}
|
|
51025
|
+
token = candidate;
|
|
51026
|
+
} catch (cause) {
|
|
51027
|
+
return {
|
|
51028
|
+
ok: false,
|
|
51029
|
+
message: `could not parse \`keryx workspace confirm-review\` output: ${cause instanceof Error ? cause.message : String(cause)}`
|
|
51030
|
+
};
|
|
51031
|
+
}
|
|
51032
|
+
const accept = await run(`keryx workspace review ${shQuote(workspaceId)} ${shQuote(proposalId)} --decision accepted --confirm-token ${shQuote(token)}`);
|
|
51033
|
+
if (accept.isError) {
|
|
51034
|
+
return { ok: false, message: accept.output };
|
|
51035
|
+
}
|
|
51036
|
+
return { ok: true };
|
|
51037
|
+
}
|
|
51038
|
+
|
|
51039
|
+
// src/tui/tui-shell.ts
|
|
51040
|
+
init_slate();
|
|
51041
|
+
|
|
50180
51042
|
// src/tui/context-usage.ts
|
|
50181
51043
|
var CONTEXT_BAR_WIDTH = 28;
|
|
50182
51044
|
function lastTurnUsed(usage) {
|
|
@@ -50533,6 +51395,16 @@ var AGENT_SLASH_COMMANDS = [
|
|
|
50533
51395
|
modes: BOTH
|
|
50534
51396
|
},
|
|
50535
51397
|
{ name: "/flows", description: "Browse project flows and inspect one", modes: BOTH },
|
|
51398
|
+
{
|
|
51399
|
+
name: "/workspace",
|
|
51400
|
+
description: "Show this session's SAC workspace and its slates",
|
|
51401
|
+
modes: AGENT_ONLY
|
|
51402
|
+
},
|
|
51403
|
+
{
|
|
51404
|
+
name: "/review",
|
|
51405
|
+
description: "Show project-wide items needing review (proposals, blocked sessions)",
|
|
51406
|
+
modes: AGENT_ONLY
|
|
51407
|
+
},
|
|
50536
51408
|
{
|
|
50537
51409
|
name: "/compact",
|
|
50538
51410
|
description: "Compact model context \u2014 /compact [focus] (archive kept)",
|
|
@@ -50896,10 +51768,10 @@ init_shell_config();
|
|
|
50896
51768
|
// src/lib/permission-mode-config.ts
|
|
50897
51769
|
init_permission_mode();
|
|
50898
51770
|
init_config_dir();
|
|
50899
|
-
import
|
|
51771
|
+
import path146 from "path";
|
|
50900
51772
|
var EMPTY3 = { schemaVersion: 1, projects: {} };
|
|
50901
51773
|
function permissionModeConfigPath(dir) {
|
|
50902
|
-
return
|
|
51774
|
+
return path146.join(keryxConfigDir(dir), "permission-mode.json");
|
|
50903
51775
|
}
|
|
50904
51776
|
function withRegistryLock2(dir, fn) {
|
|
50905
51777
|
return withFileLock(`${permissionModeConfigPath(dir)}.lock`, fn, {
|
|
@@ -50965,10 +51837,6 @@ function setProjectPermissionMode(projectPath, mode, dir) {
|
|
|
50965
51837
|
init_permission_mode();
|
|
50966
51838
|
init_enrich2();
|
|
50967
51839
|
|
|
50968
|
-
// src/session/index.ts
|
|
50969
|
-
init_paths();
|
|
50970
|
-
init_store3();
|
|
50971
|
-
|
|
50972
51840
|
// src/tui/herdr-report.ts
|
|
50973
51841
|
import net from "net";
|
|
50974
51842
|
var SOURCE = "herdr:keryx";
|
|
@@ -51248,7 +52116,7 @@ function showComposerChoice(otui, r, dock, request) {
|
|
|
51248
52116
|
// src/lib/version-check.ts
|
|
51249
52117
|
init_config_dir();
|
|
51250
52118
|
init_fs();
|
|
51251
|
-
import
|
|
52119
|
+
import path147 from "path";
|
|
51252
52120
|
var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
|
|
51253
52121
|
var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
|
|
51254
52122
|
var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
|
|
@@ -51450,7 +52318,7 @@ async function checkVersion(options) {
|
|
|
51450
52318
|
const now = options.now ?? Date.now;
|
|
51451
52319
|
const timestamp = now();
|
|
51452
52320
|
const configDir = ensureKeryxConfigDir(options.cacheDir);
|
|
51453
|
-
const cacheFile =
|
|
52321
|
+
const cacheFile = path147.join(configDir, "version-check.json");
|
|
51454
52322
|
const cache = parseCache(cacheFile);
|
|
51455
52323
|
if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
|
|
51456
52324
|
return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
|
|
@@ -51681,6 +52549,21 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
51681
52549
|
});
|
|
51682
52550
|
main.add(scroll);
|
|
51683
52551
|
const transcript = scroll.content;
|
|
52552
|
+
const queueDock = new otui.BoxRenderable(r, {
|
|
52553
|
+
id: "queue-dock",
|
|
52554
|
+
flexShrink: 0,
|
|
52555
|
+
flexDirection: "column",
|
|
52556
|
+
visible: false,
|
|
52557
|
+
backgroundColor: getTheme().panel,
|
|
52558
|
+
borderStyle: "rounded",
|
|
52559
|
+
border: true,
|
|
52560
|
+
borderColor: getTheme().border,
|
|
52561
|
+
paddingLeft: 1,
|
|
52562
|
+
paddingRight: 1,
|
|
52563
|
+
paddingTop: 0,
|
|
52564
|
+
paddingBottom: 0
|
|
52565
|
+
});
|
|
52566
|
+
main.add(queueDock);
|
|
51684
52567
|
const dock = new otui.BoxRenderable(r, {
|
|
51685
52568
|
id: "choice-dock",
|
|
51686
52569
|
flexShrink: 0,
|
|
@@ -51817,6 +52700,16 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
51817
52700
|
textarea.focus();
|
|
51818
52701
|
}
|
|
51819
52702
|
};
|
|
52703
|
+
scroll.onMouseDown = (event) => {
|
|
52704
|
+
if (overlayActive())
|
|
52705
|
+
return;
|
|
52706
|
+
input2.focus();
|
|
52707
|
+
event.preventDefault();
|
|
52708
|
+
};
|
|
52709
|
+
sidebar.onMouseDown = () => {
|
|
52710
|
+
if (overlayActive())
|
|
52711
|
+
return;
|
|
52712
|
+
};
|
|
51820
52713
|
const footer = new otui.BoxRenderable(r, {
|
|
51821
52714
|
id: "footer",
|
|
51822
52715
|
flexShrink: 0,
|
|
@@ -51983,6 +52876,8 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
51983
52876
|
sidebar.borderColor = theme.highlight;
|
|
51984
52877
|
dock.backgroundColor = theme.panel;
|
|
51985
52878
|
dock.borderColor = theme.border;
|
|
52879
|
+
queueDock.backgroundColor = theme.panel;
|
|
52880
|
+
queueDock.borderColor = theme.border;
|
|
51986
52881
|
composer.borderColor = theme.border;
|
|
51987
52882
|
menu.backgroundColor = theme.panel;
|
|
51988
52883
|
menu.focusedBackgroundColor = theme.panel;
|
|
@@ -52004,6 +52899,7 @@ async function createShellChrome(otui, renderer, opts) {
|
|
|
52004
52899
|
scroll,
|
|
52005
52900
|
transcript,
|
|
52006
52901
|
dock,
|
|
52902
|
+
queueDock,
|
|
52007
52903
|
menu,
|
|
52008
52904
|
composer,
|
|
52009
52905
|
textarea,
|
|
@@ -52150,6 +53046,28 @@ function reinsertMainQueueItem(items, at, item) {
|
|
|
52150
53046
|
return next;
|
|
52151
53047
|
}
|
|
52152
53048
|
|
|
53049
|
+
// src/tui/queue-nav.ts
|
|
53050
|
+
var QUEUE_NAV_ACTIONS = ["force", "edit", "delete"];
|
|
53051
|
+
function stepQueueNavIndex(selected, count, direction) {
|
|
53052
|
+
if (count <= 0)
|
|
53053
|
+
return 0;
|
|
53054
|
+
if (direction === "up") {
|
|
53055
|
+
return selected > 0 ? selected - 1 : count - 1;
|
|
53056
|
+
}
|
|
53057
|
+
return selected < count - 1 ? selected + 1 : 0;
|
|
53058
|
+
}
|
|
53059
|
+
function clampQueueNavIndex(selected, count) {
|
|
53060
|
+
if (count <= 0)
|
|
53061
|
+
return 0;
|
|
53062
|
+
return Math.min(Math.max(selected, 0), count - 1);
|
|
53063
|
+
}
|
|
53064
|
+
function stepQueueNavAction(selected, direction) {
|
|
53065
|
+
const idx = QUEUE_NAV_ACTIONS.indexOf(selected);
|
|
53066
|
+
const count = QUEUE_NAV_ACTIONS.length;
|
|
53067
|
+
const nextIdx = direction === "left" ? idx > 0 ? idx - 1 : count - 1 : idx < count - 1 ? idx + 1 : 0;
|
|
53068
|
+
return QUEUE_NAV_ACTIONS[nextIdx];
|
|
53069
|
+
}
|
|
53070
|
+
|
|
52153
53071
|
// src/tui/worker-fleet.ts
|
|
52154
53072
|
var STATUS_GLYPH = {
|
|
52155
53073
|
queued: "\u25CB",
|
|
@@ -53528,6 +54446,7 @@ async function launchTuiAgentShell(opts) {
|
|
|
53528
54446
|
...opts.versionCheck !== undefined ? { versionCheck: opts.versionCheck } : {}
|
|
53529
54447
|
});
|
|
53530
54448
|
mountedChrome = chrome;
|
|
54449
|
+
chrome.input.focus();
|
|
53531
54450
|
const transcript = chrome.transcript;
|
|
53532
54451
|
const input2 = chrome.input;
|
|
53533
54452
|
let busyPhase = "waiting for model";
|
|
@@ -53551,6 +54470,51 @@ async function launchTuiAgentShell(opts) {
|
|
|
53551
54470
|
const sbModelV = new otui.TextRenderable(r, { id: "sb-model-v", content: otui.t`${otui.dim(`${sel.provider}/${sel.model}`)}` });
|
|
53552
54471
|
sidebar.add(sbModelV);
|
|
53553
54472
|
mountCwdPanel(otui, r, sidebar, opts.session?.cwd ?? process.cwd());
|
|
54473
|
+
sidebar.add(new otui.TextRenderable(r, { id: "sb-workspace-k", content: otui.t`${otui.dim("Workspace")}`, marginTop: 1 }));
|
|
54474
|
+
const sbWorkspaceV = new otui.TextRenderable(r, {
|
|
54475
|
+
id: "sb-workspace-v",
|
|
54476
|
+
content: otui.t`${otui.dim("\u2014")}`,
|
|
54477
|
+
onMouseDown: () => {
|
|
54478
|
+
showWorkspace();
|
|
54479
|
+
}
|
|
54480
|
+
});
|
|
54481
|
+
sidebar.add(sbWorkspaceV);
|
|
54482
|
+
let currentWorkspace;
|
|
54483
|
+
let currentSlates = [];
|
|
54484
|
+
const refreshWorkspaceSidebar = async () => {
|
|
54485
|
+
const dir = slateSession?.dir;
|
|
54486
|
+
const workspaceId = dir !== undefined ? (await readSlate(dir).catch(() => {
|
|
54487
|
+
return;
|
|
54488
|
+
}))?.workspaceId : undefined;
|
|
54489
|
+
if (workspaceId === undefined) {
|
|
54490
|
+
currentWorkspace = undefined;
|
|
54491
|
+
currentSlates = [];
|
|
54492
|
+
sbWorkspaceV.content = otui.t`${otui.dim("\u2014")}`;
|
|
54493
|
+
return;
|
|
54494
|
+
}
|
|
54495
|
+
const cwd = opts.session?.cwd ?? process.cwd();
|
|
54496
|
+
const [workspace, slates] = await Promise.all([
|
|
54497
|
+
loadInspectorWorkspace(cwd, workspaceId),
|
|
54498
|
+
loadInspectorSlates(cwd, workspaceId)
|
|
54499
|
+
]);
|
|
54500
|
+
currentWorkspace = workspace;
|
|
54501
|
+
currentSlates = slates;
|
|
54502
|
+
sbWorkspaceV.content = workspace === undefined ? otui.t`${otui.dim("\u2014")}` : otui.t`${otui.dim(`${shortenCwd(workspace.title, SIDEBAR_TEXT_WIDTH)} \xB7 ${workspace.status} \xB7 ${slates.length} slate${slates.length === 1 ? "" : "s"}`)}`;
|
|
54503
|
+
};
|
|
54504
|
+
sidebar.add(new otui.TextRenderable(r, { id: "sb-review-k", content: otui.t`${otui.dim("Review")}`, marginTop: 1 }));
|
|
54505
|
+
const sbReviewV = new otui.TextRenderable(r, {
|
|
54506
|
+
id: "sb-review-v",
|
|
54507
|
+
content: otui.t`${otui.dim("\u2014")}`,
|
|
54508
|
+
onMouseDown: () => {
|
|
54509
|
+
showReview();
|
|
54510
|
+
}
|
|
54511
|
+
});
|
|
54512
|
+
sidebar.add(sbReviewV);
|
|
54513
|
+
const refreshReviewSidebar = async () => {
|
|
54514
|
+
const cwd = opts.session?.cwd ?? process.cwd();
|
|
54515
|
+
const count = catchUpItems(await loadInspectorCatchUp(cwd)).length;
|
|
54516
|
+
sbReviewV.content = count === 0 ? otui.t`${otui.dim("\u2014 nothing to review")}` : otui.t`${otui.yellow(`${count} item${count === 1 ? "" : "s"} need review`)}`;
|
|
54517
|
+
};
|
|
53554
54518
|
sidebar.add(new otui.TextRenderable(r, { id: "sb-ctx-k", content: otui.t`${otui.dim("Context")}`, marginTop: 1 }));
|
|
53555
54519
|
const sbContext = new otui.TextRenderable(r, { id: "sb-ctx-v", content: otui.t`${otui.dim("0 tokens")}` });
|
|
53556
54520
|
sidebar.add(sbContext);
|
|
@@ -53990,6 +54954,8 @@ async function launchTuiAgentShell(opts) {
|
|
|
53990
54954
|
}));
|
|
53991
54955
|
}
|
|
53992
54956
|
slateSession = { dir: liveSession.dir, cwd: sessionCwd, opened: false };
|
|
54957
|
+
refreshWorkspaceSidebar();
|
|
54958
|
+
refreshReviewSidebar();
|
|
53993
54959
|
const paintSessionHeader = () => {
|
|
53994
54960
|
const label = `${currentSel.provider}/${currentSel.model}`;
|
|
53995
54961
|
const sid = shortSessionId(liveSession.summary.id);
|
|
@@ -54130,6 +55096,50 @@ Staying in the current session.
|
|
|
54130
55096
|
});
|
|
54131
55097
|
})();
|
|
54132
55098
|
};
|
|
55099
|
+
const showWorkspace = () => {
|
|
55100
|
+
(async () => {
|
|
55101
|
+
const dir = slateSession?.dir;
|
|
55102
|
+
const workspaceId = dir !== undefined ? (await readSlate(dir).catch(() => {
|
|
55103
|
+
return;
|
|
55104
|
+
}))?.workspaceId : undefined;
|
|
55105
|
+
if (workspaceId === undefined) {
|
|
55106
|
+
io.onSystem?.(`No workspace bound to this session yet \u2014 the agent binds one automatically on its first real task.
|
|
55107
|
+
`);
|
|
55108
|
+
return;
|
|
55109
|
+
}
|
|
55110
|
+
const cwd = inspectorCwd();
|
|
55111
|
+
const [workspace, slates] = await Promise.all([
|
|
55112
|
+
loadInspectorWorkspace(cwd, workspaceId),
|
|
55113
|
+
loadInspectorSlates(cwd, workspaceId)
|
|
55114
|
+
]);
|
|
55115
|
+
if (workspace === undefined) {
|
|
55116
|
+
io.onSystem?.(`Workspace ${workspaceId} could not be loaded.
|
|
55117
|
+
`);
|
|
55118
|
+
return;
|
|
55119
|
+
}
|
|
55120
|
+
openWorkspace(otui, chrome, {
|
|
55121
|
+
workspace,
|
|
55122
|
+
slates,
|
|
55123
|
+
renderer: r,
|
|
55124
|
+
...inspectorKeys
|
|
55125
|
+
});
|
|
55126
|
+
})();
|
|
55127
|
+
};
|
|
55128
|
+
const showReview = () => {
|
|
55129
|
+
(async () => {
|
|
55130
|
+
const cwd = inspectorCwd();
|
|
55131
|
+
const items = catchUpItems(await loadInspectorCatchUp(cwd));
|
|
55132
|
+
openReview(otui, chrome, {
|
|
55133
|
+
items,
|
|
55134
|
+
acceptProposal: (item) => acceptProposalViaShell(makeCommandRunner(cwd), item.workspaceId, item.proposalId),
|
|
55135
|
+
onAccepted: () => {
|
|
55136
|
+
refreshReviewSidebar();
|
|
55137
|
+
},
|
|
55138
|
+
renderer: r,
|
|
55139
|
+
...inspectorKeys
|
|
55140
|
+
});
|
|
55141
|
+
})();
|
|
55142
|
+
};
|
|
54133
55143
|
const updateModelLabels = () => {
|
|
54134
55144
|
paintSessionHeader();
|
|
54135
55145
|
const label = `${currentSel.provider}/${currentSel.model}`;
|
|
@@ -54168,26 +55178,110 @@ Staying in the current session.
|
|
|
54168
55178
|
detail: `queued \xD7${sideQueue.length}`
|
|
54169
55179
|
});
|
|
54170
55180
|
};
|
|
55181
|
+
let queueNavActive = false;
|
|
55182
|
+
let selectedQueueIndex = 0;
|
|
55183
|
+
let selectedQueueAction = "force";
|
|
55184
|
+
chrome.addOverlaySource(() => queueNavActive);
|
|
54171
55185
|
let mainQueueBlocks = [];
|
|
55186
|
+
const mainQueueButton = (label, id, color, onMouseDown) => {
|
|
55187
|
+
const box = new otui.BoxRenderable(r, {
|
|
55188
|
+
id,
|
|
55189
|
+
flexShrink: 0,
|
|
55190
|
+
marginLeft: 1,
|
|
55191
|
+
paddingLeft: 1,
|
|
55192
|
+
paddingRight: 1,
|
|
55193
|
+
onMouseDown: (event) => {
|
|
55194
|
+
event.stopPropagation();
|
|
55195
|
+
onMouseDown();
|
|
55196
|
+
}
|
|
55197
|
+
});
|
|
55198
|
+
const text = new otui.TextRenderable(r, { id: `${id}-t`, content: `[${label}]` });
|
|
55199
|
+
text.fg = color;
|
|
55200
|
+
box.add(text);
|
|
55201
|
+
const setActive = (active) => {
|
|
55202
|
+
box.backgroundColor = active ? getTheme().highlight : undefined;
|
|
55203
|
+
text.content = active ? otui.t`${otui.bold(`[${label}]`)}` : `[${label}]`;
|
|
55204
|
+
text.fg = color;
|
|
55205
|
+
};
|
|
55206
|
+
return { box, setActive };
|
|
55207
|
+
};
|
|
55208
|
+
const applyQueueNavHighlight = () => {
|
|
55209
|
+
for (let i = 0;i < mainQueueBlocks.length; i++) {
|
|
55210
|
+
const entry = mainQueueBlocks[i];
|
|
55211
|
+
if (entry === undefined)
|
|
55212
|
+
continue;
|
|
55213
|
+
const rowActive = queueNavActive && i === selectedQueueIndex;
|
|
55214
|
+
entry.setRowActive(rowActive);
|
|
55215
|
+
entry.buttons.force.setActive(rowActive && selectedQueueAction === "force");
|
|
55216
|
+
entry.buttons.edit.setActive(rowActive && selectedQueueAction === "edit");
|
|
55217
|
+
entry.buttons.delete.setActive(rowActive && selectedQueueAction === "delete");
|
|
55218
|
+
}
|
|
55219
|
+
};
|
|
55220
|
+
const exitQueueNav = () => {
|
|
55221
|
+
if (!queueNavActive)
|
|
55222
|
+
return;
|
|
55223
|
+
queueNavActive = false;
|
|
55224
|
+
applyQueueNavHighlight();
|
|
55225
|
+
};
|
|
55226
|
+
const enterQueueNav = () => {
|
|
55227
|
+
if (mainQueue.length === 0)
|
|
55228
|
+
return;
|
|
55229
|
+
queueNavActive = true;
|
|
55230
|
+
selectedQueueIndex = clampQueueNavIndex(selectedQueueIndex, mainQueue.length);
|
|
55231
|
+
applyQueueNavHighlight();
|
|
55232
|
+
};
|
|
55233
|
+
chrome.queueDock.onMouseDown = () => {
|
|
55234
|
+
if (chrome.overlayActive())
|
|
55235
|
+
return;
|
|
55236
|
+
enterQueueNav();
|
|
55237
|
+
};
|
|
54172
55238
|
const paintMainQueue = () => {
|
|
54173
55239
|
for (const entry of mainQueueBlocks) {
|
|
54174
55240
|
try {
|
|
54175
|
-
|
|
55241
|
+
chrome.queueDock.remove(entry.box);
|
|
54176
55242
|
} catch {}
|
|
54177
55243
|
}
|
|
54178
55244
|
mainQueueBlocks = [];
|
|
55245
|
+
selectedQueueIndex = clampQueueNavIndex(selectedQueueIndex, mainQueue.length);
|
|
55246
|
+
if (mainQueue.length === 0) {
|
|
55247
|
+
exitQueueNav();
|
|
55248
|
+
}
|
|
55249
|
+
const theme = getTheme();
|
|
54179
55250
|
for (let i = 0;i < mainQueue.length; i++) {
|
|
54180
55251
|
const item = mainQueue[i];
|
|
54181
55252
|
if (item === undefined)
|
|
54182
55253
|
continue;
|
|
54183
|
-
const
|
|
55254
|
+
const index = i;
|
|
55255
|
+
const row = new otui.BoxRenderable(r, {
|
|
54184
55256
|
id: `mq-${item.id}`,
|
|
54185
|
-
|
|
54186
|
-
|
|
54187
|
-
|
|
55257
|
+
width: "100%",
|
|
55258
|
+
flexDirection: "row"
|
|
55259
|
+
});
|
|
55260
|
+
const label = new otui.TextRenderable(r, {
|
|
55261
|
+
id: `mq-${item.id}-t`,
|
|
55262
|
+
flexGrow: 1,
|
|
55263
|
+
minWidth: 0,
|
|
55264
|
+
content: otui.t`${otui.dim(`${formatMainQueueMarker(index, mainQueue.length)} ${item.displayQuestion}`)}`
|
|
55265
|
+
});
|
|
55266
|
+
row.add(label);
|
|
55267
|
+
const force = mainQueueButton("Force", `mq-force-${item.id}`, theme.focus, () => forceMainQueue(index));
|
|
55268
|
+
const edit = mainQueueButton("Edit", `mq-edit-${item.id}`, theme.text, () => editMainQueue(index));
|
|
55269
|
+
const del = mainQueueButton("Delete", `mq-del-${item.id}`, theme.error, () => removeMainQueue(index));
|
|
55270
|
+
row.add(force.box);
|
|
55271
|
+
row.add(edit.box);
|
|
55272
|
+
row.add(del.box);
|
|
55273
|
+
chrome.queueDock.add(row);
|
|
55274
|
+
mainQueueBlocks.push({
|
|
55275
|
+
id: item.id,
|
|
55276
|
+
box: row,
|
|
55277
|
+
setRowActive: (active) => {
|
|
55278
|
+
row.backgroundColor = active ? theme.highlight : undefined;
|
|
55279
|
+
},
|
|
55280
|
+
buttons: { force, edit, delete: del }
|
|
54188
55281
|
});
|
|
54189
|
-
mainQueueBlocks.push({ id: item.id, box });
|
|
54190
55282
|
}
|
|
55283
|
+
chrome.queueDock.visible = mainQueue.length > 0;
|
|
55284
|
+
applyQueueNavHighlight();
|
|
54191
55285
|
if (mainQueue.length > 0) {
|
|
54192
55286
|
fleet.upsert({ id: "agent:queue", label: "mainQ", status: "queued", detail: `queued \xD7${mainQueue.length}` });
|
|
54193
55287
|
} else {
|
|
@@ -54226,6 +55320,63 @@ Staying in the current session.
|
|
|
54226
55320
|
}
|
|
54227
55321
|
runLine(item.question);
|
|
54228
55322
|
};
|
|
55323
|
+
const handleQueueNavKey = (key) => {
|
|
55324
|
+
if (!queueNavActive) {
|
|
55325
|
+
if (key.ctrl && key.name === "q" && mainQueue.length > 0 && !chrome.overlayActive() && !chrome.menuActive() && !nav.active()) {
|
|
55326
|
+
enterQueueNav();
|
|
55327
|
+
key.preventDefault();
|
|
55328
|
+
key.stopPropagation();
|
|
55329
|
+
}
|
|
55330
|
+
return;
|
|
55331
|
+
}
|
|
55332
|
+
if (key.name === "escape") {
|
|
55333
|
+
exitQueueNav();
|
|
55334
|
+
key.preventDefault();
|
|
55335
|
+
key.stopPropagation();
|
|
55336
|
+
return;
|
|
55337
|
+
}
|
|
55338
|
+
if (key.name === "up") {
|
|
55339
|
+
selectedQueueIndex = stepQueueNavIndex(selectedQueueIndex, mainQueue.length, "up");
|
|
55340
|
+
applyQueueNavHighlight();
|
|
55341
|
+
key.preventDefault();
|
|
55342
|
+
key.stopPropagation();
|
|
55343
|
+
return;
|
|
55344
|
+
}
|
|
55345
|
+
if (key.name === "down") {
|
|
55346
|
+
selectedQueueIndex = stepQueueNavIndex(selectedQueueIndex, mainQueue.length, "down");
|
|
55347
|
+
applyQueueNavHighlight();
|
|
55348
|
+
key.preventDefault();
|
|
55349
|
+
key.stopPropagation();
|
|
55350
|
+
return;
|
|
55351
|
+
}
|
|
55352
|
+
if (key.name === "left") {
|
|
55353
|
+
selectedQueueAction = stepQueueNavAction(selectedQueueAction, "left");
|
|
55354
|
+
applyQueueNavHighlight();
|
|
55355
|
+
key.preventDefault();
|
|
55356
|
+
key.stopPropagation();
|
|
55357
|
+
return;
|
|
55358
|
+
}
|
|
55359
|
+
if (key.name === "right") {
|
|
55360
|
+
selectedQueueAction = stepQueueNavAction(selectedQueueAction, "right");
|
|
55361
|
+
applyQueueNavHighlight();
|
|
55362
|
+
key.preventDefault();
|
|
55363
|
+
key.stopPropagation();
|
|
55364
|
+
return;
|
|
55365
|
+
}
|
|
55366
|
+
if (key.name === "return" || key.name === "linefeed" || key.name === "kpenter") {
|
|
55367
|
+
const index = selectedQueueIndex;
|
|
55368
|
+
const action = selectedQueueAction;
|
|
55369
|
+
exitQueueNav();
|
|
55370
|
+
if (action === "force")
|
|
55371
|
+
forceMainQueue(index);
|
|
55372
|
+
else if (action === "edit")
|
|
55373
|
+
editMainQueue(index);
|
|
55374
|
+
else
|
|
55375
|
+
removeMainQueue(index);
|
|
55376
|
+
key.preventDefault();
|
|
55377
|
+
key.stopPropagation();
|
|
55378
|
+
}
|
|
55379
|
+
};
|
|
54229
55380
|
const clearSideWorkerSlot = () => {
|
|
54230
55381
|
if (sideClearTimeout !== undefined) {
|
|
54231
55382
|
clearTimeout(sideClearTimeout);
|
|
@@ -54507,6 +55658,8 @@ Staying in the current session.
|
|
|
54507
55658
|
await closeSlateSession(slateSession, mintTimestampAttemptId);
|
|
54508
55659
|
startNewSession();
|
|
54509
55660
|
slateSession = { dir: liveSession.dir, cwd: sessionCwd, opened: false };
|
|
55661
|
+
refreshWorkspaceSidebar();
|
|
55662
|
+
refreshReviewSidebar();
|
|
54510
55663
|
sessions.clear();
|
|
54511
55664
|
deps.resetSubagentBudget?.();
|
|
54512
55665
|
io.onSystem?.(`New session ${shortSessionId(liveSession.summary.id)} (previous kept on disk \xB7 /resume)
|
|
@@ -54641,6 +55794,14 @@ Staying in the current session.
|
|
|
54641
55794
|
showFlows();
|
|
54642
55795
|
return;
|
|
54643
55796
|
}
|
|
55797
|
+
if (isWorkspaceCommand(command.name)) {
|
|
55798
|
+
showWorkspace();
|
|
55799
|
+
return;
|
|
55800
|
+
}
|
|
55801
|
+
if (isReviewCommand(command.name)) {
|
|
55802
|
+
showReview();
|
|
55803
|
+
return;
|
|
55804
|
+
}
|
|
54644
55805
|
if (command.name === "/copy") {
|
|
54645
55806
|
const target = newestBlock();
|
|
54646
55807
|
if (target === undefined || !copyBlock(target.id)) {
|
|
@@ -54959,6 +56120,8 @@ ${formatThemeList(getThemeId())}`);
|
|
|
54959
56120
|
mainTurnAbortController = undefined;
|
|
54960
56121
|
const secs = ((Date.now() - startedAt) / 1000).toFixed(1);
|
|
54961
56122
|
stopBusy();
|
|
56123
|
+
refreshWorkspaceSidebar();
|
|
56124
|
+
refreshReviewSidebar();
|
|
54962
56125
|
setMainAgent(turnFailed ? "failed" : "done", turnFailed ? "error" : "idle");
|
|
54963
56126
|
try {
|
|
54964
56127
|
flushSessionCheckpoint();
|
|
@@ -54985,6 +56148,9 @@ ${formatThemeList(getThemeId())}`);
|
|
|
54985
56148
|
onKeypress4(r, (key) => {
|
|
54986
56149
|
nav.handleKey(key);
|
|
54987
56150
|
});
|
|
56151
|
+
onKeypress4(r, (key) => {
|
|
56152
|
+
handleQueueNavKey(key);
|
|
56153
|
+
});
|
|
54988
56154
|
chrome.onSubmit((line) => {
|
|
54989
56155
|
runLine(line);
|
|
54990
56156
|
});
|
|
@@ -56324,7 +57490,7 @@ ${indentBlock(style.dim(context), GUTTER)}`);
|
|
|
56324
57490
|
out(`${GUTTER}${style.yellow(hint)}
|
|
56325
57491
|
`);
|
|
56326
57492
|
}
|
|
56327
|
-
const rememberable = !evaled.destructive && !evaled.credentials;
|
|
57493
|
+
const rememberable = !evaled.destructive && !evaled.credentials && !evaled.sacReviewConfirmation;
|
|
56328
57494
|
const prompt = rememberable ? "[y/N/A=always] " : "[y/N] ";
|
|
56329
57495
|
out(`
|
|
56330
57496
|
${GUTTER}${style.yellow(`Run: ${evaled.command}`)} ${style.dim(prompt)}`);
|
|
@@ -56866,7 +58032,7 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
56866
58032
|
modelId: sel.model
|
|
56867
58033
|
}),
|
|
56868
58034
|
maxToolCalls: resolveAgentMaxToolCalls(),
|
|
56869
|
-
idSeq: () =>
|
|
58035
|
+
idSeq: () => randomUUID25(),
|
|
56870
58036
|
...resetSubagentBudget !== undefined ? { resetSubagentBudget } : {}
|
|
56871
58037
|
};
|
|
56872
58038
|
};
|
|
@@ -56898,7 +58064,7 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
56898
58064
|
makeShellDeps: (sel) => ({
|
|
56899
58065
|
makeProvider: chatFactory,
|
|
56900
58066
|
clock: () => new Date().toISOString(),
|
|
56901
|
-
idSeq: () =>
|
|
58067
|
+
idSeq: () => randomUUID25(),
|
|
56902
58068
|
initial: sel,
|
|
56903
58069
|
session: {
|
|
56904
58070
|
cwd,
|
|
@@ -56969,7 +58135,7 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
56969
58135
|
const deps = {
|
|
56970
58136
|
makeProvider: baseFactory,
|
|
56971
58137
|
clock: () => new Date().toISOString(),
|
|
56972
|
-
idSeq: () =>
|
|
58138
|
+
idSeq: () => randomUUID25(),
|
|
56973
58139
|
initial: baseUrl2 === undefined ? { provider, model } : { provider, model, baseUrl: baseUrl2 },
|
|
56974
58140
|
selectProviderModel: realSelectProviderModel(baseUrl2)
|
|
56975
58141
|
};
|
|
@@ -57020,7 +58186,7 @@ async function shellCommand(args2, runtime = {}) {
|
|
|
57020
58186
|
modelId: model
|
|
57021
58187
|
}),
|
|
57022
58188
|
maxToolCalls: resolveAgentMaxToolCalls(),
|
|
57023
|
-
idSeq: () =>
|
|
58189
|
+
idSeq: () => randomUUID25(),
|
|
57024
58190
|
...resetSubagentBudget !== undefined ? { resetSubagentBudget } : {}
|
|
57025
58191
|
};
|
|
57026
58192
|
let resumeId = flags.resumeId;
|
|
@@ -57192,7 +58358,7 @@ Shell:
|
|
|
57192
58358
|
init_fs();
|
|
57193
58359
|
import { readFile as readFile78 } from "fs/promises";
|
|
57194
58360
|
import { stdin } from "process";
|
|
57195
|
-
import
|
|
58361
|
+
import path148 from "path";
|
|
57196
58362
|
var MODULES = [
|
|
57197
58363
|
{ name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
|
|
57198
58364
|
{ name: "gdctx", flag: "--no-gdctx", desc: "token-aware command/read output", defaultEnabled: true },
|
|
@@ -57238,8 +58404,8 @@ async function modulesCommand(args2 = []) {
|
|
|
57238
58404
|
return;
|
|
57239
58405
|
}
|
|
57240
58406
|
const wantsJson = args2.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
|
|
57241
|
-
const metaprojectRoot =
|
|
57242
|
-
const manifestPath =
|
|
58407
|
+
const metaprojectRoot = path148.join(process.cwd(), ".metaproject");
|
|
58408
|
+
const manifestPath = path148.join(metaprojectRoot, "metaproject.json");
|
|
57243
58409
|
if (!await pathExists(manifestPath)) {
|
|
57244
58410
|
if (wantsJson) {
|
|
57245
58411
|
console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
|
|
@@ -57353,18 +58519,18 @@ function printHelp16() {
|
|
|
57353
58519
|
}
|
|
57354
58520
|
|
|
57355
58521
|
// src/commands/serve.ts
|
|
57356
|
-
import { randomUUID as
|
|
58522
|
+
import { randomUUID as randomUUID28 } from "crypto";
|
|
57357
58523
|
|
|
57358
58524
|
// src/lib/serve-config.ts
|
|
57359
58525
|
init_config_dir();
|
|
57360
58526
|
import { existsSync as existsSync27 } from "fs";
|
|
57361
|
-
import
|
|
58527
|
+
import path149 from "path";
|
|
57362
58528
|
var SERVE_CONFIG_SCHEMA_VERSION = "1.0.0";
|
|
57363
58529
|
var DEFAULT_SERVE_BIND_ADDRESS = "127.0.0.1";
|
|
57364
58530
|
var DEFAULT_SERVE_PORT = 7377;
|
|
57365
58531
|
var DEFAULT_SERVE_PROFILE = "remote-restricted";
|
|
57366
58532
|
function serveConfigPath(dir) {
|
|
57367
|
-
return
|
|
58533
|
+
return path149.join(keryxConfigDir(dir), "serve.json");
|
|
57368
58534
|
}
|
|
57369
58535
|
function parseIpv4(value) {
|
|
57370
58536
|
const parts = value.split(".");
|
|
@@ -57680,7 +58846,7 @@ function saveServeConfig(config, dir, onWarn) {
|
|
|
57680
58846
|
|
|
57681
58847
|
// src/lib/serve-credential.ts
|
|
57682
58848
|
init_config_dir();
|
|
57683
|
-
import { createHash as createHash33, randomBytes as randomBytes3, randomUUID as
|
|
58849
|
+
import { createHash as createHash33, randomBytes as randomBytes3, randomUUID as randomUUID26 } from "crypto";
|
|
57684
58850
|
import {
|
|
57685
58851
|
chmodSync as chmodSync4,
|
|
57686
58852
|
closeSync as closeSync3,
|
|
@@ -57692,9 +58858,9 @@ import {
|
|
|
57692
58858
|
unlinkSync as unlinkSync3,
|
|
57693
58859
|
writeFileSync as writeFileSync8
|
|
57694
58860
|
} from "fs";
|
|
57695
|
-
import
|
|
58861
|
+
import path150 from "path";
|
|
57696
58862
|
function serveCredentialPath(dir) {
|
|
57697
|
-
return
|
|
58863
|
+
return path150.join(keryxConfigDir(dir), "serve-credentials.json");
|
|
57698
58864
|
}
|
|
57699
58865
|
function constantTimeEqual(a, b) {
|
|
57700
58866
|
const width = Math.max(a.length, b.length);
|
|
@@ -57764,7 +58930,7 @@ function readServeCredential(dir) {
|
|
|
57764
58930
|
}
|
|
57765
58931
|
function writeStore(store, dir) {
|
|
57766
58932
|
const file = serveCredentialPath(dir);
|
|
57767
|
-
const temp = `${file}.${
|
|
58933
|
+
const temp = `${file}.${randomUUID26()}.tmp`;
|
|
57768
58934
|
try {
|
|
57769
58935
|
ensureKeryxConfigDir(dir);
|
|
57770
58936
|
const handle = openSync3(temp, "wx", 384);
|
|
@@ -57804,7 +58970,7 @@ function mintRecord(now) {
|
|
|
57804
58970
|
const salt = randomBytes3(32).toString("hex");
|
|
57805
58971
|
return {
|
|
57806
58972
|
token,
|
|
57807
|
-
record: { id:
|
|
58973
|
+
record: { id: randomUUID26(), algorithm: "sha256", salt, hash: hashToken(salt, token), createdAt: now }
|
|
57808
58974
|
};
|
|
57809
58975
|
}
|
|
57810
58976
|
function issueServeToken(dir, now = () => new Date().toISOString(), onWaiting) {
|
|
@@ -57928,22 +59094,22 @@ class AuthFailureThrottle {
|
|
|
57928
59094
|
init_config_dir();
|
|
57929
59095
|
import { createHash as createHash34 } from "crypto";
|
|
57930
59096
|
import { existsSync as existsSync29, readdirSync as readdirSync2, rmSync as rmSync2 } from "fs";
|
|
57931
|
-
import
|
|
59097
|
+
import path151 from "path";
|
|
57932
59098
|
var MAX_TURN_EVENTS = 1e4;
|
|
57933
59099
|
function turnsRoot(dir) {
|
|
57934
|
-
return
|
|
59100
|
+
return path151.join(keryxConfigDir(dir), "turns");
|
|
57935
59101
|
}
|
|
57936
59102
|
function turnDir(turnId, dir) {
|
|
57937
|
-
return
|
|
59103
|
+
return path151.join(turnsRoot(dir), turnId);
|
|
57938
59104
|
}
|
|
57939
59105
|
function keyPath(project, idempotencyKey, dir) {
|
|
57940
59106
|
const projectBytes = Buffer.byteLength(project, "utf8");
|
|
57941
59107
|
const digest2 = createHash34("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
|
|
57942
|
-
return
|
|
59108
|
+
return path151.join(turnsRoot(dir), "keys", `${digest2}.json`);
|
|
57943
59109
|
}
|
|
57944
59110
|
function legacyKeyPath(idempotencyKey, dir) {
|
|
57945
59111
|
const digest2 = createHash34("sha256").update(idempotencyKey, "utf8").digest("hex");
|
|
57946
|
-
return
|
|
59112
|
+
return path151.join(turnsRoot(dir), "keys", `${digest2}.json`);
|
|
57947
59113
|
}
|
|
57948
59114
|
function adoptLegacyClaim(project, idempotencyKey, dir) {
|
|
57949
59115
|
const legacy = legacyKeyPath(idempotencyKey, dir);
|
|
@@ -58007,7 +59173,7 @@ function ensureTurnDir(turnId, dir) {
|
|
|
58007
59173
|
}
|
|
58008
59174
|
function createTurnRecord(record, dir) {
|
|
58009
59175
|
ensureTurnDir(record.turnId, dir);
|
|
58010
|
-
writeOwnerOnlyFile(
|
|
59176
|
+
writeOwnerOnlyFile(path151.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
|
|
58011
59177
|
`);
|
|
58012
59178
|
}
|
|
58013
59179
|
function appendTurnEvent(event, dir, opts) {
|
|
@@ -58016,12 +59182,12 @@ function appendTurnEvent(event, dir, opts) {
|
|
|
58016
59182
|
}
|
|
58017
59183
|
const line = JSON.stringify(event);
|
|
58018
59184
|
try {
|
|
58019
|
-
appendOwnerOnlyLine(
|
|
59185
|
+
appendOwnerOnlyLine(path151.join(turnDir(event.turnId, dir), "events.jsonl"), line);
|
|
58020
59186
|
} catch (error2) {
|
|
58021
59187
|
if (error2?.code !== "ENOENT") {
|
|
58022
59188
|
throw error2;
|
|
58023
59189
|
}
|
|
58024
|
-
appendOwnerOnlyLine(
|
|
59190
|
+
appendOwnerOnlyLine(path151.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
|
|
58025
59191
|
}
|
|
58026
59192
|
return true;
|
|
58027
59193
|
}
|
|
@@ -58029,7 +59195,7 @@ function readTurnEvents(turnId, after = -1, dir) {
|
|
|
58029
59195
|
if (!isTurnId(turnId)) {
|
|
58030
59196
|
return { ok: false, reason: "not-a-turn-id" };
|
|
58031
59197
|
}
|
|
58032
|
-
const read = readTurnFile(
|
|
59198
|
+
const read = readTurnFile(path151.join(turnDir(turnId, dir), "events.jsonl"));
|
|
58033
59199
|
if (!read.ok) {
|
|
58034
59200
|
if (isDefiniteAbsence2(read.reason)) {
|
|
58035
59201
|
return { ok: true, value: [] };
|
|
@@ -58057,7 +59223,7 @@ function readTurnRecord(turnId, dir) {
|
|
|
58057
59223
|
if (!isTurnId(turnId)) {
|
|
58058
59224
|
return { ok: false, reason: "not-a-turn-id" };
|
|
58059
59225
|
}
|
|
58060
|
-
const read = readTurnFile(
|
|
59226
|
+
const read = readTurnFile(path151.join(turnDir(turnId, dir), "turn.json"));
|
|
58061
59227
|
if (!read.ok) {
|
|
58062
59228
|
return { ok: false, reason: read.reason };
|
|
58063
59229
|
}
|
|
@@ -58076,7 +59242,7 @@ function finishTurn(turnId, result, dir) {
|
|
|
58076
59242
|
if (!record.ok) {
|
|
58077
59243
|
return false;
|
|
58078
59244
|
}
|
|
58079
|
-
writeOwnerOnlyFile(
|
|
59245
|
+
writeOwnerOnlyFile(path151.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
|
|
58080
59246
|
`);
|
|
58081
59247
|
return true;
|
|
58082
59248
|
}
|
|
@@ -58121,8 +59287,8 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
|
|
|
58121
59287
|
}
|
|
58122
59288
|
|
|
58123
59289
|
// src/lib/serve-turn.ts
|
|
58124
|
-
import { randomUUID as
|
|
58125
|
-
import
|
|
59290
|
+
import { randomUUID as randomUUID27 } from "crypto";
|
|
59291
|
+
import path152 from "path";
|
|
58126
59292
|
init_service();
|
|
58127
59293
|
var REMOTE_ORIGIN = "remote:http";
|
|
58128
59294
|
var MAX_PROMPT_CHARS = 32000;
|
|
@@ -58191,9 +59357,9 @@ function isUuid(value) {
|
|
|
58191
59357
|
return typeof value === "string" && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(value);
|
|
58192
59358
|
}
|
|
58193
59359
|
function resolveProject(declared, dir) {
|
|
58194
|
-
const wanted =
|
|
59360
|
+
const wanted = path152.resolve(declared);
|
|
58195
59361
|
for (const entry of listProjects(dir, () => {})) {
|
|
58196
|
-
if (
|
|
59362
|
+
if (path152.resolve(entry.path) === wanted) {
|
|
58197
59363
|
return { ok: true, project: entry.path };
|
|
58198
59364
|
}
|
|
58199
59365
|
}
|
|
@@ -58240,7 +59406,7 @@ async function redactOut(security, text) {
|
|
|
58240
59406
|
async function runRemoteTurn(input2) {
|
|
58241
59407
|
const scanRoot = input2.scanRoot;
|
|
58242
59408
|
const security = createSecurityService(scanRoot);
|
|
58243
|
-
const newId = input2.newId ?? (() =>
|
|
59409
|
+
const newId = input2.newId ?? (() => randomUUID27());
|
|
58244
59410
|
const clock = input2.clock ?? (() => new Date().toISOString());
|
|
58245
59411
|
const turnId = input2.turnId ?? newId();
|
|
58246
59412
|
const sessionId = input2.request.sessionId ?? newId();
|
|
@@ -58383,7 +59549,7 @@ function outcomeOf(status, gate, unresolvedBlockerIds) {
|
|
|
58383
59549
|
}
|
|
58384
59550
|
function createSubmitTurn(deps) {
|
|
58385
59551
|
return async (request, project) => {
|
|
58386
|
-
const turnId = (deps.newId ?? (() =>
|
|
59552
|
+
const turnId = (deps.newId ?? (() => randomUUID27()))();
|
|
58387
59553
|
const scanned = await scanPrompt(deps.dir, request.prompt);
|
|
58388
59554
|
if (scanned.rejected) {
|
|
58389
59555
|
return { kind: "rejected" };
|
|
@@ -59070,7 +60236,7 @@ function runConfig(args2) {
|
|
|
59070
60236
|
return;
|
|
59071
60237
|
}
|
|
59072
60238
|
const credential2 = readServeCredential();
|
|
59073
|
-
const credentialId = credential2.status === "ok" ? credential2.record.id :
|
|
60239
|
+
const credentialId = credential2.status === "ok" ? credential2.record.id : randomUUID28();
|
|
59074
60240
|
const config = defaultServeConfig(credentialId, {
|
|
59075
60241
|
address: parsed.parsed.values.get("--bind") ?? DEFAULT_SERVE_BIND_ADDRESS,
|
|
59076
60242
|
port: port ?? DEFAULT_SERVE_PORT,
|
|
@@ -59226,9 +60392,9 @@ function printHelp17() {
|
|
|
59226
60392
|
|
|
59227
60393
|
// src/commands/update.ts
|
|
59228
60394
|
import { spawn as spawn5 } from "child_process";
|
|
59229
|
-
import { chmod as chmod4, mkdir as mkdir54, readFile as readFile79, readdir as
|
|
60395
|
+
import { chmod as chmod4, mkdir as mkdir54, readFile as readFile79, readdir as readdir25, writeFile as writeFile48 } from "fs/promises";
|
|
59230
60396
|
import { access as access4, constants as constants2, existsSync as existsSync30 } from "fs";
|
|
59231
|
-
import
|
|
60397
|
+
import path153 from "path";
|
|
59232
60398
|
import { fileURLToPath as fileURLToPath7 } from "url";
|
|
59233
60399
|
init_config();
|
|
59234
60400
|
init_config2();
|
|
@@ -59244,8 +60410,8 @@ async function updateCommand(args2 = []) {
|
|
|
59244
60410
|
return;
|
|
59245
60411
|
}
|
|
59246
60412
|
const projectRoot = process.cwd();
|
|
59247
|
-
const metaprojectRoot =
|
|
59248
|
-
banner("keryx update", `Refreshing the .metaproject workspace in ${
|
|
60413
|
+
const metaprojectRoot = path153.join(projectRoot, ".metaproject");
|
|
60414
|
+
banner("keryx update", `Refreshing the .metaproject workspace in ${path153.basename(projectRoot)}/`);
|
|
59249
60415
|
if (!await pathExists(metaprojectRoot)) {
|
|
59250
60416
|
console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
|
|
59251
60417
|
console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
|
|
@@ -59291,12 +60457,12 @@ async function updateCommand(args2 = []) {
|
|
|
59291
60457
|
nextSteps(steps);
|
|
59292
60458
|
}
|
|
59293
60459
|
async function refreshServiceFiles(projectRoot, options) {
|
|
59294
|
-
const metaprojectRoot =
|
|
60460
|
+
const metaprojectRoot = path153.join(projectRoot, ".metaproject");
|
|
59295
60461
|
const manifestState = await readManifest5(metaprojectRoot);
|
|
59296
60462
|
const manifest = manifestState.manifest;
|
|
59297
60463
|
const recoveredManifest = !manifestState.exists || !manifestState.valid;
|
|
59298
60464
|
if (manifestState.migrated) {
|
|
59299
|
-
await writeFile48(
|
|
60465
|
+
await writeFile48(path153.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
59300
60466
|
`, "utf8");
|
|
59301
60467
|
}
|
|
59302
60468
|
const enableGdgraph = moduleEnabled2(manifest, "gdgraph");
|
|
@@ -59333,11 +60499,11 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
59333
60499
|
enableSecurity,
|
|
59334
60500
|
enableSac
|
|
59335
60501
|
});
|
|
59336
|
-
await writeTextIfChanged4(
|
|
59337
|
-
await writeTextIfChanged4(
|
|
59338
|
-
await writeTextIfChanged4(
|
|
59339
|
-
await writeTextIfChanged4(
|
|
59340
|
-
await writeTextIfChanged4(
|
|
60502
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
|
|
60503
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
|
|
60504
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "rules", "README.md"), renderProjectRulesReadme());
|
|
60505
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
|
|
60506
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "index.md"), renderIndexMarkdown({
|
|
59341
60507
|
enableGdgraph,
|
|
59342
60508
|
enableGdctx,
|
|
59343
60509
|
enableGdwiki,
|
|
@@ -59350,7 +60516,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
59350
60516
|
ruleSources,
|
|
59351
60517
|
hasDistilledEntrypoints: await hasDistilledEntrypoints(metaprojectRoot)
|
|
59352
60518
|
}));
|
|
59353
|
-
await writeTextIfChanged4(
|
|
60519
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
|
|
59354
60520
|
enableGdgraph,
|
|
59355
60521
|
enableGdctx,
|
|
59356
60522
|
enableGdwiki,
|
|
@@ -59362,7 +60528,7 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
59362
60528
|
enableSecurity,
|
|
59363
60529
|
data: dashboardData
|
|
59364
60530
|
}));
|
|
59365
|
-
await writeTextIfMissing4(
|
|
60531
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
|
|
59366
60532
|
enableGdgraph,
|
|
59367
60533
|
enableGdctx,
|
|
59368
60534
|
enableGdwiki,
|
|
@@ -59375,31 +60541,31 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
59375
60541
|
}));
|
|
59376
60542
|
if (enableGdgraph) {
|
|
59377
60543
|
await installGdgraphCoreScripts2(metaprojectRoot);
|
|
59378
|
-
await writeTextIfChanged4(
|
|
59379
|
-
await writeTextIfChanged4(
|
|
59380
|
-
await writeTextIfChanged4(
|
|
60544
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
|
|
60545
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
|
|
60546
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
|
|
59381
60547
|
await seedAssetsLock(metaprojectRoot);
|
|
59382
60548
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
59383
60549
|
await installManagedHook2(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
|
|
59384
60550
|
}
|
|
59385
60551
|
}
|
|
59386
60552
|
if (enableGdctx) {
|
|
59387
|
-
await writeTextIfMissing4(
|
|
59388
|
-
await writeTextIfChanged4(
|
|
59389
|
-
await writeTextIfChanged4(
|
|
59390
|
-
await writeTextIfChanged4(
|
|
60553
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
|
|
60554
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
|
|
60555
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
|
|
60556
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
|
|
59391
60557
|
}
|
|
59392
60558
|
if (enableGdwiki) {
|
|
59393
|
-
await writeTextIfMissing4(
|
|
59394
|
-
await writeTextIfChanged4(
|
|
59395
|
-
await writeTextIfChanged4(
|
|
60559
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
|
|
60560
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
|
|
60561
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
|
|
59396
60562
|
if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
|
|
59397
60563
|
await installManagedHook2(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
|
|
59398
60564
|
}
|
|
59399
60565
|
}
|
|
59400
60566
|
if (enableSac) {
|
|
59401
|
-
await writeTextIfMissing4(
|
|
59402
|
-
await writeTextIfChanged4(
|
|
60567
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
|
|
60568
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
|
|
59403
60569
|
}
|
|
59404
60570
|
if (enableGdskills) {
|
|
59405
60571
|
await installGdskills(metaprojectRoot, gdskillsProfile, { createDataDirs: false });
|
|
@@ -59408,25 +60574,25 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
59408
60574
|
}
|
|
59409
60575
|
}
|
|
59410
60576
|
if (enableHealth) {
|
|
59411
|
-
await writeTextIfMissing4(
|
|
59412
|
-
await writeTextIfChanged4(
|
|
59413
|
-
await writeTextIfChanged4(
|
|
59414
|
-
await writeTextIfChanged4(
|
|
60577
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
|
|
60578
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
|
|
60579
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
|
|
60580
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
|
|
59415
60581
|
if (manifest.modules?.health?.hooks?.gitPostCommit) {
|
|
59416
60582
|
await installManagedHook2(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
|
|
59417
60583
|
}
|
|
59418
60584
|
}
|
|
59419
60585
|
if (enableTesting) {
|
|
59420
|
-
await writeTextIfMissing4(
|
|
60586
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
|
|
59421
60587
|
postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
|
|
59422
60588
|
prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
|
|
59423
60589
|
}));
|
|
59424
|
-
await writeTextIfChanged4(
|
|
59425
|
-
await writeTextIfChanged4(
|
|
59426
|
-
await writeTextIfChanged4(
|
|
60590
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
|
|
60591
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
|
|
60592
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
|
|
59427
60593
|
if (enableGdwiki) {
|
|
59428
|
-
await writeTextIfMissing4(
|
|
59429
|
-
await writeTextIfMissing4(
|
|
60594
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
|
|
60595
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
|
|
59430
60596
|
}
|
|
59431
60597
|
if (manifest.modules?.testing?.hooks?.gitPostCommit) {
|
|
59432
60598
|
await installManagedHook2(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
|
|
@@ -59439,24 +60605,24 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
59439
60605
|
await installManagedHook2(projectRoot, "post-commit", "metaproject-dashboard-post-commit", renderMetaprojectDashboardPostCommitHook());
|
|
59440
60606
|
}
|
|
59441
60607
|
if (enableMemory) {
|
|
59442
|
-
await writeTextIfMissing4(
|
|
59443
|
-
await writeTextIfMissing4(
|
|
59444
|
-
await writeTextIfChanged4(
|
|
59445
|
-
await writeTextIfChanged4(
|
|
59446
|
-
await writeTextIfChanged4(
|
|
60608
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
|
|
60609
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
|
|
60610
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
|
|
60611
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
|
|
60612
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
|
|
59447
60613
|
}
|
|
59448
60614
|
if (enableTasks) {
|
|
59449
|
-
await writeTextIfChanged4(
|
|
59450
|
-
await writeTextIfChanged4(
|
|
59451
|
-
await writeTextIfChanged4(
|
|
59452
|
-
await writeTextIfChanged4(
|
|
59453
|
-
await writeTextIfChanged4(
|
|
59454
|
-
await writeTextIfChanged4(
|
|
60615
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
|
|
60616
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
|
|
60617
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
|
|
60618
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
|
|
60619
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
|
|
60620
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
|
|
59455
60621
|
}
|
|
59456
60622
|
if (enableSecurity) {
|
|
59457
|
-
await writeTextIfMissing4(
|
|
59458
|
-
await writeTextIfChanged4(
|
|
59459
|
-
await writeTextIfChanged4(
|
|
60623
|
+
await writeTextIfMissing4(path153.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
|
|
60624
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
|
|
60625
|
+
await writeTextIfChanged4(path153.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
|
|
59460
60626
|
if (manifest.modules?.security?.hooks?.prePush) {
|
|
59461
60627
|
await installManagedHook2(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
|
|
59462
60628
|
}
|
|
@@ -59507,13 +60673,13 @@ async function refreshServiceFiles(projectRoot, options) {
|
|
|
59507
60673
|
};
|
|
59508
60674
|
}
|
|
59509
60675
|
async function buildDashboard(projectRoot = process.cwd()) {
|
|
59510
|
-
const metaprojectRoot =
|
|
60676
|
+
const metaprojectRoot = path153.join(projectRoot, ".metaproject");
|
|
59511
60677
|
if (!await pathExists(metaprojectRoot)) {
|
|
59512
60678
|
throw new Error("Metaproject is not initialized. Run: keryx init");
|
|
59513
60679
|
}
|
|
59514
60680
|
const manifest = (await readManifest5(metaprojectRoot)).manifest;
|
|
59515
60681
|
const data = await collectDashboardData(metaprojectRoot);
|
|
59516
|
-
const dashboardPath =
|
|
60682
|
+
const dashboardPath = path153.join(metaprojectRoot, "keryx-dashboard.html");
|
|
59517
60683
|
await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
|
|
59518
60684
|
enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
|
|
59519
60685
|
enableGdctx: moduleEnabled2(manifest, "gdctx"),
|
|
@@ -59533,7 +60699,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
|
|
|
59533
60699
|
if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
|
|
59534
60700
|
return true;
|
|
59535
60701
|
}
|
|
59536
|
-
const hookPath =
|
|
60702
|
+
const hookPath = path153.join(projectRoot, ".git", "hooks", "post-commit");
|
|
59537
60703
|
if (!await pathExists(hookPath)) {
|
|
59538
60704
|
return false;
|
|
59539
60705
|
}
|
|
@@ -59553,11 +60719,11 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
59553
60719
|
if (testing) {
|
|
59554
60720
|
data.testing = testing;
|
|
59555
60721
|
}
|
|
59556
|
-
const wiki = await collectMarkdownPages(
|
|
60722
|
+
const wiki = await collectMarkdownPages(path153.join(metaprojectRoot, "wiki"), "wiki");
|
|
59557
60723
|
if (wiki.length > 0) {
|
|
59558
60724
|
data.wiki = { pages: wiki };
|
|
59559
60725
|
}
|
|
59560
|
-
const memory = await collectMarkdownPages(
|
|
60726
|
+
const memory = await collectMarkdownPages(path153.join(metaprojectRoot, "memory"), "memory");
|
|
59561
60727
|
if (memory.length > 0) {
|
|
59562
60728
|
data.memory = { entries: memory };
|
|
59563
60729
|
}
|
|
@@ -59572,19 +60738,19 @@ async function collectDashboardData(metaprojectRoot) {
|
|
|
59572
60738
|
return data;
|
|
59573
60739
|
}
|
|
59574
60740
|
async function collectTasksDashboardData(metaprojectRoot) {
|
|
59575
|
-
const flowsRoot2 =
|
|
60741
|
+
const flowsRoot2 = path153.join(metaprojectRoot, "flows");
|
|
59576
60742
|
if (!await pathExists(flowsRoot2)) {
|
|
59577
60743
|
return null;
|
|
59578
60744
|
}
|
|
59579
60745
|
let dirEntries;
|
|
59580
60746
|
try {
|
|
59581
|
-
dirEntries = (await
|
|
60747
|
+
dirEntries = (await readdir25(flowsRoot2, { withFileTypes: true })).filter((entry) => entry.isDirectory() && /^\d{3}-/.test(entry.name)).map((entry) => entry.name).sort();
|
|
59582
60748
|
} catch {
|
|
59583
60749
|
return null;
|
|
59584
60750
|
}
|
|
59585
60751
|
const flows = [];
|
|
59586
60752
|
for (const dir of dirEntries) {
|
|
59587
|
-
const flowPath =
|
|
60753
|
+
const flowPath = path153.join(flowsRoot2, dir, "flow.json");
|
|
59588
60754
|
if (!await pathExists(flowPath)) {
|
|
59589
60755
|
continue;
|
|
59590
60756
|
}
|
|
@@ -59592,7 +60758,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
|
|
|
59592
60758
|
const flow = JSON.parse(await readFile79(flowPath, "utf8"));
|
|
59593
60759
|
const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
|
|
59594
60760
|
let acTotal = 0;
|
|
59595
|
-
const acPath2 =
|
|
60761
|
+
const acPath2 = path153.join(flowsRoot2, dir, "acceptance-criteria.md");
|
|
59596
60762
|
if (await pathExists(acPath2)) {
|
|
59597
60763
|
const acContent = await readFile79(acPath2, "utf8");
|
|
59598
60764
|
acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
|
|
@@ -59646,7 +60812,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
59646
60812
|
"data/testing/context.md"
|
|
59647
60813
|
];
|
|
59648
60814
|
for (const href of staticHrefs) {
|
|
59649
|
-
const filePath =
|
|
60815
|
+
const filePath = path153.join(metaprojectRoot, ...href.split("/"));
|
|
59650
60816
|
if (!await pathExists(filePath)) {
|
|
59651
60817
|
continue;
|
|
59652
60818
|
}
|
|
@@ -59663,7 +60829,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
|
|
|
59663
60829
|
return docs;
|
|
59664
60830
|
}
|
|
59665
60831
|
async function collectHealthDashboardData(metaprojectRoot) {
|
|
59666
|
-
const reportPath2 =
|
|
60832
|
+
const reportPath2 = path153.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
|
|
59667
60833
|
if (!await pathExists(reportPath2)) {
|
|
59668
60834
|
return;
|
|
59669
60835
|
}
|
|
@@ -59772,8 +60938,8 @@ function metricToScope(metric) {
|
|
|
59772
60938
|
};
|
|
59773
60939
|
}
|
|
59774
60940
|
async function collectGraphDashboardData(metaprojectRoot) {
|
|
59775
|
-
const nodesPath =
|
|
59776
|
-
const edgesPath =
|
|
60941
|
+
const nodesPath = path153.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
|
|
60942
|
+
const edgesPath = path153.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
|
|
59777
60943
|
if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
|
|
59778
60944
|
return;
|
|
59779
60945
|
}
|
|
@@ -59824,8 +60990,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
|
|
|
59824
60990
|
};
|
|
59825
60991
|
}
|
|
59826
60992
|
async function collectTestingDashboardData(metaprojectRoot) {
|
|
59827
|
-
const reportPath2 =
|
|
59828
|
-
const contextPath =
|
|
60993
|
+
const reportPath2 = path153.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
|
|
60994
|
+
const contextPath = path153.join(metaprojectRoot, "data", "testing", "context.md");
|
|
59829
60995
|
if (await pathExists(reportPath2)) {
|
|
59830
60996
|
const report = JSON.parse(await readFile79(reportPath2, "utf8"));
|
|
59831
60997
|
const totalTests = numberOrUndefined(report.total);
|
|
@@ -59854,7 +61020,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
59854
61020
|
const files = await listMarkdownFiles(root);
|
|
59855
61021
|
const pages = [];
|
|
59856
61022
|
for (const filePath of files.slice(0, 40)) {
|
|
59857
|
-
const relativePath =
|
|
61023
|
+
const relativePath = path153.relative(root, filePath).split(path153.sep).join("/");
|
|
59858
61024
|
if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
|
|
59859
61025
|
continue;
|
|
59860
61026
|
}
|
|
@@ -59872,10 +61038,10 @@ async function collectMarkdownPages(root, hrefPrefix) {
|
|
|
59872
61038
|
return pages;
|
|
59873
61039
|
}
|
|
59874
61040
|
async function listMarkdownFiles(root) {
|
|
59875
|
-
const entries = await
|
|
61041
|
+
const entries = await readdir25(root, { withFileTypes: true });
|
|
59876
61042
|
const files = [];
|
|
59877
61043
|
for (const entry of entries) {
|
|
59878
|
-
const fullPath =
|
|
61044
|
+
const fullPath = path153.join(root, entry.name);
|
|
59879
61045
|
if (entry.isDirectory()) {
|
|
59880
61046
|
files.push(...await listMarkdownFiles(fullPath));
|
|
59881
61047
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -59923,7 +61089,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
59923
61089
|
const manifest = {
|
|
59924
61090
|
schemaVersion: 1,
|
|
59925
61091
|
standardVersion: STANDARD_VERSION,
|
|
59926
|
-
name: `${
|
|
61092
|
+
name: `${path153.basename(path153.dirname(metaprojectRoot))}-metaproject`,
|
|
59927
61093
|
createdBy: "keryx",
|
|
59928
61094
|
profiles: computeProfiles(enabledModuleKeys2),
|
|
59929
61095
|
paths: {
|
|
@@ -60024,11 +61190,11 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
|
|
|
60024
61190
|
metaproject: ".metaproject/index.md"
|
|
60025
61191
|
}
|
|
60026
61192
|
};
|
|
60027
|
-
await writeFile48(
|
|
61193
|
+
await writeFile48(path153.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
|
|
60028
61194
|
`, "utf8");
|
|
60029
61195
|
}
|
|
60030
61196
|
async function enableTasksInManifest(metaprojectRoot) {
|
|
60031
|
-
const manifestPath =
|
|
61197
|
+
const manifestPath = path153.join(metaprojectRoot, "metaproject.json");
|
|
60032
61198
|
if (!await pathExists(manifestPath)) {
|
|
60033
61199
|
return;
|
|
60034
61200
|
}
|
|
@@ -60051,7 +61217,7 @@ async function enableTasksInManifest(metaprojectRoot) {
|
|
|
60051
61217
|
`, "utf8");
|
|
60052
61218
|
}
|
|
60053
61219
|
async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
|
|
60054
|
-
const manifestPath =
|
|
61220
|
+
const manifestPath = path153.join(metaprojectRoot, "metaproject.json");
|
|
60055
61221
|
if (!await pathExists(manifestPath)) {
|
|
60056
61222
|
return;
|
|
60057
61223
|
}
|
|
@@ -60087,72 +61253,72 @@ async function updateRuntime(projectRoot) {
|
|
|
60087
61253
|
}
|
|
60088
61254
|
}
|
|
60089
61255
|
async function findRuntimeRoot(projectRoot) {
|
|
60090
|
-
const projectRuntime =
|
|
60091
|
-
if (await pathExists(
|
|
61256
|
+
const projectRuntime = path153.join(projectRoot, ".metaproject", "runtime", "keryx");
|
|
61257
|
+
if (await pathExists(path153.join(projectRuntime, ".git"))) {
|
|
60092
61258
|
return projectRuntime;
|
|
60093
61259
|
}
|
|
60094
61260
|
const home = process.env.HOME;
|
|
60095
61261
|
if (!home) {
|
|
60096
61262
|
return null;
|
|
60097
61263
|
}
|
|
60098
|
-
const globalRuntime =
|
|
60099
|
-
if (await pathExists(
|
|
61264
|
+
const globalRuntime = path153.join(home, ".keryx", "keryx");
|
|
61265
|
+
if (await pathExists(path153.join(globalRuntime, ".git"))) {
|
|
60100
61266
|
return globalRuntime;
|
|
60101
61267
|
}
|
|
60102
61268
|
return null;
|
|
60103
61269
|
}
|
|
60104
61270
|
async function createServiceDirs(metaprojectRoot, modules) {
|
|
60105
61271
|
const dirs = [
|
|
60106
|
-
|
|
60107
|
-
|
|
60108
|
-
|
|
60109
|
-
|
|
60110
|
-
|
|
61272
|
+
path153.join(metaprojectRoot, "core"),
|
|
61273
|
+
path153.join(metaprojectRoot, "hooks", "post-update.d"),
|
|
61274
|
+
path153.join(metaprojectRoot, "modules"),
|
|
61275
|
+
path153.join(metaprojectRoot, "rules"),
|
|
61276
|
+
path153.join(metaprojectRoot, "skills", "project-rules"),
|
|
60111
61277
|
...modules.enableGdgraph ? [
|
|
60112
|
-
|
|
60113
|
-
|
|
61278
|
+
path153.join(metaprojectRoot, "core", "gdgraph"),
|
|
61279
|
+
path153.join(metaprojectRoot, "skills", "gdgraph")
|
|
60114
61280
|
] : [],
|
|
60115
61281
|
...modules.enableGdctx ? [
|
|
60116
|
-
|
|
60117
|
-
|
|
61282
|
+
path153.join(metaprojectRoot, "core", "gdctx"),
|
|
61283
|
+
path153.join(metaprojectRoot, "skills", "gdctx")
|
|
60118
61284
|
] : [],
|
|
60119
61285
|
...modules.enableGdwiki ? [
|
|
60120
|
-
|
|
60121
|
-
|
|
61286
|
+
path153.join(metaprojectRoot, "skills", "gdwiki"),
|
|
61287
|
+
path153.join(metaprojectRoot, "wiki", "templates")
|
|
60122
61288
|
] : [],
|
|
60123
61289
|
...modules.enableHealth ? [
|
|
60124
|
-
|
|
60125
|
-
|
|
61290
|
+
path153.join(metaprojectRoot, "core", "health"),
|
|
61291
|
+
path153.join(metaprojectRoot, "skills", "health")
|
|
60126
61292
|
] : [],
|
|
60127
61293
|
...modules.enableTesting ? [
|
|
60128
|
-
|
|
60129
|
-
|
|
61294
|
+
path153.join(metaprojectRoot, "core", "testing"),
|
|
61295
|
+
path153.join(metaprojectRoot, "skills", "testing")
|
|
60130
61296
|
] : [],
|
|
60131
61297
|
...modules.enableMemory ? [
|
|
60132
|
-
|
|
60133
|
-
|
|
60134
|
-
|
|
61298
|
+
path153.join(metaprojectRoot, "core", "memory"),
|
|
61299
|
+
path153.join(metaprojectRoot, "skills", "memory"),
|
|
61300
|
+
path153.join(metaprojectRoot, "memory", "templates")
|
|
60135
61301
|
] : [],
|
|
60136
61302
|
...modules.enableTasks ? [
|
|
60137
|
-
|
|
60138
|
-
|
|
61303
|
+
path153.join(metaprojectRoot, "flows"),
|
|
61304
|
+
path153.join(metaprojectRoot, "skills", "flow")
|
|
60139
61305
|
] : [],
|
|
60140
61306
|
...modules.enableSecurity ? [
|
|
60141
|
-
|
|
61307
|
+
path153.join(metaprojectRoot, "core", "security")
|
|
60142
61308
|
] : [],
|
|
60143
61309
|
...modules.enableSac ? [
|
|
60144
|
-
|
|
61310
|
+
path153.join(metaprojectRoot, "skills", "sac")
|
|
60145
61311
|
] : []
|
|
60146
61312
|
];
|
|
60147
61313
|
await Promise.all(dirs.map((dir) => mkdir54(dir, { recursive: true })));
|
|
60148
61314
|
}
|
|
60149
61315
|
async function installGdgraphCoreScripts2(metaprojectRoot) {
|
|
60150
|
-
const gdgraphCoreRoot =
|
|
61316
|
+
const gdgraphCoreRoot = path153.join(metaprojectRoot, "core", "gdgraph");
|
|
60151
61317
|
await mkdir54(gdgraphCoreRoot, { recursive: true });
|
|
60152
61318
|
for (const file of GDGRAPH_CORE_SOURCES) {
|
|
60153
|
-
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`),
|
|
61319
|
+
await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path153.join(gdgraphCoreRoot, file));
|
|
60154
61320
|
}
|
|
60155
|
-
await writeTextIfChanged4(
|
|
61321
|
+
await writeTextIfChanged4(path153.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
|
|
60156
61322
|
}
|
|
60157
61323
|
async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
60158
61324
|
const hooksRoot = await resolveGitHooksRoot(projectRoot);
|
|
@@ -60160,7 +61326,7 @@ async function installManagedHook2(projectRoot, hookName, blockId, content) {
|
|
|
60160
61326
|
return;
|
|
60161
61327
|
}
|
|
60162
61328
|
await mkdir54(hooksRoot, { recursive: true });
|
|
60163
|
-
const hookPath =
|
|
61329
|
+
const hookPath = path153.join(hooksRoot, hookName);
|
|
60164
61330
|
const blockStart = `# keryx:${blockId}:begin`;
|
|
60165
61331
|
const blockEnd = `# keryx:${blockId}:end`;
|
|
60166
61332
|
const managedBlock = `${blockStart}
|
|
@@ -60181,7 +61347,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
|
|
|
60181
61347
|
if (!hooksRoot) {
|
|
60182
61348
|
return;
|
|
60183
61349
|
}
|
|
60184
|
-
const hookPath =
|
|
61350
|
+
const hookPath = path153.join(hooksRoot, hookName);
|
|
60185
61351
|
if (!await pathExists(hookPath)) {
|
|
60186
61352
|
return;
|
|
60187
61353
|
}
|
|
@@ -60203,7 +61369,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
|
|
|
60203
61369
|
if (!hooksRoot) {
|
|
60204
61370
|
return false;
|
|
60205
61371
|
}
|
|
60206
|
-
const hookPath =
|
|
61372
|
+
const hookPath = path153.join(hooksRoot, "pre-push");
|
|
60207
61373
|
if (!await pathExists(hookPath)) {
|
|
60208
61374
|
return false;
|
|
60209
61375
|
}
|
|
@@ -60218,7 +61384,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
|
|
|
60218
61384
|
return (await readFile79(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
|
|
60219
61385
|
}
|
|
60220
61386
|
async function readManifest5(metaprojectRoot) {
|
|
60221
|
-
const manifestPath =
|
|
61387
|
+
const manifestPath = path153.join(metaprojectRoot, "metaproject.json");
|
|
60222
61388
|
if (!await pathExists(manifestPath)) {
|
|
60223
61389
|
return {
|
|
60224
61390
|
exists: false,
|
|
@@ -60287,7 +61453,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
|
|
|
60287
61453
|
}
|
|
60288
61454
|
async function anyPathExists(root, candidates) {
|
|
60289
61455
|
for (const candidate of candidates) {
|
|
60290
|
-
if (await pathExists(
|
|
61456
|
+
if (await pathExists(path153.join(root, candidate))) {
|
|
60291
61457
|
return true;
|
|
60292
61458
|
}
|
|
60293
61459
|
}
|
|
@@ -60308,13 +61474,13 @@ function parseUpdateArgs(args2) {
|
|
|
60308
61474
|
};
|
|
60309
61475
|
}
|
|
60310
61476
|
async function runPostUpdateHooks(projectRoot) {
|
|
60311
|
-
const hooksDir =
|
|
61477
|
+
const hooksDir = path153.join(projectRoot, ".metaproject", "hooks", "post-update.d");
|
|
60312
61478
|
if (!await pathExists(hooksDir)) {
|
|
60313
61479
|
return;
|
|
60314
61480
|
}
|
|
60315
|
-
const entries = (await
|
|
61481
|
+
const entries = (await readdir25(hooksDir)).sort();
|
|
60316
61482
|
for (const entry of entries) {
|
|
60317
|
-
const hookPath =
|
|
61483
|
+
const hookPath = path153.join(hooksDir, entry);
|
|
60318
61484
|
try {
|
|
60319
61485
|
await accessExecutable(hookPath);
|
|
60320
61486
|
} catch {
|
|
@@ -60355,14 +61521,14 @@ async function writeTextIfChanged4(filePath, content) {
|
|
|
60355
61521
|
if (await pathExists(filePath) && await readFile79(filePath, "utf8") === content) {
|
|
60356
61522
|
return;
|
|
60357
61523
|
}
|
|
60358
|
-
await mkdir54(
|
|
61524
|
+
await mkdir54(path153.dirname(filePath), { recursive: true });
|
|
60359
61525
|
await writeFile48(filePath, content, "utf8");
|
|
60360
61526
|
}
|
|
60361
61527
|
async function writeTextIfMissing4(filePath, content) {
|
|
60362
61528
|
if (await pathExists(filePath)) {
|
|
60363
61529
|
return;
|
|
60364
61530
|
}
|
|
60365
|
-
await mkdir54(
|
|
61531
|
+
await mkdir54(path153.dirname(filePath), { recursive: true });
|
|
60366
61532
|
await writeFile48(filePath, content, "utf8");
|
|
60367
61533
|
}
|
|
60368
61534
|
async function copyFileIfChanged2(from, to) {
|
|
@@ -60370,7 +61536,7 @@ async function copyFileIfChanged2(from, to) {
|
|
|
60370
61536
|
if (await pathExists(to) && await readFile79(to, "utf8") === next) {
|
|
60371
61537
|
return;
|
|
60372
61538
|
}
|
|
60373
|
-
await mkdir54(
|
|
61539
|
+
await mkdir54(path153.dirname(to), { recursive: true });
|
|
60374
61540
|
await writeFile48(to, next, "utf8");
|
|
60375
61541
|
}
|
|
60376
61542
|
function runtimeSourcePath2(relativePath) {
|
|
@@ -60379,7 +61545,7 @@ function runtimeSourcePath2(relativePath) {
|
|
|
60379
61545
|
return directPath;
|
|
60380
61546
|
}
|
|
60381
61547
|
if (relativePath.startsWith("../")) {
|
|
60382
|
-
const packagedSourcePath =
|
|
61548
|
+
const packagedSourcePath = path153.join(path153.dirname(fileURLToPath7(import.meta.url)), "..", "src", relativePath.slice(3));
|
|
60383
61549
|
if (existsSync30(packagedSourcePath)) {
|
|
60384
61550
|
return packagedSourcePath;
|
|
60385
61551
|
}
|
|
@@ -60411,7 +61577,7 @@ function printHelp18() {
|
|
|
60411
61577
|
|
|
60412
61578
|
// src/commands/dashboard.ts
|
|
60413
61579
|
import { spawn as spawn6 } from "child_process";
|
|
60414
|
-
import
|
|
61580
|
+
import path154 from "path";
|
|
60415
61581
|
init_args();
|
|
60416
61582
|
async function dashboardCommand(args2 = []) {
|
|
60417
61583
|
const options = parseOptions(args2);
|
|
@@ -60422,7 +61588,7 @@ async function dashboardCommand(args2 = []) {
|
|
|
60422
61588
|
}
|
|
60423
61589
|
if (subcommand === "build") {
|
|
60424
61590
|
const result = await buildDashboard();
|
|
60425
|
-
const rel =
|
|
61591
|
+
const rel = path154.relative(process.cwd(), result.path);
|
|
60426
61592
|
console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
|
|
60427
61593
|
note(`Open it: keryx dashboard open`);
|
|
60428
61594
|
return;
|
|
@@ -60430,7 +61596,7 @@ async function dashboardCommand(args2 = []) {
|
|
|
60430
61596
|
if (subcommand === "open") {
|
|
60431
61597
|
const result = await buildDashboard();
|
|
60432
61598
|
await openFile(result.path);
|
|
60433
|
-
const rel =
|
|
61599
|
+
const rel = path154.relative(process.cwd(), result.path);
|
|
60434
61600
|
console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
|
|
60435
61601
|
return;
|
|
60436
61602
|
}
|
|
@@ -60479,7 +61645,7 @@ import { readFileSync as readFileSync10 } from "fs";
|
|
|
60479
61645
|
// src/agents/bootstrap.ts
|
|
60480
61646
|
import { mkdir as mkdir55, readFile as readFile80, writeFile as writeFile49 } from "fs/promises";
|
|
60481
61647
|
import { homedir as homedir7 } from "os";
|
|
60482
|
-
import
|
|
61648
|
+
import path155 from "path";
|
|
60483
61649
|
init_fs();
|
|
60484
61650
|
var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
|
|
60485
61651
|
var AGENT_BOOTSTRAP_END = "<!-- /keryx:global-bootstrap -->";
|
|
@@ -60489,35 +61655,35 @@ var AGENT_BOOTSTRAP_RUNTIMES = [
|
|
|
60489
61655
|
aliases: ["claude-code"],
|
|
60490
61656
|
label: "Claude Code",
|
|
60491
61657
|
fileName: "CLAUDE.md",
|
|
60492
|
-
filePath: (homeRoot) =>
|
|
61658
|
+
filePath: (homeRoot) => path155.join(homeRoot, ".claude", "CLAUDE.md")
|
|
60493
61659
|
},
|
|
60494
61660
|
{
|
|
60495
61661
|
id: "opencode",
|
|
60496
61662
|
aliases: ["open-code"],
|
|
60497
61663
|
label: "OpenCode",
|
|
60498
61664
|
fileName: "AGENTS.md",
|
|
60499
|
-
filePath: (homeRoot) =>
|
|
61665
|
+
filePath: (homeRoot) => path155.join(homeRoot, ".config", "opencode", "AGENTS.md")
|
|
60500
61666
|
},
|
|
60501
61667
|
{
|
|
60502
61668
|
id: "zcode",
|
|
60503
61669
|
aliases: ["zed", "zed-code"],
|
|
60504
61670
|
label: "ZCode",
|
|
60505
61671
|
fileName: "AGENTS.md",
|
|
60506
|
-
filePath: (homeRoot) =>
|
|
61672
|
+
filePath: (homeRoot) => path155.join(homeRoot, ".zcode", "AGENTS.md")
|
|
60507
61673
|
},
|
|
60508
61674
|
{
|
|
60509
61675
|
id: "codex",
|
|
60510
61676
|
aliases: [],
|
|
60511
61677
|
label: "Codex",
|
|
60512
61678
|
fileName: "AGENTS.md",
|
|
60513
|
-
filePath: (homeRoot) =>
|
|
61679
|
+
filePath: (homeRoot) => path155.join(homeRoot, ".codex", "AGENTS.md")
|
|
60514
61680
|
},
|
|
60515
61681
|
{
|
|
60516
61682
|
id: "antigravity",
|
|
60517
61683
|
aliases: ["antigravuty", "antigravity-code"],
|
|
60518
61684
|
label: "Antigravity",
|
|
60519
61685
|
fileName: "AGENTS.md",
|
|
60520
|
-
filePath: (homeRoot) =>
|
|
61686
|
+
filePath: (homeRoot) => path155.join(homeRoot, ".config", "antigravity", "AGENTS.md")
|
|
60521
61687
|
}
|
|
60522
61688
|
];
|
|
60523
61689
|
function agentBootstrapRuntimeIds() {
|
|
@@ -60565,7 +61731,7 @@ async function installAgentBootstrap(runtime, options = {}) {
|
|
|
60565
61731
|
const dryRun = options.dryRun === true;
|
|
60566
61732
|
const wrote = next !== current;
|
|
60567
61733
|
if (wrote && !dryRun) {
|
|
60568
|
-
await mkdir55(
|
|
61734
|
+
await mkdir55(path155.dirname(filePath), { recursive: true });
|
|
60569
61735
|
await writeFile49(filePath, next, "utf8");
|
|
60570
61736
|
}
|
|
60571
61737
|
const status = dryRun ? statusFromContent(runtime, filePath, exists2, next) : await agentBootstrapStatus(runtime, homeRoot);
|
|
@@ -60913,7 +62079,7 @@ function printBootstrapHelp() {
|
|
|
60913
62079
|
// src/commands/metrics.ts
|
|
60914
62080
|
init_args();
|
|
60915
62081
|
import { readFile as readFile81 } from "fs/promises";
|
|
60916
|
-
import
|
|
62082
|
+
import path157 from "path";
|
|
60917
62083
|
|
|
60918
62084
|
// src/metrics/benchmark.ts
|
|
60919
62085
|
var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
|
|
@@ -61692,7 +62858,7 @@ function buildContainmentManifest(inputs, options = {}) {
|
|
|
61692
62858
|
|
|
61693
62859
|
// src/metrics/oracle-runner.ts
|
|
61694
62860
|
import { mkdir as mkdir56, writeFile as writeFile50 } from "fs/promises";
|
|
61695
|
-
import
|
|
62861
|
+
import path156 from "path";
|
|
61696
62862
|
|
|
61697
62863
|
// src/metrics/ir.ts
|
|
61698
62864
|
function toIdSet(ids) {
|
|
@@ -62134,9 +63300,9 @@ function buildEvidenceBundle(input2, options = {}) {
|
|
|
62134
63300
|
async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
|
|
62135
63301
|
const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
|
|
62136
63302
|
const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
62137
|
-
const dir =
|
|
63303
|
+
const dir = path156.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
|
|
62138
63304
|
await mkdir56(dir, { recursive: true });
|
|
62139
|
-
const write = (name, value) => writeFile50(
|
|
63305
|
+
const write = (name, value) => writeFile50(path156.join(dir, name), `${JSON.stringify(value, null, 2)}
|
|
62140
63306
|
`, "utf8");
|
|
62141
63307
|
await Promise.all([
|
|
62142
63308
|
write("inputs.json", bundle.inputs),
|
|
@@ -62176,7 +63342,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
62176
63342
|
console.log("# metrics status");
|
|
62177
63343
|
console.log("");
|
|
62178
63344
|
console.log(`root: ${root}`);
|
|
62179
|
-
console.log(`enabled: ${await Bun.file(
|
|
63345
|
+
console.log(`enabled: ${await Bun.file(path157.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
|
|
62180
63346
|
const latest2 = await readLatestPointer(root);
|
|
62181
63347
|
console.log(`latest: ${latest2.status}`);
|
|
62182
63348
|
return;
|
|
@@ -62188,7 +63354,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
62188
63354
|
process.exitCode = 1;
|
|
62189
63355
|
return;
|
|
62190
63356
|
}
|
|
62191
|
-
const record2 = JSON.parse(await readFile81(
|
|
63357
|
+
const record2 = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
62192
63358
|
const result = validateRunRecord(record2);
|
|
62193
63359
|
console.log(result.valid ? "valid: yes" : "valid: no");
|
|
62194
63360
|
for (const error2 of result.errors)
|
|
@@ -62213,7 +63379,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
62213
63379
|
process.exitCode = 1;
|
|
62214
63380
|
return;
|
|
62215
63381
|
}
|
|
62216
|
-
const file =
|
|
63382
|
+
const file = path157.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
|
|
62217
63383
|
if (!await Bun.file(file).exists()) {
|
|
62218
63384
|
console.error(`Run not found: ${runId}`);
|
|
62219
63385
|
process.exitCode = 1;
|
|
@@ -62230,8 +63396,8 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
62230
63396
|
process.exitCode = 1;
|
|
62231
63397
|
return;
|
|
62232
63398
|
}
|
|
62233
|
-
const a = JSON.parse(await readFile81(
|
|
62234
|
-
const b = JSON.parse(await readFile81(
|
|
63399
|
+
const a = JSON.parse(await readFile81(path157.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
|
|
63400
|
+
const b = JSON.parse(await readFile81(path157.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
|
|
62235
63401
|
const comparison = compareExecutionRuns(a, b);
|
|
62236
63402
|
console.log(stableJson(comparison));
|
|
62237
63403
|
return;
|
|
@@ -62265,8 +63431,8 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
62265
63431
|
return;
|
|
62266
63432
|
}
|
|
62267
63433
|
const template = createPairedBenchmarkTemplate(taskIds);
|
|
62268
|
-
await Bun.write(
|
|
62269
|
-
console.log(`manifest: ${
|
|
63434
|
+
await Bun.write(path157.resolve(projectRoot, out), stableJson(template));
|
|
63435
|
+
console.log(`manifest: ${path157.relative(projectRoot, path157.resolve(projectRoot, out))}`);
|
|
62270
63436
|
return;
|
|
62271
63437
|
}
|
|
62272
63438
|
if (subcommand === "benchmark" && args2[1] === "run") {
|
|
@@ -62280,7 +63446,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
62280
63446
|
process.exitCode = 1;
|
|
62281
63447
|
return;
|
|
62282
63448
|
}
|
|
62283
|
-
const raw = JSON.parse(await readFile81(
|
|
63449
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
62284
63450
|
const input2 = Array.isArray(raw) ? raw : raw.runs ?? [];
|
|
62285
63451
|
const result = validatePairedBenchmark(input2);
|
|
62286
63452
|
console.log(stableJson(result));
|
|
@@ -62292,7 +63458,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
|
|
|
62292
63458
|
process.exitCode = 1;
|
|
62293
63459
|
}
|
|
62294
63460
|
async function loadAffectedSets(projectRoot, file) {
|
|
62295
|
-
const raw = JSON.parse(await readFile81(
|
|
63461
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
62296
63462
|
const map = new Map;
|
|
62297
63463
|
for (const entry of raw.targets ?? []) {
|
|
62298
63464
|
if (typeof entry.target === "string")
|
|
@@ -62363,7 +63529,7 @@ async function runHarnessLayer(projectRoot, args2, ladder) {
|
|
|
62363
63529
|
let tasks;
|
|
62364
63530
|
let model;
|
|
62365
63531
|
try {
|
|
62366
|
-
const raw = JSON.parse(await readFile81(
|
|
63532
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, resultsPath), "utf8"));
|
|
62367
63533
|
tasks = raw.tasks ?? [];
|
|
62368
63534
|
model = raw.model;
|
|
62369
63535
|
} catch (error2) {
|
|
@@ -62394,7 +63560,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args2, ladder) {
|
|
|
62394
63560
|
let cases;
|
|
62395
63561
|
let model;
|
|
62396
63562
|
try {
|
|
62397
|
-
const raw = JSON.parse(await readFile81(
|
|
63563
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, resultsPath), "utf8"));
|
|
62398
63564
|
cases = raw.cases ?? [];
|
|
62399
63565
|
model = raw.model;
|
|
62400
63566
|
} catch (error2) {
|
|
@@ -62419,7 +63585,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args2, ladder) {
|
|
|
62419
63585
|
let cases;
|
|
62420
63586
|
let model;
|
|
62421
63587
|
try {
|
|
62422
|
-
const raw = JSON.parse(await readFile81(
|
|
63588
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, resultsPath), "utf8"));
|
|
62423
63589
|
cases = raw.cases ?? [];
|
|
62424
63590
|
model = raw.model;
|
|
62425
63591
|
} catch (error2) {
|
|
@@ -62444,7 +63610,7 @@ async function runSafetyContainmentLayer(projectRoot, args2, ladder, caseClass)
|
|
|
62444
63610
|
let cases;
|
|
62445
63611
|
let model;
|
|
62446
63612
|
try {
|
|
62447
|
-
const raw = JSON.parse(await readFile81(
|
|
63613
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, resultsPath), "utf8"));
|
|
62448
63614
|
cases = raw.cases ?? [];
|
|
62449
63615
|
model = raw.model;
|
|
62450
63616
|
} catch (error2) {
|
|
@@ -62506,12 +63672,12 @@ async function runGdgraphLayer(projectRoot, args2, ladder) {
|
|
|
62506
63672
|
}
|
|
62507
63673
|
const manifests = buildOracleManifestsByGold(inputs, { ladder });
|
|
62508
63674
|
if (outDir) {
|
|
62509
|
-
const resolvedOut =
|
|
63675
|
+
const resolvedOut = path157.resolve(projectRoot, outDir);
|
|
62510
63676
|
for (const input2 of inputs) {
|
|
62511
63677
|
for (const named of input2.golds) {
|
|
62512
63678
|
const bundle = buildEvidenceBundle({ target: input2.target, system: input2.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
|
|
62513
|
-
const dir = await persistEvidenceBundle(
|
|
62514
|
-
console.error(`bundle[${named.kind}]: ${
|
|
63679
|
+
const dir = await persistEvidenceBundle(path157.join(resolvedOut, named.kind), bundle, ladder);
|
|
63680
|
+
console.error(`bundle[${named.kind}]: ${path157.relative(projectRoot, dir)}`);
|
|
62515
63681
|
}
|
|
62516
63682
|
}
|
|
62517
63683
|
}
|
|
@@ -62535,7 +63701,7 @@ async function runGdgraphLayer(projectRoot, args2, ladder) {
|
|
|
62535
63701
|
return allValid;
|
|
62536
63702
|
}
|
|
62537
63703
|
async function loadCoverageMap2(projectRoot, file) {
|
|
62538
|
-
const raw = JSON.parse(await readFile81(
|
|
63704
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
62539
63705
|
return raw.coverageMap ?? {};
|
|
62540
63706
|
}
|
|
62541
63707
|
async function runTestingLayer(projectRoot, args2, ladder) {
|
|
@@ -62572,7 +63738,7 @@ async function runTestingLayer(projectRoot, args2, ladder) {
|
|
|
62572
63738
|
return result.valid;
|
|
62573
63739
|
}
|
|
62574
63740
|
async function loadMemoryGoldK(projectRoot, file) {
|
|
62575
|
-
const raw = JSON.parse(await readFile81(
|
|
63741
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
62576
63742
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
|
|
62577
63743
|
}
|
|
62578
63744
|
async function runMemoryLayer(projectRoot, args2, ladder) {
|
|
@@ -62609,11 +63775,11 @@ async function runMemoryLayer(projectRoot, args2, ladder) {
|
|
|
62609
63775
|
return result.valid;
|
|
62610
63776
|
}
|
|
62611
63777
|
async function loadWikiGoldK(projectRoot, file) {
|
|
62612
|
-
const raw = JSON.parse(await readFile81(
|
|
63778
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
62613
63779
|
return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
|
|
62614
63780
|
}
|
|
62615
63781
|
async function loadWikiGroundedness(projectRoot, file) {
|
|
62616
|
-
const raw = JSON.parse(await readFile81(
|
|
63782
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
62617
63783
|
const map = new Map;
|
|
62618
63784
|
for (const entry of raw.targets ?? []) {
|
|
62619
63785
|
if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
|
|
@@ -62669,7 +63835,7 @@ async function runWikiLayer(projectRoot, args2, ladder) {
|
|
|
62669
63835
|
return result.valid;
|
|
62670
63836
|
}
|
|
62671
63837
|
async function loadGdctxFacts(projectRoot, file) {
|
|
62672
|
-
const raw = JSON.parse(await readFile81(
|
|
63838
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
|
|
62673
63839
|
const inputs = [];
|
|
62674
63840
|
for (const entry of raw.inputs ?? []) {
|
|
62675
63841
|
if (typeof entry.input === "string") {
|
|
@@ -62707,7 +63873,7 @@ async function collect(projectRoot, args2) {
|
|
|
62707
63873
|
process.exitCode = 1;
|
|
62708
63874
|
return;
|
|
62709
63875
|
}
|
|
62710
|
-
const raw = JSON.parse(await readFile81(
|
|
63876
|
+
const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, eventFile), "utf8"));
|
|
62711
63877
|
const events2 = Array.isArray(raw) ? raw : raw.events;
|
|
62712
63878
|
const startedAt = optionValue(args2, "--started-at") ?? events2[0]?.timestamp_utc ?? new Date().toISOString();
|
|
62713
63879
|
const finishedAt = optionValue(args2, "--finished-at") ?? events2.at(-1)?.timestamp_utc ?? startedAt;
|
|
@@ -62723,11 +63889,11 @@ async function collect(projectRoot, args2) {
|
|
|
62723
63889
|
parentRunId: optionValue(args2, "--parent-run-id") ?? null
|
|
62724
63890
|
});
|
|
62725
63891
|
const result = await writeRunArtifacts(metricsRoot(projectRoot), record2, { cwd: projectRoot });
|
|
62726
|
-
console.log(`json: ${
|
|
62727
|
-
console.log(`markdown: ${
|
|
63892
|
+
console.log(`json: ${path157.relative(projectRoot, result.jsonPath)}`);
|
|
63893
|
+
console.log(`markdown: ${path157.relative(projectRoot, result.markdownPath)}`);
|
|
62728
63894
|
}
|
|
62729
63895
|
function metricsRoot(projectRoot) {
|
|
62730
|
-
return
|
|
63896
|
+
return path157.join(projectRoot, ".metaproject", "data", "metrics");
|
|
62731
63897
|
}
|
|
62732
63898
|
function printMetricsHelp() {
|
|
62733
63899
|
console.log(`keryx metrics
|
|
@@ -62833,193 +63999,6 @@ init_session_wrap_up();
|
|
|
62833
63999
|
init_proposal_evidence();
|
|
62834
64000
|
init_review_confirm_token();
|
|
62835
64001
|
init_store3();
|
|
62836
|
-
|
|
62837
|
-
// src/sac/catch-up.ts
|
|
62838
|
-
init_fs();
|
|
62839
|
-
init_config_dir();
|
|
62840
|
-
init_paths();
|
|
62841
|
-
init_slate();
|
|
62842
|
-
init_store3();
|
|
62843
|
-
init_proposal_lifecycle();
|
|
62844
|
-
init_workspace_service();
|
|
62845
|
-
import { randomUUID as randomUUID28 } from "crypto";
|
|
62846
|
-
import { readdir as readdir25 } from "fs/promises";
|
|
62847
|
-
import path157 from "path";
|
|
62848
|
-
|
|
62849
|
-
// src/sac/lifecycle-flag.ts
|
|
62850
|
-
init_store();
|
|
62851
|
-
init_service3();
|
|
62852
|
-
init_decision_dedup();
|
|
62853
|
-
init_workspace_service();
|
|
62854
|
-
import { randomUUID as randomUUID27 } from "crypto";
|
|
62855
|
-
function normalize3(raw) {
|
|
62856
|
-
return raw.replace(/^\.\//, "");
|
|
62857
|
-
}
|
|
62858
|
-
function isStillPresent(recorded, valid) {
|
|
62859
|
-
const normalized = normalize3(recorded);
|
|
62860
|
-
return valid.has(normalized) || valid.has(moduleNameFromProjectPath(normalized));
|
|
62861
|
-
}
|
|
62862
|
-
async function computeLifecycleFlags(cwd, now = () => new Date) {
|
|
62863
|
-
const valid = await validModuleNames(cwd);
|
|
62864
|
-
if (valid === undefined)
|
|
62865
|
-
return [];
|
|
62866
|
-
const flaggedAt = now().toISOString();
|
|
62867
|
-
const flags = [];
|
|
62868
|
-
const workspaceService = new WorkspaceService({
|
|
62869
|
-
workspaceRoot: cwd,
|
|
62870
|
-
authorizationServer: localWorkspaceAuthorizationServer(),
|
|
62871
|
-
strictGuard: { mode: "strict", availability: "available", decision: "pass", policyRevision: "local-offline-v1" }
|
|
62872
|
-
});
|
|
62873
|
-
const workspaces = await workspaceService.list({ request: undefined, requestCorrelationId: randomUUID27(), includeArchived: true });
|
|
62874
|
-
for (const workspace of workspaces) {
|
|
62875
|
-
const component = workspace.resources.find((r) => r.kind === "component")?.uri;
|
|
62876
|
-
if (component !== undefined && !isStillPresent(component, valid)) {
|
|
62877
|
-
flags.push({ kind: "workspace", ref: workspace.id, missingComponent: normalize3(component), flaggedAt });
|
|
62878
|
-
}
|
|
62879
|
-
}
|
|
62880
|
-
for (const entry of await collectEntries(cwd)) {
|
|
62881
|
-
const module = entry.scopes.module;
|
|
62882
|
-
if (module !== null && module.length > 0 && !isStillPresent(module, valid)) {
|
|
62883
|
-
flags.push({ kind: "memory-entry", ref: entry.relativePath, missingComponent: normalize3(module), flaggedAt });
|
|
62884
|
-
}
|
|
62885
|
-
}
|
|
62886
|
-
for (const decision of await collectWikiDecisionEntries(cwd)) {
|
|
62887
|
-
const module = decision.scopes.module;
|
|
62888
|
-
if (module !== null && module.length > 0 && !isStillPresent(module, valid)) {
|
|
62889
|
-
flags.push({ kind: "wiki-decision", ref: decision.relativePath, missingComponent: normalize3(module), flaggedAt });
|
|
62890
|
-
}
|
|
62891
|
-
}
|
|
62892
|
-
return flags;
|
|
62893
|
-
}
|
|
62894
|
-
|
|
62895
|
-
// src/sac/catch-up.ts
|
|
62896
|
-
async function buildCatchUp(input2) {
|
|
62897
|
-
const [proposals, sessionCategories, lifecycleFlagsAll] = await Promise.all([
|
|
62898
|
-
collectProposals(input2.cwd, input2.workspaceId),
|
|
62899
|
-
collectSessionCategories(input2.cwd),
|
|
62900
|
-
computeLifecycleFlags(input2.cwd)
|
|
62901
|
-
]);
|
|
62902
|
-
const lifecycleFlags = input2.workspaceId === undefined ? lifecycleFlagsAll : lifecycleFlagsAll.filter((flag) => flag.kind !== "workspace" || flag.ref === input2.workspaceId);
|
|
62903
|
-
return { proposals, ...sessionCategories, lifecycleFlags };
|
|
62904
|
-
}
|
|
62905
|
-
async function collectProposals(cwd, workspaceId) {
|
|
62906
|
-
const authorizationServer = localWorkspaceAuthorizationServer();
|
|
62907
|
-
const actor = await authorizationServer.actorContextFor(undefined, randomUUID28());
|
|
62908
|
-
if (!actor)
|
|
62909
|
-
throw new Error("trusted ActorContext is required for catch-up");
|
|
62910
|
-
const proposalService = createLocalProposalLifecycleService(cwd);
|
|
62911
|
-
const groups = await proposalService.listVisibleProposedProposals(actor);
|
|
62912
|
-
const scoped = workspaceId === undefined ? groups : groups.filter((group) => group.workspace.id === workspaceId);
|
|
62913
|
-
const flattened = scoped.flatMap((group) => group.proposals.map((proposal) => ({ group, proposal })));
|
|
62914
|
-
return Promise.all(flattened.map(async ({ group, proposal }) => {
|
|
62915
|
-
const fresh = await proposalService.isEvidenceFresh(proposal, actor);
|
|
62916
|
-
return { type: "proposal", workspaceId: group.workspace.id, proposalId: proposal.id, fresh };
|
|
62917
|
-
}));
|
|
62918
|
-
}
|
|
62919
|
-
async function classifySession(session) {
|
|
62920
|
-
const dir = sessionDir(session.projectPath, session.id);
|
|
62921
|
-
if (await isLockHeld(slateLockPath(dir)))
|
|
62922
|
-
return;
|
|
62923
|
-
const terminalState = await readTerminalState(dir);
|
|
62924
|
-
if (terminalState !== undefined) {
|
|
62925
|
-
const workspaceId2 = (await safeReadSlate(dir))?.workspaceId;
|
|
62926
|
-
return { kind: "blocked", item: { type: "blocked", sessionId: session.id, ...workspaceId2 !== undefined ? { workspaceId: workspaceId2 } : {}, terminalState } };
|
|
62927
|
-
}
|
|
62928
|
-
const unboundCandidate = await readNewestUnboundCandidate(dir);
|
|
62929
|
-
if (unboundCandidate !== undefined) {
|
|
62930
|
-
return {
|
|
62931
|
-
kind: "unbound-candidate",
|
|
62932
|
-
item: { type: "unbound-candidate", sessionId: session.id, evidencePath: unboundCandidate.evidencePath, summary: unboundCandidate.summary }
|
|
62933
|
-
};
|
|
62934
|
-
}
|
|
62935
|
-
if (!await isSlateEngaged(dir))
|
|
62936
|
-
return;
|
|
62937
|
-
const workspaceId = (await safeReadSlate(dir))?.workspaceId;
|
|
62938
|
-
return { kind: "unknown", item: { type: "unknown", sessionId: session.id, ...workspaceId !== undefined ? { workspaceId } : {}, lastSeenAt: session.updatedAt } };
|
|
62939
|
-
}
|
|
62940
|
-
async function collectSessionCategories(cwd) {
|
|
62941
|
-
const classified = await Promise.all(listSessions(cwd).map((session) => classifySession(session)));
|
|
62942
|
-
const blocked2 = [];
|
|
62943
|
-
const unboundCandidates = [];
|
|
62944
|
-
const unknown = [];
|
|
62945
|
-
for (const category of classified) {
|
|
62946
|
-
if (category === undefined)
|
|
62947
|
-
continue;
|
|
62948
|
-
if (category.kind === "blocked")
|
|
62949
|
-
blocked2.push(category.item);
|
|
62950
|
-
else if (category.kind === "unbound-candidate")
|
|
62951
|
-
unboundCandidates.push(category.item);
|
|
62952
|
-
else
|
|
62953
|
-
unknown.push(category.item);
|
|
62954
|
-
}
|
|
62955
|
-
return { blocked: blocked2, unboundCandidates, unknown };
|
|
62956
|
-
}
|
|
62957
|
-
async function isSlateEngaged(dir) {
|
|
62958
|
-
if (await pathExists(path157.join(dir, "slate.json")))
|
|
62959
|
-
return true;
|
|
62960
|
-
if (await pathExists(path157.join(dir, "terminal-state.json")))
|
|
62961
|
-
return true;
|
|
62962
|
-
try {
|
|
62963
|
-
const entries = await readdir25(path157.join(dir, "slate-archive"));
|
|
62964
|
-
return entries.length > 0;
|
|
62965
|
-
} catch {
|
|
62966
|
-
return false;
|
|
62967
|
-
}
|
|
62968
|
-
}
|
|
62969
|
-
async function safeReadSlate(dir) {
|
|
62970
|
-
try {
|
|
62971
|
-
return await readSlate(dir);
|
|
62972
|
-
} catch {
|
|
62973
|
-
return;
|
|
62974
|
-
}
|
|
62975
|
-
}
|
|
62976
|
-
async function readTerminalState(dir) {
|
|
62977
|
-
const result = readConfigFile(path157.join(dir, "terminal-state.json"));
|
|
62978
|
-
if (!result.ok) {
|
|
62979
|
-
return;
|
|
62980
|
-
}
|
|
62981
|
-
try {
|
|
62982
|
-
return JSON.parse(result.text);
|
|
62983
|
-
} catch {
|
|
62984
|
-
return;
|
|
62985
|
-
}
|
|
62986
|
-
}
|
|
62987
|
-
async function readNewestUnboundCandidate(dir) {
|
|
62988
|
-
const archiveDir = path157.join(dir, "slate-archive");
|
|
62989
|
-
let entries;
|
|
62990
|
-
try {
|
|
62991
|
-
entries = (await readdir25(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
|
|
62992
|
-
} catch {
|
|
62993
|
-
return;
|
|
62994
|
-
}
|
|
62995
|
-
entries.sort();
|
|
62996
|
-
for (let i = entries.length - 1;i >= 0; i--) {
|
|
62997
|
-
const evidencePath = path157.join(archiveDir, entries[i]);
|
|
62998
|
-
const result = readConfigFile(evidencePath);
|
|
62999
|
-
if (!result.ok) {
|
|
63000
|
-
continue;
|
|
63001
|
-
}
|
|
63002
|
-
try {
|
|
63003
|
-
const parsed = JSON.parse(result.text);
|
|
63004
|
-
if (parsed.recordType !== "unbound-candidate")
|
|
63005
|
-
continue;
|
|
63006
|
-
return { evidencePath, summary: summarizeUnboundCandidate(parsed.groups) };
|
|
63007
|
-
} catch {
|
|
63008
|
-
continue;
|
|
63009
|
-
}
|
|
63010
|
-
}
|
|
63011
|
-
return;
|
|
63012
|
-
}
|
|
63013
|
-
function summarizeUnboundCandidate(groups) {
|
|
63014
|
-
const safeGroups = groups ?? [];
|
|
63015
|
-
if (safeGroups.length === 0)
|
|
63016
|
-
return "no seeds captured";
|
|
63017
|
-
const seedCount = safeGroups.reduce((sum, group) => sum + (group.seeds?.length ?? 0), 0);
|
|
63018
|
-
const kinds = safeGroups.map((group) => typeof group.kind === "string" ? group.kind : "unknown").join(", ");
|
|
63019
|
-
return `${seedCount} untriaged seed(s) across ${safeGroups.length} kind(s) (${kinds})`;
|
|
63020
|
-
}
|
|
63021
|
-
|
|
63022
|
-
// src/commands/workspace.ts
|
|
63023
64002
|
var PROPOSAL_KINDS2 = ["decision", "wiki-update", "memory-entry", "follow-up", "contract-change", "risk"];
|
|
63024
64003
|
function service5() {
|
|
63025
64004
|
return new WorkspaceService({
|