@mrciphersmith/keryx 0.2.17 → 0.2.19

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +26 -0
  2. package/dist/cli.js +743 -293
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -55,9 +55,12 @@ import {
55
55
  chmodSync,
56
56
  mkdirSync,
57
57
  readFileSync,
58
+ renameSync,
58
59
  statSync,
60
+ unlinkSync,
59
61
  writeFileSync
60
62
  } from "fs";
63
+ import { randomUUID } from "crypto";
61
64
  import { homedir } from "os";
62
65
  import path from "path";
63
66
  function keryxConfigDir(dir) {
@@ -146,6 +149,21 @@ function writeOwnerOnlyFile(file, body) {
146
149
  chmodSync(file, 384);
147
150
  } catch {}
148
151
  }
152
+ function writeOwnerOnlyFileAtomic(file, body) {
153
+ const temporary = `${file}.${process.pid}.${randomUUID()}.tmp`;
154
+ try {
155
+ writeFileSync(temporary, body, { encoding: "utf8", mode: 384 });
156
+ if (process.platform !== "win32") {
157
+ chmodSync(temporary, 384);
158
+ }
159
+ renameSync(temporary, file);
160
+ } catch (cause) {
161
+ try {
162
+ unlinkSync(temporary);
163
+ } catch {}
164
+ throw cause;
165
+ }
166
+ }
149
167
  function ensureKeryxConfigDir(dir) {
150
168
  const base = keryxConfigDir(dir);
151
169
  try {
@@ -174,7 +192,7 @@ __export(exports_fs, {
174
192
  pathExists: () => pathExists,
175
193
  isPathInside: () => isPathInside
176
194
  });
177
- import { randomUUID as randomUUID3 } from "crypto";
195
+ import { randomUUID as randomUUID4 } from "crypto";
178
196
  import { access, mkdir, rename, rm, stat, writeFile } from "fs/promises";
179
197
  import path5 from "path";
180
198
  async function pathExists(filePath) {
@@ -195,7 +213,7 @@ function isPathInside(root, candidate) {
195
213
  async function writeFileAtomic(filePath, content) {
196
214
  const dir = path5.dirname(filePath);
197
215
  await mkdir(dir, { recursive: true });
198
- const tmp = path5.join(dir, `.${path5.basename(filePath)}.${process.pid}.${Date.now()}.${randomUUID3()}.tmp`);
216
+ const tmp = path5.join(dir, `.${path5.basename(filePath)}.${process.pid}.${Date.now()}.${randomUUID4()}.tmp`);
199
217
  try {
200
218
  await writeFile(tmp, content, "utf8");
201
219
  await rename(tmp, filePath);
@@ -7819,6 +7837,12 @@ var init_gdgraph = __esm(() => {
7819
7837
  });
7820
7838
 
7821
7839
  // src/commands/providers.ts
7840
+ function isProviderPlatformSupported(provider, platform = process.platform) {
7841
+ if (provider.platforms === undefined || provider.platforms.length === 0) {
7842
+ return true;
7843
+ }
7844
+ return provider.platforms.includes(platform);
7845
+ }
7822
7846
  function providerByName(name) {
7823
7847
  return OPENAI_COMPAT_PROVIDERS.find((p) => p.name === name);
7824
7848
  }
@@ -7863,7 +7887,7 @@ async function resolveModelsForPicker(fetchFn, provider, env = process.env, opts
7863
7887
  return { models: [...provider.models], source: "fallback" };
7864
7888
  }
7865
7889
  const envKey = provider.envKey ?? compat.envKey;
7866
- const raw = env[envKey];
7890
+ const raw = envKey === undefined ? undefined : env[envKey];
7867
7891
  const apiKey = typeof raw === "string" && raw.length > 0 ? raw : undefined;
7868
7892
  return fetchOpenAiCompatModelsDetailed(fetchFn, compat, apiKey, opts);
7869
7893
  }
@@ -7938,6 +7962,16 @@ var init_providers = __esm(() => {
7938
7962
  models: ["llama-3.3-70b-versatile", "llama-3.1-8b-instant", "gpt-oss-120b"],
7939
7963
  note: "free tier \xB7 fast"
7940
7964
  },
7965
+ {
7966
+ name: "rapid-mlx",
7967
+ label: "Rapid-MLX (Local)",
7968
+ baseUrl: "http://127.0.0.1:8010",
7969
+ requiresApiKey: false,
7970
+ allowLoopback: true,
7971
+ platforms: ["darwin"],
7972
+ models: ["qwen3.5-9b-4bit"],
7973
+ note: "local \xB7 no key"
7974
+ },
7941
7975
  {
7942
7976
  name: "moonshot",
7943
7977
  label: "Moonshot (Kimi)",
@@ -9624,18 +9658,21 @@ function makeProvider(name, _model, opts) {
9624
9658
  }
9625
9659
  const compat = providerByName(name);
9626
9660
  if (compat !== undefined) {
9627
- const apiKey = env[compat.envKey];
9628
- if (apiKey === undefined || apiKey.length === 0) {
9661
+ const needsKey = compat.requiresApiKey !== false;
9662
+ const apiKey = compat.envKey === undefined ? undefined : env[compat.envKey];
9663
+ if (needsKey && (apiKey === undefined || apiKey.length === 0)) {
9629
9664
  return new FakeProvider([]);
9630
9665
  }
9666
+ const grant = {
9667
+ network: true,
9668
+ baseUrl: opts.baseUrl ?? compat.baseUrl,
9669
+ ...compat.allowLoopback === true ? { allowLoopback: true } : {},
9670
+ ...compat.chatPath !== undefined ? { chatPath: compat.chatPath } : {},
9671
+ ...apiKey !== undefined ? { apiKey } : {}
9672
+ };
9631
9673
  return new OllamaProvider({
9632
9674
  fetch: opts.fetch,
9633
- grant: {
9634
- network: true,
9635
- baseUrl: opts.baseUrl ?? compat.baseUrl,
9636
- apiKey,
9637
- ...compat.chatPath !== undefined ? { chatPath: compat.chatPath } : {}
9638
- }
9675
+ grant
9639
9676
  });
9640
9677
  }
9641
9678
  return new FakeProvider([]);
@@ -9752,13 +9789,22 @@ function hasCredential(provider, env) {
9752
9789
  }
9753
9790
  const compat = providerByName(provider);
9754
9791
  if (compat) {
9792
+ if (compat.requiresApiKey === false) {
9793
+ return true;
9794
+ }
9795
+ if (compat.envKey === undefined) {
9796
+ return false;
9797
+ }
9755
9798
  const key = env[compat.envKey];
9756
9799
  return key !== undefined && key.length > 0;
9757
9800
  }
9758
9801
  return false;
9759
9802
  }
9760
9803
  function keyedProviderCandidates() {
9761
- return ["anthropic", ...OPENAI_COMPAT_PROVIDERS.map((p) => p.name)];
9804
+ return [
9805
+ "anthropic",
9806
+ ...OPENAI_COMPAT_PROVIDERS.filter((provider) => provider.requiresApiKey !== false).map((p) => p.name)
9807
+ ];
9762
9808
  }
9763
9809
  function resolveAutoProvider(env, opts) {
9764
9810
  if (opts?.preferSavedShell !== false) {
@@ -11146,7 +11192,7 @@ var init_search = __esm(() => {
11146
11192
  });
11147
11193
 
11148
11194
  // src/memory/report.ts
11149
- import { randomUUID as randomUUID5 } from "crypto";
11195
+ import { randomUUID as randomUUID6 } from "crypto";
11150
11196
  import { access as access2, mkdir as mkdir27, readdir as readdir7, rename as rename3, rm as rm5, stat as stat4, writeFile as writeFile30 } from "fs/promises";
11151
11197
  import path71 from "path";
11152
11198
  function runtimeRoot(cwd) {
@@ -11306,7 +11352,7 @@ class MemoryReportStore {
11306
11352
  nextRunId;
11307
11353
  constructor(dependencies = {}) {
11308
11354
  this.clock = dependencies.clock ?? (() => new Date);
11309
- this.nextRunId = dependencies.runId ?? (() => randomUUID5());
11355
+ this.nextRunId = dependencies.runId ?? (() => randomUUID6());
11310
11356
  }
11311
11357
  async writeReport(input2) {
11312
11358
  const runId = input2.runId ?? this.nextRunId();
@@ -11327,7 +11373,7 @@ class MemoryReportStore {
11327
11373
  if ((await stat4(candidate)).mtimeMs <= staleBefore)
11328
11374
  await rm5(candidate, { recursive: true, force: true });
11329
11375
  }
11330
- const staging = path71.join(tempRoot, `${runId}.${randomUUID5()}`);
11376
+ const staging = path71.join(tempRoot, `${runId}.${randomUUID6()}`);
11331
11377
  try {
11332
11378
  await mkdir27(staging);
11333
11379
  await Promise.all([
@@ -12714,17 +12760,17 @@ import {
12714
12760
  fsyncSync,
12715
12761
  openSync as openSync2,
12716
12762
  realpathSync,
12717
- renameSync,
12763
+ renameSync as renameSync2,
12718
12764
  statSync as statSync3,
12719
- unlinkSync,
12765
+ unlinkSync as unlinkSync2,
12720
12766
  writeFileSync as writeFileSync3
12721
12767
  } from "fs";
12722
12768
  import path3 from "path";
12723
- import { randomUUID as randomUUID2 } from "crypto";
12769
+ import { randomUUID as randomUUID3 } from "crypto";
12724
12770
 
12725
12771
  // src/lib/file-lock.ts
12726
12772
  import { closeSync, mkdirSync as mkdirSync2, openSync, readFileSync as readFileSync2, rmSync, statSync as statSync2, writeFileSync as writeFileSync2 } from "fs";
12727
- import { randomUUID } from "crypto";
12773
+ import { randomUUID as randomUUID2 } from "crypto";
12728
12774
  import path2 from "path";
12729
12775
  var LOCK_STALE_MS = 1e4;
12730
12776
  var LOCK_TIMEOUT_MS = 15000;
@@ -12743,7 +12789,7 @@ function withFileLock(lockPath, fn, options = {}) {
12743
12789
  } catch {
12744
12790
  return null;
12745
12791
  }
12746
- const nonce = randomUUID();
12792
+ const nonce = randomUUID2();
12747
12793
  const started = Date.now();
12748
12794
  const deadline = started + LOCK_TIMEOUT_MS;
12749
12795
  let handle = null;
@@ -12925,7 +12971,7 @@ function quarantineDamagedRegistry(dir, onWarn) {
12925
12971
  }
12926
12972
  function saveProjectRegistry(registry, dir, onWarn) {
12927
12973
  const file = projectRegistryPath(dir);
12928
- const temp = `${file}.${randomUUID2()}.tmp`;
12974
+ const temp = `${file}.${randomUUID3()}.tmp`;
12929
12975
  try {
12930
12976
  ensureKeryxConfigDir(dir);
12931
12977
  const sorted = {
@@ -12940,11 +12986,11 @@ function saveProjectRegistry(registry, dir, onWarn) {
12940
12986
  } finally {
12941
12987
  closeSync2(handle);
12942
12988
  }
12943
- renameSync(temp, file);
12989
+ renameSync2(temp, file);
12944
12990
  return true;
12945
12991
  } catch {
12946
12992
  try {
12947
- unlinkSync(temp);
12993
+ unlinkSync2(temp);
12948
12994
  } catch {}
12949
12995
  return false;
12950
12996
  }
@@ -12981,7 +13027,7 @@ function registerProject(projectPath, options = {}) {
12981
13027
  return { ok: true, entry: existing, created: false };
12982
13028
  }
12983
13029
  const entry = {
12984
- projectId: randomUUID2(),
13030
+ projectId: randomUUID3(),
12985
13031
  path: absolute,
12986
13032
  displayName: options.displayName ?? path3.basename(absolute),
12987
13033
  state: "active",
@@ -13694,7 +13740,7 @@ function sessionDir(projectPath, sessionId, dataDir) {
13694
13740
 
13695
13741
  // src/harness/process/sandbox/network-run.ts
13696
13742
  import { Worker } from "worker_threads";
13697
- import { randomUUID as randomUUID4 } from "crypto";
13743
+ import { randomUUID as randomUUID5 } from "crypto";
13698
13744
  import { existsSync as existsSync3 } from "fs";
13699
13745
  import { rm as rm2, writeFile as writeFile3 } from "fs/promises";
13700
13746
  import { fileURLToPath } from "url";
@@ -13789,7 +13835,7 @@ async function setupNetworkRun(profile, options = {}) {
13789
13835
  for (const cred of options.masks ?? []) {
13790
13836
  if (cred.realValue.length === 0)
13791
13837
  continue;
13792
- const sentinel = `keryx-sentinel-${randomUUID4()}`;
13838
+ const sentinel = `keryx-sentinel-${randomUUID5()}`;
13793
13839
  maskedEnv[cred.name] = sentinel;
13794
13840
  proxyMasks.push({ sentinel, realValue: cred.realValue, injectHosts: cred.injectHosts });
13795
13841
  }
@@ -13799,7 +13845,7 @@ async function setupNetworkRun(profile, options = {}) {
13799
13845
  const trustEnv = {};
13800
13846
  let caPemPath;
13801
13847
  if (tlsTerminate && caCertPem) {
13802
- caPemPath = path9.join(tmpdir(), `keryx-run-ca-${randomUUID4()}.pem`);
13848
+ caPemPath = path9.join(tmpdir(), `keryx-run-ca-${randomUUID5()}.pem`);
13803
13849
  await writeFile3(caPemPath, caCertPem, { mode: 420 });
13804
13850
  trustEnv.SSL_CERT_FILE = caPemPath;
13805
13851
  trustEnv.CURL_CA_BUNDLE = caPemPath;
@@ -17192,7 +17238,8 @@ function renderIndexMarkdown({
17192
17238
  "First choose the capability: graph/navigation, compact context, wiki/domain knowledge, memory, testing, health, security, skills/orchestration, or flow lifecycle.",
17193
17239
  "If the same capability is available through MCP tools/resources, prefer MCP because it preserves structured inputs and outputs. If MCP is unavailable, use the module skill and `keryx` CLI fallback.",
17194
17240
  "Load the narrowest relevant skill/rule before reading broad source files. Do not ask the user which internal command to run unless multiple user-level outcomes are genuinely possible.",
17195
- "When reporting results, name the Metaproject sources used at a high level (for example: graph, wiki, memory, health), not every internal command."
17241
+ "When reporting results, name the Metaproject sources used at a high level (for example: graph, wiki, memory, health), not every internal command.",
17242
+ "Once per session, run `keryx version check --json`. Notify the user only when the result is `update-available`; this is an advisory and never installs anything. A timeout, offline registry, `unavailable`, or `unknown-command` result is non-blocking: never block project work on the check."
17196
17243
  ].map((item) => `- ${item}`).join(`
17197
17244
  `);
17198
17245
  const intentRows = [
@@ -32023,6 +32070,15 @@ var COMMAND_DESCRIPTORS = [
32023
32070
  args: [{ name: "json", type: "bool", required: false, desc: "structured module state" }],
32024
32071
  json: true,
32025
32072
  read: true
32073
+ },
32074
+ {
32075
+ module: "core",
32076
+ command: "version check",
32077
+ summary: "Check the fixed npm latest endpoint for a newer Keryx release.",
32078
+ intent: ["check keryx version", "check for update", "\u043F\u0440\u043E\u0432\u0435\u0440\u044C \u0432\u0435\u0440\u0441\u0438\u044E keryx"],
32079
+ args: [{ name: "json", type: "bool", required: false, desc: "typed structured result" }],
32080
+ json: true,
32081
+ read: true
32026
32082
  }
32027
32083
  ];
32028
32084
  function sortKey(descriptor) {
@@ -34743,7 +34799,7 @@ Reports the workspace root and one enabled/disabled line per module. Use
34743
34799
  // src/commands/harness.ts
34744
34800
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "fs";
34745
34801
  import path118 from "path";
34746
- import { randomUUID as randomUUID6 } from "crypto";
34802
+ import { randomUUID as randomUUID7 } from "crypto";
34747
34803
 
34748
34804
  // src/security/harness-scan.ts
34749
34805
  init_guard();
@@ -36427,6 +36483,9 @@ function projectPolicyTrusted(env) {
36427
36483
  function buildDefaultMaskProviders(openaiCompat) {
36428
36484
  const out = [];
36429
36485
  for (const p of openaiCompat) {
36486
+ if (p.envKey === undefined) {
36487
+ continue;
36488
+ }
36430
36489
  out.push({ envKey: p.envKey, baseUrl: p.baseUrl });
36431
36490
  }
36432
36491
  out.push({ envKey: "ANTHROPIC_API_KEY", baseUrl: "https://api.anthropic.com" });
@@ -37069,6 +37128,13 @@ function checkApproval(input2) {
37069
37128
  }
37070
37129
 
37071
37130
  // src/commands/harness.ts
37131
+ var HARNESS_PROVIDER_OPTIONS = [
37132
+ "fake",
37133
+ "anthropic",
37134
+ "ollama",
37135
+ ...OPENAI_COMPAT_PROVIDERS.map((provider) => provider.name)
37136
+ ];
37137
+ var HARNESS_PROVIDER_USAGE = `Usage: keryx harness run --provider <${HARNESS_PROVIDER_OPTIONS.join("|")}> --model <m> [--base-url <url>] "<prompt>"`;
37072
37138
  function canonicalPath(p) {
37073
37139
  try {
37074
37140
  return realpathSync3(p);
@@ -37093,14 +37159,14 @@ function resolveRuntime(deps) {
37093
37159
  const env = deps?.env ?? process.env;
37094
37160
  const clock = deps?.clock ?? (() => new Date().toISOString());
37095
37161
  let idCounter = 0;
37096
- const idSeq = deps?.idSeq ?? (() => `${randomUUID6()}-${idCounter++}`);
37162
+ const idSeq = deps?.idSeq ?? (() => `${randomUUID7()}-${idCounter++}`);
37097
37163
  return { env, clock, idSeq };
37098
37164
  }
37099
37165
  function shellAllowProfile() {
37100
37166
  return resolveLocalProfile("monitored-trusted-local");
37101
37167
  }
37102
37168
  var USAGE = [
37103
- 'Usage: keryx harness run --provider <fake|anthropic|ollama> --model <m> [--base-url <url>] "<prompt>"',
37169
+ HARNESS_PROVIDER_USAGE,
37104
37170
  " keryx harness exec [--allow-env KEY]... [--max-runtime-ms N] [--allow-real-subprocess]",
37105
37171
  " [--allowed-domains a,b] [--mask-env NAME@host] [--tls-terminate] [--mask-mode auto|manual|off] [--auto-mask]",
37106
37172
  " -- <path> [args...]",
@@ -37176,7 +37242,7 @@ async function harnessCommand(args, deps) {
37176
37242
  return;
37177
37243
  }
37178
37244
  const { provider, model, baseUrl, prompt, record } = parseArgs2(args);
37179
- const validProviders = new Set(["fake", "anthropic", "ollama"]);
37245
+ const validProviders = new Set(HARNESS_PROVIDER_OPTIONS);
37180
37246
  if (!validProviders.has(provider) || prompt.length === 0) {
37181
37247
  console.log(USAGE);
37182
37248
  return;
@@ -37184,7 +37250,7 @@ async function harnessCommand(args, deps) {
37184
37250
  const env = deps?.env ?? process.env;
37185
37251
  const clock = deps?.clock ?? (() => new Date().toISOString());
37186
37252
  let idCounter = 0;
37187
- const idSeq = deps?.idSeq ?? (() => `${randomUUID6()}-${idCounter++}`);
37253
+ const idSeq = deps?.idSeq ?? (() => `${randomUUID7()}-${idCounter++}`);
37188
37254
  const fetchImpl = deps?.fetch ?? globalThis.fetch;
37189
37255
  if (provider === "anthropic") {
37190
37256
  const apiKey = env.ANTHROPIC_API_KEY;
@@ -37301,7 +37367,7 @@ function harnessReplay(args, deps) {
37301
37367
  }
37302
37368
  const clock = deps?.clock ?? (() => new Date().toISOString());
37303
37369
  let idCounter = 0;
37304
- const idSeq = deps?.idSeq ?? (() => `${randomUUID6()}-${idCounter++}`);
37370
+ const idSeq = deps?.idSeq ?? (() => `${randomUUID7()}-${idCounter++}`);
37305
37371
  const fixture = (() => {
37306
37372
  if (parsed.fixture === undefined || parsed.fixture.length === 0) {
37307
37373
  return { ok: true, value: buildReplayFixture(run, { idSeq }), built: true };
@@ -37741,7 +37807,7 @@ function harnessWave(args, deps) {
37741
37807
  // src/commands/shell.ts
37742
37808
  init_make_provider();
37743
37809
  init_orient();
37744
- import { randomUUID as randomUUID9 } from "crypto";
37810
+ import { randomUUID as randomUUID10 } from "crypto";
37745
37811
  import * as readline2 from "readline";
37746
37812
 
37747
37813
  // src/harness/tool/builtin/ask-user-tool.ts
@@ -38385,7 +38451,7 @@ function shellExecTool(root, run = makeCommandRunner(root)) {
38385
38451
  }
38386
38452
 
38387
38453
  // src/harness/tool/builtin/spawn-subagent-tool.ts
38388
- import { createHash as createHash17, randomUUID as randomUUID7 } from "crypto";
38454
+ import { createHash as createHash17, randomUUID as randomUUID8 } from "crypto";
38389
38455
 
38390
38456
  // src/harness/child/ledger.ts
38391
38457
  function decrement(remaining, reservation) {
@@ -39501,7 +39567,7 @@ function sha2565(text) {
39501
39567
  var parentShellPolicy = shellParentProfile;
39502
39568
  var childReadOnlyPolicy = shellChildReadOnlyProfile;
39503
39569
  function createSpawnSubagentTool(deps) {
39504
- const idSeq = deps.idSeq ?? (() => randomUUID7());
39570
+ const idSeq = deps.idSeq ?? (() => randomUUID8());
39505
39571
  const clock = deps.clock ?? (() => new Date().toISOString());
39506
39572
  const parentRunId = deps.parentRunId ?? idSeq();
39507
39573
  const parentSessionId = deps.parentSessionId ?? idSeq();
@@ -40350,11 +40416,11 @@ import {
40350
40416
  existsSync as existsSync23,
40351
40417
  mkdirSync as mkdirSync6,
40352
40418
  readdirSync,
40353
- renameSync as renameSync2,
40419
+ renameSync as renameSync3,
40354
40420
  writeFileSync as writeFileSync7
40355
40421
  } from "fs";
40356
40422
  import path120 from "path";
40357
- import { randomUUID as randomUUID8 } from "crypto";
40423
+ import { randomUUID as randomUUID9 } from "crypto";
40358
40424
  var SESSION_SCHEMA_VERSION = 1;
40359
40425
  function nowIso() {
40360
40426
  return new Date().toISOString();
@@ -40393,7 +40459,7 @@ function tighten(target) {
40393
40459
  function atomicWriteText(file, body) {
40394
40460
  const tmp = `${file}.${process.pid}.${Date.now()}.tmp`;
40395
40461
  writeFileSync7(tmp, body, { encoding: "utf8", mode: 384 });
40396
- renameSync2(tmp, file);
40462
+ renameSync3(tmp, file);
40397
40463
  }
40398
40464
  function atomicWriteJson(file, value) {
40399
40465
  atomicWriteText(file, `${JSON.stringify(value, null, 2)}
@@ -40501,7 +40567,7 @@ function readJsonl2(file) {
40501
40567
  function createSession(opts) {
40502
40568
  const projectPath = resolveProjectRoot(opts.cwd);
40503
40569
  const projectKey2 = projectKeyFromPath(projectPath);
40504
- const id = opts.id ?? randomUUID8();
40570
+ const id = opts.id ?? randomUUID9();
40505
40571
  const dir = sessionDir(projectPath, id, opts.dataDir);
40506
40572
  ensureDir(dir, opts.dataDir);
40507
40573
  const ts = nowIso();
@@ -40944,6 +41010,279 @@ function showComposerChoice(otui, r, dock, request) {
40944
41010
  });
40945
41011
  }
40946
41012
 
41013
+ // src/lib/version-check.ts
41014
+ init_config_dir();
41015
+ init_fs();
41016
+ import path121 from "path";
41017
+ var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
41018
+ var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
41019
+ var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
41020
+ var VERSION_STRING_LIMIT_CHARS = 64;
41021
+ var REQUEST_TIMEOUT_MS = 2000;
41022
+ var SUCCESS_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
41023
+ var FAILURE_BACKOFF_MS = 15 * 60 * 1000;
41024
+ var CACHE_LOCK_TIMEOUT_MS = 250;
41025
+ var CACHE_LOCK_RETRY_MS = 10;
41026
+ var CACHE_LOCK_STALE_MS = 5000;
41027
+ var SYSTEM_TIMER = {
41028
+ schedule: (callback, delayMs) => setTimeout(callback, delayMs),
41029
+ cancel: (handle) => clearTimeout(handle)
41030
+ };
41031
+ function formatVersionUpdateAdvisory(result) {
41032
+ if (result.status !== "update-available")
41033
+ return;
41034
+ return `Keryx update ${result.currentVersion} \u2192 ${result.latestVersion}
41035
+ ${result.installCommand}`;
41036
+ }
41037
+ 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-]+)*))?$/;
41038
+ function parseSemVer(value) {
41039
+ if (value.length > VERSION_STRING_LIMIT_CHARS)
41040
+ return;
41041
+ const match = STRICT_SEMVER.exec(value);
41042
+ if (match === null)
41043
+ return;
41044
+ const prereleaseText = match[4];
41045
+ const prerelease = [];
41046
+ if (prereleaseText !== undefined) {
41047
+ for (const identifier of prereleaseText.split(".")) {
41048
+ if (/^\d+$/.test(identifier)) {
41049
+ if (identifier.length > 1 && identifier.startsWith("0"))
41050
+ return;
41051
+ prerelease.push(BigInt(identifier));
41052
+ } else {
41053
+ prerelease.push(identifier);
41054
+ }
41055
+ }
41056
+ }
41057
+ return {
41058
+ major: BigInt(match[1]),
41059
+ minor: BigInt(match[2]),
41060
+ patch: BigInt(match[3]),
41061
+ prerelease
41062
+ };
41063
+ }
41064
+ function compareSemVer(left, right) {
41065
+ for (const key of ["major", "minor", "patch"]) {
41066
+ if (left[key] < right[key])
41067
+ return -1;
41068
+ if (left[key] > right[key])
41069
+ return 1;
41070
+ }
41071
+ if (left.prerelease.length === 0 || right.prerelease.length === 0) {
41072
+ if (left.prerelease.length === right.prerelease.length)
41073
+ return 0;
41074
+ return left.prerelease.length === 0 ? 1 : -1;
41075
+ }
41076
+ const length = Math.max(left.prerelease.length, right.prerelease.length);
41077
+ for (let index = 0;index < length; index += 1) {
41078
+ const a = left.prerelease[index];
41079
+ const b = right.prerelease[index];
41080
+ if (a === undefined || b === undefined) {
41081
+ if (a === b)
41082
+ return 0;
41083
+ return a === undefined ? -1 : 1;
41084
+ }
41085
+ if (typeof a === "bigint" && typeof b === "bigint") {
41086
+ if (a < b)
41087
+ return -1;
41088
+ if (a > b)
41089
+ return 1;
41090
+ } else if (typeof a === "bigint") {
41091
+ return -1;
41092
+ } else if (typeof b === "bigint") {
41093
+ return 1;
41094
+ } else if (a !== b) {
41095
+ return a < b ? -1 : 1;
41096
+ }
41097
+ }
41098
+ return 0;
41099
+ }
41100
+ function parseCache(file) {
41101
+ const read = readConfigFile(file);
41102
+ if (!read.ok)
41103
+ return;
41104
+ try {
41105
+ const value = JSON.parse(read.text);
41106
+ if (value === null || typeof value !== "object" || Array.isArray(value))
41107
+ return;
41108
+ const record = value;
41109
+ const cache = {};
41110
+ if (typeof record.latestVersion === "string" && parseSemVer(record.latestVersion) !== undefined) {
41111
+ cache.latestVersion = record.latestVersion;
41112
+ }
41113
+ if (typeof record.successAt === "number" && Number.isFinite(record.successAt))
41114
+ cache.successAt = record.successAt;
41115
+ if (typeof record.failureAt === "number" && Number.isFinite(record.failureAt))
41116
+ cache.failureAt = record.failureAt;
41117
+ return cache;
41118
+ } catch {
41119
+ return;
41120
+ }
41121
+ }
41122
+ function resultFor(currentVersion, current, latestVersion, source) {
41123
+ const latest2 = parseSemVer(latestVersion);
41124
+ return compareSemVer(current, latest2) < 0 ? { status: "update-available", currentVersion, latestVersion, installCommand: FIXED_INSTALL_COMMAND, source } : { status: "up-to-date", currentVersion, latestVersion, source };
41125
+ }
41126
+ function cancelWithoutWaiting(cancel) {
41127
+ try {
41128
+ cancel().catch(() => {});
41129
+ } catch {}
41130
+ }
41131
+ async function boundedResponseText(response) {
41132
+ const declared = response.headers.get("content-length");
41133
+ if (declared !== null && /^\d+$/.test(declared) && BigInt(declared) > BigInt(RESPONSE_BODY_LIMIT_BYTES)) {
41134
+ if (response.body !== null) {
41135
+ cancelWithoutWaiting(() => response.body.cancel());
41136
+ }
41137
+ return;
41138
+ }
41139
+ if (response.body === null)
41140
+ return "";
41141
+ const reader = response.body.getReader();
41142
+ const decoder = new TextDecoder;
41143
+ let size = 0;
41144
+ let text = "";
41145
+ try {
41146
+ for (;; ) {
41147
+ const chunk = await reader.read();
41148
+ if (chunk.done)
41149
+ break;
41150
+ size += chunk.value.byteLength;
41151
+ if (size > RESPONSE_BODY_LIMIT_BYTES) {
41152
+ cancelWithoutWaiting(() => reader.cancel());
41153
+ return;
41154
+ }
41155
+ text += decoder.decode(chunk.value, { stream: true });
41156
+ }
41157
+ return text + decoder.decode();
41158
+ } finally {
41159
+ reader.releaseLock();
41160
+ }
41161
+ }
41162
+ function unavailable(currentVersion, reason, cache) {
41163
+ return {
41164
+ status: "unavailable",
41165
+ currentVersion,
41166
+ reason,
41167
+ ...cache?.latestVersion !== undefined ? { cachedLatestVersion: cache.latestVersion } : {}
41168
+ };
41169
+ }
41170
+ function saveCache(file, cache) {
41171
+ try {
41172
+ writeOwnerOnlyFileAtomic(file, `${JSON.stringify(cache, null, 2)}
41173
+ `);
41174
+ return true;
41175
+ } catch {
41176
+ return false;
41177
+ }
41178
+ }
41179
+ function mergeSuccessfulCache(committed, latestVersion, successAt) {
41180
+ if (committed.latestVersion === undefined || committed.successAt === undefined) {
41181
+ return { latestVersion, successAt };
41182
+ }
41183
+ if (committed.successAt > successAt)
41184
+ return committed;
41185
+ if (committed.successAt < successAt)
41186
+ return { latestVersion, successAt };
41187
+ const committedVersion = parseSemVer(committed.latestVersion);
41188
+ const candidateVersion = parseSemVer(latestVersion);
41189
+ if (committedVersion !== undefined && candidateVersion !== undefined) {
41190
+ const precedence = compareSemVer(committedVersion, candidateVersion);
41191
+ if (precedence > 0)
41192
+ return committed;
41193
+ if (precedence < 0)
41194
+ return { latestVersion, successAt };
41195
+ if (committed.latestVersion >= latestVersion)
41196
+ return committed;
41197
+ }
41198
+ return { latestVersion, successAt };
41199
+ }
41200
+ async function updateCache(file, update) {
41201
+ try {
41202
+ return await withFileLock2(`${file}.lock`, async () => saveCache(file, update(parseCache(file) ?? {})), {
41203
+ timeoutMs: CACHE_LOCK_TIMEOUT_MS,
41204
+ retryMs: CACHE_LOCK_RETRY_MS,
41205
+ staleMs: CACHE_LOCK_STALE_MS
41206
+ });
41207
+ } catch {
41208
+ return false;
41209
+ }
41210
+ }
41211
+ async function checkVersion(options) {
41212
+ const current = parseSemVer(options.currentVersion);
41213
+ if (current === undefined)
41214
+ return unavailable(options.currentVersion, "invalid-current-version");
41215
+ const now = options.now ?? Date.now;
41216
+ const timestamp = now();
41217
+ const configDir = ensureKeryxConfigDir(options.cacheDir);
41218
+ const cacheFile = path121.join(configDir, "version-check.json");
41219
+ const cache = parseCache(cacheFile);
41220
+ if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
41221
+ return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
41222
+ }
41223
+ if (cache?.failureAt !== undefined && timestamp - cache.failureAt >= 0 && timestamp - cache.failureAt < FAILURE_BACKOFF_MS) {
41224
+ return unavailable(options.currentVersion, "suppressed", cache);
41225
+ }
41226
+ const fetchImpl = options.fetch ?? globalThis.fetch;
41227
+ const controller = new AbortController;
41228
+ const timer = options.timer ?? SYSTEM_TIMER;
41229
+ let timeout;
41230
+ let timeoutScheduled = false;
41231
+ let failure;
41232
+ let latestVersion;
41233
+ try {
41234
+ timeout = timer.schedule(() => controller.abort(), REQUEST_TIMEOUT_MS);
41235
+ timeoutScheduled = true;
41236
+ const response = await fetchImpl(REGISTRY_URL, {
41237
+ method: "GET",
41238
+ headers: { accept: "application/json" },
41239
+ signal: controller.signal,
41240
+ credentials: "omit"
41241
+ });
41242
+ if (!response.ok) {
41243
+ failure = "http";
41244
+ } else {
41245
+ const body = await boundedResponseText(response);
41246
+ if (body === undefined) {
41247
+ failure = "response-too-large";
41248
+ } else {
41249
+ let parsed;
41250
+ try {
41251
+ parsed = JSON.parse(body);
41252
+ } catch {
41253
+ parsed = undefined;
41254
+ }
41255
+ if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
41256
+ failure = "malformed-response";
41257
+ } else {
41258
+ const record = parsed;
41259
+ if (record.name !== "@mrciphersmith/keryx" || typeof record.version !== "string") {
41260
+ failure = "malformed-response";
41261
+ } else if (parseSemVer(record.version) === undefined) {
41262
+ failure = "invalid-latest-version";
41263
+ } else {
41264
+ latestVersion = record.version;
41265
+ }
41266
+ }
41267
+ }
41268
+ }
41269
+ } catch {
41270
+ failure = controller.signal.aborted ? "timeout" : "network";
41271
+ } finally {
41272
+ if (timeoutScheduled) {
41273
+ try {
41274
+ timer.cancel(timeout);
41275
+ } catch {}
41276
+ }
41277
+ }
41278
+ if (latestVersion === undefined) {
41279
+ await updateCache(cacheFile, (committed) => ({ ...committed, failureAt: timestamp }));
41280
+ return unavailable(options.currentVersion, failure ?? "network", cache);
41281
+ }
41282
+ await updateCache(cacheFile, (committed) => mergeSuccessfulCache(committed, latestVersion, timestamp));
41283
+ return resultFor(options.currentVersion, current, latestVersion, "registry");
41284
+ }
41285
+
40947
41286
  // src/tui/shell-chrome.ts
40948
41287
  function onKeypress2(r, handler) {
40949
41288
  r._internalKeyInput.onInternal("keypress", handler);
@@ -40957,6 +41296,14 @@ var SIDEBAR_BORDER_LEFT = 1;
40957
41296
  var SIDEBAR_PADDING_LEFT = 2;
40958
41297
  var SIDEBAR_PADDING_RIGHT = 1;
40959
41298
  var SIDEBAR_TEXT_WIDTH = SIDEBAR_WIDTH - SIDEBAR_BORDER_LEFT - SIDEBAR_PADDING_LEFT - SIDEBAR_PADDING_RIGHT;
41299
+ function formatSidebarVersionUpdateAdvisory(result) {
41300
+ const advisory = formatVersionUpdateAdvisory(result);
41301
+ if (advisory === undefined || result.status !== "update-available")
41302
+ return;
41303
+ const split = Math.ceil(result.installCommand.length / 2);
41304
+ return advisory.replace(result.installCommand, `${result.installCommand.slice(0, split)}
41305
+ ${result.installCommand.slice(split)}`);
41306
+ }
40960
41307
  var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
40961
41308
  var SPINNER_MS = 120;
40962
41309
  var TOAST_MS = 5000;
@@ -40986,6 +41333,7 @@ async function createShellChrome(otui, renderer, opts) {
40986
41333
  const toastMs = opts.toastMs ?? TOAST_MS;
40987
41334
  const filter = opts.filterCommands ?? ((query) => prefixFilter(opts.commands, query));
40988
41335
  let uid = 0;
41336
+ let alive = true;
40989
41337
  const rootRow = new otui.BoxRenderable(r, { id: "root-row", flexGrow: 1, flexDirection: "row" });
40990
41338
  r.root.add(rootRow);
40991
41339
  const main = new otui.BoxRenderable(r, { id: "main", flexGrow: 1, minWidth: 0, flexDirection: "column" });
@@ -41004,6 +41352,21 @@ async function createShellChrome(otui, renderer, opts) {
41004
41352
  rootRow.add(sidebar);
41005
41353
  const sidebarTop = new otui.BoxRenderable(r, { id: "sb-top", flexShrink: 0, flexDirection: "column" });
41006
41354
  sidebar.add(sidebarTop);
41355
+ if (opts.versionCheck !== undefined) {
41356
+ const versionNotice = new otui.TextRenderable(r, {
41357
+ id: `sb-version-${uid++}`,
41358
+ content: ""
41359
+ });
41360
+ sidebarTop.add(versionNotice);
41361
+ opts.versionCheck.then((result) => {
41362
+ const advisory = formatSidebarVersionUpdateAdvisory(result);
41363
+ if (!alive || advisory === undefined)
41364
+ return;
41365
+ try {
41366
+ versionNotice.content = otui.t`${otui.yellow(advisory)}`;
41367
+ } catch {}
41368
+ }, () => {});
41369
+ }
41007
41370
  const sidebarSpacer = new otui.BoxRenderable(r, { id: "sb-spacer", flexGrow: 1 });
41008
41371
  sidebar.add(sidebarSpacer);
41009
41372
  const toastText = new otui.TextRenderable(r, { id: "sb-toast", content: "" });
@@ -41384,6 +41747,7 @@ async function createShellChrome(otui, renderer, opts) {
41384
41747
  };
41385
41748
  },
41386
41749
  destroy: () => {
41750
+ alive = false;
41387
41751
  clearBusyTimer();
41388
41752
  clearToastTimer();
41389
41753
  unsubscribeMenuKeys();
@@ -42847,7 +43211,8 @@ async function launchTuiAgentShell(opts) {
42847
43211
  placeholder: "type a task or / for commands \xB7 Enter send \xB7 Shift+Enter newline",
42848
43212
  commands: commandsForMode("agent"),
42849
43213
  headerMeta: "\u21910 \u21930",
42850
- filterCommands: (query) => filterCommands(query, "agent")
43214
+ filterCommands: (query) => filterCommands(query, "agent"),
43215
+ ...opts.versionCheck !== undefined ? { versionCheck: opts.versionCheck } : {}
42851
43216
  });
42852
43217
  mountedChrome = chrome;
42853
43218
  const transcript = chrome.transcript;
@@ -43905,7 +44270,8 @@ async function mountChatShell(otui, renderer, opts) {
43905
44270
  placeholder: "type a message or / for commands \xB7 Enter send \xB7 Shift+Enter newline",
43906
44271
  commands: commandsForMode("chat"),
43907
44272
  headerMeta: "~0",
43908
- filterCommands: (query) => filterCommands(query, "chat")
44273
+ filterCommands: (query) => filterCommands(query, "chat"),
44274
+ ...opts.versionCheck !== undefined ? { versionCheck: opts.versionCheck } : {}
43909
44275
  });
43910
44276
  let uid = 0;
43911
44277
  const transcript = chrome.transcript;
@@ -44057,7 +44423,8 @@ async function launchTuiChatShell(opts) {
44057
44423
  },
44058
44424
  onExit: () => {
44059
44425
  r.destroy();
44060
- }
44426
+ },
44427
+ ...opts.versionCheck !== undefined ? { versionCheck: opts.versionCheck } : {}
44061
44428
  });
44062
44429
  await Promise.race([exited, handle.done]);
44063
44430
  return true;
@@ -44072,6 +44439,76 @@ async function launchTuiChatShell(opts) {
44072
44439
 
44073
44440
  // src/commands/shell.ts
44074
44441
  init_shell_config();
44442
+ // package.json
44443
+ var package_default = {
44444
+ name: "@mrciphersmith/keryx",
44445
+ version: "0.2.19",
44446
+ description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
44447
+ private: false,
44448
+ publishConfig: {
44449
+ access: "public"
44450
+ },
44451
+ license: "MIT",
44452
+ type: "module",
44453
+ repository: {
44454
+ type: "git",
44455
+ url: "git+ssh://git@github.com/MrCipherSmith/keryx.git"
44456
+ },
44457
+ keywords: [
44458
+ "ai-agents",
44459
+ "coding-agents",
44460
+ "agent-harness",
44461
+ "agent-context",
44462
+ "repository-context",
44463
+ "code-graph",
44464
+ "project-memory",
44465
+ "test-impact-analysis",
44466
+ "developer-tools",
44467
+ "model-context-protocol",
44468
+ "mcp",
44469
+ "claude-code",
44470
+ "cursor",
44471
+ "codex",
44472
+ "cli",
44473
+ "bun"
44474
+ ],
44475
+ bin: {
44476
+ keryx: "./dist/cli.js"
44477
+ },
44478
+ scripts: {
44479
+ keryx: "bun ./src/cli.ts",
44480
+ build: "bun build ./src/cli.ts --outdir ./dist --target bun --external @modelcontextprotocol/sdk --external web-tree-sitter --external @opentui/core && bun build ./src/harness/process/sandbox/proxy-worker.ts --outdir ./dist --target bun --external @modelcontextprotocol/sdk --external web-tree-sitter --external @opentui/core",
44481
+ prepare: "bun run build",
44482
+ typecheck: "tsc --noEmit",
44483
+ test: "bun test",
44484
+ check: "tsc --noEmit && bun test",
44485
+ "check:doc-links": "bun scripts/check-doc-links.ts",
44486
+ "test:guards": "bun test src/lib/config-dir.ast.test.ts src/lib/config-dir.readers.test.ts src/lib/production-graph.test.ts src/harness/policy/profiles.test.ts src/lib/serve-server.test.ts"
44487
+ },
44488
+ files: [
44489
+ "dist",
44490
+ "src/gdgraph",
44491
+ "src/gdskills/bundled",
44492
+ "src/gdskills/contracts",
44493
+ "LICENSE",
44494
+ "README.md",
44495
+ "package.json"
44496
+ ],
44497
+ dependencies: {},
44498
+ optionalDependencies: {
44499
+ "@modelcontextprotocol/sdk": "^1.0.0",
44500
+ "@opentui/core": "^0.4.5",
44501
+ "web-tree-sitter": "^0.22.0"
44502
+ },
44503
+ devDependencies: {
44504
+ "@types/bun": "latest",
44505
+ "bun-types": "latest",
44506
+ typescript: "^5"
44507
+ },
44508
+ engines: {
44509
+ bun: ">=1.1.0"
44510
+ }
44511
+ };
44075
44512
 
44076
44513
  // src/commands/select.ts
44077
44514
  init_guard2();
@@ -44128,6 +44565,7 @@ async function probeOllamaModels(deps, baseUrl) {
44128
44565
  }
44129
44566
  async function detectProviders(deps) {
44130
44567
  const baseUrl = deps.baseUrl ?? DEFAULT_OLLAMA_BASE_URL;
44568
+ const platform = deps.platform ?? process.platform;
44131
44569
  const detected = [];
44132
44570
  const ollamaModels = await probeOllamaModels(deps, baseUrl);
44133
44571
  if (ollamaModels !== undefined) {
@@ -44138,15 +44576,18 @@ async function detectProviders(deps) {
44138
44576
  detected.push({ name: "anthropic", models: [...ANTHROPIC_MODELS] });
44139
44577
  }
44140
44578
  for (const p of OPENAI_COMPAT_PROVIDERS) {
44579
+ if (!isProviderPlatformSupported(p, platform)) {
44580
+ continue;
44581
+ }
44141
44582
  detected.push({
44142
44583
  name: p.name,
44143
44584
  models: [...p.models],
44144
44585
  baseUrl: p.baseUrl,
44145
- envKey: p.envKey,
44146
44586
  label: p.label,
44147
44587
  ...p.chatPath !== undefined ? { chatPath: p.chatPath } : {},
44148
44588
  ...p.modelsPath !== undefined ? { modelsPath: p.modelsPath } : {},
44149
- ...p.note !== undefined ? { note: p.note } : {}
44589
+ ...p.note !== undefined ? { note: p.note } : {},
44590
+ ...p.envKey !== undefined ? { envKey: p.envKey } : {}
44150
44591
  });
44151
44592
  }
44152
44593
  detected.push({ name: "fake", models: [...FAKE_MODELS] });
@@ -44380,6 +44821,7 @@ Starting a new session.
44380
44821
  provider = makeActive();
44381
44822
  };
44382
44823
  for await (const line of io.lines) {
44824
+ io.onSafeBoundary?.();
44383
44825
  if (line.startsWith("/")) {
44384
44826
  const parts = line.trim().split(/\s+/);
44385
44827
  const command = parts[0] ?? "";
@@ -44528,6 +44970,7 @@ Starting a new session.
44528
44970
  } else {
44529
44971
  history.pop();
44530
44972
  }
44973
+ io.onSafeBoundary?.();
44531
44974
  io.write(`
44532
44975
 
44533
44976
  `);
@@ -44560,6 +45003,7 @@ function realSelectProviderModel(baseUrl) {
44560
45003
  const detected = await detectProviders({
44561
45004
  fetch: globalThis.fetch,
44562
45005
  env: process.env,
45006
+ platform: process.platform,
44563
45007
  ...baseUrl !== undefined ? { baseUrl } : {}
44564
45008
  });
44565
45009
  const filtered = opts?.onlyProvider !== undefined ? detected.filter((d) => d.name === opts.onlyProvider) : detected;
@@ -44609,7 +45053,32 @@ function countRows(text, columns) {
44609
45053
  }
44610
45054
  return rows;
44611
45055
  }
44612
- function createRichIo(lines) {
45056
+ function createVersionAdvisoryBoundary(versionCheck) {
45057
+ let active = true;
45058
+ let pending;
45059
+ let shown = false;
45060
+ versionCheck.then((result) => {
45061
+ if (active)
45062
+ pending = formatVersionUpdateAdvisory(result);
45063
+ }, () => {});
45064
+ return {
45065
+ flush: (write) => {
45066
+ if (!active || shown || pending === undefined)
45067
+ return;
45068
+ shown = true;
45069
+ const advisory = pending;
45070
+ pending = undefined;
45071
+ write(`
45072
+ ${advisory}
45073
+ `);
45074
+ },
45075
+ destroy: () => {
45076
+ active = false;
45077
+ pending = undefined;
45078
+ }
45079
+ };
45080
+ }
45081
+ function createRichIo(lines, versionCheck) {
44613
45082
  const stdout2 = process.stdout;
44614
45083
  const rich = colorEnabled() && Boolean(stdout2.isTTY);
44615
45084
  const out = (s) => {
@@ -44619,6 +45088,7 @@ function createRichIo(lines) {
44619
45088
  let frame = 0;
44620
45089
  let awaitingFirstToken = false;
44621
45090
  let raw = "";
45091
+ const advisory = createVersionAdvisoryBoundary(versionCheck);
44622
45092
  const stopSpinner = () => {
44623
45093
  if (spinner !== undefined) {
44624
45094
  clearInterval(spinner);
@@ -44702,10 +45172,18 @@ ${GUTTER}${style.cyan("\u25C6")} ${style.bold(title)} ${style.dim(subtitle)}
44702
45172
 
44703
45173
  `);
44704
45174
  }
45175
+ advisory.flush(emitSystem);
44705
45176
  printPrompt();
44706
45177
  };
44707
- const io = { lines, write, onTurnStart, onTurnEnd, onSystem: emitSystem };
44708
- return { io, emitSystem, printHeader, printPrompt };
45178
+ const io = {
45179
+ lines,
45180
+ write,
45181
+ onTurnStart,
45182
+ onTurnEnd,
45183
+ onSystem: emitSystem,
45184
+ onSafeBoundary: () => advisory.flush(emitSystem)
45185
+ };
45186
+ return { io, emitSystem, printHeader, printPrompt, destroy: advisory.destroy };
44709
45187
  }
44710
45188
  function turnSeparator() {
44711
45189
  return style.dim("\u2500".repeat(24));
@@ -44926,6 +45404,7 @@ New session ${shortSessionId(live.summary.id)}.
44926
45404
  if (line === undefined) {
44927
45405
  return;
44928
45406
  }
45407
+ rich.safeBoundary?.();
44929
45408
  if (line.startsWith("/")) {
44930
45409
  const parts = line.trim().split(/\s+/);
44931
45410
  const command = parts[0] ?? "";
@@ -45022,6 +45501,7 @@ ${GUTTER}${usageLine}
45022
45501
  ${GUTTER}${turnSeparator()}
45023
45502
 
45024
45503
  `);
45504
+ rich.safeBoundary?.();
45025
45505
  rich.printPrompt();
45026
45506
  }
45027
45507
  }
@@ -45100,13 +45580,17 @@ function chooseShellSurface(flags, isTty) {
45100
45580
  }
45101
45581
  return flags.modeFlag === false ? "tui-chat" : "tui-agent";
45102
45582
  }
45103
- async function shellCommand(args2) {
45583
+ async function shellCommand(args2, runtime = {}) {
45584
+ const versionCheck = (runtime.checkVersion ?? (() => checkVersion({
45585
+ currentVersion: package_default.version,
45586
+ ...runtime.cacheDir !== undefined ? { cacheDir: runtime.cacheDir } : {}
45587
+ })))();
45104
45588
  const flags = parseShellCliFlags(args2);
45105
45589
  let providerArg = flags.providerArg;
45106
45590
  let modelArg = flags.modelArg;
45107
45591
  let baseUrl = flags.baseUrl;
45108
45592
  let modeFlag = flags.modeFlag;
45109
- const surface = chooseShellSurface(flags, process.stdout.isTTY === true);
45593
+ const surface = chooseShellSurface(flags, runtime.isTty ?? process.stdout.isTTY === true);
45110
45594
  if (surface !== "readline") {
45111
45595
  const cwd = process.cwd();
45112
45596
  const tuiProviderFactory = realMakeProvider(() => {});
@@ -45151,7 +45635,7 @@ async function shellCommand(args2) {
45151
45635
  modelId: sel.model
45152
45636
  }),
45153
45637
  maxToolCalls: resolveAgentMaxToolCalls(),
45154
- idSeq: () => randomUUID9()
45638
+ idSeq: () => randomUUID10()
45155
45639
  };
45156
45640
  };
45157
45641
  const redetect = () => detectProviders({
@@ -45163,7 +45647,8 @@ async function shellCommand(args2) {
45163
45647
  providerArg,
45164
45648
  modelArg,
45165
45649
  baseUrl,
45166
- detect: redetect
45650
+ detect: redetect,
45651
+ ...runtime.cacheDir !== undefined ? { configDir: runtime.cacheDir } : {}
45167
45652
  });
45168
45653
  const tuiInitial = startup.initial;
45169
45654
  const tuiDetected = startup.detected;
@@ -45173,7 +45658,7 @@ async function shellCommand(args2) {
45173
45658
  if (flags.resumePick === true && chatResumeId === undefined) {
45174
45659
  chatResumeId = latestSession(cwd)?.id;
45175
45660
  }
45176
- if (await launchTuiChatShell({
45661
+ if (await (runtime.launchChat ?? launchTuiChatShell)({
45177
45662
  detected: tuiDetected,
45178
45663
  redetect,
45179
45664
  ...tuiInitial !== undefined ? { initial: tuiInitial } : {},
@@ -45181,18 +45666,19 @@ async function shellCommand(args2) {
45181
45666
  makeShellDeps: (sel) => ({
45182
45667
  makeProvider: chatFactory,
45183
45668
  clock: () => new Date().toISOString(),
45184
- idSeq: () => randomUUID9(),
45669
+ idSeq: () => randomUUID10(),
45185
45670
  initial: sel,
45186
45671
  session: {
45187
45672
  cwd,
45188
45673
  ...flags.continueLast === true ? { continueLast: true } : {},
45189
45674
  ...chatResumeId !== undefined ? { resumeId: chatResumeId } : {}
45190
45675
  }
45191
- })
45676
+ }),
45677
+ versionCheck
45192
45678
  })) {
45193
45679
  return;
45194
45680
  }
45195
- } else if (await launchTuiAgentShell({
45681
+ } else if (await (runtime.launchAgent ?? launchTuiAgentShell)({
45196
45682
  detected: tuiDetected,
45197
45683
  makeAgentDeps,
45198
45684
  redetect,
@@ -45202,7 +45688,8 @@ async function shellCommand(args2) {
45202
45688
  ...flags.continueLast === true ? { continueLast: true } : {},
45203
45689
  ...flags.resumeId !== undefined ? { resumeId: flags.resumeId } : {},
45204
45690
  ...flags.resumePick === true ? { pickOnStart: true } : {}
45205
- }
45691
+ },
45692
+ versionCheck
45206
45693
  })) {
45207
45694
  return;
45208
45695
  }
@@ -45210,7 +45697,7 @@ async function shellCommand(args2) {
45210
45697
  const rl = readline2.createInterface({ input: process.stdin });
45211
45698
  const lineIterator = rl[Symbol.asyncIterator]();
45212
45699
  const sharedLines = { [Symbol.asyncIterator]: () => lineIterator };
45213
- const { io, emitSystem, printHeader, printPrompt } = createRichIo(sharedLines);
45700
+ const { io, emitSystem, printHeader, printPrompt, destroy } = createRichIo(sharedLines, versionCheck);
45214
45701
  let provider;
45215
45702
  let model;
45216
45703
  try {
@@ -45249,7 +45736,7 @@ async function shellCommand(args2) {
45249
45736
  const deps = {
45250
45737
  makeProvider: baseFactory,
45251
45738
  clock: () => new Date().toISOString(),
45252
- idSeq: () => randomUUID9(),
45739
+ idSeq: () => randomUUID10(),
45253
45740
  initial: baseUrl === undefined ? { provider, model } : { provider, model, baseUrl },
45254
45741
  selectProviderModel: realSelectProviderModel(baseUrl)
45255
45742
  };
@@ -45293,13 +45780,13 @@ async function shellCommand(args2) {
45293
45780
  modelId: model
45294
45781
  }),
45295
45782
  maxToolCalls: resolveAgentMaxToolCalls(),
45296
- idSeq: () => randomUUID9()
45783
+ idSeq: () => randomUUID10()
45297
45784
  };
45298
45785
  let resumeId = flags.resumeId;
45299
45786
  if (flags.resumePick === true && resumeId === undefined) {
45300
45787
  resumeId = latestSession(process.cwd())?.id;
45301
45788
  }
45302
- await runAgentRepl(sharedLines, { printPrompt }, agentDeps, metaprojectPort, {
45789
+ await runAgentRepl(sharedLines, { printPrompt, safeBoundary: io.onSafeBoundary }, agentDeps, metaprojectPort, {
45303
45790
  cwd: process.cwd(),
45304
45791
  ...flags.continueLast === true ? { continueLast: true } : {},
45305
45792
  ...resumeId !== undefined ? { resumeId } : {}
@@ -45319,6 +45806,7 @@ async function shellCommand(args2) {
45319
45806
  });
45320
45807
  }
45321
45808
  } finally {
45809
+ destroy();
45322
45810
  rl.close();
45323
45811
  }
45324
45812
  }
@@ -45463,7 +45951,7 @@ Shell:
45463
45951
  init_fs();
45464
45952
  import { readFile as readFile63 } from "fs/promises";
45465
45953
  import { stdin } from "process";
45466
- import path121 from "path";
45954
+ import path122 from "path";
45467
45955
  var MODULES = [
45468
45956
  { name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
45469
45957
  { name: "gdctx", flag: "--no-gdctx", desc: "token-aware command/read output", defaultEnabled: true },
@@ -45502,8 +45990,8 @@ async function modulesCommand(args2 = []) {
45502
45990
  return;
45503
45991
  }
45504
45992
  const wantsJson = args2.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
45505
- const metaprojectRoot = path121.join(process.cwd(), ".metaproject");
45506
- const manifestPath = path121.join(metaprojectRoot, "metaproject.json");
45993
+ const metaprojectRoot = path122.join(process.cwd(), ".metaproject");
45994
+ const manifestPath = path122.join(metaprojectRoot, "metaproject.json");
45507
45995
  if (!await pathExists(manifestPath)) {
45508
45996
  if (wantsJson) {
45509
45997
  console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
@@ -45617,18 +46105,18 @@ function printHelp16() {
45617
46105
  }
45618
46106
 
45619
46107
  // src/commands/serve.ts
45620
- import { randomUUID as randomUUID12 } from "crypto";
46108
+ import { randomUUID as randomUUID13 } from "crypto";
45621
46109
 
45622
46110
  // src/lib/serve-config.ts
45623
46111
  init_config_dir();
45624
46112
  import { existsSync as existsSync24 } from "fs";
45625
- import path122 from "path";
46113
+ import path123 from "path";
45626
46114
  var SERVE_CONFIG_SCHEMA_VERSION = "1.0.0";
45627
46115
  var DEFAULT_SERVE_BIND_ADDRESS = "127.0.0.1";
45628
46116
  var DEFAULT_SERVE_PORT = 7377;
45629
46117
  var DEFAULT_SERVE_PROFILE = "remote-restricted";
45630
46118
  function serveConfigPath(dir) {
45631
- return path122.join(keryxConfigDir(dir), "serve.json");
46119
+ return path123.join(keryxConfigDir(dir), "serve.json");
45632
46120
  }
45633
46121
  function parseIpv4(value) {
45634
46122
  const parts = value.split(".");
@@ -45944,21 +46432,21 @@ function saveServeConfig(config, dir, onWarn) {
45944
46432
 
45945
46433
  // src/lib/serve-credential.ts
45946
46434
  init_config_dir();
45947
- import { createHash as createHash19, randomBytes as randomBytes2, randomUUID as randomUUID10 } from "crypto";
46435
+ import { createHash as createHash19, randomBytes as randomBytes2, randomUUID as randomUUID11 } from "crypto";
45948
46436
  import {
45949
46437
  chmodSync as chmodSync4,
45950
46438
  closeSync as closeSync3,
45951
46439
  existsSync as existsSync25,
45952
46440
  fsyncSync as fsyncSync2,
45953
46441
  openSync as openSync3,
45954
- renameSync as renameSync3,
46442
+ renameSync as renameSync4,
45955
46443
  statSync as statSync5,
45956
- unlinkSync as unlinkSync2,
46444
+ unlinkSync as unlinkSync3,
45957
46445
  writeFileSync as writeFileSync8
45958
46446
  } from "fs";
45959
- import path123 from "path";
46447
+ import path124 from "path";
45960
46448
  function serveCredentialPath(dir) {
45961
- return path123.join(keryxConfigDir(dir), "serve-credentials.json");
46449
+ return path124.join(keryxConfigDir(dir), "serve-credentials.json");
45962
46450
  }
45963
46451
  function constantTimeEqual(a, b) {
45964
46452
  const width = Math.max(a.length, b.length);
@@ -46028,7 +46516,7 @@ function readServeCredential(dir) {
46028
46516
  }
46029
46517
  function writeStore(store, dir) {
46030
46518
  const file = serveCredentialPath(dir);
46031
- const temp = `${file}.${randomUUID10()}.tmp`;
46519
+ const temp = `${file}.${randomUUID11()}.tmp`;
46032
46520
  try {
46033
46521
  ensureKeryxConfigDir(dir);
46034
46522
  const handle = openSync3(temp, "wx", 384);
@@ -46039,12 +46527,12 @@ function writeStore(store, dir) {
46039
46527
  } finally {
46040
46528
  closeSync3(handle);
46041
46529
  }
46042
- renameSync3(temp, file);
46530
+ renameSync4(temp, file);
46043
46531
  tighten2(file, 384);
46044
46532
  return true;
46045
46533
  } catch {
46046
46534
  try {
46047
- unlinkSync2(temp);
46535
+ unlinkSync3(temp);
46048
46536
  } catch {}
46049
46537
  return false;
46050
46538
  }
@@ -46068,7 +46556,7 @@ function mintRecord(now) {
46068
46556
  const salt = randomBytes2(32).toString("hex");
46069
46557
  return {
46070
46558
  token,
46071
- record: { id: randomUUID10(), algorithm: "sha256", salt, hash: hashToken(salt, token), createdAt: now }
46559
+ record: { id: randomUUID11(), algorithm: "sha256", salt, hash: hashToken(salt, token), createdAt: now }
46072
46560
  };
46073
46561
  }
46074
46562
  function issueServeToken(dir, now = () => new Date().toISOString(), onWaiting) {
@@ -46189,22 +46677,22 @@ class AuthFailureThrottle {
46189
46677
  init_config_dir();
46190
46678
  import { createHash as createHash20 } from "crypto";
46191
46679
  import { existsSync as existsSync26, readdirSync as readdirSync2, rmSync as rmSync2 } from "fs";
46192
- import path124 from "path";
46680
+ import path125 from "path";
46193
46681
  var MAX_TURN_EVENTS = 1e4;
46194
46682
  function turnsRoot(dir) {
46195
- return path124.join(keryxConfigDir(dir), "turns");
46683
+ return path125.join(keryxConfigDir(dir), "turns");
46196
46684
  }
46197
46685
  function turnDir(turnId, dir) {
46198
- return path124.join(turnsRoot(dir), turnId);
46686
+ return path125.join(turnsRoot(dir), turnId);
46199
46687
  }
46200
46688
  function keyPath(project, idempotencyKey, dir) {
46201
46689
  const projectBytes = Buffer.byteLength(project, "utf8");
46202
46690
  const digest = createHash20("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
46203
- return path124.join(turnsRoot(dir), "keys", `${digest}.json`);
46691
+ return path125.join(turnsRoot(dir), "keys", `${digest}.json`);
46204
46692
  }
46205
46693
  function legacyKeyPath(idempotencyKey, dir) {
46206
46694
  const digest = createHash20("sha256").update(idempotencyKey, "utf8").digest("hex");
46207
- return path124.join(turnsRoot(dir), "keys", `${digest}.json`);
46695
+ return path125.join(turnsRoot(dir), "keys", `${digest}.json`);
46208
46696
  }
46209
46697
  function adoptLegacyClaim(project, idempotencyKey, dir) {
46210
46698
  const legacy = legacyKeyPath(idempotencyKey, dir);
@@ -46268,7 +46756,7 @@ function ensureTurnDir(turnId, dir) {
46268
46756
  }
46269
46757
  function createTurnRecord(record, dir) {
46270
46758
  ensureTurnDir(record.turnId, dir);
46271
- writeOwnerOnlyFile(path124.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
46759
+ writeOwnerOnlyFile(path125.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
46272
46760
  `);
46273
46761
  }
46274
46762
  function appendTurnEvent(event, dir, opts) {
@@ -46277,12 +46765,12 @@ function appendTurnEvent(event, dir, opts) {
46277
46765
  }
46278
46766
  const line = JSON.stringify(event);
46279
46767
  try {
46280
- appendOwnerOnlyLine(path124.join(turnDir(event.turnId, dir), "events.jsonl"), line);
46768
+ appendOwnerOnlyLine(path125.join(turnDir(event.turnId, dir), "events.jsonl"), line);
46281
46769
  } catch (error) {
46282
46770
  if (error?.code !== "ENOENT") {
46283
46771
  throw error;
46284
46772
  }
46285
- appendOwnerOnlyLine(path124.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
46773
+ appendOwnerOnlyLine(path125.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
46286
46774
  }
46287
46775
  return true;
46288
46776
  }
@@ -46290,7 +46778,7 @@ function readTurnEvents(turnId, after = -1, dir) {
46290
46778
  if (!isTurnId(turnId)) {
46291
46779
  return { ok: false, reason: "not-a-turn-id" };
46292
46780
  }
46293
- const read = readTurnFile(path124.join(turnDir(turnId, dir), "events.jsonl"));
46781
+ const read = readTurnFile(path125.join(turnDir(turnId, dir), "events.jsonl"));
46294
46782
  if (!read.ok) {
46295
46783
  if (isDefiniteAbsence2(read.reason)) {
46296
46784
  return { ok: true, value: [] };
@@ -46318,7 +46806,7 @@ function readTurnRecord(turnId, dir) {
46318
46806
  if (!isTurnId(turnId)) {
46319
46807
  return { ok: false, reason: "not-a-turn-id" };
46320
46808
  }
46321
- const read = readTurnFile(path124.join(turnDir(turnId, dir), "turn.json"));
46809
+ const read = readTurnFile(path125.join(turnDir(turnId, dir), "turn.json"));
46322
46810
  if (!read.ok) {
46323
46811
  return { ok: false, reason: read.reason };
46324
46812
  }
@@ -46337,7 +46825,7 @@ function finishTurn(turnId, result, dir) {
46337
46825
  if (!record.ok) {
46338
46826
  return false;
46339
46827
  }
46340
- writeOwnerOnlyFile(path124.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
46828
+ writeOwnerOnlyFile(path125.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
46341
46829
  `);
46342
46830
  return true;
46343
46831
  }
@@ -46382,8 +46870,8 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
46382
46870
  }
46383
46871
 
46384
46872
  // src/lib/serve-turn.ts
46385
- import { randomUUID as randomUUID11 } from "crypto";
46386
- import path125 from "path";
46873
+ import { randomUUID as randomUUID12 } from "crypto";
46874
+ import path126 from "path";
46387
46875
  init_service();
46388
46876
  var REMOTE_ORIGIN = "remote:http";
46389
46877
  var MAX_PROMPT_CHARS = 32000;
@@ -46452,9 +46940,9 @@ function isUuid(value) {
46452
46940
  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);
46453
46941
  }
46454
46942
  function resolveProject(declared, dir) {
46455
- const wanted = path125.resolve(declared);
46943
+ const wanted = path126.resolve(declared);
46456
46944
  for (const entry of listProjects(dir, () => {})) {
46457
- if (path125.resolve(entry.path) === wanted) {
46945
+ if (path126.resolve(entry.path) === wanted) {
46458
46946
  return { ok: true, project: entry.path };
46459
46947
  }
46460
46948
  }
@@ -46501,7 +46989,7 @@ async function redactOut(security, text) {
46501
46989
  async function runRemoteTurn(input2) {
46502
46990
  const scanRoot = input2.scanRoot;
46503
46991
  const security = createSecurityService(scanRoot);
46504
- const newId = input2.newId ?? (() => randomUUID11());
46992
+ const newId = input2.newId ?? (() => randomUUID12());
46505
46993
  const clock = input2.clock ?? (() => new Date().toISOString());
46506
46994
  const turnId = input2.turnId ?? newId();
46507
46995
  const sessionId = input2.request.sessionId ?? newId();
@@ -46644,7 +47132,7 @@ function outcomeOf(status, gate, unresolvedBlockerIds) {
46644
47132
  }
46645
47133
  function createSubmitTurn(deps) {
46646
47134
  return async (request, project) => {
46647
- const turnId = (deps.newId ?? (() => randomUUID11()))();
47135
+ const turnId = (deps.newId ?? (() => randomUUID12()))();
46648
47136
  const scanned = await scanPrompt(deps.dir, request.prompt);
46649
47137
  if (scanned.rejected) {
46650
47138
  return { kind: "rejected" };
@@ -47331,7 +47819,7 @@ function runConfig(args2) {
47331
47819
  return;
47332
47820
  }
47333
47821
  const credential = readServeCredential();
47334
- const credentialId = credential.status === "ok" ? credential.record.id : randomUUID12();
47822
+ const credentialId = credential.status === "ok" ? credential.record.id : randomUUID13();
47335
47823
  const config = defaultServeConfig(credentialId, {
47336
47824
  address: parsed.parsed.values.get("--bind") ?? DEFAULT_SERVE_BIND_ADDRESS,
47337
47825
  port: port ?? DEFAULT_SERVE_PORT,
@@ -47489,7 +47977,7 @@ function printHelp17() {
47489
47977
  import { spawn as spawn5 } from "child_process";
47490
47978
  import { chmod as chmod4, mkdir as mkdir45, readFile as readFile64, readdir as readdir20, writeFile as writeFile42 } from "fs/promises";
47491
47979
  import { access as access3, constants, existsSync as existsSync27 } from "fs";
47492
- import path126 from "path";
47980
+ import path127 from "path";
47493
47981
  import { fileURLToPath as fileURLToPath6 } from "url";
47494
47982
  init_config();
47495
47983
  init_config2();
@@ -47504,8 +47992,8 @@ async function updateCommand(args2 = []) {
47504
47992
  return;
47505
47993
  }
47506
47994
  const projectRoot = process.cwd();
47507
- const metaprojectRoot = path126.join(projectRoot, ".metaproject");
47508
- banner("keryx update", `Refreshing the .metaproject workspace in ${path126.basename(projectRoot)}/`);
47995
+ const metaprojectRoot = path127.join(projectRoot, ".metaproject");
47996
+ banner("keryx update", `Refreshing the .metaproject workspace in ${path127.basename(projectRoot)}/`);
47509
47997
  if (!await pathExists(metaprojectRoot)) {
47510
47998
  console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
47511
47999
  console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
@@ -47548,12 +48036,12 @@ async function updateCommand(args2 = []) {
47548
48036
  nextSteps(steps);
47549
48037
  }
47550
48038
  async function refreshServiceFiles(projectRoot, options) {
47551
- const metaprojectRoot = path126.join(projectRoot, ".metaproject");
48039
+ const metaprojectRoot = path127.join(projectRoot, ".metaproject");
47552
48040
  const manifestState = await readManifest5(metaprojectRoot);
47553
48041
  const manifest = manifestState.manifest;
47554
48042
  const recoveredManifest = !manifestState.exists || !manifestState.valid;
47555
48043
  if (manifestState.migrated) {
47556
- await writeFile42(path126.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
48044
+ await writeFile42(path127.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
47557
48045
  `, "utf8");
47558
48046
  }
47559
48047
  const enableGdgraph = moduleEnabled2(manifest, "gdgraph");
@@ -47588,11 +48076,11 @@ async function refreshServiceFiles(projectRoot, options) {
47588
48076
  enableTasks,
47589
48077
  enableSecurity
47590
48078
  });
47591
- await writeTextIfChanged4(path126.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
47592
- await writeTextIfChanged4(path126.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
47593
- await writeTextIfChanged4(path126.join(metaprojectRoot, "rules", "README.md"), renderProjectRulesReadme());
47594
- await writeTextIfChanged4(path126.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
47595
- await writeTextIfChanged4(path126.join(metaprojectRoot, "index.md"), renderIndexMarkdown({
48079
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
48080
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
48081
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "rules", "README.md"), renderProjectRulesReadme());
48082
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
48083
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "index.md"), renderIndexMarkdown({
47596
48084
  enableGdgraph,
47597
48085
  enableGdctx,
47598
48086
  enableGdwiki,
@@ -47605,7 +48093,7 @@ async function refreshServiceFiles(projectRoot, options) {
47605
48093
  ruleSources,
47606
48094
  hasDistilledEntrypoints: await hasDistilledEntrypoints(metaprojectRoot)
47607
48095
  }));
47608
- await writeTextIfChanged4(path126.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
48096
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
47609
48097
  enableGdgraph,
47610
48098
  enableGdctx,
47611
48099
  enableGdwiki,
@@ -47617,7 +48105,7 @@ async function refreshServiceFiles(projectRoot, options) {
47617
48105
  enableSecurity,
47618
48106
  data: dashboardData
47619
48107
  }));
47620
- await writeTextIfMissing4(path126.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
48108
+ await writeTextIfMissing4(path127.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
47621
48109
  enableGdgraph,
47622
48110
  enableGdctx,
47623
48111
  enableGdwiki,
@@ -47630,24 +48118,24 @@ async function refreshServiceFiles(projectRoot, options) {
47630
48118
  }));
47631
48119
  if (enableGdgraph) {
47632
48120
  await installGdgraphCoreScripts2(metaprojectRoot);
47633
- await writeTextIfChanged4(path126.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
47634
- await writeTextIfChanged4(path126.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
47635
- await writeTextIfChanged4(path126.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
48121
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
48122
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
48123
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
47636
48124
  await seedAssetsLock(metaprojectRoot);
47637
48125
  if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
47638
48126
  await installManagedHook2(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
47639
48127
  }
47640
48128
  }
47641
48129
  if (enableGdctx) {
47642
- await writeTextIfMissing4(path126.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
47643
- await writeTextIfChanged4(path126.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
47644
- await writeTextIfChanged4(path126.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
47645
- await writeTextIfChanged4(path126.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
48130
+ await writeTextIfMissing4(path127.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
48131
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
48132
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
48133
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
47646
48134
  }
47647
48135
  if (enableGdwiki) {
47648
- await writeTextIfMissing4(path126.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
47649
- await writeTextIfChanged4(path126.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
47650
- await writeTextIfChanged4(path126.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
48136
+ await writeTextIfMissing4(path127.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
48137
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
48138
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
47651
48139
  if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
47652
48140
  await installManagedHook2(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
47653
48141
  }
@@ -47659,25 +48147,25 @@ async function refreshServiceFiles(projectRoot, options) {
47659
48147
  }
47660
48148
  }
47661
48149
  if (enableHealth) {
47662
- await writeTextIfMissing4(path126.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
47663
- await writeTextIfChanged4(path126.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
47664
- await writeTextIfChanged4(path126.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
47665
- await writeTextIfChanged4(path126.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
48150
+ await writeTextIfMissing4(path127.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
48151
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
48152
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
48153
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
47666
48154
  if (manifest.modules?.health?.hooks?.gitPostCommit) {
47667
48155
  await installManagedHook2(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
47668
48156
  }
47669
48157
  }
47670
48158
  if (enableTesting) {
47671
- await writeTextIfMissing4(path126.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
48159
+ await writeTextIfMissing4(path127.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
47672
48160
  postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
47673
48161
  prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
47674
48162
  }));
47675
- await writeTextIfChanged4(path126.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
47676
- await writeTextIfChanged4(path126.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
47677
- await writeTextIfChanged4(path126.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
48163
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
48164
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
48165
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
47678
48166
  if (enableGdwiki) {
47679
- await writeTextIfMissing4(path126.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
47680
- await writeTextIfMissing4(path126.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
48167
+ await writeTextIfMissing4(path127.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
48168
+ await writeTextIfMissing4(path127.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
47681
48169
  }
47682
48170
  if (manifest.modules?.testing?.hooks?.gitPostCommit) {
47683
48171
  await installManagedHook2(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
@@ -47690,24 +48178,24 @@ async function refreshServiceFiles(projectRoot, options) {
47690
48178
  await installManagedHook2(projectRoot, "post-commit", "metaproject-dashboard-post-commit", renderMetaprojectDashboardPostCommitHook());
47691
48179
  }
47692
48180
  if (enableMemory) {
47693
- await writeTextIfMissing4(path126.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
47694
- await writeTextIfMissing4(path126.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
47695
- await writeTextIfChanged4(path126.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
47696
- await writeTextIfChanged4(path126.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
47697
- await writeTextIfChanged4(path126.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
48181
+ await writeTextIfMissing4(path127.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
48182
+ await writeTextIfMissing4(path127.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
48183
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
48184
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
48185
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
47698
48186
  }
47699
48187
  if (enableTasks) {
47700
- await writeTextIfChanged4(path126.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
47701
- await writeTextIfChanged4(path126.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
47702
- await writeTextIfChanged4(path126.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
47703
- await writeTextIfChanged4(path126.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
47704
- await writeTextIfChanged4(path126.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
47705
- await writeTextIfChanged4(path126.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
48188
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
48189
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
48190
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
48191
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
48192
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
48193
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
47706
48194
  }
47707
48195
  if (enableSecurity) {
47708
- await writeTextIfMissing4(path126.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
47709
- await writeTextIfChanged4(path126.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
47710
- await writeTextIfChanged4(path126.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
48196
+ await writeTextIfMissing4(path127.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
48197
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
48198
+ await writeTextIfChanged4(path127.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
47711
48199
  if (manifest.modules?.security?.hooks?.prePush) {
47712
48200
  await installManagedHook2(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
47713
48201
  }
@@ -47756,13 +48244,13 @@ async function refreshServiceFiles(projectRoot, options) {
47756
48244
  };
47757
48245
  }
47758
48246
  async function buildDashboard(projectRoot = process.cwd()) {
47759
- const metaprojectRoot = path126.join(projectRoot, ".metaproject");
48247
+ const metaprojectRoot = path127.join(projectRoot, ".metaproject");
47760
48248
  if (!await pathExists(metaprojectRoot)) {
47761
48249
  throw new Error("Metaproject is not initialized. Run: keryx init");
47762
48250
  }
47763
48251
  const manifest = (await readManifest5(metaprojectRoot)).manifest;
47764
48252
  const data = await collectDashboardData(metaprojectRoot);
47765
- const dashboardPath = path126.join(metaprojectRoot, "keryx-dashboard.html");
48253
+ const dashboardPath = path127.join(metaprojectRoot, "keryx-dashboard.html");
47766
48254
  await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
47767
48255
  enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
47768
48256
  enableGdctx: moduleEnabled2(manifest, "gdctx"),
@@ -47782,7 +48270,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
47782
48270
  if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
47783
48271
  return true;
47784
48272
  }
47785
- const hookPath = path126.join(projectRoot, ".git", "hooks", "post-commit");
48273
+ const hookPath = path127.join(projectRoot, ".git", "hooks", "post-commit");
47786
48274
  if (!await pathExists(hookPath)) {
47787
48275
  return false;
47788
48276
  }
@@ -47802,11 +48290,11 @@ async function collectDashboardData(metaprojectRoot) {
47802
48290
  if (testing) {
47803
48291
  data.testing = testing;
47804
48292
  }
47805
- const wiki = await collectMarkdownPages(path126.join(metaprojectRoot, "wiki"), "wiki");
48293
+ const wiki = await collectMarkdownPages(path127.join(metaprojectRoot, "wiki"), "wiki");
47806
48294
  if (wiki.length > 0) {
47807
48295
  data.wiki = { pages: wiki };
47808
48296
  }
47809
- const memory = await collectMarkdownPages(path126.join(metaprojectRoot, "memory"), "memory");
48297
+ const memory = await collectMarkdownPages(path127.join(metaprojectRoot, "memory"), "memory");
47810
48298
  if (memory.length > 0) {
47811
48299
  data.memory = { entries: memory };
47812
48300
  }
@@ -47821,7 +48309,7 @@ async function collectDashboardData(metaprojectRoot) {
47821
48309
  return data;
47822
48310
  }
47823
48311
  async function collectTasksDashboardData(metaprojectRoot) {
47824
- const flowsRoot2 = path126.join(metaprojectRoot, "flows");
48312
+ const flowsRoot2 = path127.join(metaprojectRoot, "flows");
47825
48313
  if (!await pathExists(flowsRoot2)) {
47826
48314
  return null;
47827
48315
  }
@@ -47833,7 +48321,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
47833
48321
  }
47834
48322
  const flows = [];
47835
48323
  for (const dir of dirEntries) {
47836
- const flowPath = path126.join(flowsRoot2, dir, "flow.json");
48324
+ const flowPath = path127.join(flowsRoot2, dir, "flow.json");
47837
48325
  if (!await pathExists(flowPath)) {
47838
48326
  continue;
47839
48327
  }
@@ -47841,7 +48329,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
47841
48329
  const flow = JSON.parse(await readFile64(flowPath, "utf8"));
47842
48330
  const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
47843
48331
  let acTotal = 0;
47844
- const acPath2 = path126.join(flowsRoot2, dir, "acceptance-criteria.md");
48332
+ const acPath2 = path127.join(flowsRoot2, dir, "acceptance-criteria.md");
47845
48333
  if (await pathExists(acPath2)) {
47846
48334
  const acContent = await readFile64(acPath2, "utf8");
47847
48335
  acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
@@ -47894,7 +48382,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
47894
48382
  "data/testing/context.md"
47895
48383
  ];
47896
48384
  for (const href of staticHrefs) {
47897
- const filePath = path126.join(metaprojectRoot, ...href.split("/"));
48385
+ const filePath = path127.join(metaprojectRoot, ...href.split("/"));
47898
48386
  if (!await pathExists(filePath)) {
47899
48387
  continue;
47900
48388
  }
@@ -47911,7 +48399,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
47911
48399
  return docs;
47912
48400
  }
47913
48401
  async function collectHealthDashboardData(metaprojectRoot) {
47914
- const reportPath2 = path126.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
48402
+ const reportPath2 = path127.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
47915
48403
  if (!await pathExists(reportPath2)) {
47916
48404
  return;
47917
48405
  }
@@ -48020,8 +48508,8 @@ function metricToScope(metric) {
48020
48508
  };
48021
48509
  }
48022
48510
  async function collectGraphDashboardData(metaprojectRoot) {
48023
- const nodesPath = path126.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
48024
- const edgesPath = path126.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
48511
+ const nodesPath = path127.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
48512
+ const edgesPath = path127.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
48025
48513
  if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
48026
48514
  return;
48027
48515
  }
@@ -48072,8 +48560,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
48072
48560
  };
48073
48561
  }
48074
48562
  async function collectTestingDashboardData(metaprojectRoot) {
48075
- const reportPath2 = path126.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
48076
- const contextPath = path126.join(metaprojectRoot, "data", "testing", "context.md");
48563
+ const reportPath2 = path127.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
48564
+ const contextPath = path127.join(metaprojectRoot, "data", "testing", "context.md");
48077
48565
  if (await pathExists(reportPath2)) {
48078
48566
  const report = JSON.parse(await readFile64(reportPath2, "utf8"));
48079
48567
  const totalTests = numberOrUndefined(report.total);
@@ -48102,7 +48590,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
48102
48590
  const files = await listMarkdownFiles(root);
48103
48591
  const pages = [];
48104
48592
  for (const filePath of files.slice(0, 40)) {
48105
- const relativePath = path126.relative(root, filePath).split(path126.sep).join("/");
48593
+ const relativePath = path127.relative(root, filePath).split(path127.sep).join("/");
48106
48594
  if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
48107
48595
  continue;
48108
48596
  }
@@ -48123,7 +48611,7 @@ async function listMarkdownFiles(root) {
48123
48611
  const entries = await readdir20(root, { withFileTypes: true });
48124
48612
  const files = [];
48125
48613
  for (const entry of entries) {
48126
- const fullPath = path126.join(root, entry.name);
48614
+ const fullPath = path127.join(root, entry.name);
48127
48615
  if (entry.isDirectory()) {
48128
48616
  files.push(...await listMarkdownFiles(fullPath));
48129
48617
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -48171,7 +48659,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
48171
48659
  const manifest = {
48172
48660
  schemaVersion: 1,
48173
48661
  standardVersion: STANDARD_VERSION,
48174
- name: `${path126.basename(path126.dirname(metaprojectRoot))}-metaproject`,
48662
+ name: `${path127.basename(path127.dirname(metaprojectRoot))}-metaproject`,
48175
48663
  createdBy: "keryx",
48176
48664
  profiles: computeProfiles(enabledModuleKeys2),
48177
48665
  paths: {
@@ -48254,11 +48742,11 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
48254
48742
  metaproject: ".metaproject/index.md"
48255
48743
  }
48256
48744
  };
48257
- await writeFile42(path126.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
48745
+ await writeFile42(path127.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
48258
48746
  `, "utf8");
48259
48747
  }
48260
48748
  async function enableTasksInManifest(metaprojectRoot) {
48261
- const manifestPath = path126.join(metaprojectRoot, "metaproject.json");
48749
+ const manifestPath = path127.join(metaprojectRoot, "metaproject.json");
48262
48750
  if (!await pathExists(manifestPath)) {
48263
48751
  return;
48264
48752
  }
@@ -48281,7 +48769,7 @@ async function enableTasksInManifest(metaprojectRoot) {
48281
48769
  `, "utf8");
48282
48770
  }
48283
48771
  async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
48284
- const manifestPath = path126.join(metaprojectRoot, "metaproject.json");
48772
+ const manifestPath = path127.join(metaprojectRoot, "metaproject.json");
48285
48773
  if (!await pathExists(manifestPath)) {
48286
48774
  return;
48287
48775
  }
@@ -48317,69 +48805,69 @@ async function updateRuntime(projectRoot) {
48317
48805
  }
48318
48806
  }
48319
48807
  async function findRuntimeRoot(projectRoot) {
48320
- const projectRuntime = path126.join(projectRoot, ".metaproject", "runtime", "keryx");
48321
- if (await pathExists(path126.join(projectRuntime, ".git"))) {
48808
+ const projectRuntime = path127.join(projectRoot, ".metaproject", "runtime", "keryx");
48809
+ if (await pathExists(path127.join(projectRuntime, ".git"))) {
48322
48810
  return projectRuntime;
48323
48811
  }
48324
48812
  const home = process.env.HOME;
48325
48813
  if (!home) {
48326
48814
  return null;
48327
48815
  }
48328
- const globalRuntime = path126.join(home, ".keryx", "keryx");
48329
- if (await pathExists(path126.join(globalRuntime, ".git"))) {
48816
+ const globalRuntime = path127.join(home, ".keryx", "keryx");
48817
+ if (await pathExists(path127.join(globalRuntime, ".git"))) {
48330
48818
  return globalRuntime;
48331
48819
  }
48332
48820
  return null;
48333
48821
  }
48334
48822
  async function createServiceDirs(metaprojectRoot, modules) {
48335
48823
  const dirs = [
48336
- path126.join(metaprojectRoot, "core"),
48337
- path126.join(metaprojectRoot, "hooks", "post-update.d"),
48338
- path126.join(metaprojectRoot, "modules"),
48339
- path126.join(metaprojectRoot, "rules"),
48340
- path126.join(metaprojectRoot, "skills", "project-rules"),
48824
+ path127.join(metaprojectRoot, "core"),
48825
+ path127.join(metaprojectRoot, "hooks", "post-update.d"),
48826
+ path127.join(metaprojectRoot, "modules"),
48827
+ path127.join(metaprojectRoot, "rules"),
48828
+ path127.join(metaprojectRoot, "skills", "project-rules"),
48341
48829
  ...modules.enableGdgraph ? [
48342
- path126.join(metaprojectRoot, "core", "gdgraph"),
48343
- path126.join(metaprojectRoot, "skills", "gdgraph")
48830
+ path127.join(metaprojectRoot, "core", "gdgraph"),
48831
+ path127.join(metaprojectRoot, "skills", "gdgraph")
48344
48832
  ] : [],
48345
48833
  ...modules.enableGdctx ? [
48346
- path126.join(metaprojectRoot, "core", "gdctx"),
48347
- path126.join(metaprojectRoot, "skills", "gdctx")
48834
+ path127.join(metaprojectRoot, "core", "gdctx"),
48835
+ path127.join(metaprojectRoot, "skills", "gdctx")
48348
48836
  ] : [],
48349
48837
  ...modules.enableGdwiki ? [
48350
- path126.join(metaprojectRoot, "skills", "gdwiki"),
48351
- path126.join(metaprojectRoot, "wiki", "templates")
48838
+ path127.join(metaprojectRoot, "skills", "gdwiki"),
48839
+ path127.join(metaprojectRoot, "wiki", "templates")
48352
48840
  ] : [],
48353
48841
  ...modules.enableHealth ? [
48354
- path126.join(metaprojectRoot, "core", "health"),
48355
- path126.join(metaprojectRoot, "skills", "health")
48842
+ path127.join(metaprojectRoot, "core", "health"),
48843
+ path127.join(metaprojectRoot, "skills", "health")
48356
48844
  ] : [],
48357
48845
  ...modules.enableTesting ? [
48358
- path126.join(metaprojectRoot, "core", "testing"),
48359
- path126.join(metaprojectRoot, "skills", "testing")
48846
+ path127.join(metaprojectRoot, "core", "testing"),
48847
+ path127.join(metaprojectRoot, "skills", "testing")
48360
48848
  ] : [],
48361
48849
  ...modules.enableMemory ? [
48362
- path126.join(metaprojectRoot, "core", "memory"),
48363
- path126.join(metaprojectRoot, "skills", "memory"),
48364
- path126.join(metaprojectRoot, "memory", "templates")
48850
+ path127.join(metaprojectRoot, "core", "memory"),
48851
+ path127.join(metaprojectRoot, "skills", "memory"),
48852
+ path127.join(metaprojectRoot, "memory", "templates")
48365
48853
  ] : [],
48366
48854
  ...modules.enableTasks ? [
48367
- path126.join(metaprojectRoot, "flows"),
48368
- path126.join(metaprojectRoot, "skills", "flow")
48855
+ path127.join(metaprojectRoot, "flows"),
48856
+ path127.join(metaprojectRoot, "skills", "flow")
48369
48857
  ] : [],
48370
48858
  ...modules.enableSecurity ? [
48371
- path126.join(metaprojectRoot, "core", "security")
48859
+ path127.join(metaprojectRoot, "core", "security")
48372
48860
  ] : []
48373
48861
  ];
48374
48862
  await Promise.all(dirs.map((dir) => mkdir45(dir, { recursive: true })));
48375
48863
  }
48376
48864
  async function installGdgraphCoreScripts2(metaprojectRoot) {
48377
- const gdgraphCoreRoot = path126.join(metaprojectRoot, "core", "gdgraph");
48865
+ const gdgraphCoreRoot = path127.join(metaprojectRoot, "core", "gdgraph");
48378
48866
  await mkdir45(gdgraphCoreRoot, { recursive: true });
48379
48867
  for (const file of GDGRAPH_CORE_SOURCES) {
48380
- await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path126.join(gdgraphCoreRoot, file));
48868
+ await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path127.join(gdgraphCoreRoot, file));
48381
48869
  }
48382
- await writeTextIfChanged4(path126.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
48870
+ await writeTextIfChanged4(path127.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
48383
48871
  }
48384
48872
  async function installManagedHook2(projectRoot, hookName, blockId, content) {
48385
48873
  const hooksRoot = await resolveGitHooksRoot(projectRoot);
@@ -48387,7 +48875,7 @@ async function installManagedHook2(projectRoot, hookName, blockId, content) {
48387
48875
  return;
48388
48876
  }
48389
48877
  await mkdir45(hooksRoot, { recursive: true });
48390
- const hookPath = path126.join(hooksRoot, hookName);
48878
+ const hookPath = path127.join(hooksRoot, hookName);
48391
48879
  const blockStart = `# keryx:${blockId}:begin`;
48392
48880
  const blockEnd = `# keryx:${blockId}:end`;
48393
48881
  const managedBlock = `${blockStart}
@@ -48408,7 +48896,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
48408
48896
  if (!hooksRoot) {
48409
48897
  return;
48410
48898
  }
48411
- const hookPath = path126.join(hooksRoot, hookName);
48899
+ const hookPath = path127.join(hooksRoot, hookName);
48412
48900
  if (!await pathExists(hookPath)) {
48413
48901
  return;
48414
48902
  }
@@ -48430,7 +48918,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
48430
48918
  if (!hooksRoot) {
48431
48919
  return false;
48432
48920
  }
48433
- const hookPath = path126.join(hooksRoot, "pre-push");
48921
+ const hookPath = path127.join(hooksRoot, "pre-push");
48434
48922
  if (!await pathExists(hookPath)) {
48435
48923
  return false;
48436
48924
  }
@@ -48445,7 +48933,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
48445
48933
  return (await readFile64(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
48446
48934
  }
48447
48935
  async function readManifest5(metaprojectRoot) {
48448
- const manifestPath = path126.join(metaprojectRoot, "metaproject.json");
48936
+ const manifestPath = path127.join(metaprojectRoot, "metaproject.json");
48449
48937
  if (!await pathExists(manifestPath)) {
48450
48938
  return {
48451
48939
  exists: false,
@@ -48513,7 +49001,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
48513
49001
  }
48514
49002
  async function anyPathExists(root, candidates) {
48515
49003
  for (const candidate of candidates) {
48516
- if (await pathExists(path126.join(root, candidate))) {
49004
+ if (await pathExists(path127.join(root, candidate))) {
48517
49005
  return true;
48518
49006
  }
48519
49007
  }
@@ -48534,13 +49022,13 @@ function parseUpdateArgs(args2) {
48534
49022
  };
48535
49023
  }
48536
49024
  async function runPostUpdateHooks(projectRoot) {
48537
- const hooksDir = path126.join(projectRoot, ".metaproject", "hooks", "post-update.d");
49025
+ const hooksDir = path127.join(projectRoot, ".metaproject", "hooks", "post-update.d");
48538
49026
  if (!await pathExists(hooksDir)) {
48539
49027
  return;
48540
49028
  }
48541
49029
  const entries = (await readdir20(hooksDir)).sort();
48542
49030
  for (const entry of entries) {
48543
- const hookPath = path126.join(hooksDir, entry);
49031
+ const hookPath = path127.join(hooksDir, entry);
48544
49032
  try {
48545
49033
  await accessExecutable(hookPath);
48546
49034
  } catch {
@@ -48581,14 +49069,14 @@ async function writeTextIfChanged4(filePath, content) {
48581
49069
  if (await pathExists(filePath) && await readFile64(filePath, "utf8") === content) {
48582
49070
  return;
48583
49071
  }
48584
- await mkdir45(path126.dirname(filePath), { recursive: true });
49072
+ await mkdir45(path127.dirname(filePath), { recursive: true });
48585
49073
  await writeFile42(filePath, content, "utf8");
48586
49074
  }
48587
49075
  async function writeTextIfMissing4(filePath, content) {
48588
49076
  if (await pathExists(filePath)) {
48589
49077
  return;
48590
49078
  }
48591
- await mkdir45(path126.dirname(filePath), { recursive: true });
49079
+ await mkdir45(path127.dirname(filePath), { recursive: true });
48592
49080
  await writeFile42(filePath, content, "utf8");
48593
49081
  }
48594
49082
  async function copyFileIfChanged2(from, to) {
@@ -48596,7 +49084,7 @@ async function copyFileIfChanged2(from, to) {
48596
49084
  if (await pathExists(to) && await readFile64(to, "utf8") === next) {
48597
49085
  return;
48598
49086
  }
48599
- await mkdir45(path126.dirname(to), { recursive: true });
49087
+ await mkdir45(path127.dirname(to), { recursive: true });
48600
49088
  await writeFile42(to, next, "utf8");
48601
49089
  }
48602
49090
  function runtimeSourcePath2(relativePath) {
@@ -48605,7 +49093,7 @@ function runtimeSourcePath2(relativePath) {
48605
49093
  return directPath;
48606
49094
  }
48607
49095
  if (relativePath.startsWith("../")) {
48608
- const packagedSourcePath = path126.join(path126.dirname(fileURLToPath6(import.meta.url)), "..", "src", relativePath.slice(3));
49096
+ const packagedSourcePath = path127.join(path127.dirname(fileURLToPath6(import.meta.url)), "..", "src", relativePath.slice(3));
48609
49097
  if (existsSync27(packagedSourcePath)) {
48610
49098
  return packagedSourcePath;
48611
49099
  }
@@ -48637,7 +49125,7 @@ function printHelp18() {
48637
49125
 
48638
49126
  // src/commands/dashboard.ts
48639
49127
  import { spawn as spawn6 } from "child_process";
48640
- import path127 from "path";
49128
+ import path128 from "path";
48641
49129
  init_args();
48642
49130
  async function dashboardCommand(args2 = []) {
48643
49131
  const options = parseOptions(args2);
@@ -48648,7 +49136,7 @@ async function dashboardCommand(args2 = []) {
48648
49136
  }
48649
49137
  if (subcommand === "build") {
48650
49138
  const result = await buildDashboard();
48651
- const rel = path127.relative(process.cwd(), result.path);
49139
+ const rel = path128.relative(process.cwd(), result.path);
48652
49140
  console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
48653
49141
  note(`Open it: keryx dashboard open`);
48654
49142
  return;
@@ -48656,7 +49144,7 @@ async function dashboardCommand(args2 = []) {
48656
49144
  if (subcommand === "open") {
48657
49145
  const result = await buildDashboard();
48658
49146
  await openFile(result.path);
48659
- const rel = path127.relative(process.cwd(), result.path);
49147
+ const rel = path128.relative(process.cwd(), result.path);
48660
49148
  console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
48661
49149
  return;
48662
49150
  }
@@ -48705,7 +49193,7 @@ import { readFileSync as readFileSync10 } from "fs";
48705
49193
  // src/agents/bootstrap.ts
48706
49194
  import { mkdir as mkdir46, readFile as readFile65, writeFile as writeFile43 } from "fs/promises";
48707
49195
  import { homedir as homedir6 } from "os";
48708
- import path128 from "path";
49196
+ import path129 from "path";
48709
49197
  init_fs();
48710
49198
  var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
48711
49199
  var AGENT_BOOTSTRAP_END = "<!-- /keryx:global-bootstrap -->";
@@ -48715,35 +49203,35 @@ var AGENT_BOOTSTRAP_RUNTIMES = [
48715
49203
  aliases: ["claude-code"],
48716
49204
  label: "Claude Code",
48717
49205
  fileName: "CLAUDE.md",
48718
- filePath: (homeRoot) => path128.join(homeRoot, ".claude", "CLAUDE.md")
49206
+ filePath: (homeRoot) => path129.join(homeRoot, ".claude", "CLAUDE.md")
48719
49207
  },
48720
49208
  {
48721
49209
  id: "opencode",
48722
49210
  aliases: ["open-code"],
48723
49211
  label: "OpenCode",
48724
49212
  fileName: "AGENTS.md",
48725
- filePath: (homeRoot) => path128.join(homeRoot, ".config", "opencode", "AGENTS.md")
49213
+ filePath: (homeRoot) => path129.join(homeRoot, ".config", "opencode", "AGENTS.md")
48726
49214
  },
48727
49215
  {
48728
49216
  id: "zcode",
48729
49217
  aliases: ["zed", "zed-code"],
48730
49218
  label: "ZCode",
48731
49219
  fileName: "AGENTS.md",
48732
- filePath: (homeRoot) => path128.join(homeRoot, ".zcode", "AGENTS.md")
49220
+ filePath: (homeRoot) => path129.join(homeRoot, ".zcode", "AGENTS.md")
48733
49221
  },
48734
49222
  {
48735
49223
  id: "codex",
48736
49224
  aliases: [],
48737
49225
  label: "Codex",
48738
49226
  fileName: "AGENTS.md",
48739
- filePath: (homeRoot) => path128.join(homeRoot, ".codex", "AGENTS.md")
49227
+ filePath: (homeRoot) => path129.join(homeRoot, ".codex", "AGENTS.md")
48740
49228
  },
48741
49229
  {
48742
49230
  id: "antigravity",
48743
49231
  aliases: ["antigravuty", "antigravity-code"],
48744
49232
  label: "Antigravity",
48745
49233
  fileName: "AGENTS.md",
48746
- filePath: (homeRoot) => path128.join(homeRoot, ".config", "antigravity", "AGENTS.md")
49234
+ filePath: (homeRoot) => path129.join(homeRoot, ".config", "antigravity", "AGENTS.md")
48747
49235
  }
48748
49236
  ];
48749
49237
  function agentBootstrapRuntimeIds() {
@@ -48791,7 +49279,7 @@ async function installAgentBootstrap(runtime, options = {}) {
48791
49279
  const dryRun = options.dryRun === true;
48792
49280
  const wrote = next !== current;
48793
49281
  if (wrote && !dryRun) {
48794
- await mkdir46(path128.dirname(filePath), { recursive: true });
49282
+ await mkdir46(path129.dirname(filePath), { recursive: true });
48795
49283
  await writeFile43(filePath, next, "utf8");
48796
49284
  }
48797
49285
  const status = dryRun ? statusFromContent(runtime, filePath, exists2, next) : await agentBootstrapStatus(runtime, homeRoot);
@@ -49139,7 +49627,7 @@ function printBootstrapHelp() {
49139
49627
  // src/commands/metrics.ts
49140
49628
  init_args();
49141
49629
  import { readFile as readFile66 } from "fs/promises";
49142
- import path129 from "path";
49630
+ import path130 from "path";
49143
49631
 
49144
49632
  // src/metrics/benchmark.ts
49145
49633
  function createPairedBenchmarkTemplate(taskIds) {
@@ -49367,7 +49855,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
49367
49855
  console.log("# metrics status");
49368
49856
  console.log("");
49369
49857
  console.log(`root: ${root}`);
49370
- console.log(`enabled: ${await Bun.file(path129.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
49858
+ console.log(`enabled: ${await Bun.file(path130.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
49371
49859
  const latest2 = await readLatestPointer(root);
49372
49860
  console.log(`latest: ${latest2.status}`);
49373
49861
  return;
@@ -49379,7 +49867,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
49379
49867
  process.exitCode = 1;
49380
49868
  return;
49381
49869
  }
49382
- const record2 = JSON.parse(await readFile66(path129.resolve(projectRoot, file), "utf8"));
49870
+ const record2 = JSON.parse(await readFile66(path130.resolve(projectRoot, file), "utf8"));
49383
49871
  const result = validateRunRecord(record2);
49384
49872
  console.log(result.valid ? "valid: yes" : "valid: no");
49385
49873
  for (const error of result.errors)
@@ -49404,7 +49892,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
49404
49892
  process.exitCode = 1;
49405
49893
  return;
49406
49894
  }
49407
- const file = path129.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
49895
+ const file = path130.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
49408
49896
  if (!await Bun.file(file).exists()) {
49409
49897
  console.error(`Run not found: ${runId}`);
49410
49898
  process.exitCode = 1;
@@ -49421,8 +49909,8 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
49421
49909
  process.exitCode = 1;
49422
49910
  return;
49423
49911
  }
49424
- const a = JSON.parse(await readFile66(path129.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
49425
- const b = JSON.parse(await readFile66(path129.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
49912
+ const a = JSON.parse(await readFile66(path130.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
49913
+ const b = JSON.parse(await readFile66(path130.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
49426
49914
  const comparison = compareExecutionRuns(a, b);
49427
49915
  console.log(stableJson(comparison));
49428
49916
  return;
@@ -49456,8 +49944,8 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
49456
49944
  return;
49457
49945
  }
49458
49946
  const template = createPairedBenchmarkTemplate(taskIds);
49459
- await Bun.write(path129.resolve(projectRoot, out), stableJson(template));
49460
- console.log(`manifest: ${path129.relative(projectRoot, path129.resolve(projectRoot, out))}`);
49947
+ await Bun.write(path130.resolve(projectRoot, out), stableJson(template));
49948
+ console.log(`manifest: ${path130.relative(projectRoot, path130.resolve(projectRoot, out))}`);
49461
49949
  return;
49462
49950
  }
49463
49951
  if (subcommand === "benchmark" && args2[1] === "validate") {
@@ -49467,7 +49955,7 @@ async function metricsCommand(args2 = [], projectRoot = process.cwd()) {
49467
49955
  process.exitCode = 1;
49468
49956
  return;
49469
49957
  }
49470
- const raw = JSON.parse(await readFile66(path129.resolve(projectRoot, file), "utf8"));
49958
+ const raw = JSON.parse(await readFile66(path130.resolve(projectRoot, file), "utf8"));
49471
49959
  const input2 = Array.isArray(raw) ? raw : raw.runs ?? [];
49472
49960
  const result = validatePairedBenchmark(input2);
49473
49961
  console.log(stableJson(result));
@@ -49485,7 +49973,7 @@ async function collect(projectRoot, args2) {
49485
49973
  process.exitCode = 1;
49486
49974
  return;
49487
49975
  }
49488
- const raw = JSON.parse(await readFile66(path129.resolve(projectRoot, eventFile), "utf8"));
49976
+ const raw = JSON.parse(await readFile66(path130.resolve(projectRoot, eventFile), "utf8"));
49489
49977
  const events2 = Array.isArray(raw) ? raw : raw.events;
49490
49978
  const startedAt = optionValue(args2, "--started-at") ?? events2[0]?.timestamp_utc ?? new Date().toISOString();
49491
49979
  const finishedAt = optionValue(args2, "--finished-at") ?? events2.at(-1)?.timestamp_utc ?? startedAt;
@@ -49501,11 +49989,11 @@ async function collect(projectRoot, args2) {
49501
49989
  parentRunId: optionValue(args2, "--parent-run-id") ?? null
49502
49990
  });
49503
49991
  const result = await writeRunArtifacts(metricsRoot(projectRoot), record2, { cwd: projectRoot });
49504
- console.log(`json: ${path129.relative(projectRoot, result.jsonPath)}`);
49505
- console.log(`markdown: ${path129.relative(projectRoot, result.markdownPath)}`);
49992
+ console.log(`json: ${path130.relative(projectRoot, result.jsonPath)}`);
49993
+ console.log(`markdown: ${path130.relative(projectRoot, result.markdownPath)}`);
49506
49994
  }
49507
49995
  function metricsRoot(projectRoot) {
49508
- return path129.join(projectRoot, ".metaproject", "data", "metrics");
49996
+ return path130.join(projectRoot, ".metaproject", "data", "metrics");
49509
49997
  }
49510
49998
  function printMetricsHelp() {
49511
49999
  console.log(`keryx metrics
@@ -49523,76 +50011,35 @@ Usage:
49523
50011
  keryx metrics benchmark validate <manifest.json>
49524
50012
  `);
49525
50013
  }
49526
- // package.json
49527
- var package_default = {
49528
- name: "@mrciphersmith/keryx",
49529
- version: "0.2.17",
49530
- description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
49531
- private: false,
49532
- publishConfig: {
49533
- access: "public"
49534
- },
49535
- license: "MIT",
49536
- type: "module",
49537
- repository: {
49538
- type: "git",
49539
- url: "git+ssh://git@github.com/MrCipherSmith/keryx.git"
49540
- },
49541
- keywords: [
49542
- "ai-agents",
49543
- "coding-agents",
49544
- "agent-harness",
49545
- "agent-context",
49546
- "repository-context",
49547
- "code-graph",
49548
- "project-memory",
49549
- "test-impact-analysis",
49550
- "developer-tools",
49551
- "model-context-protocol",
49552
- "mcp",
49553
- "claude-code",
49554
- "cursor",
49555
- "codex",
49556
- "cli",
49557
- "bun"
49558
- ],
49559
- bin: {
49560
- keryx: "./dist/cli.js"
49561
- },
49562
- scripts: {
49563
- keryx: "bun ./src/cli.ts",
49564
- build: "bun build ./src/cli.ts --outdir ./dist --target bun --external @modelcontextprotocol/sdk --external web-tree-sitter --external @opentui/core && bun build ./src/harness/process/sandbox/proxy-worker.ts --outdir ./dist --target bun --external @modelcontextprotocol/sdk --external web-tree-sitter --external @opentui/core",
49565
- prepare: "bun run build",
49566
- typecheck: "tsc --noEmit",
49567
- test: "bun test",
49568
- check: "tsc --noEmit && bun test",
49569
- "check:doc-links": "bun scripts/check-doc-links.ts",
49570
- "test:guards": "bun test src/lib/config-dir.ast.test.ts src/lib/config-dir.readers.test.ts src/lib/production-graph.test.ts src/harness/policy/profiles.test.ts src/lib/serve-server.test.ts"
49571
- },
49572
- files: [
49573
- "dist",
49574
- "src/gdgraph",
49575
- "src/gdskills/bundled",
49576
- "src/gdskills/contracts",
49577
- "LICENSE",
49578
- "README.md",
49579
- "package.json"
49580
- ],
49581
- dependencies: {},
49582
- optionalDependencies: {
49583
- "@modelcontextprotocol/sdk": "^1.0.0",
49584
- "@opentui/core": "^0.4.5",
49585
- "web-tree-sitter": "^0.22.0"
49586
- },
49587
- devDependencies: {
49588
- "@types/bun": "latest",
49589
- "bun-types": "latest",
49590
- typescript: "^5"
49591
- },
49592
- engines: {
49593
- bun: ">=1.1.0"
50014
+
50015
+ // src/commands/version.ts
50016
+ async function versionCommand(args2, deps = {}) {
50017
+ const json = args2.length === 2 && args2[1] === "--json";
50018
+ if (args2[0] !== "check" || args2.length !== 1 && !json) {
50019
+ console.error("Usage: keryx version check [--json]");
50020
+ process.exitCode = 1;
50021
+ return;
49594
50022
  }
49595
- };
50023
+ const options = {
50024
+ currentVersion: deps.currentVersion ?? package_default.version,
50025
+ ...deps.fetch !== undefined ? { fetch: deps.fetch } : {},
50026
+ ...deps.cacheDir !== undefined ? { cacheDir: deps.cacheDir } : {},
50027
+ ...deps.now !== undefined ? { now: deps.now } : {}
50028
+ };
50029
+ const result = await (deps.check ?? checkVersion)(options);
50030
+ if (json) {
50031
+ console.log(JSON.stringify(result));
50032
+ return;
50033
+ }
50034
+ if (result.status === "update-available") {
50035
+ console.log(`Keryx ${result.currentVersion} \u2192 ${result.latestVersion}`);
50036
+ console.log(result.installCommand);
50037
+ } else if (result.status === "up-to-date") {
50038
+ console.log(`Keryx ${result.currentVersion} is up to date.`);
50039
+ } else {
50040
+ console.log(`Keryx version check unavailable (${result.reason}).`);
50041
+ }
50042
+ }
49596
50043
 
49597
50044
  // src/cli.ts
49598
50045
  var VERSION2 = package_default.version;
@@ -49627,7 +50074,8 @@ var CLI_ROUTES = {
49627
50074
  harness: harnessCommand,
49628
50075
  shell: shellCommand,
49629
50076
  sessions: sessionsCommand,
49630
- session: sessionsCommand
50077
+ session: sessionsCommand,
50078
+ version: versionCommand
49631
50079
  };
49632
50080
  async function main() {
49633
50081
  const args2 = process.argv.slice(2);
@@ -49658,6 +50106,7 @@ Usage:
49658
50106
  Start TUI agent shell (sessions are per-project)
49659
50107
  keryx sessions list|fork <id>|export <id>|path
49660
50108
  List / branch / export sessions for the current project
50109
+ keryx version check [--json] Check npm latest (advisory; never installs)
49661
50110
  keryx harness run --provider <fake|anthropic|ollama> --model <m> [--base-url <url>] [--record <path>] "<prompt>"
49662
50111
  keryx harness exec [--allow-env KEY]... [--max-runtime-ms N] [--allow-real-subprocess]
49663
50112
  [--allowed-domains a,b] [--mask-env NAME@host] [--tls-terminate] [--mask-mode auto|manual|off] [--auto-mask] -- <path> [args...]
@@ -49741,6 +50190,7 @@ Commands:
49741
50190
  shell Start the interactive TUI agent harness. Use --no-tui or --chat to opt out.
49742
50191
  Sessions: -c continue last in this project, -r [id] resume (per-project).
49743
50192
  sessions List or export per-project shell sessions
50193
+ version Check whether a newer npm release is available
49744
50194
  harness Run a single provider turn (harness run) and print structured events
49745
50195
  init Initialize .metaproject in the current project
49746
50196
  status Show local Metaproject status