@mrciphersmith/keryx 0.2.43 → 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.
Files changed (2) hide show
  1. package/dist/cli.js +789 -389
  2. 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 decision = resolveApprovalDecision({ mode, risk, destructive, credentials });
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 randomUUID23 } from "crypto";
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 destructive = trimmed.length > 0 && isDestructiveCommand(trimmed);
47437
+ const neverRemember = trimmed.length > 0 && (isDestructiveCommand(trimmed) || touchesSacConfirmReview(trimmed));
47420
47438
  return {
47421
47439
  exact,
47422
47440
  prefix,
47423
- offerExact: !destructive && validateShellPattern(exact).ok,
47424
- offerPrefix: !destructive && validateShellPattern(prefix).ok
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.43",
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: {
@@ -49832,8 +49855,195 @@ function openModal(otui, chrome, input2) {
49832
49855
  // src/tui/inspector-sources.ts
49833
49856
  init_store2();
49834
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();
49835
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();
49836
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();
49837
50047
 
49838
50048
  // src/session/index.ts
49839
50049
  init_paths();
@@ -49912,7 +50122,7 @@ async function loadInspectorWorkspaces(cwd) {
49912
50122
  authorizationServer: localWorkspaceAuthorizationServer(),
49913
50123
  strictGuard: localSacGuard()
49914
50124
  });
49915
- const listed = await service5.list({ request: undefined, requestCorrelationId: randomUUID22() });
50125
+ const listed = await service5.list({ request: undefined, requestCorrelationId: randomUUID24() });
49916
50126
  return listed.map(workspaceFromManifest);
49917
50127
  } catch {
49918
50128
  return [];
@@ -49925,7 +50135,7 @@ async function loadInspectorWorkspace(cwd, workspaceId) {
49925
50135
  authorizationServer: localWorkspaceAuthorizationServer(),
49926
50136
  strictGuard: localSacGuard()
49927
50137
  });
49928
- const manifest = await service5.show({ request: undefined, requestCorrelationId: randomUUID22(), workspaceId });
50138
+ const manifest = await service5.show({ request: undefined, requestCorrelationId: randomUUID24(), workspaceId });
49929
50139
  return workspaceFromManifest(manifest);
49930
50140
  } catch {
49931
50141
  return;
@@ -49998,6 +50208,16 @@ function formatWorkspaceLines(workspaces) {
49998
50208
  const width = workspaces.reduce((max, workspace) => Math.max(max, workspace.id.length), 0);
49999
50209
  return workspaces.map((workspace) => `${workspace.id.padEnd(width)} ${workspace.status} ${workspace.title}`);
50000
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
+ }
50001
50221
  function formatSessionFlowLines(flows) {
50002
50222
  if (flows.length === 0) {
50003
50223
  return ["No flows recorded in this session."];
@@ -50490,6 +50710,332 @@ function openWorkspace(otui, chrome, options) {
50490
50710
  return presentWorkspace((hostOtui, hostChrome, input2) => openModal(hostOtui, hostChrome, input2), otui, chrome, options);
50491
50711
  }
50492
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
+
50493
51039
  // src/tui/tui-shell.ts
50494
51040
  init_slate();
50495
51041
 
@@ -50854,6 +51400,11 @@ var AGENT_SLASH_COMMANDS = [
50854
51400
  description: "Show this session's SAC workspace and its slates",
50855
51401
  modes: AGENT_ONLY
50856
51402
  },
51403
+ {
51404
+ name: "/review",
51405
+ description: "Show project-wide items needing review (proposals, blocked sessions)",
51406
+ modes: AGENT_ONLY
51407
+ },
50857
51408
  {
50858
51409
  name: "/compact",
50859
51410
  description: "Compact model context \u2014 /compact [focus] (archive kept)",
@@ -51217,10 +51768,10 @@ init_shell_config();
51217
51768
  // src/lib/permission-mode-config.ts
51218
51769
  init_permission_mode();
51219
51770
  init_config_dir();
51220
- import path145 from "path";
51771
+ import path146 from "path";
51221
51772
  var EMPTY3 = { schemaVersion: 1, projects: {} };
51222
51773
  function permissionModeConfigPath(dir) {
51223
- return path145.join(keryxConfigDir(dir), "permission-mode.json");
51774
+ return path146.join(keryxConfigDir(dir), "permission-mode.json");
51224
51775
  }
51225
51776
  function withRegistryLock2(dir, fn) {
51226
51777
  return withFileLock(`${permissionModeConfigPath(dir)}.lock`, fn, {
@@ -51565,7 +52116,7 @@ function showComposerChoice(otui, r, dock, request) {
51565
52116
  // src/lib/version-check.ts
51566
52117
  init_config_dir();
51567
52118
  init_fs();
51568
- import path146 from "path";
52119
+ import path147 from "path";
51569
52120
  var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
51570
52121
  var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
51571
52122
  var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
@@ -51767,7 +52318,7 @@ async function checkVersion(options) {
51767
52318
  const now = options.now ?? Date.now;
51768
52319
  const timestamp = now();
51769
52320
  const configDir = ensureKeryxConfigDir(options.cacheDir);
51770
- const cacheFile = path146.join(configDir, "version-check.json");
52321
+ const cacheFile = path147.join(configDir, "version-check.json");
51771
52322
  const cache = parseCache(cacheFile);
51772
52323
  if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
51773
52324
  return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
@@ -53950,6 +54501,20 @@ async function launchTuiAgentShell(opts) {
53950
54501
  currentSlates = slates;
53951
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"}`)}`;
53952
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
+ };
53953
54518
  sidebar.add(new otui.TextRenderable(r, { id: "sb-ctx-k", content: otui.t`${otui.dim("Context")}`, marginTop: 1 }));
53954
54519
  const sbContext = new otui.TextRenderable(r, { id: "sb-ctx-v", content: otui.t`${otui.dim("0 tokens")}` });
53955
54520
  sidebar.add(sbContext);
@@ -54390,6 +54955,7 @@ async function launchTuiAgentShell(opts) {
54390
54955
  }
54391
54956
  slateSession = { dir: liveSession.dir, cwd: sessionCwd, opened: false };
54392
54957
  refreshWorkspaceSidebar();
54958
+ refreshReviewSidebar();
54393
54959
  const paintSessionHeader = () => {
54394
54960
  const label = `${currentSel.provider}/${currentSel.model}`;
54395
54961
  const sid = shortSessionId(liveSession.summary.id);
@@ -54559,6 +55125,21 @@ Staying in the current session.
54559
55125
  });
54560
55126
  })();
54561
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
+ };
54562
55143
  const updateModelLabels = () => {
54563
55144
  paintSessionHeader();
54564
55145
  const label = `${currentSel.provider}/${currentSel.model}`;
@@ -55078,6 +55659,7 @@ Staying in the current session.
55078
55659
  startNewSession();
55079
55660
  slateSession = { dir: liveSession.dir, cwd: sessionCwd, opened: false };
55080
55661
  refreshWorkspaceSidebar();
55662
+ refreshReviewSidebar();
55081
55663
  sessions.clear();
55082
55664
  deps.resetSubagentBudget?.();
55083
55665
  io.onSystem?.(`New session ${shortSessionId(liveSession.summary.id)} (previous kept on disk \xB7 /resume)
@@ -55216,6 +55798,10 @@ Staying in the current session.
55216
55798
  showWorkspace();
55217
55799
  return;
55218
55800
  }
55801
+ if (isReviewCommand(command.name)) {
55802
+ showReview();
55803
+ return;
55804
+ }
55219
55805
  if (command.name === "/copy") {
55220
55806
  const target = newestBlock();
55221
55807
  if (target === undefined || !copyBlock(target.id)) {
@@ -55535,6 +56121,7 @@ ${formatThemeList(getThemeId())}`);
55535
56121
  const secs = ((Date.now() - startedAt) / 1000).toFixed(1);
55536
56122
  stopBusy();
55537
56123
  refreshWorkspaceSidebar();
56124
+ refreshReviewSidebar();
55538
56125
  setMainAgent(turnFailed ? "failed" : "done", turnFailed ? "error" : "idle");
55539
56126
  try {
55540
56127
  flushSessionCheckpoint();
@@ -56903,7 +57490,7 @@ ${indentBlock(style.dim(context), GUTTER)}`);
56903
57490
  out(`${GUTTER}${style.yellow(hint)}
56904
57491
  `);
56905
57492
  }
56906
- const rememberable = !evaled.destructive && !evaled.credentials;
57493
+ const rememberable = !evaled.destructive && !evaled.credentials && !evaled.sacReviewConfirmation;
56907
57494
  const prompt = rememberable ? "[y/N/A=always] " : "[y/N] ";
56908
57495
  out(`
56909
57496
  ${GUTTER}${style.yellow(`Run: ${evaled.command}`)} ${style.dim(prompt)}`);
@@ -57445,7 +58032,7 @@ async function shellCommand(args2, runtime = {}) {
57445
58032
  modelId: sel.model
57446
58033
  }),
57447
58034
  maxToolCalls: resolveAgentMaxToolCalls(),
57448
- idSeq: () => randomUUID23(),
58035
+ idSeq: () => randomUUID25(),
57449
58036
  ...resetSubagentBudget !== undefined ? { resetSubagentBudget } : {}
57450
58037
  };
57451
58038
  };
@@ -57477,7 +58064,7 @@ async function shellCommand(args2, runtime = {}) {
57477
58064
  makeShellDeps: (sel) => ({
57478
58065
  makeProvider: chatFactory,
57479
58066
  clock: () => new Date().toISOString(),
57480
- idSeq: () => randomUUID23(),
58067
+ idSeq: () => randomUUID25(),
57481
58068
  initial: sel,
57482
58069
  session: {
57483
58070
  cwd,
@@ -57548,7 +58135,7 @@ async function shellCommand(args2, runtime = {}) {
57548
58135
  const deps = {
57549
58136
  makeProvider: baseFactory,
57550
58137
  clock: () => new Date().toISOString(),
57551
- idSeq: () => randomUUID23(),
58138
+ idSeq: () => randomUUID25(),
57552
58139
  initial: baseUrl2 === undefined ? { provider, model } : { provider, model, baseUrl: baseUrl2 },
57553
58140
  selectProviderModel: realSelectProviderModel(baseUrl2)
57554
58141
  };
@@ -57599,7 +58186,7 @@ async function shellCommand(args2, runtime = {}) {
57599
58186
  modelId: model
57600
58187
  }),
57601
58188
  maxToolCalls: resolveAgentMaxToolCalls(),
57602
- idSeq: () => randomUUID23(),
58189
+ idSeq: () => randomUUID25(),
57603
58190
  ...resetSubagentBudget !== undefined ? { resetSubagentBudget } : {}
57604
58191
  };
57605
58192
  let resumeId = flags.resumeId;
@@ -57771,7 +58358,7 @@ Shell:
57771
58358
  init_fs();
57772
58359
  import { readFile as readFile78 } from "fs/promises";
57773
58360
  import { stdin } from "process";
57774
- import path147 from "path";
58361
+ import path148 from "path";
57775
58362
  var MODULES = [
57776
58363
  { name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
57777
58364
  { name: "gdctx", flag: "--no-gdctx", desc: "token-aware command/read output", defaultEnabled: true },
@@ -57817,8 +58404,8 @@ async function modulesCommand(args2 = []) {
57817
58404
  return;
57818
58405
  }
57819
58406
  const wantsJson = args2.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
57820
- const metaprojectRoot = path147.join(process.cwd(), ".metaproject");
57821
- const manifestPath = path147.join(metaprojectRoot, "metaproject.json");
58407
+ const metaprojectRoot = path148.join(process.cwd(), ".metaproject");
58408
+ const manifestPath = path148.join(metaprojectRoot, "metaproject.json");
57822
58409
  if (!await pathExists(manifestPath)) {
57823
58410
  if (wantsJson) {
57824
58411
  console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
@@ -57932,18 +58519,18 @@ function printHelp16() {
57932
58519
  }
57933
58520
 
57934
58521
  // src/commands/serve.ts
57935
- import { randomUUID as randomUUID26 } from "crypto";
58522
+ import { randomUUID as randomUUID28 } from "crypto";
57936
58523
 
57937
58524
  // src/lib/serve-config.ts
57938
58525
  init_config_dir();
57939
58526
  import { existsSync as existsSync27 } from "fs";
57940
- import path148 from "path";
58527
+ import path149 from "path";
57941
58528
  var SERVE_CONFIG_SCHEMA_VERSION = "1.0.0";
57942
58529
  var DEFAULT_SERVE_BIND_ADDRESS = "127.0.0.1";
57943
58530
  var DEFAULT_SERVE_PORT = 7377;
57944
58531
  var DEFAULT_SERVE_PROFILE = "remote-restricted";
57945
58532
  function serveConfigPath(dir) {
57946
- return path148.join(keryxConfigDir(dir), "serve.json");
58533
+ return path149.join(keryxConfigDir(dir), "serve.json");
57947
58534
  }
57948
58535
  function parseIpv4(value) {
57949
58536
  const parts = value.split(".");
@@ -58259,7 +58846,7 @@ function saveServeConfig(config, dir, onWarn) {
58259
58846
 
58260
58847
  // src/lib/serve-credential.ts
58261
58848
  init_config_dir();
58262
- import { createHash as createHash33, randomBytes as randomBytes3, randomUUID as randomUUID24 } from "crypto";
58849
+ import { createHash as createHash33, randomBytes as randomBytes3, randomUUID as randomUUID26 } from "crypto";
58263
58850
  import {
58264
58851
  chmodSync as chmodSync4,
58265
58852
  closeSync as closeSync3,
@@ -58271,9 +58858,9 @@ import {
58271
58858
  unlinkSync as unlinkSync3,
58272
58859
  writeFileSync as writeFileSync8
58273
58860
  } from "fs";
58274
- import path149 from "path";
58861
+ import path150 from "path";
58275
58862
  function serveCredentialPath(dir) {
58276
- return path149.join(keryxConfigDir(dir), "serve-credentials.json");
58863
+ return path150.join(keryxConfigDir(dir), "serve-credentials.json");
58277
58864
  }
58278
58865
  function constantTimeEqual(a, b) {
58279
58866
  const width = Math.max(a.length, b.length);
@@ -58343,7 +58930,7 @@ function readServeCredential(dir) {
58343
58930
  }
58344
58931
  function writeStore(store, dir) {
58345
58932
  const file = serveCredentialPath(dir);
58346
- const temp = `${file}.${randomUUID24()}.tmp`;
58933
+ const temp = `${file}.${randomUUID26()}.tmp`;
58347
58934
  try {
58348
58935
  ensureKeryxConfigDir(dir);
58349
58936
  const handle = openSync3(temp, "wx", 384);
@@ -58383,7 +58970,7 @@ function mintRecord(now) {
58383
58970
  const salt = randomBytes3(32).toString("hex");
58384
58971
  return {
58385
58972
  token,
58386
- record: { id: randomUUID24(), algorithm: "sha256", salt, hash: hashToken(salt, token), createdAt: now }
58973
+ record: { id: randomUUID26(), algorithm: "sha256", salt, hash: hashToken(salt, token), createdAt: now }
58387
58974
  };
58388
58975
  }
58389
58976
  function issueServeToken(dir, now = () => new Date().toISOString(), onWaiting) {
@@ -58507,22 +59094,22 @@ class AuthFailureThrottle {
58507
59094
  init_config_dir();
58508
59095
  import { createHash as createHash34 } from "crypto";
58509
59096
  import { existsSync as existsSync29, readdirSync as readdirSync2, rmSync as rmSync2 } from "fs";
58510
- import path150 from "path";
59097
+ import path151 from "path";
58511
59098
  var MAX_TURN_EVENTS = 1e4;
58512
59099
  function turnsRoot(dir) {
58513
- return path150.join(keryxConfigDir(dir), "turns");
59100
+ return path151.join(keryxConfigDir(dir), "turns");
58514
59101
  }
58515
59102
  function turnDir(turnId, dir) {
58516
- return path150.join(turnsRoot(dir), turnId);
59103
+ return path151.join(turnsRoot(dir), turnId);
58517
59104
  }
58518
59105
  function keyPath(project, idempotencyKey, dir) {
58519
59106
  const projectBytes = Buffer.byteLength(project, "utf8");
58520
59107
  const digest2 = createHash34("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
58521
- return path150.join(turnsRoot(dir), "keys", `${digest2}.json`);
59108
+ return path151.join(turnsRoot(dir), "keys", `${digest2}.json`);
58522
59109
  }
58523
59110
  function legacyKeyPath(idempotencyKey, dir) {
58524
59111
  const digest2 = createHash34("sha256").update(idempotencyKey, "utf8").digest("hex");
58525
- return path150.join(turnsRoot(dir), "keys", `${digest2}.json`);
59112
+ return path151.join(turnsRoot(dir), "keys", `${digest2}.json`);
58526
59113
  }
58527
59114
  function adoptLegacyClaim(project, idempotencyKey, dir) {
58528
59115
  const legacy = legacyKeyPath(idempotencyKey, dir);
@@ -58586,7 +59173,7 @@ function ensureTurnDir(turnId, dir) {
58586
59173
  }
58587
59174
  function createTurnRecord(record, dir) {
58588
59175
  ensureTurnDir(record.turnId, dir);
58589
- writeOwnerOnlyFile(path150.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
59176
+ writeOwnerOnlyFile(path151.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
58590
59177
  `);
58591
59178
  }
58592
59179
  function appendTurnEvent(event, dir, opts) {
@@ -58595,12 +59182,12 @@ function appendTurnEvent(event, dir, opts) {
58595
59182
  }
58596
59183
  const line = JSON.stringify(event);
58597
59184
  try {
58598
- appendOwnerOnlyLine(path150.join(turnDir(event.turnId, dir), "events.jsonl"), line);
59185
+ appendOwnerOnlyLine(path151.join(turnDir(event.turnId, dir), "events.jsonl"), line);
58599
59186
  } catch (error2) {
58600
59187
  if (error2?.code !== "ENOENT") {
58601
59188
  throw error2;
58602
59189
  }
58603
- appendOwnerOnlyLine(path150.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
59190
+ appendOwnerOnlyLine(path151.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
58604
59191
  }
58605
59192
  return true;
58606
59193
  }
@@ -58608,7 +59195,7 @@ function readTurnEvents(turnId, after = -1, dir) {
58608
59195
  if (!isTurnId(turnId)) {
58609
59196
  return { ok: false, reason: "not-a-turn-id" };
58610
59197
  }
58611
- const read = readTurnFile(path150.join(turnDir(turnId, dir), "events.jsonl"));
59198
+ const read = readTurnFile(path151.join(turnDir(turnId, dir), "events.jsonl"));
58612
59199
  if (!read.ok) {
58613
59200
  if (isDefiniteAbsence2(read.reason)) {
58614
59201
  return { ok: true, value: [] };
@@ -58636,7 +59223,7 @@ function readTurnRecord(turnId, dir) {
58636
59223
  if (!isTurnId(turnId)) {
58637
59224
  return { ok: false, reason: "not-a-turn-id" };
58638
59225
  }
58639
- const read = readTurnFile(path150.join(turnDir(turnId, dir), "turn.json"));
59226
+ const read = readTurnFile(path151.join(turnDir(turnId, dir), "turn.json"));
58640
59227
  if (!read.ok) {
58641
59228
  return { ok: false, reason: read.reason };
58642
59229
  }
@@ -58655,7 +59242,7 @@ function finishTurn(turnId, result, dir) {
58655
59242
  if (!record.ok) {
58656
59243
  return false;
58657
59244
  }
58658
- writeOwnerOnlyFile(path150.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
59245
+ writeOwnerOnlyFile(path151.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
58659
59246
  `);
58660
59247
  return true;
58661
59248
  }
@@ -58700,8 +59287,8 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
58700
59287
  }
58701
59288
 
58702
59289
  // src/lib/serve-turn.ts
58703
- import { randomUUID as randomUUID25 } from "crypto";
58704
- import path151 from "path";
59290
+ import { randomUUID as randomUUID27 } from "crypto";
59291
+ import path152 from "path";
58705
59292
  init_service();
58706
59293
  var REMOTE_ORIGIN = "remote:http";
58707
59294
  var MAX_PROMPT_CHARS = 32000;
@@ -58770,9 +59357,9 @@ function isUuid(value) {
58770
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);
58771
59358
  }
58772
59359
  function resolveProject(declared, dir) {
58773
- const wanted = path151.resolve(declared);
59360
+ const wanted = path152.resolve(declared);
58774
59361
  for (const entry of listProjects(dir, () => {})) {
58775
- if (path151.resolve(entry.path) === wanted) {
59362
+ if (path152.resolve(entry.path) === wanted) {
58776
59363
  return { ok: true, project: entry.path };
58777
59364
  }
58778
59365
  }
@@ -58819,7 +59406,7 @@ async function redactOut(security, text) {
58819
59406
  async function runRemoteTurn(input2) {
58820
59407
  const scanRoot = input2.scanRoot;
58821
59408
  const security = createSecurityService(scanRoot);
58822
- const newId = input2.newId ?? (() => randomUUID25());
59409
+ const newId = input2.newId ?? (() => randomUUID27());
58823
59410
  const clock = input2.clock ?? (() => new Date().toISOString());
58824
59411
  const turnId = input2.turnId ?? newId();
58825
59412
  const sessionId = input2.request.sessionId ?? newId();
@@ -58962,7 +59549,7 @@ function outcomeOf(status, gate, unresolvedBlockerIds) {
58962
59549
  }
58963
59550
  function createSubmitTurn(deps) {
58964
59551
  return async (request, project) => {
58965
- const turnId = (deps.newId ?? (() => randomUUID25()))();
59552
+ const turnId = (deps.newId ?? (() => randomUUID27()))();
58966
59553
  const scanned = await scanPrompt(deps.dir, request.prompt);
58967
59554
  if (scanned.rejected) {
58968
59555
  return { kind: "rejected" };
@@ -59649,7 +60236,7 @@ function runConfig(args2) {
59649
60236
  return;
59650
60237
  }
59651
60238
  const credential2 = readServeCredential();
59652
- const credentialId = credential2.status === "ok" ? credential2.record.id : randomUUID26();
60239
+ const credentialId = credential2.status === "ok" ? credential2.record.id : randomUUID28();
59653
60240
  const config = defaultServeConfig(credentialId, {
59654
60241
  address: parsed.parsed.values.get("--bind") ?? DEFAULT_SERVE_BIND_ADDRESS,
59655
60242
  port: port ?? DEFAULT_SERVE_PORT,
@@ -59805,9 +60392,9 @@ function printHelp17() {
59805
60392
 
59806
60393
  // src/commands/update.ts
59807
60394
  import { spawn as spawn5 } from "child_process";
59808
- import { chmod as chmod4, mkdir as mkdir54, readFile as readFile79, readdir as readdir24, writeFile as writeFile48 } from "fs/promises";
60395
+ import { chmod as chmod4, mkdir as mkdir54, readFile as readFile79, readdir as readdir25, writeFile as writeFile48 } from "fs/promises";
59809
60396
  import { access as access4, constants as constants2, existsSync as existsSync30 } from "fs";
59810
- import path152 from "path";
60397
+ import path153 from "path";
59811
60398
  import { fileURLToPath as fileURLToPath7 } from "url";
59812
60399
  init_config();
59813
60400
  init_config2();
@@ -59823,8 +60410,8 @@ async function updateCommand(args2 = []) {
59823
60410
  return;
59824
60411
  }
59825
60412
  const projectRoot = process.cwd();
59826
- const metaprojectRoot = path152.join(projectRoot, ".metaproject");
59827
- banner("keryx update", `Refreshing the .metaproject workspace in ${path152.basename(projectRoot)}/`);
60413
+ const metaprojectRoot = path153.join(projectRoot, ".metaproject");
60414
+ banner("keryx update", `Refreshing the .metaproject workspace in ${path153.basename(projectRoot)}/`);
59828
60415
  if (!await pathExists(metaprojectRoot)) {
59829
60416
  console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
59830
60417
  console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
@@ -59870,12 +60457,12 @@ async function updateCommand(args2 = []) {
59870
60457
  nextSteps(steps);
59871
60458
  }
59872
60459
  async function refreshServiceFiles(projectRoot, options) {
59873
- const metaprojectRoot = path152.join(projectRoot, ".metaproject");
60460
+ const metaprojectRoot = path153.join(projectRoot, ".metaproject");
59874
60461
  const manifestState = await readManifest5(metaprojectRoot);
59875
60462
  const manifest = manifestState.manifest;
59876
60463
  const recoveredManifest = !manifestState.exists || !manifestState.valid;
59877
60464
  if (manifestState.migrated) {
59878
- await writeFile48(path152.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
60465
+ await writeFile48(path153.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
59879
60466
  `, "utf8");
59880
60467
  }
59881
60468
  const enableGdgraph = moduleEnabled2(manifest, "gdgraph");
@@ -59912,11 +60499,11 @@ async function refreshServiceFiles(projectRoot, options) {
59912
60499
  enableSecurity,
59913
60500
  enableSac
59914
60501
  });
59915
- await writeTextIfChanged4(path152.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
59916
- await writeTextIfChanged4(path152.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
59917
- await writeTextIfChanged4(path152.join(metaprojectRoot, "rules", "README.md"), renderProjectRulesReadme());
59918
- await writeTextIfChanged4(path152.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
59919
- await writeTextIfChanged4(path152.join(metaprojectRoot, "index.md"), renderIndexMarkdown({
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({
59920
60507
  enableGdgraph,
59921
60508
  enableGdctx,
59922
60509
  enableGdwiki,
@@ -59929,7 +60516,7 @@ async function refreshServiceFiles(projectRoot, options) {
59929
60516
  ruleSources,
59930
60517
  hasDistilledEntrypoints: await hasDistilledEntrypoints(metaprojectRoot)
59931
60518
  }));
59932
- await writeTextIfChanged4(path152.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
60519
+ await writeTextIfChanged4(path153.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
59933
60520
  enableGdgraph,
59934
60521
  enableGdctx,
59935
60522
  enableGdwiki,
@@ -59941,7 +60528,7 @@ async function refreshServiceFiles(projectRoot, options) {
59941
60528
  enableSecurity,
59942
60529
  data: dashboardData
59943
60530
  }));
59944
- await writeTextIfMissing4(path152.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
60531
+ await writeTextIfMissing4(path153.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
59945
60532
  enableGdgraph,
59946
60533
  enableGdctx,
59947
60534
  enableGdwiki,
@@ -59954,31 +60541,31 @@ async function refreshServiceFiles(projectRoot, options) {
59954
60541
  }));
59955
60542
  if (enableGdgraph) {
59956
60543
  await installGdgraphCoreScripts2(metaprojectRoot);
59957
- await writeTextIfChanged4(path152.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
59958
- await writeTextIfChanged4(path152.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
59959
- await writeTextIfChanged4(path152.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
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());
59960
60547
  await seedAssetsLock(metaprojectRoot);
59961
60548
  if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
59962
60549
  await installManagedHook2(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
59963
60550
  }
59964
60551
  }
59965
60552
  if (enableGdctx) {
59966
- await writeTextIfMissing4(path152.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
59967
- await writeTextIfChanged4(path152.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
59968
- await writeTextIfChanged4(path152.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
59969
- await writeTextIfChanged4(path152.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
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());
59970
60557
  }
59971
60558
  if (enableGdwiki) {
59972
- await writeTextIfMissing4(path152.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
59973
- await writeTextIfChanged4(path152.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
59974
- await writeTextIfChanged4(path152.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
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());
59975
60562
  if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
59976
60563
  await installManagedHook2(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
59977
60564
  }
59978
60565
  }
59979
60566
  if (enableSac) {
59980
- await writeTextIfMissing4(path152.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
59981
- await writeTextIfChanged4(path152.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
60567
+ await writeTextIfMissing4(path153.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
60568
+ await writeTextIfChanged4(path153.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
59982
60569
  }
59983
60570
  if (enableGdskills) {
59984
60571
  await installGdskills(metaprojectRoot, gdskillsProfile, { createDataDirs: false });
@@ -59987,25 +60574,25 @@ async function refreshServiceFiles(projectRoot, options) {
59987
60574
  }
59988
60575
  }
59989
60576
  if (enableHealth) {
59990
- await writeTextIfMissing4(path152.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
59991
- await writeTextIfChanged4(path152.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
59992
- await writeTextIfChanged4(path152.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
59993
- await writeTextIfChanged4(path152.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
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());
59994
60581
  if (manifest.modules?.health?.hooks?.gitPostCommit) {
59995
60582
  await installManagedHook2(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
59996
60583
  }
59997
60584
  }
59998
60585
  if (enableTesting) {
59999
- await writeTextIfMissing4(path152.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
60586
+ await writeTextIfMissing4(path153.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
60000
60587
  postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
60001
60588
  prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
60002
60589
  }));
60003
- await writeTextIfChanged4(path152.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
60004
- await writeTextIfChanged4(path152.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
60005
- await writeTextIfChanged4(path152.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
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());
60006
60593
  if (enableGdwiki) {
60007
- await writeTextIfMissing4(path152.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
60008
- await writeTextIfMissing4(path152.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
60594
+ await writeTextIfMissing4(path153.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
60595
+ await writeTextIfMissing4(path153.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
60009
60596
  }
60010
60597
  if (manifest.modules?.testing?.hooks?.gitPostCommit) {
60011
60598
  await installManagedHook2(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
@@ -60018,24 +60605,24 @@ async function refreshServiceFiles(projectRoot, options) {
60018
60605
  await installManagedHook2(projectRoot, "post-commit", "metaproject-dashboard-post-commit", renderMetaprojectDashboardPostCommitHook());
60019
60606
  }
60020
60607
  if (enableMemory) {
60021
- await writeTextIfMissing4(path152.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
60022
- await writeTextIfMissing4(path152.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
60023
- await writeTextIfChanged4(path152.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
60024
- await writeTextIfChanged4(path152.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
60025
- await writeTextIfChanged4(path152.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
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());
60026
60613
  }
60027
60614
  if (enableTasks) {
60028
- await writeTextIfChanged4(path152.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
60029
- await writeTextIfChanged4(path152.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
60030
- await writeTextIfChanged4(path152.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
60031
- await writeTextIfChanged4(path152.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
60032
- await writeTextIfChanged4(path152.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
60033
- await writeTextIfChanged4(path152.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
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());
60034
60621
  }
60035
60622
  if (enableSecurity) {
60036
- await writeTextIfMissing4(path152.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
60037
- await writeTextIfChanged4(path152.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
60038
- await writeTextIfChanged4(path152.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
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());
60039
60626
  if (manifest.modules?.security?.hooks?.prePush) {
60040
60627
  await installManagedHook2(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
60041
60628
  }
@@ -60086,13 +60673,13 @@ async function refreshServiceFiles(projectRoot, options) {
60086
60673
  };
60087
60674
  }
60088
60675
  async function buildDashboard(projectRoot = process.cwd()) {
60089
- const metaprojectRoot = path152.join(projectRoot, ".metaproject");
60676
+ const metaprojectRoot = path153.join(projectRoot, ".metaproject");
60090
60677
  if (!await pathExists(metaprojectRoot)) {
60091
60678
  throw new Error("Metaproject is not initialized. Run: keryx init");
60092
60679
  }
60093
60680
  const manifest = (await readManifest5(metaprojectRoot)).manifest;
60094
60681
  const data = await collectDashboardData(metaprojectRoot);
60095
- const dashboardPath = path152.join(metaprojectRoot, "keryx-dashboard.html");
60682
+ const dashboardPath = path153.join(metaprojectRoot, "keryx-dashboard.html");
60096
60683
  await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
60097
60684
  enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
60098
60685
  enableGdctx: moduleEnabled2(manifest, "gdctx"),
@@ -60112,7 +60699,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
60112
60699
  if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
60113
60700
  return true;
60114
60701
  }
60115
- const hookPath = path152.join(projectRoot, ".git", "hooks", "post-commit");
60702
+ const hookPath = path153.join(projectRoot, ".git", "hooks", "post-commit");
60116
60703
  if (!await pathExists(hookPath)) {
60117
60704
  return false;
60118
60705
  }
@@ -60132,11 +60719,11 @@ async function collectDashboardData(metaprojectRoot) {
60132
60719
  if (testing) {
60133
60720
  data.testing = testing;
60134
60721
  }
60135
- const wiki = await collectMarkdownPages(path152.join(metaprojectRoot, "wiki"), "wiki");
60722
+ const wiki = await collectMarkdownPages(path153.join(metaprojectRoot, "wiki"), "wiki");
60136
60723
  if (wiki.length > 0) {
60137
60724
  data.wiki = { pages: wiki };
60138
60725
  }
60139
- const memory = await collectMarkdownPages(path152.join(metaprojectRoot, "memory"), "memory");
60726
+ const memory = await collectMarkdownPages(path153.join(metaprojectRoot, "memory"), "memory");
60140
60727
  if (memory.length > 0) {
60141
60728
  data.memory = { entries: memory };
60142
60729
  }
@@ -60151,19 +60738,19 @@ async function collectDashboardData(metaprojectRoot) {
60151
60738
  return data;
60152
60739
  }
60153
60740
  async function collectTasksDashboardData(metaprojectRoot) {
60154
- const flowsRoot2 = path152.join(metaprojectRoot, "flows");
60741
+ const flowsRoot2 = path153.join(metaprojectRoot, "flows");
60155
60742
  if (!await pathExists(flowsRoot2)) {
60156
60743
  return null;
60157
60744
  }
60158
60745
  let dirEntries;
60159
60746
  try {
60160
- dirEntries = (await readdir24(flowsRoot2, { withFileTypes: true })).filter((entry) => entry.isDirectory() && /^\d{3}-/.test(entry.name)).map((entry) => entry.name).sort();
60747
+ dirEntries = (await readdir25(flowsRoot2, { withFileTypes: true })).filter((entry) => entry.isDirectory() && /^\d{3}-/.test(entry.name)).map((entry) => entry.name).sort();
60161
60748
  } catch {
60162
60749
  return null;
60163
60750
  }
60164
60751
  const flows = [];
60165
60752
  for (const dir of dirEntries) {
60166
- const flowPath = path152.join(flowsRoot2, dir, "flow.json");
60753
+ const flowPath = path153.join(flowsRoot2, dir, "flow.json");
60167
60754
  if (!await pathExists(flowPath)) {
60168
60755
  continue;
60169
60756
  }
@@ -60171,7 +60758,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
60171
60758
  const flow = JSON.parse(await readFile79(flowPath, "utf8"));
60172
60759
  const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
60173
60760
  let acTotal = 0;
60174
- const acPath2 = path152.join(flowsRoot2, dir, "acceptance-criteria.md");
60761
+ const acPath2 = path153.join(flowsRoot2, dir, "acceptance-criteria.md");
60175
60762
  if (await pathExists(acPath2)) {
60176
60763
  const acContent = await readFile79(acPath2, "utf8");
60177
60764
  acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
@@ -60225,7 +60812,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
60225
60812
  "data/testing/context.md"
60226
60813
  ];
60227
60814
  for (const href of staticHrefs) {
60228
- const filePath = path152.join(metaprojectRoot, ...href.split("/"));
60815
+ const filePath = path153.join(metaprojectRoot, ...href.split("/"));
60229
60816
  if (!await pathExists(filePath)) {
60230
60817
  continue;
60231
60818
  }
@@ -60242,7 +60829,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
60242
60829
  return docs;
60243
60830
  }
60244
60831
  async function collectHealthDashboardData(metaprojectRoot) {
60245
- const reportPath2 = path152.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
60832
+ const reportPath2 = path153.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
60246
60833
  if (!await pathExists(reportPath2)) {
60247
60834
  return;
60248
60835
  }
@@ -60351,8 +60938,8 @@ function metricToScope(metric) {
60351
60938
  };
60352
60939
  }
60353
60940
  async function collectGraphDashboardData(metaprojectRoot) {
60354
- const nodesPath = path152.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
60355
- const edgesPath = path152.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
60941
+ const nodesPath = path153.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
60942
+ const edgesPath = path153.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
60356
60943
  if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
60357
60944
  return;
60358
60945
  }
@@ -60403,8 +60990,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
60403
60990
  };
60404
60991
  }
60405
60992
  async function collectTestingDashboardData(metaprojectRoot) {
60406
- const reportPath2 = path152.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
60407
- const contextPath = path152.join(metaprojectRoot, "data", "testing", "context.md");
60993
+ const reportPath2 = path153.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
60994
+ const contextPath = path153.join(metaprojectRoot, "data", "testing", "context.md");
60408
60995
  if (await pathExists(reportPath2)) {
60409
60996
  const report = JSON.parse(await readFile79(reportPath2, "utf8"));
60410
60997
  const totalTests = numberOrUndefined(report.total);
@@ -60433,7 +61020,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
60433
61020
  const files = await listMarkdownFiles(root);
60434
61021
  const pages = [];
60435
61022
  for (const filePath of files.slice(0, 40)) {
60436
- const relativePath = path152.relative(root, filePath).split(path152.sep).join("/");
61023
+ const relativePath = path153.relative(root, filePath).split(path153.sep).join("/");
60437
61024
  if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
60438
61025
  continue;
60439
61026
  }
@@ -60451,10 +61038,10 @@ async function collectMarkdownPages(root, hrefPrefix) {
60451
61038
  return pages;
60452
61039
  }
60453
61040
  async function listMarkdownFiles(root) {
60454
- const entries = await readdir24(root, { withFileTypes: true });
61041
+ const entries = await readdir25(root, { withFileTypes: true });
60455
61042
  const files = [];
60456
61043
  for (const entry of entries) {
60457
- const fullPath = path152.join(root, entry.name);
61044
+ const fullPath = path153.join(root, entry.name);
60458
61045
  if (entry.isDirectory()) {
60459
61046
  files.push(...await listMarkdownFiles(fullPath));
60460
61047
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -60502,7 +61089,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
60502
61089
  const manifest = {
60503
61090
  schemaVersion: 1,
60504
61091
  standardVersion: STANDARD_VERSION,
60505
- name: `${path152.basename(path152.dirname(metaprojectRoot))}-metaproject`,
61092
+ name: `${path153.basename(path153.dirname(metaprojectRoot))}-metaproject`,
60506
61093
  createdBy: "keryx",
60507
61094
  profiles: computeProfiles(enabledModuleKeys2),
60508
61095
  paths: {
@@ -60603,11 +61190,11 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
60603
61190
  metaproject: ".metaproject/index.md"
60604
61191
  }
60605
61192
  };
60606
- await writeFile48(path152.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
61193
+ await writeFile48(path153.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
60607
61194
  `, "utf8");
60608
61195
  }
60609
61196
  async function enableTasksInManifest(metaprojectRoot) {
60610
- const manifestPath = path152.join(metaprojectRoot, "metaproject.json");
61197
+ const manifestPath = path153.join(metaprojectRoot, "metaproject.json");
60611
61198
  if (!await pathExists(manifestPath)) {
60612
61199
  return;
60613
61200
  }
@@ -60630,7 +61217,7 @@ async function enableTasksInManifest(metaprojectRoot) {
60630
61217
  `, "utf8");
60631
61218
  }
60632
61219
  async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
60633
- const manifestPath = path152.join(metaprojectRoot, "metaproject.json");
61220
+ const manifestPath = path153.join(metaprojectRoot, "metaproject.json");
60634
61221
  if (!await pathExists(manifestPath)) {
60635
61222
  return;
60636
61223
  }
@@ -60666,72 +61253,72 @@ async function updateRuntime(projectRoot) {
60666
61253
  }
60667
61254
  }
60668
61255
  async function findRuntimeRoot(projectRoot) {
60669
- const projectRuntime = path152.join(projectRoot, ".metaproject", "runtime", "keryx");
60670
- if (await pathExists(path152.join(projectRuntime, ".git"))) {
61256
+ const projectRuntime = path153.join(projectRoot, ".metaproject", "runtime", "keryx");
61257
+ if (await pathExists(path153.join(projectRuntime, ".git"))) {
60671
61258
  return projectRuntime;
60672
61259
  }
60673
61260
  const home = process.env.HOME;
60674
61261
  if (!home) {
60675
61262
  return null;
60676
61263
  }
60677
- const globalRuntime = path152.join(home, ".keryx", "keryx");
60678
- if (await pathExists(path152.join(globalRuntime, ".git"))) {
61264
+ const globalRuntime = path153.join(home, ".keryx", "keryx");
61265
+ if (await pathExists(path153.join(globalRuntime, ".git"))) {
60679
61266
  return globalRuntime;
60680
61267
  }
60681
61268
  return null;
60682
61269
  }
60683
61270
  async function createServiceDirs(metaprojectRoot, modules) {
60684
61271
  const dirs = [
60685
- path152.join(metaprojectRoot, "core"),
60686
- path152.join(metaprojectRoot, "hooks", "post-update.d"),
60687
- path152.join(metaprojectRoot, "modules"),
60688
- path152.join(metaprojectRoot, "rules"),
60689
- path152.join(metaprojectRoot, "skills", "project-rules"),
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"),
60690
61277
  ...modules.enableGdgraph ? [
60691
- path152.join(metaprojectRoot, "core", "gdgraph"),
60692
- path152.join(metaprojectRoot, "skills", "gdgraph")
61278
+ path153.join(metaprojectRoot, "core", "gdgraph"),
61279
+ path153.join(metaprojectRoot, "skills", "gdgraph")
60693
61280
  ] : [],
60694
61281
  ...modules.enableGdctx ? [
60695
- path152.join(metaprojectRoot, "core", "gdctx"),
60696
- path152.join(metaprojectRoot, "skills", "gdctx")
61282
+ path153.join(metaprojectRoot, "core", "gdctx"),
61283
+ path153.join(metaprojectRoot, "skills", "gdctx")
60697
61284
  ] : [],
60698
61285
  ...modules.enableGdwiki ? [
60699
- path152.join(metaprojectRoot, "skills", "gdwiki"),
60700
- path152.join(metaprojectRoot, "wiki", "templates")
61286
+ path153.join(metaprojectRoot, "skills", "gdwiki"),
61287
+ path153.join(metaprojectRoot, "wiki", "templates")
60701
61288
  ] : [],
60702
61289
  ...modules.enableHealth ? [
60703
- path152.join(metaprojectRoot, "core", "health"),
60704
- path152.join(metaprojectRoot, "skills", "health")
61290
+ path153.join(metaprojectRoot, "core", "health"),
61291
+ path153.join(metaprojectRoot, "skills", "health")
60705
61292
  ] : [],
60706
61293
  ...modules.enableTesting ? [
60707
- path152.join(metaprojectRoot, "core", "testing"),
60708
- path152.join(metaprojectRoot, "skills", "testing")
61294
+ path153.join(metaprojectRoot, "core", "testing"),
61295
+ path153.join(metaprojectRoot, "skills", "testing")
60709
61296
  ] : [],
60710
61297
  ...modules.enableMemory ? [
60711
- path152.join(metaprojectRoot, "core", "memory"),
60712
- path152.join(metaprojectRoot, "skills", "memory"),
60713
- path152.join(metaprojectRoot, "memory", "templates")
61298
+ path153.join(metaprojectRoot, "core", "memory"),
61299
+ path153.join(metaprojectRoot, "skills", "memory"),
61300
+ path153.join(metaprojectRoot, "memory", "templates")
60714
61301
  ] : [],
60715
61302
  ...modules.enableTasks ? [
60716
- path152.join(metaprojectRoot, "flows"),
60717
- path152.join(metaprojectRoot, "skills", "flow")
61303
+ path153.join(metaprojectRoot, "flows"),
61304
+ path153.join(metaprojectRoot, "skills", "flow")
60718
61305
  ] : [],
60719
61306
  ...modules.enableSecurity ? [
60720
- path152.join(metaprojectRoot, "core", "security")
61307
+ path153.join(metaprojectRoot, "core", "security")
60721
61308
  ] : [],
60722
61309
  ...modules.enableSac ? [
60723
- path152.join(metaprojectRoot, "skills", "sac")
61310
+ path153.join(metaprojectRoot, "skills", "sac")
60724
61311
  ] : []
60725
61312
  ];
60726
61313
  await Promise.all(dirs.map((dir) => mkdir54(dir, { recursive: true })));
60727
61314
  }
60728
61315
  async function installGdgraphCoreScripts2(metaprojectRoot) {
60729
- const gdgraphCoreRoot = path152.join(metaprojectRoot, "core", "gdgraph");
61316
+ const gdgraphCoreRoot = path153.join(metaprojectRoot, "core", "gdgraph");
60730
61317
  await mkdir54(gdgraphCoreRoot, { recursive: true });
60731
61318
  for (const file of GDGRAPH_CORE_SOURCES) {
60732
- await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path152.join(gdgraphCoreRoot, file));
61319
+ await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path153.join(gdgraphCoreRoot, file));
60733
61320
  }
60734
- await writeTextIfChanged4(path152.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
61321
+ await writeTextIfChanged4(path153.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
60735
61322
  }
60736
61323
  async function installManagedHook2(projectRoot, hookName, blockId, content) {
60737
61324
  const hooksRoot = await resolveGitHooksRoot(projectRoot);
@@ -60739,7 +61326,7 @@ async function installManagedHook2(projectRoot, hookName, blockId, content) {
60739
61326
  return;
60740
61327
  }
60741
61328
  await mkdir54(hooksRoot, { recursive: true });
60742
- const hookPath = path152.join(hooksRoot, hookName);
61329
+ const hookPath = path153.join(hooksRoot, hookName);
60743
61330
  const blockStart = `# keryx:${blockId}:begin`;
60744
61331
  const blockEnd = `# keryx:${blockId}:end`;
60745
61332
  const managedBlock = `${blockStart}
@@ -60760,7 +61347,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
60760
61347
  if (!hooksRoot) {
60761
61348
  return;
60762
61349
  }
60763
- const hookPath = path152.join(hooksRoot, hookName);
61350
+ const hookPath = path153.join(hooksRoot, hookName);
60764
61351
  if (!await pathExists(hookPath)) {
60765
61352
  return;
60766
61353
  }
@@ -60782,7 +61369,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
60782
61369
  if (!hooksRoot) {
60783
61370
  return false;
60784
61371
  }
60785
- const hookPath = path152.join(hooksRoot, "pre-push");
61372
+ const hookPath = path153.join(hooksRoot, "pre-push");
60786
61373
  if (!await pathExists(hookPath)) {
60787
61374
  return false;
60788
61375
  }
@@ -60797,7 +61384,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
60797
61384
  return (await readFile79(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
60798
61385
  }
60799
61386
  async function readManifest5(metaprojectRoot) {
60800
- const manifestPath = path152.join(metaprojectRoot, "metaproject.json");
61387
+ const manifestPath = path153.join(metaprojectRoot, "metaproject.json");
60801
61388
  if (!await pathExists(manifestPath)) {
60802
61389
  return {
60803
61390
  exists: false,
@@ -60866,7 +61453,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
60866
61453
  }
60867
61454
  async function anyPathExists(root, candidates) {
60868
61455
  for (const candidate of candidates) {
60869
- if (await pathExists(path152.join(root, candidate))) {
61456
+ if (await pathExists(path153.join(root, candidate))) {
60870
61457
  return true;
60871
61458
  }
60872
61459
  }
@@ -60887,13 +61474,13 @@ function parseUpdateArgs(args2) {
60887
61474
  };
60888
61475
  }
60889
61476
  async function runPostUpdateHooks(projectRoot) {
60890
- const hooksDir = path152.join(projectRoot, ".metaproject", "hooks", "post-update.d");
61477
+ const hooksDir = path153.join(projectRoot, ".metaproject", "hooks", "post-update.d");
60891
61478
  if (!await pathExists(hooksDir)) {
60892
61479
  return;
60893
61480
  }
60894
- const entries = (await readdir24(hooksDir)).sort();
61481
+ const entries = (await readdir25(hooksDir)).sort();
60895
61482
  for (const entry of entries) {
60896
- const hookPath = path152.join(hooksDir, entry);
61483
+ const hookPath = path153.join(hooksDir, entry);
60897
61484
  try {
60898
61485
  await accessExecutable(hookPath);
60899
61486
  } catch {
@@ -60934,14 +61521,14 @@ async function writeTextIfChanged4(filePath, content) {
60934
61521
  if (await pathExists(filePath) && await readFile79(filePath, "utf8") === content) {
60935
61522
  return;
60936
61523
  }
60937
- await mkdir54(path152.dirname(filePath), { recursive: true });
61524
+ await mkdir54(path153.dirname(filePath), { recursive: true });
60938
61525
  await writeFile48(filePath, content, "utf8");
60939
61526
  }
60940
61527
  async function writeTextIfMissing4(filePath, content) {
60941
61528
  if (await pathExists(filePath)) {
60942
61529
  return;
60943
61530
  }
60944
- await mkdir54(path152.dirname(filePath), { recursive: true });
61531
+ await mkdir54(path153.dirname(filePath), { recursive: true });
60945
61532
  await writeFile48(filePath, content, "utf8");
60946
61533
  }
60947
61534
  async function copyFileIfChanged2(from, to) {
@@ -60949,7 +61536,7 @@ async function copyFileIfChanged2(from, to) {
60949
61536
  if (await pathExists(to) && await readFile79(to, "utf8") === next) {
60950
61537
  return;
60951
61538
  }
60952
- await mkdir54(path152.dirname(to), { recursive: true });
61539
+ await mkdir54(path153.dirname(to), { recursive: true });
60953
61540
  await writeFile48(to, next, "utf8");
60954
61541
  }
60955
61542
  function runtimeSourcePath2(relativePath) {
@@ -60958,7 +61545,7 @@ function runtimeSourcePath2(relativePath) {
60958
61545
  return directPath;
60959
61546
  }
60960
61547
  if (relativePath.startsWith("../")) {
60961
- const packagedSourcePath = path152.join(path152.dirname(fileURLToPath7(import.meta.url)), "..", "src", relativePath.slice(3));
61548
+ const packagedSourcePath = path153.join(path153.dirname(fileURLToPath7(import.meta.url)), "..", "src", relativePath.slice(3));
60962
61549
  if (existsSync30(packagedSourcePath)) {
60963
61550
  return packagedSourcePath;
60964
61551
  }
@@ -60990,7 +61577,7 @@ function printHelp18() {
60990
61577
 
60991
61578
  // src/commands/dashboard.ts
60992
61579
  import { spawn as spawn6 } from "child_process";
60993
- import path153 from "path";
61580
+ import path154 from "path";
60994
61581
  init_args();
60995
61582
  async function dashboardCommand(args2 = []) {
60996
61583
  const options = parseOptions(args2);
@@ -61001,7 +61588,7 @@ async function dashboardCommand(args2 = []) {
61001
61588
  }
61002
61589
  if (subcommand === "build") {
61003
61590
  const result = await buildDashboard();
61004
- const rel = path153.relative(process.cwd(), result.path);
61591
+ const rel = path154.relative(process.cwd(), result.path);
61005
61592
  console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
61006
61593
  note(`Open it: keryx dashboard open`);
61007
61594
  return;
@@ -61009,7 +61596,7 @@ async function dashboardCommand(args2 = []) {
61009
61596
  if (subcommand === "open") {
61010
61597
  const result = await buildDashboard();
61011
61598
  await openFile(result.path);
61012
- const rel = path153.relative(process.cwd(), result.path);
61599
+ const rel = path154.relative(process.cwd(), result.path);
61013
61600
  console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
61014
61601
  return;
61015
61602
  }
@@ -61058,7 +61645,7 @@ import { readFileSync as readFileSync10 } from "fs";
61058
61645
  // src/agents/bootstrap.ts
61059
61646
  import { mkdir as mkdir55, readFile as readFile80, writeFile as writeFile49 } from "fs/promises";
61060
61647
  import { homedir as homedir7 } from "os";
61061
- import path154 from "path";
61648
+ import path155 from "path";
61062
61649
  init_fs();
61063
61650
  var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
61064
61651
  var AGENT_BOOTSTRAP_END = "<!-- /keryx:global-bootstrap -->";
@@ -61068,35 +61655,35 @@ var AGENT_BOOTSTRAP_RUNTIMES = [
61068
61655
  aliases: ["claude-code"],
61069
61656
  label: "Claude Code",
61070
61657
  fileName: "CLAUDE.md",
61071
- filePath: (homeRoot) => path154.join(homeRoot, ".claude", "CLAUDE.md")
61658
+ filePath: (homeRoot) => path155.join(homeRoot, ".claude", "CLAUDE.md")
61072
61659
  },
61073
61660
  {
61074
61661
  id: "opencode",
61075
61662
  aliases: ["open-code"],
61076
61663
  label: "OpenCode",
61077
61664
  fileName: "AGENTS.md",
61078
- filePath: (homeRoot) => path154.join(homeRoot, ".config", "opencode", "AGENTS.md")
61665
+ filePath: (homeRoot) => path155.join(homeRoot, ".config", "opencode", "AGENTS.md")
61079
61666
  },
61080
61667
  {
61081
61668
  id: "zcode",
61082
61669
  aliases: ["zed", "zed-code"],
61083
61670
  label: "ZCode",
61084
61671
  fileName: "AGENTS.md",
61085
- filePath: (homeRoot) => path154.join(homeRoot, ".zcode", "AGENTS.md")
61672
+ filePath: (homeRoot) => path155.join(homeRoot, ".zcode", "AGENTS.md")
61086
61673
  },
61087
61674
  {
61088
61675
  id: "codex",
61089
61676
  aliases: [],
61090
61677
  label: "Codex",
61091
61678
  fileName: "AGENTS.md",
61092
- filePath: (homeRoot) => path154.join(homeRoot, ".codex", "AGENTS.md")
61679
+ filePath: (homeRoot) => path155.join(homeRoot, ".codex", "AGENTS.md")
61093
61680
  },
61094
61681
  {
61095
61682
  id: "antigravity",
61096
61683
  aliases: ["antigravuty", "antigravity-code"],
61097
61684
  label: "Antigravity",
61098
61685
  fileName: "AGENTS.md",
61099
- filePath: (homeRoot) => path154.join(homeRoot, ".config", "antigravity", "AGENTS.md")
61686
+ filePath: (homeRoot) => path155.join(homeRoot, ".config", "antigravity", "AGENTS.md")
61100
61687
  }
61101
61688
  ];
61102
61689
  function agentBootstrapRuntimeIds() {
@@ -61144,7 +61731,7 @@ async function installAgentBootstrap(runtime, options = {}) {
61144
61731
  const dryRun = options.dryRun === true;
61145
61732
  const wrote = next !== current;
61146
61733
  if (wrote && !dryRun) {
61147
- await mkdir55(path154.dirname(filePath), { recursive: true });
61734
+ await mkdir55(path155.dirname(filePath), { recursive: true });
61148
61735
  await writeFile49(filePath, next, "utf8");
61149
61736
  }
61150
61737
  const status = dryRun ? statusFromContent(runtime, filePath, exists2, next) : await agentBootstrapStatus(runtime, homeRoot);
@@ -61492,7 +62079,7 @@ function printBootstrapHelp() {
61492
62079
  // src/commands/metrics.ts
61493
62080
  init_args();
61494
62081
  import { readFile as readFile81 } from "fs/promises";
61495
- import path156 from "path";
62082
+ import path157 from "path";
61496
62083
 
61497
62084
  // src/metrics/benchmark.ts
61498
62085
  var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
@@ -62271,7 +62858,7 @@ function buildContainmentManifest(inputs, options = {}) {
62271
62858
 
62272
62859
  // src/metrics/oracle-runner.ts
62273
62860
  import { mkdir as mkdir56, writeFile as writeFile50 } from "fs/promises";
62274
- import path155 from "path";
62861
+ import path156 from "path";
62275
62862
 
62276
62863
  // src/metrics/ir.ts
62277
62864
  function toIdSet(ids) {
@@ -62713,9 +63300,9 @@ function buildEvidenceBundle(input2, options = {}) {
62713
63300
  async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
62714
63301
  const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
62715
63302
  const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
62716
- const dir = path155.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
63303
+ const dir = path156.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
62717
63304
  await mkdir56(dir, { recursive: true });
62718
- const write = (name, value) => writeFile50(path155.join(dir, name), `${JSON.stringify(value, null, 2)}
63305
+ const write = (name, value) => writeFile50(path156.join(dir, name), `${JSON.stringify(value, null, 2)}
62719
63306
  `, "utf8");
62720
63307
  await Promise.all([
62721
63308
  write("inputs.json", bundle.inputs),
@@ -62755,7 +63342,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
62755
63342
  console.log("# metrics status");
62756
63343
  console.log("");
62757
63344
  console.log(`root: ${root}`);
62758
- console.log(`enabled: ${await Bun.file(path156.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
63345
+ console.log(`enabled: ${await Bun.file(path157.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
62759
63346
  const latest2 = await readLatestPointer(root);
62760
63347
  console.log(`latest: ${latest2.status}`);
62761
63348
  return;
@@ -62767,7 +63354,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
62767
63354
  process.exitCode = 1;
62768
63355
  return;
62769
63356
  }
62770
- const record2 = JSON.parse(await readFile81(path156.resolve(projectRoot, file), "utf8"));
63357
+ const record2 = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
62771
63358
  const result = validateRunRecord(record2);
62772
63359
  console.log(result.valid ? "valid: yes" : "valid: no");
62773
63360
  for (const error2 of result.errors)
@@ -62792,7 +63379,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
62792
63379
  process.exitCode = 1;
62793
63380
  return;
62794
63381
  }
62795
- const file = path156.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
63382
+ const file = path157.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
62796
63383
  if (!await Bun.file(file).exists()) {
62797
63384
  console.error(`Run not found: ${runId}`);
62798
63385
  process.exitCode = 1;
@@ -62809,8 +63396,8 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
62809
63396
  process.exitCode = 1;
62810
63397
  return;
62811
63398
  }
62812
- const a = JSON.parse(await readFile81(path156.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
62813
- const b = JSON.parse(await readFile81(path156.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
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"));
62814
63401
  const comparison = compareExecutionRuns(a, b);
62815
63402
  console.log(stableJson(comparison));
62816
63403
  return;
@@ -62844,8 +63431,8 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
62844
63431
  return;
62845
63432
  }
62846
63433
  const template = createPairedBenchmarkTemplate(taskIds);
62847
- await Bun.write(path156.resolve(projectRoot, out), stableJson(template));
62848
- console.log(`manifest: ${path156.relative(projectRoot, path156.resolve(projectRoot, out))}`);
63434
+ await Bun.write(path157.resolve(projectRoot, out), stableJson(template));
63435
+ console.log(`manifest: ${path157.relative(projectRoot, path157.resolve(projectRoot, out))}`);
62849
63436
  return;
62850
63437
  }
62851
63438
  if (subcommand === "benchmark" && args2[1] === "run") {
@@ -62859,7 +63446,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
62859
63446
  process.exitCode = 1;
62860
63447
  return;
62861
63448
  }
62862
- const raw = JSON.parse(await readFile81(path156.resolve(projectRoot, file), "utf8"));
63449
+ const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
62863
63450
  const input2 = Array.isArray(raw) ? raw : raw.runs ?? [];
62864
63451
  const result = validatePairedBenchmark(input2);
62865
63452
  console.log(stableJson(result));
@@ -62871,7 +63458,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
62871
63458
  process.exitCode = 1;
62872
63459
  }
62873
63460
  async function loadAffectedSets(projectRoot, file) {
62874
- const raw = JSON.parse(await readFile81(path156.resolve(projectRoot, file), "utf8"));
63461
+ const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
62875
63462
  const map = new Map;
62876
63463
  for (const entry of raw.targets ?? []) {
62877
63464
  if (typeof entry.target === "string")
@@ -62942,7 +63529,7 @@ async function runHarnessLayer(projectRoot, args2, ladder) {
62942
63529
  let tasks;
62943
63530
  let model;
62944
63531
  try {
62945
- const raw = JSON.parse(await readFile81(path156.resolve(projectRoot, resultsPath), "utf8"));
63532
+ const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, resultsPath), "utf8"));
62946
63533
  tasks = raw.tasks ?? [];
62947
63534
  model = raw.model;
62948
63535
  } catch (error2) {
@@ -62973,7 +63560,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args2, ladder) {
62973
63560
  let cases;
62974
63561
  let model;
62975
63562
  try {
62976
- const raw = JSON.parse(await readFile81(path156.resolve(projectRoot, resultsPath), "utf8"));
63563
+ const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, resultsPath), "utf8"));
62977
63564
  cases = raw.cases ?? [];
62978
63565
  model = raw.model;
62979
63566
  } catch (error2) {
@@ -62998,7 +63585,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args2, ladder) {
62998
63585
  let cases;
62999
63586
  let model;
63000
63587
  try {
63001
- const raw = JSON.parse(await readFile81(path156.resolve(projectRoot, resultsPath), "utf8"));
63588
+ const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, resultsPath), "utf8"));
63002
63589
  cases = raw.cases ?? [];
63003
63590
  model = raw.model;
63004
63591
  } catch (error2) {
@@ -63023,7 +63610,7 @@ async function runSafetyContainmentLayer(projectRoot, args2, ladder, caseClass)
63023
63610
  let cases;
63024
63611
  let model;
63025
63612
  try {
63026
- const raw = JSON.parse(await readFile81(path156.resolve(projectRoot, resultsPath), "utf8"));
63613
+ const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, resultsPath), "utf8"));
63027
63614
  cases = raw.cases ?? [];
63028
63615
  model = raw.model;
63029
63616
  } catch (error2) {
@@ -63085,12 +63672,12 @@ async function runGdgraphLayer(projectRoot, args2, ladder) {
63085
63672
  }
63086
63673
  const manifests = buildOracleManifestsByGold(inputs, { ladder });
63087
63674
  if (outDir) {
63088
- const resolvedOut = path156.resolve(projectRoot, outDir);
63675
+ const resolvedOut = path157.resolve(projectRoot, outDir);
63089
63676
  for (const input2 of inputs) {
63090
63677
  for (const named of input2.golds) {
63091
63678
  const bundle = buildEvidenceBundle({ target: input2.target, system: input2.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
63092
- const dir = await persistEvidenceBundle(path156.join(resolvedOut, named.kind), bundle, ladder);
63093
- console.error(`bundle[${named.kind}]: ${path156.relative(projectRoot, dir)}`);
63679
+ const dir = await persistEvidenceBundle(path157.join(resolvedOut, named.kind), bundle, ladder);
63680
+ console.error(`bundle[${named.kind}]: ${path157.relative(projectRoot, dir)}`);
63094
63681
  }
63095
63682
  }
63096
63683
  }
@@ -63114,7 +63701,7 @@ async function runGdgraphLayer(projectRoot, args2, ladder) {
63114
63701
  return allValid;
63115
63702
  }
63116
63703
  async function loadCoverageMap2(projectRoot, file) {
63117
- const raw = JSON.parse(await readFile81(path156.resolve(projectRoot, file), "utf8"));
63704
+ const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
63118
63705
  return raw.coverageMap ?? {};
63119
63706
  }
63120
63707
  async function runTestingLayer(projectRoot, args2, ladder) {
@@ -63151,7 +63738,7 @@ async function runTestingLayer(projectRoot, args2, ladder) {
63151
63738
  return result.valid;
63152
63739
  }
63153
63740
  async function loadMemoryGoldK(projectRoot, file) {
63154
- const raw = JSON.parse(await readFile81(path156.resolve(projectRoot, file), "utf8"));
63741
+ const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
63155
63742
  return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
63156
63743
  }
63157
63744
  async function runMemoryLayer(projectRoot, args2, ladder) {
@@ -63188,11 +63775,11 @@ async function runMemoryLayer(projectRoot, args2, ladder) {
63188
63775
  return result.valid;
63189
63776
  }
63190
63777
  async function loadWikiGoldK(projectRoot, file) {
63191
- const raw = JSON.parse(await readFile81(path156.resolve(projectRoot, file), "utf8"));
63778
+ const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
63192
63779
  return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
63193
63780
  }
63194
63781
  async function loadWikiGroundedness(projectRoot, file) {
63195
- const raw = JSON.parse(await readFile81(path156.resolve(projectRoot, file), "utf8"));
63782
+ const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
63196
63783
  const map = new Map;
63197
63784
  for (const entry of raw.targets ?? []) {
63198
63785
  if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
@@ -63248,7 +63835,7 @@ async function runWikiLayer(projectRoot, args2, ladder) {
63248
63835
  return result.valid;
63249
63836
  }
63250
63837
  async function loadGdctxFacts(projectRoot, file) {
63251
- const raw = JSON.parse(await readFile81(path156.resolve(projectRoot, file), "utf8"));
63838
+ const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, file), "utf8"));
63252
63839
  const inputs = [];
63253
63840
  for (const entry of raw.inputs ?? []) {
63254
63841
  if (typeof entry.input === "string") {
@@ -63286,7 +63873,7 @@ async function collect(projectRoot, args2) {
63286
63873
  process.exitCode = 1;
63287
63874
  return;
63288
63875
  }
63289
- const raw = JSON.parse(await readFile81(path156.resolve(projectRoot, eventFile), "utf8"));
63876
+ const raw = JSON.parse(await readFile81(path157.resolve(projectRoot, eventFile), "utf8"));
63290
63877
  const events2 = Array.isArray(raw) ? raw : raw.events;
63291
63878
  const startedAt = optionValue(args2, "--started-at") ?? events2[0]?.timestamp_utc ?? new Date().toISOString();
63292
63879
  const finishedAt = optionValue(args2, "--finished-at") ?? events2.at(-1)?.timestamp_utc ?? startedAt;
@@ -63302,11 +63889,11 @@ async function collect(projectRoot, args2) {
63302
63889
  parentRunId: optionValue(args2, "--parent-run-id") ?? null
63303
63890
  });
63304
63891
  const result = await writeRunArtifacts(metricsRoot(projectRoot), record2, { cwd: projectRoot });
63305
- console.log(`json: ${path156.relative(projectRoot, result.jsonPath)}`);
63306
- console.log(`markdown: ${path156.relative(projectRoot, result.markdownPath)}`);
63892
+ console.log(`json: ${path157.relative(projectRoot, result.jsonPath)}`);
63893
+ console.log(`markdown: ${path157.relative(projectRoot, result.markdownPath)}`);
63307
63894
  }
63308
63895
  function metricsRoot(projectRoot) {
63309
- return path156.join(projectRoot, ".metaproject", "data", "metrics");
63896
+ return path157.join(projectRoot, ".metaproject", "data", "metrics");
63310
63897
  }
63311
63898
  function printMetricsHelp() {
63312
63899
  console.log(`keryx metrics
@@ -63412,193 +63999,6 @@ init_session_wrap_up();
63412
63999
  init_proposal_evidence();
63413
64000
  init_review_confirm_token();
63414
64001
  init_store3();
63415
-
63416
- // src/sac/catch-up.ts
63417
- init_fs();
63418
- init_config_dir();
63419
- init_paths();
63420
- init_slate();
63421
- init_store3();
63422
- init_proposal_lifecycle();
63423
- init_workspace_service();
63424
- import { randomUUID as randomUUID28 } from "crypto";
63425
- import { readdir as readdir25 } from "fs/promises";
63426
- import path157 from "path";
63427
-
63428
- // src/sac/lifecycle-flag.ts
63429
- init_store();
63430
- init_service3();
63431
- init_decision_dedup();
63432
- init_workspace_service();
63433
- import { randomUUID as randomUUID27 } from "crypto";
63434
- function normalize3(raw) {
63435
- return raw.replace(/^\.\//, "");
63436
- }
63437
- function isStillPresent(recorded, valid) {
63438
- const normalized = normalize3(recorded);
63439
- return valid.has(normalized) || valid.has(moduleNameFromProjectPath(normalized));
63440
- }
63441
- async function computeLifecycleFlags(cwd, now = () => new Date) {
63442
- const valid = await validModuleNames(cwd);
63443
- if (valid === undefined)
63444
- return [];
63445
- const flaggedAt = now().toISOString();
63446
- const flags = [];
63447
- const workspaceService = new WorkspaceService({
63448
- workspaceRoot: cwd,
63449
- authorizationServer: localWorkspaceAuthorizationServer(),
63450
- strictGuard: { mode: "strict", availability: "available", decision: "pass", policyRevision: "local-offline-v1" }
63451
- });
63452
- const workspaces = await workspaceService.list({ request: undefined, requestCorrelationId: randomUUID27(), includeArchived: true });
63453
- for (const workspace of workspaces) {
63454
- const component = workspace.resources.find((r) => r.kind === "component")?.uri;
63455
- if (component !== undefined && !isStillPresent(component, valid)) {
63456
- flags.push({ kind: "workspace", ref: workspace.id, missingComponent: normalize3(component), flaggedAt });
63457
- }
63458
- }
63459
- for (const entry of await collectEntries(cwd)) {
63460
- const module = entry.scopes.module;
63461
- if (module !== null && module.length > 0 && !isStillPresent(module, valid)) {
63462
- flags.push({ kind: "memory-entry", ref: entry.relativePath, missingComponent: normalize3(module), flaggedAt });
63463
- }
63464
- }
63465
- for (const decision of await collectWikiDecisionEntries(cwd)) {
63466
- const module = decision.scopes.module;
63467
- if (module !== null && module.length > 0 && !isStillPresent(module, valid)) {
63468
- flags.push({ kind: "wiki-decision", ref: decision.relativePath, missingComponent: normalize3(module), flaggedAt });
63469
- }
63470
- }
63471
- return flags;
63472
- }
63473
-
63474
- // src/sac/catch-up.ts
63475
- async function buildCatchUp(input2) {
63476
- const [proposals, sessionCategories, lifecycleFlagsAll] = await Promise.all([
63477
- collectProposals(input2.cwd, input2.workspaceId),
63478
- collectSessionCategories(input2.cwd),
63479
- computeLifecycleFlags(input2.cwd)
63480
- ]);
63481
- const lifecycleFlags = input2.workspaceId === undefined ? lifecycleFlagsAll : lifecycleFlagsAll.filter((flag) => flag.kind !== "workspace" || flag.ref === input2.workspaceId);
63482
- return { proposals, ...sessionCategories, lifecycleFlags };
63483
- }
63484
- async function collectProposals(cwd, workspaceId) {
63485
- const authorizationServer = localWorkspaceAuthorizationServer();
63486
- const actor = await authorizationServer.actorContextFor(undefined, randomUUID28());
63487
- if (!actor)
63488
- throw new Error("trusted ActorContext is required for catch-up");
63489
- const proposalService = createLocalProposalLifecycleService(cwd);
63490
- const groups = await proposalService.listVisibleProposedProposals(actor);
63491
- const scoped = workspaceId === undefined ? groups : groups.filter((group) => group.workspace.id === workspaceId);
63492
- const flattened = scoped.flatMap((group) => group.proposals.map((proposal) => ({ group, proposal })));
63493
- return Promise.all(flattened.map(async ({ group, proposal }) => {
63494
- const fresh = await proposalService.isEvidenceFresh(proposal, actor);
63495
- return { type: "proposal", workspaceId: group.workspace.id, proposalId: proposal.id, fresh };
63496
- }));
63497
- }
63498
- async function classifySession(session) {
63499
- const dir = sessionDir(session.projectPath, session.id);
63500
- if (await isLockHeld(slateLockPath(dir)))
63501
- return;
63502
- const terminalState = await readTerminalState(dir);
63503
- if (terminalState !== undefined) {
63504
- const workspaceId2 = (await safeReadSlate(dir))?.workspaceId;
63505
- return { kind: "blocked", item: { type: "blocked", sessionId: session.id, ...workspaceId2 !== undefined ? { workspaceId: workspaceId2 } : {}, terminalState } };
63506
- }
63507
- const unboundCandidate = await readNewestUnboundCandidate(dir);
63508
- if (unboundCandidate !== undefined) {
63509
- return {
63510
- kind: "unbound-candidate",
63511
- item: { type: "unbound-candidate", sessionId: session.id, evidencePath: unboundCandidate.evidencePath, summary: unboundCandidate.summary }
63512
- };
63513
- }
63514
- if (!await isSlateEngaged(dir))
63515
- return;
63516
- const workspaceId = (await safeReadSlate(dir))?.workspaceId;
63517
- return { kind: "unknown", item: { type: "unknown", sessionId: session.id, ...workspaceId !== undefined ? { workspaceId } : {}, lastSeenAt: session.updatedAt } };
63518
- }
63519
- async function collectSessionCategories(cwd) {
63520
- const classified = await Promise.all(listSessions(cwd).map((session) => classifySession(session)));
63521
- const blocked2 = [];
63522
- const unboundCandidates = [];
63523
- const unknown = [];
63524
- for (const category of classified) {
63525
- if (category === undefined)
63526
- continue;
63527
- if (category.kind === "blocked")
63528
- blocked2.push(category.item);
63529
- else if (category.kind === "unbound-candidate")
63530
- unboundCandidates.push(category.item);
63531
- else
63532
- unknown.push(category.item);
63533
- }
63534
- return { blocked: blocked2, unboundCandidates, unknown };
63535
- }
63536
- async function isSlateEngaged(dir) {
63537
- if (await pathExists(path157.join(dir, "slate.json")))
63538
- return true;
63539
- if (await pathExists(path157.join(dir, "terminal-state.json")))
63540
- return true;
63541
- try {
63542
- const entries = await readdir25(path157.join(dir, "slate-archive"));
63543
- return entries.length > 0;
63544
- } catch {
63545
- return false;
63546
- }
63547
- }
63548
- async function safeReadSlate(dir) {
63549
- try {
63550
- return await readSlate(dir);
63551
- } catch {
63552
- return;
63553
- }
63554
- }
63555
- async function readTerminalState(dir) {
63556
- const result = readConfigFile(path157.join(dir, "terminal-state.json"));
63557
- if (!result.ok) {
63558
- return;
63559
- }
63560
- try {
63561
- return JSON.parse(result.text);
63562
- } catch {
63563
- return;
63564
- }
63565
- }
63566
- async function readNewestUnboundCandidate(dir) {
63567
- const archiveDir = path157.join(dir, "slate-archive");
63568
- let entries;
63569
- try {
63570
- entries = (await readdir25(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
63571
- } catch {
63572
- return;
63573
- }
63574
- entries.sort();
63575
- for (let i = entries.length - 1;i >= 0; i--) {
63576
- const evidencePath = path157.join(archiveDir, entries[i]);
63577
- const result = readConfigFile(evidencePath);
63578
- if (!result.ok) {
63579
- continue;
63580
- }
63581
- try {
63582
- const parsed = JSON.parse(result.text);
63583
- if (parsed.recordType !== "unbound-candidate")
63584
- continue;
63585
- return { evidencePath, summary: summarizeUnboundCandidate(parsed.groups) };
63586
- } catch {
63587
- continue;
63588
- }
63589
- }
63590
- return;
63591
- }
63592
- function summarizeUnboundCandidate(groups) {
63593
- const safeGroups = groups ?? [];
63594
- if (safeGroups.length === 0)
63595
- return "no seeds captured";
63596
- const seedCount = safeGroups.reduce((sum, group) => sum + (group.seeds?.length ?? 0), 0);
63597
- const kinds = safeGroups.map((group) => typeof group.kind === "string" ? group.kind : "unknown").join(", ");
63598
- return `${seedCount} untriaged seed(s) across ${safeGroups.length} kind(s) (${kinds})`;
63599
- }
63600
-
63601
- // src/commands/workspace.ts
63602
64002
  var PROPOSAL_KINDS2 = ["decision", "wiki-update", "memory-entry", "follow-up", "contract-change", "risk"];
63603
64003
  function service5() {
63604
64004
  return new WorkspaceService({