@mrciphersmith/keryx 0.3.15 → 0.3.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -5275,10 +5275,14 @@ function configuredProviders(env = process.env, dir) {
5275
5275
  }
5276
5276
  async function providersCommand(args, deps = {}) {
5277
5277
  const command = args[0];
5278
- if (!command || command === "--help" || command === "-h") {
5278
+ if (command === "--help" || command === "-h") {
5279
5279
  printProvidersHelp();
5280
5280
  return;
5281
5281
  }
5282
+ if (!command) {
5283
+ await runProvidersStatus([], deps);
5284
+ return;
5285
+ }
5282
5286
  if (command === "list") {
5283
5287
  runProvidersList(args.slice(1));
5284
5288
  return;
@@ -17637,7 +17641,7 @@ var package_default;
17637
17641
  var init_package = __esm(() => {
17638
17642
  package_default = {
17639
17643
  name: "@mrciphersmith/keryx",
17640
- version: "0.3.15",
17644
+ version: "0.3.17",
17641
17645
  description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
17642
17646
  private: false,
17643
17647
  publishConfig: {
@@ -34739,6 +34743,23 @@ function tokenize(text) {
34739
34743
  function tokenSet(text) {
34740
34744
  return new Set(tokenize(text));
34741
34745
  }
34746
+ function stem(token) {
34747
+ let base = token;
34748
+ if (base.length >= 6 && base.endsWith("ies")) {
34749
+ base = `${base.slice(0, -3)}y`;
34750
+ } else {
34751
+ for (const suffix of ["ing", "edly", "ed", "es", "s"]) {
34752
+ if (base.length - suffix.length >= 3 && base.endsWith(suffix)) {
34753
+ base = base.slice(0, base.length - suffix.length);
34754
+ break;
34755
+ }
34756
+ }
34757
+ }
34758
+ if (base.length >= 4 && base.endsWith("e")) {
34759
+ base = base.slice(0, -1);
34760
+ }
34761
+ return base;
34762
+ }
34742
34763
  function jaccard(a, b) {
34743
34764
  if (a.size === 0 || b.size === 0) {
34744
34765
  return 0;
@@ -35296,7 +35317,7 @@ var init_ingest = __esm(() => {
35296
35317
  function searchEntries(entries, query, filters, config, now) {
35297
35318
  validateQuery(query);
35298
35319
  validateSearchFilters(filters, now);
35299
- const queryTokens = [...new Set(tokenize(query))];
35320
+ const queryTokens = [...new Set(tokenize(query).map(stem))];
35300
35321
  const today = now.toISOString().slice(0, 10);
35301
35322
  const filtered = entries.filter((entry) => matchesFilters(entry, filters) && classMatch(entry, filters.class) && temporalMatch(entry, filters.asOf ?? null, config, today, Boolean(filters.status)));
35302
35323
  const scored = filtered.map((entry) => scoreEntry(entry, queryTokens, filters, config, now));
@@ -35305,7 +35326,7 @@ function searchEntries(entries, query, filters, config, now) {
35305
35326
  function candidatePool(entries, query, filters, config, now, k) {
35306
35327
  validateQuery(query);
35307
35328
  validateSearchFilters(filters, now);
35308
- const queryTokens = [...new Set(tokenize(query))];
35329
+ const queryTokens = [...new Set(tokenize(query).map(stem))];
35309
35330
  const today = now.toISOString().slice(0, 10);
35310
35331
  return entries.filter((entry) => matchesFilters(entry, filters) && classMatch(entry, filters.class) && temporalMatch(entry, filters.asOf ?? null, config, today, Boolean(filters.status))).map((entry) => scoreEntry(entry, queryTokens, filters, config, now)).sort((a, b) => b.score - a.score).slice(0, Math.min(MAX_GENERAL_RESULTS, Math.max(0, k)));
35311
35332
  }
@@ -35356,8 +35377,8 @@ function temporalMatch(entry, asOf, config, today, hasStatusFilter) {
35356
35377
  }, new Date(`${today}T00:00:00.000Z`)).current;
35357
35378
  }
35358
35379
  function scoreEntry(entry, queryTokens, filters, config, now) {
35359
- const bodyTokens = tokenSet(`${entry.title} ${entry.summary} ${entry.details} ${entry.tags.join(" ")}`);
35360
- const titleTokens = tokenSet(entry.title);
35380
+ const bodyTokens = new Set([...tokenSet(`${entry.title} ${entry.summary} ${entry.details} ${entry.tags.join(" ")}`)].map(stem));
35381
+ const titleTokens = new Set([...tokenSet(entry.title)].map(stem));
35361
35382
  let hits = 0;
35362
35383
  let titleHits = 0;
35363
35384
  for (const token of queryTokens) {
@@ -36214,6 +36235,7 @@ var init_service5 = __esm(() => {
36214
36235
  init_harness_identity();
36215
36236
  init_embedding();
36216
36237
  init_types3();
36238
+ init_embedding();
36217
36239
  });
36218
36240
 
36219
36241
  // src/wiki/ask.ts
@@ -39595,6 +39617,7 @@ var init_service7 = __esm(() => {
39595
39617
  init_query();
39596
39618
  init_repomap();
39597
39619
  init_affected_report();
39620
+ init_staleness();
39598
39621
  UnknownGraphTargetError = class UnknownGraphTargetError extends Error {
39599
39622
  target;
39600
39623
  constructor(target) {
@@ -58996,7 +59019,13 @@ ${hint}
58996
59019
  }
58997
59020
  if (roundState.round < roundState.maxRounds) {
58998
59021
  roundState.round += 1;
58999
- await finishWithBudgetSummary(io, deps, history, parentRunId, { maxAttempts, toolLog }, signal);
59022
+ const wrapUp = await finishWithBudgetSummary(io, deps, history, parentRunId, { maxAttempts, toolLog }, signal);
59023
+ if (wrapUp.aborted) {
59024
+ system(`
59025
+ [stopped] Model turn interrupted by user.
59026
+ `);
59027
+ return { finishReason: "interrupted" };
59028
+ }
59000
59029
  } else {
59001
59030
  system(`
59002
59031
  [budget] Stopping tools: no progress (only repeated/exhausted tool signatures; ` + `max ${maxAttempts} attempts each). No model rounds remain for a wrap-up.
@@ -59208,6 +59237,9 @@ ${logBlock}
59208
59237
  };
59209
59238
  const request = signal === undefined ? { ...baseRequest } : { ...baseRequest, signal };
59210
59239
  const round = await streamWrapUpRound(io, deps, request, signal, system);
59240
+ if (round.aborted || signal?.aborted === true) {
59241
+ return { aborted: true };
59242
+ }
59211
59243
  if (round.thrownError !== undefined) {
59212
59244
  system(`
59213
59245
  [error] wrap-up failed: ${round.thrownError}
@@ -59226,6 +59258,7 @@ ${logBlock}
59226
59258
  system(`
59227
59259
  [budget] No wrap-up text from the model. Re-run your request, or call the ` + "needed `keryx \u2026` command directly (e.g. `keryx wiki enrich --all`).\n");
59228
59260
  }
59261
+ return { aborted: false };
59229
59262
  }
59230
59263
  async function finishWithSubmitResult(io, deps, history, parentRunId, why, signal) {
59231
59264
  const system = (text) => {
@@ -60492,7 +60525,8 @@ function computeGate2(input) {
60492
60525
  }
60493
60526
  const missingOptional = sources.filter((s) => !s.required && s.status === "missing");
60494
60527
  for (const source of missingOptional) {
60495
- reasons.push(`OPTIONAL: ${source.source} source missing`);
60528
+ const detail = source.error ? `: ${source.error}` : "";
60529
+ reasons.push(`OPTIONAL: ${source.source} source missing${detail}`);
60496
60530
  }
60497
60531
  const brokenOptional = sources.filter((s) => !s.required && s.status === "configured-but-failed");
60498
60532
  if (brokenOptional.length > 0) {
@@ -62297,6 +62331,12 @@ function errorCodeSuffix(error) {
62297
62331
  const code = safeErrorCode(error);
62298
62332
  return code ? ` (${code})` : "";
62299
62333
  }
62334
+ function missingSourceReason(sourceId) {
62335
+ if (sourceId === "tests") {
62336
+ return "no `bun` binary found (checked node_modules/.bin and PATH)";
62337
+ }
62338
+ return;
62339
+ }
62300
62340
  function safeValidationError(error) {
62301
62341
  return error !== undefined && KNOWN_VALIDATION_ERRORS.has(error) ? error : "source output format was not recognized";
62302
62342
  }
@@ -62331,7 +62371,8 @@ async function runAdapter(adapter, ctx, cfg, stamp) {
62331
62371
  };
62332
62372
  }
62333
62373
  if (status === "skipped" || status === "missing") {
62334
- return { info: { ...base, status }, findings: [] };
62374
+ const reason = missingSourceReason(adapter.id);
62375
+ return { info: { ...base, status, ...reason !== undefined ? { error: reason } : {} }, findings: [] };
62335
62376
  }
62336
62377
  let raw;
62337
62378
  try {
@@ -62345,7 +62386,7 @@ async function runAdapter(adapter, ctx, cfg, stamp) {
62345
62386
  } catch (error) {
62346
62387
  if (error instanceof NoImportError) {
62347
62388
  if (adapter.id === "tests") {
62348
- return { info: { ...base, status: "missing" }, findings: [] };
62389
+ return { info: { ...base, status }, findings: [] };
62349
62390
  }
62350
62391
  raw = await adapter.run(ctx);
62351
62392
  } else {
@@ -62710,6 +62751,7 @@ var init_service10 = __esm(() => {
62710
62751
  init_sources();
62711
62752
  init_util();
62712
62753
  init_lifecycle2();
62754
+ init_wiki_freshness();
62713
62755
  });
62714
62756
 
62715
62757
  // src/flow/machine.ts
@@ -68912,6 +68954,7 @@ async function enrichPageDeep(input) {
68912
68954
  if (input.signal?.aborted) {
68913
68955
  return { fallback: true, reason: "deep enrich cancelled", toolCalls };
68914
68956
  }
68957
+ let disposeComposed;
68915
68958
  try {
68916
68959
  const env = envWithSavedApiKeys(input.env ?? process.env);
68917
68960
  const credentialAvailable = hasCredential(input.provider, env);
@@ -68988,6 +69031,7 @@ async function enrichPageDeep(input) {
68988
69031
  let pending;
68989
69032
  const abort = new AbortController;
68990
69033
  const composed = composeAbortSignals(input.signal, abort.signal);
69034
+ disposeComposed = composed.dispose;
68991
69035
  const io = {
68992
69036
  write: (s) => {
68993
69037
  assistant += s;
@@ -69086,6 +69130,7 @@ async function enrichPageDeep(input) {
69086
69130
  }
69087
69131
  return { enriched: raw, toolCalls };
69088
69132
  } catch (cause) {
69133
+ disposeComposed?.();
69089
69134
  return { fallback: true, reason: `deep enrich failed: ${errorMessage2(cause)}`, toolCalls };
69090
69135
  }
69091
69136
  }
@@ -70718,6 +70763,7 @@ async function runSearch(args) {
70718
70763
  }
70719
70764
  const report = args.includes("--save-report") ? await getService().writeReport({ cwd: process.cwd(), search: result, filters }) : undefined;
70720
70765
  const removalTrail = result.results.length === 0 ? searchRemovals(await loadDeletionTrail(process.cwd()), query) : null;
70766
+ const semanticHint = result.results.length === 0 && !filters.semantic ? await loadEmbeddingIndex(process.cwd()) !== null ? "Zero hits in lexical mode. An embeddings index exists for this project \u2014 try `--semantic`." : "Zero hits in lexical mode. No embeddings index exists yet \u2014 build one with `keryx memory index --embeddings`, then retry with `--semantic`." : undefined;
70721
70767
  const asOfMode = Boolean(filters.asOf);
70722
70768
  const now = new Date;
70723
70769
  const historicalByPath = new Map;
@@ -70746,11 +70792,16 @@ async function runSearch(args) {
70746
70792
  };
70747
70793
  }),
70748
70794
  ...removalTrail ? { removalTrail } : {},
70795
+ ...semanticHint ? { semanticHint } : {},
70749
70796
  ...report ? { report } : {}
70750
70797
  }, null, 2));
70751
70798
  return;
70752
70799
  }
70753
70800
  console.log(renderSearchMarkdown(query, result.results).trimEnd());
70801
+ if (semanticHint) {
70802
+ console.log("");
70803
+ console.log(semanticHint);
70804
+ }
70754
70805
  if (removalTrail) {
70755
70806
  console.log("");
70756
70807
  console.log("## Removal trail");
@@ -71047,6 +71098,7 @@ var init_memory = __esm(() => {
71047
71098
  init_config4();
71048
71099
  init_reflect();
71049
71100
  init_search();
71101
+ init_service5();
71050
71102
  init_report2();
71051
71103
  init_lifecycle();
71052
71104
  init_args();
@@ -71623,7 +71675,7 @@ var init_debug_log = __esm(() => {
71623
71675
  // src/tui/debug-watcher.ts
71624
71676
  import { spawn as spawn8, spawnSync as spawnSync8 } from "child_process";
71625
71677
  import { closeSync as closeSync6, constants as constants4, openSync as openSync7, readdirSync as readdirSync11, readFileSync as readFileSync24, readlinkSync as readlinkSync3, statSync as statSync16 } from "fs";
71626
- import path316 from "path";
71678
+ import path317 from "path";
71627
71679
  function parseEpollFdinfo(text) {
71628
71680
  const out = [];
71629
71681
  for (const line of text.split(`
@@ -71812,8 +71864,8 @@ async function runDebugWatcher(argv) {
71812
71864
  process.exitCode = 2;
71813
71865
  return;
71814
71866
  }
71815
- const logFile = path316.join(dirArg, "watcher.ndjson");
71816
- const shellLog = path316.join(dirArg, "shell.ndjson");
71867
+ const logFile = path317.join(dirArg, "watcher.ndjson");
71868
+ const shellLog = path317.join(dirArg, "shell.ndjson");
71817
71869
  const log = (kind, data = {}) => {
71818
71870
  try {
71819
71871
  appendOwnerOnlyLine(logFile, JSON.stringify({ ts: new Date().toISOString(), watcher: process.pid, target: pid, kind, ...data }));
@@ -107071,6 +107123,18 @@ var HELP_GROUPS = [
107071
107123
  summary: "Grouped command help by task \u2014 keryx help [group|command]."
107072
107124
  },
107073
107125
  { kind: "slash", name: "/help", group: "Start here", summary: "Show available commands, grouped by task." },
107126
+ {
107127
+ kind: "cli",
107128
+ name: "doctor",
107129
+ group: "Start here",
107130
+ summary: "One-page health check: version, Bun floor, ripgrep, sandbox, providers, MCP, integrations, standard, worktrees, graph/wiki freshness."
107131
+ },
107132
+ {
107133
+ kind: "slash",
107134
+ name: "/doctor",
107135
+ group: "Start here",
107136
+ summary: "Same one-page health check, inside the session."
107137
+ },
107074
107138
  {
107075
107139
  kind: "cli",
107076
107140
  name: "auth",
@@ -109203,6 +109267,19 @@ var COMMAND_DESCRIPTORS = [
109203
109267
  json: false,
109204
109268
  read: true
109205
109269
  },
109270
+ {
109271
+ module: "core",
109272
+ command: "doctor",
109273
+ summary: "One-page health check: version, Bun floor, ripgrep, sandbox, providers, MCP, integrations, standard, worktrees, graph/wiki freshness.",
109274
+ intent: ["keryx doctor", "health check", "\u043F\u0440\u043E\u0432\u0435\u0440\u044C \u043E\u043A\u0440\u0443\u0436\u0435\u043D\u0438\u0435", "is keryx set up correctly", "\u0434\u0438\u0430\u0433\u043D\u043E\u0441\u0442\u0438\u043A\u0430"],
109275
+ args: [{ name: "json", type: "bool", required: false, desc: "structured {checks:[{id,status,detail,fix?}]}" }],
109276
+ json: true,
109277
+ read: false,
109278
+ sideEffects: [
109279
+ "makes an outbound HTTPS request to the npm registry as part of its version check, unless served from the local cache or failure-backoff window",
109280
+ "writes <per-user keryx config dir>/version-check.json (outside this project) as part of that same check"
109281
+ ]
109282
+ },
109206
109283
  {
109207
109284
  module: "core",
109208
109285
  command: "status",
@@ -111294,6 +111371,49 @@ async function sandboxCommand(args = [], deps = {}) {
111294
111371
  init_args();
111295
111372
  init_ui();
111296
111373
 
111374
+ // src/lib/suggest.ts
111375
+ var MAX_SUGGESTIONS = 3;
111376
+ var MAX_EDIT_DISTANCE = 2;
111377
+ function levenshtein2(a, b) {
111378
+ const rows = a.length + 1;
111379
+ const cols = b.length + 1;
111380
+ const dp = Array.from({ length: rows }, () => new Array(cols).fill(0));
111381
+ for (let i = 0;i < rows; i += 1) {
111382
+ const row = dp[i];
111383
+ if (row !== undefined)
111384
+ row[0] = i;
111385
+ }
111386
+ const first = dp[0];
111387
+ if (first !== undefined) {
111388
+ for (let j = 0;j < cols; j += 1)
111389
+ first[j] = j;
111390
+ }
111391
+ for (let i = 1;i < rows; i += 1) {
111392
+ for (let j = 1;j < cols; j += 1) {
111393
+ const same = a[i - 1] === b[j - 1];
111394
+ const up = dp[i - 1]?.[j] ?? 0;
111395
+ const left = dp[i]?.[j - 1] ?? 0;
111396
+ const diag = dp[i - 1]?.[j - 1] ?? 0;
111397
+ const row = dp[i];
111398
+ if (row !== undefined)
111399
+ row[j] = same ? diag : 1 + Math.min(up, left, diag);
111400
+ }
111401
+ }
111402
+ return dp[rows - 1]?.[cols - 1] ?? Math.max(a.length, b.length);
111403
+ }
111404
+ function suggestClosest(input, known) {
111405
+ const needle = input.trim().toLowerCase();
111406
+ if (needle.length === 0) {
111407
+ return [];
111408
+ }
111409
+ return [...new Set(known)].map((candidate) => ({ candidate, distance: levenshtein2(needle, candidate.toLowerCase()) })).filter((entry) => entry.distance <= MAX_EDIT_DISTANCE).sort((a, b) => a.distance - b.distance || a.candidate.localeCompare(b.candidate)).slice(0, MAX_SUGGESTIONS).map((entry) => entry.candidate);
111410
+ }
111411
+ function formatUnknownCommandMessage(input, known, helpCommand = "keryx --help") {
111412
+ const suggestions = suggestClosest(input, known);
111413
+ const didYouMean = suggestions.length > 0 ? ` Did you mean: ${suggestions.join(", ")}?` : "";
111414
+ return `Unknown command: ${input}.${didYouMean} Run \`${helpCommand}\` for the list.`;
111415
+ }
111416
+
111297
111417
  // src/commands/integrate.ts
111298
111418
  init_ui();
111299
111419
  import path257 from "path";
@@ -111840,12 +111960,12 @@ function loadMcpServers(options) {
111840
111960
  const home = options.home ?? (options.configDir === undefined ? undefined : NO_HOME);
111841
111961
  for (const entry of compatFiles(options.cwd, home)) {
111842
111962
  const read = readCompatFile(entry, options.cwd);
111843
- problems.push(...read.problems);
111963
+ problems.push(...read.problems.map((p) => ({ ...p, foreign: true })));
111844
111964
  for (const [name, value] of Object.entries(read.servers)) {
111845
111965
  const found = entryProblems(name, value);
111846
111966
  if (found.length > 0) {
111847
111967
  for (const message of found)
111848
- problems.push({ file: entry.file, message });
111968
+ problems.push({ file: entry.file, message, foreign: true });
111849
111969
  continue;
111850
111970
  }
111851
111971
  winner.set(name, {
@@ -114019,19 +114139,31 @@ function publicView(server, env) {
114019
114139
  function listCommand(args, deps) {
114020
114140
  const config = load(deps);
114021
114141
  const wantJson = splitAtSeparator(args).own.includes("--json");
114142
+ const ownProblems = config.problems.filter((p) => !p.foreign);
114143
+ const foreignProblems = config.problems.filter((p) => p.foreign);
114022
114144
  if (wantJson) {
114023
114145
  const env = deps.env ?? process.env;
114024
- deps.log(JSON.stringify({ servers: config.servers.map((s) => publicView(s, env)), problems: config.problems }, null, 2));
114025
- return config.problems.length > 0 ? 1 : 0;
114146
+ deps.log(JSON.stringify({
114147
+ servers: config.servers.map((s) => publicView(s, env)),
114148
+ problems: ownProblems,
114149
+ warnings: foreignProblems
114150
+ }, null, 2));
114151
+ return ownProblems.length > 0 ? 1 : 0;
114026
114152
  }
114027
- for (const problem of config.problems) {
114153
+ for (const problem of ownProblems) {
114028
114154
  deps.err(`config problem \u2014 ${problem.file}: ${problem.message}`);
114029
114155
  }
114156
+ if (foreignProblems.length > 0) {
114157
+ deps.err("warnings:");
114158
+ for (const problem of foreignProblems) {
114159
+ deps.err(` ${problem.file}: ${problem.message}`);
114160
+ }
114161
+ }
114030
114162
  if (config.servers.length === 0) {
114031
114163
  deps.log("No MCP servers configured.");
114032
114164
  deps.log(` user: ${userConfigFile(deps.configDir)}`);
114033
114165
  deps.log(` project: ${projectConfigFile(projectRootOf2(deps))}`);
114034
- return config.problems.length > 0 ? 1 : 0;
114166
+ return ownProblems.length > 0 ? 1 : 0;
114035
114167
  }
114036
114168
  const approvals = loadTrustStore(deps.configDir);
114037
114169
  let held = 0;
@@ -114050,7 +114182,7 @@ function listCommand(args, deps) {
114050
114182
  deps.log(`${held} server(s) from committed config are not started until approved \u2014 a committed config is code someone else wrote.`);
114051
114183
  deps.log("Read what it launches above, then: keryx mcp trust <name>");
114052
114184
  }
114053
- return config.problems.length > 0 ? 1 : 0;
114185
+ return ownProblems.length > 0 ? 1 : 0;
114054
114186
  }
114055
114187
  function describeTarget(server) {
114056
114188
  const raw = server.raw;
@@ -116947,8 +117079,12 @@ async function mcpCommand(args = [], cwd = process.cwd()) {
116947
117079
  await serveMcpCommand(args.slice(1), cwd);
116948
117080
  return;
116949
117081
  }
116950
- console.error(`Unknown mcp command: ${subcommand}`);
116951
- printMcpHelp();
117082
+ console.error(formatUnknownCommandMessage(subcommand ?? "", [
117083
+ ...MCP_CONSUMER_SUBCOMMANDS,
117084
+ "install",
117085
+ "uninstall",
117086
+ "serve"
117087
+ ], "keryx mcp --help"));
116952
117088
  process.exitCode = 1;
116953
117089
  }
116954
117090
  function announceRename(retired, replacement) {
@@ -135466,6 +135602,569 @@ class NextStepSuggestionGate {
135466
135602
  }
135467
135603
  }
135468
135604
 
135605
+ // src/commands/doctor.ts
135606
+ init_package();
135607
+ import { readdir as readdir53 } from "fs/promises";
135608
+ import { existsSync as existsSync48 } from "fs";
135609
+ import path302 from "path";
135610
+
135611
+ // src/lib/version-check.ts
135612
+ init_config_dir();
135613
+ init_fs();
135614
+ import path301 from "path";
135615
+ var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
135616
+ var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
135617
+ var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
135618
+ var VERSION_STRING_LIMIT_CHARS = 64;
135619
+ var REQUEST_TIMEOUT_MS = 2000;
135620
+ var SUCCESS_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
135621
+ var FAILURE_BACKOFF_MS = 15 * 60 * 1000;
135622
+ var CACHE_LOCK_TIMEOUT_MS2 = 250;
135623
+ var CACHE_LOCK_RETRY_MS2 = 10;
135624
+ var CACHE_LOCK_STALE_MS2 = 5000;
135625
+ var SYSTEM_TIMER = {
135626
+ schedule: (callback, delayMs) => setTimeout(callback, delayMs),
135627
+ cancel: (handle) => clearTimeout(handle)
135628
+ };
135629
+ function formatVersionUpdateAdvisory(result) {
135630
+ if (result.status !== "update-available")
135631
+ return;
135632
+ return `Keryx update ${result.currentVersion} \u2192 ${result.latestVersion}
135633
+ ${result.installCommand}`;
135634
+ }
135635
+ var STRICT_SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
135636
+ function parseSemVer(value) {
135637
+ if (value.length > VERSION_STRING_LIMIT_CHARS)
135638
+ return;
135639
+ const match = STRICT_SEMVER.exec(value);
135640
+ if (match === null)
135641
+ return;
135642
+ const prereleaseText = match[4];
135643
+ const prerelease = [];
135644
+ if (prereleaseText !== undefined) {
135645
+ for (const identifier of prereleaseText.split(".")) {
135646
+ if (/^\d+$/.test(identifier)) {
135647
+ if (identifier.length > 1 && identifier.startsWith("0"))
135648
+ return;
135649
+ prerelease.push(BigInt(identifier));
135650
+ } else {
135651
+ prerelease.push(identifier);
135652
+ }
135653
+ }
135654
+ }
135655
+ return {
135656
+ major: BigInt(match[1]),
135657
+ minor: BigInt(match[2]),
135658
+ patch: BigInt(match[3]),
135659
+ prerelease
135660
+ };
135661
+ }
135662
+ function compareSemVer(left, right) {
135663
+ for (const key of ["major", "minor", "patch"]) {
135664
+ if (left[key] < right[key])
135665
+ return -1;
135666
+ if (left[key] > right[key])
135667
+ return 1;
135668
+ }
135669
+ if (left.prerelease.length === 0 || right.prerelease.length === 0) {
135670
+ if (left.prerelease.length === right.prerelease.length)
135671
+ return 0;
135672
+ return left.prerelease.length === 0 ? 1 : -1;
135673
+ }
135674
+ const length = Math.max(left.prerelease.length, right.prerelease.length);
135675
+ for (let index = 0;index < length; index += 1) {
135676
+ const a = left.prerelease[index];
135677
+ const b = right.prerelease[index];
135678
+ if (a === undefined || b === undefined) {
135679
+ if (a === b)
135680
+ return 0;
135681
+ return a === undefined ? -1 : 1;
135682
+ }
135683
+ if (typeof a === "bigint" && typeof b === "bigint") {
135684
+ if (a < b)
135685
+ return -1;
135686
+ if (a > b)
135687
+ return 1;
135688
+ } else if (typeof a === "bigint") {
135689
+ return -1;
135690
+ } else if (typeof b === "bigint") {
135691
+ return 1;
135692
+ } else if (a !== b) {
135693
+ return a < b ? -1 : 1;
135694
+ }
135695
+ }
135696
+ return 0;
135697
+ }
135698
+ function parseCache(file) {
135699
+ const read = readConfigFile(file);
135700
+ if (!read.ok)
135701
+ return;
135702
+ try {
135703
+ const value = JSON.parse(read.text);
135704
+ if (value === null || typeof value !== "object" || Array.isArray(value))
135705
+ return;
135706
+ const record = value;
135707
+ const cache = {};
135708
+ if (typeof record.latestVersion === "string" && parseSemVer(record.latestVersion) !== undefined) {
135709
+ cache.latestVersion = record.latestVersion;
135710
+ }
135711
+ if (typeof record.successAt === "number" && Number.isFinite(record.successAt))
135712
+ cache.successAt = record.successAt;
135713
+ if (typeof record.failureAt === "number" && Number.isFinite(record.failureAt))
135714
+ cache.failureAt = record.failureAt;
135715
+ return cache;
135716
+ } catch {
135717
+ return;
135718
+ }
135719
+ }
135720
+ function resultFor(currentVersion, current, latestVersion, source) {
135721
+ const latest = parseSemVer(latestVersion);
135722
+ return compareSemVer(current, latest) < 0 ? { status: "update-available", currentVersion, latestVersion, installCommand: FIXED_INSTALL_COMMAND, source } : { status: "up-to-date", currentVersion, latestVersion, source };
135723
+ }
135724
+ function cancelWithoutWaiting(cancel) {
135725
+ try {
135726
+ cancel().catch(() => {});
135727
+ } catch {}
135728
+ }
135729
+ async function boundedResponseText(response) {
135730
+ const declared = response.headers.get("content-length");
135731
+ if (declared !== null && /^\d+$/.test(declared) && BigInt(declared) > BigInt(RESPONSE_BODY_LIMIT_BYTES)) {
135732
+ if (response.body !== null) {
135733
+ cancelWithoutWaiting(() => response.body.cancel());
135734
+ }
135735
+ return;
135736
+ }
135737
+ if (response.body === null)
135738
+ return "";
135739
+ const reader = response.body.getReader();
135740
+ const decoder = new TextDecoder;
135741
+ let size = 0;
135742
+ let text = "";
135743
+ try {
135744
+ for (;; ) {
135745
+ const chunk = await reader.read();
135746
+ if (chunk.done)
135747
+ break;
135748
+ size += chunk.value.byteLength;
135749
+ if (size > RESPONSE_BODY_LIMIT_BYTES) {
135750
+ cancelWithoutWaiting(() => reader.cancel());
135751
+ return;
135752
+ }
135753
+ text += decoder.decode(chunk.value, { stream: true });
135754
+ }
135755
+ return text + decoder.decode();
135756
+ } finally {
135757
+ reader.releaseLock();
135758
+ }
135759
+ }
135760
+ function unavailable(currentVersion, reason, cache) {
135761
+ return {
135762
+ status: "unavailable",
135763
+ currentVersion,
135764
+ reason,
135765
+ ...cache?.latestVersion !== undefined ? { cachedLatestVersion: cache.latestVersion } : {}
135766
+ };
135767
+ }
135768
+ function saveCache2(file, cache) {
135769
+ try {
135770
+ writeOwnerOnlyFileAtomic(file, `${JSON.stringify(cache, null, 2)}
135771
+ `);
135772
+ return true;
135773
+ } catch {
135774
+ return false;
135775
+ }
135776
+ }
135777
+ function mergeSuccessfulCache(committed, latestVersion, successAt) {
135778
+ if (committed.latestVersion === undefined || committed.successAt === undefined) {
135779
+ return { latestVersion, successAt };
135780
+ }
135781
+ if (committed.successAt > successAt)
135782
+ return committed;
135783
+ if (committed.successAt < successAt)
135784
+ return { latestVersion, successAt };
135785
+ const committedVersion = parseSemVer(committed.latestVersion);
135786
+ const candidateVersion = parseSemVer(latestVersion);
135787
+ if (committedVersion !== undefined && candidateVersion !== undefined) {
135788
+ const precedence = compareSemVer(committedVersion, candidateVersion);
135789
+ if (precedence > 0)
135790
+ return committed;
135791
+ if (precedence < 0)
135792
+ return { latestVersion, successAt };
135793
+ if (committed.latestVersion >= latestVersion)
135794
+ return committed;
135795
+ }
135796
+ return { latestVersion, successAt };
135797
+ }
135798
+ async function updateCache(file, update) {
135799
+ try {
135800
+ return await withFileLock(`${file}.lock`, async () => saveCache2(file, update(parseCache(file) ?? {})), {
135801
+ timeoutMs: CACHE_LOCK_TIMEOUT_MS2,
135802
+ retryMs: CACHE_LOCK_RETRY_MS2,
135803
+ staleMs: CACHE_LOCK_STALE_MS2
135804
+ });
135805
+ } catch {
135806
+ return false;
135807
+ }
135808
+ }
135809
+ async function checkVersion(options) {
135810
+ const current = parseSemVer(options.currentVersion);
135811
+ if (current === undefined)
135812
+ return unavailable(options.currentVersion, "invalid-current-version");
135813
+ const now = options.now ?? Date.now;
135814
+ const timestamp = now();
135815
+ const configDir = ensureKeryxConfigDir(options.cacheDir);
135816
+ const cacheFile = path301.join(configDir, "version-check.json");
135817
+ const cache = parseCache(cacheFile);
135818
+ if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
135819
+ return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
135820
+ }
135821
+ if (cache?.failureAt !== undefined && timestamp - cache.failureAt >= 0 && timestamp - cache.failureAt < FAILURE_BACKOFF_MS) {
135822
+ return unavailable(options.currentVersion, "suppressed", cache);
135823
+ }
135824
+ const fetchImpl = options.fetch ?? globalThis.fetch;
135825
+ const controller = new AbortController;
135826
+ const timer = options.timer ?? SYSTEM_TIMER;
135827
+ let timeout;
135828
+ let timeoutScheduled = false;
135829
+ let failure;
135830
+ let latestVersion;
135831
+ try {
135832
+ timeout = timer.schedule(() => controller.abort(), REQUEST_TIMEOUT_MS);
135833
+ timeoutScheduled = true;
135834
+ const response = await fetchImpl(REGISTRY_URL, {
135835
+ method: "GET",
135836
+ headers: { accept: "application/json" },
135837
+ signal: controller.signal,
135838
+ credentials: "omit"
135839
+ });
135840
+ if (!response.ok) {
135841
+ failure = "http";
135842
+ } else {
135843
+ const body = await boundedResponseText(response);
135844
+ if (body === undefined) {
135845
+ failure = "response-too-large";
135846
+ } else {
135847
+ let parsed;
135848
+ try {
135849
+ parsed = JSON.parse(body);
135850
+ } catch {
135851
+ parsed = undefined;
135852
+ }
135853
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
135854
+ failure = "malformed-response";
135855
+ } else {
135856
+ const record = parsed;
135857
+ if (record.name !== "@mrciphersmith/keryx" || typeof record.version !== "string") {
135858
+ failure = "malformed-response";
135859
+ } else if (parseSemVer(record.version) === undefined) {
135860
+ failure = "invalid-latest-version";
135861
+ } else {
135862
+ latestVersion = record.version;
135863
+ }
135864
+ }
135865
+ }
135866
+ }
135867
+ } catch {
135868
+ failure = controller.signal.aborted ? "timeout" : "network";
135869
+ } finally {
135870
+ if (timeoutScheduled) {
135871
+ try {
135872
+ timer.cancel(timeout);
135873
+ } catch {}
135874
+ }
135875
+ }
135876
+ if (latestVersion === undefined) {
135877
+ await updateCache(cacheFile, (committed) => ({ ...committed, failureAt: timestamp }));
135878
+ return unavailable(options.currentVersion, failure ?? "network", cache);
135879
+ }
135880
+ await updateCache(cacheFile, (committed) => mergeSuccessfulCache(committed, latestVersion, timestamp));
135881
+ return resultFor(options.currentVersion, current, latestVersion, "registry");
135882
+ }
135883
+
135884
+ // src/commands/doctor.ts
135885
+ init_contained_path();
135886
+ init_providers();
135887
+ init_service();
135888
+ init_service7();
135889
+ init_service10();
135890
+ async function registeredWorktrees(cwd) {
135891
+ if (!Bun.which("git"))
135892
+ return new Set;
135893
+ try {
135894
+ const proc = Bun.spawn(["git", "worktree", "list", "--porcelain"], { cwd, stdout: "pipe", stderr: "ignore" });
135895
+ const [stdout, code] = await Promise.all([new Response(proc.stdout).text(), proc.exited]);
135896
+ if (code !== 0)
135897
+ return new Set;
135898
+ const paths = new Set;
135899
+ for (const line of stdout.split(`
135900
+ `)) {
135901
+ if (line.startsWith("worktree ")) {
135902
+ paths.add(path302.resolve(line.slice("worktree ".length).trim()));
135903
+ }
135904
+ }
135905
+ return paths;
135906
+ } catch {
135907
+ return new Set;
135908
+ }
135909
+ }
135910
+ async function checkWorktrees(cwd) {
135911
+ const worktreesDir = path302.join(cwd, ".claude", "worktrees");
135912
+ let entries;
135913
+ try {
135914
+ entries = (await readdir53(worktreesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
135915
+ } catch {
135916
+ return { id: "worktrees", status: "ok", detail: "no .claude/worktrees directory" };
135917
+ }
135918
+ if (entries.length === 0) {
135919
+ return { id: "worktrees", status: "ok", detail: ".claude/worktrees is empty" };
135920
+ }
135921
+ const registered = await registeredWorktrees(cwd);
135922
+ const stale = entries.filter((name) => {
135923
+ const full = path302.resolve(worktreesDir, name);
135924
+ return !existsSync48(path302.join(full, ".git")) || !registered.has(full);
135925
+ });
135926
+ if (stale.length === 0) {
135927
+ return {
135928
+ id: "worktrees",
135929
+ status: "ok",
135930
+ detail: `${entries.length} worktree(s) under .claude/worktrees, all registered`
135931
+ };
135932
+ }
135933
+ return {
135934
+ id: "worktrees",
135935
+ status: "warn",
135936
+ detail: `${stale.length} stale entr${stale.length === 1 ? "y" : "ies"} under .claude/worktrees: ${stale.join(", ")}`,
135937
+ fix: "git worktree prune, then remove the listed directories under .claude/worktrees by hand"
135938
+ };
135939
+ }
135940
+ function meetsBunFloor(current, floor) {
135941
+ const floorMatch = /^>=\s*(\d+)\.(\d+)\.(\d+)/.exec(floor);
135942
+ const currentMatch = /^(\d+)\.(\d+)\.(\d+)/.exec(current);
135943
+ if (!floorMatch || !currentMatch)
135944
+ return true;
135945
+ for (let i = 1;i <= 3; i += 1) {
135946
+ const f = Number(floorMatch[i]);
135947
+ const c = Number(currentMatch[i]);
135948
+ if (c !== f)
135949
+ return c > f;
135950
+ }
135951
+ return true;
135952
+ }
135953
+ function checkBun(bunFloorOverride) {
135954
+ const floor = bunFloorOverride ?? package_default.engines?.bun;
135955
+ const current = Bun.version;
135956
+ if (floor === undefined || meetsBunFloor(current, floor)) {
135957
+ return { id: "bun", status: "ok", detail: `Bun ${current}${floor ? ` (floor ${floor})` : ""}` };
135958
+ }
135959
+ return {
135960
+ id: "bun",
135961
+ status: "fail",
135962
+ detail: `Bun ${current} is below the documented floor ${floor}`,
135963
+ fix: "upgrade Bun: https://bun.sh/docs/installation"
135964
+ };
135965
+ }
135966
+ function checkRipgrep() {
135967
+ const rg = Bun.which("rg");
135968
+ return rg ? { id: "ripgrep", status: "ok", detail: `rg at ${rg}` } : {
135969
+ id: "ripgrep",
135970
+ status: "warn",
135971
+ detail: "ripgrep (rg) not found on PATH \u2014 keryx ctx rg and search_code are unavailable",
135972
+ fix: "brew install ripgrep (macOS) or apt install ripgrep (Debian/Ubuntu)"
135973
+ };
135974
+ }
135975
+ async function checkVersionAvailability() {
135976
+ const result = await checkVersion({ currentVersion: package_default.version });
135977
+ if (result.status === "update-available") {
135978
+ return {
135979
+ id: "version",
135980
+ status: "warn",
135981
+ detail: `keryx ${result.currentVersion} \u2192 ${result.latestVersion} is available`,
135982
+ fix: result.installCommand
135983
+ };
135984
+ }
135985
+ if (result.status === "up-to-date") {
135986
+ return { id: "version", status: "ok", detail: `keryx ${result.currentVersion} is up to date` };
135987
+ }
135988
+ return { id: "version", status: "ok", detail: `keryx ${result.currentVersion}; update check unavailable (${result.reason})` };
135989
+ }
135990
+ function checkSandbox() {
135991
+ const report = buildSandboxReport();
135992
+ if (report.launcher.available) {
135993
+ return { id: "sandbox", status: "ok", detail: `${report.launcherName ?? "sandbox launcher"} available` };
135994
+ }
135995
+ const known = report.launcherName !== undefined;
135996
+ return {
135997
+ id: "sandbox",
135998
+ status: "warn",
135999
+ detail: known ? `${report.launcherName} not installed on ${report.platform} \u2014 OS-sandboxed shell exec is unavailable` : `no OS sandbox launcher support on ${report.platform}`,
136000
+ fix: known ? "keryx sandbox status for what installing it would unlock" : "keryx sandbox status for details"
136001
+ };
136002
+ }
136003
+ function checkProviders(env) {
136004
+ const providers = configuredProviders(env);
136005
+ if (providers.length === 0) {
136006
+ return {
136007
+ id: "providers",
136008
+ status: "warn",
136009
+ detail: "no provider credentials configured",
136010
+ fix: "keryx auth login <provider>, or set a provider API key"
136011
+ };
136012
+ }
136013
+ return {
136014
+ id: "providers",
136015
+ status: "ok",
136016
+ detail: `${providers.length} provider(s) configured: ${providers.map((p) => p.name).join(", ")}`
136017
+ };
136018
+ }
136019
+ function checkMcp(cwd) {
136020
+ const projectRoot = resolveProjectRoot(cwd);
136021
+ const config = loadMcpServers({ cwd, gitRoot: projectRoot });
136022
+ const ownProblems = config.problems.filter((p) => !p.foreign);
136023
+ if (ownProblems.length > 0) {
136024
+ return {
136025
+ id: "mcp",
136026
+ status: "fail",
136027
+ detail: `${ownProblems.length} config problem(s) in keryx's own MCP config: ${ownProblems.map((p) => p.message).join("; ")}`,
136028
+ fix: "keryx mcp list for details"
136029
+ };
136030
+ }
136031
+ const approvals = loadTrustStore();
136032
+ const held = config.servers.filter((server) => server.enabled && requiresApproval(server, approvals));
136033
+ if (held.length > 0) {
136034
+ return {
136035
+ id: "mcp",
136036
+ status: "warn",
136037
+ detail: `${held.length} server(s) from committed config awaiting approval: ${held.map((s) => s.name).join(", ")}`,
136038
+ fix: "keryx mcp trust <name>, after reading what it launches"
136039
+ };
136040
+ }
136041
+ return {
136042
+ id: "mcp",
136043
+ status: "ok",
136044
+ detail: `${config.servers.length} server(s) configured; run \`keryx mcp doctor\` to also check connectivity`
136045
+ };
136046
+ }
136047
+ async function checkIntegrations(cwd) {
136048
+ const results = await Promise.all(harnessAdapterIds().map(async (id) => {
136049
+ try {
136050
+ return await doctorIntegration(cwd, id);
136051
+ } catch {
136052
+ return { runtimeId: id, surfaces: [], problems: [], ok: true };
136053
+ }
136054
+ }));
136055
+ const drifted = results.filter((r) => !r.ok);
136056
+ if (drifted.length === 0) {
136057
+ return { id: "integrations", status: "ok", detail: "no drift in any installed integration" };
136058
+ }
136059
+ return {
136060
+ id: "integrations",
136061
+ status: "warn",
136062
+ detail: `drift in: ${drifted.map((r) => r.runtimeId).join(", ")}`,
136063
+ fix: "keryx integrations doctor --runtime all"
136064
+ };
136065
+ }
136066
+ async function checkStandard(cwd) {
136067
+ const result = await runDoctor(cwd);
136068
+ if (result.errors.length > 0) {
136069
+ return {
136070
+ id: "standard",
136071
+ status: "fail",
136072
+ detail: `${result.errors.length} error(s): ${result.errors.map((e) => e.message).join("; ")}`,
136073
+ fix: "keryx standard doctor"
136074
+ };
136075
+ }
136076
+ if (result.warnings.length > 0) {
136077
+ return {
136078
+ id: "standard",
136079
+ status: "warn",
136080
+ detail: `${result.warnings.length} warning(s): ${result.warnings.map((w) => w.message).join("; ")}`,
136081
+ fix: "keryx standard doctor"
136082
+ };
136083
+ }
136084
+ return { id: "standard", status: "ok", detail: "workspace is Metaproject Standard compliant" };
136085
+ }
136086
+ async function checkGraphFreshness(cwd) {
136087
+ const result = await checkGraphStaleness(cwd);
136088
+ if (result.status === "fresh") {
136089
+ return { id: "graph-freshness", status: "ok", detail: "code graph is fresh" };
136090
+ }
136091
+ return {
136092
+ id: "graph-freshness",
136093
+ status: "warn",
136094
+ detail: result.reasons.join("; ") || `code graph is ${result.status}`,
136095
+ fix: "keryx gdgraph build"
136096
+ };
136097
+ }
136098
+ async function checkWikiFreshness(cwd) {
136099
+ const metric = await readWikiFreshnessMetric(cwd);
136100
+ if (metric.status === "measured") {
136101
+ const pct = metric.ratio !== undefined ? `${Math.round(metric.ratio * 100)}%` : "an unmeasured share";
136102
+ return {
136103
+ id: "wiki-freshness",
136104
+ status: "ok",
136105
+ detail: `${pct} of ${metric.pagesTotal ?? "?"} wiki page(s) fresh`
136106
+ };
136107
+ }
136108
+ return {
136109
+ id: "wiki-freshness",
136110
+ status: "warn",
136111
+ detail: metric.reason ?? `wiki freshness: ${metric.status}`,
136112
+ fix: "keryx wiki freshness"
136113
+ };
136114
+ }
136115
+ async function buildDoctorReport(cwd, env = process.env, overrides = {}) {
136116
+ const checks = await Promise.all([
136117
+ checkVersionAvailability(),
136118
+ Promise.resolve(checkBun(overrides.bunFloor)),
136119
+ Promise.resolve(checkRipgrep()),
136120
+ Promise.resolve(checkSandbox()),
136121
+ Promise.resolve(checkProviders(env)),
136122
+ Promise.resolve(checkMcp(cwd)),
136123
+ checkIntegrations(cwd),
136124
+ checkStandard(cwd),
136125
+ checkWorktrees(cwd),
136126
+ checkGraphFreshness(cwd),
136127
+ checkWikiFreshness(cwd)
136128
+ ]);
136129
+ return { checks };
136130
+ }
136131
+ function doctorFailed(report) {
136132
+ return report.checks.some((check) => check.status === "fail");
136133
+ }
136134
+ function formatDoctorReport2(report) {
136135
+ const symbolFor = { ok: "\u2713", warn: "!", fail: "\u2717" };
136136
+ const lines = ["keryx doctor", ""];
136137
+ for (const check of report.checks) {
136138
+ lines.push(` ${symbolFor[check.status]} ${check.id}: ${check.detail}`);
136139
+ if (check.fix) {
136140
+ lines.push(` fix: ${check.fix}`);
136141
+ }
136142
+ }
136143
+ return lines.join(`
136144
+ `);
136145
+ }
136146
+ async function doctorCommand2(args = [], cwd = process.cwd(), overrides = {}) {
136147
+ if (args.includes("--help") || args.includes("-h")) {
136148
+ console.log(`keryx doctor [--json]
136149
+
136150
+ One page: keryx's own version and update availability, the Bun floor,
136151
+ ripgrep on PATH, the OS sandbox launcher, providers with a credential
136152
+ present (names only), MCP servers and their trust state, integrations
136153
+ drift, Metaproject Standard warnings, stale .claude/worktrees entries, and
136154
+ graph/wiki freshness. Exits 0 unless something is a "fail".`);
136155
+ return;
136156
+ }
136157
+ const report = await buildDoctorReport(cwd, process.env, overrides);
136158
+ if (args.includes("--json")) {
136159
+ console.log(JSON.stringify(report, null, 2));
136160
+ } else {
136161
+ console.log(formatDoctorReport2(report));
136162
+ }
136163
+ if (doctorFailed(report)) {
136164
+ process.exitCode = 1;
136165
+ }
136166
+ }
136167
+
135469
136168
  // src/tui/tui-shell.ts
135470
136169
  init_slate_lifecycle();
135471
136170
  init_slate();
@@ -135478,7 +136177,7 @@ init_workspace_service();
135478
136177
  init_service11();
135479
136178
  init_store3();
135480
136179
  init_fs();
135481
- import path301 from "path";
136180
+ import path303 from "path";
135482
136181
  import { readFile as readFile157 } from "fs/promises";
135483
136182
  var POSITIVE_INTEGER = /^[1-9][0-9]*$/;
135484
136183
  var DEFAULT_AUTO_GOAL_ROUNDS = 8;
@@ -135554,7 +136253,7 @@ function getFlowService() {
135554
136253
  async function autoProvisionFlow(cwd, goalText) {
135555
136254
  const service = getFlowService();
135556
136255
  const result = await service.init({ cwd, title: goalText });
135557
- const acFile = path301.join(cwd, result.dir, "acceptance-criteria.md");
136256
+ const acFile = path303.join(cwd, result.dir, "acceptance-criteria.md");
135558
136257
  await writeFileAtomic(acFile, [
135559
136258
  "# Acceptance Criteria",
135560
136259
  "",
@@ -137242,8 +137941,8 @@ init_store4();
137242
137941
  init_proposal_lifecycle();
137243
137942
  init_workspace_service();
137244
137943
  import { randomUUID as randomUUID34 } from "crypto";
137245
- import { readdir as readdir53, stat as stat30 } from "fs/promises";
137246
- import path302 from "path";
137944
+ import { readdir as readdir54, stat as stat30 } from "fs/promises";
137945
+ import path304 from "path";
137247
137946
 
137248
137947
  // src/sac/lifecycle-flag.ts
137249
137948
  init_store();
@@ -137382,7 +138081,7 @@ async function readExternalUnboundCandidates(cwd) {
137382
138081
  const extDir = externalSlatesDir(cwd);
137383
138082
  let externalIds;
137384
138083
  try {
137385
- const entries = await readdir53(extDir);
138084
+ const entries = await readdir54(extDir);
137386
138085
  externalIds = entries.filter((name) => name.endsWith(".json")).map((name) => name.slice(0, -".json".length));
137387
138086
  } catch {
137388
138087
  return [];
@@ -137413,7 +138112,7 @@ async function readExternalUnboundCandidates(cwd) {
137413
138112
  }
137414
138113
  return groups;
137415
138114
  }, []));
137416
- const metaPath = path302.join(extDir, `${id}.json`);
138115
+ const metaPath = path304.join(extDir, `${id}.json`);
137417
138116
  if (await pathExists(unboundDismissedReceiptPath(metaPath)))
137418
138117
  continue;
137419
138118
  candidates.push({
@@ -137427,16 +138126,16 @@ async function readExternalUnboundCandidates(cwd) {
137427
138126
  return candidates;
137428
138127
  }
137429
138128
  async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
137430
- const evidenceDir = path302.join(externalSlatesDir(cwd), externalSessionId);
138129
+ const evidenceDir = path304.join(externalSlatesDir(cwd), externalSessionId);
137431
138130
  let entries;
137432
138131
  try {
137433
- entries = (await readdir53(evidenceDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
138132
+ entries = (await readdir54(evidenceDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
137434
138133
  } catch {
137435
138134
  return;
137436
138135
  }
137437
138136
  entries.sort();
137438
138137
  for (let i = entries.length - 1;i >= 0; i--) {
137439
- const evidencePath = path302.join(evidenceDir, entries[i]);
138138
+ const evidencePath = path304.join(evidenceDir, entries[i]);
137440
138139
  if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
137441
138140
  continue;
137442
138141
  const result = readConfigFile(evidencePath);
@@ -137479,23 +138178,23 @@ var SESSION_ATTRIBUTION_SLACK_MS = 5 * 60000;
137479
138178
  var MTIME_CLOCK_SKEW_TOLERANCE_MS = 5000;
137480
138179
  async function collectReceiptTargets(cwd, owner) {
137481
138180
  const targets = new Set;
137482
- const workspacesDir = path302.join(cwd, ".metaproject", "workspaces");
138181
+ const workspacesDir = path304.join(cwd, ".metaproject", "workspaces");
137483
138182
  let workspaceIds;
137484
138183
  try {
137485
- workspaceIds = (await readdir53(workspacesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
138184
+ workspaceIds = (await readdir54(workspacesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
137486
138185
  } catch {
137487
138186
  return targets;
137488
138187
  }
137489
138188
  for (const workspaceId of workspaceIds) {
137490
- const receiptsDir = path302.join(workspacesDir, workspaceId, `${owner}-write-receipts`);
138189
+ const receiptsDir = path304.join(workspacesDir, workspaceId, `${owner}-write-receipts`);
137491
138190
  let files;
137492
138191
  try {
137493
- files = (await readdir53(receiptsDir)).filter((name) => name.endsWith(".json"));
138192
+ files = (await readdir54(receiptsDir)).filter((name) => name.endsWith(".json"));
137494
138193
  } catch {
137495
138194
  continue;
137496
138195
  }
137497
138196
  for (const file of files) {
137498
- const result = readConfigFile(path302.join(receiptsDir, file));
138197
+ const result = readConfigFile(path304.join(receiptsDir, file));
137499
138198
  if (!result.ok)
137500
138199
  continue;
137501
138200
  try {
@@ -137532,13 +138231,13 @@ async function attributeToSession(absolutePath, sessionsNewestFirst) {
137532
138231
  async function findSkillFiles(dir) {
137533
138232
  let entries;
137534
138233
  try {
137535
- entries = await readdir53(dir, { withFileTypes: true });
138234
+ entries = await readdir54(dir, { withFileTypes: true });
137536
138235
  } catch {
137537
138236
  return [];
137538
138237
  }
137539
138238
  const found = [];
137540
138239
  for (const entry of entries) {
137541
- const full = path302.join(dir, entry.name);
138240
+ const full = path304.join(dir, entry.name);
137542
138241
  if (entry.isDirectory()) {
137543
138242
  found.push(...await findSkillFiles(full));
137544
138243
  } else if (entry.isFile() && entry.name === "SKILL.md") {
@@ -137597,10 +138296,10 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
137597
138296
  changedAt: attribution.changedAt
137598
138297
  });
137599
138298
  }
137600
- const sacSkillsDir = path302.join(cwd, ".metaproject", "project-skills", "sac");
138299
+ const sacSkillsDir = path304.join(cwd, ".metaproject", "project-skills", "sac");
137601
138300
  const skillFiles = await findSkillFiles(sacSkillsDir);
137602
138301
  for (const absolutePath of skillFiles) {
137603
- const targetRef = path302.relative(path302.join(cwd, ".metaproject"), absolutePath).split(path302.sep).join("/");
138302
+ const targetRef = path304.relative(path304.join(cwd, ".metaproject"), absolutePath).split(path304.sep).join("/");
137604
138303
  if (skillReceipts.has(targetRef))
137605
138304
  continue;
137606
138305
  const attribution = await attributeToSession(absolutePath, scoped);
@@ -137618,12 +138317,12 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
137618
138317
  return items;
137619
138318
  }
137620
138319
  async function isSlateEngaged(dir) {
137621
- if (await pathExists(path302.join(dir, "slate.json")))
138320
+ if (await pathExists(path304.join(dir, "slate.json")))
137622
138321
  return true;
137623
- if (await pathExists(path302.join(dir, "terminal-state.json")))
138322
+ if (await pathExists(path304.join(dir, "terminal-state.json")))
137624
138323
  return true;
137625
138324
  try {
137626
- const entries = await readdir53(path302.join(dir, "slate-archive"));
138325
+ const entries = await readdir54(path304.join(dir, "slate-archive"));
137627
138326
  return entries.length > 0;
137628
138327
  } catch {
137629
138328
  return false;
@@ -137648,7 +138347,7 @@ async function safeReadSlate(dir) {
137648
138347
  }
137649
138348
  }
137650
138349
  async function readTerminalState(dir) {
137651
- const result = readConfigFile(path302.join(dir, "terminal-state.json"));
138350
+ const result = readConfigFile(path304.join(dir, "terminal-state.json"));
137652
138351
  if (!result.ok) {
137653
138352
  return;
137654
138353
  }
@@ -137672,17 +138371,17 @@ async function resolveUnboundCandidateTarget(cwd, target) {
137672
138371
  if (session === undefined) {
137673
138372
  throw new Error(`No session or evidence path matches "${target}"`);
137674
138373
  }
137675
- const archiveDir = path302.join(sessionDir(session.projectPath, session.id), "slate-archive");
138374
+ const archiveDir = path304.join(sessionDir(session.projectPath, session.id), "slate-archive");
137676
138375
  let entries;
137677
138376
  try {
137678
- entries = (await readdir53(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
138377
+ entries = (await readdir54(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
137679
138378
  } catch {
137680
138379
  throw new Error(`No unbound-candidate artifacts for session ${session.id}`);
137681
138380
  }
137682
138381
  if (entries.length === 0)
137683
138382
  throw new Error(`No unbound-candidate artifacts for session ${session.id}`);
137684
138383
  entries.sort();
137685
- return path302.join(archiveDir, entries[entries.length - 1]);
138384
+ return path304.join(archiveDir, entries[entries.length - 1]);
137686
138385
  }
137687
138386
  async function dismissUnboundByTarget(cwd, target, reason) {
137688
138387
  const evidencePath = await resolveUnboundCandidateTarget(cwd, target);
@@ -137708,16 +138407,16 @@ async function dismissUnboundCandidate(candidatePath, reason) {
137708
138407
  return { removed: candidatePath, receipt: receiptPath };
137709
138408
  }
137710
138409
  async function readNewestUnboundCandidate(dir) {
137711
- const archiveDir = path302.join(dir, "slate-archive");
138410
+ const archiveDir = path304.join(dir, "slate-archive");
137712
138411
  let entries;
137713
138412
  try {
137714
- entries = (await readdir53(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
138413
+ entries = (await readdir54(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
137715
138414
  } catch {
137716
138415
  return;
137717
138416
  }
137718
138417
  entries.sort();
137719
138418
  for (let i = entries.length - 1;i >= 0; i--) {
137720
- const evidencePath = path302.join(archiveDir, entries[i]);
138419
+ const evidencePath = path304.join(archiveDir, entries[i]);
137721
138420
  if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
137722
138421
  continue;
137723
138422
  const result = readConfigFile(evidencePath);
@@ -137747,16 +138446,16 @@ function isFailureOutcome2(g) {
137747
138446
  return g.outcome === "error" || g.outcome === "no_credential" || g.outcome === "conflict";
137748
138447
  }
137749
138448
  async function readNewestWrapUpOutcome(dir) {
137750
- const archiveDir = path302.join(dir, "slate-archive");
138449
+ const archiveDir = path304.join(dir, "slate-archive");
137751
138450
  let entries;
137752
138451
  try {
137753
- entries = (await readdir53(archiveDir)).filter((name) => name.endsWith("-wrap-up-outcome.json"));
138452
+ entries = (await readdir54(archiveDir)).filter((name) => name.endsWith("-wrap-up-outcome.json"));
137754
138453
  } catch {
137755
138454
  return;
137756
138455
  }
137757
138456
  entries.sort();
137758
138457
  for (let i = entries.length - 1;i >= 0; i--) {
137759
- const evidencePath = path302.join(archiveDir, entries[i]);
138458
+ const evidencePath = path304.join(archiveDir, entries[i]);
137760
138459
  const result = readConfigFile(evidencePath);
137761
138460
  if (!result.ok) {
137762
138461
  continue;
@@ -138363,6 +139062,11 @@ var AGENT_SLASH_COMMANDS = [
138363
139062
  modes: BOTH
138364
139063
  },
138365
139064
  { name: "/flows", description: "Browse project flows and inspect one", modes: BOTH },
139065
+ {
139066
+ name: "/doctor",
139067
+ description: "One-page health check: version, sandbox, providers, MCP, integrations, freshness",
139068
+ modes: BOTH
139069
+ },
138366
139070
  {
138367
139071
  name: "/ac",
138368
139072
  description: "Check the active flow's acceptance criteria (advisory, cached) \u2014 press `c` in /flows to re-check",
@@ -138705,7 +139409,7 @@ function classifyBusyDispatch(params) {
138705
139409
  init_fs();
138706
139410
  init_json();
138707
139411
  import { mkdir as mkdir67 } from "fs/promises";
138708
- import path304 from "path";
139412
+ import path306 from "path";
138709
139413
 
138710
139414
  // src/governance/aggregate.ts
138711
139415
  init_store3();
@@ -138713,17 +139417,17 @@ init_store3();
138713
139417
  // src/governance/spend.ts
138714
139418
  init_fs();
138715
139419
  init_store3();
138716
- import { readdir as readdir54, readFile as readFile158 } from "fs/promises";
138717
- import path303 from "path";
139420
+ import { readdir as readdir55, readFile as readFile158 } from "fs/promises";
139421
+ import path305 from "path";
138718
139422
  async function readFlowReviewManifests(cwd, flowDir) {
138719
- const reviewsDir = path303.join(flowsRoot(cwd), flowDir, "reviews");
139423
+ const reviewsDir = path305.join(flowsRoot(cwd), flowDir, "reviews");
138720
139424
  if (!await pathExists(reviewsDir)) {
138721
139425
  return [];
138722
139426
  }
138723
- const entries = (await readdir54(reviewsDir, { withFileTypes: true })).filter((entry) => entry.isDirectory());
139427
+ const entries = (await readdir55(reviewsDir, { withFileTypes: true })).filter((entry) => entry.isDirectory());
138724
139428
  const manifests = [];
138725
139429
  for (const entry of entries) {
138726
- const manifestPath = path303.join(reviewsDir, entry.name, "manifest.json");
139430
+ const manifestPath = path305.join(reviewsDir, entry.name, "manifest.json");
138727
139431
  if (!await pathExists(manifestPath)) {
138728
139432
  continue;
138729
139433
  }
@@ -138970,11 +139674,11 @@ async function collectProjectGovernance(root, filters, displayName) {
138970
139674
  // src/governance/report.ts
138971
139675
  var GOVERNANCE_SCHEMA_VERSION = 1;
138972
139676
  function governanceDataRoot(cwd) {
138973
- return path304.join(cwd, ".metaproject", "data", "governance");
139677
+ return path306.join(cwd, ".metaproject", "data", "governance");
138974
139678
  }
138975
139679
  function artifactPaths(cwd) {
138976
- const artifacts = path304.join(governanceDataRoot(cwd), "artifacts");
138977
- return { markdown: path304.join(artifacts, "latest.md"), json: path304.join(artifacts, "latest.json") };
139680
+ const artifacts = path306.join(governanceDataRoot(cwd), "artifacts");
139681
+ return { markdown: path306.join(artifacts, "latest.md"), json: path306.join(artifacts, "latest.json") };
138978
139682
  }
138979
139683
  async function buildGovernanceReport(options) {
138980
139684
  const { cwd, filters, allProjects, now } = options;
@@ -138982,9 +139686,9 @@ async function buildGovernanceReport(options) {
138982
139686
  if (allProjects) {
138983
139687
  const warnings = [];
138984
139688
  const registered = listProjects(options.registryDir, (message) => warnings.push(message));
138985
- const seen = new Set([path304.resolve(cwd)]);
139689
+ const seen = new Set([path306.resolve(cwd)]);
138986
139690
  for (const entry of registered) {
138987
- const resolved = path304.resolve(entry.path);
139691
+ const resolved = path306.resolve(entry.path);
138988
139692
  if (seen.has(resolved)) {
138989
139693
  continue;
138990
139694
  }
@@ -139137,7 +139841,7 @@ function renderTriggerSpendLine(spend) {
139137
139841
  }
139138
139842
  async function writeGovernanceArtifacts(cwd, report) {
139139
139843
  const { markdown, json } = artifactPaths(cwd);
139140
- await mkdir67(path304.dirname(markdown), { recursive: true });
139844
+ await mkdir67(path306.dirname(markdown), { recursive: true });
139141
139845
  await writeFileAtomic(markdown, renderGovernanceMarkdown(report));
139142
139846
  await writeFileAtomic(json, `${JSON.stringify(report, null, 2)}
139143
139847
  `);
@@ -139342,7 +140046,7 @@ function mountGovernancePanel(otui, renderer, parent, options) {
139342
140046
 
139343
140047
  // src/tui/governance-inspector.ts
139344
140048
  import { readFile as readFile159 } from "fs/promises";
139345
- import path305 from "path";
140049
+ import path307 from "path";
139346
140050
  var GOVERNANCE_COMMAND = "/governance";
139347
140051
  var GOVERNANCE_FOOTER = [
139348
140052
  { key: "\u2191/\u2193 j/k", label: "scroll" },
@@ -139353,7 +140057,7 @@ var GOVERNANCE_FOOTER = [
139353
140057
  var HEADER_TEXT_ROWS = 2;
139354
140058
  var GOVERNANCE_HEADER_ROWS = HEADER_TEXT_ROWS + 1;
139355
140059
  function governanceMarkdownPath(cwd) {
139356
- return path305.join(governanceDataRoot(cwd), "artifacts", "latest.md");
140060
+ return path307.join(governanceDataRoot(cwd), "artifacts", "latest.md");
139357
140061
  }
139358
140062
  async function defaultReadMarkdown(cwd) {
139359
140063
  try {
@@ -139498,7 +140202,7 @@ function openGovernanceReport(otui, chrome, options) {
139498
140202
 
139499
140203
  // src/tui/trigger-ledger.ts
139500
140204
  import { stat as fsStat2 } from "fs/promises";
139501
- import path306 from "path";
140205
+ import path308 from "path";
139502
140206
  var DEFAULT_RECORDS_PER_ENTRY = 20;
139503
140207
  function isScheduledEntry(entry) {
139504
140208
  return entry.fire.kind === "schedule";
@@ -139557,7 +140261,7 @@ function triggerLedgerPaths(root) {
139557
140261
  return {
139558
140262
  runs: triggerRunsPath(root),
139559
140263
  triggers: triggersConfigPath(root),
139560
- governance: path306.join(governanceDataRoot(root), "artifacts", "latest.json"),
140264
+ governance: path308.join(governanceDataRoot(root), "artifacts", "latest.json"),
139561
140265
  schedules: scheduleStorePath(root)
139562
140266
  };
139563
140267
  }
@@ -140130,7 +140834,7 @@ function openTriggers(otui, chrome, options) {
140130
140834
  // src/tui/trigger-run-now.ts
140131
140835
  import { spawn as nodeSpawn2 } from "child_process";
140132
140836
  import { closeSync as closeSync5, constants as constants3, fstatSync as fstatSync3, lstatSync as lstatSync6, mkdirSync as mkdirSync7, openSync as openSync5, readdirSync as readdirSync10, readSync as readSync3, unlinkSync as unlinkSync4, writeSync } from "fs";
140133
- import path307 from "path";
140837
+ import path309 from "path";
140134
140838
  var OUTPUT_TAIL_CHARS = 4000;
140135
140839
  var defaultSpawn = (command, args, options) => nodeSpawn2(command, [...args], { cwd: options.cwd, env: options.env, detached: true, stdio: [...options.stdio] });
140136
140840
  function triggerRunArgv(name, invocation = resolveKeryxInvocation(), options = {}) {
@@ -140145,7 +140849,7 @@ function runNowChildEnv(env) {
140145
140849
  return out;
140146
140850
  }
140147
140851
  function runNowLogDir(root) {
140148
- return path307.join(triggerDataDir(root), "run-now");
140852
+ return path309.join(triggerDataDir(root), "run-now");
140149
140853
  }
140150
140854
  var RUN_NOW_LOGS_KEPT = 5;
140151
140855
  var OUTPUT_TAIL_BYTES = 16 * 1024;
@@ -140153,13 +140857,13 @@ function stampOf(iso) {
140153
140857
  return iso.replace(/[:.]/g, "-");
140154
140858
  }
140155
140859
  function runNowLogPath(root, name, startedAt) {
140156
- return path307.join(runNowLogDir(root), `${name}-${stampOf(startedAt)}.log`);
140860
+ return path309.join(runNowLogDir(root), `${name}-${stampOf(startedAt)}.log`);
140157
140861
  }
140158
140862
  function isCode(error, code) {
140159
140863
  return typeof error === "object" && error !== null && "code" in error && error.code === code;
140160
140864
  }
140161
140865
  function prepareRunNowLogDir(root) {
140162
- mkdirSync7(path307.join(root, ".metaproject", "data"), { recursive: true });
140866
+ mkdirSync7(path309.join(root, ".metaproject", "data"), { recursive: true });
140163
140867
  for (const segment of [triggerDataDir(root), runNowLogDir(root)]) {
140164
140868
  let info;
140165
140869
  try {
@@ -140178,7 +140882,7 @@ function prepareRunNowLogDir(root) {
140178
140882
  }
140179
140883
  const dir = runNowLogDir(root);
140180
140884
  try {
140181
- const fd = openSync5(path307.join(dir, ".gitignore"), constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | (constants3.O_NOFOLLOW ?? 0), 420);
140885
+ const fd = openSync5(path309.join(dir, ".gitignore"), constants3.O_WRONLY | constants3.O_CREAT | constants3.O_EXCL | (constants3.O_NOFOLLOW ?? 0), 420);
140182
140886
  try {
140183
140887
  writeSync(fd, `# keryx run-now logs \u2014 never commit
140184
140888
  *
@@ -140219,7 +140923,7 @@ function pruneRunLogs(dir, name, keep) {
140219
140923
  }
140220
140924
  for (const old of names.slice(0, Math.max(0, names.length - keep))) {
140221
140925
  try {
140222
- unlinkSync4(path307.join(dir, old));
140926
+ unlinkSync4(path309.join(dir, old));
140223
140927
  } catch {}
140224
140928
  }
140225
140929
  }
@@ -141237,279 +141941,6 @@ function mountSchedulesSidebar(options) {
141237
141941
  // src/tui/tui-shell.ts
141238
141942
  init_debug_log();
141239
141943
 
141240
- // src/lib/version-check.ts
141241
- init_config_dir();
141242
- init_fs();
141243
- import path308 from "path";
141244
- var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
141245
- var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
141246
- var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
141247
- var VERSION_STRING_LIMIT_CHARS = 64;
141248
- var REQUEST_TIMEOUT_MS = 2000;
141249
- var SUCCESS_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
141250
- var FAILURE_BACKOFF_MS = 15 * 60 * 1000;
141251
- var CACHE_LOCK_TIMEOUT_MS2 = 250;
141252
- var CACHE_LOCK_RETRY_MS2 = 10;
141253
- var CACHE_LOCK_STALE_MS2 = 5000;
141254
- var SYSTEM_TIMER = {
141255
- schedule: (callback, delayMs) => setTimeout(callback, delayMs),
141256
- cancel: (handle) => clearTimeout(handle)
141257
- };
141258
- function formatVersionUpdateAdvisory(result) {
141259
- if (result.status !== "update-available")
141260
- return;
141261
- return `Keryx update ${result.currentVersion} \u2192 ${result.latestVersion}
141262
- ${result.installCommand}`;
141263
- }
141264
- var STRICT_SEMVER = /^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?$/;
141265
- function parseSemVer(value) {
141266
- if (value.length > VERSION_STRING_LIMIT_CHARS)
141267
- return;
141268
- const match = STRICT_SEMVER.exec(value);
141269
- if (match === null)
141270
- return;
141271
- const prereleaseText = match[4];
141272
- const prerelease = [];
141273
- if (prereleaseText !== undefined) {
141274
- for (const identifier of prereleaseText.split(".")) {
141275
- if (/^\d+$/.test(identifier)) {
141276
- if (identifier.length > 1 && identifier.startsWith("0"))
141277
- return;
141278
- prerelease.push(BigInt(identifier));
141279
- } else {
141280
- prerelease.push(identifier);
141281
- }
141282
- }
141283
- }
141284
- return {
141285
- major: BigInt(match[1]),
141286
- minor: BigInt(match[2]),
141287
- patch: BigInt(match[3]),
141288
- prerelease
141289
- };
141290
- }
141291
- function compareSemVer(left, right) {
141292
- for (const key of ["major", "minor", "patch"]) {
141293
- if (left[key] < right[key])
141294
- return -1;
141295
- if (left[key] > right[key])
141296
- return 1;
141297
- }
141298
- if (left.prerelease.length === 0 || right.prerelease.length === 0) {
141299
- if (left.prerelease.length === right.prerelease.length)
141300
- return 0;
141301
- return left.prerelease.length === 0 ? 1 : -1;
141302
- }
141303
- const length = Math.max(left.prerelease.length, right.prerelease.length);
141304
- for (let index = 0;index < length; index += 1) {
141305
- const a = left.prerelease[index];
141306
- const b = right.prerelease[index];
141307
- if (a === undefined || b === undefined) {
141308
- if (a === b)
141309
- return 0;
141310
- return a === undefined ? -1 : 1;
141311
- }
141312
- if (typeof a === "bigint" && typeof b === "bigint") {
141313
- if (a < b)
141314
- return -1;
141315
- if (a > b)
141316
- return 1;
141317
- } else if (typeof a === "bigint") {
141318
- return -1;
141319
- } else if (typeof b === "bigint") {
141320
- return 1;
141321
- } else if (a !== b) {
141322
- return a < b ? -1 : 1;
141323
- }
141324
- }
141325
- return 0;
141326
- }
141327
- function parseCache(file) {
141328
- const read = readConfigFile(file);
141329
- if (!read.ok)
141330
- return;
141331
- try {
141332
- const value = JSON.parse(read.text);
141333
- if (value === null || typeof value !== "object" || Array.isArray(value))
141334
- return;
141335
- const record = value;
141336
- const cache = {};
141337
- if (typeof record.latestVersion === "string" && parseSemVer(record.latestVersion) !== undefined) {
141338
- cache.latestVersion = record.latestVersion;
141339
- }
141340
- if (typeof record.successAt === "number" && Number.isFinite(record.successAt))
141341
- cache.successAt = record.successAt;
141342
- if (typeof record.failureAt === "number" && Number.isFinite(record.failureAt))
141343
- cache.failureAt = record.failureAt;
141344
- return cache;
141345
- } catch {
141346
- return;
141347
- }
141348
- }
141349
- function resultFor(currentVersion, current, latestVersion, source) {
141350
- const latest = parseSemVer(latestVersion);
141351
- return compareSemVer(current, latest) < 0 ? { status: "update-available", currentVersion, latestVersion, installCommand: FIXED_INSTALL_COMMAND, source } : { status: "up-to-date", currentVersion, latestVersion, source };
141352
- }
141353
- function cancelWithoutWaiting(cancel) {
141354
- try {
141355
- cancel().catch(() => {});
141356
- } catch {}
141357
- }
141358
- async function boundedResponseText(response) {
141359
- const declared = response.headers.get("content-length");
141360
- if (declared !== null && /^\d+$/.test(declared) && BigInt(declared) > BigInt(RESPONSE_BODY_LIMIT_BYTES)) {
141361
- if (response.body !== null) {
141362
- cancelWithoutWaiting(() => response.body.cancel());
141363
- }
141364
- return;
141365
- }
141366
- if (response.body === null)
141367
- return "";
141368
- const reader = response.body.getReader();
141369
- const decoder = new TextDecoder;
141370
- let size = 0;
141371
- let text = "";
141372
- try {
141373
- for (;; ) {
141374
- const chunk = await reader.read();
141375
- if (chunk.done)
141376
- break;
141377
- size += chunk.value.byteLength;
141378
- if (size > RESPONSE_BODY_LIMIT_BYTES) {
141379
- cancelWithoutWaiting(() => reader.cancel());
141380
- return;
141381
- }
141382
- text += decoder.decode(chunk.value, { stream: true });
141383
- }
141384
- return text + decoder.decode();
141385
- } finally {
141386
- reader.releaseLock();
141387
- }
141388
- }
141389
- function unavailable(currentVersion, reason, cache) {
141390
- return {
141391
- status: "unavailable",
141392
- currentVersion,
141393
- reason,
141394
- ...cache?.latestVersion !== undefined ? { cachedLatestVersion: cache.latestVersion } : {}
141395
- };
141396
- }
141397
- function saveCache2(file, cache) {
141398
- try {
141399
- writeOwnerOnlyFileAtomic(file, `${JSON.stringify(cache, null, 2)}
141400
- `);
141401
- return true;
141402
- } catch {
141403
- return false;
141404
- }
141405
- }
141406
- function mergeSuccessfulCache(committed, latestVersion, successAt) {
141407
- if (committed.latestVersion === undefined || committed.successAt === undefined) {
141408
- return { latestVersion, successAt };
141409
- }
141410
- if (committed.successAt > successAt)
141411
- return committed;
141412
- if (committed.successAt < successAt)
141413
- return { latestVersion, successAt };
141414
- const committedVersion = parseSemVer(committed.latestVersion);
141415
- const candidateVersion = parseSemVer(latestVersion);
141416
- if (committedVersion !== undefined && candidateVersion !== undefined) {
141417
- const precedence = compareSemVer(committedVersion, candidateVersion);
141418
- if (precedence > 0)
141419
- return committed;
141420
- if (precedence < 0)
141421
- return { latestVersion, successAt };
141422
- if (committed.latestVersion >= latestVersion)
141423
- return committed;
141424
- }
141425
- return { latestVersion, successAt };
141426
- }
141427
- async function updateCache(file, update) {
141428
- try {
141429
- return await withFileLock(`${file}.lock`, async () => saveCache2(file, update(parseCache(file) ?? {})), {
141430
- timeoutMs: CACHE_LOCK_TIMEOUT_MS2,
141431
- retryMs: CACHE_LOCK_RETRY_MS2,
141432
- staleMs: CACHE_LOCK_STALE_MS2
141433
- });
141434
- } catch {
141435
- return false;
141436
- }
141437
- }
141438
- async function checkVersion(options) {
141439
- const current = parseSemVer(options.currentVersion);
141440
- if (current === undefined)
141441
- return unavailable(options.currentVersion, "invalid-current-version");
141442
- const now = options.now ?? Date.now;
141443
- const timestamp = now();
141444
- const configDir = ensureKeryxConfigDir(options.cacheDir);
141445
- const cacheFile = path308.join(configDir, "version-check.json");
141446
- const cache = parseCache(cacheFile);
141447
- if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
141448
- return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
141449
- }
141450
- if (cache?.failureAt !== undefined && timestamp - cache.failureAt >= 0 && timestamp - cache.failureAt < FAILURE_BACKOFF_MS) {
141451
- return unavailable(options.currentVersion, "suppressed", cache);
141452
- }
141453
- const fetchImpl = options.fetch ?? globalThis.fetch;
141454
- const controller = new AbortController;
141455
- const timer = options.timer ?? SYSTEM_TIMER;
141456
- let timeout;
141457
- let timeoutScheduled = false;
141458
- let failure;
141459
- let latestVersion;
141460
- try {
141461
- timeout = timer.schedule(() => controller.abort(), REQUEST_TIMEOUT_MS);
141462
- timeoutScheduled = true;
141463
- const response = await fetchImpl(REGISTRY_URL, {
141464
- method: "GET",
141465
- headers: { accept: "application/json" },
141466
- signal: controller.signal,
141467
- credentials: "omit"
141468
- });
141469
- if (!response.ok) {
141470
- failure = "http";
141471
- } else {
141472
- const body = await boundedResponseText(response);
141473
- if (body === undefined) {
141474
- failure = "response-too-large";
141475
- } else {
141476
- let parsed;
141477
- try {
141478
- parsed = JSON.parse(body);
141479
- } catch {
141480
- parsed = undefined;
141481
- }
141482
- if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
141483
- failure = "malformed-response";
141484
- } else {
141485
- const record = parsed;
141486
- if (record.name !== "@mrciphersmith/keryx" || typeof record.version !== "string") {
141487
- failure = "malformed-response";
141488
- } else if (parseSemVer(record.version) === undefined) {
141489
- failure = "invalid-latest-version";
141490
- } else {
141491
- latestVersion = record.version;
141492
- }
141493
- }
141494
- }
141495
- }
141496
- } catch {
141497
- failure = controller.signal.aborted ? "timeout" : "network";
141498
- } finally {
141499
- if (timeoutScheduled) {
141500
- try {
141501
- timer.cancel(timeout);
141502
- } catch {}
141503
- }
141504
- }
141505
- if (latestVersion === undefined) {
141506
- await updateCache(cacheFile, (committed) => ({ ...committed, failureAt: timestamp }));
141507
- return unavailable(options.currentVersion, failure ?? "network", cache);
141508
- }
141509
- await updateCache(cacheFile, (committed) => mergeSuccessfulCache(committed, latestVersion, timestamp));
141510
- return resultFor(options.currentVersion, current, latestVersion, "registry");
141511
- }
141512
-
141513
141944
  // src/tui/shell-chrome.ts
141514
141945
  init_debug_log();
141515
141946
 
@@ -143813,7 +144244,7 @@ init_model_tier();
143813
144244
  init_config_dir();
143814
144245
  init_fs();
143815
144246
  init_table();
143816
- import path309 from "path";
144247
+ import path310 from "path";
143817
144248
  var UNCATEGORIZED = "uncategorized";
143818
144249
  var EMPTY_TASK_COST_STORE = {};
143819
144250
  var MIN_MEASURED_TASKS = 20;
@@ -143879,7 +144310,7 @@ var TASK_COST_LOCK_TIMEOUT_MS = 3000;
143879
144310
  var TASK_COST_LOCK_RETRY_MS = 15;
143880
144311
  var TASK_COST_LOCK_STALE_MS = 1e4;
143881
144312
  function taskCostFilePath(dir) {
143882
- return path309.join(ensureKeryxSubdir(["routing"], dir), TASK_COST_FILE);
144313
+ return path310.join(ensureKeryxSubdir(["routing"], dir), TASK_COST_FILE);
143883
144314
  }
143884
144315
  function isTaskCostRecord(value) {
143885
144316
  if (typeof value !== "object" || value === null)
@@ -144943,11 +145374,11 @@ function openConform(otui, chrome, options) {
144943
145374
  }
144944
145375
 
144945
145376
  // src/tui/conform-source.ts
144946
- import { readFile as readFile160, readdir as readdir55, stat as stat31 } from "fs/promises";
144947
- import path310 from "path";
145377
+ import { readFile as readFile160, readdir as readdir56, stat as stat31 } from "fs/promises";
145378
+ import path311 from "path";
144948
145379
  async function readRecentConformDocs2(cwd) {
144949
145380
  try {
144950
- const raw = JSON.parse(await readFile160(path310.join(cwd, CONFORM_RECENTS_PATH), "utf8"));
145381
+ const raw = JSON.parse(await readFile160(path311.join(cwd, CONFORM_RECENTS_PATH), "utf8"));
144951
145382
  return Array.isArray(raw) ? raw : [];
144952
145383
  } catch {
144953
145384
  return [];
@@ -144968,25 +145399,25 @@ async function currentBranchPrTarget(cwd) {
144968
145399
  }
144969
145400
  }
144970
145401
  async function mostRecentReviewPackage(cwd) {
144971
- const flowsDir = path310.join(cwd, ".metaproject", "flows");
145402
+ const flowsDir = path311.join(cwd, ".metaproject", "flows");
144972
145403
  let best;
144973
145404
  try {
144974
- for (const flowEntry of await readdir55(flowsDir, { withFileTypes: true })) {
145405
+ for (const flowEntry of await readdir56(flowsDir, { withFileTypes: true })) {
144975
145406
  if (!flowEntry.isDirectory())
144976
145407
  continue;
144977
- const reviewsDir = path310.join(flowsDir, flowEntry.name, "reviews");
145408
+ const reviewsDir = path311.join(flowsDir, flowEntry.name, "reviews");
144978
145409
  let reviewEntries;
144979
145410
  try {
144980
- reviewEntries = await readdir55(reviewsDir, { withFileTypes: true });
145411
+ reviewEntries = await readdir56(reviewsDir, { withFileTypes: true });
144981
145412
  } catch {
144982
145413
  continue;
144983
145414
  }
144984
145415
  for (const reviewEntry of reviewEntries) {
144985
145416
  if (!reviewEntry.isDirectory())
144986
145417
  continue;
144987
- const dir = path310.join(reviewsDir, reviewEntry.name);
145418
+ const dir = path311.join(reviewsDir, reviewEntry.name);
144988
145419
  try {
144989
- const info = await stat31(path310.join(dir, "report.md"));
145420
+ const info = await stat31(path311.join(dir, "report.md"));
144990
145421
  if (best === undefined || info.mtimeMs > best.mtimeMs)
144991
145422
  best = { dir, mtimeMs: info.mtimeMs };
144992
145423
  } catch {}
@@ -145012,14 +145443,14 @@ async function loadConformSetup(cwd) {
145012
145443
  targets.push(pr);
145013
145444
  const reviewDir = await mostRecentReviewPackage(cwd);
145014
145445
  if (reviewDir !== undefined) {
145015
- targets.push({ id: `report:${reviewDir}`, kind: "report", label: `most recent review package \u2014 ${path310.relative(cwd, reviewDir)}` });
145446
+ targets.push({ id: `report:${reviewDir}`, kind: "report", label: `most recent review package \u2014 ${path311.relative(cwd, reviewDir)}` });
145016
145447
  }
145017
145448
  targets.push({ id: "diff:working", kind: "diff", label: "the working diff" });
145018
145449
  return { recents, targets };
145019
145450
  }
145020
145451
  async function readReportFindings2(reportDir) {
145021
145452
  try {
145022
- const raw = JSON.parse(await readFile160(path310.join(reportDir, "findings.json"), "utf8"));
145453
+ const raw = JSON.parse(await readFile160(path311.join(reportDir, "findings.json"), "utf8"));
145023
145454
  return Array.isArray(raw) ? raw : [];
145024
145455
  } catch {
145025
145456
  return [];
@@ -145135,7 +145566,7 @@ async function runConformForTarget(cwd, refPath, target, signal) {
145135
145566
  await scoreHunkRegions(hunkClauses, hunkRegionsFromDiff(info.diff));
145136
145567
  } else if (target.kind === "report") {
145137
145568
  const reportDir = target.id.slice("report:".length);
145138
- const reportMarkdown = await readFile160(path310.join(reportDir, "report.md"), "utf8").catch(() => "");
145569
+ const reportMarkdown = await readFile160(path311.join(reportDir, "report.md"), "utf8").catch(() => "");
145139
145570
  const findings = await readReportFindings2(reportDir);
145140
145571
  const facts = computeReportConformFacts(reportMarkdown, findings);
145141
145572
  const redacted = reportRedactedStateText(reportMarkdown);
@@ -145161,7 +145592,7 @@ async function runConformForTarget(cwd, refPath, target, signal) {
145161
145592
  async function writeRecentConformDoc(cwd, refPath) {
145162
145593
  await Promise.resolve().then(() => init_fs());
145163
145594
  const current = await readRecentConformDocs2(cwd);
145164
- await writeFileAtomic(path310.join(cwd, CONFORM_RECENTS_PATH), `${JSON.stringify(withRecentDoc(current, refPath), null, 2)}
145595
+ await writeFileAtomic(path311.join(cwd, CONFORM_RECENTS_PATH), `${JSON.stringify(withRecentDoc(current, refPath), null, 2)}
145165
145596
  `, {
145166
145597
  mode: 384
145167
145598
  }).catch(() => {});
@@ -145297,8 +145728,8 @@ async function runJevContractForShell(cwd, gitDiff = defaultGitDiff3) {
145297
145728
  }
145298
145729
 
145299
145730
  // src/tui/jev-triage-command.ts
145300
- import { readdir as readdir56, readFile as readFile161, stat as stat32 } from "fs/promises";
145301
- import path311 from "path";
145731
+ import { readdir as readdir57, readFile as readFile161, stat as stat32 } from "fs/promises";
145732
+ import path312 from "path";
145302
145733
  var JEV_TRIAGE_COMMAND = "/triage";
145303
145734
  function isJevTriageCommand(name) {
145304
145735
  return name === JEV_TRIAGE_COMMAND;
@@ -145307,25 +145738,25 @@ function renderJevTriageForShell(result) {
145307
145738
  return renderTriageMarkdown(result).trimEnd();
145308
145739
  }
145309
145740
  async function resolveLatestReviewPackage(cwd) {
145310
- const flowsDir = path311.join(cwd, ".metaproject", "flows");
145741
+ const flowsDir = path312.join(cwd, ".metaproject", "flows");
145311
145742
  let flowEntries;
145312
145743
  try {
145313
- flowEntries = (await readdir56(flowsDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
145744
+ flowEntries = (await readdir57(flowsDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
145314
145745
  } catch {
145315
145746
  return;
145316
145747
  }
145317
145748
  const candidates = [];
145318
145749
  for (const flowName of flowEntries) {
145319
- const reviewsDir = path311.join(flowsDir, flowName, "reviews");
145750
+ const reviewsDir = path312.join(flowsDir, flowName, "reviews");
145320
145751
  let reviewEntries;
145321
145752
  try {
145322
- reviewEntries = (await readdir56(reviewsDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
145753
+ reviewEntries = (await readdir57(reviewsDir, { withFileTypes: true })).filter((e) => e.isDirectory()).map((e) => e.name);
145323
145754
  } catch {
145324
145755
  continue;
145325
145756
  }
145326
145757
  for (const reviewName of reviewEntries) {
145327
- const packageDir = path311.join(reviewsDir, reviewName);
145328
- const findingsPath = path311.join(packageDir, "findings.json");
145758
+ const packageDir = path312.join(reviewsDir, reviewName);
145759
+ const findingsPath = path312.join(packageDir, "findings.json");
145329
145760
  try {
145330
145761
  const stats = await stat32(findingsPath);
145331
145762
  candidates.push({ dir: packageDir, mtimeMs: stats.mtimeMs });
@@ -145348,9 +145779,9 @@ async function runJevTriageForShell(cwd) {
145348
145779
  if (packageDir === undefined) {
145349
145780
  return "review-jev-triage: no review package found under .metaproject/flows/*/reviews/ \u2014 run a review round first.";
145350
145781
  }
145351
- const findingsRaw = JSON.parse(await readFile161(path311.join(packageDir, "findings.json"), "utf8"));
145782
+ const findingsRaw = JSON.parse(await readFile161(path312.join(packageDir, "findings.json"), "utf8"));
145352
145783
  const result = await computeJevTriageResult({ findingsRaw });
145353
- return `review-jev-triage: ${path311.relative(cwd, packageDir)}
145784
+ return `review-jev-triage: ${path312.relative(cwd, packageDir)}
145354
145785
  ${renderJevTriageForShell(result)}`;
145355
145786
  }
145356
145787
 
@@ -148264,10 +148695,10 @@ function openHelpModal(otui, chrome, options) {
148264
148695
 
148265
148696
  // src/tui/help-first-run.ts
148266
148697
  init_config_dir();
148267
- import path312 from "path";
148698
+ import path313 from "path";
148268
148699
  var FIRST_RUN_HELP_FILE = "help-first-run.json";
148269
148700
  function firstRunHelpPath(dir) {
148270
- return path312.join(keryxConfigDir(dir), FIRST_RUN_HELP_FILE);
148701
+ return path313.join(keryxConfigDir(dir), FIRST_RUN_HELP_FILE);
148271
148702
  }
148272
148703
  function helpFirstRunShown(dir) {
148273
148704
  const result = readConfigFile(firstRunHelpPath(dir));
@@ -149351,10 +149782,10 @@ function describeElicitationPrompt(tool, inputJson) {
149351
149782
  // src/lib/permission-mode-config.ts
149352
149783
  init_permission_mode();
149353
149784
  init_config_dir();
149354
- import path313 from "path";
149785
+ import path314 from "path";
149355
149786
  var EMPTY3 = { schemaVersion: 1, projects: {} };
149356
149787
  function permissionModeConfigPath(dir) {
149357
- return path313.join(keryxConfigDir(dir), "permission-mode.json");
149788
+ return path314.join(keryxConfigDir(dir), "permission-mode.json");
149358
149789
  }
149359
149790
  function withRegistryLock2(dir, fn) {
149360
149791
  return withFileLock2(`${permissionModeConfigPath(dir)}.lock`, fn, {
@@ -152303,7 +152734,7 @@ function mountExecutionPlanPanel(otui, renderer, parent, options) {
152303
152734
  // src/commands/schedule.ts
152304
152735
  import { createInterface } from "readline/promises";
152305
152736
  import { readFile as readFile162 } from "fs/promises";
152306
- import path315 from "path";
152737
+ import path316 from "path";
152307
152738
 
152308
152739
  // src/commands/trigger.ts
152309
152740
  init_gdgraph();
@@ -152314,9 +152745,9 @@ init_agent();
152314
152745
  init_agent_hooks();
152315
152746
  import { execFile as execFile11 } from "child_process";
152316
152747
  import { randomUUID as randomUUID36 } from "crypto";
152317
- import { mkdtemp as mkdtemp5, mkdir as mkdir68, readdir as readdir57, rm as rm19, writeFile as writeFile47 } from "fs/promises";
152748
+ import { mkdtemp as mkdtemp5, mkdir as mkdir68, readdir as readdir58, rm as rm19, writeFile as writeFile47 } from "fs/promises";
152318
152749
  import { homedir as homedir13, tmpdir as tmpdir11 } from "os";
152319
- import path314 from "path";
152750
+ import path315 from "path";
152320
152751
  init_interactive_tools();
152321
152752
 
152322
152753
  // src/harness/process/sandbox/proxy.ts
@@ -152784,9 +153215,9 @@ function refusal3(runId, code, detail, action) {
152784
153215
  function agentTaskScratchParent(env = process.env) {
152785
153216
  const uid = typeof process.getuid === "function" ? process.getuid() : 0;
152786
153217
  const runtime = env["XDG_RUNTIME_DIR"];
152787
- if (runtime !== undefined && runtime.length > 0 && path314.isAbsolute(runtime))
152788
- return path314.join(runtime, "keryx-agent-tasks");
152789
- return path314.join(tmpdir11(), `keryx-agent-tasks-${uid}`);
153218
+ if (runtime !== undefined && runtime.length > 0 && path315.isAbsolute(runtime))
153219
+ return path315.join(runtime, "keryx-agent-tasks");
153220
+ return path315.join(tmpdir11(), `keryx-agent-tasks-${uid}`);
152790
153221
  }
152791
153222
  function lastAssistantText(history) {
152792
153223
  for (let i = history.length - 1;i >= 0; i -= 1) {
@@ -152797,9 +153228,9 @@ function lastAssistantText(history) {
152797
153228
  return "";
152798
153229
  }
152799
153230
  async function pruneReports(dir, keep) {
152800
- const names = (await readdir57(dir).catch(() => [])).filter((n) => n.endsWith(".md")).sort();
153231
+ const names = (await readdir58(dir).catch(() => [])).filter((n) => n.endsWith(".md")).sort();
152801
153232
  for (const old of names.slice(0, Math.max(0, names.length - keep))) {
152802
- await rm19(path314.join(dir, old), { force: true }).catch(() => {});
153233
+ await rm19(path315.join(dir, old), { force: true }).catch(() => {});
152803
153234
  }
152804
153235
  }
152805
153236
  async function runAgentTaskDispatch(projectRoot, entry, action, deps = {}) {
@@ -152836,10 +153267,10 @@ async function runLocked(projectRoot, entry, action, deps, runId, now) {
152836
153267
  };
152837
153268
  }
152838
153269
  const scratchParent = parent.dir;
152839
- const scratch = await mkdtemp5(path314.join(scratchParent, `${entry.name.replace(/[^A-Za-z0-9._-]/g, "-")}-`));
152840
- const workdir = path314.join(scratch, "work");
152841
- const scratchHome = path314.join(scratch, "home");
152842
- const grantedCwd = path314.join(scratch, "granted-cwd");
153270
+ const scratch = await mkdtemp5(path315.join(scratchParent, `${entry.name.replace(/[^A-Za-z0-9._-]/g, "-")}-`));
153271
+ const workdir = path315.join(scratch, "work");
153272
+ const scratchHome = path315.join(scratch, "home");
153273
+ const grantedCwd = path315.join(scratch, "granted-cwd");
152843
153274
  await mkdir68(workdir, { recursive: true });
152844
153275
  await mkdir68(scratchHome, { recursive: true });
152845
153276
  await mkdir68(grantedCwd, { recursive: true, mode: 448 });
@@ -152852,7 +153283,7 @@ async function runLocked(projectRoot, entry, action, deps, runId, now) {
152852
153283
  const invocation = keryxInvocation();
152853
153284
  let networkInput = action.grants.network === "full";
152854
153285
  if (action.grants.network === "allowlist") {
152855
- const proxySocketPath = path314.join(scratch, "net-proxy.sock");
153286
+ const proxySocketPath = path315.join(scratch, "net-proxy.sock");
152856
153287
  try {
152857
153288
  const proxy = await createAllowlistProxy({
152858
153289
  allowedDomains: [...action.grants.domains],
@@ -153001,12 +153432,12 @@ async function runLocked(projectRoot, entry, action, deps, runId, now) {
153001
153432
  let reportNote = "";
153002
153433
  try {
153003
153434
  await ensureTriggerDataIgnored(projectRoot);
153004
- const dir = path314.join(triggerReportsDir(projectRoot), entry.name);
153435
+ const dir = path315.join(triggerReportsDir(projectRoot), entry.name);
153005
153436
  await mkdir68(dir, { recursive: true });
153006
- const file = path314.join(dir, `${runId}.md`);
153437
+ const file = path315.join(dir, `${runId}.md`);
153007
153438
  await writeFile47(file, renderReport2({ entry, action, runId, at: now().toISOString(), outcome, why, cost, grantedCalls, denials, sandboxNote, summary, networkDecisions }), "utf8");
153008
153439
  await pruneReports(dir, action.report.keep);
153009
- reportPath = path314.relative(projectRoot, file);
153440
+ reportPath = path315.relative(projectRoot, file);
153010
153441
  } catch (error) {
153011
153442
  reportNote = ` (report NOT written: ${error instanceof Error ? error.message : String(error)})`;
153012
153443
  }
@@ -153914,7 +154345,7 @@ async function showSubcommand(cwd, name, host, deps) {
153914
154345
  if (row.reportPath !== undefined) {
153915
154346
  console.log(`
153916
154347
  --- ${row.reportPath} ---`);
153917
- console.log(await readFile162(path315.join(cwd, row.reportPath), "utf8").catch(() => "(the report could not be read)"));
154348
+ console.log(await readFile162(path316.join(cwd, row.reportPath), "utf8").catch(() => "(the report could not be read)"));
153918
154349
  }
153919
154350
  }
153920
154351
  async function simpleAction(cwd, name, verb, action) {
@@ -159547,6 +159978,14 @@ ${formatThemeList(getThemeId())}`);
159547
159978
  });
159548
159979
  return;
159549
159980
  }
159981
+ if (command.name === "/doctor") {
159982
+ (async () => {
159983
+ const report = await buildDoctorReport(sessionCwd);
159984
+ io.onSystem?.(`${formatDoctorReport2(report)}
159985
+ `);
159986
+ })();
159987
+ return;
159988
+ }
159550
159989
  if (command.name === "/game") {
159551
159990
  showGame(line);
159552
159991
  return;
@@ -160502,6 +160941,7 @@ var READLINE_AGENT_COMMANDS = [
160502
160941
  "/compact",
160503
160942
  "/status",
160504
160943
  "/flows",
160944
+ "/doctor",
160505
160945
  "/theme",
160506
160946
  "/mode",
160507
160947
  "/reasoning",
@@ -160994,256 +161434,263 @@ Kept the current session.
160994
161434
  `);
160995
161435
  }
160996
161436
  };
160997
- for await (const line of io.lines) {
160998
- io.onSafeBoundary?.();
160999
- if (line.startsWith("/")) {
161000
- const parts = line.trim().split(/\s+/);
161001
- const command = parts[0] ?? "";
161002
- const argument = parts.slice(1).join(" ");
161003
- if (command === "/exit" || command === "/quit") {
161004
- leaveBus();
161005
- releaseLease();
161006
- return;
161007
- }
161008
- if (command === "/bus") {
161009
- await runBusSlashCommand(bus, argument, system);
161010
- continue;
161011
- }
161012
- if (command === "/help") {
161013
- system(HELP_TEXT);
161014
- continue;
161015
- }
161016
- if (isSessionInfoCommand(command)) {
161017
- const cwd = deps.session?.cwd;
161018
- const [workspaces, flows] = cwd === undefined ? [[], []] : await Promise.all([loadInspectorWorkspaces(cwd), loadInspectorFlows(cwd)]);
161019
- system(formatSessionInfoText(buildSessionInfoSnapshot({
161020
- summary: live?.summary,
161021
- selection: { provider: providerName, model: modelName },
161022
- version: package_default.version,
161023
- estimateTokens: estimateContextTokens(history),
161024
- limits: await loadSessionLimits({
161025
- provider: providerName,
161026
- model: modelName,
161027
- ...baseUrl !== undefined ? { baseUrl } : {}
161028
- }),
161029
- sessionText: history.map((message) => message.content).join(`
161030
- `),
161031
- workspaces,
161032
- flows
161033
- })));
161034
- continue;
161035
- }
161036
- if (isFlowsCommand(command)) {
161037
- const cwd = deps.session?.cwd;
161038
- const items = cwd === undefined ? [] : await loadInspectorFlows(cwd);
161039
- const query = argument.trim();
161040
- if (query.length > 0) {
161041
- const found = findFlowItem(items, query);
161042
- system(found !== undefined ? formatFlowDetailText(found) : `No flow matching '${query}'.
161043
- `);
161044
- continue;
161437
+ try {
161438
+ for await (const line of io.lines) {
161439
+ io.onSafeBoundary?.();
161440
+ if (line.startsWith("/")) {
161441
+ const parts = line.trim().split(/\s+/);
161442
+ const command = parts[0] ?? "";
161443
+ const argument = parts.slice(1).join(" ");
161444
+ if (command === "/exit" || command === "/quit") {
161445
+ return;
161045
161446
  }
161046
- system(formatFlowListText(items));
161047
- continue;
161048
- }
161049
- if (command === "/theme") {
161050
- const wanted = argument.trim();
161051
- if (wanted.length === 0) {
161052
- system(formatThemeList(getThemeId()));
161447
+ if (command === "/bus") {
161448
+ await runBusSlashCommand(bus, argument, system);
161053
161449
  continue;
161054
161450
  }
161055
- const next = parseThemeId(wanted);
161056
- if (next === undefined) {
161057
- system(`Unknown theme '${wanted}'.
161058
- ${formatThemeList(getThemeId())}`);
161451
+ if (command === "/help") {
161452
+ system(HELP_TEXT);
161059
161453
  continue;
161060
161454
  }
161061
- applyThemeId(next);
161062
- persistThemeId(next);
161063
- system(`Theme: ${themeLabel(next)}
161455
+ if (command === "/doctor") {
161456
+ const report = await buildDoctorReport(deps.session?.cwd ?? process.cwd());
161457
+ system(`${formatDoctorReport2(report)}
161064
161458
  `);
161065
- continue;
161066
- }
161067
- if (command === "/clear" || command === "/new") {
161068
- if (sessionsOn) {
161069
- try {
161070
- const next = switchLeasedSession(lease, () => openLeased({ cwd: sessionCwd, provider: providerName, model: modelName }));
161071
- holdLease(next.lease);
161072
- live = next.handle;
161073
- } catch (cause) {
161074
- system(`${cause instanceof Error ? cause.message : String(cause)}
161075
- Kept the current session.
161459
+ continue;
161460
+ }
161461
+ if (isSessionInfoCommand(command)) {
161462
+ const cwd = deps.session?.cwd;
161463
+ const [workspaces, flows] = cwd === undefined ? [[], []] : await Promise.all([loadInspectorWorkspaces(cwd), loadInspectorFlows(cwd)]);
161464
+ system(formatSessionInfoText(buildSessionInfoSnapshot({
161465
+ summary: live?.summary,
161466
+ selection: { provider: providerName, model: modelName },
161467
+ version: package_default.version,
161468
+ estimateTokens: estimateContextTokens(history),
161469
+ limits: await loadSessionLimits({
161470
+ provider: providerName,
161471
+ model: modelName,
161472
+ ...baseUrl !== undefined ? { baseUrl } : {}
161473
+ }),
161474
+ sessionText: history.map((message) => message.content).join(`
161475
+ `),
161476
+ workspaces,
161477
+ flows
161478
+ })));
161479
+ continue;
161480
+ }
161481
+ if (isFlowsCommand(command)) {
161482
+ const cwd = deps.session?.cwd;
161483
+ const items = cwd === undefined ? [] : await loadInspectorFlows(cwd);
161484
+ const query = argument.trim();
161485
+ if (query.length > 0) {
161486
+ const found = findFlowItem(items, query);
161487
+ system(found !== undefined ? formatFlowDetailText(found) : `No flow matching '${query}'.
161076
161488
  `);
161077
161489
  continue;
161078
161490
  }
161079
- history = [];
161080
- archive = [];
161081
- try {
161082
- await bus?.setSession(live.summary.id);
161083
- } catch (cause) {
161084
- system(busErrorLine(cause));
161085
- }
161086
- system(`New session ${shortSessionId(live.summary.id)} (previous kept on disk)
161087
- `);
161088
- } else {
161089
- history.length = 0;
161491
+ system(formatFlowListText(items));
161492
+ continue;
161090
161493
  }
161091
- continue;
161092
- }
161093
- if (command === "/compact") {
161094
- if (live === undefined) {
161095
- system(`No persistent session in this mode.
161494
+ if (command === "/theme") {
161495
+ const wanted = argument.trim();
161496
+ if (wanted.length === 0) {
161497
+ system(formatThemeList(getThemeId()));
161498
+ continue;
161499
+ }
161500
+ const next = parseThemeId(wanted);
161501
+ if (next === undefined) {
161502
+ system(`Unknown theme '${wanted}'.
161503
+ ${formatThemeList(getThemeId())}`);
161504
+ continue;
161505
+ }
161506
+ applyThemeId(next);
161507
+ persistThemeId(next);
161508
+ system(`Theme: ${themeLabel(next)}
161096
161509
  `);
161097
161510
  continue;
161098
161511
  }
161099
- if (!leaseWatch.canPersist()) {
161100
- system(LOST_LEASE_COMPACT_REFUSAL);
161512
+ if (command === "/clear" || command === "/new") {
161513
+ if (sessionsOn) {
161514
+ try {
161515
+ const next = switchLeasedSession(lease, () => openLeased({ cwd: sessionCwd, provider: providerName, model: modelName }));
161516
+ holdLease(next.lease);
161517
+ live = next.handle;
161518
+ } catch (cause) {
161519
+ system(`${cause instanceof Error ? cause.message : String(cause)}
161520
+ Kept the current session.
161521
+ `);
161522
+ continue;
161523
+ }
161524
+ history = [];
161525
+ archive = [];
161526
+ try {
161527
+ await bus?.setSession(live.summary.id);
161528
+ } catch (cause) {
161529
+ system(busErrorLine(cause));
161530
+ }
161531
+ system(`New session ${shortSessionId(live.summary.id)} (previous kept on disk)
161532
+ `);
161533
+ } else {
161534
+ history.length = 0;
161535
+ }
161101
161536
  continue;
161102
161537
  }
161103
- const focus = argument.trim();
161104
- const packed = compactSession(live, history, archive, {
161105
- keepLastUserTurns: 3,
161106
- ...focus.length > 0 ? { focus } : {},
161107
- provider: providerName,
161108
- model: modelName
161109
- });
161110
- live = packed.handle;
161111
- history = packed.context;
161112
- if (packed.result.noop) {
161113
- system(`Nothing to compact (context already small).
161538
+ if (command === "/compact") {
161539
+ if (live === undefined) {
161540
+ system(`No persistent session in this mode.
161114
161541
  `);
161115
- } else {
161116
- system(`Compacted: removed ${packed.result.removed} msgs from context \xB7 archive ${live.summary.archiveMessageCount} \xB7 compact\xD7${live.summary.compactCount}
161542
+ continue;
161543
+ }
161544
+ if (!leaseWatch.canPersist()) {
161545
+ system(LOST_LEASE_COMPACT_REFUSAL);
161546
+ continue;
161547
+ }
161548
+ const focus = argument.trim();
161549
+ const packed = compactSession(live, history, archive, {
161550
+ keepLastUserTurns: 3,
161551
+ ...focus.length > 0 ? { focus } : {},
161552
+ provider: providerName,
161553
+ model: modelName
161554
+ });
161555
+ live = packed.handle;
161556
+ history = packed.context;
161557
+ if (packed.result.noop) {
161558
+ system(`Nothing to compact (context already small).
161559
+ `);
161560
+ } else {
161561
+ system(`Compacted: removed ${packed.result.removed} msgs from context \xB7 archive ${live.summary.archiveMessageCount} \xB7 compact\xD7${live.summary.compactCount}
161117
161562
  `);
161563
+ }
161564
+ continue;
161118
161565
  }
161119
- continue;
161120
- }
161121
- if (command === "/model") {
161122
- if (argument.length > 0) {
161123
- modelName = argument;
161124
- provider = makeActive();
161566
+ if (command === "/model") {
161567
+ if (argument.length > 0) {
161568
+ modelName = argument;
161569
+ provider = makeActive();
161570
+ }
161571
+ continue;
161125
161572
  }
161126
- continue;
161127
- }
161128
- if (command === "/models") {
161129
- if (deps.selectProviderModel === undefined) {
161130
- system(`Interactive model selection is not available in this session.
161573
+ if (command === "/models") {
161574
+ if (deps.selectProviderModel === undefined) {
161575
+ system(`Interactive model selection is not available in this session.
161131
161576
  `);
161577
+ continue;
161578
+ }
161579
+ await selectAndApply({ onlyProvider: providerName });
161132
161580
  continue;
161133
161581
  }
161134
- await selectAndApply({ onlyProvider: providerName });
161135
- continue;
161136
- }
161137
- if (command === "/provider") {
161138
- if (argument.length > 0) {
161139
- if (argument === "openai-codex") {
161140
- if (deps.selectProviderModel === undefined) {
161141
- system("Interactive subscription selection is not available. Run `keryx auth login openai-codex` and select an authorized model.\n");
161142
- } else {
161143
- await selectAndApply({ onlyProvider: "openai-codex" });
161582
+ if (command === "/provider") {
161583
+ if (argument.length > 0) {
161584
+ if (argument === "openai-codex") {
161585
+ if (deps.selectProviderModel === undefined) {
161586
+ system("Interactive subscription selection is not available. Run `keryx auth login openai-codex` and select an authorized model.\n");
161587
+ } else {
161588
+ await selectAndApply({ onlyProvider: "openai-codex" });
161589
+ }
161590
+ continue;
161144
161591
  }
161592
+ providerName = argument;
161593
+ provider = makeActive();
161145
161594
  continue;
161146
161595
  }
161147
- providerName = argument;
161148
- provider = makeActive();
161596
+ if (deps.selectProviderModel === undefined) {
161597
+ system(`Interactive provider selection is not available in this session.
161598
+ `);
161599
+ continue;
161600
+ }
161601
+ await selectAndApply({});
161149
161602
  continue;
161150
161603
  }
161151
- if (deps.selectProviderModel === undefined) {
161152
- system(`Interactive provider selection is not available in this session.
161153
- `);
161604
+ if (command === "/connect") {
161605
+ if (deps.selectProviderModel === undefined) {
161606
+ system(CONNECT_GUIDANCE);
161607
+ continue;
161608
+ }
161609
+ await selectAndApply({ onlyConnected: true });
161154
161610
  continue;
161155
161611
  }
161156
- await selectAndApply({});
161157
- continue;
161158
- }
161159
- if (command === "/connect") {
161160
- if (deps.selectProviderModel === undefined) {
161161
- system(CONNECT_GUIDANCE);
161612
+ const wrongMode = describeUnavailableCommand(command, "chat");
161613
+ if (wrongMode !== undefined) {
161614
+ system(wrongMode);
161162
161615
  continue;
161163
161616
  }
161164
- await selectAndApply({ onlyConnected: true });
161617
+ system(`Unknown command: ${command}. Type /help for commands.
161618
+ `);
161165
161619
  continue;
161166
161620
  }
161167
- const wrongMode = describeUnavailableCommand(command, "chat");
161168
- if (wrongMode !== undefined) {
161169
- system(wrongMode);
161621
+ if (line.trim().length === 0) {
161170
161622
  continue;
161171
161623
  }
161172
- system(`Unknown command: ${command}. Type /help for commands.
161173
- `);
161174
- continue;
161175
- }
161176
- if (line.trim().length === 0) {
161177
- continue;
161178
- }
161179
- history.push({ role: "user", content: line, provenance: "project" });
161180
- const resolvedMaxOutputTokens = modelParams.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS;
161181
- const request = {
161182
- providerId: providerName,
161183
- modelId: modelName,
161184
- systemInstruction: SYSTEM_INSTRUCTION,
161185
- messages: [...history],
161186
- budget: { maxOutputTokens: resolvedMaxOutputTokens, runReservation: resolvedMaxOutputTokens },
161187
- ...modelParams.temperature !== undefined ? { options: { temperature: modelParams.temperature } } : {},
161188
- stream: true,
161189
- requestId: deps.idSeq(),
161190
- parentRunId
161191
- };
161192
- let accumulated = "";
161193
- let errored = false;
161194
- busWorking = true;
161195
- io.onTurnStart?.();
161196
- try {
161197
- const streamOptions = {
161198
- attemptId: deps.idSeq(),
161199
- ...modelParams.timeoutMs !== undefined ? { timeoutMs: modelParams.timeoutMs } : {}
161624
+ history.push({ role: "user", content: line, provenance: "project" });
161625
+ const resolvedMaxOutputTokens = modelParams.maxOutputTokens ?? DEFAULT_MAX_OUTPUT_TOKENS;
161626
+ const request = {
161627
+ providerId: providerName,
161628
+ modelId: modelName,
161629
+ systemInstruction: SYSTEM_INSTRUCTION,
161630
+ messages: [...history],
161631
+ budget: { maxOutputTokens: resolvedMaxOutputTokens, runReservation: resolvedMaxOutputTokens },
161632
+ ...modelParams.temperature !== undefined ? { options: { temperature: modelParams.temperature } } : {},
161633
+ stream: true,
161634
+ requestId: deps.idSeq(),
161635
+ parentRunId
161200
161636
  };
161201
- for await (const event of provider.stream(request, streamOptions)) {
161202
- if (event.kind === "text_delta") {
161203
- const text = event.text ?? "";
161204
- io.write(text);
161205
- accumulated += text;
161206
- } else if (event.kind === "provider_error") {
161207
- const detail = event.error?.message ?? event.error?.kind ?? "provider error";
161208
- system(`
161637
+ let accumulated = "";
161638
+ let errored = false;
161639
+ busWorking = true;
161640
+ io.onTurnStart?.();
161641
+ try {
161642
+ const streamOptions = {
161643
+ attemptId: deps.idSeq(),
161644
+ ...modelParams.timeoutMs !== undefined ? { timeoutMs: modelParams.timeoutMs } : {}
161645
+ };
161646
+ for await (const event of provider.stream(request, streamOptions)) {
161647
+ if (event.kind === "text_delta") {
161648
+ const text = event.text ?? "";
161649
+ io.write(text);
161650
+ accumulated += text;
161651
+ } else if (event.kind === "provider_error") {
161652
+ const detail = event.error?.message ?? event.error?.kind ?? "provider error";
161653
+ system(`
161209
161654
  [error] ${detail}
161210
161655
  `);
161211
- errored = true;
161212
- break;
161213
- } else if (event.kind === "model_end") {
161214
- break;
161656
+ errored = true;
161657
+ break;
161658
+ } else if (event.kind === "model_end") {
161659
+ break;
161660
+ }
161215
161661
  }
161216
- }
161217
- } catch (cause) {
161218
- system(`
161662
+ } catch (cause) {
161663
+ system(`
161219
161664
  [error] ${cause instanceof Error ? cause.message : String(cause)}
161220
161665
  `);
161221
- errored = true;
161222
- }
161223
- if (accumulated.length > 0) {
161224
- io.onTurnEnd?.(accumulated);
161225
- }
161226
- if (!errored) {
161227
- history.push({ role: "assistant", content: accumulated, provenance: "model" });
161228
- archive.push({ role: "user", content: line, provenance: "project" });
161229
- archive.push({ role: "assistant", content: accumulated, provenance: "model" });
161230
- save();
161231
- } else if (accumulated.length > 0) {
161232
- history.push({ role: "assistant", content: accumulated, provenance: "model" });
161233
- archive.push({ role: "user", content: line, provenance: "project" });
161234
- archive.push({ role: "assistant", content: accumulated, provenance: "model" });
161235
- save();
161236
- } else {
161237
- history.pop();
161238
- }
161239
- busWorking = false;
161240
- io.onSafeBoundary?.();
161241
- io.write(`
161666
+ errored = true;
161667
+ }
161668
+ if (accumulated.length > 0) {
161669
+ io.onTurnEnd?.(accumulated);
161670
+ }
161671
+ if (!errored) {
161672
+ history.push({ role: "assistant", content: accumulated, provenance: "model" });
161673
+ archive.push({ role: "user", content: line, provenance: "project" });
161674
+ archive.push({ role: "assistant", content: accumulated, provenance: "model" });
161675
+ save();
161676
+ } else if (accumulated.length > 0) {
161677
+ history.push({ role: "assistant", content: accumulated, provenance: "model" });
161678
+ archive.push({ role: "user", content: line, provenance: "project" });
161679
+ archive.push({ role: "assistant", content: accumulated, provenance: "model" });
161680
+ save();
161681
+ } else {
161682
+ history.pop();
161683
+ }
161684
+ busWorking = false;
161685
+ io.onSafeBoundary?.();
161686
+ io.write(`
161242
161687
 
161243
161688
  `);
161689
+ }
161690
+ } finally {
161691
+ leaveBus();
161692
+ releaseLease();
161244
161693
  }
161245
- leaveBus();
161246
- releaseLease();
161247
161694
  }
161248
161695
  function realMakeProvider(write) {
161249
161696
  return (name, model, baseUrl) => {
@@ -162180,6 +162627,10 @@ ${GUTTER}${turnSeparator()}
162180
162627
  await runBusSlashCommand(bus, rest, (text) => agentIo.onSystem?.(text));
162181
162628
  } else if (command === "/help") {
162182
162629
  agentIo.onSystem?.(readlineAgentHelpText());
162630
+ } else if (command === "/doctor") {
162631
+ const report = await buildDoctorReport(sessionCwd);
162632
+ agentIo.onSystem?.(`${formatDoctorReport2(report)}
162633
+ `);
162183
162634
  } else if (isSessionInfoCommand(command)) {
162184
162635
  const cwd = sessionCwd;
162185
162636
  const [workspaces, flows] = await Promise.all([
@@ -165724,7 +166175,7 @@ init_fs();
165724
166175
  init_prompt();
165725
166176
  import { readFile as readFile163 } from "fs/promises";
165726
166177
  import { stdin } from "process";
165727
- import path317 from "path";
166178
+ import path318 from "path";
165728
166179
  init_ui();
165729
166180
  var MODULES = [
165730
166181
  { name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
@@ -165771,8 +166222,8 @@ async function modulesCommand(args = []) {
165771
166222
  return;
165772
166223
  }
165773
166224
  const wantsJson = args.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
165774
- const metaprojectRoot = path317.join(process.cwd(), ".metaproject");
165775
- const manifestPath = path317.join(metaprojectRoot, "metaproject.json");
166225
+ const metaprojectRoot = path318.join(process.cwd(), ".metaproject");
166226
+ const manifestPath = path318.join(metaprojectRoot, "metaproject.json");
165776
166227
  if (!await pathExists(manifestPath)) {
165777
166228
  if (wantsJson) {
165778
166229
  console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
@@ -165895,7 +166346,7 @@ import { createHash as createHash68, randomBytes as randomBytes11, randomUUID as
165895
166346
  import {
165896
166347
  chmodSync as chmodSync5,
165897
166348
  closeSync as closeSync7,
165898
- existsSync as existsSync48,
166349
+ existsSync as existsSync49,
165899
166350
  fsyncSync as fsyncSync2,
165900
166351
  openSync as openSync8,
165901
166352
  renameSync as renameSync5,
@@ -165903,9 +166354,9 @@ import {
165903
166354
  unlinkSync as unlinkSync6,
165904
166355
  writeFileSync as writeFileSync10
165905
166356
  } from "fs";
165906
- import path318 from "path";
166357
+ import path319 from "path";
165907
166358
  function serveCredentialPath(dir) {
165908
- return path318.join(keryxConfigDir(dir), "serve-credentials.json");
166359
+ return path319.join(keryxConfigDir(dir), "serve-credentials.json");
165909
166360
  }
165910
166361
  function constantTimeEqual(a, b) {
165911
166362
  const width = Math.max(a.length, b.length);
@@ -165939,7 +166390,7 @@ function isGroupOrOtherAccessible(file) {
165939
166390
  }
165940
166391
  function readServeCredential(dir) {
165941
166392
  const file = serveCredentialPath(dir);
165942
- if (!existsSync48(file)) {
166393
+ if (!existsSync49(file)) {
165943
166394
  return { status: "absent" };
165944
166395
  }
165945
166396
  if (isGroupOrOtherAccessible(file)) {
@@ -166138,23 +166589,23 @@ class AuthFailureThrottle {
166138
166589
  // src/lib/serve-turn-store.ts
166139
166590
  init_config_dir();
166140
166591
  import { createHash as createHash69 } from "crypto";
166141
- import { existsSync as existsSync49, readdirSync as readdirSync12, rmSync as rmSync4 } from "fs";
166142
- import path319 from "path";
166592
+ import { existsSync as existsSync50, readdirSync as readdirSync12, rmSync as rmSync4 } from "fs";
166593
+ import path320 from "path";
166143
166594
  var MAX_TURN_EVENTS = 1e4;
166144
166595
  function turnsRoot(dir) {
166145
- return path319.join(keryxConfigDir(dir), "turns");
166596
+ return path320.join(keryxConfigDir(dir), "turns");
166146
166597
  }
166147
166598
  function turnDir(turnId, dir) {
166148
- return path319.join(turnsRoot(dir), turnId);
166599
+ return path320.join(turnsRoot(dir), turnId);
166149
166600
  }
166150
166601
  function keyPath(project, idempotencyKey, dir) {
166151
166602
  const projectBytes = Buffer.byteLength(project, "utf8");
166152
166603
  const digest = createHash69("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
166153
- return path319.join(turnsRoot(dir), "keys", `${digest}.json`);
166604
+ return path320.join(turnsRoot(dir), "keys", `${digest}.json`);
166154
166605
  }
166155
166606
  function legacyKeyPath(idempotencyKey, dir) {
166156
166607
  const digest = createHash69("sha256").update(idempotencyKey, "utf8").digest("hex");
166157
- return path319.join(turnsRoot(dir), "keys", `${digest}.json`);
166608
+ return path320.join(turnsRoot(dir), "keys", `${digest}.json`);
166158
166609
  }
166159
166610
  function adoptLegacyClaim(project, idempotencyKey, dir) {
166160
166611
  const legacy = legacyKeyPath(idempotencyKey, dir);
@@ -166218,7 +166669,7 @@ function ensureTurnDir(turnId, dir) {
166218
166669
  }
166219
166670
  function createTurnRecord(record, dir) {
166220
166671
  ensureTurnDir(record.turnId, dir);
166221
- writeOwnerOnlyFile(path319.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
166672
+ writeOwnerOnlyFile(path320.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
166222
166673
  `);
166223
166674
  }
166224
166675
  function appendTurnEvent(event, dir, opts) {
@@ -166227,12 +166678,12 @@ function appendTurnEvent(event, dir, opts) {
166227
166678
  }
166228
166679
  const line = JSON.stringify(event);
166229
166680
  try {
166230
- appendOwnerOnlyLine(path319.join(turnDir(event.turnId, dir), "events.jsonl"), line);
166681
+ appendOwnerOnlyLine(path320.join(turnDir(event.turnId, dir), "events.jsonl"), line);
166231
166682
  } catch (error) {
166232
166683
  if (error?.code !== "ENOENT") {
166233
166684
  throw error;
166234
166685
  }
166235
- appendOwnerOnlyLine(path319.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
166686
+ appendOwnerOnlyLine(path320.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
166236
166687
  }
166237
166688
  return true;
166238
166689
  }
@@ -166240,7 +166691,7 @@ function readTurnEvents(turnId, after = -1, dir) {
166240
166691
  if (!isTurnId(turnId)) {
166241
166692
  return { ok: false, reason: "not-a-turn-id" };
166242
166693
  }
166243
- const read = readTurnFile(path319.join(turnDir(turnId, dir), "events.jsonl"));
166694
+ const read = readTurnFile(path320.join(turnDir(turnId, dir), "events.jsonl"));
166244
166695
  if (!read.ok) {
166245
166696
  if (isDefiniteAbsence2(read.reason)) {
166246
166697
  return { ok: true, value: [] };
@@ -166268,7 +166719,7 @@ function readTurnRecord(turnId, dir) {
166268
166719
  if (!isTurnId(turnId)) {
166269
166720
  return { ok: false, reason: "not-a-turn-id" };
166270
166721
  }
166271
- const read = readTurnFile(path319.join(turnDir(turnId, dir), "turn.json"));
166722
+ const read = readTurnFile(path320.join(turnDir(turnId, dir), "turn.json"));
166272
166723
  if (!read.ok) {
166273
166724
  return { ok: false, reason: read.reason };
166274
166725
  }
@@ -166287,7 +166738,7 @@ function finishTurn(turnId, result, dir) {
166287
166738
  if (!record.ok) {
166288
166739
  return false;
166289
166740
  }
166290
- writeOwnerOnlyFile(path319.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
166741
+ writeOwnerOnlyFile(path320.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
166291
166742
  `);
166292
166743
  return true;
166293
166744
  }
@@ -166334,7 +166785,7 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
166334
166785
  // src/lib/serve-turn.ts
166335
166786
  import { randomUUID as randomUUID44 } from "crypto";
166336
166787
  init_hooks();
166337
- import path320 from "path";
166788
+ import path321 from "path";
166338
166789
  init_service4();
166339
166790
  init_service9();
166340
166791
  init_impact_evidence_hook_adapter();
@@ -166405,9 +166856,9 @@ function isUuid(value) {
166405
166856
  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);
166406
166857
  }
166407
166858
  function resolveProject(declared, dir) {
166408
- const wanted = path320.resolve(declared);
166859
+ const wanted = path321.resolve(declared);
166409
166860
  for (const entry of listProjects(dir, () => {})) {
166410
- if (path320.resolve(entry.path) === wanted) {
166861
+ if (path321.resolve(entry.path) === wanted) {
166411
166862
  return { ok: true, project: entry.path };
166412
166863
  }
166413
166864
  }
@@ -166480,7 +166931,7 @@ function buildRemoteHookRuntime(opts) {
166480
166931
  });
166481
166932
  emitRemoteHookNotices(opts.projectRoot, formatHookLoadNotices(loaded, {
166482
166933
  projectRoot: opts.projectRoot,
166483
- userFile: path320.join(homeDir, ".keryx", "hooks.json"),
166934
+ userFile: path321.join(homeDir, ".keryx", "hooks.json"),
166484
166935
  surface: "headless"
166485
166936
  }));
166486
166937
  if (!loaded.ok) {
@@ -167529,9 +167980,9 @@ function printHelp23() {
167529
167980
  // src/commands/update.ts
167530
167981
  init_contained_write();
167531
167982
  import { spawn as spawn9 } from "child_process";
167532
- import { readFile as readFile164, readdir as readdir58 } from "fs/promises";
167533
- import { access as access4, constants as constants5, existsSync as existsSync50 } from "fs";
167534
- import path321 from "path";
167983
+ import { readFile as readFile164, readdir as readdir59 } from "fs/promises";
167984
+ import { access as access4, constants as constants5, existsSync as existsSync51 } from "fs";
167985
+ import path322 from "path";
167535
167986
  import { fileURLToPath as fileURLToPath10 } from "url";
167536
167987
  init_catalog2();
167537
167988
  init_model_choice();
@@ -167546,17 +167997,17 @@ init_ui();
167546
167997
  init_install_plan();
167547
167998
  init_templates3();
167548
167999
  function containFromMetaprojectPath(filePath) {
167549
- const marker = `${path321.sep}.metaproject${path321.sep}`;
168000
+ const marker = `${path322.sep}.metaproject${path322.sep}`;
167550
168001
  const idx = filePath.lastIndexOf(marker);
167551
168002
  if (idx < 0) {
167552
- if (filePath.endsWith(`${path321.sep}.metaproject`)) {
167553
- const root = filePath.slice(0, filePath.length - ".metaproject".length - path321.sep.length);
168003
+ if (filePath.endsWith(`${path322.sep}.metaproject`)) {
168004
+ const root = filePath.slice(0, filePath.length - ".metaproject".length - path322.sep.length);
167554
168005
  return { root, rel: ".metaproject" };
167555
168006
  }
167556
168007
  throw new Error(`${filePath}: expected a path under a .metaproject directory`);
167557
168008
  }
167558
168009
  const root = filePath.slice(0, idx);
167559
- const rel = filePath.slice(idx + path321.sep.length).split(path321.sep).join("/");
168010
+ const rel = filePath.slice(idx + path322.sep.length).split(path322.sep).join("/");
167560
168011
  return { root, rel };
167561
168012
  }
167562
168013
  async function updateCommand(args = []) {
@@ -167566,8 +168017,8 @@ async function updateCommand(args = []) {
167566
168017
  return;
167567
168018
  }
167568
168019
  const projectRoot = process.cwd();
167569
- const metaprojectRoot = path321.join(projectRoot, ".metaproject");
167570
- banner("keryx update", `Refreshing the .metaproject workspace in ${path321.basename(projectRoot)}/`);
168020
+ const metaprojectRoot = path322.join(projectRoot, ".metaproject");
168021
+ banner("keryx update", `Refreshing the .metaproject workspace in ${path322.basename(projectRoot)}/`);
167571
168022
  if (!await pathExists(metaprojectRoot)) {
167572
168023
  console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
167573
168024
  console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
@@ -167643,7 +168094,7 @@ async function updateCommand(args = []) {
167643
168094
  nextSteps(steps);
167644
168095
  }
167645
168096
  async function previewServiceFiles(projectRoot, options) {
167646
- const metaprojectRoot = path321.join(projectRoot, ".metaproject");
168097
+ const metaprojectRoot = path322.join(projectRoot, ".metaproject");
167647
168098
  const manifestState = await readManifest5(metaprojectRoot);
167648
168099
  const manifest = manifestState.manifest;
167649
168100
  const enableTasks = moduleEnabled2(manifest, "tasks") || !options.noTasks;
@@ -167672,13 +168123,13 @@ async function previewServiceFiles(projectRoot, options) {
167672
168123
  });
167673
168124
  }
167674
168125
  async function refreshServiceFiles(projectRoot, options) {
167675
- const metaprojectRoot = path321.join(projectRoot, ".metaproject");
168126
+ const metaprojectRoot = path322.join(projectRoot, ".metaproject");
167676
168127
  const manifestState = await readManifest5(metaprojectRoot);
167677
168128
  const manifest = manifestState.manifest;
167678
168129
  const recoveredManifest = !manifestState.exists || !manifestState.valid;
167679
168130
  if (manifestState.migrated) {
167680
168131
  {
167681
- const { root, rel } = containFromMetaprojectPath(path321.join(metaprojectRoot, "metaproject.json"));
168132
+ const { root, rel } = containFromMetaprojectPath(path322.join(metaprojectRoot, "metaproject.json"));
167682
168133
  await writeContained(root, rel, `${JSON.stringify(manifest, null, 2)}
167683
168134
  `);
167684
168135
  }
@@ -167718,9 +168169,9 @@ async function refreshServiceFiles(projectRoot, options) {
167718
168169
  enableSecurity,
167719
168170
  enableSac
167720
168171
  });
167721
- await writeTextIfChanged4(path321.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
167722
- await writeTextIfChanged4(path321.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
167723
- await writeTextIfChanged4(path321.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
168172
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
168173
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
168174
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
167724
168175
  await writeRoutingEntrypointPair(metaprojectRoot, {
167725
168176
  enableGdgraph,
167726
168177
  enableGdctx,
@@ -167744,7 +168195,7 @@ async function refreshServiceFiles(projectRoot, options) {
167744
168195
  }
167745
168196
  });
167746
168197
  console.log(` ${await describeModelChoiceStatus(projectRoot)}`);
167747
- await writeTextIfChanged4(path321.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
168198
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
167748
168199
  enableGdgraph,
167749
168200
  enableGdctx,
167750
168201
  enableGdwiki,
@@ -167756,7 +168207,7 @@ async function refreshServiceFiles(projectRoot, options) {
167756
168207
  enableSecurity,
167757
168208
  data: dashboardData
167758
168209
  }));
167759
- await writeTextIfMissing4(path321.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
168210
+ await writeTextIfMissing4(path322.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
167760
168211
  enableGdgraph,
167761
168212
  enableGdctx,
167762
168213
  enableGdwiki,
@@ -167769,9 +168220,9 @@ async function refreshServiceFiles(projectRoot, options) {
167769
168220
  }));
167770
168221
  if (enableGdgraph) {
167771
168222
  await installGdgraphCoreScripts2(metaprojectRoot);
167772
- await writeTextIfChanged4(path321.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
167773
- await writeTextIfChanged4(path321.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
167774
- await writeTextIfChanged4(path321.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
168223
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
168224
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
168225
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
167775
168226
  await seedAssetsLock(metaprojectRoot);
167776
168227
  if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
167777
168228
  const warning = await installManagedHookOrWarn(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
@@ -167780,15 +168231,15 @@ async function refreshServiceFiles(projectRoot, options) {
167780
168231
  }
167781
168232
  }
167782
168233
  if (enableGdctx) {
167783
- await writeTextIfMissing4(path321.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
167784
- await writeTextIfChanged4(path321.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
167785
- await writeTextIfChanged4(path321.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
167786
- await writeTextIfChanged4(path321.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
168234
+ await writeTextIfMissing4(path322.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
168235
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
168236
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
168237
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
167787
168238
  }
167788
168239
  if (enableGdwiki) {
167789
- await writeTextIfMissing4(path321.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
167790
- await writeTextIfChanged4(path321.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
167791
- await writeTextIfChanged4(path321.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
168240
+ await writeTextIfMissing4(path322.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
168241
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
168242
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
167792
168243
  if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
167793
168244
  const warning = await installManagedHookOrWarn(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
167794
168245
  if (warning)
@@ -167796,8 +168247,8 @@ async function refreshServiceFiles(projectRoot, options) {
167796
168247
  }
167797
168248
  }
167798
168249
  if (enableSac) {
167799
- await writeTextIfMissing4(path321.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
167800
- await writeTextIfChanged4(path321.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
168250
+ await writeTextIfMissing4(path322.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
168251
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
167801
168252
  }
167802
168253
  let gdskillsWarnings = [];
167803
168254
  let gdskillsNotices = [];
@@ -167812,10 +168263,10 @@ async function refreshServiceFiles(projectRoot, options) {
167812
168263
  }
167813
168264
  }
167814
168265
  if (enableHealth) {
167815
- await writeTextIfMissing4(path321.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
167816
- await writeTextIfChanged4(path321.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
167817
- await writeTextIfChanged4(path321.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
167818
- await writeTextIfChanged4(path321.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
168266
+ await writeTextIfMissing4(path322.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
168267
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
168268
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
168269
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
167819
168270
  if (manifest.modules?.health?.hooks?.gitPostCommit) {
167820
168271
  const warning = await installManagedHookOrWarn(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
167821
168272
  if (warning)
@@ -167823,16 +168274,16 @@ async function refreshServiceFiles(projectRoot, options) {
167823
168274
  }
167824
168275
  }
167825
168276
  if (enableTesting) {
167826
- await writeTextIfMissing4(path321.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
168277
+ await writeTextIfMissing4(path322.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
167827
168278
  postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
167828
168279
  prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
167829
168280
  }));
167830
- await writeTextIfChanged4(path321.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
167831
- await writeTextIfChanged4(path321.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
167832
- await writeTextIfChanged4(path321.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
168281
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
168282
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
168283
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
167833
168284
  if (enableGdwiki) {
167834
- await writeTextIfMissing4(path321.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
167835
- await writeTextIfMissing4(path321.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
168285
+ await writeTextIfMissing4(path322.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
168286
+ await writeTextIfMissing4(path322.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
167836
168287
  }
167837
168288
  if (manifest.modules?.testing?.hooks?.gitPostCommit) {
167838
168289
  const warning = await installManagedHookOrWarn(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
@@ -167851,24 +168302,24 @@ async function refreshServiceFiles(projectRoot, options) {
167851
168302
  hookWarnings.push(warning);
167852
168303
  }
167853
168304
  if (enableMemory) {
167854
- await writeTextIfMissing4(path321.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
167855
- await writeTextIfMissing4(path321.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
167856
- await writeTextIfChanged4(path321.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
167857
- await writeTextIfChanged4(path321.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
167858
- await writeTextIfChanged4(path321.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
168305
+ await writeTextIfMissing4(path322.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
168306
+ await writeTextIfMissing4(path322.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
168307
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
168308
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
168309
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
167859
168310
  }
167860
168311
  if (enableTasks) {
167861
- await writeTextIfChanged4(path321.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
167862
- await writeTextIfChanged4(path321.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
167863
- await writeTextIfChanged4(path321.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
167864
- await writeTextIfChanged4(path321.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
167865
- await writeTextIfChanged4(path321.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
167866
- await writeTextIfChanged4(path321.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
168312
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
168313
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
168314
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
168315
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
168316
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
168317
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
167867
168318
  }
167868
168319
  if (enableSecurity) {
167869
- await writeTextIfMissing4(path321.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
167870
- await writeTextIfChanged4(path321.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
167871
- await writeTextIfChanged4(path321.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
168320
+ await writeTextIfMissing4(path322.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
168321
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
168322
+ await writeTextIfChanged4(path322.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
167872
168323
  if (manifest.modules?.security?.hooks?.prePush) {
167873
168324
  const warning = await installManagedHookOrWarn(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
167874
168325
  if (warning)
@@ -167926,13 +168377,13 @@ async function refreshServiceFiles(projectRoot, options) {
167926
168377
  };
167927
168378
  }
167928
168379
  async function buildDashboard(projectRoot = process.cwd()) {
167929
- const metaprojectRoot = path321.join(projectRoot, ".metaproject");
168380
+ const metaprojectRoot = path322.join(projectRoot, ".metaproject");
167930
168381
  if (!await pathExists(metaprojectRoot)) {
167931
168382
  throw new Error("Metaproject is not initialized. Run: keryx init");
167932
168383
  }
167933
168384
  const manifest = (await readManifest5(metaprojectRoot)).manifest;
167934
168385
  const data = await collectDashboardData(metaprojectRoot);
167935
- const dashboardPath = path321.join(metaprojectRoot, "keryx-dashboard.html");
168386
+ const dashboardPath = path322.join(metaprojectRoot, "keryx-dashboard.html");
167936
168387
  await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
167937
168388
  enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
167938
168389
  enableGdctx: moduleEnabled2(manifest, "gdctx"),
@@ -167952,7 +168403,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
167952
168403
  if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
167953
168404
  return true;
167954
168405
  }
167955
- const hookPath = path321.join(projectRoot, ".git", "hooks", "post-commit");
168406
+ const hookPath = path322.join(projectRoot, ".git", "hooks", "post-commit");
167956
168407
  if (!await pathExists(hookPath)) {
167957
168408
  return false;
167958
168409
  }
@@ -167972,11 +168423,11 @@ async function collectDashboardData(metaprojectRoot) {
167972
168423
  if (testing) {
167973
168424
  data.testing = testing;
167974
168425
  }
167975
- const wiki = await collectMarkdownPages(path321.join(metaprojectRoot, "wiki"), "wiki");
168426
+ const wiki = await collectMarkdownPages(path322.join(metaprojectRoot, "wiki"), "wiki");
167976
168427
  if (wiki.length > 0) {
167977
168428
  data.wiki = { pages: wiki };
167978
168429
  }
167979
- const memory = await collectMarkdownPages(path321.join(metaprojectRoot, "memory"), "memory");
168430
+ const memory = await collectMarkdownPages(path322.join(metaprojectRoot, "memory"), "memory");
167980
168431
  if (memory.length > 0) {
167981
168432
  data.memory = { entries: memory };
167982
168433
  }
@@ -167991,19 +168442,19 @@ async function collectDashboardData(metaprojectRoot) {
167991
168442
  return data;
167992
168443
  }
167993
168444
  async function collectTasksDashboardData(metaprojectRoot) {
167994
- const flowsRoot = path321.join(metaprojectRoot, "flows");
168445
+ const flowsRoot = path322.join(metaprojectRoot, "flows");
167995
168446
  if (!await pathExists(flowsRoot)) {
167996
168447
  return null;
167997
168448
  }
167998
168449
  let dirEntries;
167999
168450
  try {
168000
- dirEntries = (await readdir58(flowsRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory() && /^\d{3}-/.test(entry.name)).map((entry) => entry.name).sort();
168451
+ dirEntries = (await readdir59(flowsRoot, { withFileTypes: true })).filter((entry) => entry.isDirectory() && /^\d{3}-/.test(entry.name)).map((entry) => entry.name).sort();
168001
168452
  } catch {
168002
168453
  return null;
168003
168454
  }
168004
168455
  const flows = [];
168005
168456
  for (const dir of dirEntries) {
168006
- const flowPath = path321.join(flowsRoot, dir, "flow.json");
168457
+ const flowPath = path322.join(flowsRoot, dir, "flow.json");
168007
168458
  if (!await pathExists(flowPath)) {
168008
168459
  continue;
168009
168460
  }
@@ -168011,7 +168462,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
168011
168462
  const flow = JSON.parse(await readFile164(flowPath, "utf8"));
168012
168463
  const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
168013
168464
  let acTotal = 0;
168014
- const acPath = path321.join(flowsRoot, dir, "acceptance-criteria.md");
168465
+ const acPath = path322.join(flowsRoot, dir, "acceptance-criteria.md");
168015
168466
  if (await pathExists(acPath)) {
168016
168467
  const acContent = await readFile164(acPath, "utf8");
168017
168468
  acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
@@ -168065,7 +168516,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
168065
168516
  "data/testing/context.md"
168066
168517
  ];
168067
168518
  for (const href of staticHrefs) {
168068
- const filePath = path321.join(metaprojectRoot, ...href.split("/"));
168519
+ const filePath = path322.join(metaprojectRoot, ...href.split("/"));
168069
168520
  if (!await pathExists(filePath)) {
168070
168521
  continue;
168071
168522
  }
@@ -168082,7 +168533,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
168082
168533
  return docs;
168083
168534
  }
168084
168535
  async function collectHealthDashboardData(metaprojectRoot) {
168085
- const reportPath = path321.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
168536
+ const reportPath = path322.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
168086
168537
  if (!await pathExists(reportPath)) {
168087
168538
  return;
168088
168539
  }
@@ -168191,8 +168642,8 @@ function metricToScope(metric) {
168191
168642
  };
168192
168643
  }
168193
168644
  async function collectGraphDashboardData(metaprojectRoot) {
168194
- const nodesPath = path321.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
168195
- const edgesPath = path321.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
168645
+ const nodesPath = path322.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
168646
+ const edgesPath = path322.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
168196
168647
  if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
168197
168648
  return;
168198
168649
  }
@@ -168243,8 +168694,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
168243
168694
  };
168244
168695
  }
168245
168696
  async function collectTestingDashboardData(metaprojectRoot) {
168246
- const reportPath = path321.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
168247
- const contextPath = path321.join(metaprojectRoot, "data", "testing", "context.md");
168697
+ const reportPath = path322.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
168698
+ const contextPath = path322.join(metaprojectRoot, "data", "testing", "context.md");
168248
168699
  if (await pathExists(reportPath)) {
168249
168700
  const report = JSON.parse(await readFile164(reportPath, "utf8"));
168250
168701
  const totalTests = numberOrUndefined(report.total);
@@ -168273,7 +168724,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
168273
168724
  const files = await listMarkdownFiles(root);
168274
168725
  const pages = [];
168275
168726
  for (const filePath of files.slice(0, 40)) {
168276
- const relativePath = path321.relative(root, filePath).split(path321.sep).join("/");
168727
+ const relativePath = path322.relative(root, filePath).split(path322.sep).join("/");
168277
168728
  if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
168278
168729
  continue;
168279
168730
  }
@@ -168291,10 +168742,10 @@ async function collectMarkdownPages(root, hrefPrefix) {
168291
168742
  return pages;
168292
168743
  }
168293
168744
  async function listMarkdownFiles(root) {
168294
- const entries = await readdir58(root, { withFileTypes: true });
168745
+ const entries = await readdir59(root, { withFileTypes: true });
168295
168746
  const files = [];
168296
168747
  for (const entry of entries) {
168297
- const fullPath = path321.join(root, entry.name);
168748
+ const fullPath = path322.join(root, entry.name);
168298
168749
  if (entry.isDirectory()) {
168299
168750
  files.push(...await listMarkdownFiles(fullPath));
168300
168751
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -168342,7 +168793,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
168342
168793
  const manifest = {
168343
168794
  schemaVersion: 1,
168344
168795
  standardVersion: STANDARD_VERSION,
168345
- name: `${path321.basename(path321.dirname(metaprojectRoot))}-metaproject`,
168796
+ name: `${path322.basename(path322.dirname(metaprojectRoot))}-metaproject`,
168346
168797
  createdBy: "keryx",
168347
168798
  profiles: computeProfiles(enabledModuleKeys),
168348
168799
  paths: {
@@ -168433,13 +168884,13 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
168433
168884
  }
168434
168885
  };
168435
168886
  {
168436
- const { root, rel } = containFromMetaprojectPath(path321.join(metaprojectRoot, "metaproject.json"));
168887
+ const { root, rel } = containFromMetaprojectPath(path322.join(metaprojectRoot, "metaproject.json"));
168437
168888
  await writeContained(root, rel, `${JSON.stringify(manifest, null, 2)}
168438
168889
  `);
168439
168890
  }
168440
168891
  }
168441
168892
  async function enableTasksInManifest(metaprojectRoot) {
168442
- const manifestPath = path321.join(metaprojectRoot, "metaproject.json");
168893
+ const manifestPath = path322.join(metaprojectRoot, "metaproject.json");
168443
168894
  if (!await pathExists(manifestPath)) {
168444
168895
  return;
168445
168896
  }
@@ -168465,7 +168916,7 @@ async function enableTasksInManifest(metaprojectRoot) {
168465
168916
  }
168466
168917
  }
168467
168918
  async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
168468
- const manifestPath = path321.join(metaprojectRoot, "metaproject.json");
168919
+ const manifestPath = path322.join(metaprojectRoot, "metaproject.json");
168469
168920
  if (!await pathExists(manifestPath)) {
168470
168921
  return;
168471
168922
  }
@@ -168533,61 +168984,61 @@ async function updateRuntime(projectRoot) {
168533
168984
  }
168534
168985
  }
168535
168986
  async function findRuntimeRoot(projectRoot) {
168536
- const projectRuntime = path321.join(projectRoot, ".metaproject", "runtime", "keryx");
168537
- if (await pathExists(path321.join(projectRuntime, ".git"))) {
168987
+ const projectRuntime = path322.join(projectRoot, ".metaproject", "runtime", "keryx");
168988
+ if (await pathExists(path322.join(projectRuntime, ".git"))) {
168538
168989
  return projectRuntime;
168539
168990
  }
168540
168991
  const home = process.env.HOME;
168541
168992
  if (!home) {
168542
168993
  return null;
168543
168994
  }
168544
- const globalRuntime = path321.join(home, ".keryx", "keryx");
168545
- if (await pathExists(path321.join(globalRuntime, ".git"))) {
168995
+ const globalRuntime = path322.join(home, ".keryx", "keryx");
168996
+ if (await pathExists(path322.join(globalRuntime, ".git"))) {
168546
168997
  return globalRuntime;
168547
168998
  }
168548
168999
  return null;
168549
169000
  }
168550
169001
  async function createServiceDirs(metaprojectRoot, modules) {
168551
169002
  const dirs = [
168552
- path321.join(metaprojectRoot, "core"),
168553
- path321.join(metaprojectRoot, "hooks", "post-update.d"),
168554
- path321.join(metaprojectRoot, "modules"),
168555
- path321.join(metaprojectRoot, "rules"),
168556
- path321.join(metaprojectRoot, "skills", "project-rules"),
169003
+ path322.join(metaprojectRoot, "core"),
169004
+ path322.join(metaprojectRoot, "hooks", "post-update.d"),
169005
+ path322.join(metaprojectRoot, "modules"),
169006
+ path322.join(metaprojectRoot, "rules"),
169007
+ path322.join(metaprojectRoot, "skills", "project-rules"),
168557
169008
  ...modules.enableGdgraph ? [
168558
- path321.join(metaprojectRoot, "core", "gdgraph"),
168559
- path321.join(metaprojectRoot, "skills", "gdgraph")
169009
+ path322.join(metaprojectRoot, "core", "gdgraph"),
169010
+ path322.join(metaprojectRoot, "skills", "gdgraph")
168560
169011
  ] : [],
168561
169012
  ...modules.enableGdctx ? [
168562
- path321.join(metaprojectRoot, "core", "gdctx"),
168563
- path321.join(metaprojectRoot, "skills", "gdctx")
169013
+ path322.join(metaprojectRoot, "core", "gdctx"),
169014
+ path322.join(metaprojectRoot, "skills", "gdctx")
168564
169015
  ] : [],
168565
169016
  ...modules.enableGdwiki ? [
168566
- path321.join(metaprojectRoot, "skills", "gdwiki"),
168567
- path321.join(metaprojectRoot, "wiki", "templates")
169017
+ path322.join(metaprojectRoot, "skills", "gdwiki"),
169018
+ path322.join(metaprojectRoot, "wiki", "templates")
168568
169019
  ] : [],
168569
169020
  ...modules.enableHealth ? [
168570
- path321.join(metaprojectRoot, "core", "health"),
168571
- path321.join(metaprojectRoot, "skills", "health")
169021
+ path322.join(metaprojectRoot, "core", "health"),
169022
+ path322.join(metaprojectRoot, "skills", "health")
168572
169023
  ] : [],
168573
169024
  ...modules.enableTesting ? [
168574
- path321.join(metaprojectRoot, "core", "testing"),
168575
- path321.join(metaprojectRoot, "skills", "testing")
169025
+ path322.join(metaprojectRoot, "core", "testing"),
169026
+ path322.join(metaprojectRoot, "skills", "testing")
168576
169027
  ] : [],
168577
169028
  ...modules.enableMemory ? [
168578
- path321.join(metaprojectRoot, "core", "memory"),
168579
- path321.join(metaprojectRoot, "skills", "memory"),
168580
- path321.join(metaprojectRoot, "memory", "templates")
169029
+ path322.join(metaprojectRoot, "core", "memory"),
169030
+ path322.join(metaprojectRoot, "skills", "memory"),
169031
+ path322.join(metaprojectRoot, "memory", "templates")
168581
169032
  ] : [],
168582
169033
  ...modules.enableTasks ? [
168583
- path321.join(metaprojectRoot, "flows"),
168584
- path321.join(metaprojectRoot, "skills", "flow")
169034
+ path322.join(metaprojectRoot, "flows"),
169035
+ path322.join(metaprojectRoot, "skills", "flow")
168585
169036
  ] : [],
168586
169037
  ...modules.enableSecurity ? [
168587
- path321.join(metaprojectRoot, "core", "security")
169038
+ path322.join(metaprojectRoot, "core", "security")
168588
169039
  ] : [],
168589
169040
  ...modules.enableSac ? [
168590
- path321.join(metaprojectRoot, "skills", "sac")
169041
+ path322.join(metaprojectRoot, "skills", "sac")
168591
169042
  ] : []
168592
169043
  ];
168593
169044
  await Promise.all(dirs.map((dir) => {
@@ -168596,22 +169047,22 @@ async function createServiceDirs(metaprojectRoot, modules) {
168596
169047
  }));
168597
169048
  }
168598
169049
  async function installGdgraphCoreScripts2(metaprojectRoot) {
168599
- const gdgraphCoreRoot = path321.join(metaprojectRoot, "core", "gdgraph");
169050
+ const gdgraphCoreRoot = path322.join(metaprojectRoot, "core", "gdgraph");
168600
169051
  {
168601
169052
  const { root, rel } = containFromMetaprojectPath(gdgraphCoreRoot);
168602
169053
  await mkdirContained(root, rel);
168603
169054
  }
168604
169055
  for (const file of GDGRAPH_CORE_SOURCES) {
168605
- await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path321.join(gdgraphCoreRoot, file));
169056
+ await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path322.join(gdgraphCoreRoot, file));
168606
169057
  }
168607
- await writeTextIfChanged4(path321.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
169058
+ await writeTextIfChanged4(path322.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
168608
169059
  }
168609
169060
  async function prePushHasSecurityBlock2(projectRoot) {
168610
169061
  const hooksRoot = await resolveGitHooksRoot(projectRoot);
168611
169062
  if (!hooksRoot) {
168612
169063
  return false;
168613
169064
  }
168614
- const hookPath = path321.join(hooksRoot, "pre-push");
169065
+ const hookPath = path322.join(hooksRoot, "pre-push");
168615
169066
  if (!await pathExists(hookPath)) {
168616
169067
  return false;
168617
169068
  }
@@ -168626,7 +169077,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
168626
169077
  return (await readFile164(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
168627
169078
  }
168628
169079
  async function readManifest5(metaprojectRoot) {
168629
- const manifestPath = path321.join(metaprojectRoot, "metaproject.json");
169080
+ const manifestPath = path322.join(metaprojectRoot, "metaproject.json");
168630
169081
  if (!await pathExists(manifestPath)) {
168631
169082
  return {
168632
169083
  exists: false,
@@ -168695,7 +169146,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
168695
169146
  }
168696
169147
  async function anyPathExists(root, candidates) {
168697
169148
  for (const candidate of candidates) {
168698
- if (await pathExists(path321.join(root, candidate))) {
169149
+ if (await pathExists(path322.join(root, candidate))) {
168699
169150
  return true;
168700
169151
  }
168701
169152
  }
@@ -168718,13 +169169,13 @@ function parseUpdateArgs(args) {
168718
169169
  };
168719
169170
  }
168720
169171
  async function runPostUpdateHooks(projectRoot) {
168721
- const hooksDir = path321.join(projectRoot, ".metaproject", "hooks", "post-update.d");
169172
+ const hooksDir = path322.join(projectRoot, ".metaproject", "hooks", "post-update.d");
168722
169173
  if (!await pathExists(hooksDir)) {
168723
169174
  return;
168724
169175
  }
168725
- const entries = (await readdir58(hooksDir)).sort();
169176
+ const entries = (await readdir59(hooksDir)).sort();
168726
169177
  for (const entry of entries) {
168727
- const hookPath = path321.join(hooksDir, entry);
169178
+ const hookPath = path322.join(hooksDir, entry);
168728
169179
  try {
168729
169180
  await accessExecutable(hookPath);
168730
169181
  } catch {
@@ -168785,12 +169236,12 @@ async function copyFileIfChanged2(from, to) {
168785
169236
  }
168786
169237
  function runtimeSourcePath2(relativePath) {
168787
169238
  const directPath = fileURLToPath10(new URL(relativePath, import.meta.url));
168788
- if (existsSync50(directPath)) {
169239
+ if (existsSync51(directPath)) {
168789
169240
  return directPath;
168790
169241
  }
168791
169242
  if (relativePath.startsWith("../")) {
168792
- const packagedSourcePath = path321.join(path321.dirname(fileURLToPath10(import.meta.url)), "..", "src", relativePath.slice(3));
168793
- if (existsSync50(packagedSourcePath)) {
169243
+ const packagedSourcePath = path322.join(path322.dirname(fileURLToPath10(import.meta.url)), "..", "src", relativePath.slice(3));
169244
+ if (existsSync51(packagedSourcePath)) {
168794
169245
  return packagedSourcePath;
168795
169246
  }
168796
169247
  }
@@ -168821,7 +169272,7 @@ function printHelp24() {
168821
169272
 
168822
169273
  // src/commands/dashboard.ts
168823
169274
  import { spawn as spawn10 } from "child_process";
168824
- import path322 from "path";
169275
+ import path323 from "path";
168825
169276
  init_args();
168826
169277
  init_ui();
168827
169278
  async function dashboardCommand(args = []) {
@@ -168833,7 +169284,7 @@ async function dashboardCommand(args = []) {
168833
169284
  }
168834
169285
  if (subcommand === "build") {
168835
169286
  const result = await buildDashboard();
168836
- const rel = path322.relative(process.cwd(), result.path);
169287
+ const rel = path323.relative(process.cwd(), result.path);
168837
169288
  console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
168838
169289
  note(`Open it: keryx dashboard open`);
168839
169290
  return;
@@ -168841,7 +169292,7 @@ async function dashboardCommand(args = []) {
168841
169292
  if (subcommand === "open") {
168842
169293
  const result = await buildDashboard();
168843
169294
  await openFile(result.path);
168844
- const rel = path322.relative(process.cwd(), result.path);
169295
+ const rel = path323.relative(process.cwd(), result.path);
168845
169296
  console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
168846
169297
  return;
168847
169298
  }
@@ -168892,7 +169343,7 @@ import { readFileSync as readFileSync27 } from "fs";
168892
169343
  import { randomUUID as randomUUID46 } from "crypto";
168893
169344
  import { mkdir as mkdir69 } from "fs/promises";
168894
169345
  import { tmpdir as tmpdir12 } from "os";
168895
- import path323 from "path";
169346
+ import path324 from "path";
168896
169347
  import { createInterface as createInterface3 } from "readline";
168897
169348
 
168898
169349
  // src/harness/external-agent-probe.ts
@@ -169154,7 +169605,7 @@ async function runCommand4(args, deps, log) {
169154
169605
  const requestApproval = unattended ? undefined : seams.requestApproval ?? terminalApprover();
169155
169606
  const timeoutRaw = optionValue(args, "--timeout");
169156
169607
  const timeoutMs = timeoutRaw !== undefined && Number.isInteger(Number(timeoutRaw)) && Number(timeoutRaw) > 0 ? Number(timeoutRaw) : gate.config.defaultTimeoutMs;
169157
- const worktreesDir = path323.join(tmpdir12(), "keryx-external-worktrees");
169608
+ const worktreesDir = path324.join(tmpdir12(), "keryx-external-worktrees");
169158
169609
  const worktree = seams.worktree ?? createGitWorktreePort({ repoRoot: cwd, worktreesDir });
169159
169610
  if (seams.worktree === undefined)
169160
169611
  await mkdir69(worktreesDir, { recursive: true });
@@ -169242,8 +169693,8 @@ init_service8();
169242
169693
  init_bundled_eval();
169243
169694
  init_args();
169244
169695
  init_ui();
169245
- import { existsSync as existsSync51, lstatSync as lstatSync7, readFileSync as readFileSync26, writeFileSync as writeFileSync11 } from "fs";
169246
- import path324 from "path";
169696
+ import { existsSync as existsSync52, lstatSync as lstatSync7, readFileSync as readFileSync26, writeFileSync as writeFileSync11 } from "fs";
169697
+ import path325 from "path";
169247
169698
  function resolveDeps(deps) {
169248
169699
  return {
169249
169700
  cwd: deps.cwd ?? process.cwd(),
@@ -169576,11 +170027,11 @@ function generateCommand(args, depsIn) {
169576
170027
  process.exitCode = 1;
169577
170028
  return;
169578
170029
  }
169579
- const bundledAgentsRoot = path324.join(depsIn.bundledRoot ?? defaultBundledRoot(), "agents");
169580
- const stacksRoot = path324.join(bundledAgentsRoot, "..", "stacks");
169581
- const packDir = path324.join(stacksRoot, stackId);
169582
- const packJsonPath = path324.join(packDir, "pack.json");
169583
- if (!existsSync51(packJsonPath)) {
170030
+ const bundledAgentsRoot = path325.join(depsIn.bundledRoot ?? defaultBundledRoot(), "agents");
170031
+ const stacksRoot = path325.join(bundledAgentsRoot, "..", "stacks");
170032
+ const packDir = path325.join(stacksRoot, stackId);
170033
+ const packJsonPath = path325.join(packDir, "pack.json");
170034
+ if (!existsSync52(packJsonPath)) {
169584
170035
  error(`No stack pack "${stackId}" found at ${packJsonPath}.`);
169585
170036
  process.exitCode = 1;
169586
170037
  return;
@@ -169626,9 +170077,9 @@ function generateCommand(args, depsIn) {
169626
170077
  }
169627
170078
  const prepared = [];
169628
170079
  for (const file of generatedFiles) {
169629
- const filePath = path324.join(bundledAgentsRoot, file.fileName);
169630
- const relative = path324.relative(bundledAgentsRoot, filePath);
169631
- if (relative.startsWith("..") || path324.isAbsolute(relative)) {
170080
+ const filePath = path325.join(bundledAgentsRoot, file.fileName);
170081
+ const relative = path325.relative(bundledAgentsRoot, filePath);
170082
+ if (relative.startsWith("..") || path325.isAbsolute(relative)) {
169632
170083
  error(`Refusing to write "${file.fileName}": it resolves outside the bundled agents directory.`);
169633
170084
  process.exitCode = 1;
169634
170085
  return;
@@ -169949,7 +170400,7 @@ function printBootstrapHelp() {
169949
170400
  // src/commands/metrics.ts
169950
170401
  init_args();
169951
170402
  import { readFile as readFile165 } from "fs/promises";
169952
- import path326 from "path";
170403
+ import path327 from "path";
169953
170404
 
169954
170405
  // src/metrics/benchmark.ts
169955
170406
  var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
@@ -170766,7 +171217,7 @@ function buildContainmentManifest(inputs, options = {}) {
170766
171217
 
170767
171218
  // src/metrics/oracle-runner.ts
170768
171219
  import { mkdir as mkdir70, writeFile as writeFile48 } from "fs/promises";
170769
- import path325 from "path";
171220
+ import path326 from "path";
170770
171221
 
170771
171222
  // src/metrics/ir.ts
170772
171223
  function toIdSet(ids) {
@@ -171212,9 +171663,9 @@ function buildEvidenceBundle(input, options = {}) {
171212
171663
  async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
171213
171664
  const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
171214
171665
  const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
171215
- const dir = path325.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
171666
+ const dir = path326.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
171216
171667
  await mkdir70(dir, { recursive: true });
171217
- const write = (name, value) => writeFile48(path325.join(dir, name), `${JSON.stringify(value, null, 2)}
171668
+ const write = (name, value) => writeFile48(path326.join(dir, name), `${JSON.stringify(value, null, 2)}
171218
171669
  `, "utf8");
171219
171670
  await Promise.all([
171220
171671
  write("inputs.json", bundle.inputs),
@@ -171254,7 +171705,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
171254
171705
  console.log("# metrics status");
171255
171706
  console.log("");
171256
171707
  console.log(`root: ${root}`);
171257
- console.log(`enabled: ${await Bun.file(path326.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
171708
+ console.log(`enabled: ${await Bun.file(path327.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
171258
171709
  const latest = await readLatestPointer(root);
171259
171710
  console.log(`latest: ${latest.status}`);
171260
171711
  return;
@@ -171266,7 +171717,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
171266
171717
  process.exitCode = 1;
171267
171718
  return;
171268
171719
  }
171269
- const record = JSON.parse(await readFile165(path326.resolve(projectRoot, file), "utf8"));
171720
+ const record = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
171270
171721
  const result = validateRunRecord(record);
171271
171722
  console.log(result.valid ? "valid: yes" : "valid: no");
171272
171723
  for (const error of result.errors)
@@ -171291,7 +171742,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
171291
171742
  process.exitCode = 1;
171292
171743
  return;
171293
171744
  }
171294
- const file = path326.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
171745
+ const file = path327.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
171295
171746
  if (!await Bun.file(file).exists()) {
171296
171747
  console.error(`Run not found: ${runId}`);
171297
171748
  process.exitCode = 1;
@@ -171308,8 +171759,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
171308
171759
  process.exitCode = 1;
171309
171760
  return;
171310
171761
  }
171311
- const a = JSON.parse(await readFile165(path326.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
171312
- const b = JSON.parse(await readFile165(path326.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
171762
+ const a = JSON.parse(await readFile165(path327.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
171763
+ const b = JSON.parse(await readFile165(path327.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
171313
171764
  const comparison = compareExecutionRuns(a, b);
171314
171765
  console.log(stableJson(comparison));
171315
171766
  return;
@@ -171343,8 +171794,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
171343
171794
  return;
171344
171795
  }
171345
171796
  const template = createPairedBenchmarkTemplate(taskIds);
171346
- await Bun.write(path326.resolve(projectRoot, out), stableJson(template));
171347
- console.log(`manifest: ${path326.relative(projectRoot, path326.resolve(projectRoot, out))}`);
171797
+ await Bun.write(path327.resolve(projectRoot, out), stableJson(template));
171798
+ console.log(`manifest: ${path327.relative(projectRoot, path327.resolve(projectRoot, out))}`);
171348
171799
  return;
171349
171800
  }
171350
171801
  if (subcommand === "benchmark" && args[1] === "run") {
@@ -171358,7 +171809,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
171358
171809
  process.exitCode = 1;
171359
171810
  return;
171360
171811
  }
171361
- const raw = JSON.parse(await readFile165(path326.resolve(projectRoot, file), "utf8"));
171812
+ const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
171362
171813
  const input = Array.isArray(raw) ? raw : raw.runs ?? [];
171363
171814
  const result = validatePairedBenchmark(input);
171364
171815
  console.log(stableJson(result));
@@ -171370,7 +171821,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
171370
171821
  process.exitCode = 1;
171371
171822
  }
171372
171823
  async function loadAffectedSets(projectRoot, file) {
171373
- const raw = JSON.parse(await readFile165(path326.resolve(projectRoot, file), "utf8"));
171824
+ const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
171374
171825
  const map = new Map;
171375
171826
  for (const entry of raw.targets ?? []) {
171376
171827
  if (typeof entry.target === "string")
@@ -171441,7 +171892,7 @@ async function runHarnessLayer(projectRoot, args, ladder) {
171441
171892
  let tasks;
171442
171893
  let model;
171443
171894
  try {
171444
- const raw = JSON.parse(await readFile165(path326.resolve(projectRoot, resultsPath), "utf8"));
171895
+ const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, resultsPath), "utf8"));
171445
171896
  tasks = raw.tasks ?? [];
171446
171897
  model = raw.model;
171447
171898
  } catch (error) {
@@ -171472,7 +171923,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args, ladder) {
171472
171923
  let cases;
171473
171924
  let model;
171474
171925
  try {
171475
- const raw = JSON.parse(await readFile165(path326.resolve(projectRoot, resultsPath), "utf8"));
171926
+ const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, resultsPath), "utf8"));
171476
171927
  cases = raw.cases ?? [];
171477
171928
  model = raw.model;
171478
171929
  } catch (error) {
@@ -171497,7 +171948,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args, ladder) {
171497
171948
  let cases;
171498
171949
  let model;
171499
171950
  try {
171500
- const raw = JSON.parse(await readFile165(path326.resolve(projectRoot, resultsPath), "utf8"));
171951
+ const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, resultsPath), "utf8"));
171501
171952
  cases = raw.cases ?? [];
171502
171953
  model = raw.model;
171503
171954
  } catch (error) {
@@ -171522,7 +171973,7 @@ async function runSafetyContainmentLayer(projectRoot, args, ladder, caseClass) {
171522
171973
  let cases;
171523
171974
  let model;
171524
171975
  try {
171525
- const raw = JSON.parse(await readFile165(path326.resolve(projectRoot, resultsPath), "utf8"));
171976
+ const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, resultsPath), "utf8"));
171526
171977
  cases = raw.cases ?? [];
171527
171978
  model = raw.model;
171528
171979
  } catch (error) {
@@ -171584,12 +172035,12 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
171584
172035
  }
171585
172036
  const manifests = buildOracleManifestsByGold(inputs, { ladder });
171586
172037
  if (outDir) {
171587
- const resolvedOut = path326.resolve(projectRoot, outDir);
172038
+ const resolvedOut = path327.resolve(projectRoot, outDir);
171588
172039
  for (const input of inputs) {
171589
172040
  for (const named of input.golds) {
171590
172041
  const bundle = buildEvidenceBundle({ target: input.target, system: input.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
171591
- const dir = await persistEvidenceBundle(path326.join(resolvedOut, named.kind), bundle, ladder);
171592
- console.error(`bundle[${named.kind}]: ${path326.relative(projectRoot, dir)}`);
172042
+ const dir = await persistEvidenceBundle(path327.join(resolvedOut, named.kind), bundle, ladder);
172043
+ console.error(`bundle[${named.kind}]: ${path327.relative(projectRoot, dir)}`);
171593
172044
  }
171594
172045
  }
171595
172046
  }
@@ -171613,7 +172064,7 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
171613
172064
  return allValid;
171614
172065
  }
171615
172066
  async function loadCoverageMap2(projectRoot, file) {
171616
- const raw = JSON.parse(await readFile165(path326.resolve(projectRoot, file), "utf8"));
172067
+ const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
171617
172068
  return raw.coverageMap ?? {};
171618
172069
  }
171619
172070
  async function runTestingLayer(projectRoot, args, ladder) {
@@ -171650,7 +172101,7 @@ async function runTestingLayer(projectRoot, args, ladder) {
171650
172101
  return result.valid;
171651
172102
  }
171652
172103
  async function loadMemoryGoldK(projectRoot, file) {
171653
- const raw = JSON.parse(await readFile165(path326.resolve(projectRoot, file), "utf8"));
172104
+ const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
171654
172105
  return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
171655
172106
  }
171656
172107
  async function runMemoryLayer(projectRoot, args, ladder) {
@@ -171687,11 +172138,11 @@ async function runMemoryLayer(projectRoot, args, ladder) {
171687
172138
  return result.valid;
171688
172139
  }
171689
172140
  async function loadWikiGoldK(projectRoot, file) {
171690
- const raw = JSON.parse(await readFile165(path326.resolve(projectRoot, file), "utf8"));
172141
+ const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
171691
172142
  return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
171692
172143
  }
171693
172144
  async function loadWikiGroundedness(projectRoot, file) {
171694
- const raw = JSON.parse(await readFile165(path326.resolve(projectRoot, file), "utf8"));
172145
+ const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
171695
172146
  const map = new Map;
171696
172147
  for (const entry of raw.targets ?? []) {
171697
172148
  if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
@@ -171747,7 +172198,7 @@ async function runWikiLayer(projectRoot, args, ladder) {
171747
172198
  return result.valid;
171748
172199
  }
171749
172200
  async function loadGdctxFacts(projectRoot, file) {
171750
- const raw = JSON.parse(await readFile165(path326.resolve(projectRoot, file), "utf8"));
172201
+ const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, file), "utf8"));
171751
172202
  const inputs = [];
171752
172203
  for (const entry of raw.inputs ?? []) {
171753
172204
  if (typeof entry.input === "string") {
@@ -171785,7 +172236,7 @@ async function collect(projectRoot, args) {
171785
172236
  process.exitCode = 1;
171786
172237
  return;
171787
172238
  }
171788
- const raw = JSON.parse(await readFile165(path326.resolve(projectRoot, eventFile), "utf8"));
172239
+ const raw = JSON.parse(await readFile165(path327.resolve(projectRoot, eventFile), "utf8"));
171789
172240
  const events = Array.isArray(raw) ? raw : raw.events;
171790
172241
  const startedAt = optionValue(args, "--started-at") ?? events[0]?.timestamp_utc ?? new Date().toISOString();
171791
172242
  const finishedAt = optionValue(args, "--finished-at") ?? events.at(-1)?.timestamp_utc ?? startedAt;
@@ -171801,11 +172252,11 @@ async function collect(projectRoot, args) {
171801
172252
  parentRunId: optionValue(args, "--parent-run-id") ?? null
171802
172253
  });
171803
172254
  const result = await writeRunArtifacts(metricsRoot(projectRoot), record, { cwd: projectRoot });
171804
- console.log(`json: ${path326.relative(projectRoot, result.jsonPath)}`);
171805
- console.log(`markdown: ${path326.relative(projectRoot, result.markdownPath)}`);
172255
+ console.log(`json: ${path327.relative(projectRoot, result.jsonPath)}`);
172256
+ console.log(`markdown: ${path327.relative(projectRoot, result.markdownPath)}`);
171806
172257
  }
171807
172258
  function metricsRoot(projectRoot) {
171808
- return path326.join(projectRoot, ".metaproject", "data", "metrics");
172259
+ return path327.join(projectRoot, ".metaproject", "data", "metrics");
171809
172260
  }
171810
172261
  function printMetricsHelp() {
171811
172262
  console.log(`keryx metrics
@@ -173222,8 +173673,8 @@ init_contained_write();
173222
173673
  init_prompt();
173223
173674
  init_terminal_safe();
173224
173675
  init_agent_hooks();
173225
- import { existsSync as existsSync52, readFileSync as readFileSync28 } from "fs";
173226
- import path327 from "path";
173676
+ import { existsSync as existsSync53, readFileSync as readFileSync28 } from "fs";
173677
+ import path328 from "path";
173227
173678
  init_hooks();
173228
173679
  init_package();
173229
173680
  var CLI_VERSION = package_default.version;
@@ -173239,10 +173690,10 @@ function optConfigDir(deps) {
173239
173690
  return deps.configDir !== undefined ? { configDir: deps.configDir } : {};
173240
173691
  }
173241
173692
  function userHooksPath(homeDir) {
173242
- return path327.join(homeDir, ".keryx", "hooks.json");
173693
+ return path328.join(homeDir, ".keryx", "hooks.json");
173243
173694
  }
173244
173695
  function projectHooksPath(projectRoot) {
173245
- return path327.join(projectRoot, ".metaproject", "hooks.json");
173696
+ return path328.join(projectRoot, ".metaproject", "hooks.json");
173246
173697
  }
173247
173698
  function fail3(message) {
173248
173699
  console.error(message);
@@ -173363,10 +173814,10 @@ async function runList6(args, deps) {
173363
173814
  function argv0Resolves(argv0) {
173364
173815
  if (argv0 === "keryx")
173365
173816
  return true;
173366
- if (path327.isAbsolute(argv0))
173367
- return existsSync52(argv0);
173817
+ if (path328.isAbsolute(argv0))
173818
+ return existsSync53(argv0);
173368
173819
  const pathEnv = process.env.PATH ?? "";
173369
- return pathEnv.split(path327.delimiter).filter((dir) => dir.length > 0).some((dir) => existsSync52(path327.join(dir, argv0)));
173820
+ return pathEnv.split(path328.delimiter).filter((dir) => dir.length > 0).some((dir) => existsSync53(path328.join(dir, argv0)));
173370
173821
  }
173371
173822
  function checkArgvResolutions(registrations) {
173372
173823
  const seen = new Set;
@@ -173821,8 +174272,8 @@ async function writeDocAtomic(filePath, doc, scope, cwd, homeDir) {
173821
174272
  if (scope === "project") {
173822
174273
  await writeContained(cwd, PROJECT_HOOKS_REL, body);
173823
174274
  } else {
173824
- const userConfigDir = path327.join(homeDir, ".keryx");
173825
- if (!existsSync52(userConfigDir)) {
174275
+ const userConfigDir = path328.join(homeDir, ".keryx");
174276
+ if (!existsSync53(userConfigDir)) {
173826
174277
  await mkdirContained(homeDir, ".keryx");
173827
174278
  }
173828
174279
  await writeContained(userConfigDir, "hooks.json", body);
@@ -174079,7 +174530,7 @@ init_ui();
174079
174530
  init_args();
174080
174531
  init_contained_path();
174081
174532
  init_package();
174082
- import path336 from "path";
174533
+ import path337 from "path";
174083
174534
 
174084
174535
  // src/bundle/types.ts
174085
174536
  var BUNDLE_FORMAT_VERSION = "1.0.0";
@@ -174133,13 +174584,13 @@ init_contained_path();
174133
174584
  init_keryx_home();
174134
174585
  init_types3();
174135
174586
  import { lstat as lstat18, readFile as readFile166 } from "fs/promises";
174136
- import path328 from "path";
174587
+ import path329 from "path";
174137
174588
  var MEMORY_FOLDERS = new Set(MEMORY_TYPES.map((entry) => entry.folder));
174138
174589
  function scopeRoot(scope, ctx) {
174139
174590
  if (scope === "user") {
174140
174591
  return userStoreRoot(ctx.env ?? process.env, ctx.homeDir);
174141
174592
  }
174142
- return path328.join(ctx.projectRoot, ".metaproject");
174593
+ return path329.join(ctx.projectRoot, ".metaproject");
174143
174594
  }
174144
174595
  var CONTROL_CHAR_RE = /[\x00-\x1f\x7f\u2028\u2029]/;
174145
174596
  var MARKER_FORGING_RE = /[<>`]|<!--|-->/;
@@ -174328,10 +174779,10 @@ async function targetFor(entry, targetScope, ctx) {
174328
174779
  return { ok: true, absolutePath: containment.absolutePath };
174329
174780
  }
174330
174781
  async function resolveContainedPathForWrite(root, relPath) {
174331
- const absolute = path328.resolve(root, relPath);
174332
- const rootResolved = path328.resolve(root);
174333
- const relative = path328.relative(rootResolved, absolute);
174334
- if (relative === "" || relative === ".." || relative.startsWith(`..${path328.sep}`) || path328.isAbsolute(relative)) {
174782
+ const absolute = path329.resolve(root, relPath);
174783
+ const rootResolved = path329.resolve(root);
174784
+ const relative = path329.relative(rootResolved, absolute);
174785
+ if (relative === "" || relative === ".." || relative.startsWith(`..${path329.sep}`) || path329.isAbsolute(relative)) {
174335
174786
  return {
174336
174787
  ok: false,
174337
174788
  refusal: { reason: BUNDLE_REFUSAL.pathEscape, path: relPath, message: `${relPath} resolves outside the scope root` }
@@ -174340,7 +174791,7 @@ async function resolveContainedPathForWrite(root, relPath) {
174340
174791
  return { ok: true, absolutePath: absolute };
174341
174792
  }
174342
174793
  async function refuseSymlinkChain(root, relPath) {
174343
- const rootResolved = path328.resolve(root);
174794
+ const rootResolved = path329.resolve(root);
174344
174795
  try {
174345
174796
  const rootStat = await lstat18(rootResolved);
174346
174797
  if (rootStat.isSymbolicLink()) {
@@ -174353,7 +174804,7 @@ async function refuseSymlinkChain(root, relPath) {
174353
174804
  const segments = relPath.split("/");
174354
174805
  let current = rootResolved;
174355
174806
  for (const segment of segments) {
174356
- current = path328.join(current, segment);
174807
+ current = path329.join(current, segment);
174357
174808
  try {
174358
174809
  const stat = await lstat18(current);
174359
174810
  if (stat.isSymbolicLink()) {
@@ -174381,8 +174832,8 @@ async function readTargetFile(absolutePath) {
174381
174832
  }
174382
174833
  }
174383
174834
  // src/bundle/archive.ts
174384
- import { readdir as readdir59, readFile as readFile167, lstat as lstat19 } from "fs/promises";
174385
- import path329 from "path";
174835
+ import { readdir as readdir60, readFile as readFile167, lstat as lstat19 } from "fs/promises";
174836
+ import path330 from "path";
174386
174837
  import { gzipSync, gunzipSync } from "zlib";
174387
174838
  var BLOCK = 512;
174388
174839
  var BUNDLE_MANIFEST_NAME = "bundle.json";
@@ -174473,11 +174924,11 @@ function buildBundleArchive(files, manifestBytes) {
174473
174924
  const dirs = new Set;
174474
174925
  const sortedPaths = [...files.keys()].sort();
174475
174926
  for (const p of sortedPaths) {
174476
- let dir = path329.dirname(p);
174927
+ let dir = path330.dirname(p);
174477
174928
  const segs = [];
174478
174929
  while (dir !== "." && dir !== "/" && dir !== "") {
174479
174930
  segs.unshift(dir);
174480
- dir = path329.dirname(dir);
174931
+ dir = path330.dirname(dir);
174481
174932
  }
174482
174933
  for (const d of segs)
174483
174934
  dirs.add(d);
@@ -174545,7 +174996,7 @@ function parseUstar(tar, limits) {
174545
174996
  const name = prefix.length > 0 ? `${prefix}/${nameRaw}` : nameRaw;
174546
174997
  const typeflag = String.fromCharCode(header[156] ?? 0);
174547
174998
  const size = readOctal(header, 124, 12);
174548
- if (path329.isAbsolute(name) || name.split("/").includes("..") || name.length === 0) {
174999
+ if (path330.isAbsolute(name) || name.split("/").includes("..") || name.length === 0) {
174549
175000
  return { ok: false, refusal: { reason: BUNDLE_REFUSAL.archiveInvalid, path: name, message: `unsafe entry name: ${name}` } };
174550
175001
  }
174551
175002
  const isDir = typeflag === "5";
@@ -174607,9 +175058,9 @@ async function openBundle(bundlePath, limits) {
174607
175058
  let entryCount = 0;
174608
175059
  let totalBytes = 0;
174609
175060
  async function walk(dir, relPrefix) {
174610
- const entries = await readdir59(dir, { withFileTypes: true });
175061
+ const entries = await readdir60(dir, { withFileTypes: true });
174611
175062
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
174612
- const abs = path329.join(dir, entry.name);
175063
+ const abs = path330.join(dir, entry.name);
174613
175064
  const rel = relPrefix.length > 0 ? `${relPrefix}/${entry.name}` : entry.name;
174614
175065
  const entryStat = await lstat19(abs);
174615
175066
  if (entryStat.isSymbolicLink()) {
@@ -174996,9 +175447,9 @@ init_validator();
174996
175447
  init_frontmatter();
174997
175448
  init_types3();
174998
175449
  import { execFileSync as execFileSync3 } from "child_process";
174999
- import { mkdir as mkdir71, readdir as readdir60, readFile as readFile168, writeFile as writeFile50, lstat as lstat20 } from "fs/promises";
175000
- import { existsSync as existsSync53 } from "fs";
175001
- import path330 from "path";
175450
+ import { mkdir as mkdir71, readdir as readdir61, readFile as readFile168, writeFile as writeFile50, lstat as lstat20 } from "fs/promises";
175451
+ import { existsSync as existsSync54 } from "fs";
175452
+ import path331 from "path";
175002
175453
  // docs/requirements/keryx-agent-platform-expansion/schemas/hook-config.schema.json
175003
175454
  var hook_config_schema_default2 = {
175004
175455
  $schema: "https://json-schema.org/draft/2020-12/schema",
@@ -175542,12 +175993,12 @@ function contentDigestBundleId(scope, entries) {
175542
175993
  async function walkFiles2(dir, relPrefix, out, skipped) {
175543
175994
  let entries;
175544
175995
  try {
175545
- entries = await readdir60(dir, { withFileTypes: true });
175996
+ entries = await readdir61(dir, { withFileTypes: true });
175546
175997
  } catch {
175547
175998
  return;
175548
175999
  }
175549
176000
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
175550
- const abs = path330.join(dir, entry.name);
176001
+ const abs = path331.join(dir, entry.name);
175551
176002
  const rel = relPrefix.length > 0 ? `${relPrefix}/${entry.name}` : entry.name;
175552
176003
  const st = await lstat20(abs);
175553
176004
  if (st.isSymbolicLink()) {
@@ -175564,8 +176015,8 @@ async function walkFiles2(dir, relPrefix, out, skipped) {
175564
176015
  async function collectSkills(root, scope, skipped) {
175565
176016
  const candidates = [];
175566
176017
  for (const baseDirName of scope === "project" ? ["skills", "project-skills"] : ["skills"]) {
175567
- const base = path330.join(root, baseDirName);
175568
- if (!existsSync53(base))
176018
+ const base = path331.join(root, baseDirName);
176019
+ if (!existsSync54(base))
175569
176020
  continue;
175570
176021
  const files = [];
175571
176022
  await walkFiles2(base, baseDirName, files, skipped);
@@ -175577,26 +176028,26 @@ async function collectSkills(root, scope, skipped) {
175577
176028
  async function collectRules(root, scope, skipped) {
175578
176029
  if (scope === "user")
175579
176030
  return [];
175580
- const base = path330.join(root, "rules");
175581
- if (!existsSync53(base))
176031
+ const base = path331.join(root, "rules");
176032
+ if (!existsSync54(base))
175582
176033
  return [];
175583
176034
  const files = [];
175584
176035
  await walkFiles2(base, "rules", files, skipped);
175585
176036
  return files.filter((f) => /\.(md|mdc)$/.test(f.path)).map((f) => ({ bundlePath: f.path, kind: "rule", absolutePath: f.abs }));
175586
176037
  }
175587
176038
  async function collectAgents(root, skipped) {
175588
- const base = path330.join(root, "agents");
175589
- if (!existsSync53(base))
176039
+ const base = path331.join(root, "agents");
176040
+ if (!existsSync54(base))
175590
176041
  return [];
175591
176042
  let entries;
175592
176043
  try {
175593
- entries = await readdir60(base, { withFileTypes: true });
176044
+ entries = await readdir61(base, { withFileTypes: true });
175594
176045
  } catch {
175595
176046
  return [];
175596
176047
  }
175597
176048
  const candidates = [];
175598
176049
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
175599
- const abs = path330.join(base, entry.name);
176050
+ const abs = path331.join(base, entry.name);
175600
176051
  const st = await lstat20(abs);
175601
176052
  if (st.isSymbolicLink()) {
175602
176053
  skipped.push({ path: `agents/${entry.name}`, reason: "symlink" });
@@ -175611,17 +176062,17 @@ async function collectAgents(root, skipped) {
175611
176062
  async function collectMemory(root, skipped) {
175612
176063
  const candidates = [];
175613
176064
  for (const { folder } of MEMORY_TYPES) {
175614
- const base = path330.join(root, "memory", folder);
175615
- if (!existsSync53(base))
176065
+ const base = path331.join(root, "memory", folder);
176066
+ if (!existsSync54(base))
175616
176067
  continue;
175617
176068
  let entries;
175618
176069
  try {
175619
- entries = await readdir60(base, { withFileTypes: true });
176070
+ entries = await readdir61(base, { withFileTypes: true });
175620
176071
  } catch {
175621
176072
  continue;
175622
176073
  }
175623
176074
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
175624
- const abs = path330.join(base, entry.name);
176075
+ const abs = path331.join(base, entry.name);
175625
176076
  const st = await lstat20(abs);
175626
176077
  if (st.isSymbolicLink()) {
175627
176078
  skipped.push({ path: `memory/${folder}/${entry.name}`, reason: "symlink" });
@@ -175635,8 +176086,8 @@ async function collectMemory(root, skipped) {
175635
176086
  return candidates;
175636
176087
  }
175637
176088
  async function collectHookConfig(root) {
175638
- const abs = path330.join(root, "hooks.json");
175639
- if (!existsSync53(abs))
176089
+ const abs = path331.join(root, "hooks.json");
176090
+ if (!existsSync54(abs))
175640
176091
  return [];
175641
176092
  const st = await lstat20(abs);
175642
176093
  if (st.isSymbolicLink() || !st.isFile())
@@ -175645,19 +176096,19 @@ async function collectHookConfig(root) {
175645
176096
  }
175646
176097
  async function collectLearnedPatterns(root, scope) {
175647
176098
  const relDir = scope === "user" ? "learning/patterns" : "data/learning/candidates";
175648
- const base = path330.join(root, ...relDir.split("/"));
175649
- if (!existsSync53(base))
176099
+ const base = path331.join(root, ...relDir.split("/"));
176100
+ if (!existsSync54(base))
175650
176101
  return [];
175651
176102
  let entries;
175652
176103
  try {
175653
- entries = await readdir60(base, { withFileTypes: true });
176104
+ entries = await readdir61(base, { withFileTypes: true });
175654
176105
  } catch {
175655
176106
  return [];
175656
176107
  }
175657
176108
  const candidates = [];
175658
176109
  for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
175659
176110
  if (entry.isFile() && entry.name.endsWith(".json")) {
175660
- candidates.push({ bundlePath: `${relDir}/${entry.name}`, kind: "learned-pattern", absolutePath: path330.join(base, entry.name) });
176111
+ candidates.push({ bundlePath: `${relDir}/${entry.name}`, kind: "learned-pattern", absolutePath: path331.join(base, entry.name) });
175661
176112
  }
175662
176113
  }
175663
176114
  return candidates;
@@ -175690,7 +176141,7 @@ function rewriteAgentOrigin(content, sourceRef) {
175690
176141
  }
175691
176142
  async function outDirIsEmpty(out) {
175692
176143
  try {
175693
- const entries = await readdir60(out);
176144
+ const entries = await readdir61(out);
175694
176145
  return entries.length === 0;
175695
176146
  } catch {
175696
176147
  return true;
@@ -175698,10 +176149,10 @@ async function outDirIsEmpty(out) {
175698
176149
  }
175699
176150
  async function writeDirectoryBundle(out, manifestBytes, files) {
175700
176151
  await mkdir71(out, { recursive: true });
175701
- await writeFile50(path330.join(out, "bundle.json"), manifestBytes);
176152
+ await writeFile50(path331.join(out, "bundle.json"), manifestBytes);
175702
176153
  for (const [rel, bytes] of files) {
175703
- const abs = path330.join(out, ...rel.split("/"));
175704
- await mkdir71(path330.dirname(abs), { recursive: true });
176154
+ const abs = path331.join(out, ...rel.split("/"));
176155
+ await mkdir71(path331.dirname(abs), { recursive: true });
175705
176156
  await writeFile50(abs, bytes);
175706
176157
  }
175707
176158
  }
@@ -175829,16 +176280,16 @@ async function exportBundle(opts) {
175829
176280
  const manifestBytes = Buffer.from(serializeManifest(manifest), "utf8");
175830
176281
  const isArchive = opts.out.endsWith(".tar.gz") || opts.out.endsWith(".tgz");
175831
176282
  if (isArchive) {
175832
- if (existsSync53(opts.out)) {
176283
+ if (existsSync54(opts.out)) {
175833
176284
  return { ok: false, refusals: [{ reason: BUNDLE_REFUSAL.notABundle, message: `${opts.out} already exists` }] };
175834
176285
  }
175835
176286
  const archive = buildBundleArchive(files, manifestBytes);
175836
176287
  if (!archive.ok)
175837
176288
  return { ok: false, refusals: [archive.refusal] };
175838
- await mkdir71(path330.dirname(opts.out), { recursive: true });
176289
+ await mkdir71(path331.dirname(opts.out), { recursive: true });
175839
176290
  await writeFile50(opts.out, archive.value);
175840
176291
  } else {
175841
- if (existsSync53(opts.out) && !await outDirIsEmpty(opts.out)) {
176292
+ if (existsSync54(opts.out) && !await outDirIsEmpty(opts.out)) {
175842
176293
  return { ok: false, refusals: [{ reason: BUNDLE_REFUSAL.notABundle, message: `${opts.out} already exists and is not empty` }] };
175843
176294
  }
175844
176295
  await writeDirectoryBundle(opts.out, manifestBytes, files);
@@ -175885,16 +176336,16 @@ async function verifyBundlePath(bundlePath) {
175885
176336
  // src/bundle/applied-state.ts
175886
176337
  init_contained_write();
175887
176338
  import { readFile as readFile169 } from "fs/promises";
175888
- import path331 from "path";
176339
+ import path332 from "path";
175889
176340
  var APPLIED_STATE_SCHEMA_VERSION = 2;
175890
176341
  function emptyState() {
175891
176342
  return { schemaVersion: APPLIED_STATE_SCHEMA_VERSION, entries: {} };
175892
176343
  }
175893
176344
  function appliedStatePath(scope, ctx) {
175894
176345
  if (scope === "user") {
175895
- return path331.join(scopeRoot("user", ctx), "bundles", "applied-state.json");
176346
+ return path332.join(scopeRoot("user", ctx), "bundles", "applied-state.json");
175896
176347
  }
175897
- return path331.join(scopeRoot(scope, ctx), "data", "bundles", "applied-state.json");
176348
+ return path332.join(scopeRoot(scope, ctx), "data", "bundles", "applied-state.json");
175898
176349
  }
175899
176350
  function isPlainEntryShape(value) {
175900
176351
  if (typeof value !== "object" || value === null)
@@ -175976,14 +176427,14 @@ async function readAppliedState(ledgerPath) {
175976
176427
  return { ok: false, reason: "corrupt-ledger", message: `applied-state ledger at ${ledgerPath} has an unrecognized shape` };
175977
176428
  }
175978
176429
  function containedRootAndRel(ledgerPath) {
175979
- const marker = `${path331.sep}.metaproject${path331.sep}`;
176430
+ const marker = `${path332.sep}.metaproject${path332.sep}`;
175980
176431
  const idx = ledgerPath.indexOf(marker);
175981
176432
  if (idx >= 0) {
175982
- return { root: ledgerPath.slice(0, idx), rel: ledgerPath.slice(idx + path331.sep.length).split(path331.sep).join("/") };
176433
+ return { root: ledgerPath.slice(0, idx), rel: ledgerPath.slice(idx + path332.sep.length).split(path332.sep).join("/") };
175983
176434
  }
175984
- const bundlesDir = path331.dirname(ledgerPath);
175985
- const root = path331.dirname(bundlesDir);
175986
- const rel = path331.join(path331.basename(bundlesDir), path331.basename(ledgerPath)).split(path331.sep).join("/");
176435
+ const bundlesDir = path332.dirname(ledgerPath);
176436
+ const root = path332.dirname(bundlesDir);
176437
+ const rel = path332.join(path332.basename(bundlesDir), path332.basename(ledgerPath)).split(path332.sep).join("/");
175987
176438
  return { root, rel };
175988
176439
  }
175989
176440
  async function writeAppliedState(ledgerPath, state) {
@@ -176004,17 +176455,17 @@ init_service8();
176004
176455
  init_fs();
176005
176456
  init_symlink_safety();
176006
176457
  import { lstat as lstat21, mkdir as mkdir72, open as open8, readFile as readFile170 } from "fs/promises";
176007
- import path332 from "path";
176458
+ import path333 from "path";
176008
176459
  var PRIVATE_DIR_GITIGNORE = `# keryx:private-dir \u2014 managed by keryx; this directory stays out of version control
176009
176460
  *
176010
176461
  !.gitignore
176011
176462
  `;
176012
176463
  async function checkPrivateDirGitignore(dir, root) {
176013
176464
  if (root !== undefined) {
176014
- const rootResolved = path332.resolve(root);
176015
- const dirResolved = path332.resolve(dir);
176016
- const relative = path332.relative(rootResolved, dirResolved);
176017
- const relativePosix = relative.split(path332.sep).join("/");
176465
+ const rootResolved = path333.resolve(root);
176466
+ const dirResolved = path333.resolve(dir);
176467
+ const relative = path333.relative(rootResolved, dirResolved);
176468
+ const relativePosix = relative.split(path333.sep).join("/");
176018
176469
  const refusal = await refuseEscapingSymlink(rootResolved, relativePosix);
176019
176470
  if (refusal !== undefined) {
176020
176471
  return {
@@ -176044,7 +176495,7 @@ async function checkPrivateDirGitignore(dir, root) {
176044
176495
  message: `${dir} is a symlink; keryx will not manage a private directory reached through a symlink.`
176045
176496
  };
176046
176497
  }
176047
- const gitignorePath = path332.join(dir, ".gitignore");
176498
+ const gitignorePath = path333.join(dir, ".gitignore");
176048
176499
  let stats;
176049
176500
  try {
176050
176501
  stats = await lstat21(gitignorePath);
@@ -176096,7 +176547,7 @@ async function ensurePrivateDirGitignore(dir, root) {
176096
176547
  if (!check.ok || check.action === "present") {
176097
176548
  return check;
176098
176549
  }
176099
- const gitignorePath = path332.join(dir, ".gitignore");
176550
+ const gitignorePath = path333.join(dir, ".gitignore");
176100
176551
  await mkdir72(dir, { recursive: true });
176101
176552
  try {
176102
176553
  const handle = await open8(gitignorePath, "wx");
@@ -176493,7 +176944,7 @@ async function planBundleImport(opts) {
176493
176944
  init_service4();
176494
176945
  import { mkdtemp as mkdtemp6, mkdir as mkdir73, rm as rm20, writeFile as writeFile51 } from "fs/promises";
176495
176946
  import { tmpdir as tmpdir13 } from "os";
176496
- import path333 from "path";
176947
+ import path334 from "path";
176497
176948
  function entriesToStage(plan) {
176498
176949
  return plan.entries.filter((e) => e.bucket === "new" || e.bucket === "update" || e.bucket === "conflict" && e.forced);
176499
176950
  }
@@ -176503,12 +176954,12 @@ async function auditBundlePlan(plan, opts = {}) {
176503
176954
  return { ok: true, refusals: [], report: null };
176504
176955
  }
176505
176956
  const runAudit = opts.runAudit ?? runHarnessAudit;
176506
- const stagingDir = await mkdtemp6(path333.join(tmpdir13(), "keryx-bundle-audit-"));
176957
+ const stagingDir = await mkdtemp6(path334.join(tmpdir13(), "keryx-bundle-audit-"));
176507
176958
  try {
176508
176959
  try {
176509
176960
  for (const entry of toStage) {
176510
- const abs = path333.join(stagingDir, ...entry.path.split("/"));
176511
- await mkdir73(path333.dirname(abs), { recursive: true });
176961
+ const abs = path334.join(stagingDir, ...entry.path.split("/"));
176962
+ await mkdir73(path334.dirname(abs), { recursive: true });
176512
176963
  await writeFile51(abs, entry.bytes);
176513
176964
  }
176514
176965
  } catch (err) {
@@ -176539,7 +176990,7 @@ async function auditBundlePlan(plan, opts = {}) {
176539
176990
  // src/bundle/apply.ts
176540
176991
  init_contained_write();
176541
176992
  import { mkdir as mkdir74, rmdir as rmdir2, stat as stat33 } from "fs/promises";
176542
- import path334 from "path";
176993
+ import path335 from "path";
176543
176994
  init_keryx_home();
176544
176995
  init_checksum();
176545
176996
  class ApplyAbort extends Error {
@@ -176619,7 +177070,7 @@ async function applyBundlePlan(plan, auditResult, opts = {}) {
176619
177070
  throw new ApplyAbort({ ...previousFile.refusal, path: entry.path });
176620
177071
  }
176621
177072
  const previousBytes = previousFile.bytes ?? null;
176622
- const createdDirs = await mkdirRecordingCreated(path334.dirname(entry.targetPath));
177073
+ const createdDirs = await mkdirRecordingCreated(path335.dirname(entry.targetPath));
176623
177074
  for (const dir of [...createdDirs].reverse())
176624
177075
  rollback.push({ kind: "dir", absolutePath: dir });
176625
177076
  rollback.push({ kind: "file", root, rel: entry.targetRelative, previousBytes });
@@ -176702,14 +177153,14 @@ async function applyBundlePlan(plan, auditResult, opts = {}) {
176702
177153
  }
176703
177154
  async function mkdirRecordingCreated(dir) {
176704
177155
  const missing = [];
176705
- let current = path334.resolve(dir);
177156
+ let current = path335.resolve(dir);
176706
177157
  for (;; ) {
176707
177158
  try {
176708
177159
  await stat33(current);
176709
177160
  break;
176710
177161
  } catch {
176711
177162
  missing.push(current);
176712
- const parent = path334.dirname(current);
177163
+ const parent = path335.dirname(current);
176713
177164
  if (parent === current)
176714
177165
  break;
176715
177166
  current = parent;
@@ -176724,9 +177175,9 @@ function resolveLedgerPathForEntry(entry, opts) {
176724
177175
  if (entry.targetScope === "user") {
176725
177176
  return userStorePaths(opts.env ?? process.env, opts.homeDir).appliedState;
176726
177177
  }
176727
- const metaprojectIdx = entry.targetPath.indexOf(`${path334.sep}.metaproject${path334.sep}`);
176728
- const metaprojectRoot = metaprojectIdx === -1 ? path334.join(path334.dirname(entry.targetPath), ".metaproject") : entry.targetPath.slice(0, metaprojectIdx + `${path334.sep}.metaproject`.length);
176729
- return path334.join(metaprojectRoot, "data", "bundles", "applied-state.json");
177178
+ const metaprojectIdx = entry.targetPath.indexOf(`${path335.sep}.metaproject${path335.sep}`);
177179
+ const metaprojectRoot = metaprojectIdx === -1 ? path335.join(path335.dirname(entry.targetPath), ".metaproject") : entry.targetPath.slice(0, metaprojectIdx + `${path335.sep}.metaproject`.length);
177180
+ return path335.join(metaprojectRoot, "data", "bundles", "applied-state.json");
176730
177181
  }
176731
177182
  // src/bundle/inspect.ts
176732
177183
  init_matrix();
@@ -176778,28 +177229,28 @@ function defaultCapabilityLookup() {
176778
177229
  }
176779
177230
  // src/bundle/uninstall.ts
176780
177231
  init_contained_write();
176781
- import path335 from "path";
177232
+ import path336 from "path";
176782
177233
  init_checksum();
176783
177234
  var PROTECTED_KIND_ROOTS = new Set(["skills", "project-skills", "rules", "agents", "memory", "learning", "data/learning"]);
176784
177235
  function isContained(root, candidate) {
176785
- const rel = path335.relative(root, candidate);
176786
- return rel === "" || !rel.startsWith(`..${path335.sep}`) && rel !== ".." && !path335.isAbsolute(rel);
177236
+ const rel = path336.relative(root, candidate);
177237
+ return rel === "" || !rel.startsWith(`..${path336.sep}`) && rel !== ".." && !path336.isAbsolute(rel);
176787
177238
  }
176788
177239
  async function removeNowEmptyParents(root, relFilePath) {
176789
- let relDir = path335.posix.dirname(relFilePath);
177240
+ let relDir = path336.posix.dirname(relFilePath);
176790
177241
  while (relDir !== "." && relDir !== "/" && relDir !== "") {
176791
177242
  if (PROTECTED_KIND_ROOTS.has(relDir))
176792
177243
  break;
176793
177244
  const removed = await rmdirIfEmptyContained(root, relDir).catch(() => false);
176794
177245
  if (!removed)
176795
177246
  break;
176796
- relDir = path335.posix.dirname(relDir);
177247
+ relDir = path336.posix.dirname(relDir);
176797
177248
  }
176798
177249
  }
176799
177250
  async function uninstallBundle(opts) {
176800
177251
  const ctx = { projectRoot: opts.projectRoot, env: opts.env, homeDir: opts.homeDir };
176801
177252
  const root = scopeRoot(opts.targetScope, ctx);
176802
- const rootResolved = path335.resolve(root);
177253
+ const rootResolved = path336.resolve(root);
176803
177254
  const ledgerPath = appliedStatePath(opts.targetScope, ctx);
176804
177255
  const ledgerRead = await readAppliedState(ledgerPath);
176805
177256
  if (!ledgerRead.ok) {
@@ -176839,7 +177290,7 @@ async function uninstallBundle(opts) {
176839
177290
  if (!kindCheck.ok) {
176840
177291
  return { ok: false, refusals: [{ reason: BUNDLE_REFUSAL.corruptLedger, path: relPath, message: `applied-state ledger record "${relPath}" does not match its recorded kind "${record.kind}": ${kindCheck.refusal.message}` }], removed: [], kept: [], missing: [] };
176841
177292
  }
176842
- const absolute = path335.resolve(rootResolved, normalized.path);
177293
+ const absolute = path336.resolve(rootResolved, normalized.path);
176843
177294
  if (!isContained(rootResolved, absolute)) {
176844
177295
  return { ok: false, refusals: [{ reason: BUNDLE_REFUSAL.corruptLedger, path: relPath, message: `applied-state ledger record "${relPath}" resolves outside the scope root` }], removed: [], kept: [], missing: [] };
176845
177296
  }
@@ -177031,7 +177482,7 @@ async function handleExport(args, cwd) {
177031
177482
  const outcome = await exportBundle({
177032
177483
  projectRoot,
177033
177484
  scope: scopeArg,
177034
- out: path336.resolve(cwd, out),
177485
+ out: path337.resolve(cwd, out),
177035
177486
  include: include.length > 0 ? include : undefined,
177036
177487
  kinds: kindsRaw.length > 0 ? kindsRaw : undefined,
177037
177488
  bundleId,
@@ -177078,7 +177529,7 @@ async function handleImport(args, cwd) {
177078
177529
  }
177079
177530
  const projectRoot = resolveBundleProjectRoot(cwd);
177080
177531
  if (external) {
177081
- await handleImportExternal(path336.resolve(cwd, target), projectRoot, { dryRun, json });
177532
+ await handleImportExternal(path337.resolve(cwd, target), projectRoot, { dryRun, json });
177082
177533
  return;
177083
177534
  }
177084
177535
  const targetScopeArg = optionValue([...args], "--target-scope");
@@ -177105,7 +177556,7 @@ async function handleImport(args, cwd) {
177105
177556
  return;
177106
177557
  }
177107
177558
  }
177108
- const opened = await openBundle(path336.resolve(cwd, target));
177559
+ const opened = await openBundle(path337.resolve(cwd, target));
177109
177560
  if (!opened.ok) {
177110
177561
  printRefusals([opened.refusal], json);
177111
177562
  process.exitCode = 1;
@@ -177283,7 +177734,7 @@ async function handleInspect(args, cwd) {
177283
177734
  return;
177284
177735
  }
177285
177736
  const projectRoot = resolveBundleProjectRoot(cwd);
177286
- const result = await inspectBundle(path336.resolve(cwd, bundlePath), {
177737
+ const result = await inspectBundle(path337.resolve(cwd, bundlePath), {
177287
177738
  projectRoot,
177288
177739
  targetScope: targetScopeArg
177289
177740
  });
@@ -177327,7 +177778,7 @@ async function handleVerify(args, cwd) {
177327
177778
  failUsage("keryx bundle verify requires a <bundle> argument (or --external-imports)");
177328
177779
  return;
177329
177780
  }
177330
- const outcome = await verifyBundlePath(path336.resolve(cwd, bundlePath));
177781
+ const outcome = await verifyBundlePath(path337.resolve(cwd, bundlePath));
177331
177782
  if (!outcome.ok) {
177332
177783
  printRefusals(outcome.refusals, json);
177333
177784
  process.exitCode = 1;
@@ -177455,10 +177906,10 @@ function printUninstallHelp2() {
177455
177906
  }
177456
177907
 
177457
177908
  // src/commands/learn.ts
177458
- import { existsSync as existsSync54 } from "fs";
177909
+ import { existsSync as existsSync55 } from "fs";
177459
177910
  import { readFile as readFile171 } from "fs/promises";
177460
177911
  import { createInterface as createInterface4 } from "readline/promises";
177461
- import path337 from "path";
177912
+ import path338 from "path";
177462
177913
  init_contained_path();
177463
177914
  init_service9();
177464
177915
  var STDIN_DEADLINE_MS2 = 2000;
@@ -177481,16 +177932,16 @@ function resolveRoot2(deps) {
177481
177932
  return resolveProjectRoot(deps.cwd ?? process.cwd());
177482
177933
  }
177483
177934
  function resolveLearnRoot(startDir, env) {
177484
- const hasMetaproject = (dir) => existsSync54(path337.join(dir, ".metaproject"));
177935
+ const hasMetaproject = (dir) => existsSync55(path338.join(dir, ".metaproject"));
177485
177936
  const fromEnv = env.CLAUDE_PROJECT_DIR;
177486
177937
  if (typeof fromEnv === "string" && fromEnv.length > 0 && hasMetaproject(fromEnv)) {
177487
177938
  return fromEnv;
177488
177939
  }
177489
- let current = path337.resolve(startDir);
177940
+ let current = path338.resolve(startDir);
177490
177941
  for (;; ) {
177491
177942
  if (hasMetaproject(current))
177492
177943
  return current;
177493
- const parent = path337.dirname(current);
177944
+ const parent = path338.dirname(current);
177494
177945
  if (parent === current)
177495
177946
  return;
177496
177947
  current = parent;
@@ -178155,6 +178606,190 @@ async function helpCommand(rest) {
178155
178606
  process.exitCode = 1;
178156
178607
  }
178157
178608
 
178609
+ // src/lib/group-subcommands.ts
178610
+ var GROUP_SUBCOMMANDS = new Map([
178611
+ ["modules", ["status", "list", "enable", "on", "disable", "off", "interactive", "help"]],
178612
+ ["projects", ["list", "register", "forget", "help"]],
178613
+ ["providers", ["list", "status", "cross-family", "test", "remove"]],
178614
+ ["routing", ["list", "set", "unset", "trust", "profile", "stats"]],
178615
+ ["external", ["on", "off", "status", "list"]],
178616
+ ["auth", ["list", "login", "logout", "status"]],
178617
+ ["version", ["check"]],
178618
+ ["gdgraph", ["build", "query", "find", "symbol", "symbols", "path", "affected", "repomap", "context", "assets"]],
178619
+ ["ctx", ["status", "diff", "rg", "read", "run", "show", "hook", "install-hook", "uninstall-hook"]],
178620
+ [
178621
+ "wiki",
178622
+ [
178623
+ "status",
178624
+ "new",
178625
+ "index",
178626
+ "collect",
178627
+ "check-links",
178628
+ "validate",
178629
+ "freshness",
178630
+ "refresh",
178631
+ "verify",
178632
+ "migrate-markers",
178633
+ "ask",
178634
+ "sections",
178635
+ "enrich",
178636
+ "context",
178637
+ "backlinks"
178638
+ ]
178639
+ ],
178640
+ ["stack", ["detect"]],
178641
+ ["health", ["run", "status", "gate", "sources", "explain", "baseline", "trend"]],
178642
+ ["metrics", ["status", "validate", "collect", "latest", "show", "compare", "rebuild", "plan", "benchmark"]],
178643
+ ["test", ["init", "analyze", "run", "status", "context", "report", "related", "explain", "coverage-map", "suggest"]],
178644
+ ["memory", ["new", "index", "search", "supersede", "transition", "assets", "ingest", "check", "reflect", "handoff"]],
178645
+ [
178646
+ "flow",
178647
+ [
178648
+ "init",
178649
+ "list",
178650
+ "status",
178651
+ "freeze",
178652
+ "start",
178653
+ "next",
178654
+ "task",
178655
+ "ac",
178656
+ "check-ac",
178657
+ "owner",
178658
+ "implemented",
178659
+ "complete",
178660
+ "confirm",
178661
+ "recover",
178662
+ "block",
178663
+ "unblock",
178664
+ "check",
178665
+ "renumber",
178666
+ "repair-reviews",
178667
+ "plan",
178668
+ "schema"
178669
+ ]
178670
+ ],
178671
+ ["job", ["init", "status", "step", "document", "complete", "list"]],
178672
+ [
178673
+ "review",
178674
+ [
178675
+ "attach",
178676
+ "start",
178677
+ "ingest",
178678
+ "scope",
178679
+ "floor",
178680
+ "blast-radius",
178681
+ "budget",
178682
+ "tier",
178683
+ "comments",
178684
+ "ci-triage",
178685
+ "conform",
178686
+ "jev-rules",
178687
+ "jev-edit-guard",
178688
+ "jev-risk",
178689
+ "jev-scenarios",
178690
+ "jev-docs",
178691
+ "jev-comments",
178692
+ "jev-contract",
178693
+ "jev-triage",
178694
+ "jev-select",
178695
+ "jev-profile",
178696
+ "learn",
178697
+ "loop",
178698
+ "stack",
178699
+ "reviewers",
178700
+ "import",
178701
+ "status",
178702
+ "complete",
178703
+ "lightweight"
178704
+ ]
178705
+ ],
178706
+ ["rules", ["distill", "sync"]],
178707
+ ["standard", ["validate", "doctor", "capabilities", "baseline", "emit"]],
178708
+ [
178709
+ "security",
178710
+ [
178711
+ "status",
178712
+ "scan",
178713
+ "scan-mcp",
178714
+ "audit-harness",
178715
+ "impact-evidence",
178716
+ "check-input",
178717
+ "check-output",
178718
+ "redact",
178719
+ "report",
178720
+ "policy",
178721
+ "incidents",
178722
+ "hooks",
178723
+ "eval"
178724
+ ]
178725
+ ],
178726
+ ["sandbox", ["status"]],
178727
+ ["integrations", ["install", "uninstall", "doctor", "matrix"]],
178728
+ [
178729
+ "workspace",
178730
+ [
178731
+ "create",
178732
+ "list",
178733
+ "show",
178734
+ "add-resource",
178735
+ "archive",
178736
+ "remove-resource",
178737
+ "rename",
178738
+ "overview",
178739
+ "read",
178740
+ "propose",
178741
+ "confirm-review",
178742
+ "review",
178743
+ "dismiss-candidate",
178744
+ "handoff",
178745
+ "collaboration",
178746
+ "policy-readiness",
178747
+ "catch-up",
178748
+ "list-proposals",
178749
+ "help"
178750
+ ]
178751
+ ],
178752
+ ["retention", ["status", "sweep"]],
178753
+ ["forgetting", ["trail", "lookup"]],
178754
+ ["trigger", ["run", "install", "uninstall", "list", "status", "schedule", "resolve"]],
178755
+ ["schedule", ["add", "list", "show", "pause", "resume", "run", "remove"]],
178756
+ ["governance", ["report", "show"]],
178757
+ ["hooks", ["list", "validate", "test", "enable", "disable", "trust", "untrust"]],
178758
+ ["bundle", ["export", "import", "inspect", "verify", "uninstall"]],
178759
+ ["learn", ["observe", "extract", "list", "review", "accept", "reject", "apply", "promote", "graduate", "prune"]],
178760
+ ["sessions", ["list", "fork", "export", "path", "help"]],
178761
+ ["session", ["list", "fork", "export", "path", "help"]],
178762
+ ["serve", ["status", "token", "config"]],
178763
+ ["bus", ["list", "log", "send", "pause", "resume", "prune", "help"]],
178764
+ ["dashboard", ["build", "open"]],
178765
+ [
178766
+ "skills",
178767
+ [
178768
+ "catalog",
178769
+ "install",
178770
+ "doctor",
178771
+ "uninstall",
178772
+ "status",
178773
+ "list",
178774
+ "inspect",
178775
+ "route",
178776
+ "create",
178777
+ "generate",
178778
+ "import",
178779
+ "update",
178780
+ "verify",
178781
+ "learn",
178782
+ "export",
178783
+ "sync",
178784
+ "contracts",
178785
+ "scout",
178786
+ "eval",
178787
+ "judge-check",
178788
+ "stocktake"
178789
+ ]
178790
+ ]
178791
+ ]);
178792
+
178158
178793
  // src/cli.ts
178159
178794
  init_package();
178160
178795
  if (import.meta.main) {
@@ -178164,6 +178799,7 @@ var VERSION2 = package_default.version;
178164
178799
  var CLI_ROUTES = {
178165
178800
  init: initCommand,
178166
178801
  help: helpCommand,
178802
+ doctor: doctorCommand2,
178167
178803
  status: statusCommand,
178168
178804
  modules: modulesCommand,
178169
178805
  projects: projectsCommand,
@@ -178218,6 +178854,15 @@ var CLI_ROUTES = {
178218
178854
  learn: learnCommand,
178219
178855
  "__sandbox-net-forward": sandboxNetForwardCommand
178220
178856
  };
178857
+ var MCP_KNOWN_SUBCOMMANDS = [...MCP_CONSUMER_SUBCOMMANDS, "install", "uninstall", "serve"];
178858
+ function knownSubcommandsFor(command) {
178859
+ if (command === "mcp")
178860
+ return MCP_KNOWN_SUBCOMMANDS;
178861
+ return GROUP_SUBCOMMANDS.get(command);
178862
+ }
178863
+ function groupsWithKnownSubcommands() {
178864
+ return ["mcp", ...GROUP_SUBCOMMANDS.keys()];
178865
+ }
178221
178866
  async function main() {
178222
178867
  setModelTurnPort(async (request) => runModelTurn(request));
178223
178868
  const args = process.argv.slice(2);
@@ -178236,16 +178881,23 @@ async function main() {
178236
178881
  await printCommandHelp(command);
178237
178882
  return;
178238
178883
  }
178884
+ const known = knownSubcommandsFor(command);
178885
+ const sub = args[1];
178886
+ if (known !== undefined && sub !== undefined && !sub.startsWith("-") && !known.includes(sub)) {
178887
+ console.error(formatUnknownCommandMessage(sub, known, `keryx ${command} --help`));
178888
+ process.exitCode = 1;
178889
+ return;
178890
+ }
178239
178891
  await route(args.slice(1));
178240
178892
  return;
178241
178893
  }
178242
- console.error(`Unknown command: ${command}`);
178243
- printHelp30();
178894
+ console.error(formatUnknownCommandMessage(command, Object.keys(CLI_ROUTES).filter((name) => name !== "__sandbox-net-forward")));
178244
178895
  process.exitCode = 1;
178245
178896
  }
178246
178897
  var USAGE_BODY = `Usage:
178247
178898
  keryx Show CLI usage
178248
178899
  keryx help [group|command] Grouped command help by task (--help/-h keep this flat usage)
178900
+ keryx doctor [--json] One page: version, Bun floor, ripgrep, sandbox, providers, MCP, integrations, standard, worktrees, graph/wiki freshness
178249
178901
  keryx shell [-c|--continue] [-r|--resume [id]] [--provider <p>] [--model <m>] [--base-url <url>] [--agent|--chat] [--tui|--no-tui]
178250
178902
  Start TUI agent shell (sessions are per-project)
178251
178903
  keryx sessions list|fork <id>|export <id>|path
@@ -178425,6 +179077,7 @@ var USAGE_BODY = `Usage:
178425
179077
 
178426
179078
  Commands:
178427
179079
  help Grouped command help by task: every verb, in nine onboarding-ordered groups
179080
+ doctor One-page health check with a fix hint per line; --json for {checks:[...]}
178428
179081
  shell Start the interactive TUI agent harness. Use --no-tui or --chat to opt out.
178429
179082
  Sessions: -c continue last in this project, -r [id] resume (per-project).
178430
179083
  sessions List or export per-project shell sessions
@@ -178573,7 +179226,9 @@ export {
178573
179226
  USAGE_BODY,
178574
179227
  exitCodeForError,
178575
179228
  groupUsage,
179229
+ groupsWithKnownSubcommands,
178576
179230
  helpRequestedFor,
179231
+ knownSubcommandsFor,
178577
179232
  main,
178578
179233
  printCommandHelp,
178579
179234
  shouldInterceptHelp