@mrciphersmith/keryx 0.2.121 → 0.2.122

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 +887 -194
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -42980,6 +42980,103 @@ async function startStdioTransport(server) {
42980
42980
  await server.connect(transport);
42981
42981
  }
42982
42982
 
42983
+ // src/tui/debug-log.ts
42984
+ import path192 from "path";
42985
+ function isDebugEnabled() {
42986
+ return activeRun !== undefined;
42987
+ }
42988
+ function currentDebugRun() {
42989
+ return activeRun;
42990
+ }
42991
+ function debugEvent(kind, data = {}) {
42992
+ const run = activeRun;
42993
+ if (run === undefined) {
42994
+ return;
42995
+ }
42996
+ seq += 1;
42997
+ try {
42998
+ appendOwnerOnlyLine(run.shellLog, safeStringify({ ts: new Date().toISOString(), seq, pid: process.pid, kind, ...data }));
42999
+ } catch {}
43000
+ }
43001
+ function callerStack() {
43002
+ const raw = new Error("stack").stack ?? "";
43003
+ return raw.split(`
43004
+ `).slice(1).filter((line) => !line.includes("debug-log.ts")).slice(0, 14).map((line) => line.trim()).join(" | ");
43005
+ }
43006
+ function startDebugRun(opts = {}) {
43007
+ const stamp = (opts.now ?? new Date).toISOString().replace(/[:.]/g, "-");
43008
+ const name = `${stamp}-${process.pid}`;
43009
+ const dir = ensureKeryxSubdir(["debug", name], opts.configDir);
43010
+ const run = {
43011
+ dir,
43012
+ shellLog: path192.join(dir, "shell.ndjson"),
43013
+ watcherLog: path192.join(dir, "watcher.ndjson")
43014
+ };
43015
+ try {
43016
+ writeOwnerOnlyFile(path192.join(path192.dirname(dir), "latest.txt"), `${dir}
43017
+ `);
43018
+ } catch {}
43019
+ activeRun = run;
43020
+ seq = 0;
43021
+ return run;
43022
+ }
43023
+ function summarizeInputChunk(chunk) {
43024
+ const buf = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
43025
+ const text = buf.toString("utf8");
43026
+ let printable = 0;
43027
+ let control = 0;
43028
+ const sequences = [];
43029
+ for (let i = 0;i < text.length; i++) {
43030
+ const ch = text[i] ?? "";
43031
+ const code = ch.charCodeAt(0);
43032
+ if (ch === "\x1B") {
43033
+ let j = i + 1;
43034
+ while (j < text.length && j - i < 48) {
43035
+ const c = text.charCodeAt(j);
43036
+ if (j > i + 1 && c >= 64 && c <= 126 && text[j] !== "[" && text[j] !== "<" && text[j] !== ";") {
43037
+ break;
43038
+ }
43039
+ j++;
43040
+ }
43041
+ if (sequences.length < 12) {
43042
+ sequences.push(Buffer.from(text.slice(i, j + 1)).toString("hex"));
43043
+ }
43044
+ control++;
43045
+ i = j;
43046
+ } else if (code < 32 || code === 127) {
43047
+ control++;
43048
+ if (sequences.length < 12) {
43049
+ sequences.push(Buffer.from(ch).toString("hex"));
43050
+ }
43051
+ } else {
43052
+ printable++;
43053
+ }
43054
+ }
43055
+ return { bytes: buf.length, printable, control, sequences };
43056
+ }
43057
+ function safeStringify(value) {
43058
+ const seen = new WeakSet;
43059
+ return JSON.stringify(value, (_key, v) => {
43060
+ if (typeof v === "bigint") {
43061
+ return v.toString();
43062
+ }
43063
+ if (v instanceof Error) {
43064
+ return { name: v.name, message: v.message, stack: v.stack };
43065
+ }
43066
+ if (v !== null && typeof v === "object") {
43067
+ if (seen.has(v)) {
43068
+ return "[circular]";
43069
+ }
43070
+ seen.add(v);
43071
+ }
43072
+ return v;
43073
+ });
43074
+ }
43075
+ var activeRun, seq = 0;
43076
+ var init_debug_log = __esm(() => {
43077
+ init_config_dir();
43078
+ });
43079
+
42983
43080
  // src/lib/oauth/device-code.ts
42984
43081
  function requestInit(base, signal) {
42985
43082
  return signal === undefined ? base : { ...base, signal };
@@ -43472,6 +43569,259 @@ var init_login = __esm(() => {
43472
43569
  SESSION_REFRESHED_GRANTS = ["grok", "github-copilot"];
43473
43570
  });
43474
43571
 
43572
+ // src/tui/debug-watcher.ts
43573
+ import { spawn as spawn6, spawnSync as spawnSync3 } from "child_process";
43574
+ import { closeSync as closeSync3, constants as constants2, openSync as openSync3, readdirSync as readdirSync3, readFileSync as readFileSync9, readlinkSync, statSync as statSync6 } from "fs";
43575
+ import path195 from "path";
43576
+ function parseEpollFdinfo(text) {
43577
+ const out = [];
43578
+ for (const line of text.split(`
43579
+ `)) {
43580
+ const match = /^tfd:\s*(\d+)\s+events:\s*([0-9a-fA-F]+)/.exec(line.trim());
43581
+ if (match?.[1] !== undefined && match[2] !== undefined) {
43582
+ out.push({ tfd: Number(match[1]), events: Number.parseInt(match[2], 16) });
43583
+ }
43584
+ }
43585
+ return out;
43586
+ }
43587
+ function ttyReaderArmed(ttyFds, epoll) {
43588
+ if (ttyFds.length === 0) {
43589
+ return;
43590
+ }
43591
+ return epoll.some((entry) => ttyFds.includes(entry.tfd) && (entry.events & EPOLLIN) !== 0);
43592
+ }
43593
+ function detectStall(history, window = 3) {
43594
+ if (history.length < window) {
43595
+ return;
43596
+ }
43597
+ const recent = history.slice(-window);
43598
+ if (recent.every((s) => s.alive && s.readerArmed === false)) {
43599
+ return "reader-not-polled";
43600
+ }
43601
+ if (recent.every((s) => s.alive && (s.pending ?? 0) > 0) && recent.every((s, i) => i === 0 || (s.pending ?? 0) >= (recent[i - 1]?.pending ?? 0))) {
43602
+ return "input-not-drained";
43603
+ }
43604
+ if (recent.every((s) => s.alive && (s.heartbeatAgeMs ?? 0) > 5000)) {
43605
+ return "event-loop-silent";
43606
+ }
43607
+ return;
43608
+ }
43609
+ function isAlive(pid) {
43610
+ try {
43611
+ process.kill(pid, 0);
43612
+ return true;
43613
+ } catch {
43614
+ return false;
43615
+ }
43616
+ }
43617
+ function readText(file) {
43618
+ try {
43619
+ return readFileSync9(file, "utf8");
43620
+ } catch {
43621
+ return;
43622
+ }
43623
+ }
43624
+ function listFds(pid) {
43625
+ const out = [];
43626
+ let entries;
43627
+ try {
43628
+ entries = readdirSync3(`/proc/${pid}/fd`);
43629
+ } catch {
43630
+ return out;
43631
+ }
43632
+ for (const name of entries) {
43633
+ const fd = Number(name);
43634
+ let link;
43635
+ try {
43636
+ link = readlinkSync(`/proc/${pid}/fd/${name}`);
43637
+ } catch {
43638
+ continue;
43639
+ }
43640
+ const flags = /flags:\s*(\S+)/.exec(readText(`/proc/${pid}/fdinfo/${name}`) ?? "")?.[1];
43641
+ out.push({ fd, link, flags });
43642
+ }
43643
+ return out;
43644
+ }
43645
+ function epollEntries(pid, fds) {
43646
+ return fds.filter((f) => f.link === "anon_inode:[eventpoll]").map((f) => ({ epfd: f.fd, entries: parseEpollFdinfo(readText(`/proc/${pid}/fdinfo/${f.fd}`) ?? "") }));
43647
+ }
43648
+ function threads(pid) {
43649
+ let tids;
43650
+ try {
43651
+ tids = readdirSync3(`/proc/${pid}/task`);
43652
+ } catch {
43653
+ return [];
43654
+ }
43655
+ return tids.map((tid) => {
43656
+ const base = `/proc/${pid}/task/${tid}`;
43657
+ const stat = readText(`${base}/stat`) ?? "";
43658
+ const state = /\)\s+(\S)/.exec(stat)?.[1] ?? "?";
43659
+ return { tid, comm: (readText(`${base}/comm`) ?? "").trim(), wchan: (readText(`${base}/wchan`) ?? "").trim(), state };
43660
+ });
43661
+ }
43662
+ async function makePendingReader() {
43663
+ if (process.platform !== "linux") {
43664
+ return () => {
43665
+ return;
43666
+ };
43667
+ }
43668
+ try {
43669
+ const ffi = await import("bun:ffi");
43670
+ const libc = ffi.dlopen("libc.so.6", {
43671
+ ioctl: { args: [ffi.FFIType.i32, ffi.FFIType.u64, ffi.FFIType.ptr], returns: ffi.FFIType.i32 }
43672
+ });
43673
+ return (fd) => {
43674
+ const out = new Int32Array(1);
43675
+ const rc = libc.symbols.ioctl(fd, FIONREAD, ffi.ptr(out));
43676
+ return rc === 0 ? out[0] ?? 0 : undefined;
43677
+ };
43678
+ } catch {
43679
+ return () => {
43680
+ return;
43681
+ };
43682
+ }
43683
+ }
43684
+ function spawnDebugWatcher(run) {
43685
+ const entry = process.argv[1];
43686
+ const args = [
43687
+ ...entry !== undefined ? [entry] : [],
43688
+ "shell",
43689
+ "--debug-watcher",
43690
+ "--pid",
43691
+ String(process.pid),
43692
+ "--dir",
43693
+ run.dir
43694
+ ];
43695
+ try {
43696
+ const child = spawn6(process.execPath, args, { detached: true, stdio: "ignore", env: process.env });
43697
+ child.unref();
43698
+ return child.pid;
43699
+ } catch {
43700
+ return;
43701
+ }
43702
+ }
43703
+ async function runDebugWatcher(argv) {
43704
+ const pidArg = argv[argv.indexOf("--pid") + 1];
43705
+ const dirArg = argv[argv.indexOf("--dir") + 1];
43706
+ const pid = Number(pidArg);
43707
+ if (!Number.isInteger(pid) || pid <= 0 || dirArg === undefined || !argv.includes("--dir")) {
43708
+ process.stderr.write(`usage: keryx shell --debug-watcher --pid <pid> --dir <debug run dir>
43709
+ `);
43710
+ process.exitCode = 2;
43711
+ return;
43712
+ }
43713
+ const logFile = path195.join(dirArg, "watcher.ndjson");
43714
+ const shellLog = path195.join(dirArg, "shell.ndjson");
43715
+ const log = (kind, data = {}) => {
43716
+ try {
43717
+ appendOwnerOnlyLine(logFile, JSON.stringify({ ts: new Date().toISOString(), watcher: process.pid, target: pid, kind, ...data }));
43718
+ } catch {}
43719
+ };
43720
+ const linux = process.platform === "linux" && isAlive(pid) && readText(`/proc/${pid}/stat`) !== undefined;
43721
+ const pendingOf = await makePendingReader();
43722
+ let ttyPath;
43723
+ let ttyFd;
43724
+ if (linux) {
43725
+ try {
43726
+ ttyPath = readlinkSync(`/proc/${pid}/fd/0`);
43727
+ if (isTtyLink(ttyPath)) {
43728
+ ttyFd = openSync3(ttyPath, constants2.O_RDONLY | constants2.O_NONBLOCK | constants2.O_NOCTTY);
43729
+ }
43730
+ } catch (error) {
43731
+ log("tty.open-failed", { error: String(error) });
43732
+ }
43733
+ }
43734
+ log("watcher.start", { linux, ttyPath, fionread: ttyFd !== undefined && pendingOf(ttyFd) !== undefined });
43735
+ const history = [];
43736
+ let lastLogged = "";
43737
+ let lastPeriodic = 0;
43738
+ let lastSignal = 0;
43739
+ const fullSnapshot = () => {
43740
+ const fds = listFds(pid);
43741
+ let stty;
43742
+ if (ttyPath !== undefined) {
43743
+ const res = spawnSync3("stty", ["-a", "-F", ttyPath], { encoding: "utf8", timeout: 2000 });
43744
+ stty = (res.stdout ?? "").trim() || (res.stderr ?? "").trim();
43745
+ }
43746
+ return {
43747
+ stat: (readText(`/proc/${pid}/stat`) ?? "").trim(),
43748
+ status: (readText(`/proc/${pid}/status`) ?? "").split(`
43749
+ `).filter((l) => /^(State|Threads|SigBlk|SigIgn|SigCgt|VmRSS):/.test(l)),
43750
+ fds,
43751
+ epoll: epollEntries(pid, fds),
43752
+ threads: threads(pid),
43753
+ stty
43754
+ };
43755
+ };
43756
+ const tick = () => {
43757
+ const alive = isAlive(pid);
43758
+ const sample = { alive };
43759
+ if (alive && linux) {
43760
+ const fds = listFds(pid);
43761
+ const ttyFds = fds.filter((f) => isTtyLink(f.link)).map((f) => f.fd);
43762
+ const entries = epollEntries(pid, fds).flatMap((e) => e.entries);
43763
+ sample.readerArmed = ttyReaderArmed(ttyFds, entries);
43764
+ }
43765
+ if (alive && ttyFd !== undefined) {
43766
+ sample.pending = pendingOf(ttyFd);
43767
+ }
43768
+ try {
43769
+ sample.heartbeatAgeMs = Date.now() - statSync6(shellLog).mtimeMs;
43770
+ } catch {
43771
+ sample.heartbeatAgeMs = undefined;
43772
+ }
43773
+ history.push(sample);
43774
+ if (history.length > 30)
43775
+ history.shift();
43776
+ if (!alive) {
43777
+ log("target.exit");
43778
+ return false;
43779
+ }
43780
+ const key = JSON.stringify({ a: sample.readerArmed, p: (sample.pending ?? 0) > 0 });
43781
+ const now = Date.now();
43782
+ if (key !== lastLogged || now - lastPeriodic > 1e4) {
43783
+ log("sample", { ...sample });
43784
+ lastLogged = key;
43785
+ lastPeriodic = now;
43786
+ }
43787
+ const stall = detectStall(history);
43788
+ if (stall !== undefined && now - lastSignal > 15000) {
43789
+ lastSignal = now;
43790
+ log("stall.detected", { reason: stall, recent: history.slice(-5), snapshot: linux ? fullSnapshot() : {} });
43791
+ if (stall !== "event-loop-silent" && process.platform !== "win32") {
43792
+ try {
43793
+ process.kill(pid, "SIGUSR2");
43794
+ log("recover.signal-sent", { signal: "SIGUSR2" });
43795
+ } catch (error) {
43796
+ log("recover.signal-failed", { error: String(error) });
43797
+ }
43798
+ setTimeout(() => {
43799
+ log("recover.after", { snapshot: linux ? fullSnapshot() : {}, pending: ttyFd !== undefined ? pendingOf(ttyFd) : undefined });
43800
+ }, 2000);
43801
+ }
43802
+ }
43803
+ return true;
43804
+ };
43805
+ await new Promise((resolve) => {
43806
+ const timer = setInterval(() => {
43807
+ if (!tick()) {
43808
+ clearInterval(timer);
43809
+ resolve();
43810
+ }
43811
+ }, 1000);
43812
+ });
43813
+ if (ttyFd !== undefined) {
43814
+ try {
43815
+ closeSync3(ttyFd);
43816
+ } catch {}
43817
+ }
43818
+ log("watcher.stop");
43819
+ }
43820
+ var EPOLLIN = 1, FIONREAD = 21531, isTtyLink = (link) => link.startsWith("/dev/pts/") || /^\/dev\/tty/.test(link);
43821
+ var init_debug_watcher = __esm(() => {
43822
+ init_config_dir();
43823
+ });
43824
+
43475
43825
  // src/cli.ts
43476
43826
  init_single_turn();
43477
43827
  init_model_turn_port();
@@ -50274,7 +50624,7 @@ import path47 from "path";
50274
50624
  // package.json
50275
50625
  var package_default = {
50276
50626
  name: "@mrciphersmith/keryx",
50277
- version: "0.2.121",
50627
+ version: "0.2.122",
50278
50628
  description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
50279
50629
  private: false,
50280
50630
  publishConfig: {
@@ -90550,6 +90900,9 @@ function classifyBusyDispatch(params) {
90550
90900
  return "not-a-command";
90551
90901
  }
90552
90902
 
90903
+ // src/tui/tui-shell.ts
90904
+ init_debug_log();
90905
+
90553
90906
  // src/tui/boot-animation.ts
90554
90907
  var DEFAULT_BOOT_DURATION_MS = 350;
90555
90908
  var WORDMARK = ["========================", " K E R Y X ", "========================"];
@@ -94050,10 +94403,10 @@ function describeElicitationPrompt(tool, inputJson) {
94050
94403
  // src/lib/permission-mode-config.ts
94051
94404
  init_permission_mode();
94052
94405
  init_config_dir();
94053
- import path192 from "path";
94406
+ import path193 from "path";
94054
94407
  var EMPTY3 = { schemaVersion: 1, projects: {} };
94055
94408
  function permissionModeConfigPath(dir) {
94056
- return path192.join(keryxConfigDir(dir), "permission-mode.json");
94409
+ return path193.join(keryxConfigDir(dir), "permission-mode.json");
94057
94410
  }
94058
94411
  function withRegistryLock2(dir, fn) {
94059
94412
  return withFileLock2(`${permissionModeConfigPath(dir)}.lock`, fn, {
@@ -94297,6 +94650,7 @@ function createForegroundAgentIoFacade(owner, token, io) {
94297
94650
  }
94298
94651
 
94299
94652
  // src/tui/herdr-report.ts
94653
+ init_debug_log();
94300
94654
  import net from "net";
94301
94655
  var SOURCE = "herdr:keryx";
94302
94656
  var AGENT = "keryx";
@@ -94336,6 +94690,7 @@ function createHerdrReporter(clock = () => Date.now(), connect = writeOnce) {
94336
94690
  if (state === last)
94337
94691
  return;
94338
94692
  last = state;
94693
+ debugEvent("herdr.report", { state });
94339
94694
  send("pane.report_agent", { state });
94340
94695
  },
94341
94696
  release() {
@@ -94371,6 +94726,14 @@ function writeOnce(socketPath, request) {
94371
94726
  }
94372
94727
 
94373
94728
  // src/tui/composer-choice.ts
94729
+ init_debug_log();
94730
+ var DEFAULT_ACCEPT_DELAY_MS = 400;
94731
+ function resolveAcceptDelay(request) {
94732
+ if (request.acceptDelayMs !== undefined) {
94733
+ return Math.max(0, request.acceptDelayMs);
94734
+ }
94735
+ return DEFAULT_ACCEPT_DELAY_MS;
94736
+ }
94374
94737
  var MAX_SUBTITLE_CHARS = 8000;
94375
94738
  var MAX_SUBTITLE_ROWS = 10;
94376
94739
  var SUBTITLE_INLINE_MAX_CHARS = 76;
@@ -94408,12 +94771,17 @@ function lockDock(dock) {
94408
94771
  async function showComposerChoice(otui, r, dock, request) {
94409
94772
  const enqueue = request.enqueue !== false;
94410
94773
  if (!enqueue && dockBusy(dock)) {
94774
+ debugEvent("choice.busy-cancel", { title: request.title });
94411
94775
  request.onBusy?.();
94412
94776
  return request.cancelId;
94413
94777
  }
94778
+ const queuedAt = Date.now();
94779
+ debugEvent("choice.request", { title: request.title, waitingBehind: dockDepth.get(dock) ?? 0 });
94414
94780
  const unlock = await lockDock(dock);
94781
+ debugEvent("choice.lock-acquired", { title: request.title, waitedMs: Date.now() - queuedAt });
94415
94782
  try {
94416
94783
  if (request.signal?.aborted === true) {
94784
+ debugEvent("choice.aborted-before-open", { title: request.title });
94417
94785
  return request.cancelId;
94418
94786
  }
94419
94787
  return await presentComposerChoice(otui, r, dock, request);
@@ -94423,6 +94791,7 @@ async function showComposerChoice(otui, r, dock, request) {
94423
94791
  }
94424
94792
  function presentComposerChoice(otui, r, dock, request) {
94425
94793
  if (dock.visible === true) {
94794
+ debugEvent("choice.dock-already-visible", { title: request.title });
94426
94795
  request.onBusy?.();
94427
94796
  return Promise.resolve(request.cancelId);
94428
94797
  }
@@ -94434,6 +94803,9 @@ function presentComposerChoice(otui, r, dock, request) {
94434
94803
  const recommendedIdx = options.findIndex((o) => o.recommended === true);
94435
94804
  let selected = recommendedIdx >= 0 ? recommendedIdx : 0;
94436
94805
  dock.visible = true;
94806
+ const openedAt = Date.now();
94807
+ const acceptDelayMs = resolveAcceptDelay(request);
94808
+ let lastTypedAt = 0;
94437
94809
  request.onOpen?.();
94438
94810
  const theme = getTheme();
94439
94811
  const rawSubtitle = request.subtitle !== undefined && request.subtitle.length > 0 ? request.subtitle : undefined;
@@ -94501,13 +94873,14 @@ function presentComposerChoice(otui, r, dock, request) {
94501
94873
  };
94502
94874
  let settled = false;
94503
94875
  const onAbort = () => {
94504
- finish(request.cancelId);
94876
+ finish(request.cancelId, "abort");
94505
94877
  };
94506
- const finish = (id) => {
94878
+ const finish = (id, via = "key") => {
94507
94879
  if (settled) {
94508
94880
  return;
94509
94881
  }
94510
94882
  settled = true;
94883
+ debugEvent("choice.finish", { title: request.title, id, via, openMs: Date.now() - openedAt });
94511
94884
  cleanup();
94512
94885
  resolve(id);
94513
94886
  };
@@ -94518,7 +94891,7 @@ function presentComposerChoice(otui, r, dock, request) {
94518
94891
  width: "100%",
94519
94892
  flexDirection: "column",
94520
94893
  onMouseDown: () => {
94521
- finish(o.id);
94894
+ finish(o.id, "click");
94522
94895
  }
94523
94896
  });
94524
94897
  const label = new otui.TextRenderable(r, { id: `ch-opt-l-${i}-${Date.now()}`, content: o.displayLabel });
@@ -94576,8 +94949,11 @@ function presentComposerChoice(otui, r, dock, request) {
94576
94949
  dock.visible = false;
94577
94950
  }
94578
94951
  const onKey = (key) => {
94952
+ if (!key.ctrl && typeof key.sequence === "string" && key.sequence.length === 1 && key.sequence >= " ") {
94953
+ lastTypedAt = Date.now();
94954
+ }
94579
94955
  if (key.name === "escape") {
94580
- finish(request.cancelId);
94956
+ finish(request.cancelId, "escape");
94581
94957
  key.preventDefault();
94582
94958
  key.stopPropagation();
94583
94959
  return;
@@ -94614,6 +94990,17 @@ function presentComposerChoice(otui, r, dock, request) {
94614
94990
  return;
94615
94991
  }
94616
94992
  if (key.name === "return" || key.name === "linefeed" || key.name === "kpenter") {
94993
+ const now = Date.now();
94994
+ if (now - openedAt < acceptDelayMs || now - lastTypedAt < acceptDelayMs) {
94995
+ debugEvent("choice.enter-ignored", {
94996
+ title: request.title,
94997
+ sinceOpenMs: now - openedAt,
94998
+ sinceTypingMs: lastTypedAt === 0 ? undefined : now - lastTypedAt
94999
+ });
95000
+ key.preventDefault();
95001
+ key.stopPropagation();
95002
+ return;
95003
+ }
94617
95004
  const chosen = options[selected];
94618
95005
  if (chosen !== undefined) {
94619
95006
  finish(chosen.id);
@@ -94627,13 +95014,20 @@ function presentComposerChoice(otui, r, dock, request) {
94627
95014
  request.signal.addEventListener("abort", onAbort, { once: true });
94628
95015
  }
94629
95016
  optionsScroll.focus();
95017
+ debugEvent("choice.open", {
95018
+ title: request.title,
95019
+ options: options.map((o) => o.id),
95020
+ selected: options[selected]?.id,
95021
+ scrollableSubtitle: hasScrollableSubtitle,
95022
+ acceptDelayMs
95023
+ });
94630
95024
  });
94631
95025
  }
94632
95026
 
94633
95027
  // src/lib/version-check.ts
94634
95028
  init_config_dir();
94635
95029
  init_fs();
94636
- import path193 from "path";
95030
+ import path194 from "path";
94637
95031
  var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
94638
95032
  var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
94639
95033
  var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
@@ -94835,7 +95229,7 @@ async function checkVersion(options) {
94835
95229
  const now = options.now ?? Date.now;
94836
95230
  const timestamp = now();
94837
95231
  const configDir = ensureKeryxConfigDir(options.cacheDir);
94838
- const cacheFile = path193.join(configDir, "version-check.json");
95232
+ const cacheFile = path194.join(configDir, "version-check.json");
94839
95233
  const cache = parseCache(cacheFile);
94840
95234
  if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
94841
95235
  return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
@@ -94903,6 +95297,236 @@ async function checkVersion(options) {
94903
95297
  return resultFor(options.currentVersion, current, latestVersion, "registry");
94904
95298
  }
94905
95299
 
95300
+ // src/tui/shell-chrome.ts
95301
+ init_debug_log();
95302
+
95303
+ // src/tui/renderer-debug.ts
95304
+ init_debug_log();
95305
+
95306
+ // src/tui/stdin-guard.ts
95307
+ init_debug_log();
95308
+ var rendererListener;
95309
+ function stdinSnapshot(stdin) {
95310
+ const safe = (read) => {
95311
+ try {
95312
+ return read();
95313
+ } catch (error) {
95314
+ return `error: ${error instanceof Error ? error.message : String(error)}`;
95315
+ }
95316
+ };
95317
+ return {
95318
+ isTTY: safe(() => stdin.isTTY === true),
95319
+ isRaw: safe(() => stdin.isRaw),
95320
+ paused: safe(() => stdin.isPaused()),
95321
+ flowing: safe(() => stdin.readableFlowing),
95322
+ ended: safe(() => stdin.readableEnded),
95323
+ destroyed: safe(() => stdin.destroyed),
95324
+ buffered: safe(() => stdin.readableLength),
95325
+ dataListeners: safe(() => stdin.listenerCount("data")),
95326
+ readableListeners: safe(() => stdin.listenerCount("readable")),
95327
+ rendererAttached: safe(() => rendererListener === undefined ? undefined : stdin.listeners("data").includes(rendererListener))
95328
+ };
95329
+ }
95330
+ function stdinLooksStalled(snapshot) {
95331
+ return snapshot.paused === true || snapshot.flowing === false || snapshot.ended === true || snapshot.destroyed === true || snapshot.dataListeners === 0 || snapshot.rendererAttached === false || snapshot.isTTY === true && snapshot.isRaw === false;
95332
+ }
95333
+ function registerRendererInputListener(listener) {
95334
+ rendererListener = listener;
95335
+ }
95336
+ function recoverStdin(stdin, opts = {}) {
95337
+ const actions = [];
95338
+ const listener = opts.listener ?? rendererListener;
95339
+ const step = (name, run) => {
95340
+ try {
95341
+ run();
95342
+ actions.push(name);
95343
+ } catch (error) {
95344
+ actions.push(`${name} failed: ${error instanceof Error ? error.message : String(error)}`);
95345
+ }
95346
+ };
95347
+ if (stdin.isTTY === true && typeof stdin.setRawMode === "function" && stdin.isRaw !== true) {
95348
+ step("setRawMode(true)", () => stdin.setRawMode?.(true));
95349
+ }
95350
+ if (listener !== undefined && !stdin.listeners("data").includes(listener)) {
95351
+ step("re-attach renderer data listener", () => stdin.on("data", listener));
95352
+ }
95353
+ if (opts.cycle === true) {
95354
+ step("pause()", () => stdin.pause());
95355
+ }
95356
+ step("resume()", () => stdin.resume());
95357
+ return actions;
95358
+ }
95359
+ function instrumentStdin(stdin) {
95360
+ const target = stdin;
95361
+ const restores = [];
95362
+ const wrap = (method, describe = () => ({})) => {
95363
+ const original = target[method];
95364
+ if (typeof original !== "function") {
95365
+ return;
95366
+ }
95367
+ const fn = original;
95368
+ target[method] = function(...args) {
95369
+ debugEvent(`stdin.${method}`, { ...describe(args), before: stdinSnapshot(stdin), stack: callerStack() });
95370
+ return fn.apply(this, args);
95371
+ };
95372
+ restores.push(() => {
95373
+ target[method] = original;
95374
+ });
95375
+ };
95376
+ const eventName = (args) => ({ event: String(args[0]) });
95377
+ wrap("pause");
95378
+ wrap("resume");
95379
+ wrap("setRawMode", (args) => ({ mode: args[0] }));
95380
+ wrap("removeListener", eventName);
95381
+ wrap("off", eventName);
95382
+ wrap("removeAllListeners", eventName);
95383
+ wrap("destroy");
95384
+ wrap("unref");
95385
+ wrap("unpipe");
95386
+ wrap("setEncoding", (args) => ({ encoding: args[0] }));
95387
+ const lifecycle = [];
95388
+ for (const event of ["end", "close", "error", "pause", "resume"]) {
95389
+ const handler = (...args) => {
95390
+ debugEvent(`stdin.event.${event}`, { args: args.map((a) => a instanceof Error ? a : String(a)), state: stdinSnapshot(stdin) });
95391
+ };
95392
+ lifecycle.push([event, handler]);
95393
+ }
95394
+ const onData = (chunk) => {
95395
+ debugEvent("stdin.data", summarizeInputChunk(chunk));
95396
+ };
95397
+ lifecycle.push(["data", onData]);
95398
+ for (const [event, handler] of lifecycle) {
95399
+ if (event === "error" && stdin.listenerCount("error") === 0) {
95400
+ continue;
95401
+ }
95402
+ stdin.on(event, handler);
95403
+ }
95404
+ debugEvent("stdin.instrumented", { state: stdinSnapshot(stdin) });
95405
+ return () => {
95406
+ for (const restore of restores)
95407
+ restore();
95408
+ for (const [event, handler] of lifecycle)
95409
+ stdin.off?.(event, handler);
95410
+ };
95411
+ }
95412
+ function startStdinHeartbeat(stdin, opts = {}) {
95413
+ const intervalMs = opts.intervalMs ?? 1000;
95414
+ const cooldownMs = opts.cooldownMs ?? 1e4;
95415
+ let lastRecovery = 0;
95416
+ const timer = setInterval(() => {
95417
+ const state = stdinSnapshot(stdin);
95418
+ const stalled = stdinLooksStalled(state);
95419
+ debugEvent("heartbeat", { stdin: state, stalled, ...opts.extra?.() ?? {} });
95420
+ if (stalled && (opts.shouldRecover?.() ?? true) && Date.now() - lastRecovery > cooldownMs) {
95421
+ lastRecovery = Date.now();
95422
+ const actions = recoverStdin(stdin, { cycle: true });
95423
+ debugEvent("stdin.stall.recovered-in-process", { before: state, actions, after: stdinSnapshot(stdin) });
95424
+ }
95425
+ }, intervalMs);
95426
+ timer.unref?.();
95427
+ return () => clearInterval(timer);
95428
+ }
95429
+ function installInputRecoverySignal(stdin, onRecovered, shouldRecover = () => true) {
95430
+ if (process.platform === "win32") {
95431
+ return () => {};
95432
+ }
95433
+ const handler = () => {
95434
+ if (!shouldRecover()) {
95435
+ debugEvent("signal.SIGUSR2.skipped", { reason: "renderer suspended or destroyed", stdin: stdinSnapshot(stdin) });
95436
+ return;
95437
+ }
95438
+ const before = stdinSnapshot(stdin);
95439
+ const actions = recoverStdin(stdin, { cycle: true });
95440
+ const after = stdinSnapshot(stdin);
95441
+ debugEvent("signal.SIGUSR2.recover", { before, actions, after, debug: isDebugEnabled() });
95442
+ try {
95443
+ onRecovered?.(actions);
95444
+ } catch {}
95445
+ };
95446
+ process.on("SIGUSR2", handler);
95447
+ return () => {
95448
+ process.off("SIGUSR2", handler);
95449
+ };
95450
+ }
95451
+
95452
+ // src/tui/renderer-debug.ts
95453
+ function attachRendererGuards(r, stdin = process.stdin) {
95454
+ const internals = r;
95455
+ registerRendererInputListener(internals.stdinListener);
95456
+ const shouldRecover = () => !r.isDestroyed && internals.controlState !== "explicit_suspended";
95457
+ const teardown = [];
95458
+ teardown.push(installInputRecoverySignal(stdin, () => {
95459
+ r.requestRender();
95460
+ }, shouldRecover));
95461
+ teardown.push(() => registerRendererInputListener(undefined));
95462
+ if (!isDebugEnabled()) {
95463
+ return () => {
95464
+ for (const fn of teardown.splice(0))
95465
+ fn();
95466
+ };
95467
+ }
95468
+ debugEvent("renderer.created", {
95469
+ controlState: internals.controlState,
95470
+ width: r.width,
95471
+ height: r.height,
95472
+ hasStdinListener: internals.stdinListener !== undefined
95473
+ });
95474
+ teardown.push(instrumentStdin(stdin));
95475
+ teardown.push(startStdinHeartbeat(stdin, {
95476
+ shouldRecover,
95477
+ extra: () => ({
95478
+ controlState: internals.controlState,
95479
+ focused: internals.currentFocusedRenderable?.id
95480
+ })
95481
+ }));
95482
+ const onKey = (key) => {
95483
+ const printable = key.sequence.length === 1 && key.sequence >= " " && !key.ctrl && !key.meta;
95484
+ queueMicrotask(() => {
95485
+ debugEvent("key", {
95486
+ name: printable ? "<printable>" : key.name,
95487
+ ctrl: key.ctrl,
95488
+ meta: key.meta,
95489
+ shift: key.shift,
95490
+ prevented: key.defaultPrevented,
95491
+ stopped: key.propagationStopped,
95492
+ focused: internals.currentFocusedRenderable?.id
95493
+ });
95494
+ });
95495
+ };
95496
+ internals._internalKeyInput?.onInternal("keypress", onKey);
95497
+ teardown.push(() => internals._internalKeyInput?.offInternal("keypress", onKey));
95498
+ const emitter = r;
95499
+ for (const event of ["resize", "focus", "blur", "capabilities"]) {
95500
+ const fn = (...args) => {
95501
+ debugEvent(`renderer.${event}`, event === "capabilities" ? {} : { args: args.map(String) });
95502
+ };
95503
+ emitter.on?.(event, fn);
95504
+ teardown.push(() => emitter.off?.(event, fn));
95505
+ }
95506
+ const target = r;
95507
+ for (const method of ["suspend", "resume", "pause", "stop", "destroy"]) {
95508
+ const original = target[method];
95509
+ if (typeof original !== "function")
95510
+ continue;
95511
+ const fn = original;
95512
+ target[method] = function(...args) {
95513
+ debugEvent(`renderer.${method}`, { controlState: internals.controlState, stack: callerStack() });
95514
+ return fn.apply(this, args);
95515
+ };
95516
+ teardown.push(() => {
95517
+ target[method] = original;
95518
+ });
95519
+ }
95520
+ return () => {
95521
+ debugEvent("renderer.teardown");
95522
+ for (const fn of teardown.splice(0)) {
95523
+ try {
95524
+ fn();
95525
+ } catch {}
95526
+ }
95527
+ };
95528
+ }
95529
+
94906
95530
  // src/tui/shell-chrome.ts
94907
95531
  function onKeypress3(r, handler) {
94908
95532
  r._internalKeyInput.onInternal("keypress", handler);
@@ -95008,14 +95632,20 @@ function prefixFilter(commands, query) {
95008
95632
  return commands.filter((c) => c.name.slice(1).toLowerCase().startsWith(needle));
95009
95633
  }
95010
95634
  async function createShellRenderer(otui, opts = {}) {
95011
- return await otui.createCliRenderer({
95635
+ const guards = {};
95636
+ const renderer = await otui.createCliRenderer({
95012
95637
  exitOnCtrlC: true,
95013
95638
  screenMode: "alternate-screen",
95014
95639
  clearOnShutdown: true,
95015
95640
  useMouse: true,
95016
95641
  backgroundColor: getTheme().bg,
95017
- ...opts.onDestroy !== undefined ? { onDestroy: opts.onDestroy } : {}
95642
+ onDestroy: () => {
95643
+ guards.detach?.();
95644
+ opts.onDestroy?.();
95645
+ }
95018
95646
  });
95647
+ guards.detach = attachRendererGuards(renderer);
95648
+ return renderer;
95019
95649
  }
95020
95650
  async function createShellChrome(otui, renderer, opts) {
95021
95651
  const r = renderer;
@@ -95178,10 +95808,12 @@ async function createShellChrome(otui, renderer, opts) {
95178
95808
  };
95179
95809
  const withOverlay = async (run) => {
95180
95810
  overlayDepth += 1;
95811
+ debugEvent("overlay.enter", { depth: overlayDepth, dockVisible: dock.visible });
95181
95812
  try {
95182
95813
  return await run();
95183
95814
  } finally {
95184
95815
  overlayDepth -= 1;
95816
+ debugEvent("overlay.exit", { depth: overlayDepth, dockVisible: dock.visible });
95185
95817
  }
95186
95818
  };
95187
95819
  const addOverlaySource = (isActive) => {
@@ -95430,9 +96062,11 @@ async function createShellChrome(otui, renderer, opts) {
95430
96062
  });
95431
96063
  textarea.onSubmit = () => {
95432
96064
  if (overlayActive()) {
96065
+ debugEvent("composer.submit-blocked", { reason: "overlay active", dockVisible: dock.visible, overlayDepth });
95433
96066
  return;
95434
96067
  }
95435
96068
  const line = input.value.trim();
96069
+ debugEvent("composer.submit", { chars: line.length, slash: line.startsWith("/") });
95436
96070
  input.value = "";
95437
96071
  hideMenu();
95438
96072
  syncComposerHeight();
@@ -95550,6 +96184,15 @@ async function createShellChrome(otui, renderer, opts) {
95550
96184
  appliedTheme = theme;
95551
96185
  };
95552
96186
  const unsubTheme = onThemeChange((theme) => applyTheme(theme));
96187
+ {
96188
+ const run = currentDebugRun();
96189
+ if (run !== undefined) {
96190
+ setTimeout(() => {
96191
+ if (alive)
96192
+ showToast(`debug log: ${run.dir}`);
96193
+ }, 0);
96194
+ }
96195
+ }
95553
96196
  return {
95554
96197
  renderer: r,
95555
96198
  root: rootRow,
@@ -98114,6 +98757,7 @@ function attachBlockIo(io, addBlock, chrome = {}, thinkDisplay = () => "auto") {
98114
98757
  });
98115
98758
  };
98116
98759
  io.onToolCall = (name, input) => {
98760
+ debugEvent("tool.call", { name, inputChars: input.length });
98117
98761
  chrome.onToolCall?.(name, input);
98118
98762
  const args = summarizeToolArgs(input);
98119
98763
  addBlock({
@@ -98125,6 +98769,7 @@ function attachBlockIo(io, addBlock, chrome = {}, thinkDisplay = () => "auto") {
98125
98769
  }, { hint: "ctrl+o", tone: "cyan" });
98126
98770
  };
98127
98771
  io.onToolResult = (name, result) => {
98772
+ debugEvent("tool.result", { name, isError: result.isError === true, outputChars: result.output.length });
98128
98773
  chrome.onToolResult?.(name, result);
98129
98774
  const { summary, lineCount, hidden } = collapseToolOutput(result.output);
98130
98775
  const more = hidden > 0 ? ` \xB7 +${hidden} more` : "";
@@ -98292,6 +98937,9 @@ async function applyRuntimeSwitchToSlate(params) {
98292
98937
  params.onHistoryChange?.("tool");
98293
98938
  return true;
98294
98939
  }
98940
+ function freshSlateSessionRef(sessionDir, cwd) {
98941
+ return { dir: sessionDir, cwd, opened: false };
98942
+ }
98295
98943
  function fmtTokens(n) {
98296
98944
  return n >= 1000 ? `${(n / 1000).toFixed(1)}K` : String(n);
98297
98945
  }
@@ -99432,6 +100080,7 @@ async function launchTuiAgentShell(opts) {
99432
100080
  let busyStartedAt = 0;
99433
100081
  const setBusyPhase = (phase) => {
99434
100082
  busyPhase = phase;
100083
+ debugEvent("busy.phase", { phase });
99435
100084
  chrome.setBusyPhase(phase);
99436
100085
  };
99437
100086
  const startBusy = (phase = "waiting for model") => {
@@ -99654,6 +100303,7 @@ async function launchTuiAgentShell(opts) {
99654
100303
  })();
99655
100304
  };
99656
100305
  const setMainAgent = (status, detail) => {
100306
+ debugEvent("main-agent", { status, detail });
99657
100307
  fleet.upsert({
99658
100308
  id: MAIN_AGENT_ID,
99659
100309
  label: "main",
@@ -100099,6 +100749,9 @@ async function launchTuiAgentShell(opts) {
100099
100749
  let nextArchiveIndex = 0;
100100
100750
  let sessionPersistTimer;
100101
100751
  let slateSession;
100752
+ const bindSlateToLiveSession = () => {
100753
+ slateSession = freshSlateSessionRef(liveSession.dir, sessionCwd);
100754
+ };
100102
100755
  const onContextCompaction = (r) => {
100103
100756
  const persisted = persistCompacted(liveSession, r.context, archive, {
100104
100757
  provider: currentSel.provider,
@@ -100232,7 +100885,7 @@ async function launchTuiAgentShell(opts) {
100232
100885
  model: currentSel.model
100233
100886
  }));
100234
100887
  }
100235
- slateSession = { dir: liveSession.dir, cwd: sessionCwd, opened: false };
100888
+ bindSlateToLiveSession();
100236
100889
  if (history.length === 0) {
100237
100890
  removeSplash = mountEmptyTranscriptSplash(otui, r, transcript);
100238
100891
  }
@@ -100333,6 +100986,8 @@ Staying in the current session.
100333
100986
  return;
100334
100987
  }
100335
100988
  applyOpened(opened, true);
100989
+ bindSlateToLiveSession();
100990
+ refreshWorkspaceSidebar();
100336
100991
  paintSessionHeader();
100337
100992
  if (opened.archiveDegraded !== undefined) {
100338
100993
  io.onSystem?.(`Archive unavailable \u2014 resumed from the active context (${opened.archiveDegraded})
@@ -101231,7 +101886,7 @@ Staying in the current session.
101231
101886
  (async () => {
101232
101887
  await closeSlateSession(slateSession, mintTimestampAttemptId);
101233
101888
  startNewSession();
101234
- slateSession = { dir: liveSession.dir, cwd: sessionCwd, opened: false };
101889
+ bindSlateToLiveSession();
101235
101890
  refreshWorkspaceSidebar();
101236
101891
  refreshReviewSidebar();
101237
101892
  sessions.clear();
@@ -103663,6 +104318,7 @@ function parseShellCliFlags(args) {
103663
104318
  let printPromptArg;
103664
104319
  let eventsFile;
103665
104320
  let eventsMaxField;
104321
+ let debug;
103666
104322
  for (let i = 0;i < args.length; i++) {
103667
104323
  const arg = args[i];
103668
104324
  if (arg === "--provider") {
@@ -103713,6 +104369,8 @@ function parseShellCliFlags(args) {
103713
104369
  i += 1;
103714
104370
  } else if (arg === "--events-file") {
103715
104371
  eventsFile = valueAfter(i++);
104372
+ } else if (arg === "--debug") {
104373
+ debug = true;
103716
104374
  } else if (arg === "--events-max-field") {
103717
104375
  const raw = valueAfter(i++);
103718
104376
  const parsed = Number(raw);
@@ -103742,7 +104400,8 @@ function parseShellCliFlags(args) {
103742
104400
  ...denyTools !== undefined ? { denyTools } : {},
103743
104401
  ...printPromptArg !== undefined ? { printPrompt: printPromptArg } : {},
103744
104402
  ...eventsFile !== undefined ? { eventsFile } : {},
103745
- ...eventsMaxField !== undefined ? { eventsMaxField } : {}
104403
+ ...eventsMaxField !== undefined ? { eventsMaxField } : {},
104404
+ ...debug === true ? { debug: true } : {}
103746
104405
  };
103747
104406
  }
103748
104407
  function chooseShellSurface(flags, isTty) {
@@ -103755,6 +104414,11 @@ function chooseShellSurface(flags, isTty) {
103755
104414
  return flags.modeFlag === false ? "tui-chat" : "tui-agent";
103756
104415
  }
103757
104416
  async function shellCommand(args, runtime = {}) {
104417
+ if (args[0] === "--debug-watcher") {
104418
+ await Promise.resolve().then(() => init_debug_watcher());
104419
+ await runDebugWatcher(args.slice(1));
104420
+ return;
104421
+ }
103758
104422
  const flags = parseShellCliFlags(args);
103759
104423
  if (flags.help) {
103760
104424
  console.log(`Usage: keryx shell [options]
@@ -103773,13 +104437,42 @@ async function shellCommand(args, runtime = {}) {
103773
104437
  --print, -p <prompt> Run one agent turn on this prompt and exit
103774
104438
  --events-file <path> Append an NDJSON transcript (turns, tools, usage)
103775
104439
  --events-max-field <n> Per-field character cap in that transcript (default 4000)
104440
+ --debug Log the session (input, keys, dialogs, tools) and start a watcher
104441
+ that re-arms terminal input if it stops; see debug/latest.txt
103776
104442
 
103777
104443
  Session continuation and resume are mutually exclusive. Permission flags
103778
104444
  are ignored in chat mode. Without a TTY, resume without an ID uses the latest session.
103779
104445
 
104446
+ A shell that stops reacting to keys can be recovered from another terminal
104447
+ with: kill -USR2 <keryx pid>
104448
+
103780
104449
  Example: keryx shell --provider ollama --model llama3.1:latest`);
103781
104450
  return;
103782
104451
  }
104452
+ if (flags.debug === true) {
104453
+ await Promise.resolve().then(() => init_debug_log());
104454
+ await Promise.resolve().then(() => init_debug_watcher());
104455
+ const run = startDebugRun(runtime.cacheDir !== undefined ? { configDir: runtime.cacheDir } : {});
104456
+ const watcherPid = spawnDebugWatcher(run);
104457
+ debugEvent("session.start", {
104458
+ version: package_default.version,
104459
+ argv: args,
104460
+ bun: typeof Bun !== "undefined" ? Bun.version : undefined,
104461
+ platform: process.platform,
104462
+ execPath: process.execPath,
104463
+ cwd: process.cwd(),
104464
+ stdinTTY: process.stdin.isTTY === true,
104465
+ stdoutTTY: process.stdout.isTTY === true,
104466
+ env: Object.fromEntries(["TERM", "TERM_PROGRAM", "COLORTERM", "TMUX", "HERDR_ENV", "HERDR_PANE_ID", "SSH_TTY", "LANG"].map((k) => [k, process.env[k]])),
104467
+ watcherPid,
104468
+ dir: run.dir
104469
+ });
104470
+ process.on("exit", (code) => {
104471
+ debugEvent("session.exit", { code });
104472
+ process.stderr.write(`keryx debug log: ${run.dir}
104473
+ `);
104474
+ });
104475
+ }
103783
104476
  const versionCheck = (runtime.checkVersion ?? (() => checkVersion({
103784
104477
  currentVersion: package_default.version,
103785
104478
  ...runtime.cacheDir !== undefined ? { cacheDir: runtime.cacheDir } : {}
@@ -104300,7 +104993,7 @@ Shell:
104300
104993
  init_fs();
104301
104994
  import { readFile as readFile99 } from "fs/promises";
104302
104995
  import { stdin } from "process";
104303
- import path194 from "path";
104996
+ import path196 from "path";
104304
104997
  var MODULES = [
104305
104998
  { name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
104306
104999
  { name: "gdctx", flag: "--no-gdctx", desc: "token-aware command/read output", defaultEnabled: true },
@@ -104346,8 +105039,8 @@ async function modulesCommand(args = []) {
104346
105039
  return;
104347
105040
  }
104348
105041
  const wantsJson = args.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
104349
- const metaprojectRoot = path194.join(process.cwd(), ".metaproject");
104350
- const manifestPath = path194.join(metaprojectRoot, "metaproject.json");
105042
+ const metaprojectRoot = path196.join(process.cwd(), ".metaproject");
105043
+ const manifestPath = path196.join(metaprojectRoot, "metaproject.json");
104351
105044
  if (!await pathExists(manifestPath)) {
104352
105045
  if (wantsJson) {
104353
105046
  console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
@@ -104469,18 +105162,18 @@ init_config_dir();
104469
105162
  import { createHash as createHash44, randomBytes as randomBytes5, randomUUID as randomUUID30 } from "crypto";
104470
105163
  import {
104471
105164
  chmodSync as chmodSync4,
104472
- closeSync as closeSync3,
105165
+ closeSync as closeSync4,
104473
105166
  existsSync as existsSync34,
104474
105167
  fsyncSync as fsyncSync2,
104475
- openSync as openSync3,
105168
+ openSync as openSync4,
104476
105169
  renameSync as renameSync4,
104477
- statSync as statSync6,
105170
+ statSync as statSync7,
104478
105171
  unlinkSync as unlinkSync3,
104479
105172
  writeFileSync as writeFileSync9
104480
105173
  } from "fs";
104481
- import path195 from "path";
105174
+ import path197 from "path";
104482
105175
  function serveCredentialPath(dir) {
104483
- return path195.join(keryxConfigDir(dir), "serve-credentials.json");
105176
+ return path197.join(keryxConfigDir(dir), "serve-credentials.json");
104484
105177
  }
104485
105178
  function constantTimeEqual(a, b) {
104486
105179
  const width = Math.max(a.length, b.length);
@@ -104509,7 +105202,7 @@ function isGroupOrOtherAccessible(file) {
104509
105202
  if (process.platform === "win32") {
104510
105203
  return false;
104511
105204
  }
104512
- const mode = statSync6(file, { throwIfNoEntry: false })?.mode;
105205
+ const mode = statSync7(file, { throwIfNoEntry: false })?.mode;
104513
105206
  return mode !== undefined && (mode & 63) !== 0;
104514
105207
  }
104515
105208
  function readServeCredential(dir) {
@@ -104553,13 +105246,13 @@ function writeStore(store, dir) {
104553
105246
  const temp = `${file}.${randomUUID30()}.tmp`;
104554
105247
  try {
104555
105248
  ensureKeryxConfigDir(dir);
104556
- const handle = openSync3(temp, "wx", 384);
105249
+ const handle = openSync4(temp, "wx", 384);
104557
105250
  try {
104558
105251
  writeFileSync9(handle, `${JSON.stringify(store, null, 2)}
104559
105252
  `, { encoding: "utf8" });
104560
105253
  fsyncSync2(handle);
104561
105254
  } finally {
104562
- closeSync3(handle);
105255
+ closeSync4(handle);
104563
105256
  }
104564
105257
  renameSync4(temp, file);
104565
105258
  tighten2(file, 384);
@@ -104713,23 +105406,23 @@ class AuthFailureThrottle {
104713
105406
  // src/lib/serve-turn-store.ts
104714
105407
  init_config_dir();
104715
105408
  import { createHash as createHash45 } from "crypto";
104716
- import { existsSync as existsSync35, readdirSync as readdirSync3, rmSync as rmSync2 } from "fs";
104717
- import path196 from "path";
105409
+ import { existsSync as existsSync35, readdirSync as readdirSync4, rmSync as rmSync2 } from "fs";
105410
+ import path198 from "path";
104718
105411
  var MAX_TURN_EVENTS = 1e4;
104719
105412
  function turnsRoot(dir) {
104720
- return path196.join(keryxConfigDir(dir), "turns");
105413
+ return path198.join(keryxConfigDir(dir), "turns");
104721
105414
  }
104722
105415
  function turnDir(turnId, dir) {
104723
- return path196.join(turnsRoot(dir), turnId);
105416
+ return path198.join(turnsRoot(dir), turnId);
104724
105417
  }
104725
105418
  function keyPath(project, idempotencyKey, dir) {
104726
105419
  const projectBytes = Buffer.byteLength(project, "utf8");
104727
105420
  const digest = createHash45("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
104728
- return path196.join(turnsRoot(dir), "keys", `${digest}.json`);
105421
+ return path198.join(turnsRoot(dir), "keys", `${digest}.json`);
104729
105422
  }
104730
105423
  function legacyKeyPath(idempotencyKey, dir) {
104731
105424
  const digest = createHash45("sha256").update(idempotencyKey, "utf8").digest("hex");
104732
- return path196.join(turnsRoot(dir), "keys", `${digest}.json`);
105425
+ return path198.join(turnsRoot(dir), "keys", `${digest}.json`);
104733
105426
  }
104734
105427
  function adoptLegacyClaim(project, idempotencyKey, dir) {
104735
105428
  const legacy = legacyKeyPath(idempotencyKey, dir);
@@ -104793,7 +105486,7 @@ function ensureTurnDir(turnId, dir) {
104793
105486
  }
104794
105487
  function createTurnRecord(record, dir) {
104795
105488
  ensureTurnDir(record.turnId, dir);
104796
- writeOwnerOnlyFile(path196.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
105489
+ writeOwnerOnlyFile(path198.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
104797
105490
  `);
104798
105491
  }
104799
105492
  function appendTurnEvent(event, dir, opts) {
@@ -104802,12 +105495,12 @@ function appendTurnEvent(event, dir, opts) {
104802
105495
  }
104803
105496
  const line = JSON.stringify(event);
104804
105497
  try {
104805
- appendOwnerOnlyLine(path196.join(turnDir(event.turnId, dir), "events.jsonl"), line);
105498
+ appendOwnerOnlyLine(path198.join(turnDir(event.turnId, dir), "events.jsonl"), line);
104806
105499
  } catch (error) {
104807
105500
  if (error?.code !== "ENOENT") {
104808
105501
  throw error;
104809
105502
  }
104810
- appendOwnerOnlyLine(path196.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
105503
+ appendOwnerOnlyLine(path198.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
104811
105504
  }
104812
105505
  return true;
104813
105506
  }
@@ -104815,7 +105508,7 @@ function readTurnEvents(turnId, after = -1, dir) {
104815
105508
  if (!isTurnId(turnId)) {
104816
105509
  return { ok: false, reason: "not-a-turn-id" };
104817
105510
  }
104818
- const read = readTurnFile(path196.join(turnDir(turnId, dir), "events.jsonl"));
105511
+ const read = readTurnFile(path198.join(turnDir(turnId, dir), "events.jsonl"));
104819
105512
  if (!read.ok) {
104820
105513
  if (isDefiniteAbsence2(read.reason)) {
104821
105514
  return { ok: true, value: [] };
@@ -104843,7 +105536,7 @@ function readTurnRecord(turnId, dir) {
104843
105536
  if (!isTurnId(turnId)) {
104844
105537
  return { ok: false, reason: "not-a-turn-id" };
104845
105538
  }
104846
- const read = readTurnFile(path196.join(turnDir(turnId, dir), "turn.json"));
105539
+ const read = readTurnFile(path198.join(turnDir(turnId, dir), "turn.json"));
104847
105540
  if (!read.ok) {
104848
105541
  return { ok: false, reason: read.reason };
104849
105542
  }
@@ -104862,7 +105555,7 @@ function finishTurn(turnId, result, dir) {
104862
105555
  if (!record.ok) {
104863
105556
  return false;
104864
105557
  }
104865
- writeOwnerOnlyFile(path196.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
105558
+ writeOwnerOnlyFile(path198.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
104866
105559
  `);
104867
105560
  return true;
104868
105561
  }
@@ -104908,7 +105601,7 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
104908
105601
 
104909
105602
  // src/lib/serve-turn.ts
104910
105603
  import { randomUUID as randomUUID31 } from "crypto";
104911
- import path197 from "path";
105604
+ import path199 from "path";
104912
105605
  init_service();
104913
105606
  var REMOTE_ORIGIN = "remote:http";
104914
105607
  var MAX_PROMPT_CHARS = 32000;
@@ -104977,9 +105670,9 @@ function isUuid(value) {
104977
105670
  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);
104978
105671
  }
104979
105672
  function resolveProject(declared, dir) {
104980
- const wanted = path197.resolve(declared);
105673
+ const wanted = path199.resolve(declared);
104981
105674
  for (const entry of listProjects(dir, () => {})) {
104982
- if (path197.resolve(entry.path) === wanted) {
105675
+ if (path199.resolve(entry.path) === wanted) {
104983
105676
  return { ok: true, project: entry.path };
104984
105677
  }
104985
105678
  }
@@ -106012,10 +106705,10 @@ function printHelp19() {
106012
106705
  }
106013
106706
 
106014
106707
  // src/commands/update.ts
106015
- import { spawn as spawn6 } from "child_process";
106708
+ import { spawn as spawn7 } from "child_process";
106016
106709
  import { chmod as chmod4, mkdir as mkdir63, readFile as readFile100, readdir as readdir37, writeFile as writeFile54 } from "fs/promises";
106017
- import { access as access4, constants as constants2, existsSync as existsSync36 } from "fs";
106018
- import path198 from "path";
106710
+ import { access as access4, constants as constants3, existsSync as existsSync36 } from "fs";
106711
+ import path200 from "path";
106019
106712
  import { fileURLToPath as fileURLToPath8 } from "url";
106020
106713
  init_config2();
106021
106714
  init_config();
@@ -106032,8 +106725,8 @@ async function updateCommand(args = []) {
106032
106725
  return;
106033
106726
  }
106034
106727
  const projectRoot = process.cwd();
106035
- const metaprojectRoot = path198.join(projectRoot, ".metaproject");
106036
- banner("keryx update", `Refreshing the .metaproject workspace in ${path198.basename(projectRoot)}/`);
106728
+ const metaprojectRoot = path200.join(projectRoot, ".metaproject");
106729
+ banner("keryx update", `Refreshing the .metaproject workspace in ${path200.basename(projectRoot)}/`);
106037
106730
  if (!await pathExists(metaprojectRoot)) {
106038
106731
  console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
106039
106732
  console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
@@ -106106,7 +106799,7 @@ async function updateCommand(args = []) {
106106
106799
  nextSteps(steps);
106107
106800
  }
106108
106801
  async function previewServiceFiles(projectRoot, options) {
106109
- const metaprojectRoot = path198.join(projectRoot, ".metaproject");
106802
+ const metaprojectRoot = path200.join(projectRoot, ".metaproject");
106110
106803
  const manifestState = await readManifest5(metaprojectRoot);
106111
106804
  const manifest = manifestState.manifest;
106112
106805
  const enableTasks = moduleEnabled2(manifest, "tasks") || !options.noTasks;
@@ -106135,12 +106828,12 @@ async function previewServiceFiles(projectRoot, options) {
106135
106828
  });
106136
106829
  }
106137
106830
  async function refreshServiceFiles(projectRoot, options) {
106138
- const metaprojectRoot = path198.join(projectRoot, ".metaproject");
106831
+ const metaprojectRoot = path200.join(projectRoot, ".metaproject");
106139
106832
  const manifestState = await readManifest5(metaprojectRoot);
106140
106833
  const manifest = manifestState.manifest;
106141
106834
  const recoveredManifest = !manifestState.exists || !manifestState.valid;
106142
106835
  if (manifestState.migrated) {
106143
- await writeFile54(path198.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
106836
+ await writeFile54(path200.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
106144
106837
  `, "utf8");
106145
106838
  }
106146
106839
  const enableGdgraph = moduleEnabled2(manifest, "gdgraph");
@@ -106177,9 +106870,9 @@ async function refreshServiceFiles(projectRoot, options) {
106177
106870
  enableSecurity,
106178
106871
  enableSac
106179
106872
  });
106180
- await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
106181
- await writeTextIfChanged4(path198.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
106182
- await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
106873
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
106874
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
106875
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
106183
106876
  await writeRoutingEntrypointPair(metaprojectRoot, {
106184
106877
  enableGdgraph,
106185
106878
  enableGdctx,
@@ -106202,7 +106895,7 @@ async function refreshServiceFiles(projectRoot, options) {
106202
106895
  }
106203
106896
  }
106204
106897
  });
106205
- await writeTextIfChanged4(path198.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
106898
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
106206
106899
  enableGdgraph,
106207
106900
  enableGdctx,
106208
106901
  enableGdwiki,
@@ -106214,7 +106907,7 @@ async function refreshServiceFiles(projectRoot, options) {
106214
106907
  enableSecurity,
106215
106908
  data: dashboardData
106216
106909
  }));
106217
- await writeTextIfMissing4(path198.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
106910
+ await writeTextIfMissing4(path200.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
106218
106911
  enableGdgraph,
106219
106912
  enableGdctx,
106220
106913
  enableGdwiki,
@@ -106227,31 +106920,31 @@ async function refreshServiceFiles(projectRoot, options) {
106227
106920
  }));
106228
106921
  if (enableGdgraph) {
106229
106922
  await installGdgraphCoreScripts2(metaprojectRoot);
106230
- await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
106231
- await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
106232
- await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
106923
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
106924
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
106925
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
106233
106926
  await seedAssetsLock(metaprojectRoot);
106234
106927
  if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
106235
106928
  await installManagedHook2(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
106236
106929
  }
106237
106930
  }
106238
106931
  if (enableGdctx) {
106239
- await writeTextIfMissing4(path198.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
106240
- await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
106241
- await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
106242
- await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
106932
+ await writeTextIfMissing4(path200.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
106933
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
106934
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
106935
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
106243
106936
  }
106244
106937
  if (enableGdwiki) {
106245
- await writeTextIfMissing4(path198.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
106246
- await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
106247
- await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
106938
+ await writeTextIfMissing4(path200.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
106939
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
106940
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
106248
106941
  if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
106249
106942
  await installManagedHook2(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
106250
106943
  }
106251
106944
  }
106252
106945
  if (enableSac) {
106253
- await writeTextIfMissing4(path198.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
106254
- await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
106946
+ await writeTextIfMissing4(path200.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
106947
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
106255
106948
  }
106256
106949
  let gdskillsWarnings = [];
106257
106950
  let gdskillsNotices = [];
@@ -106264,25 +106957,25 @@ async function refreshServiceFiles(projectRoot, options) {
106264
106957
  }
106265
106958
  }
106266
106959
  if (enableHealth) {
106267
- await writeTextIfMissing4(path198.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
106268
- await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
106269
- await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
106270
- await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
106960
+ await writeTextIfMissing4(path200.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
106961
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
106962
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
106963
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
106271
106964
  if (manifest.modules?.health?.hooks?.gitPostCommit) {
106272
106965
  await installManagedHook2(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
106273
106966
  }
106274
106967
  }
106275
106968
  if (enableTesting) {
106276
- await writeTextIfMissing4(path198.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
106969
+ await writeTextIfMissing4(path200.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
106277
106970
  postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
106278
106971
  prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
106279
106972
  }));
106280
- await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
106281
- await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
106282
- await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
106973
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
106974
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
106975
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
106283
106976
  if (enableGdwiki) {
106284
- await writeTextIfMissing4(path198.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
106285
- await writeTextIfMissing4(path198.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
106977
+ await writeTextIfMissing4(path200.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
106978
+ await writeTextIfMissing4(path200.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
106286
106979
  }
106287
106980
  if (manifest.modules?.testing?.hooks?.gitPostCommit) {
106288
106981
  await installManagedHook2(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
@@ -106295,24 +106988,24 @@ async function refreshServiceFiles(projectRoot, options) {
106295
106988
  await installManagedHook2(projectRoot, "post-commit", "metaproject-dashboard-post-commit", renderMetaprojectDashboardPostCommitHook());
106296
106989
  }
106297
106990
  if (enableMemory) {
106298
- await writeTextIfMissing4(path198.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
106299
- await writeTextIfMissing4(path198.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
106300
- await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
106301
- await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
106302
- await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
106991
+ await writeTextIfMissing4(path200.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
106992
+ await writeTextIfMissing4(path200.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
106993
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
106994
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
106995
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
106303
106996
  }
106304
106997
  if (enableTasks) {
106305
- await writeTextIfChanged4(path198.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
106306
- await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
106307
- await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
106308
- await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
106309
- await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
106310
- await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
106998
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
106999
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
107000
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
107001
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
107002
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
107003
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
106311
107004
  }
106312
107005
  if (enableSecurity) {
106313
- await writeTextIfMissing4(path198.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
106314
- await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
106315
- await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
107006
+ await writeTextIfMissing4(path200.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
107007
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
107008
+ await writeTextIfChanged4(path200.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
106316
107009
  if (manifest.modules?.security?.hooks?.prePush) {
106317
107010
  await installManagedHook2(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
106318
107011
  }
@@ -106365,13 +107058,13 @@ async function refreshServiceFiles(projectRoot, options) {
106365
107058
  };
106366
107059
  }
106367
107060
  async function buildDashboard(projectRoot = process.cwd()) {
106368
- const metaprojectRoot = path198.join(projectRoot, ".metaproject");
107061
+ const metaprojectRoot = path200.join(projectRoot, ".metaproject");
106369
107062
  if (!await pathExists(metaprojectRoot)) {
106370
107063
  throw new Error("Metaproject is not initialized. Run: keryx init");
106371
107064
  }
106372
107065
  const manifest = (await readManifest5(metaprojectRoot)).manifest;
106373
107066
  const data = await collectDashboardData(metaprojectRoot);
106374
- const dashboardPath = path198.join(metaprojectRoot, "keryx-dashboard.html");
107067
+ const dashboardPath = path200.join(metaprojectRoot, "keryx-dashboard.html");
106375
107068
  await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
106376
107069
  enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
106377
107070
  enableGdctx: moduleEnabled2(manifest, "gdctx"),
@@ -106391,7 +107084,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
106391
107084
  if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
106392
107085
  return true;
106393
107086
  }
106394
- const hookPath = path198.join(projectRoot, ".git", "hooks", "post-commit");
107087
+ const hookPath = path200.join(projectRoot, ".git", "hooks", "post-commit");
106395
107088
  if (!await pathExists(hookPath)) {
106396
107089
  return false;
106397
107090
  }
@@ -106411,11 +107104,11 @@ async function collectDashboardData(metaprojectRoot) {
106411
107104
  if (testing) {
106412
107105
  data.testing = testing;
106413
107106
  }
106414
- const wiki = await collectMarkdownPages(path198.join(metaprojectRoot, "wiki"), "wiki");
107107
+ const wiki = await collectMarkdownPages(path200.join(metaprojectRoot, "wiki"), "wiki");
106415
107108
  if (wiki.length > 0) {
106416
107109
  data.wiki = { pages: wiki };
106417
107110
  }
106418
- const memory = await collectMarkdownPages(path198.join(metaprojectRoot, "memory"), "memory");
107111
+ const memory = await collectMarkdownPages(path200.join(metaprojectRoot, "memory"), "memory");
106419
107112
  if (memory.length > 0) {
106420
107113
  data.memory = { entries: memory };
106421
107114
  }
@@ -106430,7 +107123,7 @@ async function collectDashboardData(metaprojectRoot) {
106430
107123
  return data;
106431
107124
  }
106432
107125
  async function collectTasksDashboardData(metaprojectRoot) {
106433
- const flowsRoot = path198.join(metaprojectRoot, "flows");
107126
+ const flowsRoot = path200.join(metaprojectRoot, "flows");
106434
107127
  if (!await pathExists(flowsRoot)) {
106435
107128
  return null;
106436
107129
  }
@@ -106442,7 +107135,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
106442
107135
  }
106443
107136
  const flows = [];
106444
107137
  for (const dir of dirEntries) {
106445
- const flowPath = path198.join(flowsRoot, dir, "flow.json");
107138
+ const flowPath = path200.join(flowsRoot, dir, "flow.json");
106446
107139
  if (!await pathExists(flowPath)) {
106447
107140
  continue;
106448
107141
  }
@@ -106450,7 +107143,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
106450
107143
  const flow = JSON.parse(await readFile100(flowPath, "utf8"));
106451
107144
  const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
106452
107145
  let acTotal = 0;
106453
- const acPath = path198.join(flowsRoot, dir, "acceptance-criteria.md");
107146
+ const acPath = path200.join(flowsRoot, dir, "acceptance-criteria.md");
106454
107147
  if (await pathExists(acPath)) {
106455
107148
  const acContent = await readFile100(acPath, "utf8");
106456
107149
  acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
@@ -106504,7 +107197,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
106504
107197
  "data/testing/context.md"
106505
107198
  ];
106506
107199
  for (const href of staticHrefs) {
106507
- const filePath = path198.join(metaprojectRoot, ...href.split("/"));
107200
+ const filePath = path200.join(metaprojectRoot, ...href.split("/"));
106508
107201
  if (!await pathExists(filePath)) {
106509
107202
  continue;
106510
107203
  }
@@ -106521,7 +107214,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
106521
107214
  return docs;
106522
107215
  }
106523
107216
  async function collectHealthDashboardData(metaprojectRoot) {
106524
- const reportPath = path198.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
107217
+ const reportPath = path200.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
106525
107218
  if (!await pathExists(reportPath)) {
106526
107219
  return;
106527
107220
  }
@@ -106630,8 +107323,8 @@ function metricToScope(metric) {
106630
107323
  };
106631
107324
  }
106632
107325
  async function collectGraphDashboardData(metaprojectRoot) {
106633
- const nodesPath = path198.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
106634
- const edgesPath = path198.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
107326
+ const nodesPath = path200.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
107327
+ const edgesPath = path200.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
106635
107328
  if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
106636
107329
  return;
106637
107330
  }
@@ -106682,8 +107375,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
106682
107375
  };
106683
107376
  }
106684
107377
  async function collectTestingDashboardData(metaprojectRoot) {
106685
- const reportPath = path198.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
106686
- const contextPath = path198.join(metaprojectRoot, "data", "testing", "context.md");
107378
+ const reportPath = path200.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
107379
+ const contextPath = path200.join(metaprojectRoot, "data", "testing", "context.md");
106687
107380
  if (await pathExists(reportPath)) {
106688
107381
  const report = JSON.parse(await readFile100(reportPath, "utf8"));
106689
107382
  const totalTests = numberOrUndefined(report.total);
@@ -106712,7 +107405,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
106712
107405
  const files = await listMarkdownFiles(root);
106713
107406
  const pages = [];
106714
107407
  for (const filePath of files.slice(0, 40)) {
106715
- const relativePath = path198.relative(root, filePath).split(path198.sep).join("/");
107408
+ const relativePath = path200.relative(root, filePath).split(path200.sep).join("/");
106716
107409
  if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
106717
107410
  continue;
106718
107411
  }
@@ -106733,7 +107426,7 @@ async function listMarkdownFiles(root) {
106733
107426
  const entries = await readdir37(root, { withFileTypes: true });
106734
107427
  const files = [];
106735
107428
  for (const entry of entries) {
106736
- const fullPath = path198.join(root, entry.name);
107429
+ const fullPath = path200.join(root, entry.name);
106737
107430
  if (entry.isDirectory()) {
106738
107431
  files.push(...await listMarkdownFiles(fullPath));
106739
107432
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -106781,7 +107474,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
106781
107474
  const manifest = {
106782
107475
  schemaVersion: 1,
106783
107476
  standardVersion: STANDARD_VERSION,
106784
- name: `${path198.basename(path198.dirname(metaprojectRoot))}-metaproject`,
107477
+ name: `${path200.basename(path200.dirname(metaprojectRoot))}-metaproject`,
106785
107478
  createdBy: "keryx",
106786
107479
  profiles: computeProfiles(enabledModuleKeys),
106787
107480
  paths: {
@@ -106871,11 +107564,11 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
106871
107564
  metaproject: ".metaproject/index.md"
106872
107565
  }
106873
107566
  };
106874
- await writeFile54(path198.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
107567
+ await writeFile54(path200.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
106875
107568
  `, "utf8");
106876
107569
  }
106877
107570
  async function enableTasksInManifest(metaprojectRoot) {
106878
- const manifestPath = path198.join(metaprojectRoot, "metaproject.json");
107571
+ const manifestPath = path200.join(metaprojectRoot, "metaproject.json");
106879
107572
  if (!await pathExists(manifestPath)) {
106880
107573
  return;
106881
107574
  }
@@ -106898,7 +107591,7 @@ async function enableTasksInManifest(metaprojectRoot) {
106898
107591
  `, "utf8");
106899
107592
  }
106900
107593
  async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
106901
- const manifestPath = path198.join(metaprojectRoot, "metaproject.json");
107594
+ const manifestPath = path200.join(metaprojectRoot, "metaproject.json");
106902
107595
  if (!await pathExists(manifestPath)) {
106903
107596
  return;
106904
107597
  }
@@ -106934,72 +107627,72 @@ async function updateRuntime(projectRoot) {
106934
107627
  }
106935
107628
  }
106936
107629
  async function findRuntimeRoot(projectRoot) {
106937
- const projectRuntime = path198.join(projectRoot, ".metaproject", "runtime", "keryx");
106938
- if (await pathExists(path198.join(projectRuntime, ".git"))) {
107630
+ const projectRuntime = path200.join(projectRoot, ".metaproject", "runtime", "keryx");
107631
+ if (await pathExists(path200.join(projectRuntime, ".git"))) {
106939
107632
  return projectRuntime;
106940
107633
  }
106941
107634
  const home = process.env.HOME;
106942
107635
  if (!home) {
106943
107636
  return null;
106944
107637
  }
106945
- const globalRuntime = path198.join(home, ".keryx", "keryx");
106946
- if (await pathExists(path198.join(globalRuntime, ".git"))) {
107638
+ const globalRuntime = path200.join(home, ".keryx", "keryx");
107639
+ if (await pathExists(path200.join(globalRuntime, ".git"))) {
106947
107640
  return globalRuntime;
106948
107641
  }
106949
107642
  return null;
106950
107643
  }
106951
107644
  async function createServiceDirs(metaprojectRoot, modules) {
106952
107645
  const dirs = [
106953
- path198.join(metaprojectRoot, "core"),
106954
- path198.join(metaprojectRoot, "hooks", "post-update.d"),
106955
- path198.join(metaprojectRoot, "modules"),
106956
- path198.join(metaprojectRoot, "rules"),
106957
- path198.join(metaprojectRoot, "skills", "project-rules"),
107646
+ path200.join(metaprojectRoot, "core"),
107647
+ path200.join(metaprojectRoot, "hooks", "post-update.d"),
107648
+ path200.join(metaprojectRoot, "modules"),
107649
+ path200.join(metaprojectRoot, "rules"),
107650
+ path200.join(metaprojectRoot, "skills", "project-rules"),
106958
107651
  ...modules.enableGdgraph ? [
106959
- path198.join(metaprojectRoot, "core", "gdgraph"),
106960
- path198.join(metaprojectRoot, "skills", "gdgraph")
107652
+ path200.join(metaprojectRoot, "core", "gdgraph"),
107653
+ path200.join(metaprojectRoot, "skills", "gdgraph")
106961
107654
  ] : [],
106962
107655
  ...modules.enableGdctx ? [
106963
- path198.join(metaprojectRoot, "core", "gdctx"),
106964
- path198.join(metaprojectRoot, "skills", "gdctx")
107656
+ path200.join(metaprojectRoot, "core", "gdctx"),
107657
+ path200.join(metaprojectRoot, "skills", "gdctx")
106965
107658
  ] : [],
106966
107659
  ...modules.enableGdwiki ? [
106967
- path198.join(metaprojectRoot, "skills", "gdwiki"),
106968
- path198.join(metaprojectRoot, "wiki", "templates")
107660
+ path200.join(metaprojectRoot, "skills", "gdwiki"),
107661
+ path200.join(metaprojectRoot, "wiki", "templates")
106969
107662
  ] : [],
106970
107663
  ...modules.enableHealth ? [
106971
- path198.join(metaprojectRoot, "core", "health"),
106972
- path198.join(metaprojectRoot, "skills", "health")
107664
+ path200.join(metaprojectRoot, "core", "health"),
107665
+ path200.join(metaprojectRoot, "skills", "health")
106973
107666
  ] : [],
106974
107667
  ...modules.enableTesting ? [
106975
- path198.join(metaprojectRoot, "core", "testing"),
106976
- path198.join(metaprojectRoot, "skills", "testing")
107668
+ path200.join(metaprojectRoot, "core", "testing"),
107669
+ path200.join(metaprojectRoot, "skills", "testing")
106977
107670
  ] : [],
106978
107671
  ...modules.enableMemory ? [
106979
- path198.join(metaprojectRoot, "core", "memory"),
106980
- path198.join(metaprojectRoot, "skills", "memory"),
106981
- path198.join(metaprojectRoot, "memory", "templates")
107672
+ path200.join(metaprojectRoot, "core", "memory"),
107673
+ path200.join(metaprojectRoot, "skills", "memory"),
107674
+ path200.join(metaprojectRoot, "memory", "templates")
106982
107675
  ] : [],
106983
107676
  ...modules.enableTasks ? [
106984
- path198.join(metaprojectRoot, "flows"),
106985
- path198.join(metaprojectRoot, "skills", "flow")
107677
+ path200.join(metaprojectRoot, "flows"),
107678
+ path200.join(metaprojectRoot, "skills", "flow")
106986
107679
  ] : [],
106987
107680
  ...modules.enableSecurity ? [
106988
- path198.join(metaprojectRoot, "core", "security")
107681
+ path200.join(metaprojectRoot, "core", "security")
106989
107682
  ] : [],
106990
107683
  ...modules.enableSac ? [
106991
- path198.join(metaprojectRoot, "skills", "sac")
107684
+ path200.join(metaprojectRoot, "skills", "sac")
106992
107685
  ] : []
106993
107686
  ];
106994
107687
  await Promise.all(dirs.map((dir) => mkdir63(dir, { recursive: true })));
106995
107688
  }
106996
107689
  async function installGdgraphCoreScripts2(metaprojectRoot) {
106997
- const gdgraphCoreRoot = path198.join(metaprojectRoot, "core", "gdgraph");
107690
+ const gdgraphCoreRoot = path200.join(metaprojectRoot, "core", "gdgraph");
106998
107691
  await mkdir63(gdgraphCoreRoot, { recursive: true });
106999
107692
  for (const file of GDGRAPH_CORE_SOURCES) {
107000
- await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path198.join(gdgraphCoreRoot, file));
107693
+ await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path200.join(gdgraphCoreRoot, file));
107001
107694
  }
107002
- await writeTextIfChanged4(path198.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
107695
+ await writeTextIfChanged4(path200.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
107003
107696
  }
107004
107697
  async function installManagedHook2(projectRoot, hookName, blockId, content) {
107005
107698
  const hooksRoot = await resolveGitHooksRoot(projectRoot);
@@ -107007,7 +107700,7 @@ async function installManagedHook2(projectRoot, hookName, blockId, content) {
107007
107700
  return;
107008
107701
  }
107009
107702
  await mkdir63(hooksRoot, { recursive: true });
107010
- const hookPath = path198.join(hooksRoot, hookName);
107703
+ const hookPath = path200.join(hooksRoot, hookName);
107011
107704
  const blockStart = `# keryx:${blockId}:begin`;
107012
107705
  const blockEnd = `# keryx:${blockId}:end`;
107013
107706
  const managedBlock = `${blockStart}
@@ -107028,7 +107721,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
107028
107721
  if (!hooksRoot) {
107029
107722
  return;
107030
107723
  }
107031
- const hookPath = path198.join(hooksRoot, hookName);
107724
+ const hookPath = path200.join(hooksRoot, hookName);
107032
107725
  if (!await pathExists(hookPath)) {
107033
107726
  return;
107034
107727
  }
@@ -107050,7 +107743,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
107050
107743
  if (!hooksRoot) {
107051
107744
  return false;
107052
107745
  }
107053
- const hookPath = path198.join(hooksRoot, "pre-push");
107746
+ const hookPath = path200.join(hooksRoot, "pre-push");
107054
107747
  if (!await pathExists(hookPath)) {
107055
107748
  return false;
107056
107749
  }
@@ -107065,7 +107758,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
107065
107758
  return (await readFile100(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
107066
107759
  }
107067
107760
  async function readManifest5(metaprojectRoot) {
107068
- const manifestPath = path198.join(metaprojectRoot, "metaproject.json");
107761
+ const manifestPath = path200.join(metaprojectRoot, "metaproject.json");
107069
107762
  if (!await pathExists(manifestPath)) {
107070
107763
  return {
107071
107764
  exists: false,
@@ -107134,7 +107827,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
107134
107827
  }
107135
107828
  async function anyPathExists(root, candidates) {
107136
107829
  for (const candidate of candidates) {
107137
- if (await pathExists(path198.join(root, candidate))) {
107830
+ if (await pathExists(path200.join(root, candidate))) {
107138
107831
  return true;
107139
107832
  }
107140
107833
  }
@@ -107157,13 +107850,13 @@ function parseUpdateArgs(args) {
107157
107850
  };
107158
107851
  }
107159
107852
  async function runPostUpdateHooks(projectRoot) {
107160
- const hooksDir = path198.join(projectRoot, ".metaproject", "hooks", "post-update.d");
107853
+ const hooksDir = path200.join(projectRoot, ".metaproject", "hooks", "post-update.d");
107161
107854
  if (!await pathExists(hooksDir)) {
107162
107855
  return;
107163
107856
  }
107164
107857
  const entries = (await readdir37(hooksDir)).sort();
107165
107858
  for (const entry of entries) {
107166
- const hookPath = path198.join(hooksDir, entry);
107859
+ const hookPath = path200.join(hooksDir, entry);
107167
107860
  try {
107168
107861
  await accessExecutable(hookPath);
107169
107862
  } catch {
@@ -107175,7 +107868,7 @@ async function runPostUpdateHooks(projectRoot) {
107175
107868
  }
107176
107869
  async function accessExecutable(filePath) {
107177
107870
  await new Promise((resolve, reject) => {
107178
- access4(filePath, constants2.X_OK, (error) => {
107871
+ access4(filePath, constants3.X_OK, (error) => {
107179
107872
  if (error) {
107180
107873
  reject(error);
107181
107874
  return;
@@ -107186,7 +107879,7 @@ async function accessExecutable(filePath) {
107186
107879
  }
107187
107880
  async function run(command, args, cwd) {
107188
107881
  await new Promise((resolve, reject) => {
107189
- const child = spawn6(command, args, {
107882
+ const child = spawn7(command, args, {
107190
107883
  cwd,
107191
107884
  stdio: "inherit"
107192
107885
  });
@@ -107204,14 +107897,14 @@ async function writeTextIfChanged4(filePath, content) {
107204
107897
  if (await pathExists(filePath) && await readFile100(filePath, "utf8") === content) {
107205
107898
  return;
107206
107899
  }
107207
- await mkdir63(path198.dirname(filePath), { recursive: true });
107900
+ await mkdir63(path200.dirname(filePath), { recursive: true });
107208
107901
  await writeFile54(filePath, content, "utf8");
107209
107902
  }
107210
107903
  async function writeTextIfMissing4(filePath, content) {
107211
107904
  if (await pathExists(filePath)) {
107212
107905
  return;
107213
107906
  }
107214
- await mkdir63(path198.dirname(filePath), { recursive: true });
107907
+ await mkdir63(path200.dirname(filePath), { recursive: true });
107215
107908
  await writeFile54(filePath, content, "utf8");
107216
107909
  }
107217
107910
  async function copyFileIfChanged2(from, to) {
@@ -107219,7 +107912,7 @@ async function copyFileIfChanged2(from, to) {
107219
107912
  if (await pathExists(to) && await readFile100(to, "utf8") === next) {
107220
107913
  return;
107221
107914
  }
107222
- await mkdir63(path198.dirname(to), { recursive: true });
107915
+ await mkdir63(path200.dirname(to), { recursive: true });
107223
107916
  await writeFile54(to, next, "utf8");
107224
107917
  }
107225
107918
  function runtimeSourcePath2(relativePath) {
@@ -107228,7 +107921,7 @@ function runtimeSourcePath2(relativePath) {
107228
107921
  return directPath;
107229
107922
  }
107230
107923
  if (relativePath.startsWith("../")) {
107231
- const packagedSourcePath = path198.join(path198.dirname(fileURLToPath8(import.meta.url)), "..", "src", relativePath.slice(3));
107924
+ const packagedSourcePath = path200.join(path200.dirname(fileURLToPath8(import.meta.url)), "..", "src", relativePath.slice(3));
107232
107925
  if (existsSync36(packagedSourcePath)) {
107233
107926
  return packagedSourcePath;
107234
107927
  }
@@ -107262,8 +107955,8 @@ function printHelp20() {
107262
107955
  }
107263
107956
 
107264
107957
  // src/commands/dashboard.ts
107265
- import { spawn as spawn7 } from "child_process";
107266
- import path199 from "path";
107958
+ import { spawn as spawn8 } from "child_process";
107959
+ import path201 from "path";
107267
107960
  init_args();
107268
107961
  async function dashboardCommand(args = []) {
107269
107962
  const options = parseOptions(args);
@@ -107274,7 +107967,7 @@ async function dashboardCommand(args = []) {
107274
107967
  }
107275
107968
  if (subcommand === "build") {
107276
107969
  const result = await buildDashboard();
107277
- const rel = path199.relative(process.cwd(), result.path);
107970
+ const rel = path201.relative(process.cwd(), result.path);
107278
107971
  console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
107279
107972
  note(`Open it: keryx dashboard open`);
107280
107973
  return;
@@ -107282,7 +107975,7 @@ async function dashboardCommand(args = []) {
107282
107975
  if (subcommand === "open") {
107283
107976
  const result = await buildDashboard();
107284
107977
  await openFile(result.path);
107285
- const rel = path199.relative(process.cwd(), result.path);
107978
+ const rel = path201.relative(process.cwd(), result.path);
107286
107979
  console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
107287
107980
  return;
107288
107981
  }
@@ -107301,7 +107994,7 @@ async function openFile(filePath) {
107301
107994
  const command = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
107302
107995
  const args = process.platform === "win32" ? ["/c", "start", "", filePath] : [filePath];
107303
107996
  await new Promise((resolve, reject) => {
107304
- const child = spawn7(command, args, { stdio: "ignore", detached: true });
107997
+ const child = spawn8(command, args, { stdio: "ignore", detached: true });
107305
107998
  child.on("error", reject);
107306
107999
  child.on("close", (code) => {
107307
108000
  if (code === 0) {
@@ -107326,12 +108019,12 @@ function printHelp21() {
107326
108019
  }
107327
108020
 
107328
108021
  // src/commands/agents.ts
107329
- import { readFileSync as readFileSync9 } from "fs";
108022
+ import { readFileSync as readFileSync10 } from "fs";
107330
108023
 
107331
108024
  // src/agents/bootstrap.ts
107332
108025
  import { mkdir as mkdir64, readFile as readFile101, writeFile as writeFile55 } from "fs/promises";
107333
108026
  import { homedir as homedir10 } from "os";
107334
- import path200 from "path";
108027
+ import path202 from "path";
107335
108028
  init_fs();
107336
108029
  var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
107337
108030
  var AGENT_BOOTSTRAP_END = "<!-- /keryx:global-bootstrap -->";
@@ -107341,35 +108034,35 @@ var AGENT_BOOTSTRAP_RUNTIMES = [
107341
108034
  aliases: ["claude-code"],
107342
108035
  label: "Claude Code",
107343
108036
  fileName: "CLAUDE.md",
107344
- filePath: (homeRoot) => path200.join(homeRoot, ".claude", "CLAUDE.md")
108037
+ filePath: (homeRoot) => path202.join(homeRoot, ".claude", "CLAUDE.md")
107345
108038
  },
107346
108039
  {
107347
108040
  id: "opencode",
107348
108041
  aliases: ["open-code"],
107349
108042
  label: "OpenCode",
107350
108043
  fileName: "AGENTS.md",
107351
- filePath: (homeRoot) => path200.join(homeRoot, ".config", "opencode", "AGENTS.md")
108044
+ filePath: (homeRoot) => path202.join(homeRoot, ".config", "opencode", "AGENTS.md")
107352
108045
  },
107353
108046
  {
107354
108047
  id: "zcode",
107355
108048
  aliases: ["zed", "zed-code"],
107356
108049
  label: "ZCode",
107357
108050
  fileName: "AGENTS.md",
107358
- filePath: (homeRoot) => path200.join(homeRoot, ".zcode", "AGENTS.md")
108051
+ filePath: (homeRoot) => path202.join(homeRoot, ".zcode", "AGENTS.md")
107359
108052
  },
107360
108053
  {
107361
108054
  id: "codex",
107362
108055
  aliases: [],
107363
108056
  label: "Codex",
107364
108057
  fileName: "AGENTS.md",
107365
- filePath: (homeRoot) => path200.join(homeRoot, ".codex", "AGENTS.md")
108058
+ filePath: (homeRoot) => path202.join(homeRoot, ".codex", "AGENTS.md")
107366
108059
  },
107367
108060
  {
107368
108061
  id: "antigravity",
107369
108062
  aliases: ["antigravuty", "antigravity-code"],
107370
108063
  label: "Antigravity",
107371
108064
  fileName: "AGENTS.md",
107372
- filePath: (homeRoot) => path200.join(homeRoot, ".config", "antigravity", "AGENTS.md")
108065
+ filePath: (homeRoot) => path202.join(homeRoot, ".config", "antigravity", "AGENTS.md")
107373
108066
  }
107374
108067
  ];
107375
108068
  function agentBootstrapRuntimeIds() {
@@ -107417,7 +108110,7 @@ async function installAgentBootstrap(runtime, options = {}) {
107417
108110
  const dryRun = options.dryRun === true;
107418
108111
  const wrote = next !== current;
107419
108112
  if (wrote && !dryRun) {
107420
- await mkdir64(path200.dirname(filePath), { recursive: true });
108113
+ await mkdir64(path202.dirname(filePath), { recursive: true });
107421
108114
  await writeFile55(filePath, next, "utf8");
107422
108115
  }
107423
108116
  const status = dryRun ? statusFromContent(runtime, filePath, exists, next) : await agentBootstrapStatus(runtime, homeRoot);
@@ -107827,7 +108520,7 @@ function monitorCommand(args) {
107827
108520
  }
107828
108521
  let events;
107829
108522
  try {
107830
- events = parseAgentEvents(readFileSync9(source, "utf8"));
108523
+ events = parseAgentEvents(readFileSync10(source, "utf8"));
107831
108524
  } catch (error) {
107832
108525
  console.error(`Failed to read/parse agent-event source "${source}": ${error.message}`);
107833
108526
  process.exitCode = 1;
@@ -107954,7 +108647,7 @@ function printBootstrapHelp() {
107954
108647
  // src/commands/metrics.ts
107955
108648
  init_args();
107956
108649
  import { readFile as readFile102 } from "fs/promises";
107957
- import path202 from "path";
108650
+ import path204 from "path";
107958
108651
 
107959
108652
  // src/metrics/benchmark.ts
107960
108653
  var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
@@ -108771,7 +109464,7 @@ function buildContainmentManifest(inputs, options = {}) {
108771
109464
 
108772
109465
  // src/metrics/oracle-runner.ts
108773
109466
  import { mkdir as mkdir65, writeFile as writeFile56 } from "fs/promises";
108774
- import path201 from "path";
109467
+ import path203 from "path";
108775
109468
 
108776
109469
  // src/metrics/ir.ts
108777
109470
  function toIdSet(ids) {
@@ -109217,9 +109910,9 @@ function buildEvidenceBundle(input, options = {}) {
109217
109910
  async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
109218
109911
  const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
109219
109912
  const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
109220
- const dir = path201.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
109913
+ const dir = path203.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
109221
109914
  await mkdir65(dir, { recursive: true });
109222
- const write = (name, value) => writeFile56(path201.join(dir, name), `${JSON.stringify(value, null, 2)}
109915
+ const write = (name, value) => writeFile56(path203.join(dir, name), `${JSON.stringify(value, null, 2)}
109223
109916
  `, "utf8");
109224
109917
  await Promise.all([
109225
109918
  write("inputs.json", bundle.inputs),
@@ -109259,7 +109952,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
109259
109952
  console.log("# metrics status");
109260
109953
  console.log("");
109261
109954
  console.log(`root: ${root}`);
109262
- console.log(`enabled: ${await Bun.file(path202.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
109955
+ console.log(`enabled: ${await Bun.file(path204.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
109263
109956
  const latest = await readLatestPointer(root);
109264
109957
  console.log(`latest: ${latest.status}`);
109265
109958
  return;
@@ -109271,7 +109964,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
109271
109964
  process.exitCode = 1;
109272
109965
  return;
109273
109966
  }
109274
- const record = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
109967
+ const record = JSON.parse(await readFile102(path204.resolve(projectRoot, file), "utf8"));
109275
109968
  const result = validateRunRecord(record);
109276
109969
  console.log(result.valid ? "valid: yes" : "valid: no");
109277
109970
  for (const error of result.errors)
@@ -109296,7 +109989,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
109296
109989
  process.exitCode = 1;
109297
109990
  return;
109298
109991
  }
109299
- const file = path202.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
109992
+ const file = path204.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
109300
109993
  if (!await Bun.file(file).exists()) {
109301
109994
  console.error(`Run not found: ${runId}`);
109302
109995
  process.exitCode = 1;
@@ -109313,8 +110006,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
109313
110006
  process.exitCode = 1;
109314
110007
  return;
109315
110008
  }
109316
- const a = JSON.parse(await readFile102(path202.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
109317
- const b = JSON.parse(await readFile102(path202.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
110009
+ const a = JSON.parse(await readFile102(path204.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
110010
+ const b = JSON.parse(await readFile102(path204.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
109318
110011
  const comparison = compareExecutionRuns(a, b);
109319
110012
  console.log(stableJson(comparison));
109320
110013
  return;
@@ -109348,8 +110041,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
109348
110041
  return;
109349
110042
  }
109350
110043
  const template = createPairedBenchmarkTemplate(taskIds);
109351
- await Bun.write(path202.resolve(projectRoot, out), stableJson(template));
109352
- console.log(`manifest: ${path202.relative(projectRoot, path202.resolve(projectRoot, out))}`);
110044
+ await Bun.write(path204.resolve(projectRoot, out), stableJson(template));
110045
+ console.log(`manifest: ${path204.relative(projectRoot, path204.resolve(projectRoot, out))}`);
109353
110046
  return;
109354
110047
  }
109355
110048
  if (subcommand === "benchmark" && args[1] === "run") {
@@ -109363,7 +110056,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
109363
110056
  process.exitCode = 1;
109364
110057
  return;
109365
110058
  }
109366
- const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
110059
+ const raw = JSON.parse(await readFile102(path204.resolve(projectRoot, file), "utf8"));
109367
110060
  const input = Array.isArray(raw) ? raw : raw.runs ?? [];
109368
110061
  const result = validatePairedBenchmark(input);
109369
110062
  console.log(stableJson(result));
@@ -109375,7 +110068,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
109375
110068
  process.exitCode = 1;
109376
110069
  }
109377
110070
  async function loadAffectedSets(projectRoot, file) {
109378
- const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
110071
+ const raw = JSON.parse(await readFile102(path204.resolve(projectRoot, file), "utf8"));
109379
110072
  const map = new Map;
109380
110073
  for (const entry of raw.targets ?? []) {
109381
110074
  if (typeof entry.target === "string")
@@ -109446,7 +110139,7 @@ async function runHarnessLayer(projectRoot, args, ladder) {
109446
110139
  let tasks;
109447
110140
  let model;
109448
110141
  try {
109449
- const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, resultsPath), "utf8"));
110142
+ const raw = JSON.parse(await readFile102(path204.resolve(projectRoot, resultsPath), "utf8"));
109450
110143
  tasks = raw.tasks ?? [];
109451
110144
  model = raw.model;
109452
110145
  } catch (error) {
@@ -109477,7 +110170,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args, ladder) {
109477
110170
  let cases;
109478
110171
  let model;
109479
110172
  try {
109480
- const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, resultsPath), "utf8"));
110173
+ const raw = JSON.parse(await readFile102(path204.resolve(projectRoot, resultsPath), "utf8"));
109481
110174
  cases = raw.cases ?? [];
109482
110175
  model = raw.model;
109483
110176
  } catch (error) {
@@ -109502,7 +110195,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args, ladder) {
109502
110195
  let cases;
109503
110196
  let model;
109504
110197
  try {
109505
- const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, resultsPath), "utf8"));
110198
+ const raw = JSON.parse(await readFile102(path204.resolve(projectRoot, resultsPath), "utf8"));
109506
110199
  cases = raw.cases ?? [];
109507
110200
  model = raw.model;
109508
110201
  } catch (error) {
@@ -109527,7 +110220,7 @@ async function runSafetyContainmentLayer(projectRoot, args, ladder, caseClass) {
109527
110220
  let cases;
109528
110221
  let model;
109529
110222
  try {
109530
- const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, resultsPath), "utf8"));
110223
+ const raw = JSON.parse(await readFile102(path204.resolve(projectRoot, resultsPath), "utf8"));
109531
110224
  cases = raw.cases ?? [];
109532
110225
  model = raw.model;
109533
110226
  } catch (error) {
@@ -109589,12 +110282,12 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
109589
110282
  }
109590
110283
  const manifests = buildOracleManifestsByGold(inputs, { ladder });
109591
110284
  if (outDir) {
109592
- const resolvedOut = path202.resolve(projectRoot, outDir);
110285
+ const resolvedOut = path204.resolve(projectRoot, outDir);
109593
110286
  for (const input of inputs) {
109594
110287
  for (const named of input.golds) {
109595
110288
  const bundle = buildEvidenceBundle({ target: input.target, system: input.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
109596
- const dir = await persistEvidenceBundle(path202.join(resolvedOut, named.kind), bundle, ladder);
109597
- console.error(`bundle[${named.kind}]: ${path202.relative(projectRoot, dir)}`);
110289
+ const dir = await persistEvidenceBundle(path204.join(resolvedOut, named.kind), bundle, ladder);
110290
+ console.error(`bundle[${named.kind}]: ${path204.relative(projectRoot, dir)}`);
109598
110291
  }
109599
110292
  }
109600
110293
  }
@@ -109618,7 +110311,7 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
109618
110311
  return allValid;
109619
110312
  }
109620
110313
  async function loadCoverageMap2(projectRoot, file) {
109621
- const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
110314
+ const raw = JSON.parse(await readFile102(path204.resolve(projectRoot, file), "utf8"));
109622
110315
  return raw.coverageMap ?? {};
109623
110316
  }
109624
110317
  async function runTestingLayer(projectRoot, args, ladder) {
@@ -109655,7 +110348,7 @@ async function runTestingLayer(projectRoot, args, ladder) {
109655
110348
  return result.valid;
109656
110349
  }
109657
110350
  async function loadMemoryGoldK(projectRoot, file) {
109658
- const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
110351
+ const raw = JSON.parse(await readFile102(path204.resolve(projectRoot, file), "utf8"));
109659
110352
  return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
109660
110353
  }
109661
110354
  async function runMemoryLayer(projectRoot, args, ladder) {
@@ -109692,11 +110385,11 @@ async function runMemoryLayer(projectRoot, args, ladder) {
109692
110385
  return result.valid;
109693
110386
  }
109694
110387
  async function loadWikiGoldK(projectRoot, file) {
109695
- const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
110388
+ const raw = JSON.parse(await readFile102(path204.resolve(projectRoot, file), "utf8"));
109696
110389
  return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
109697
110390
  }
109698
110391
  async function loadWikiGroundedness(projectRoot, file) {
109699
- const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
110392
+ const raw = JSON.parse(await readFile102(path204.resolve(projectRoot, file), "utf8"));
109700
110393
  const map = new Map;
109701
110394
  for (const entry of raw.targets ?? []) {
109702
110395
  if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
@@ -109752,7 +110445,7 @@ async function runWikiLayer(projectRoot, args, ladder) {
109752
110445
  return result.valid;
109753
110446
  }
109754
110447
  async function loadGdctxFacts(projectRoot, file) {
109755
- const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
110448
+ const raw = JSON.parse(await readFile102(path204.resolve(projectRoot, file), "utf8"));
109756
110449
  const inputs = [];
109757
110450
  for (const entry of raw.inputs ?? []) {
109758
110451
  if (typeof entry.input === "string") {
@@ -109790,7 +110483,7 @@ async function collect(projectRoot, args) {
109790
110483
  process.exitCode = 1;
109791
110484
  return;
109792
110485
  }
109793
- const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, eventFile), "utf8"));
110486
+ const raw = JSON.parse(await readFile102(path204.resolve(projectRoot, eventFile), "utf8"));
109794
110487
  const events = Array.isArray(raw) ? raw : raw.events;
109795
110488
  const startedAt = optionValue(args, "--started-at") ?? events[0]?.timestamp_utc ?? new Date().toISOString();
109796
110489
  const finishedAt = optionValue(args, "--finished-at") ?? events.at(-1)?.timestamp_utc ?? startedAt;
@@ -109806,11 +110499,11 @@ async function collect(projectRoot, args) {
109806
110499
  parentRunId: optionValue(args, "--parent-run-id") ?? null
109807
110500
  });
109808
110501
  const result = await writeRunArtifacts(metricsRoot(projectRoot), record, { cwd: projectRoot });
109809
- console.log(`json: ${path202.relative(projectRoot, result.jsonPath)}`);
109810
- console.log(`markdown: ${path202.relative(projectRoot, result.markdownPath)}`);
110502
+ console.log(`json: ${path204.relative(projectRoot, result.jsonPath)}`);
110503
+ console.log(`markdown: ${path204.relative(projectRoot, result.markdownPath)}`);
109811
110504
  }
109812
110505
  function metricsRoot(projectRoot) {
109813
- return path202.join(projectRoot, ".metaproject", "data", "metrics");
110506
+ return path204.join(projectRoot, ".metaproject", "data", "metrics");
109814
110507
  }
109815
110508
  function printMetricsHelp() {
109816
110509
  console.log(`keryx metrics