@mrciphersmith/keryx 0.2.110 → 0.2.111

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -589,6 +589,44 @@ var init_grants = __esm(() => {
589
589
  ];
590
590
  });
591
591
 
592
+ // src/lib/caller-session.ts
593
+ function resolveCallerSession(input = {}) {
594
+ const pair = (provider, model, source) => ({
595
+ providerId: (provider ?? "").trim(),
596
+ modelId: (model ?? "").trim(),
597
+ source
598
+ });
599
+ if (input.flagProvider !== undefined || input.flagModel !== undefined) {
600
+ return pair(input.flagProvider, input.flagModel, "flags");
601
+ }
602
+ const env = input.env ?? process.env;
603
+ const envProvider = env[ENV_SESSION_PROVIDER];
604
+ const envModel = env[ENV_SESSION_MODEL];
605
+ if ((envProvider ?? "").trim() !== "" || (envModel ?? "").trim() !== "") {
606
+ return pair(envProvider, envModel, "env");
607
+ }
608
+ if (input.fromShellConfig === true) {
609
+ const config = loadShellConfig(input.configDir);
610
+ return pair(config.provider, config.model, "shell-config");
611
+ }
612
+ return pair(undefined, undefined, "none");
613
+ }
614
+ function exportCallerSession(providerId, modelId, env = process.env) {
615
+ const provider = providerId.trim();
616
+ const model = modelId.trim();
617
+ if (provider === "" || model === "") {
618
+ delete env[ENV_SESSION_PROVIDER];
619
+ delete env[ENV_SESSION_MODEL];
620
+ return;
621
+ }
622
+ env[ENV_SESSION_PROVIDER] = provider;
623
+ env[ENV_SESSION_MODEL] = model;
624
+ }
625
+ var ENV_SESSION_PROVIDER = "KERYX_SESSION_PROVIDER", ENV_SESSION_MODEL = "KERYX_SESSION_MODEL";
626
+ var init_caller_session = __esm(() => {
627
+ init_shell_config();
628
+ });
629
+
592
630
  // src/lib/args.ts
593
631
  import { parseArgs } from "util";
594
632
  function optionValue(args, name) {
@@ -907,21 +945,23 @@ function runCrossFamily(args) {
907
945
  const decision = crossFamilyReviewForSession(args.includes("--opt-in"), {
908
946
  providerId: optionValue(args, "--session-provider"),
909
947
  modelId: optionValue(args, "--session-model")
910
- });
948
+ }, { fromShellConfig: args.includes("--from-shell-config") });
911
949
  if (args.includes("--json")) {
912
950
  console.log(JSON.stringify({ cross_family_review: decision }, null, 2));
913
951
  return;
914
952
  }
915
953
  console.log(renderCrossFamilyDecision(decision));
916
954
  }
917
- function crossFamilyReviewForSession(optIn, session = {}) {
918
- const config = loadShellConfig();
955
+ function crossFamilyReviewForSession(optIn, session = {}, options = {}) {
956
+ const resolved = resolveCallerSession({
957
+ flagProvider: session.providerId,
958
+ flagModel: session.modelId,
959
+ fromShellConfig: options.fromShellConfig,
960
+ env: options.env
961
+ });
919
962
  return decideCrossFamilyReview({
920
963
  optIn,
921
- session: {
922
- providerId: (session.providerId ?? config.provider ?? "").trim(),
923
- modelId: (session.modelId ?? config.model ?? "").trim()
924
- },
964
+ session: { providerId: resolved.providerId, modelId: resolved.modelId },
925
965
  configured: configuredProviders(envWithOAuthAccess(envWithSavedApiKeys()))
926
966
  });
927
967
  }
@@ -963,7 +1003,7 @@ function printProvidersHelp() {
963
1003
 
964
1004
  Usage:
965
1005
  keryx providers list [--json]
966
- keryx providers cross-family [--opt-in] [--session-provider <id>] [--session-model <id>] [--json]
1006
+ keryx providers cross-family [--opt-in] [--session-provider <id>] [--session-model <id>] [--from-shell-config] [--json]
967
1007
 
968
1008
  Commands:
969
1009
  list Providers this operator has configured, and the family of each
@@ -982,6 +1022,7 @@ var init_providers = __esm(() => {
982
1022
  init_provider_config();
983
1023
  init_catalog();
984
1024
  init_grants();
1025
+ init_caller_session();
985
1026
  init_shell_config();
986
1027
  init_args();
987
1028
  OPENAI_COMPAT_PROVIDERS = [
@@ -8724,6 +8765,7 @@ function parseSkillFrontmatter(content) {
8724
8765
  let inTriggers = false;
8725
8766
  let inMetadata = false;
8726
8767
  let metadataCategory;
8768
+ let metadataVersion;
8727
8769
  let compatibleHarnesses;
8728
8770
  let awaitingHarnessesList = false;
8729
8771
  let descriptionBlock = null;
@@ -8785,6 +8827,11 @@ function parseSkillFrontmatter(content) {
8785
8827
  metadataCategory = stripSkillFieldQuotes(categoryMatch[1].trim());
8786
8828
  continue;
8787
8829
  }
8830
+ const versionMatch = /^\s+version:\s*(.+)$/.exec(line);
8831
+ if (versionMatch !== null && versionMatch[1] !== undefined) {
8832
+ metadataVersion = stripSkillFieldQuotes(versionMatch[1].trim());
8833
+ continue;
8834
+ }
8788
8835
  const harnessesMatch = /^\s+compatible_harnesses:\s*(.*)$/.exec(line);
8789
8836
  if (harnessesMatch !== null) {
8790
8837
  const inline = parseInlineHarnesses((harnessesMatch[1] ?? "").trim());
@@ -8805,6 +8852,7 @@ function parseSkillFrontmatter(content) {
8805
8852
  ...description !== undefined ? { description } : {},
8806
8853
  ...triggers.length > 0 ? { triggers } : {},
8807
8854
  ...metadataCategory !== undefined ? { metadataCategory } : {},
8855
+ ...metadataVersion !== undefined ? { metadataVersion } : {},
8808
8856
  ...compatibleHarnesses !== undefined ? { compatibleHarnesses } : {}
8809
8857
  };
8810
8858
  }
@@ -25690,7 +25738,7 @@ async function createProjectSkill(projectRoot, options) {
25690
25738
  name: skillName,
25691
25739
  target: options.target,
25692
25740
  path: relativeSkillPath,
25693
- version: VERSION,
25741
+ version: options.version ?? VERSION,
25694
25742
  status: "active",
25695
25743
  updatedAt: new Date().toISOString()
25696
25744
  });
@@ -33188,7 +33236,7 @@ function guardGitHubRequest(request) {
33188
33236
  if (allowed.some((pattern) => pattern.test(request.path))) {
33189
33237
  return;
33190
33238
  }
33191
- throw new Error(`Refusing a GitHub ${request.method} to \`${request.path}\`: it is not one of the five endpoints this module may touch. ` + `Reads: pulls/{n}/comments, pulls/{n}/reviews, issues/{n}/comments. Writes: pulls/{n}/comments/{id}/replies, issues/{n}/comments. ` + `Resolving, hiding, minimising, editing and dismissing are deliberately unreachable \u2014 replying is ours, resolving is the reviewer's call, and a bot that auto-resolves is a bot that silences a human.`);
33239
+ throw new Error(`Refusing a GitHub ${request.method} to \`${request.path}\`: it is not one of the six endpoints this module may touch. ` + `Reads: pulls/{n}, pulls/{n}/comments, pulls/{n}/reviews, issues/{n}/comments. Writes: pulls/{n}/comments/{id}/replies, issues/{n}/comments. ` + `Resolving, hiding, minimising, editing and dismissing are deliberately unreachable \u2014 replying is ours, resolving is the reviewer's call, and a bot that auto-resolves is a bot that silences a human.`);
33192
33240
  }
33193
33241
  async function callGitHub(port, request) {
33194
33242
  guardGitHubRequest(request);
@@ -33272,6 +33320,8 @@ function createFixturePort(files, options = {}) {
33272
33320
  }
33273
33321
  function fixtureKey(apiPath) {
33274
33322
  const withoutQuery = apiPath.split("?")[0] ?? apiPath;
33323
+ if (/\/pulls\/\d+$/.test(withoutQuery))
33324
+ return "pull";
33275
33325
  if (/\/pulls\/\d+\/comments$/.test(withoutQuery))
33276
33326
  return "pull-comments";
33277
33327
  if (/\/pulls\/\d+\/reviews$/.test(withoutQuery))
@@ -33280,12 +33330,37 @@ function fixtureKey(apiPath) {
33280
33330
  return "issue-comments";
33281
33331
  return withoutQuery;
33282
33332
  }
33333
+ function parsePullRequest(raw) {
33334
+ const object = Array.isArray(raw) ? raw[0] : raw;
33335
+ const state = stringProperty(object, "state");
33336
+ const mergedAt = stringProperty(object, "merged_at") ?? null;
33337
+ const mergedFlag = object !== null && typeof object === "object" && object.merged === true;
33338
+ const head = object !== null && typeof object === "object" ? object.head : undefined;
33339
+ const headSha = stringProperty(head, "sha") ?? null;
33340
+ return {
33341
+ state: state === "open" || state === "closed" ? state : "unknown",
33342
+ merged: mergedFlag || mergedAt !== null,
33343
+ mergedAt,
33344
+ headSha: headSha === null ? null : headSha.toLowerCase()
33345
+ };
33346
+ }
33347
+ function describePullRequest(pull) {
33348
+ const status = pull.merged ? `merged${pull.mergedAt === null ? "" : ` at ${pull.mergedAt}`}` : pull.state;
33349
+ return `${status}, head ${pull.headSha ?? "not reported"}`;
33350
+ }
33351
+ function shaMatchesHead(sha, headSha) {
33352
+ if (headSha === null)
33353
+ return false;
33354
+ const wanted = sha.trim().toLowerCase();
33355
+ return wanted === headSha || wanted.length >= 7 && headSha.startsWith(wanted);
33356
+ }
33283
33357
  async function collectPrComments(input) {
33284
33358
  if (input.self.trim() === "") {
33285
33359
  throw new Error("Refusing to collect PR comments with no `self` identity: without it our own replies are indistinguishable from a reviewer's, and the reply pass answers itself every round. Resolve the acting login (`gh api user --jq .login`) and pass it.");
33286
33360
  }
33287
33361
  const base = `repos/${input.repo}`;
33288
- const [rawReviewComments, rawReviews, rawIssueComments] = await Promise.all([
33362
+ const [rawPull, rawReviewComments, rawReviews, rawIssueComments] = await Promise.all([
33363
+ callGitHub(input.port, { method: "GET", path: `${base}/pulls/${input.number}` }),
33289
33364
  callGitHub(input.port, { method: "GET", path: `${base}/pulls/${input.number}/comments` }),
33290
33365
  callGitHub(input.port, { method: "GET", path: `${base}/pulls/${input.number}/reviews` }),
33291
33366
  callGitHub(input.port, { method: "GET", path: `${base}/issues/${input.number}/comments` })
@@ -33363,6 +33438,7 @@ async function collectPrComments(input) {
33363
33438
  });
33364
33439
  }
33365
33440
  return {
33441
+ pull: parsePullRequest(rawPull),
33366
33442
  comments,
33367
33443
  skipped,
33368
33444
  counts: {
@@ -33884,10 +33960,23 @@ function unansweredComments(state) {
33884
33960
  const answered = new Set(state.handled_comments.filter((entry) => entry.reply_url !== null).map((entry) => entry.id));
33885
33961
  return state.seen.filter((entry) => !answered.has(entry.id));
33886
33962
  }
33963
+ function assertReplyTarget(input) {
33964
+ const where = `${input.repo}#${input.number}`;
33965
+ if (input.pull.state === "unknown") {
33966
+ throw new Error(`Refusing to reply on ${where}: the pull request's state could not be read, so nothing shows it is open or that ${input.sha} is its head. Re-run with a reachable \`gh\`, or supply \`pull.json\` with \`--fixtures\`.`);
33967
+ }
33968
+ if ((input.pull.merged || input.pull.state === "closed") && input.allowClosed !== true) {
33969
+ throw new Error(`Refusing to reply on ${where}: the pull request is ${describePullRequest(input.pull)}. Replies are for an open review; posting to a finished one is an outward action nobody gated. Pass \`--allow-closed-pr\` only if answering after close is intended.`);
33970
+ }
33971
+ if (!shaMatchesHead(input.sha, input.pull.headSha)) {
33972
+ throw new Error(`Refusing to reply on ${where}: \`--sha ${input.sha}\` is not the pull request's head (${input.pull.headSha ?? "not reported"}). Every reply would describe a commit the pull request no longer points at. Collect and review the current head, then reply with its SHA.`);
33973
+ }
33974
+ }
33887
33975
  async function postReplyPass(input) {
33888
33976
  if (!input.round.isFinal) {
33889
33977
  throw new Error(`Refusing to post replies during round ${input.round.index}: comments are collected every round and answered ONCE, after the final round and before the completion gate. Replying per round turns one review thread into six, and every reply would state an intention rather than a settled outcome.`);
33890
33978
  }
33979
+ assertReplyTarget(input);
33891
33980
  const now = (input.now ?? new Date).toISOString();
33892
33981
  const alreadyAnswered = new Map(input.state.handled_comments.map((entry) => [entry.id, entry]));
33893
33982
  const posted = [];
@@ -34102,6 +34191,7 @@ var init_pr_comments = __esm(() => {
34102
34191
  init_fs();
34103
34192
  init_types3();
34104
34193
  ALLOWED_GITHUB_READS = [
34194
+ /^repos\/[^/]+\/[^/]+\/pulls\/\d+$/,
34105
34195
  /^repos\/[^/]+\/[^/]+\/pulls\/\d+\/comments(\?[^\s]*)?$/,
34106
34196
  /^repos\/[^/]+\/[^/]+\/pulls\/\d+\/reviews(\?[^\s]*)?$/,
34107
34197
  /^repos\/[^/]+\/[^/]+\/issues\/\d+\/comments(\?[^\s]*)?$/
@@ -40936,9 +41026,9 @@ var init_memory = __esm(() => {
40936
41026
 
40937
41027
  // src/lib/serve-config.ts
40938
41028
  import { existsSync as existsSync26 } from "fs";
40939
- import path176 from "path";
41029
+ import path177 from "path";
40940
41030
  function serveConfigPath(dir) {
40941
- return path176.join(keryxConfigDir(dir), "serve.json");
41031
+ return path177.join(keryxConfigDir(dir), "serve.json");
40942
41032
  }
40943
41033
  function parseIpv4(value) {
40944
41034
  const parts = value.split(".");
@@ -48687,7 +48777,7 @@ import path47 from "path";
48687
48777
  // package.json
48688
48778
  var package_default = {
48689
48779
  name: "@mrciphersmith/keryx",
48690
- version: "0.2.110",
48780
+ version: "0.2.111",
48691
48781
  description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
48692
48782
  private: false,
48693
48783
  publishConfig: {
@@ -56982,7 +57072,7 @@ init_args();
56982
57072
  init_fs();
56983
57073
  init_json();
56984
57074
  import { readdir as readdir25, readFile as readFile83 } from "fs/promises";
56985
- import path147 from "path";
57075
+ import path148 from "path";
56986
57076
 
56987
57077
  // src/lib/route-tokens.ts
56988
57078
  var ROUTE_STOPWORDS = new Set([
@@ -57669,8 +57759,32 @@ init_project_skills();
57669
57759
  // src/gdskills/import-skills.ts
57670
57760
  init_args();
57671
57761
  init_fs();
57672
- import { readdir as readdir24, readFile as readFile81, stat as stat15 } from "fs/promises";
57762
+ import { mkdir as mkdir50, readdir as readdir24, readFile as readFile81, stat as stat15 } from "fs/promises";
57763
+ import { homedir as homedir4 } from "os";
57764
+ import path145 from "path";
57765
+
57766
+ // src/gdskills/rule-references.ts
57767
+ init_fs();
57673
57768
  import path144 from "path";
57769
+ function ruleReferences(content) {
57770
+ const refs = new Set;
57771
+ for (const match of content.matchAll(/`(?:\.metaproject\/)?(?:rules\/)?([a-z0-9_-]+\/[a-z0-9._-]+\.mdc)`/gi)) {
57772
+ if (match[1])
57773
+ refs.add(match[1]);
57774
+ }
57775
+ return [...refs].sort();
57776
+ }
57777
+ async function unresolvedRuleReferences(projectRoot, content) {
57778
+ const missing = [];
57779
+ for (const ref of ruleReferences(content)) {
57780
+ if (!await pathExists(path144.join(projectRoot, ".metaproject", "rules", ref))) {
57781
+ missing.push(ref);
57782
+ }
57783
+ }
57784
+ return missing;
57785
+ }
57786
+
57787
+ // src/gdskills/import-skills.ts
57674
57788
  init_project_skills();
57675
57789
  init_guard2();
57676
57790
  var BUNDLED_NAMES = new Set(BUNDLED_GDSKILLS.map((entry) => entry.name));
@@ -57687,9 +57801,11 @@ async function importProjectSkills(options) {
57687
57801
  for (const source of sources) {
57688
57802
  imported.push(await importOne(options, source));
57689
57803
  }
57804
+ const rules = await importReferencedRules(options, sources.filter((source) => options.force === true || !BUNDLED_NAMES.has(source.name)));
57690
57805
  return {
57691
57806
  from: options.from,
57692
57807
  imported,
57808
+ rules,
57693
57809
  dryRun: options.dryRun === true,
57694
57810
  force: options.force === true
57695
57811
  };
@@ -57707,6 +57823,7 @@ async function updateProjectSkills(options) {
57707
57823
  return {
57708
57824
  from: options.from ?? "(each skill Origin)",
57709
57825
  imported,
57826
+ rules: [],
57710
57827
  dryRun: options.dryRun === true,
57711
57828
  force: true
57712
57829
  };
@@ -57727,6 +57844,14 @@ function renderImportProjectSkillsMarkdown(result) {
57727
57844
  lines.push(`- ${row.module}/${row.name}: ${row.status}${extra ? ` \u2014 ${extra}` : ""}`);
57728
57845
  }
57729
57846
  lines.push("");
57847
+ if (result.rules.length > 0) {
57848
+ lines.push("## rules the skills cite", "");
57849
+ for (const rule of result.rules) {
57850
+ const detail = [rule.origin ? `from ${rule.origin}` : undefined, rule.reason].filter(Boolean).join(" \u2014 ");
57851
+ lines.push(`- ${rule.ref}: ${rule.status}${detail ? ` \u2014 ${detail}` : ""} (cited by ${rule.citedBy.join(", ")})`);
57852
+ }
57853
+ lines.push("");
57854
+ }
57730
57855
  if (result.imported.some((row) => row.module === "review" && row.status !== "skipped")) {
57731
57856
  lines.push("Reviewers: `keryx review reviewers` must list every imported review/* name. That is the same call review-orchestrator makes.");
57732
57857
  }
@@ -57752,6 +57877,21 @@ function githubBlobToRaw(url) {
57752
57877
  function isHttpsSkillUrl(from) {
57753
57878
  return /^https:\/\//i.test(from.trim());
57754
57879
  }
57880
+ function isInside4(parent, child) {
57881
+ const relative = path145.relative(parent, child);
57882
+ return relative !== "" && relative !== ".." && !relative.startsWith(`..${path145.sep}`) && !path145.isAbsolute(relative);
57883
+ }
57884
+ function portableOriginRef(absolute, projectRoot, home = homedir4()) {
57885
+ const resolved = path145.resolve(absolute);
57886
+ const root = path145.resolve(projectRoot);
57887
+ if (isInside4(root, resolved)) {
57888
+ return toPosix(path145.relative(root, resolved));
57889
+ }
57890
+ if (isInside4(home, resolved)) {
57891
+ return `~/${toPosix(path145.relative(home, resolved))}`;
57892
+ }
57893
+ return resolved;
57894
+ }
57755
57895
  async function resolveImportSources(options) {
57756
57896
  if (isHttpsSkillUrl(options.from)) {
57757
57897
  return [await sourceFromUrl(options)];
@@ -57762,7 +57902,7 @@ async function resolveImportSources(options) {
57762
57902
  }
57763
57903
  const stats = await stat15(resolved);
57764
57904
  if (stats.isFile()) {
57765
- return [await sourceFromFile(options, resolved, options.from)];
57905
+ return [await sourceFromFile(options, resolved)];
57766
57906
  }
57767
57907
  if (!stats.isDirectory()) {
57768
57908
  throw new Error(`keryx skills import: --from ${options.from} is not a file or directory`);
@@ -57787,49 +57927,55 @@ async function sourceFromUrl(options) {
57787
57927
  const moduleName = options.module ?? inferModule2(name, response.text);
57788
57928
  return { name, module: moduleName, origin: url, content: response.text };
57789
57929
  }
57790
- async function sourceFromFile(options, absolute, originRef) {
57930
+ async function sourceFromFile(options, absolute) {
57791
57931
  const content = await readFile81(absolute, "utf8");
57792
57932
  const name = options.name ?? inferNameFromPath(absolute, content);
57793
57933
  const moduleName = options.module ?? inferModule2(name, content);
57794
- return { name, module: moduleName, origin: originRef, content };
57934
+ return { name, module: moduleName, origin: portableOriginRef(absolute, options.projectRoot), content, sourcePath: absolute };
57795
57935
  }
57796
57936
  async function sourceFromDirectory(options, absolute) {
57797
57937
  const roots = await skillPackageDirs(absolute);
57798
57938
  const sources = [];
57799
57939
  for (const dir of roots) {
57800
- const name = path144.basename(dir);
57940
+ const name = path145.basename(dir);
57801
57941
  if (options.namePrefix && !name.startsWith(options.namePrefix))
57802
57942
  continue;
57803
57943
  if (options.name && name !== options.name)
57804
57944
  continue;
57805
- const skillMd = path144.join(dir, "SKILL.md");
57945
+ const skillMd = path145.join(dir, "SKILL.md");
57806
57946
  const content = await readFile81(skillMd, "utf8");
57807
57947
  const moduleName = options.module ?? inferModule2(name, content);
57808
- sources.push({ name, module: moduleName, origin: skillMd, content });
57948
+ sources.push({
57949
+ name,
57950
+ module: moduleName,
57951
+ origin: portableOriginRef(skillMd, options.projectRoot),
57952
+ content,
57953
+ sourcePath: skillMd
57954
+ });
57809
57955
  }
57810
57956
  return sources.sort((a, b) => `${a.module}/${a.name}`.localeCompare(`${b.module}/${b.name}`));
57811
57957
  }
57812
57958
  async function skillPackageDirs(root) {
57813
- if (await pathExists(path144.join(root, "SKILL.md"))) {
57959
+ if (await pathExists(path145.join(root, "SKILL.md"))) {
57814
57960
  return [root];
57815
57961
  }
57816
- const nestedSkills = path144.join(root, "skills");
57962
+ const nestedSkills = path145.join(root, "skills");
57817
57963
  const scan = await pathExists(nestedSkills) ? nestedSkills : root;
57818
57964
  const entries = await readdir24(scan, { withFileTypes: true });
57819
57965
  const dirs = [];
57820
57966
  for (const entry of entries) {
57821
57967
  if (!entry.isDirectory())
57822
57968
  continue;
57823
- const dir = path144.join(scan, entry.name);
57824
- if (await pathExists(path144.join(dir, "SKILL.md"))) {
57969
+ const dir = path145.join(scan, entry.name);
57970
+ if (await pathExists(path145.join(dir, "SKILL.md"))) {
57825
57971
  dirs.push(dir);
57826
57972
  }
57827
57973
  }
57828
57974
  return dirs;
57829
57975
  }
57830
57976
  async function importOne(options, source) {
57831
- const dest = path144.posix.join(".metaproject", "project-skills", source.module, source.name);
57832
- const destAbs = path144.join(options.projectRoot, dest, "SKILL.md");
57977
+ const dest = path145.posix.join(".metaproject", "project-skills", source.module, source.name);
57978
+ const destAbs = path145.join(options.projectRoot, dest, "SKILL.md");
57833
57979
  const exists = await pathExists(destAbs);
57834
57980
  const bundled = BUNDLED_NAMES.has(source.name);
57835
57981
  if (bundled && !options.force) {
@@ -57870,6 +58016,7 @@ async function importOne(options, source) {
57870
58016
  note: `Imported from ${options.from}`,
57871
58017
  origin: source.origin,
57872
58018
  originContent: source.content,
58019
+ ...versionOption(source.content),
57873
58020
  format: "single"
57874
58021
  });
57875
58022
  await overwriteImportedSkill(options.projectRoot, created, source.content);
@@ -57883,9 +58030,9 @@ async function importOne(options, source) {
57883
58030
  };
57884
58031
  }
57885
58032
  async function updateOne(options, entry) {
57886
- const skillMd = path144.join(options.projectRoot, entry.path, "SKILL.md");
58033
+ const skillMd = path145.join(options.projectRoot, entry.path, "SKILL.md");
57887
58034
  const current = await readFile81(skillMd, "utf8");
57888
- const origin = options.from ?? metadataLine(current, "Origin");
58035
+ let origin = options.from ?? metadataLine(current, "Origin");
57889
58036
  if (!origin) {
57890
58037
  return {
57891
58038
  name: entry.name,
@@ -57924,6 +58071,7 @@ async function updateOne(options, entry) {
57924
58071
  };
57925
58072
  }
57926
58073
  content = await readFile81(resolved, "utf8");
58074
+ origin = portableOriginRef(resolved, options.projectRoot);
57927
58075
  }
57928
58076
  if (options.dryRun) {
57929
58077
  return {
@@ -57942,6 +58090,7 @@ async function updateOne(options, entry) {
57942
58090
  note: `Updated from origin ${origin}`,
57943
58091
  origin,
57944
58092
  originContent: content,
58093
+ ...versionOption(content),
57945
58094
  format: "single"
57946
58095
  });
57947
58096
  await overwriteImportedSkill(options.projectRoot, created, content);
@@ -57955,11 +58104,11 @@ async function updateOne(options, entry) {
57955
58104
  };
57956
58105
  }
57957
58106
  async function overwriteImportedSkill(projectRoot, created, source) {
57958
- const skillPath = path144.join(projectRoot, created.skillPath, "SKILL.md");
58107
+ const skillPath = path145.join(projectRoot, created.skillPath, "SKILL.md");
57959
58108
  const scaffold = await readFile81(skillPath, "utf8");
57960
- const originLines = extractOriginBlock(scaffold);
57961
- const stamped = stampOrigin(source, originLines);
57962
- const relative = toPosix(path144.join(created.skillPath, "SKILL.md"));
58109
+ const header = extractImportHeader(scaffold, parseSkillFrontmatter(source).metadataVersion);
58110
+ const stamped = stampImportHeader(source, header);
58111
+ const relative = toPosix(path145.join(created.skillPath, "SKILL.md"));
57963
58112
  const guard = await guardOutput({
57964
58113
  cwd: projectRoot,
57965
58114
  content: stamped,
@@ -57973,15 +58122,26 @@ async function overwriteImportedSkill(projectRoot, created, source) {
57973
58122
  }
57974
58123
  await writeFileAtomic(skillPath, output.content);
57975
58124
  }
57976
- function extractOriginBlock(scaffold) {
58125
+ var IMPORT_HEADER_LABELS = [
58126
+ "Version",
58127
+ "Target",
58128
+ "Module",
58129
+ "Origin",
58130
+ "Origin Hash",
58131
+ "Imported At",
58132
+ "Status",
58133
+ "Last Verified"
58134
+ ];
58135
+ var IMPORT_HEADER_LINE = new RegExp(`^(${IMPORT_HEADER_LABELS.join("|")}):\\s`);
58136
+ function extractImportHeader(scaffold, version) {
57977
58137
  const lines = scaffold.split(`
57978
- `).filter((line) => /^(Origin:|Origin Hash:|Imported At:)\s/.test(line));
58138
+ `).filter((line) => IMPORT_HEADER_LINE.test(line)).map((line) => version && line.startsWith("Version:") ? `Version: ${version}` : line);
57979
58139
  return lines.length > 0 ? `${lines.join(`
57980
58140
  `)}
57981
58141
  ` : "";
57982
58142
  }
57983
- function stampOrigin(source, originBlock) {
57984
- if (!originBlock)
58143
+ function stampImportHeader(source, header) {
58144
+ if (!header)
57985
58145
  return source;
57986
58146
  const stripped = source.replace(/^Origin Hash:.*\n/gm, "").replace(/^Origin:.*\n/gm, "").replace(/^Imported At:.*\n/gm, "");
57987
58147
  if (stripped.startsWith("---")) {
@@ -57992,19 +58152,108 @@ function stampOrigin(source, originBlock) {
57992
58152
  ---`.length;
57993
58153
  const insertAt = stripped[afterFence] === `
57994
58154
  ` ? afterFence + 1 : afterFence;
57995
- return `${stripped.slice(0, insertAt)}${originBlock}${stripped.slice(insertAt)}`;
58155
+ let bodyStart = insertAt;
58156
+ while (true) {
58157
+ const lineEnd = stripped.indexOf(`
58158
+ `, bodyStart);
58159
+ const line = stripped.slice(bodyStart, lineEnd === -1 ? stripped.length : lineEnd);
58160
+ if (!IMPORT_HEADER_LINE.test(line))
58161
+ break;
58162
+ bodyStart = lineEnd === -1 ? stripped.length : lineEnd + 1;
58163
+ }
58164
+ return `${stripped.slice(0, insertAt)}${header}${stripped.slice(bodyStart)}`;
57996
58165
  }
57997
58166
  }
57998
- return `${originBlock}${stripped}`;
58167
+ return `${header}${stripped}`;
58168
+ }
58169
+ function versionOption(content) {
58170
+ const version = parseSkillFrontmatter(content).metadataVersion;
58171
+ return version ? { version } : {};
58172
+ }
58173
+ async function findOverlayRule(sourcePath, ref) {
58174
+ let dir = path145.dirname(sourcePath);
58175
+ for (let depth = 0;depth <= 3; depth += 1) {
58176
+ const candidate = path145.join(dir, "rules", ref);
58177
+ if (await pathExists(candidate))
58178
+ return candidate;
58179
+ const parent = path145.dirname(dir);
58180
+ if (parent === dir)
58181
+ break;
58182
+ dir = parent;
58183
+ }
58184
+ return;
58185
+ }
58186
+ async function importReferencedRules(options, sources) {
58187
+ const citedBy = new Map;
58188
+ for (const source of sources) {
58189
+ for (const ref of ruleReferences(source.content)) {
58190
+ const entry = citedBy.get(ref) ?? { names: [], sourcePaths: [] };
58191
+ entry.names.push(source.name);
58192
+ if (source.sourcePath)
58193
+ entry.sourcePaths.push(source.sourcePath);
58194
+ citedBy.set(ref, entry);
58195
+ }
58196
+ }
58197
+ const bundledRules = bundledRulesSourcePath();
58198
+ const rules = [];
58199
+ for (const [ref, cited] of [...citedBy.entries()].sort(([a], [b]) => a.localeCompare(b))) {
58200
+ const base = { ref, citedBy: cited.names.sort() };
58201
+ const target = path145.join(options.projectRoot, ".metaproject", "rules", ref);
58202
+ if (await pathExists(target)) {
58203
+ rules.push({ ...base, status: "present" });
58204
+ continue;
58205
+ }
58206
+ if (ref.startsWith("core/") && await pathExists(path145.join(bundledRules, path145.basename(ref)))) {
58207
+ rules.push({ ...base, status: "unresolved", reason: "ships with keryx \u2014 run `keryx install` to restore it" });
58208
+ continue;
58209
+ }
58210
+ let found;
58211
+ for (const sourcePath of cited.sourcePaths) {
58212
+ found = await findOverlayRule(sourcePath, ref);
58213
+ if (found)
58214
+ break;
58215
+ }
58216
+ if (!found) {
58217
+ rules.push({
58218
+ ...base,
58219
+ status: "unresolved",
58220
+ reason: cited.sourcePaths.length > 0 ? "no rules/ directory beside the source has it" : "remote source; rules are not fetched"
58221
+ });
58222
+ continue;
58223
+ }
58224
+ const origin = portableOriginRef(found, options.projectRoot);
58225
+ if (options.dryRun) {
58226
+ rules.push({ ...base, status: "would-import", origin });
58227
+ continue;
58228
+ }
58229
+ const content = await readFile81(found, "utf8");
58230
+ const relative = toPosix(path145.relative(options.projectRoot, target));
58231
+ const guard = await guardOutput({
58232
+ cwd: options.projectRoot,
58233
+ content,
58234
+ target: "skill",
58235
+ source: "untrusted-external",
58236
+ path: relative
58237
+ });
58238
+ const output = prepareOutputForPersistence(guard, content);
58239
+ if (!output.allowed) {
58240
+ rules.push({ ...base, status: "unresolved", origin, reason: `blocked by the security gate: ${output.reason}` });
58241
+ continue;
58242
+ }
58243
+ await mkdir50(path145.dirname(target), { recursive: true });
58244
+ await writeFileAtomic(target, output.content);
58245
+ rules.push({ ...base, status: "imported", origin });
58246
+ }
58247
+ return rules;
57999
58248
  }
58000
58249
  function inferNameFromPath(filePath, content) {
58001
58250
  const fromFrontmatter = frontmatterName(content);
58002
58251
  if (fromFrontmatter)
58003
58252
  return fromFrontmatter;
58004
- const parent = path144.basename(path144.dirname(filePath));
58253
+ const parent = path145.basename(path145.dirname(filePath));
58005
58254
  if (parent && parent !== "skills" && parent !== ".")
58006
58255
  return parent;
58007
- return path144.basename(filePath, path144.extname(filePath));
58256
+ return path145.basename(filePath, path145.extname(filePath));
58008
58257
  }
58009
58258
  function inferNameFromUrl(url) {
58010
58259
  const cleaned = url.replace(/\/SKILL\.md$/i, "");
@@ -58043,7 +58292,7 @@ function metadataLine(content, label) {
58043
58292
  }
58044
58293
  async function readRegistry(projectRoot) {
58045
58294
  await Promise.resolve().then(() => init_json());
58046
- const manifestPath = path144.join(projectRoot, ".metaproject", "metaproject.json");
58295
+ const manifestPath = path145.join(projectRoot, ".metaproject", "metaproject.json");
58047
58296
  const manifest = await readJsonFileOr(manifestPath, {});
58048
58297
  return manifest.modules?.gdskills?.projectSkillRegistry ?? [];
58049
58298
  }
@@ -58165,10 +58414,10 @@ init_fs();
58165
58414
  init_json();
58166
58415
  init_relevant();
58167
58416
  init_service3();
58168
- import { mkdir as mkdir50, readFile as readFile82, writeFile as writeFile46 } from "fs/promises";
58169
- import path145 from "path";
58417
+ import { mkdir as mkdir51, readFile as readFile82, writeFile as writeFile46 } from "fs/promises";
58418
+ import path146 from "path";
58170
58419
  async function verifyProjectSkill(projectRoot, options) {
58171
- const metaprojectRoot = path145.join(projectRoot, ".metaproject");
58420
+ const metaprojectRoot = path146.join(projectRoot, ".metaproject");
58172
58421
  if (!await pathExists(metaprojectRoot)) {
58173
58422
  throw new Error("Metaproject is not initialized. Run: keryx init");
58174
58423
  }
@@ -58178,11 +58427,11 @@ async function verifyProjectSkill(projectRoot, options) {
58178
58427
  if (!resolved) {
58179
58428
  throw new Error(`Project skill not found for: ${options.input}`);
58180
58429
  }
58181
- const skillMdPath = path145.join(resolved.packageRoot, "SKILL.md");
58430
+ const skillMdPath = path146.join(resolved.packageRoot, "SKILL.md");
58182
58431
  const skillMd = await readFile82(skillMdPath, "utf8");
58183
- const metadata = parseSkillMetadata(skillMd);
58432
+ const metadata = parseSkillMetadata(skillMd, resolved.entry);
58184
58433
  const moduleName = metadata.module ?? resolved.entry?.module ?? inferModuleFromPath(resolved.packageRoot);
58185
- const skillName = resolved.entry?.name ?? path145.basename(resolved.packageRoot);
58434
+ const skillName = resolved.entry?.name ?? path146.basename(resolved.packageRoot);
58186
58435
  const target = metadata.target ?? resolved.entry?.target ?? options.input;
58187
58436
  const verifiedAt = new Date().toISOString();
58188
58437
  const signals = await collectVerificationSignals({
@@ -58195,15 +58444,15 @@ async function verifyProjectSkill(projectRoot, options) {
58195
58444
  });
58196
58445
  const status = classifyStatus(signals, metadata);
58197
58446
  const recommendations = recommendationsFor(signals, status);
58198
- const reportPath = path145.join(metaprojectRoot, "data", "gdskills", "reports", `${moduleName}-${skillName}-verification.json`);
58199
- const relativeReportPath = toPosix(path145.relative(projectRoot, reportPath));
58447
+ const reportPath = path146.join(metaprojectRoot, "data", "gdskills", "reports", `${moduleName}-${skillName}-verification.json`);
58448
+ const relativeReportPath = toPosix(path146.relative(projectRoot, reportPath));
58200
58449
  const report = {
58201
58450
  schemaVersion: 1,
58202
58451
  status,
58203
58452
  module: moduleName,
58204
58453
  name: skillName,
58205
58454
  target,
58206
- skillPath: toPosix(path145.relative(projectRoot, resolved.packageRoot)),
58455
+ skillPath: toPosix(path146.relative(projectRoot, resolved.packageRoot)),
58207
58456
  reportPath: relativeReportPath,
58208
58457
  verifiedAt,
58209
58458
  signals,
@@ -58211,16 +58460,16 @@ async function verifyProjectSkill(projectRoot, options) {
58211
58460
  dryRun: options.dryRun === true
58212
58461
  };
58213
58462
  if (!options.dryRun) {
58214
- await mkdir50(path145.dirname(reportPath), { recursive: true });
58463
+ await mkdir51(path146.dirname(reportPath), { recursive: true });
58215
58464
  await writeFile46(reportPath, `${JSON.stringify(report, null, 2)}
58216
58465
  `, "utf8");
58217
- await writeFile46(path145.join(resolved.packageRoot, "verification.md"), renderVerificationMarkdown(report), "utf8");
58466
+ await writeFile46(path146.join(resolved.packageRoot, "verification.md"), renderVerificationMarkdown(report), "utf8");
58218
58467
  await writeFile46(skillMdPath, updateLastVerified(skillMd, verifiedAt), "utf8");
58219
58468
  }
58220
58469
  return report;
58221
58470
  }
58222
58471
  async function readManifest2(projectRoot) {
58223
- const manifestPath = path145.join(projectRoot, ".metaproject", "metaproject.json");
58472
+ const manifestPath = path146.join(projectRoot, ".metaproject", "metaproject.json");
58224
58473
  if (!await pathExists(manifestPath)) {
58225
58474
  return {};
58226
58475
  }
@@ -58240,31 +58489,31 @@ async function collectVerificationSignals({
58240
58489
  "skill-changelog.md"
58241
58490
  ];
58242
58491
  for (const requiredFile of requiredFiles) {
58243
- const filePath = path145.join(packageRoot, requiredFile);
58492
+ const filePath = path146.join(packageRoot, requiredFile);
58244
58493
  signals.push({
58245
58494
  name: `required:${requiredFile}`,
58246
58495
  status: await pathExists(filePath) ? "pass" : "fail",
58247
58496
  message: await pathExists(filePath) ? "Required package file exists." : "Required package file is missing.",
58248
- path: toPosix(path145.relative(projectRoot, filePath))
58497
+ path: toPosix(path146.relative(projectRoot, filePath))
58249
58498
  });
58250
58499
  }
58251
58500
  signals.push({
58252
58501
  name: "metadata:version",
58253
58502
  status: metadata.version ? "pass" : "fail",
58254
58503
  message: metadata.version ? `Version ${metadata.version}` : "SKILL.md is missing Version metadata.",
58255
- path: toPosix(path145.relative(projectRoot, skillMdPath))
58504
+ path: toPosix(path146.relative(projectRoot, skillMdPath))
58256
58505
  });
58257
58506
  signals.push({
58258
58507
  name: "metadata:target",
58259
58508
  status: metadata.target ? "pass" : "fail",
58260
58509
  message: metadata.target ? `Target ${metadata.target}` : "SKILL.md is missing Target metadata.",
58261
- path: toPosix(path145.relative(projectRoot, skillMdPath))
58510
+ path: toPosix(path146.relative(projectRoot, skillMdPath))
58262
58511
  });
58263
58512
  signals.push({
58264
58513
  name: "metadata:last-verified",
58265
58514
  status: metadata.lastVerified && metadata.lastVerified !== "never" ? "pass" : "warn",
58266
58515
  message: metadata.lastVerified && metadata.lastVerified !== "never" ? `Last verified at ${metadata.lastVerified}` : "Skill has not been verified before.",
58267
- path: toPosix(path145.relative(projectRoot, skillMdPath))
58516
+ path: toPosix(path146.relative(projectRoot, skillMdPath))
58268
58517
  });
58269
58518
  signals.push({
58270
58519
  name: "registry",
@@ -58272,14 +58521,14 @@ async function collectVerificationSignals({
58272
58521
  message: registryEntry ? "Skill is registered in metaproject manifest." : "Skill package is not registered in metaproject manifest.",
58273
58522
  path: ".metaproject/metaproject.json"
58274
58523
  });
58275
- const targetPath = path145.resolve(projectRoot, target);
58524
+ const targetPath = path146.resolve(projectRoot, target);
58276
58525
  const targetLooksLikePath = target.includes("/") || target.includes(".") || target.startsWith(".");
58277
58526
  const targetExists = targetLooksLikePath && await pathExists(targetPath);
58278
58527
  signals.push({
58279
58528
  name: "target",
58280
58529
  status: !targetLooksLikePath || targetExists ? "pass" : "fail",
58281
58530
  message: targetLooksLikePath ? targetExists ? "Target path exists." : "Target path is missing." : "Target is treated as a symbol or concept.",
58282
- path: targetLooksLikePath ? toPosix(path145.relative(projectRoot, targetPath)) : undefined
58531
+ path: targetLooksLikePath ? toPosix(path146.relative(projectRoot, targetPath)) : undefined
58283
58532
  });
58284
58533
  await pushArtifactSignal(projectRoot, signals, "gdgraph", [
58285
58534
  ".metaproject/data/gdgraph/artifacts/summary.md",
@@ -58309,7 +58558,7 @@ async function collectVerificationSignals({
58309
58558
  async function pushArtifactSignal(projectRoot, signals, name, candidates) {
58310
58559
  const existing = [];
58311
58560
  for (const candidate of candidates) {
58312
- if (await pathExists(path145.join(projectRoot, candidate))) {
58561
+ if (await pathExists(path146.join(projectRoot, candidate))) {
58313
58562
  existing.push(candidate);
58314
58563
  }
58315
58564
  }
@@ -58322,7 +58571,7 @@ async function pushArtifactSignal(projectRoot, signals, name, candidates) {
58322
58571
  }
58323
58572
  async function pushGdwikiSignal(projectRoot, signals) {
58324
58573
  const indexPath = ".metaproject/wiki/index.md";
58325
- if (!await pathExists(path145.join(projectRoot, indexPath))) {
58574
+ if (!await pathExists(path146.join(projectRoot, indexPath))) {
58326
58575
  signals.push({
58327
58576
  name: "evidence:gdwiki",
58328
58577
  status: "warn",
@@ -58420,10 +58669,10 @@ ${signalRows}
58420
58669
  ${recommendations}
58421
58670
  `;
58422
58671
  }
58423
- function parseSkillMetadata(skillMd) {
58672
+ function parseSkillMetadata(skillMd, registryEntry) {
58424
58673
  return {
58425
- version: readMetadataLine(skillMd, "Version"),
58426
- target: readMetadataLine(skillMd, "Target"),
58674
+ version: readMetadataLine(skillMd, "Version") ?? parseSkillFrontmatter(skillMd).metadataVersion,
58675
+ target: readMetadataLine(skillMd, "Target") ?? registryEntry?.target,
58427
58676
  module: readMetadataLine(skillMd, "Module"),
58428
58677
  status: readMetadataLine(skillMd, "Status"),
58429
58678
  lastVerified: readMetadataLine(skillMd, "Last Verified")
@@ -58441,6 +58690,22 @@ function updateLastVerified(skillMd, verifiedAt) {
58441
58690
  return skillMd.replace(/^Status:.*$/m, (line) => `${line}
58442
58691
  Last Verified: ${verifiedAt}`);
58443
58692
  }
58693
+ if (/^Imported At:.*$/m.test(skillMd)) {
58694
+ return skillMd.replace(/^Imported At:.*$/m, (line) => `${line}
58695
+ Last Verified: ${verifiedAt}`);
58696
+ }
58697
+ if (skillMd.startsWith("---")) {
58698
+ const end = skillMd.indexOf(`
58699
+ ---`, 3);
58700
+ if (end !== -1) {
58701
+ const afterFence = end + `
58702
+ ---`.length;
58703
+ const insertAt = skillMd[afterFence] === `
58704
+ ` ? afterFence + 1 : afterFence;
58705
+ return `${skillMd.slice(0, insertAt)}Last Verified: ${verifiedAt}
58706
+ ${skillMd.slice(insertAt)}`;
58707
+ }
58708
+ }
58444
58709
  return skillMd;
58445
58710
  }
58446
58711
  function inferModuleFromPath(packageRoot) {
@@ -58455,7 +58720,7 @@ function escapeRegExp6(value) {
58455
58720
 
58456
58721
  // src/gdskills/bundled-eval.ts
58457
58722
  import { existsSync as existsSync25, readFileSync as readFileSync7, readdirSync as readdirSync2 } from "fs";
58458
- import path146 from "path";
58723
+ import path147 from "path";
58459
58724
 
58460
58725
  // src/gdskills/model-tier.ts
58461
58726
  var MODEL_TIERS = ["light", "standard", "deep"];
@@ -58785,7 +59050,7 @@ var CEILINGS_BY_KEY = {
58785
59050
  "review/review-highload": 550,
58786
59051
  "review/review-layout": 238,
58787
59052
  "review/review-logic": 377,
58788
- "review/review-orchestrator": 1907,
59053
+ "review/review-orchestrator": 1895,
58789
59054
  "review/review-performance": 374,
58790
59055
  "review/review-pr-feedback": 895,
58791
59056
  "review/review-regression": 216,
@@ -58912,8 +59177,8 @@ function evaluatorSource() {
58912
59177
  if (here.protocol !== "file:")
58913
59178
  return "unknown";
58914
59179
  const dir = decodeURIComponent(here.pathname);
58915
- const rel = path146.relative(process.cwd(), dir);
58916
- return rel.length > 0 && !rel.startsWith("..") && !path146.isAbsolute(rel) ? rel : dir;
59180
+ const rel = path147.relative(process.cwd(), dir);
59181
+ return rel.length > 0 && !rel.startsWith("..") && !path147.isAbsolute(rel) ? rel : dir;
58917
59182
  } catch {
58918
59183
  return "unknown";
58919
59184
  }
@@ -59315,7 +59580,7 @@ function scanCrossReferences(text, root, known, checkSkillNames, add) {
59315
59580
  if (GENERATED_PATH_ROOTS.some((entry) => resolved === entry.prefix.replace(/\/$/, "") || resolved.startsWith(entry.prefix))) {
59316
59581
  continue;
59317
59582
  }
59318
- if (existsSync25(path146.join(root, resolved)))
59583
+ if (existsSync25(path147.join(root, resolved)))
59319
59584
  continue;
59320
59585
  add("xref:path", index + 1, `path \`${raw}\` resolves to nothing under the shipped tree (looked for \`${resolved}\`).`);
59321
59586
  }
@@ -59325,7 +59590,7 @@ var SKILL_DOCUMENT_NAMES = new Set(HARNESS_SKILL_RUNTIMES.map((runtime) => skill
59325
59590
  var KNOWN_SKILL_COMPANION_DOCUMENTS = new Map([
59326
59591
  [
59327
59592
  "SKILL.detail.md",
59328
- "overflow reference for `orchestration/feature-analyzer`, linked from its SKILL.md; carries no frontmatter and is not addressed by any runtime"
59593
+ "overflow reference linked from its skill's SKILL.md (`orchestration/feature-analyzer`, `review/review-orchestrator`); carries no frontmatter and is not addressed by any runtime"
59329
59594
  ],
59330
59595
  [
59331
59596
  "orchestrator-prompt.md",
@@ -59338,7 +59603,7 @@ function walkSkillDocuments(root, accept) {
59338
59603
  const out = [];
59339
59604
  const walk = (dir) => {
59340
59605
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
59341
- const full = path146.join(dir, entry.name);
59606
+ const full = path147.join(dir, entry.name);
59342
59607
  if (entry.isDirectory()) {
59343
59608
  walk(full);
59344
59609
  continue;
@@ -59360,26 +59625,26 @@ function bundledSkillCompanionDocuments(root) {
59360
59625
  return walkSkillDocuments(root, (name) => KNOWN_SKILL_COMPANION_DOCUMENTS.has(name));
59361
59626
  }
59362
59627
  function bundledRuleFiles(root) {
59363
- const rulesRoot = path146.join(root, "rules", "core");
59628
+ const rulesRoot = path147.join(root, "rules", "core");
59364
59629
  if (!existsSync25(rulesRoot))
59365
59630
  return [];
59366
- return readdirSync2(rulesRoot, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => path146.join(rulesRoot, entry.name)).sort();
59631
+ return readdirSync2(rulesRoot, { withFileTypes: true }).filter((entry) => entry.isFile()).map((entry) => path147.join(rulesRoot, entry.name)).sort();
59367
59632
  }
59368
59633
  function defaultBundledRoot() {
59369
- const directPath = path146.join(import.meta.dir, "bundled");
59634
+ const directPath = path147.join(import.meta.dir, "bundled");
59370
59635
  if (existsSync25(directPath))
59371
59636
  return directPath;
59372
- const packagedPath = path146.join(import.meta.dir, "..", "src", "gdskills", "bundled");
59637
+ const packagedPath = path147.join(import.meta.dir, "..", "src", "gdskills", "bundled");
59373
59638
  if (existsSync25(packagedPath))
59374
59639
  return packagedPath;
59375
59640
  return directPath;
59376
59641
  }
59377
59642
  function evaluateBundledTree(root = defaultBundledRoot()) {
59378
- const skillsRoot = path146.join(root, "skills");
59643
+ const skillsRoot = path147.join(root, "skills");
59379
59644
  const canonical = bundledSkillFiles(skillsRoot);
59380
59645
  const files = bundledSkillDocuments(skillsRoot);
59381
59646
  const companionDocuments = bundledSkillCompanionDocuments(skillsRoot);
59382
- const skillNames = [...new Set(canonical.map((file) => path146.basename(path146.dirname(file))))].sort();
59647
+ const skillNames = [...new Set(canonical.map((file) => path147.basename(path147.dirname(file))))].sort();
59383
59648
  const known = new Set(skillNames);
59384
59649
  const findings = [];
59385
59650
  const declaredNames = new Map;
@@ -59389,10 +59654,10 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
59389
59654
  const redFlagsByKey = new Map;
59390
59655
  const redFlagsFileByKey = new Map;
59391
59656
  for (const file of files) {
59392
- const skillDir = path146.dirname(file);
59393
- const skill = path146.basename(skillDir);
59394
- const rel = path146.relative(skillsRoot, file).split(path146.sep).join("/");
59395
- const category = path146.dirname(rel).split("/")[0] ?? "";
59657
+ const skillDir = path147.dirname(file);
59658
+ const skill = path147.basename(skillDir);
59659
+ const rel = path147.relative(skillsRoot, file).split(path147.sep).join("/");
59660
+ const category = path147.dirname(rel).split("/")[0] ?? "";
59396
59661
  const text = readFileSync7(file, "utf8");
59397
59662
  const add = (check, line, message) => {
59398
59663
  findings.push({ check, skill, file: rel, line, message });
@@ -59426,7 +59691,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
59426
59691
  if (served.length === 0) {
59427
59692
  add("frontmatter:description", 1, "frontmatter `description` is present but resolves to nothing a harness can match a request against; a block scalar (`description: |`) needs its text on the following indented lines.");
59428
59693
  } else {
59429
- if (path146.basename(file) === "SKILL.md") {
59694
+ if (path147.basename(file) === "SKILL.md") {
59430
59695
  const key = `${category}/${skill}`;
59431
59696
  descriptionsByKey.set(key, served);
59432
59697
  descriptionFileByKey.set(key, rel);
@@ -59453,14 +59718,14 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
59453
59718
  add("frontmatter:category", 1, `frontmatter \`metadata.category\` is "${declaredCategory}" but this skill ships under \`${category}/${skill}\`; set it to "${category}" or drop the field.`);
59454
59719
  }
59455
59720
  }
59456
- if (path146.basename(file) === "SKILL.md" && parsed.compatibleHarnesses !== undefined) {
59721
+ if (path147.basename(file) === "SKILL.md" && parsed.compatibleHarnesses !== undefined) {
59457
59722
  const harnesses = parsed.compatibleHarnesses;
59458
59723
  if (!harnesses.includes("claude")) {
59459
59724
  add("frontmatter:harness-claude", 1, `frontmatter \`compatible_harnesses\` ("${harnesses.join(",")}") omits \`claude\`, but this file IS the Claude build \u2014 add \`claude\` to the list.`);
59460
59725
  }
59461
59726
  }
59462
59727
  }
59463
- if (path146.basename(file) === "SKILL.md") {
59728
+ if (path147.basename(file) === "SKILL.md") {
59464
59729
  const exemptionKey = `${category}/${skill}`;
59465
59730
  redFlagsByKey.set(exemptionKey, redFlagsTableBody(text));
59466
59731
  redFlagsFileByKey.set(exemptionKey, rel);
@@ -59481,7 +59746,7 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
59481
59746
  add("anatomy:sections", null, `missing ${ANATOMY_SECTION_LABELS[section]}.`);
59482
59747
  }
59483
59748
  }
59484
- if (path146.basename(file) === "SKILL.md") {
59749
+ if (path147.basename(file) === "SKILL.md") {
59485
59750
  const ceilingKey = `${category}/${skill}`;
59486
59751
  const recordedCeiling = SKILL_LENGTH_CEILINGS.get(ceilingKey);
59487
59752
  const ceiling = recordedCeiling ?? DEFAULT_SKILL_LENGTH_CEILING;
@@ -59542,24 +59807,24 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
59542
59807
  }
59543
59808
  }
59544
59809
  for (const file of bundledRuleFiles(root)) {
59545
- const rel = path146.relative(root, file).split(path146.sep).join("/");
59810
+ const rel = path147.relative(root, file).split(path147.sep).join("/");
59546
59811
  const text = readFileSync7(file, "utf8");
59547
59812
  const add = (check, line, message) => {
59548
- findings.push({ check, skill: path146.basename(file, path146.extname(file)), file: rel, line, message });
59813
+ findings.push({ check, skill: path147.basename(file, path147.extname(file)), file: rel, line, message });
59549
59814
  };
59550
59815
  scanCrossReferences(text, root, known, false, add);
59551
59816
  }
59552
59817
  for (const file of companionDocuments) {
59553
- const skillDir = path146.dirname(file);
59554
- const skill = path146.basename(skillDir);
59555
- const rel = path146.relative(skillsRoot, file).split(path146.sep).join("/");
59818
+ const skillDir = path147.dirname(file);
59819
+ const skill = path147.basename(skillDir);
59820
+ const rel = path147.relative(skillsRoot, file).split(path147.sep).join("/");
59556
59821
  const text = readFileSync7(file, "utf8");
59557
59822
  const add = (check, line, message) => {
59558
59823
  findings.push({ check, skill, file: rel, line, message });
59559
59824
  };
59560
59825
  scanCrossReferences(text, root, known, true, add);
59561
59826
  }
59562
- for (const dir of [...new Set(files.map((file) => path146.dirname(file)))].sort()) {
59827
+ for (const dir of [...new Set(files.map((file) => path147.dirname(file)))].sort()) {
59563
59828
  for (const entry of readdirSync2(dir, { withFileTypes: true })) {
59564
59829
  if (!entry.isFile())
59565
59830
  continue;
@@ -59571,15 +59836,15 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
59571
59836
  continue;
59572
59837
  findings.push({
59573
59838
  check: "document:addressable",
59574
- skill: path146.basename(dir),
59575
- file: path146.relative(skillsRoot, path146.join(dir, entry.name)).split(path146.sep).join("/"),
59839
+ skill: path147.basename(dir),
59840
+ file: path147.relative(skillsRoot, path147.join(dir, entry.name)).split(path147.sep).join("/"),
59576
59841
  line: null,
59577
59842
  message: `\`${entry.name}\` is spelled like a harness build, but no runtime in HARNESS_SKILL_RUNTIMES resolves to it (${[...SKILL_DOCUMENT_NAMES].join(", ")}). Nothing exports it, installs it, or reads it \u2014 delete it, rename it to the build it was meant to be, or register it in KNOWN_SKILL_COMPANION_DOCUMENTS with a reason.`
59578
59843
  });
59579
59844
  }
59580
59845
  }
59581
59846
  for (const file of canonical) {
59582
- const dir = path146.dirname(file);
59847
+ const dir = path147.dirname(file);
59583
59848
  let canonicalComparable;
59584
59849
  try {
59585
59850
  canonicalComparable = buildComparableText(readFileSync7(file, "utf8"));
@@ -59588,9 +59853,9 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
59588
59853
  }
59589
59854
  for (const runtime of HARNESS_SKILL_RUNTIMES) {
59590
59855
  const buildName = skillBuildFileName(runtime);
59591
- if (buildName === path146.basename(file))
59856
+ if (buildName === path147.basename(file))
59592
59857
  continue;
59593
- const buildPath = path146.join(dir, buildName);
59858
+ const buildPath = path147.join(dir, buildName);
59594
59859
  if (!existsSync25(buildPath))
59595
59860
  continue;
59596
59861
  let buildComparable;
@@ -59603,8 +59868,8 @@ function evaluateBundledTree(root = defaultBundledRoot()) {
59603
59868
  continue;
59604
59869
  findings.push({
59605
59870
  check: "document:build-parity",
59606
- skill: path146.basename(dir),
59607
- file: path146.relative(skillsRoot, buildPath).split(path146.sep).join("/"),
59871
+ skill: path147.basename(dir),
59872
+ file: path147.relative(skillsRoot, buildPath).split(path147.sep).join("/"),
59608
59873
  line: null,
59609
59874
  message: `\`${buildName}\` no longer carries the content of its \`SKILL.md\`, ignoring the fields a build may set for itself (${[...BUILD_DIVERGENCE_ALLOWED_FIELDS].join(", ")}). An agent on that runtime reads this file, not the canonical one \u2014 re-export the build.`
59610
59875
  });
@@ -59673,7 +59938,7 @@ async function skillsCommand(args) {
59673
59938
  }
59674
59939
  if (command === "install") {
59675
59940
  const profile = normalizeGdskillsProfile(optionValue(args, "--profile"));
59676
- const metaprojectRoot = path147.join(process.cwd(), ".metaproject");
59941
+ const metaprojectRoot = path148.join(process.cwd(), ".metaproject");
59677
59942
  banner("keryx skills install", `Profile: ${profile}`);
59678
59943
  if (!await pathExists(metaprojectRoot)) {
59679
59944
  console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
@@ -59799,11 +60064,11 @@ async function inspectProjectSkill(args) {
59799
60064
  process.exitCode = 1;
59800
60065
  return;
59801
60066
  }
59802
- const skillRoot = path147.resolve(process.cwd(), entry.path);
59803
- const skillMdPath = path147.join(skillRoot, "SKILL.md");
59804
- const verificationPath = path147.join(skillRoot, "verification.md");
59805
- const changelogPath = path147.join(skillRoot, "skill-changelog.md");
59806
- const reportPath = path147.join(process.cwd(), ".metaproject", "data", "gdskills", "reports", `${entry.module}-${entry.name}-verification.json`);
60067
+ const skillRoot = path148.resolve(process.cwd(), entry.path);
60068
+ const skillMdPath = path148.join(skillRoot, "SKILL.md");
60069
+ const verificationPath = path148.join(skillRoot, "verification.md");
60070
+ const changelogPath = path148.join(skillRoot, "skill-changelog.md");
60071
+ const reportPath = path148.join(process.cwd(), ".metaproject", "data", "gdskills", "reports", `${entry.module}-${entry.name}-verification.json`);
59807
60072
  const metadata = await pathExists(skillMdPath) ? parseProjectSkillMetadata(await readFile83(skillMdPath, "utf8")) : {};
59808
60073
  const inspection = {
59809
60074
  module: entry.module,
@@ -59837,7 +60102,7 @@ async function inspectProjectSkill(args) {
59837
60102
  console.log(`- latest report: ${inspection.files.latestReport}`);
59838
60103
  }
59839
60104
  async function readProjectSkillRegistryFromManifest() {
59840
- const manifestPath = path147.join(process.cwd(), ".metaproject", "metaproject.json");
60105
+ const manifestPath = path148.join(process.cwd(), ".metaproject", "metaproject.json");
59841
60106
  if (!await pathExists(manifestPath)) {
59842
60107
  return [];
59843
60108
  }
@@ -59997,7 +60262,7 @@ function scoreProjectSkillRoute(entry, query) {
59997
60262
  return { entry, score: 0, reasons: [] };
59998
60263
  }
59999
60264
  const key = `${entry.module}/${entry.name}`;
60000
- const targetBase = path147.basename(entry.target).replace(/\.[^.]+$/, "");
60265
+ const targetBase = path148.basename(entry.target).replace(/\.[^.]+$/, "");
60001
60266
  const fields = {
60002
60267
  key: normalizeRouteText(key),
60003
60268
  module: normalizeRouteText(entry.module),
@@ -60266,7 +60531,7 @@ function verifyBundledSkills(args) {
60266
60531
  }
60267
60532
  if (evaluation.skills === 0) {
60268
60533
  console.error(`
60269
- No SKILL.md found under ${path147.join(root, "skills")}. Nothing was evaluated, so nothing was verified.`);
60534
+ No SKILL.md found under ${path148.join(root, "skills")}. Nothing was evaluated, so nothing was verified.`);
60270
60535
  process.exitCode = 1;
60271
60536
  return;
60272
60537
  }
@@ -60275,7 +60540,7 @@ No SKILL.md found under ${path147.join(root, "skills")}. Nothing was evaluated,
60275
60540
  }
60276
60541
  }
60277
60542
  async function verifyAllProjectSkills(args) {
60278
- const manifestPath = path147.join(process.cwd(), ".metaproject", "metaproject.json");
60543
+ const manifestPath = path148.join(process.cwd(), ".metaproject", "metaproject.json");
60279
60544
  if (!await pathExists(manifestPath)) {
60280
60545
  console.error("Metaproject is not initialized. Run: keryx init");
60281
60546
  process.exitCode = 1;
@@ -60379,7 +60644,7 @@ async function contractsCommand(args) {
60379
60644
  return;
60380
60645
  }
60381
60646
  try {
60382
- const result = await validateContractFile(path147.resolve(filePath), schemaName);
60647
+ const result = await validateContractFile(path148.resolve(filePath), schemaName);
60383
60648
  if (result.valid) {
60384
60649
  console.log(`valid: ${result.file}`);
60385
60650
  console.log(`schema: ${result.schema}`);
@@ -60424,10 +60689,10 @@ async function printGdskillsStatus(args) {
60424
60689
  console.log(`learning proposals: ${summary.learningProposals.total} ` + `(pending ${summary.learningProposals.pending}, applied ${summary.learningProposals.applied})`);
60425
60690
  }
60426
60691
  async function getGdskillsStatusSummary() {
60427
- const root = path147.join(process.cwd(), ".metaproject");
60428
- const manifestPath = path147.join(root, "metaproject.json");
60429
- const catalogPath = path147.join(root, "skills", "catalog.md");
60430
- const skillsRoot = path147.join(root, "skills", "gdskills");
60692
+ const root = path148.join(process.cwd(), ".metaproject");
60693
+ const manifestPath = path148.join(root, "metaproject.json");
60694
+ const catalogPath = path148.join(root, "skills", "catalog.md");
60695
+ const skillsRoot = path148.join(root, "skills", "gdskills");
60431
60696
  if (!await pathExists(root)) {
60432
60697
  return {
60433
60698
  initialized: false,
@@ -60464,9 +60729,9 @@ async function getGdskillsStatusSummary() {
60464
60729
  profile = normalizeGdskillsProfile(manifest.modules?.gdskills?.profile);
60465
60730
  projectSkillRegistry = manifest.modules?.gdskills?.projectSkillRegistry ?? [];
60466
60731
  }
60467
- const reports = await readVerificationReports(path147.join(root, "data", "gdskills", "reports"));
60732
+ const reports = await readVerificationReports(path148.join(root, "data", "gdskills", "reports"));
60468
60733
  const reportKeys = new Set(reports.map((report) => `${report.module}/${report.name}`));
60469
- const proposals = await readProposalFiles(path147.join(root, "data", "gdskills", "proposals"));
60734
+ const proposals = await readProposalFiles(path148.join(root, "data", "gdskills", "proposals"));
60470
60735
  const statusCounts = countVerificationStatuses(reports);
60471
60736
  return {
60472
60737
  initialized: true,
@@ -60510,8 +60775,8 @@ async function readVerificationReports(reportsRoot) {
60510
60775
  }
60511
60776
  async function readProposalFiles(proposalsRoot) {
60512
60777
  const files = await listJsonFiles(proposalsRoot);
60513
- const appliedIds = new Set(files.filter((filePath) => filePath.endsWith(".applied.json")).map((filePath) => path147.basename(filePath).replace(/\.applied\.json$/, "")));
60514
- const proposalIds = files.filter((filePath) => !filePath.endsWith(".applied.json")).map((filePath) => path147.basename(filePath).replace(/\.json$/, ""));
60778
+ const appliedIds = new Set(files.filter((filePath) => filePath.endsWith(".applied.json")).map((filePath) => path148.basename(filePath).replace(/\.applied\.json$/, "")));
60779
+ const proposalIds = files.filter((filePath) => !filePath.endsWith(".applied.json")).map((filePath) => path148.basename(filePath).replace(/\.json$/, ""));
60515
60780
  return {
60516
60781
  total: proposalIds.length,
60517
60782
  pending: proposalIds.filter((id) => !appliedIds.has(id)).length,
@@ -60523,7 +60788,7 @@ async function listJsonFiles(root) {
60523
60788
  return [];
60524
60789
  }
60525
60790
  const entries = await readdir25(root, { withFileTypes: true });
60526
- return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => path147.join(root, entry.name));
60791
+ return entries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map((entry) => path148.join(root, entry.name));
60527
60792
  }
60528
60793
  function countVerificationStatuses(reports) {
60529
60794
  return reports.reduce((acc, report) => {
@@ -60541,7 +60806,7 @@ function latest(values) {
60541
60806
  return sorted.at(-1) ?? "never";
60542
60807
  }
60543
60808
  function relativeToCwd(filePath) {
60544
- return path147.relative(process.cwd(), filePath) || ".";
60809
+ return path148.relative(process.cwd(), filePath) || ".";
60545
60810
  }
60546
60811
  function printSkillsHelp() {
60547
60812
  console.log(`keryx skills
@@ -60763,9 +61028,9 @@ init_service7();
60763
61028
  init_fs();
60764
61029
  init_util();
60765
61030
  import { readdir as readdir26, readFile as readFile84 } from "fs/promises";
60766
- import path148 from "path";
61031
+ import path149 from "path";
60767
61032
  async function loadHistory(cwd, limit = 20) {
60768
- const dir = path148.join(dataRoot2(cwd), "history");
61033
+ const dir = path149.join(dataRoot2(cwd), "history");
60769
61034
  if (!await pathExists(dir)) {
60770
61035
  return [];
60771
61036
  }
@@ -60773,7 +61038,7 @@ async function loadHistory(cwd, limit = 20) {
60773
61038
  const points = [];
60774
61039
  for (const file of files.slice(-limit)) {
60775
61040
  try {
60776
- const report = JSON.parse(await readFile84(path148.join(dir, file), "utf8"));
61041
+ const report = JSON.parse(await readFile84(path149.join(dir, file), "utf8"));
60777
61042
  const scores = {};
60778
61043
  for (const metric of report.metrics ?? []) {
60779
61044
  scores[metric.key] = metric.health_score;
@@ -61508,7 +61773,7 @@ init_github();
61508
61773
  init_flow_move();
61509
61774
  init_service7();
61510
61775
  init_guard2();
61511
- import path149 from "path";
61776
+ import path150 from "path";
61512
61777
  init_machine();
61513
61778
 
61514
61779
  // src/flow/types.ts
@@ -62015,7 +62280,7 @@ async function runSchema(args) {
62015
62280
  `;
62016
62281
  const out = optionValue(args, "--out");
62017
62282
  if (out) {
62018
- const target = path149.isAbsolute(out) ? out : path149.join(process.cwd(), out);
62283
+ const target = path150.isAbsolute(out) ? out : path150.join(process.cwd(), out);
62019
62284
  await writeFileAtomic(target, json);
62020
62285
  console.log(` ${style.green(symbols.ok)} Wrote flow-state schema ${style.cyan(symbols.arrow)} ${out}`);
62021
62286
  return;
@@ -62113,8 +62378,8 @@ init_args();
62113
62378
 
62114
62379
  // src/job/service.ts
62115
62380
  init_fs();
62116
- import { copyFile as copyFile5, mkdir as mkdir52 } from "fs/promises";
62117
- import path151 from "path";
62381
+ import { copyFile as copyFile5, mkdir as mkdir53 } from "fs/promises";
62382
+ import path152 from "path";
62118
62383
 
62119
62384
  // src/job/types.ts
62120
62385
  var JOB_INTENTS = ["implement", "analyze", "review", "custom"];
@@ -62252,17 +62517,17 @@ function defaultPlan(intent) {
62252
62517
 
62253
62518
  // src/job/store.ts
62254
62519
  init_validator();
62255
- import { appendFile as appendFile6, mkdir as mkdir51, readFile as readFile85, readdir as readdir27 } from "fs/promises";
62256
- import path150 from "path";
62520
+ import { appendFile as appendFile6, mkdir as mkdir52, readFile as readFile85, readdir as readdir27 } from "fs/promises";
62521
+ import path151 from "path";
62257
62522
  init_fs();
62258
62523
  function jobsRoot(cwd) {
62259
- return path150.join(cwd, ".metaproject", "jobs");
62524
+ return path151.join(cwd, ".metaproject", "jobs");
62260
62525
  }
62261
62526
  function jobDir(cwd, name) {
62262
- return path150.join(jobsRoot(cwd), name);
62527
+ return path151.join(jobsRoot(cwd), name);
62263
62528
  }
62264
62529
  function jobStatePath(cwd, name) {
62265
- return path150.join(jobDir(cwd, name), "state.json");
62530
+ return path151.join(jobDir(cwd, name), "state.json");
62266
62531
  }
62267
62532
  var JOB_NAME_PATTERN = /^[a-z0-9-]+$/;
62268
62533
  function assertJobName(name) {
@@ -62299,12 +62564,12 @@ async function writeJob(cwd, name, state) {
62299
62564
  `));
62300
62565
  }
62301
62566
  const file = jobStatePath(cwd, name);
62302
- await mkdir51(path150.dirname(file), { recursive: true });
62567
+ await mkdir52(path151.dirname(file), { recursive: true });
62303
62568
  await writeFileAtomic(file, `${JSON.stringify(state, null, 2)}
62304
62569
  `);
62305
62570
  }
62306
62571
  async function appendJournal2(cwd, name, at, line) {
62307
- await appendFile6(path150.join(jobDir(cwd, name), "journal.md"), `- ${at} - ${line}
62572
+ await appendFile6(path151.join(jobDir(cwd, name), "journal.md"), `- ${at} - ${line}
62308
62573
  `, "utf8");
62309
62574
  }
62310
62575
  function renderJournal2(name, at) {
@@ -62325,7 +62590,7 @@ function assertDocumentType(raw) {
62325
62590
  return raw;
62326
62591
  }
62327
62592
  function documentFileName(type, sourcePath) {
62328
- const extension = path150.extname(sourcePath) || ".md";
62593
+ const extension = path151.extname(sourcePath) || ".md";
62329
62594
  return `${type}${extension}`;
62330
62595
  }
62331
62596
 
@@ -62333,7 +62598,7 @@ function documentFileName(type, sourcePath) {
62333
62598
  function createJobService(deps) {
62334
62599
  const now = () => deps.now().toISOString();
62335
62600
  function lockPath(cwd, name) {
62336
- return path151.join(jobsRoot(cwd), `.job-lock-${name}`);
62601
+ return path152.join(jobsRoot(cwd), `.job-lock-${name}`);
62337
62602
  }
62338
62603
  async function mutate(cwd, name, run) {
62339
62604
  assertJobName(name);
@@ -62371,7 +62636,7 @@ function createJobService(deps) {
62371
62636
  if (await pathExists(absolute)) {
62372
62637
  throw new Error(`Job package already exists: .metaproject/jobs/${name}. ` + `Run: keryx job status ${name}`);
62373
62638
  }
62374
- await mkdir52(absolute, { recursive: true });
62639
+ await mkdir53(absolute, { recursive: true });
62375
62640
  const createdAt = now();
62376
62641
  const state = {
62377
62642
  phase: "PLAN",
@@ -62382,20 +62647,20 @@ function createJobService(deps) {
62382
62647
  },
62383
62648
  plan: { steps: defaultPlan(intent) },
62384
62649
  documentation: {
62385
- job_path: path151.join(".metaproject", "jobs", name),
62650
+ job_path: path152.join(".metaproject", "jobs", name),
62386
62651
  documents_created: []
62387
62652
  },
62388
62653
  metrics: { steps: [] },
62389
- jobs_root: path151.join(".metaproject", "jobs"),
62654
+ jobs_root: path152.join(".metaproject", "jobs"),
62390
62655
  updated_at: createdAt
62391
62656
  };
62392
62657
  const first = firstOpenStep(state.plan.steps);
62393
62658
  if (first) {
62394
62659
  state.plan.current_step = first.id;
62395
62660
  }
62396
- await writeFileAtomic(path151.join(absolute, "journal.md"), renderJournal2(name, createdAt));
62661
+ await writeFileAtomic(path152.join(absolute, "journal.md"), renderJournal2(name, createdAt));
62397
62662
  await writeJob(input.cwd, name, state);
62398
- return { state, dir: path151.relative(input.cwd, absolute) };
62663
+ return { state, dir: path152.relative(input.cwd, absolute) };
62399
62664
  },
62400
62665
  async status({ cwd, name }) {
62401
62666
  assertJobName(name);
@@ -62407,7 +62672,7 @@ function createJobService(deps) {
62407
62672
  }
62408
62673
  return {
62409
62674
  state,
62410
- dir: path151.join(".metaproject", "jobs", name),
62675
+ dir: path152.join(".metaproject", "jobs", name),
62411
62676
  stepsDone: state.plan.steps.filter((step) => isTerminalStep(step.status)).length,
62412
62677
  stepsTotal: state.plan.steps.length,
62413
62678
  nextStep: firstOpenStep(state.plan.steps),
@@ -62464,23 +62729,23 @@ function createJobService(deps) {
62464
62729
  if (!input.file) {
62465
62730
  throw new Error("Missing --file. Usage: keryx job document <name> --type <type> --file <path>");
62466
62731
  }
62467
- const source = path151.isAbsolute(input.file) ? input.file : path151.join(input.cwd, input.file);
62732
+ const source = path152.isAbsolute(input.file) ? input.file : path152.join(input.cwd, input.file);
62468
62733
  if (!await pathExists(source)) {
62469
62734
  throw new Error(`--file not found: ${input.file}. Write the document first, then record it.`);
62470
62735
  }
62471
62736
  let recorded;
62472
62737
  await mutate(input.cwd, input.name, async (state) => {
62473
62738
  const fileName = documentFileName(type, source);
62474
- const target = path151.join(jobDir(input.cwd, state.job_name), fileName);
62739
+ const target = path152.join(jobDir(input.cwd, state.job_name), fileName);
62475
62740
  await copyFile5(source, target);
62476
62741
  state.documentation ??= {};
62477
- state.documentation.job_path ??= path151.join(".metaproject", "jobs", state.job_name);
62742
+ state.documentation.job_path ??= path152.join(".metaproject", "jobs", state.job_name);
62478
62743
  const created = state.documentation.documents_created ?? [];
62479
62744
  state.documentation.documents_created = created.includes(fileName) ? created : [...created, fileName];
62480
62745
  recorded = {
62481
62746
  state,
62482
62747
  document: fileName,
62483
- path: path151.join(".metaproject", "jobs", state.job_name, fileName)
62748
+ path: path152.join(".metaproject", "jobs", state.job_name, fileName)
62484
62749
  };
62485
62750
  return { state, event: "document", detail: `${type} -> ${fileName}` };
62486
62751
  });
@@ -62515,7 +62780,7 @@ function createJobService(deps) {
62515
62780
  name: state.job_name,
62516
62781
  phase: state.phase,
62517
62782
  intent: state.intent,
62518
- dir: path151.join(".metaproject", "jobs", name),
62783
+ dir: path152.join(".metaproject", "jobs", name),
62519
62784
  stepsDone: state.plan.steps.filter((step) => isTerminalStep(step.status)).length,
62520
62785
  stepsTotal: state.plan.steps.length,
62521
62786
  nextStep: firstOpenStep(state.plan.steps)?.id ?? null
@@ -62724,17 +62989,17 @@ function printHelp11() {
62724
62989
  // src/commands/review.ts
62725
62990
  init_args();
62726
62991
  init_fs();
62727
- import { mkdir as mkdir54, readdir as readdir30, readFile as readFile88 } from "fs/promises";
62728
- import path155, { join as join3 } from "path";
62992
+ import { mkdir as mkdir55, readdir as readdir30, readFile as readFile88 } from "fs/promises";
62993
+ import path156, { join as join3 } from "path";
62729
62994
 
62730
62995
  // src/review/review-learning.ts
62731
62996
  init_fs();
62732
62997
  init_json();
62733
62998
  init_pr_comments();
62734
- import path152 from "path";
62999
+ import path153 from "path";
62735
63000
  var REVIEW_LEARNING_CONFIG_SCHEMA_VERSION = 1;
62736
63001
  function reviewLearningConfigPath(cwd) {
62737
- return path152.join(cwd, ".metaproject", "review-learning.config.json");
63002
+ return path153.join(cwd, ".metaproject", "review-learning.config.json");
62738
63003
  }
62739
63004
  async function loadReviewLearningConfig(cwd) {
62740
63005
  const file = reviewLearningConfigPath(cwd);
@@ -62742,7 +63007,7 @@ async function loadReviewLearningConfig(cwd) {
62742
63007
  return null;
62743
63008
  }
62744
63009
  const raw = await readJsonFile(file);
62745
- const where = path152.relative(cwd, file);
63010
+ const where = path153.relative(cwd, file);
62746
63011
  if (raw.schemaVersion !== REVIEW_LEARNING_CONFIG_SCHEMA_VERSION) {
62747
63012
  throw new Error(`${where}: schemaVersion must be ${REVIEW_LEARNING_CONFIG_SCHEMA_VERSION}, got ${JSON.stringify(raw.schemaVersion)}.`);
62748
63013
  }
@@ -62784,10 +63049,10 @@ function selectLearnableComments(state, authors) {
62784
63049
  return { kept, unconfigured, bodyless };
62785
63050
  }
62786
63051
  function learningSourcePath(cwd, repo, number) {
62787
- return path152.join(cwd, ".metaproject", "data", "gdskills", "learning-sources", `${slug3(repo)}__${number}.json`);
63052
+ return path153.join(cwd, ".metaproject", "data", "gdskills", "learning-sources", `${slug3(repo)}__${number}.json`);
62788
63053
  }
62789
63054
  function learningRecordPath(cwd, repo, number) {
62790
- return path152.join(cwd, ".metaproject", "data", "gdskills", "learning-sources", `${slug3(repo)}__${number}.md`);
63055
+ return path153.join(cwd, ".metaproject", "data", "gdskills", "learning-sources", `${slug3(repo)}__${number}.md`);
62791
63056
  }
62792
63057
  function slug3(repo) {
62793
63058
  return repo.replace(/\//g, "__");
@@ -62849,8 +63114,8 @@ ${learningSourceLessons(selection).map((lesson) => `- ${lesson}
62849
63114
 
62850
63115
  // src/review/managed.ts
62851
63116
  init_validator();
62852
- import { mkdir as mkdir53, readFile as readFile86, readdir as readdir28, realpath as realpath8, rm as rm11, stat as stat16 } from "fs/promises";
62853
- import path153 from "path";
63117
+ import { mkdir as mkdir54, readFile as readFile86, readdir as readdir28, realpath as realpath8, rm as rm11, stat as stat16 } from "fs/promises";
63118
+ import path154 from "path";
62854
63119
  init_fs();
62855
63120
  init_store2();
62856
63121
 
@@ -64962,7 +65227,7 @@ init_review_notes();
64962
65227
  init_types3();
64963
65228
  var REQUIRED_ARTIFACTS = ["scope", "coverage", "report", "findings", "learning", "decisions"];
64964
65229
  function reviewsRoot(cwd) {
64965
- return path153.join(cwd, ".metaproject", "reviews");
65230
+ return path154.join(cwd, ".metaproject", "reviews");
64966
65231
  }
64967
65232
  async function resolveGitHead2(cwd) {
64968
65233
  try {
@@ -65095,27 +65360,27 @@ async function createManagedReviewPackage(input) {
65095
65360
  if (contractErrors.length > 0) {
65096
65361
  throw new Error(`Refusing to record findings that do not satisfy review-finding.schema.json: ${contractErrors.map((error) => `${error.path} ${error.message}`).join("; ")}`);
65097
65362
  }
65098
- await mkdir53(packageDir, { recursive: true });
65099
- await writeFileAtomic(path153.join(packageDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
65363
+ await mkdir54(packageDir, { recursive: true });
65364
+ await writeFileAtomic(path154.join(packageDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
65100
65365
  `);
65101
- await writeFileAtomic(path153.join(packageDir, "scope.md"), renderScope(input, flowMatch, at, verification, carried, caps, externalReclaims, scopeB.record, filterStats));
65102
- await writeFileAtomic(path153.join(packageDir, "coverage.md"), renderCoverage(coverage));
65103
- await writeFileAtomic(path153.join(packageDir, "report.md"), renderReport(report, input.mode));
65104
- await writeFileAtomic(path153.join(packageDir, "findings.json"), `${JSON.stringify(findings.map(toContractFinding), null, 2)}
65366
+ await writeFileAtomic(path154.join(packageDir, "scope.md"), renderScope(input, flowMatch, at, verification, carried, caps, externalReclaims, scopeB.record, filterStats));
65367
+ await writeFileAtomic(path154.join(packageDir, "coverage.md"), renderCoverage(coverage));
65368
+ await writeFileAtomic(path154.join(packageDir, "report.md"), renderReport(report, input.mode));
65369
+ await writeFileAtomic(path154.join(packageDir, "findings.json"), `${JSON.stringify(findings.map(toContractFinding), null, 2)}
65105
65370
  `);
65106
- await writeFileAtomic(path153.join(packageDir, "learning.md"), renderLearning(findings));
65107
- await writeFileAtomic(path153.join(packageDir, "decisions.md"), renderDecisions(findings));
65371
+ await writeFileAtomic(path154.join(packageDir, "learning.md"), renderLearning(findings));
65372
+ await writeFileAtomic(path154.join(packageDir, "decisions.md"), renderDecisions(findings));
65108
65373
  await persistScreenRadius(packageDir, scopeB.resolved);
65109
65374
  const reviewNotes = await writeReviewNotes(findings, {
65110
65375
  cwd: input.cwd,
65111
65376
  reviewId,
65112
65377
  head,
65113
- packagePath: path153.relative(input.cwd, packageDir),
65378
+ packagePath: path154.relative(input.cwd, packageDir),
65114
65379
  ...input.now === undefined ? {} : { now: input.now }
65115
65380
  });
65116
65381
  return {
65117
65382
  reviewId,
65118
- path: path153.relative(input.cwd, packageDir),
65383
+ path: path154.relative(input.cwd, packageDir),
65119
65384
  manifest,
65120
65385
  verification: verification.counts,
65121
65386
  verificationRejections: verification.rejections,
@@ -65132,7 +65397,7 @@ async function screenScopeBFindings(input, packageDir, findings) {
65132
65397
  const scopeB = findings.filter((finding) => isBlastRadiusScopedFinding(finding, reviewers));
65133
65398
  if (resolved === undefined) {
65134
65399
  if (scopeB.length > 0) {
65135
- throw new Error(`Refusing to record ${scopeB.length} finding(s) raised under scope B (${scopeB.map((finding) => `${finding.id} by ${finding.reviewer}`).join(", ")}) with no blast-radius record: AC3 is enforced in code, and the screen cannot be applied without the computed set the round was dispatched over. Two ways out, and the first is the one that always works: (1) compute the set with \`keryx review blast-radius --json > <file>\` and pass \`--blast-radius <file>\` to this ingest; (2) write that record to \`${path153.relative(input.cwd, handoffBlastRadiusPath(packageDir))}\` and ingest again \u2014 the ingest reads it from there and consumes it. Do NOT write it to \`${path153.relative(input.cwd, path153.join(packageDir, BLAST_RADIUS_ARTIFACT))}\`: a round that names no --review-id has not allocated that directory yet, so creating it makes the next ingest take the NEXT free name and find nothing there either.`);
65400
+ throw new Error(`Refusing to record ${scopeB.length} finding(s) raised under scope B (${scopeB.map((finding) => `${finding.id} by ${finding.reviewer}`).join(", ")}) with no blast-radius record: AC3 is enforced in code, and the screen cannot be applied without the computed set the round was dispatched over. Two ways out, and the first is the one that always works: (1) compute the set with \`keryx review blast-radius --json > <file>\` and pass \`--blast-radius <file>\` to this ingest; (2) write that record to \`${path154.relative(input.cwd, handoffBlastRadiusPath(packageDir))}\` and ingest again \u2014 the ingest reads it from there and consumes it. Do NOT write it to \`${path154.relative(input.cwd, path154.join(packageDir, BLAST_RADIUS_ARTIFACT))}\`: a round that names no --review-id has not allocated that directory yet, so creating it makes the next ingest take the NEXT free name and find nothing there either.`);
65136
65401
  }
65137
65402
  return { retained: [...findings], record: { source: "none", scopeBFindings: 0 }, resolved: undefined };
65138
65403
  }
@@ -65149,13 +65414,13 @@ async function screenScopeBFindings(input, packageDir, findings) {
65149
65414
  };
65150
65415
  }
65151
65416
  function handoffBlastRadiusPath(packageDir) {
65152
- return path153.join(path153.dirname(packageDir), BLAST_RADIUS_ARTIFACT);
65417
+ return path154.join(path154.dirname(packageDir), BLAST_RADIUS_ARTIFACT);
65153
65418
  }
65154
65419
  async function resolveScreenRadius(input, packageDir) {
65155
65420
  if (input.blastRadius !== undefined) {
65156
65421
  return { radius: input.blastRadius, source: "input" };
65157
65422
  }
65158
- const inPackage = path153.join(packageDir, BLAST_RADIUS_ARTIFACT);
65423
+ const inPackage = path154.join(packageDir, BLAST_RADIUS_ARTIFACT);
65159
65424
  const fromPackage = await readBlastRadiusRecordFile(inPackage);
65160
65425
  if (fromPackage !== undefined) {
65161
65426
  return { radius: fromPackage, source: "package", file: inPackage };
@@ -65181,7 +65446,7 @@ async function persistScreenRadius(packageDir, resolved) {
65181
65446
  if (resolved === undefined) {
65182
65447
  return;
65183
65448
  }
65184
- const inPackage = path153.join(packageDir, BLAST_RADIUS_ARTIFACT);
65449
+ const inPackage = path154.join(packageDir, BLAST_RADIUS_ARTIFACT);
65185
65450
  if (resolved.source !== "package") {
65186
65451
  await writeFileAtomic(inPackage, `${JSON.stringify(resolved.radius, null, 2)}
65187
65452
  `);
@@ -65212,12 +65477,12 @@ function fromVerifierRefutations(findings) {
65212
65477
  }));
65213
65478
  }
65214
65479
  async function getManagedReviewStatus(cwd, ref) {
65215
- const manifestPath = ref.endsWith("manifest.json") ? path153.resolve(cwd, ref) : path153.join(await resolveReviewPackagePath(cwd, ref), "manifest.json");
65480
+ const manifestPath = ref.endsWith("manifest.json") ? path154.resolve(cwd, ref) : path154.join(await resolveReviewPackagePath(cwd, ref), "manifest.json");
65216
65481
  return JSON.parse(await readFile86(manifestPath, "utf8"));
65217
65482
  }
65218
65483
  async function completeManagedReview(cwd, ref, options = {}) {
65219
65484
  const packageDir = await resolveReviewPackagePath(cwd, ref);
65220
- const manifestPath = path153.join(packageDir, "manifest.json");
65485
+ const manifestPath = path154.join(packageDir, "manifest.json");
65221
65486
  const manifest = JSON.parse(await readFile86(manifestPath, "utf8"));
65222
65487
  const missing = await missingArtifacts(packageDir);
65223
65488
  if (missing.length > 0) {
@@ -65238,12 +65503,12 @@ async function completeManagedReview(cwd, ref, options = {}) {
65238
65503
  }
65239
65504
  await writeFileAtomic(manifestPath, `${JSON.stringify(updated, null, 2)}
65240
65505
  `);
65241
- const recorded = JSON.parse(await readFile86(path153.join(packageDir, "findings.json"), "utf8"));
65506
+ const recorded = JSON.parse(await readFile86(path154.join(packageDir, "findings.json"), "utf8"));
65242
65507
  const reviewNotes = await writeReviewNotes(recorded, {
65243
65508
  cwd,
65244
65509
  reviewId: updated.reviewId,
65245
65510
  head: updated.target.head ?? null,
65246
- packagePath: path153.relative(cwd, packageDir),
65511
+ packagePath: path154.relative(cwd, packageDir),
65247
65512
  ...options.now === undefined ? {} : { now: options.now }
65248
65513
  });
65249
65514
  return { manifest: updated, reviewNotes, dispositions: tallyDispositions(recorded) };
@@ -65309,16 +65574,16 @@ async function resolvePackageFlow(input) {
65309
65574
  }
65310
65575
  function packagePath(cwd, mode, reviewId, flowMatch) {
65311
65576
  if ((mode === "attach-review" || mode === "ingest") && flowMatch) {
65312
- return path153.join(flowsRoot(cwd), flowMatch.dir, "reviews", reviewId);
65577
+ return path154.join(flowsRoot(cwd), flowMatch.dir, "reviews", reviewId);
65313
65578
  }
65314
65579
  if (mode === "attach-review") {
65315
65580
  throw new Error("attach-review requires an explicit or matched flow");
65316
65581
  }
65317
- return path153.join(reviewsRoot(cwd), reviewId);
65582
+ return path154.join(reviewsRoot(cwd), reviewId);
65318
65583
  }
65319
65584
  function buildManifest2(args) {
65320
65585
  const crossFamily = args.input.crossFamilyReview;
65321
- const artifactPath = (name) => path153.relative(args.input.cwd, path153.join(args.packageDir, name));
65586
+ const artifactPath = (name) => path154.relative(args.input.cwd, path154.join(args.packageDir, name));
65322
65587
  const manifest = {
65323
65588
  schemaVersion: 1,
65324
65589
  reviewId: args.reviewId,
@@ -65365,7 +65630,7 @@ async function readReport(input) {
65365
65630
  return input.reportText;
65366
65631
  }
65367
65632
  if (input.reportPath) {
65368
- return readFile86(path153.resolve(input.cwd, input.reportPath), "utf8");
65633
+ return readFile86(path154.resolve(input.cwd, input.reportPath), "utf8");
65369
65634
  }
65370
65635
  if (input.mode === "ingest") {
65371
65636
  throw new Error("ingest requires --report or reportText");
@@ -65376,7 +65641,7 @@ No reviewer findings recorded yet.
65376
65641
  `;
65377
65642
  }
65378
65643
  function reportLabel(input) {
65379
- return input.reportPath ? path153.resolve(input.cwd, input.reportPath) : "the report text passed to keryx review";
65644
+ return input.reportPath ? path154.resolve(input.cwd, input.reportPath) : "the report text passed to keryx review";
65380
65645
  }
65381
65646
  async function normalizeFindings(args) {
65382
65647
  if (args.source !== undefined) {
@@ -65431,16 +65696,16 @@ async function anchorFindings(findings, input, head) {
65431
65696
  }
65432
65697
  }
65433
65698
  async function containedRelativePath(cwd, relative) {
65434
- const root = await realpath8(cwd).catch(() => path153.resolve(cwd));
65435
- const resolved = path153.resolve(root, relative);
65699
+ const root = await realpath8(cwd).catch(() => path154.resolve(cwd));
65700
+ const resolved = path154.resolve(root, relative);
65436
65701
  const real = await realpath8(resolved).catch(() => null);
65437
- if (real === null || real !== root && !real.startsWith(`${root}${path153.sep}`)) {
65702
+ if (real === null || real !== root && !real.startsWith(`${root}${path154.sep}`)) {
65438
65703
  return null;
65439
65704
  }
65440
65705
  return real;
65441
65706
  }
65442
65707
  async function readFileAtCommit(cwd, commit, absolute) {
65443
- const relative = path153.relative(await realpath8(cwd).catch(() => path153.resolve(cwd)), absolute);
65708
+ const relative = path154.relative(await realpath8(cwd).catch(() => path154.resolve(cwd)), absolute);
65444
65709
  try {
65445
65710
  const proc = Bun.spawn(["git", "show", `${commit}:${relative}`], {
65446
65711
  cwd,
@@ -65459,10 +65724,10 @@ async function readFileAtCommit(cwd, commit, absolute) {
65459
65724
  }
65460
65725
  function readFromWorkingTree(input) {
65461
65726
  return async (relative) => {
65462
- const root = await realpath8(input.cwd).catch(() => path153.resolve(input.cwd));
65463
- const resolved = path153.resolve(root, relative);
65727
+ const root = await realpath8(input.cwd).catch(() => path154.resolve(input.cwd));
65728
+ const resolved = path154.resolve(root, relative);
65464
65729
  const real = await realpath8(resolved).catch(() => null);
65465
- if (real === null || real !== root && !real.startsWith(`${root}${path153.sep}`)) {
65730
+ if (real === null || real !== root && !real.startsWith(`${root}${path154.sep}`)) {
65466
65731
  return null;
65467
65732
  }
65468
65733
  try {
@@ -65594,7 +65859,7 @@ function refutedClassification(state) {
65594
65859
  return state === "dismissed-incorrect" ? "false_positive" : "out_of_scope";
65595
65860
  }
65596
65861
  async function recordDispositions(packageDir, records) {
65597
- const findingsPath = path153.join(packageDir, "findings.json");
65862
+ const findingsPath = path154.join(packageDir, "findings.json");
65598
65863
  const findings = JSON.parse(await readFile86(findingsPath, "utf8"));
65599
65864
  const schema = await loadSchema("review-finding");
65600
65865
  const dispositionSchema = schema.properties?.["disposition"];
@@ -65930,7 +66195,7 @@ function upsertPreFilterScopeBlock(text, block) {
65930
66195
  ${body}`;
65931
66196
  }
65932
66197
  async function readPreFilterScopeBlock(packageDir) {
65933
- const scopePath = path153.join(packageDir, "scope.md");
66198
+ const scopePath = path154.join(packageDir, "scope.md");
65934
66199
  if (!await pathExists(scopePath)) {
65935
66200
  return;
65936
66201
  }
@@ -66111,39 +66376,39 @@ function matchReason(flow, dir, kind, ref) {
66111
66376
  async function missingArtifacts(packageDir) {
66112
66377
  const missing = [];
66113
66378
  for (const artifact of ["scope.md", "coverage.md", "report.md", "findings.json", "learning.md", "decisions.md"]) {
66114
- if (!await pathExists(path153.join(packageDir, artifact))) {
66379
+ if (!await pathExists(path154.join(packageDir, artifact))) {
66115
66380
  missing.push(artifact);
66116
66381
  }
66117
66382
  }
66118
66383
  return missing;
66119
66384
  }
66120
66385
  async function resolveReviewPackagePath(cwd, ref) {
66121
- const absolute = path153.resolve(cwd, ref);
66122
- if (await pathExists(path153.join(absolute, "manifest.json"))) {
66386
+ const absolute = path154.resolve(cwd, ref);
66387
+ if (await pathExists(path154.join(absolute, "manifest.json"))) {
66123
66388
  return absolute;
66124
66389
  }
66125
66390
  if (await pathExists(absolute) && absolute.endsWith("manifest.json")) {
66126
- return path153.dirname(absolute);
66391
+ return path154.dirname(absolute);
66127
66392
  }
66128
- const standalone = path153.join(reviewsRoot(cwd), ref);
66129
- if (await pathExists(path153.join(standalone, "manifest.json"))) {
66393
+ const standalone = path154.join(reviewsRoot(cwd), ref);
66394
+ if (await pathExists(path154.join(standalone, "manifest.json"))) {
66130
66395
  return standalone;
66131
66396
  }
66132
66397
  for (const flowDir of await listFlowDirs(cwd)) {
66133
- const reviewsDir = path153.join(flowsRoot(cwd), flowDir, "reviews");
66398
+ const reviewsDir = path154.join(flowsRoot(cwd), flowDir, "reviews");
66134
66399
  if (!await pathExists(reviewsDir)) {
66135
66400
  continue;
66136
66401
  }
66137
66402
  for (const entry of await readdir28(reviewsDir, { withFileTypes: true })) {
66138
66403
  if (entry.isDirectory() && entry.name === ref) {
66139
- return path153.join(reviewsDir, entry.name);
66404
+ return path154.join(reviewsDir, entry.name);
66140
66405
  }
66141
66406
  }
66142
66407
  }
66143
66408
  throw new Error(`Managed review package not found: ${ref}`);
66144
66409
  }
66145
66410
  async function loadDocpackSchema(cwd) {
66146
- const schemaPath = path153.join(cwd, "docs", "requirements", "managed-review-feedback-loop", "schemas", "managed-review-package.schema.json");
66411
+ const schemaPath = path154.join(cwd, "docs", "requirements", "managed-review-feedback-loop", "schemas", "managed-review-package.schema.json");
66147
66412
  if (!await pathExists(schemaPath)) {
66148
66413
  return null;
66149
66414
  }
@@ -66221,7 +66486,190 @@ function renderCostPerFinding(cost, retained) {
66221
66486
  // src/review/reviewers.ts
66222
66487
  init_project_skills();
66223
66488
  import { readFile as readFile87, readdir as readdir29 } from "fs/promises";
66224
- import path154 from "path";
66489
+ import path155 from "path";
66490
+ // src/review/stack.ts
66491
+ var STACK_TAGS = ["nestjs", "react", "mobx", "prisma", "playwright", "sql", "http-server"];
66492
+ function isStackTag(value) {
66493
+ return STACK_TAGS.includes(value);
66494
+ }
66495
+ var TAG_MARKERS = {
66496
+ react: ["react", "react-dom"],
66497
+ mobx: ["mobx", "mobx-react", "mobx-react-lite"],
66498
+ prisma: ["prisma", "@prisma/client"],
66499
+ playwright: ["@playwright/test", "playwright"],
66500
+ sql: ["prisma", "@prisma/client", "typeorm", "sequelize", "knex", "drizzle-orm", "pg", "mysql2", "better-sqlite3", "kysely"],
66501
+ "http-server": ["express", "fastify", "koa", "hono", "@nestjs/core", "@hapi/hapi"]
66502
+ };
66503
+ var DEPENDENCY_FIELDS = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
66504
+ async function detectProjectStack(cwd, options = {}) {
66505
+ const readFile = options.readFile ?? defaultReadFile;
66506
+ const manifestPath = `${cwd.replace(/\/+$/, "")}/package.json`;
66507
+ let raw;
66508
+ try {
66509
+ raw = await readFile(manifestPath);
66510
+ } catch {
66511
+ return uncertainStack(`package.json not found at ${manifestPath}`);
66512
+ }
66513
+ let parsed;
66514
+ try {
66515
+ parsed = JSON.parse(raw);
66516
+ } catch (error) {
66517
+ return uncertainStack(`package.json at ${manifestPath} did not parse as JSON: ${error instanceof Error ? error.message : String(error)}`);
66518
+ }
66519
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
66520
+ return uncertainStack(`package.json at ${manifestPath} is not a JSON object`);
66521
+ }
66522
+ const record = parsed;
66523
+ const names = new Set;
66524
+ for (const field of DEPENDENCY_FIELDS) {
66525
+ const value = record[field];
66526
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
66527
+ for (const name of Object.keys(value)) {
66528
+ names.add(name);
66529
+ }
66530
+ }
66531
+ }
66532
+ const workspaceGlobs = workspacePatterns(record);
66533
+ if (workspaceGlobs.length > 0) {
66534
+ return uncertainStack(`package.json at ${manifestPath} declares workspaces (${workspaceGlobs.join(", ")}); the dependencies that decide the stack live in the sub-packages, which this module does not walk`);
66535
+ }
66536
+ if (names.size === 0) {
66537
+ return uncertainStack(`package.json at ${manifestPath} declares no dependencies in ${DEPENDENCY_FIELDS.join("/")} \u2014 a manifest that declares nothing has not said the repository uses nothing`);
66538
+ }
66539
+ const matched = [];
66540
+ const nestjsMatches = [...names].filter((name) => name.startsWith("@nestjs/")).sort();
66541
+ matched.push(...nestjsMatches);
66542
+ const tags = { nestjs: nestjsMatches.length > 0 };
66543
+ for (const tag of Object.keys(TAG_MARKERS)) {
66544
+ const hit = TAG_MARKERS[tag].filter((marker) => names.has(marker));
66545
+ tags[tag] = hit.length > 0;
66546
+ matched.push(...hit);
66547
+ }
66548
+ return {
66549
+ tags,
66550
+ uncertain: false,
66551
+ reason: `detected from ${manifestPath} (${names.size} declared dependenc${names.size === 1 ? "y" : "ies"})`,
66552
+ matched
66553
+ };
66554
+ }
66555
+ function workspacePatterns(record) {
66556
+ const declared = record["workspaces"];
66557
+ const list = Array.isArray(declared) ? declared : typeof declared === "object" && declared !== null ? declared["packages"] : undefined;
66558
+ return Array.isArray(list) ? list.filter((item) => typeof item === "string" && item !== "") : [];
66559
+ }
66560
+ function uncertainStack(reason) {
66561
+ const tags = Object.fromEntries(STACK_TAGS.map((tag) => [tag, true]));
66562
+ return { tags, uncertain: true, reason, matched: [] };
66563
+ }
66564
+ async function defaultReadFile(path) {
66565
+ return await Bun.file(path).text();
66566
+ }
66567
+ function parseStackRequires(value) {
66568
+ if (value === undefined) {
66569
+ return [];
66570
+ }
66571
+ const seen = new Set;
66572
+ for (const token of value.split(",")) {
66573
+ const trimmed = token.trim().toLowerCase();
66574
+ if (isStackTag(trimmed)) {
66575
+ seen.add(trimmed);
66576
+ }
66577
+ }
66578
+ return [...seen];
66579
+ }
66580
+ function extractStackRequiresField(skillMdContent) {
66581
+ if (!skillMdContent.startsWith("---")) {
66582
+ return;
66583
+ }
66584
+ const end = skillMdContent.indexOf(`
66585
+ ---`, 3);
66586
+ if (end === -1) {
66587
+ return;
66588
+ }
66589
+ const lines = skillMdContent.slice(3, end).split(`
66590
+ `);
66591
+ let inMetadata = false;
66592
+ for (const line of lines) {
66593
+ const topMatch = /^([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$/.exec(line);
66594
+ if (topMatch) {
66595
+ inMetadata = topMatch[1] === "metadata";
66596
+ continue;
66597
+ }
66598
+ if (!inMetadata) {
66599
+ continue;
66600
+ }
66601
+ const fieldMatch = /^\s+stack_requires:\s*(.+)$/.exec(line);
66602
+ if (fieldMatch && fieldMatch[1] !== undefined) {
66603
+ return stripQuotes(fieldMatch[1].trim());
66604
+ }
66605
+ }
66606
+ return;
66607
+ }
66608
+ function stripQuotes(value) {
66609
+ if (value.length >= 2) {
66610
+ const first = value[0];
66611
+ const last = value[value.length - 1];
66612
+ if (first === '"' && last === '"' || first === "'" && last === "'") {
66613
+ return value.slice(1, -1);
66614
+ }
66615
+ }
66616
+ return value;
66617
+ }
66618
+ function scopeReviewerByStack(reviewer, requires, detected) {
66619
+ const requiresCopy = [...requires];
66620
+ if (requiresCopy.length === 0) {
66621
+ return { reviewer, requires: requiresCopy, include: true, reason: "no stack requirement declared \u2014 generic reviewer" };
66622
+ }
66623
+ if (detected.uncertain) {
66624
+ return {
66625
+ reviewer,
66626
+ requires: requiresCopy,
66627
+ include: true,
66628
+ reason: `stack detection uncertain (${detected.reason}); failing open`
66629
+ };
66630
+ }
66631
+ const present = requiresCopy.filter((tag) => detected.tags[tag]);
66632
+ if (present.length > 0) {
66633
+ return { reviewer, requires: requiresCopy, include: true, reason: `detected: ${present.join(", ")}` };
66634
+ }
66635
+ return {
66636
+ reviewer,
66637
+ requires: requiresCopy,
66638
+ include: false,
66639
+ reason: `none of [${requiresCopy.join(", ")}] detected in package.json (${detected.reason})`
66640
+ };
66641
+ }
66642
+ function renderStackScopingMarkdown(detected, decisions) {
66643
+ const lines = [];
66644
+ lines.push("## Stack scoping");
66645
+ lines.push("");
66646
+ lines.push(detected.uncertain ? `stack detection: uncertain \u2014 ${detected.reason}. Every stack-gated reviewer is included; uncertain never excludes.` : `stack detection: ${detected.reason}`);
66647
+ if (!detected.uncertain) {
66648
+ const present = STACK_TAGS.filter((tag) => detected.tags[tag]);
66649
+ lines.push(`tags present: ${present.length > 0 ? present.join(", ") : "none"}`);
66650
+ if (detected.matched.length > 0) {
66651
+ lines.push(`matched dependencies: ${detected.matched.join(", ")}`);
66652
+ }
66653
+ }
66654
+ lines.push("");
66655
+ if (decisions.length === 0) {
66656
+ lines.push("_no stack-gated reviewers were evaluated_");
66657
+ return lines.join(`
66658
+ `);
66659
+ }
66660
+ lines.push("| reviewer | requires | include | reason |");
66661
+ lines.push("|---|---|---|---|");
66662
+ for (const decision of decisions) {
66663
+ lines.push(`| ${decision.reviewer} | ${decision.requires.length > 0 ? decision.requires.join(", ") : "-"} | ${decision.include ? "yes" : "no"} | ${decision.reason} |`);
66664
+ }
66665
+ const excluded = decisions.filter((decision) => !decision.include);
66666
+ lines.push("");
66667
+ lines.push(excluded.length === 0 ? "No reviewer was excluded by stack scoping." : `Excluded (never run this round): ${excluded.map((decision) => decision.reviewer).join(", ")}.`);
66668
+ return lines.join(`
66669
+ `);
66670
+ }
66671
+
66672
+ // src/review/reviewers.ts
66225
66673
  var PROJECT_REVIEWER_MODULE = "review";
66226
66674
  function escapeRegexLiteral(literal) {
66227
66675
  return literal.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -66242,12 +66690,60 @@ async function skillDirs(root) {
66242
66690
  if (!entry.isDirectory())
66243
66691
  continue;
66244
66692
  try {
66245
- await readFile87(path154.join(root, entry.name, "SKILL.md"), "utf8");
66693
+ await readFile87(path155.join(root, entry.name, "SKILL.md"), "utf8");
66246
66694
  names.push(entry.name);
66247
66695
  } catch {}
66248
66696
  }
66249
66697
  return names.sort();
66250
66698
  }
66699
+ function metadataList(content, key) {
66700
+ if (!content.startsWith("---"))
66701
+ return [];
66702
+ const end = content.indexOf(`
66703
+ ---`, 3);
66704
+ if (end === -1)
66705
+ return [];
66706
+ let inMetadata = false;
66707
+ for (const line of content.slice(3, end).split(`
66708
+ `)) {
66709
+ const top = /^([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$/.exec(line);
66710
+ if (top) {
66711
+ inMetadata = top[1] === "metadata";
66712
+ continue;
66713
+ }
66714
+ if (!inMetadata)
66715
+ continue;
66716
+ const field = new RegExp(`^\\s+${escapeRegexLiteral(key)}:\\s*(.+)$`).exec(line);
66717
+ if (field?.[1]) {
66718
+ return field[1].trim().replace(/^["'[]|["'\]]$/g, "").split(",").map((entry) => entry.trim().replace(/^["']|["']$/g, "")).filter(Boolean);
66719
+ }
66720
+ }
66721
+ return [];
66722
+ }
66723
+ function descriptionPathTriggers(description) {
66724
+ const globs = new Set;
66725
+ for (const raw of description.split(/\s+/)) {
66726
+ const token = raw.replace(/^[("'`]+/, "").replace(/[,.;:"'`]+$/, "");
66727
+ if (!token.includes("*") || !(token.includes("/") || token.startsWith("*.")))
66728
+ continue;
66729
+ const optional = /^(.*)\(([a-z0-9]+)\)$/i.exec(token);
66730
+ if (optional?.[1] && optional[2]) {
66731
+ globs.add(optional[1]);
66732
+ globs.add(`${optional[1]}${optional[2]}`);
66733
+ } else {
66734
+ globs.add(token.replace(/\)+$/, ""));
66735
+ }
66736
+ }
66737
+ return [...globs];
66738
+ }
66739
+ function descriptionFlags(description) {
66740
+ const flags = new Set;
66741
+ for (const match of description.matchAll(/(?:^|[\s(,])(--[a-z][a-z0-9-]*)/g)) {
66742
+ if (match[1] && match[1] !== "--all")
66743
+ flags.add(match[1]);
66744
+ }
66745
+ return [...flags];
66746
+ }
66251
66747
  async function driftFor(projectRoot, origin, recordedHash) {
66252
66748
  if (!origin || !recordedHash) {
66253
66749
  return "none";
@@ -66263,24 +66759,34 @@ async function driftFor(projectRoot, origin, recordedHash) {
66263
66759
  }
66264
66760
  }
66265
66761
  async function collectReviewers(projectRoot) {
66266
- const bundledRoot = path154.join(projectRoot, ".metaproject", "skills", "gdskills", "review");
66762
+ const bundledRoot = path155.join(projectRoot, ".metaproject", "skills", "gdskills", "review");
66267
66763
  const bundled = (await skillDirs(bundledRoot)).map((name) => ({
66268
66764
  name,
66269
66765
  source: "bundled",
66270
- path: path154.posix.join(".metaproject", "skills", "gdskills", "review", name)
66766
+ path: path155.posix.join(".metaproject", "skills", "gdskills", "review", name)
66271
66767
  }));
66272
- const projectRoot_ = path154.join(projectRoot, ".metaproject", "project-skills", PROJECT_REVIEWER_MODULE);
66768
+ const projectRoot_ = path155.join(projectRoot, ".metaproject", "project-skills", PROJECT_REVIEWER_MODULE);
66273
66769
  const project = [];
66274
66770
  for (const name of await skillDirs(projectRoot_)) {
66275
- const relative = path154.posix.join(".metaproject", "project-skills", PROJECT_REVIEWER_MODULE, name);
66276
- const content = await readFile87(path154.join(projectRoot_, name, "SKILL.md"), "utf8");
66771
+ const relative = path155.posix.join(".metaproject", "project-skills", PROJECT_REVIEWER_MODULE, name);
66772
+ const content = await readFile87(path155.join(projectRoot_, name, "SKILL.md"), "utf8");
66277
66773
  const origin = metadataLine2(content, "Origin");
66278
66774
  const originHash = metadataLine2(content, "Origin Hash");
66279
66775
  const importedAt = metadataLine2(content, "Imported At");
66776
+ const description = parseSkillFrontmatter(content).description;
66777
+ const declaredPaths = metadataList(content, "paths");
66778
+ const describedPaths = description ? descriptionPathTriggers(description) : [];
66779
+ const pathsSource = declaredPaths.length > 0 ? "metadata" : describedPaths.length > 0 ? "description" : "none";
66280
66780
  project.push({
66281
66781
  name,
66282
66782
  source: "project-skill",
66283
66783
  path: relative,
66784
+ ...description ? { description } : {},
66785
+ paths: pathsSource === "metadata" ? declaredPaths : describedPaths,
66786
+ pathsSource,
66787
+ flags: description ? descriptionFlags(description) : [],
66788
+ stackRequires: parseStackRequires(extractStackRequiresField(content)),
66789
+ unresolvedRules: await unresolvedRuleReferences(projectRoot, content),
66284
66790
  ...origin ? { origin } : {},
66285
66791
  ...originHash ? { originHash } : {},
66286
66792
  ...importedAt ? { importedAt } : {},
@@ -66312,6 +66818,18 @@ function renderReviewerInventoryMarkdown(inventory) {
66312
66818
  for (const reviewer of inventory.project) {
66313
66819
  const provenance = reviewer.drift === "none" ? "no recorded origin" : `${reviewer.origin ?? "?"} \u2014 ${reviewer.drift}`;
66314
66820
  lines.push(`- ${reviewer.name} (${provenance})`);
66821
+ lines.push(` - paths: ${reviewer.paths.length > 0 ? reviewer.paths.join(", ") : "none \u2014 dispatched on every round"} [${reviewer.pathsSource}]`);
66822
+ if (reviewer.flags.length > 0) {
66823
+ lines.push(` - flags: ${reviewer.flags.join(", ")}`);
66824
+ }
66825
+ }
66826
+ const unresolved = inventory.project.filter((reviewer) => reviewer.unresolvedRules.length > 0);
66827
+ if (unresolved.length > 0) {
66828
+ lines.push("", "## rules cited but not in .metaproject/rules", "");
66829
+ for (const reviewer of unresolved) {
66830
+ lines.push(`- ${reviewer.name}: ${reviewer.unresolvedRules.join(", ")}`);
66831
+ }
66832
+ lines.push("", "The reviewer names these as its standard and the project does not have them. Re-run", "`keryx review import --from <overlay>` to copy them from the overlay, or add them by hand.");
66315
66833
  }
66316
66834
  const changed = inventory.project.filter((reviewer) => reviewer.drift === "changed");
66317
66835
  const missing = inventory.project.filter((reviewer) => reviewer.drift === "missing");
@@ -67187,194 +67705,11 @@ async function pickProviderModel(io, detected, deps = {}) {
67187
67705
  }
67188
67706
 
67189
67707
  // src/commands/review.ts
67708
+ init_caller_session();
67190
67709
  init_shell_config();
67191
67710
  init_selection();
67192
67711
  init_pr_comments();
67193
67712
  init_loop();
67194
-
67195
- // src/review/stack.ts
67196
- var STACK_TAGS = ["nestjs", "react", "mobx", "prisma", "playwright", "sql", "http-server"];
67197
- function isStackTag(value) {
67198
- return STACK_TAGS.includes(value);
67199
- }
67200
- var TAG_MARKERS = {
67201
- react: ["react", "react-dom"],
67202
- mobx: ["mobx", "mobx-react", "mobx-react-lite"],
67203
- prisma: ["prisma", "@prisma/client"],
67204
- playwright: ["@playwright/test", "playwright"],
67205
- sql: ["prisma", "@prisma/client", "typeorm", "sequelize", "knex", "drizzle-orm", "pg", "mysql2", "better-sqlite3", "kysely"],
67206
- "http-server": ["express", "fastify", "koa", "hono", "@nestjs/core", "@hapi/hapi"]
67207
- };
67208
- var DEPENDENCY_FIELDS = ["dependencies", "devDependencies", "peerDependencies", "optionalDependencies"];
67209
- async function detectProjectStack(cwd, options = {}) {
67210
- const readFile = options.readFile ?? defaultReadFile;
67211
- const manifestPath = `${cwd.replace(/\/+$/, "")}/package.json`;
67212
- let raw;
67213
- try {
67214
- raw = await readFile(manifestPath);
67215
- } catch {
67216
- return uncertainStack(`package.json not found at ${manifestPath}`);
67217
- }
67218
- let parsed;
67219
- try {
67220
- parsed = JSON.parse(raw);
67221
- } catch (error) {
67222
- return uncertainStack(`package.json at ${manifestPath} did not parse as JSON: ${error instanceof Error ? error.message : String(error)}`);
67223
- }
67224
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
67225
- return uncertainStack(`package.json at ${manifestPath} is not a JSON object`);
67226
- }
67227
- const record = parsed;
67228
- const names = new Set;
67229
- for (const field of DEPENDENCY_FIELDS) {
67230
- const value = record[field];
67231
- if (typeof value === "object" && value !== null && !Array.isArray(value)) {
67232
- for (const name of Object.keys(value)) {
67233
- names.add(name);
67234
- }
67235
- }
67236
- }
67237
- const workspaceGlobs = workspacePatterns(record);
67238
- if (workspaceGlobs.length > 0) {
67239
- return uncertainStack(`package.json at ${manifestPath} declares workspaces (${workspaceGlobs.join(", ")}); the dependencies that decide the stack live in the sub-packages, which this module does not walk`);
67240
- }
67241
- if (names.size === 0) {
67242
- return uncertainStack(`package.json at ${manifestPath} declares no dependencies in ${DEPENDENCY_FIELDS.join("/")} \u2014 a manifest that declares nothing has not said the repository uses nothing`);
67243
- }
67244
- const matched = [];
67245
- const nestjsMatches = [...names].filter((name) => name.startsWith("@nestjs/")).sort();
67246
- matched.push(...nestjsMatches);
67247
- const tags = { nestjs: nestjsMatches.length > 0 };
67248
- for (const tag of Object.keys(TAG_MARKERS)) {
67249
- const hit = TAG_MARKERS[tag].filter((marker) => names.has(marker));
67250
- tags[tag] = hit.length > 0;
67251
- matched.push(...hit);
67252
- }
67253
- return {
67254
- tags,
67255
- uncertain: false,
67256
- reason: `detected from ${manifestPath} (${names.size} declared dependenc${names.size === 1 ? "y" : "ies"})`,
67257
- matched
67258
- };
67259
- }
67260
- function workspacePatterns(record) {
67261
- const declared = record["workspaces"];
67262
- const list = Array.isArray(declared) ? declared : typeof declared === "object" && declared !== null ? declared["packages"] : undefined;
67263
- return Array.isArray(list) ? list.filter((item) => typeof item === "string" && item !== "") : [];
67264
- }
67265
- function uncertainStack(reason) {
67266
- const tags = Object.fromEntries(STACK_TAGS.map((tag) => [tag, true]));
67267
- return { tags, uncertain: true, reason, matched: [] };
67268
- }
67269
- async function defaultReadFile(path) {
67270
- return await Bun.file(path).text();
67271
- }
67272
- function parseStackRequires(value) {
67273
- if (value === undefined) {
67274
- return [];
67275
- }
67276
- const seen = new Set;
67277
- for (const token of value.split(",")) {
67278
- const trimmed = token.trim().toLowerCase();
67279
- if (isStackTag(trimmed)) {
67280
- seen.add(trimmed);
67281
- }
67282
- }
67283
- return [...seen];
67284
- }
67285
- function extractStackRequiresField(skillMdContent) {
67286
- if (!skillMdContent.startsWith("---")) {
67287
- return;
67288
- }
67289
- const end = skillMdContent.indexOf(`
67290
- ---`, 3);
67291
- if (end === -1) {
67292
- return;
67293
- }
67294
- const lines = skillMdContent.slice(3, end).split(`
67295
- `);
67296
- let inMetadata = false;
67297
- for (const line of lines) {
67298
- const topMatch = /^([A-Za-z_][A-Za-z0-9_-]*):\s*(.*)$/.exec(line);
67299
- if (topMatch) {
67300
- inMetadata = topMatch[1] === "metadata";
67301
- continue;
67302
- }
67303
- if (!inMetadata) {
67304
- continue;
67305
- }
67306
- const fieldMatch = /^\s+stack_requires:\s*(.+)$/.exec(line);
67307
- if (fieldMatch && fieldMatch[1] !== undefined) {
67308
- return stripQuotes(fieldMatch[1].trim());
67309
- }
67310
- }
67311
- return;
67312
- }
67313
- function stripQuotes(value) {
67314
- if (value.length >= 2) {
67315
- const first = value[0];
67316
- const last = value[value.length - 1];
67317
- if (first === '"' && last === '"' || first === "'" && last === "'") {
67318
- return value.slice(1, -1);
67319
- }
67320
- }
67321
- return value;
67322
- }
67323
- function scopeReviewerByStack(reviewer, requires, detected) {
67324
- const requiresCopy = [...requires];
67325
- if (requiresCopy.length === 0) {
67326
- return { reviewer, requires: requiresCopy, include: true, reason: "no stack requirement declared \u2014 generic reviewer" };
67327
- }
67328
- if (detected.uncertain) {
67329
- return {
67330
- reviewer,
67331
- requires: requiresCopy,
67332
- include: true,
67333
- reason: `stack detection uncertain (${detected.reason}); failing open`
67334
- };
67335
- }
67336
- const present = requiresCopy.filter((tag) => detected.tags[tag]);
67337
- if (present.length > 0) {
67338
- return { reviewer, requires: requiresCopy, include: true, reason: `detected: ${present.join(", ")}` };
67339
- }
67340
- return {
67341
- reviewer,
67342
- requires: requiresCopy,
67343
- include: false,
67344
- reason: `none of [${requiresCopy.join(", ")}] detected in package.json (${detected.reason})`
67345
- };
67346
- }
67347
- function renderStackScopingMarkdown(detected, decisions) {
67348
- const lines = [];
67349
- lines.push("## Stack scoping");
67350
- lines.push("");
67351
- lines.push(detected.uncertain ? `stack detection: uncertain \u2014 ${detected.reason}. Every stack-gated reviewer is included; uncertain never excludes.` : `stack detection: ${detected.reason}`);
67352
- if (!detected.uncertain) {
67353
- const present = STACK_TAGS.filter((tag) => detected.tags[tag]);
67354
- lines.push(`tags present: ${present.length > 0 ? present.join(", ") : "none"}`);
67355
- if (detected.matched.length > 0) {
67356
- lines.push(`matched dependencies: ${detected.matched.join(", ")}`);
67357
- }
67358
- }
67359
- lines.push("");
67360
- if (decisions.length === 0) {
67361
- lines.push("_no stack-gated reviewers were evaluated_");
67362
- return lines.join(`
67363
- `);
67364
- }
67365
- lines.push("| reviewer | requires | include | reason |");
67366
- lines.push("|---|---|---|---|");
67367
- for (const decision of decisions) {
67368
- lines.push(`| ${decision.reviewer} | ${decision.requires.length > 0 ? decision.requires.join(", ") : "-"} | ${decision.include ? "yes" : "no"} | ${decision.reason} |`);
67369
- }
67370
- const excluded = decisions.filter((decision) => !decision.include);
67371
- lines.push("");
67372
- lines.push(excluded.length === 0 ? "No reviewer was excluded by stack scoping." : `Excluded (never run this round): ${excluded.map((decision) => decision.reviewer).join(", ")}.`);
67373
- return lines.join(`
67374
- `);
67375
- }
67376
-
67377
- // src/commands/review.ts
67378
67713
  init_types3();
67379
67714
  var CREATE_FLAGS = [
67380
67715
  "--target",
@@ -67416,6 +67751,7 @@ var COMMENTS_REPLY_FLAGS = [
67416
67751
  "--self",
67417
67752
  "--outcomes",
67418
67753
  "--result",
67754
+ "--review",
67419
67755
  "--sha",
67420
67756
  "--round",
67421
67757
  "--final",
@@ -67424,7 +67760,8 @@ var COMMENTS_REPLY_FLAGS = [
67424
67760
  "--max-sentences",
67425
67761
  "--max-chars",
67426
67762
  "--flow-link",
67427
- "--fixtures"
67763
+ "--fixtures",
67764
+ "--allow-closed-pr"
67428
67765
  ];
67429
67766
  var LOOP_FLAGS = ["--flow", "--task"];
67430
67767
  var LEARN_FLAGS = ["--pr", "--dry-run", "--json"];
@@ -67473,6 +67810,7 @@ var TIER_FLAGS = [
67473
67810
  "--security",
67474
67811
  "--session-provider",
67475
67812
  "--session-model",
67813
+ "--from-shell-config",
67476
67814
  "--catalog",
67477
67815
  "--json"
67478
67816
  ];
@@ -67752,7 +68090,7 @@ async function runBudget(args) {
67752
68090
  async function runTier(args) {
67753
68091
  rejectUnknownFlags(args, TIER_FLAGS, "tier");
67754
68092
  const signals = tierSignalsFromArgs(args);
67755
- const session = sessionModelFromArgs(args);
68093
+ const { session, source } = sessionModelFromArgs(args);
67756
68094
  const catalog = await tierCatalog(args, session);
67757
68095
  const decision = decideDispatchModel(session, signals, catalog);
67758
68096
  const block = dispatchModelBlock(decision);
@@ -67765,8 +68103,13 @@ async function runTier(args) {
67765
68103
  console.log("");
67766
68104
  console.log(`tier: ${decision.tier}`);
67767
68105
  console.log(`tier_reasons: ${decision.tier_reasons.join(", ")}`);
67768
- console.log(`provider: ${decision.provider === "" ? "not resolved (no --session-provider, and none persisted by `keryx shell`)" : decision.provider}`);
67769
- console.log(`model: ${decision.model === "" ? "not resolved \u2014 run this dispatch on YOUR OWN model (never a downgrade)" : decision.model}`);
68106
+ console.log(`session_source: ${SESSION_SOURCE_LABELS[source]}`);
68107
+ if (pinsModel(decision)) {
68108
+ console.log(`provider: ${decision.provider}`);
68109
+ console.log(`model: ${decision.model}`);
68110
+ } else {
68111
+ console.log(`model: inherit \u2014 ${ADAPTIVE_GUIDANCE[decision.tier]}`);
68112
+ }
67770
68113
  console.log(`tier_resolution: ${decision.tier_resolution}`);
67771
68114
  console.log(`model_discovery: provider=${discovery.provider === "" ? "none" : discovery.provider} candidates=${discovery.candidates.length} ranked=${discovery.ranked.length} session_rank=${discovery.session_rank ?? "none"}`);
67772
68115
  console.log(`fallback_reason: ${discovery.fallback_reason ?? "none (ranking was not refused)"}`);
@@ -67795,12 +68138,25 @@ function tierSignalsFromArgs(args) {
67795
68138
  hasSecurityFinding: args.includes("--security")
67796
68139
  };
67797
68140
  }
67798
- function sessionModelFromArgs(args) {
67799
- const config = loadShellConfig();
67800
- return {
67801
- providerId: (optionValue(args, "--session-provider") ?? config.provider ?? "").trim(),
67802
- modelId: (optionValue(args, "--session-model") ?? config.model ?? "").trim()
67803
- };
68141
+ var SESSION_SOURCE_LABELS = {
68142
+ flags: "flags (--session-provider/--session-model)",
68143
+ env: "environment (KERYX_SESSION_PROVIDER/KERYX_SESSION_MODEL)",
68144
+ "shell-config": "the selection `keryx shell` persisted (--from-shell-config)",
68145
+ none: "none \u2014 the caller named no model, so the block is adaptive"
68146
+ };
68147
+ var ADAPTIVE_GUIDANCE = {
68148
+ light: "dispatch on a lighter model your own runtime offers (its fast/small class), or on your session model if it offers no choice",
68149
+ standard: "dispatch on your session model",
68150
+ deep: "dispatch on the most capable model your own runtime offers, or on your session model if it offers no choice"
68151
+ };
68152
+ function sessionModelFromArgs(args, env = process.env) {
68153
+ const resolved = resolveCallerSession({
68154
+ flagProvider: optionValue(args, "--session-provider"),
68155
+ flagModel: optionValue(args, "--session-model"),
68156
+ fromShellConfig: args.includes("--from-shell-config"),
68157
+ env
68158
+ });
68159
+ return { session: { providerId: resolved.providerId, modelId: resolved.modelId }, source: resolved.source };
67804
68160
  }
67805
68161
  async function tierCatalog(args, session) {
67806
68162
  const file = optionValue(args, "--catalog");
@@ -67833,12 +68189,14 @@ async function tierCatalog(args, session) {
67833
68189
  }
67834
68190
  return await detectProviders({ fetch, env: envWithSavedApiKeys() });
67835
68191
  }
68192
+ function pinsModel(decision) {
68193
+ return decision.tier_resolution === "discovered" && decision.provider !== "" && decision.model !== "";
68194
+ }
67836
68195
  function dispatchModelBlock(decision) {
67837
- const named = decision.provider !== "" && decision.model !== "";
67838
68196
  return {
67839
68197
  tier: decision.tier,
67840
68198
  tier_reasons: decision.tier_reasons,
67841
- ...named ? { provider: decision.provider, model: decision.model } : { inherit: true },
68199
+ ...pinsModel(decision) ? { provider: decision.provider, model: decision.model } : { inherit: true },
67842
68200
  tier_resolution: decision.tier_resolution,
67843
68201
  model_discovery: decision.model_discovery
67844
68202
  };
@@ -67887,6 +68245,15 @@ async function runCommentsCollect(args) {
67887
68245
  console.log(renderPrCommentsMarkdown({ repo, number, round, result }));
67888
68246
  console.log(`findings: ${findings.length}${out === undefined ? "" : ` (written to ${out})`}`);
67889
68247
  console.log(`collected against: ${sha} (round ${round})`);
68248
+ console.log(`pull request: ${describePullRequest(result.pull)}`);
68249
+ if (result.pull.state === "unknown") {
68250
+ console.log("WARNING: the pull request's state could not be read \u2014 `comments reply` will refuse this pull request.");
68251
+ } else if (result.pull.merged || result.pull.state === "closed") {
68252
+ console.log("WARNING: the pull request is no longer open \u2014 `comments reply` will refuse it unless --allow-closed-pr is passed.");
68253
+ }
68254
+ if (result.pull.headSha !== null && !shaMatchesHead(sha, result.pull.headSha)) {
68255
+ console.log(`WARNING: --sha ${sha} is not the pull request's head (${result.pull.headSha}) \u2014 this collection is stale, and \`comments reply\` refuses a SHA that is not the head.`);
68256
+ }
67890
68257
  console.log(`unanswered so far: ${unansweredComments(recorded).length} \u2014 replies are posted ONCE, after the final round.`);
67891
68258
  }
67892
68259
  async function runCommentsReply(args) {
@@ -67897,6 +68264,7 @@ async function runCommentsReply(args) {
67897
68264
  const isFinal = args.includes("--final");
67898
68265
  const dryRun = args.includes("--dry-run");
67899
68266
  await refuseInvalidResult(optionValue(args, "--result"), optionValue(args, "--outcomes"));
68267
+ await refuseUnmanagedReply(args, repo, number, dryRun || !isFinal);
67900
68268
  const cwd = process.cwd();
67901
68269
  const port = await resolvePort(args);
67902
68270
  const state = await readPrCommentState(cwd, repo, number);
@@ -67924,7 +68292,9 @@ async function runCommentsReply(args) {
67924
68292
  repo,
67925
68293
  number,
67926
68294
  pass,
67927
- sha: requiredOption(args, "--sha", "comments reply"),
68295
+ sha: requiredSha(args, "--sha", "comments reply"),
68296
+ pull: collected.pull,
68297
+ allowClosed: args.includes("--allow-closed-pr"),
67928
68298
  round: { index: round, isFinal },
67929
68299
  state,
67930
68300
  dryRun
@@ -67950,7 +68320,7 @@ async function resolvePort(args) {
67950
68320
  return createGhPort();
67951
68321
  }
67952
68322
  const files = {};
67953
- for (const key of ["pull-comments", "pull-reviews", "issue-comments"]) {
68323
+ for (const key of ["pull", "pull-comments", "pull-reviews", "issue-comments"]) {
67954
68324
  const file = join3(fixtures, `${key}.json`);
67955
68325
  try {
67956
68326
  files[key] = JSON.parse(await readFile88(file, "utf8"));
@@ -67976,6 +68346,28 @@ async function resolveSelfLogin(args) {
67976
68346
  }
67977
68347
  return login;
67978
68348
  }
68349
+ async function refuseUnmanagedReply(args, repo, number, postsNothing) {
68350
+ if (postsNothing || optionValue(args, "--result") !== undefined) {
68351
+ return;
68352
+ }
68353
+ const where = `${repo}#${number}`;
68354
+ const ref = optionValue(args, "--review");
68355
+ if (ref === undefined) {
68356
+ throw new Error(`Refusing to reply on ${where}: posting is for a MANAGED review, and nothing here shows one. Pass \`--review <review-id-or-path>\` (the package \`keryx review start/attach\` wrote for this pull request) or \`--result <file>\` (a review-pr-feedback run). A lightweight review is report-only \u2014 it answers nobody. \`--dry-run\` still shows what would be posted.`);
68357
+ }
68358
+ let manifest;
68359
+ try {
68360
+ manifest = await getManagedReviewStatus(process.cwd(), ref);
68361
+ } catch (error) {
68362
+ throw new Error(`Refusing to reply on ${where}: \`--review ${ref}\` is not a readable managed review package (${error instanceof Error ? error.message : String(error)}).`);
68363
+ }
68364
+ const target = manifest.target;
68365
+ const refNumber = /(?:^|[/#])(\d+)\/?$/.exec(target.ref.trim())?.[1];
68366
+ const repoMismatch = target.repository !== undefined && target.repository !== "" && !target.repository.toLowerCase().endsWith(repo.toLowerCase());
68367
+ if (target.kind !== "pr" || refNumber !== String(number) || repoMismatch) {
68368
+ throw new Error(`Refusing to reply on ${where}: \`--review ${ref}\` reviewed ${target.kind} \`${target.ref}\`${target.repository ? ` in ${target.repository}` : ""}, not this pull request. A reply pass answers the review that produced its outcomes.`);
68369
+ }
68370
+ }
67979
68371
  async function refuseInvalidResult(source, outcomesSource) {
67980
68372
  if (source === undefined) {
67981
68373
  return;
@@ -68035,20 +68427,20 @@ async function runLearn(args) {
68035
68427
  const cwd = process.cwd();
68036
68428
  const config = await loadReviewLearningConfig(cwd);
68037
68429
  if (config === null) {
68038
- console.log(`no review learning configured: ${path155.relative(cwd, reviewLearningConfigPath(cwd))} is absent, so this project does not learn from pull-request comments.`);
68430
+ console.log(`no review learning configured: ${path156.relative(cwd, reviewLearningConfigPath(cwd))} is absent, so this project does not learn from pull-request comments.`);
68039
68431
  return;
68040
68432
  }
68041
68433
  const number = requiredInteger(args, "--pr");
68042
68434
  const statePath = prCommentsStatePath(cwd, config.repo, number);
68043
68435
  if (!await pathExists(statePath)) {
68044
- throw new Error(`No collected comments for ${config.repo}#${number}: ${path155.relative(cwd, statePath)} does not exist. Run \`keryx review comments collect --repo ${config.repo} --pr ${number} --sha <head-sha>\` first \u2014 this command reads that record and never fetches from GitHub.`);
68436
+ throw new Error(`No collected comments for ${config.repo}#${number}: ${path156.relative(cwd, statePath)} does not exist. Run \`keryx review comments collect --repo ${config.repo} --pr ${number} --sha <head-sha>\` first \u2014 this command reads that record and never fetches from GitHub.`);
68045
68437
  }
68046
68438
  const state = await readPrCommentState(cwd, config.repo, number);
68047
68439
  const selection = selectLearnableComments(state, config.authors);
68048
68440
  const sourcePath = learningSourcePath(cwd, config.repo, number);
68049
68441
  const recordPath = learningRecordPath(cwd, config.repo, number);
68050
- const relativeSourcePath = toPosix(path155.relative(cwd, sourcePath));
68051
- await mkdir54(path155.dirname(sourcePath), { recursive: true });
68442
+ const relativeSourcePath = toPosix(path156.relative(cwd, sourcePath));
68443
+ await mkdir55(path156.dirname(sourcePath), { recursive: true });
68052
68444
  await writeFileAtomic(sourcePath, renderLearningSource(selection));
68053
68445
  await writeFileAtomic(recordPath, renderLearningRecord({
68054
68446
  repo: config.repo,
@@ -68071,7 +68463,7 @@ async function runLearn(args) {
68071
68463
  }
68072
68464
  console.log(`nothing to learn from ${config.repo}#${number} \u2014 ${detail}.`);
68073
68465
  console.log(`Source: ${relativeSourcePath}`);
68074
- console.log(`Record: ${toPosix(path155.relative(cwd, recordPath))}`);
68466
+ console.log(`Record: ${toPosix(path156.relative(cwd, recordPath))}`);
68075
68467
  return;
68076
68468
  }
68077
68469
  const proposal = await learnProjectSkill(cwd, {
@@ -68087,7 +68479,7 @@ async function runLearn(args) {
68087
68479
  console.log(`${proposal.dryRun ? "Would create" : "Created"} learning proposal: ${proposal.proposalId}`);
68088
68480
  console.log(`Skill: ${proposal.skill.module}/${proposal.skill.name}`);
68089
68481
  console.log(`Source: ${relativeSourcePath}`);
68090
- console.log(`Record: ${toPosix(path155.relative(cwd, recordPath))}`);
68482
+ console.log(`Record: ${toPosix(path156.relative(cwd, recordPath))}`);
68091
68483
  console.log(`Authors: ${config.authors.join(", ")}`);
68092
68484
  console.log(`Comments: ${selection.kept.length} used, ${selection.unconfigured.length} excluded as unconfigured, ${selection.bodyless.length} with no recorded body`);
68093
68485
  console.log(`Confidence: ${proposal.confidence}`);
@@ -68775,7 +69167,9 @@ comments:
68775
69167
  UNOBSERVED, on the same rule as no record at all.
68776
69168
  \`reply\` refuses without --final: replying per round turns one review thread
68777
69169
  into six, and a reply written mid-flow states an intention rather than an
68778
- outcome. Each reply is at most ${DEFAULT_MAX_SENTENCES_PER_REPLY} sentences (--max-sentences) AND ${DEFAULT_MAX_REPLY_CHARS} characters
69170
+ outcome. It also refuses a pull request that is closed or merged (override:
69171
+ --allow-closed-pr), one whose state could not be read, and a --sha that is not
69172
+ the pull request's head \u2014 the answers must describe the commit it points at. Each reply is at most ${DEFAULT_MAX_SENTENCES_PER_REPLY} sentences (--max-sentences) AND ${DEFAULT_MAX_REPLY_CHARS} characters
68779
69173
  (--max-chars) \u2014 CUT to both in code, with the remainder replaced by a link,
68780
69174
  because a sentence budget alone lets one 4,000-character sentence through \u2014
68781
69175
  threaded where GitHub gives a thread, and capped
@@ -68805,11 +69199,13 @@ tier:
68805
69199
  that produced it, the resolved provider/model, \`tier_resolution\`, and what was
68806
69200
  on the table when it resolved; --json prints the block alone.
68807
69201
  NO MODEL NAME EXISTS IN THIS COMMAND. The session's provider/model come from
68808
- the selection \`keryx shell\` persisted (override with --session-provider /
68809
- --session-model), and the candidate set comes from live provider detection
68810
- (override with --catalog, the \`detectProviders()\` shape). When the environment
68811
- cannot be worked out, the block says \`inherit\` and the dispatch runs on YOUR
68812
- OWN model: never a downgrade, never a non-zero exit. Detection is skipped
69202
+ the caller: --session-provider / --session-model, else KERYX_SESSION_PROVIDER /
69203
+ KERYX_SESSION_MODEL, else (only with --from-shell-config) the selection
69204
+ \`keryx shell\` persisted. The candidate set comes from live provider detection
69205
+ (override with --catalog, the \`detectProviders()\` shape). A model is named
69206
+ only when discovery assigned one other than the session's; otherwise the block
69207
+ says \`inherit\` with the tier, and the host picks its own model for that tier
69208
+ (standard: the session model). Never a non-zero exit. Detection is skipped
68813
69209
  entirely when the session names no provider and model, because ranking is
68814
69210
  refused without an anchor whatever the catalogue holds.
68815
69211
 
@@ -68852,8 +69248,8 @@ stack:
68852
69248
 
68853
69249
  // src/commands/rules.ts
68854
69250
  init_fs();
68855
- import { mkdir as mkdir55, readFile as readFile89, writeFile as writeFile47 } from "fs/promises";
68856
- import path156 from "path";
69251
+ import { mkdir as mkdir56, readFile as readFile89, writeFile as writeFile47 } from "fs/promises";
69252
+ import path157 from "path";
68857
69253
  async function rulesCommand(args = [], projectRoot = process.cwd()) {
68858
69254
  const subcommand = args[0];
68859
69255
  if (!subcommand || subcommand === "--help" || subcommand === "-h") {
@@ -68871,11 +69267,11 @@ async function rulesCommand(args = [], projectRoot = process.cwd()) {
68871
69267
  printHelp13();
68872
69268
  return;
68873
69269
  }
68874
- const metaprojectRoot = path156.join(projectRoot, ".metaproject");
69270
+ const metaprojectRoot = path157.join(projectRoot, ".metaproject");
68875
69271
  if (!await pathExists(metaprojectRoot)) {
68876
69272
  throw new Error("Metaproject is not initialized. Run `keryx init` first.");
68877
69273
  }
68878
- const manifestPath = path156.join(metaprojectRoot, "metaproject.json");
69274
+ const manifestPath = path157.join(metaprojectRoot, "metaproject.json");
68879
69275
  const manifest = await readManifest3(manifestPath);
68880
69276
  const enableTasks = moduleEnabled(manifest, "tasks");
68881
69277
  if (options.preview) {
@@ -68926,7 +69322,7 @@ async function rulesCommand(args = [], projectRoot = process.cwd()) {
68926
69322
  createDefault: true
68927
69323
  });
68928
69324
  const ruleSources = syncedRules.map((rule) => rule.source);
68929
- await mkdir55(path156.join(metaprojectRoot, "rules"), { recursive: true });
69325
+ await mkdir56(path157.join(metaprojectRoot, "rules"), { recursive: true });
68930
69326
  await persistManifestEntrypoints(manifestPath, manifest, ruleSources);
68931
69327
  await refreshRoutingEntrypoints(metaprojectRoot, manifest, ruleSources, await hasDistilledEntrypoints(metaprojectRoot), { intent: "rules sync", resolution: options.resolution });
68932
69328
  console.log(`# rules sync`);
@@ -69013,17 +69409,17 @@ Options:
69013
69409
  }
69014
69410
 
69015
69411
  // src/commands/standard.ts
69016
- import { mkdir as mkdir56, writeFile as writeFile48 } from "fs/promises";
69017
- import path160 from "path";
69412
+ import { mkdir as mkdir57, writeFile as writeFile48 } from "fs/promises";
69413
+ import path161 from "path";
69018
69414
 
69019
69415
  // src/standard/service.ts
69020
69416
  init_fs();
69021
- import path158 from "path";
69417
+ import path159 from "path";
69022
69418
  import { readFile as readFile91 } from "fs/promises";
69023
69419
 
69024
69420
  // src/standard/validate.ts
69025
69421
  init_fs();
69026
- import path157 from "path";
69422
+ import path158 from "path";
69027
69423
  import { readFile as readFile90 } from "fs/promises";
69028
69424
 
69029
69425
  // src/standard/schemas.ts
@@ -69296,20 +69692,20 @@ var MANIFEST_TOP_LEVEL_SCHEMA = {
69296
69692
  async function validateWorkspace2(cwd) {
69297
69693
  const errors = [];
69298
69694
  const warnings = [];
69299
- const metaprojectRoot = path157.join(cwd, ".metaproject");
69695
+ const metaprojectRoot = path158.join(cwd, ".metaproject");
69300
69696
  const requiredFiles = ["index.md", "README.md", "metaproject.json"];
69301
69697
  for (const file of requiredFiles) {
69302
- if (!await pathExists(path157.join(metaprojectRoot, file))) {
69698
+ if (!await pathExists(path158.join(metaprojectRoot, file))) {
69303
69699
  errors.push(issue("missing-required-file", `Required file .metaproject/${file} is missing`, `Run \`keryx init\` to scaffold .metaproject/${file}.`));
69304
69700
  }
69305
69701
  }
69306
69702
  const requiredDirs = ["modules", "rules", "skills", "data"];
69307
69703
  for (const dir of requiredDirs) {
69308
- if (!await pathExists(path157.join(metaprojectRoot, dir))) {
69704
+ if (!await pathExists(path158.join(metaprojectRoot, dir))) {
69309
69705
  errors.push(issue("missing-required-dir", `Required directory .metaproject/${dir}/ is missing`, `Run \`keryx init\` to create .metaproject/${dir}/.`));
69310
69706
  }
69311
69707
  }
69312
- const manifestPath = path157.join(metaprojectRoot, "metaproject.json");
69708
+ const manifestPath = path158.join(metaprojectRoot, "metaproject.json");
69313
69709
  if (!await pathExists(manifestPath)) {
69314
69710
  return { ok: errors.length === 0, errors, warnings };
69315
69711
  }
@@ -69341,7 +69737,7 @@ async function validateWorkspace2(cwd) {
69341
69737
  if (typeof value !== "string") {
69342
69738
  continue;
69343
69739
  }
69344
- if (!await pathExists(path157.join(cwd, value))) {
69740
+ if (!await pathExists(path158.join(cwd, value))) {
69345
69741
  errors.push(issue("missing-declared-path", `Declared path paths.${key} -> ${value} does not exist`, `Create ${value} or remove paths.${key} from metaproject.json.`));
69346
69742
  }
69347
69743
  }
@@ -69351,7 +69747,7 @@ async function validateWorkspace2(cwd) {
69351
69747
  continue;
69352
69748
  }
69353
69749
  if (typeof entry.manifest === "string") {
69354
- if (!await pathExists(path157.join(cwd, entry.manifest))) {
69750
+ if (!await pathExists(path158.join(cwd, entry.manifest))) {
69355
69751
  errors.push(issue("missing-module-manifest", `Enabled module "${key}" manifest ${entry.manifest} is missing`, `Create ${entry.manifest} or run \`keryx update\` to regenerate module manifests.`));
69356
69752
  }
69357
69753
  }
@@ -69360,14 +69756,14 @@ async function validateWorkspace2(cwd) {
69360
69756
  if (typeof value !== "string") {
69361
69757
  continue;
69362
69758
  }
69363
- if (!await pathExists(path157.join(cwd, value))) {
69759
+ if (!await pathExists(path158.join(cwd, value))) {
69364
69760
  const sink = field === "data" ? warnings : errors;
69365
69761
  sink.push(issue("missing-module-path", `Module "${key}" declares ${String(field)} ${value}, which does not exist`, field === "data" ? `Run the module (e.g. \`keryx ${key} ...\`) to generate ${value}, or update modules.${key}.data in metaproject.json.` : `Create ${value} or update modules.${key}.${String(field)} in metaproject.json.`));
69366
69762
  }
69367
69763
  }
69368
69764
  }
69369
69765
  for (const name of ROOT_ENTRYPOINTS2) {
69370
- const filePath = path157.join(cwd, name);
69766
+ const filePath = path158.join(cwd, name);
69371
69767
  if (!await pathExists(filePath)) {
69372
69768
  continue;
69373
69769
  }
@@ -69421,7 +69817,7 @@ async function runDoctor(cwd) {
69421
69817
  return validateWorkspace2(cwd);
69422
69818
  }
69423
69819
  async function readManifest4(cwd) {
69424
- const manifestPath = path158.join(cwd, ".metaproject", "metaproject.json");
69820
+ const manifestPath = path159.join(cwd, ".metaproject", "metaproject.json");
69425
69821
  if (!await pathExists(manifestPath)) {
69426
69822
  throw new Error("Metaproject is not initialized (missing .metaproject/metaproject.json). Run `keryx init` first.");
69427
69823
  }
@@ -69443,10 +69839,10 @@ async function runCapabilities(cwd) {
69443
69839
  // src/standard/emit-llms.ts
69444
69840
  init_fs();
69445
69841
  init_json();
69446
- import path159 from "path";
69842
+ import path160 from "path";
69447
69843
  import { readdir as readdir31 } from "fs/promises";
69448
69844
  function llmsPath(cwd) {
69449
- return path159.join(cwd, ".metaproject", "llms.txt");
69845
+ return path160.join(cwd, ".metaproject", "llms.txt");
69450
69846
  }
69451
69847
  function renderLlms(manifest, artifacts) {
69452
69848
  const name = manifest.name ?? "metaproject";
@@ -69489,7 +69885,7 @@ function renderLlms(manifest, artifacts) {
69489
69885
  `;
69490
69886
  }
69491
69887
  async function collectArtifactIndex(cwd) {
69492
- const dataRoot = path159.join(cwd, ".metaproject", "data");
69888
+ const dataRoot = path160.join(cwd, ".metaproject", "data");
69493
69889
  if (!await pathExists(dataRoot)) {
69494
69890
  return [];
69495
69891
  }
@@ -69502,11 +69898,11 @@ async function collectArtifactIndex(cwd) {
69502
69898
  return;
69503
69899
  }
69504
69900
  for (const entry of entries) {
69505
- const full = path159.join(dir, entry.name);
69901
+ const full = path160.join(dir, entry.name);
69506
69902
  if (entry.isDirectory()) {
69507
69903
  await walk(full);
69508
69904
  } else if (entry.isFile()) {
69509
- const rel = toPosix(path159.relative(cwd, full));
69905
+ const rel = toPosix(path160.relative(cwd, full));
69510
69906
  if (rel.includes("/artifacts/")) {
69511
69907
  out.push(rel);
69512
69908
  }
@@ -69517,7 +69913,7 @@ async function collectArtifactIndex(cwd) {
69517
69913
  return out.sort();
69518
69914
  }
69519
69915
  async function emitLlms(cwd) {
69520
- const manifestPath = path159.join(cwd, ".metaproject", "metaproject.json");
69916
+ const manifestPath = path160.join(cwd, ".metaproject", "metaproject.json");
69521
69917
  const manifest = await readJsonFileOr(manifestPath, {});
69522
69918
  const artifacts = await collectArtifactIndex(cwd);
69523
69919
  const content = renderLlms(manifest, artifacts);
@@ -69696,11 +70092,11 @@ async function handleEmit(projectRoot, args) {
69696
70092
  if (args.includes("--stdout")) {
69697
70093
  process.stdout.write(result.content);
69698
70094
  } else {
69699
- await mkdir56(path160.dirname(result.path), { recursive: true });
70095
+ await mkdir57(path161.dirname(result.path), { recursive: true });
69700
70096
  await writeFile48(result.path, result.content, "utf8");
69701
70097
  heading("keryx standard emit llms");
69702
70098
  if (problems.length === 0) {
69703
- console.log(` ${style.green(symbols.ok)} wrote ${path160.relative(projectRoot, result.path)} (valid llms.txt)`);
70099
+ console.log(` ${style.green(symbols.ok)} wrote ${path161.relative(projectRoot, result.path)} (valid llms.txt)`);
69704
70100
  } else {
69705
70101
  for (const problem of problems) {
69706
70102
  console.log(` ${style.red(symbols.cross)} ${problem}`);
@@ -70503,8 +70899,9 @@ var COMMAND_DESCRIPTORS = [
70503
70899
  ],
70504
70900
  args: [
70505
70901
  { name: "opt-in", type: "bool", required: false, desc: "request cross-family review; without it the answer is single-family" },
70506
- { name: "session-provider", type: "string", required: false, desc: "provider that authored the change; defaults to the persisted shell selection" },
70507
- { name: "session-model", type: "string", required: false, desc: "model that authored the change; defaults to the persisted shell selection" },
70902
+ { name: "session-provider", type: "string", required: false, desc: "provider that authored the change; defaults to KERYX_SESSION_PROVIDER" },
70903
+ { name: "session-model", type: "string", required: false, desc: "model that authored the change; defaults to KERYX_SESSION_MODEL" },
70904
+ { name: "from-shell-config", type: "bool", required: false, desc: "use the selection keryx shell persisted as the session (off by default)" },
70508
70905
  { name: "json", type: "bool", required: false, desc: "the record a round should carry" }
70509
70906
  ],
70510
70907
  json: true,
@@ -70699,8 +71096,8 @@ Usage:
70699
71096
  }
70700
71097
 
70701
71098
  // src/commands/security.ts
70702
- import { mkdir as mkdir57, readdir as readdir32, readFile as readFile93, writeFile as writeFile49 } from "fs/promises";
70703
- import path162 from "path";
71099
+ import { mkdir as mkdir58, readdir as readdir32, readFile as readFile93, writeFile as writeFile49 } from "fs/promises";
71100
+ import path163 from "path";
70704
71101
  import { fileURLToPath as fileURLToPath7 } from "url";
70705
71102
  init_args();
70706
71103
  init_contained_path();
@@ -70870,11 +71267,11 @@ init_detect();
70870
71267
  init_fs();
70871
71268
  init_config();
70872
71269
  init_detect();
70873
- import path161 from "path";
71270
+ import path162 from "path";
70874
71271
  import { readFile as readFile92 } from "fs/promises";
70875
71272
  var DEFAULT_CORPORA = ["injection", "exfil", "structured-pii", "secret"];
70876
71273
  async function loadEvalCases(dir) {
70877
- const file = path161.join(dir, "cases.json");
71274
+ const file = path162.join(dir, "cases.json");
70878
71275
  if (!await pathExists(file)) {
70879
71276
  return [];
70880
71277
  }
@@ -70916,7 +71313,7 @@ async function runEval(opts) {
70916
71313
  const corporaSeen = [];
70917
71314
  let totalCases = 0;
70918
71315
  for (const corpus of opts.corpora) {
70919
- const dir = path161.join(opts.fixturesRoot, corpus);
71316
+ const dir = path162.join(opts.fixturesRoot, corpus);
70920
71317
  const cases = await loadEvalCases(dir);
70921
71318
  if (cases.length === 0) {
70922
71319
  continue;
@@ -71155,7 +71552,7 @@ async function handleScan(cwd, args) {
71155
71552
  ownerRoot: projectRoot,
71156
71553
  targetPath: contained.path,
71157
71554
  source,
71158
- path: path162.relative(projectRoot, contained.path) || ".",
71555
+ path: path163.relative(projectRoot, contained.path) || ".",
71159
71556
  ...exclusions.length > 0 ? { exclusions } : {},
71160
71557
  ...recursive !== undefined ? { recursive } : {},
71161
71558
  ...maxFiles !== undefined ? { limits: { maxFiles } } : {},
@@ -71231,7 +71628,7 @@ function scanPathArgument(args) {
71231
71628
  return optionValue(args, "--file");
71232
71629
  }
71233
71630
  function mcpBaselinePath(cwd) {
71234
- return path162.join(securityDataRoot(cwd), "mcp-baseline.json");
71631
+ return path163.join(securityDataRoot(cwd), "mcp-baseline.json");
71235
71632
  }
71236
71633
  async function readMcpBaseline(file) {
71237
71634
  if (!await pathExists(file)) {
@@ -71259,7 +71656,7 @@ async function collectManifestFiles(target) {
71259
71656
  }
71260
71657
  const files = [];
71261
71658
  for (const entry of entries) {
71262
- const full = path162.join(target, entry.name);
71659
+ const full = path163.join(target, entry.name);
71263
71660
  if (entry.isDirectory()) {
71264
71661
  files.push(...await collectManifestFiles(full));
71265
71662
  } else if (entry.isFile() && entry.name.endsWith(".json") && entry.name !== "cases.json") {
@@ -71294,7 +71691,7 @@ async function handleScanMcp(cwd, args) {
71294
71691
  const { manifest } = extractManifestAndBaseline(read.value, {});
71295
71692
  const baseline = { schemaVersion: 1, tools: buildMcpBaseline(manifest) };
71296
71693
  const outPath = mcpBaselinePath(cwd);
71297
- await mkdir57(path162.dirname(outPath), { recursive: true });
71694
+ await mkdir58(path163.dirname(outPath), { recursive: true });
71298
71695
  await writeFile49(outPath, `${JSON.stringify(baseline, null, 2)}
71299
71696
  `, "utf8");
71300
71697
  if (asJson) {
@@ -71338,7 +71735,7 @@ async function handleScanMcp(cwd, args) {
71338
71735
  coverage,
71339
71736
  baseline: baselineState.state,
71340
71737
  files: perFile.map((entry) => ({
71341
- file: path162.relative(cwd, entry.file),
71738
+ file: path163.relative(cwd, entry.file),
71342
71739
  readable: entry.readable,
71343
71740
  findings: entry.matches.map((m) => ({
71344
71741
  category: m.category,
@@ -71355,7 +71752,7 @@ async function handleScanMcp(cwd, args) {
71355
71752
  console.log("");
71356
71753
  console.log(` ${style.bold(`${unreadable} manifest(s) could not be read and were NOT scanned:`)}`);
71357
71754
  for (const entry of perFile.filter((e) => !e.readable)) {
71358
- console.log(` ${symbols.cross} ${path162.relative(cwd, entry.file)}`);
71755
+ console.log(` ${symbols.cross} ${path163.relative(cwd, entry.file)}`);
71359
71756
  }
71360
71757
  }
71361
71758
  if (baselineState.state === "unreadable") {
@@ -71364,7 +71761,7 @@ async function handleScanMcp(cwd, args) {
71364
71761
  }
71365
71762
  for (const entry of flagged) {
71366
71763
  console.log("");
71367
- console.log(` ${style.bold(path162.relative(cwd, entry.file))}`);
71764
+ console.log(` ${style.bold(path163.relative(cwd, entry.file))}`);
71368
71765
  for (const m of entry.matches) {
71369
71766
  console.log(` ${severityMarker(m.severity)} ${m.category}/${m.policyId} ${style.dim(`(conf ${m.confidence})`)}`);
71370
71767
  }
@@ -71525,9 +71922,9 @@ async function handleHooks(cwd, args) {
71525
71922
  await installRuntimeHooks(cwd, runtime);
71526
71923
  const errors = runtime.validate(JSON.parse(await readFile93(runtime.settingsPath(cwd), "utf8")));
71527
71924
  if (errors.length === 0) {
71528
- console.log(` ${style.green(symbols.ok)} ${runtime.id} \u2192 ${path162.relative(cwd, runtime.settingsPath(cwd))}`);
71925
+ console.log(` ${style.green(symbols.ok)} ${runtime.id} \u2192 ${path163.relative(cwd, runtime.settingsPath(cwd))}`);
71529
71926
  if (await modeOf(cwd) === "advisory") {
71530
- note(`advisory mode: ${runtime.id} will report findings and allow the call. Set \`mode\` to \`enforced\`, \`ci\` or \`gateway\` in ${path162.join(".metaproject", "security.config.json")} to make it refuse.`);
71927
+ note(`advisory mode: ${runtime.id} will report findings and allow the call. Set \`mode\` to \`enforced\`, \`ci\` or \`gateway\` in ${path163.join(".metaproject", "security.config.json")} to make it refuse.`);
71531
71928
  }
71532
71929
  } else {
71533
71930
  for (const e of errors) {
@@ -71542,7 +71939,7 @@ async function handleHooks(cwd, args) {
71542
71939
  }
71543
71940
  }
71544
71941
  function fixturesRoot() {
71545
- return path162.join(path162.dirname(fileURLToPath7(import.meta.url)), "..", "..", "fixtures");
71942
+ return path163.join(path163.dirname(fileURLToPath7(import.meta.url)), "..", "..", "fixtures");
71546
71943
  }
71547
71944
  async function handleEval(cwd, args) {
71548
71945
  const corpusArg = optionValue(args, "--corpus") ?? "all";
@@ -71564,7 +71961,7 @@ async function handleEval(cwd, args) {
71564
71961
  detect = await pureDetect(cwd);
71565
71962
  }
71566
71963
  const report = await runEval({ fixturesRoot: root, corpora, detect });
71567
- const thresholds = await loadThresholds(path162.join(root, "thresholds.json"));
71964
+ const thresholds = await loadThresholds(path163.join(root, "thresholds.json"));
71568
71965
  const gate = gateEval(report, thresholds);
71569
71966
  if (asJson) {
71570
71967
  console.log(JSON.stringify({ report, gate }, null, 2));
@@ -71660,7 +72057,7 @@ async function policyIsUnderstood(cwd) {
71660
72057
  const config = await loadSecurityConfig(cwd);
71661
72058
  const errors = validateSecurityConfig(config);
71662
72059
  if (errors.length > 0) {
71663
- process.stderr.write(`keryx security: ${path162.relative(cwd, configPath(cwd))} does not match the policy schema, so no approval is emitted \u2014 ${errors[0]}
72060
+ process.stderr.write(`keryx security: ${path163.relative(cwd, configPath(cwd))} does not match the policy schema, so no approval is emitted \u2014 ${errors[0]}
71664
72061
  `);
71665
72062
  return false;
71666
72063
  }
@@ -71710,7 +72107,7 @@ function printSecurityHelp() {
71710
72107
 
71711
72108
  // src/harness/process/sandbox/detect.ts
71712
72109
  import { existsSync as realExistsSync } from "fs";
71713
- import path163 from "path";
72110
+ import path164 from "path";
71714
72111
 
71715
72112
  // src/harness/process/sandbox/seatbelt.ts
71716
72113
  var SANDBOX_EXEC_PATH = "/usr/bin/sandbox-exec";
@@ -71904,9 +72301,9 @@ function detectSandboxLauncher(opts = {}) {
71904
72301
  }
71905
72302
  if (platform === "linux") {
71906
72303
  const env = opts.env ?? process.env;
71907
- const dirs = (env.PATH ?? "").split(path163.delimiter).filter(Boolean);
72304
+ const dirs = (env.PATH ?? "").split(path164.delimiter).filter(Boolean);
71908
72305
  for (const dir of dirs) {
71909
- const candidate = path163.join(dir, BWRAP_PROGRAM);
72306
+ const candidate = path164.join(dir, BWRAP_PROGRAM);
71910
72307
  if (exists(candidate)) {
71911
72308
  return { available: true, platform, path: candidate };
71912
72309
  }
@@ -72045,7 +72442,7 @@ async function sandboxCommand(args = [], deps = {}) {
72045
72442
  init_args();
72046
72443
 
72047
72444
  // src/commands/integrate.ts
72048
- import path164 from "path";
72445
+ import path165 from "path";
72049
72446
  var EDITOR_USAGE = `<cursor|claude|opencode|vscode|generic|all>`;
72050
72447
  var DEFAULT_EDITORS = ["all"];
72051
72448
  async function integrateCommand(args = [], cwd = process.cwd(), label) {
@@ -72078,7 +72475,7 @@ async function addIntegration(cwd, editors, dryRun, label) {
72078
72475
  console.log(outcome.snippet ?? "");
72079
72476
  continue;
72080
72477
  }
72081
- const rel = path164.relative(cwd, outcome.filePath);
72478
+ const rel = path165.relative(cwd, outcome.filePath);
72082
72479
  if (outcome.errors.length > 0) {
72083
72480
  for (const e of outcome.errors) {
72084
72481
  console.log(` ${style.red(symbols.cross)} ${e}`);
@@ -72117,7 +72514,7 @@ async function removeIntegration(cwd, editors, dryRun, label) {
72117
72514
  console.log(` ${style.gray(symbols.off)} ${outcome.id} ${style.dim("no file to change")}`);
72118
72515
  continue;
72119
72516
  }
72120
- const rel = path164.relative(cwd, outcome.filePath);
72517
+ const rel = path165.relative(cwd, outcome.filePath);
72121
72518
  console.log(` ${outcome.removed ? style.green(symbols.ok) : style.gray(symbols.off)} ${outcome.id} ${style.dim(outcome.removed ? `${dryRun ? "would be removed from" : "removed from"} ${rel}` : "nothing to remove")}`);
72122
72519
  }
72123
72520
  }
@@ -72144,21 +72541,21 @@ init_contained_path();
72144
72541
 
72145
72542
  // src/mcp-servers/config.ts
72146
72543
  init_config_dir();
72147
- import path166 from "path";
72544
+ import path167 from "path";
72148
72545
 
72149
72546
  // src/mcp-servers/compat.ts
72150
72547
  init_config_dir();
72151
- import path165 from "path";
72548
+ import path166 from "path";
72152
72549
  import os2 from "os";
72153
72550
  function compatFiles(cwd, home) {
72154
72551
  const base = home ?? os2.homedir();
72155
72552
  return [
72156
- { source: "grok", file: path165.join(base, ".grok", "config.toml"), projectLocal: false },
72157
- { source: "grok", file: path165.join(cwd, ".grok", "config.toml"), projectLocal: true },
72158
- { source: "mcp.json", file: path165.join(cwd, ".mcp.json"), projectLocal: true },
72159
- { source: "cursor", file: path165.join(base, ".cursor", "mcp.json"), projectLocal: false },
72160
- { source: "cursor", file: path165.join(cwd, ".cursor", "mcp.json"), projectLocal: true },
72161
- { source: "claude", file: path165.join(base, ".claude.json"), projectLocal: false }
72553
+ { source: "grok", file: path166.join(base, ".grok", "config.toml"), projectLocal: false },
72554
+ { source: "grok", file: path166.join(cwd, ".grok", "config.toml"), projectLocal: true },
72555
+ { source: "mcp.json", file: path166.join(cwd, ".mcp.json"), projectLocal: true },
72556
+ { source: "cursor", file: path166.join(base, ".cursor", "mcp.json"), projectLocal: false },
72557
+ { source: "cursor", file: path166.join(cwd, ".cursor", "mcp.json"), projectLocal: true },
72558
+ { source: "claude", file: path166.join(base, ".claude.json"), projectLocal: false }
72162
72559
  ];
72163
72560
  }
72164
72561
  function readMcpServersShape(file, text) {
@@ -72554,25 +72951,25 @@ function parseConfigFile(file) {
72554
72951
  }
72555
72952
  function projectConfigFiles(cwd, gitRoot) {
72556
72953
  const files = [];
72557
- let dir = path166.resolve(cwd);
72558
- const stop = gitRoot === undefined ? undefined : path166.resolve(gitRoot);
72954
+ let dir = path167.resolve(cwd);
72955
+ const stop = gitRoot === undefined ? undefined : path167.resolve(gitRoot);
72559
72956
  for (;; ) {
72560
- files.push(path166.join(dir, ".keryx", "mcp-servers.json"));
72957
+ files.push(path167.join(dir, ".keryx", "mcp-servers.json"));
72561
72958
  if (stop !== undefined && dir === stop)
72562
72959
  break;
72563
- const parent = path166.dirname(dir);
72960
+ const parent = path167.dirname(dir);
72564
72961
  if (parent === dir)
72565
72962
  break;
72566
72963
  dir = parent;
72567
72964
  }
72568
72965
  return files.reverse();
72569
72966
  }
72570
- var NO_HOME = path166.join(path166.sep, "\x00keryx-no-home");
72967
+ var NO_HOME = path167.join(path167.sep, "\x00keryx-no-home");
72571
72968
  function loadMcpServers(options) {
72572
72969
  const env = options.env ?? process.env;
72573
72970
  const configDir = options.configDir ?? keryxConfigDir();
72574
72971
  const problems = [];
72575
- const userFile = path166.join(configDir, "mcp-servers.json");
72972
+ const userFile = path167.join(configDir, "mcp-servers.json");
72576
72973
  const user = parseConfigFile(userFile);
72577
72974
  problems.push(...user.problems);
72578
72975
  const winner = new Map;
@@ -72607,7 +73004,7 @@ function loadMcpServers(options) {
72607
73004
  winner.set(name, { entry, source: "project", file, projectLocal: true });
72608
73005
  }
72609
73006
  }
72610
- const overlayFile = path166.join(configDir, "mcp-servers-disabled.json");
73007
+ const overlayFile = path167.join(configDir, "mcp-servers-disabled.json");
72611
73008
  const overlay = readOverlay(overlayFile);
72612
73009
  problems.push(...overlay.problems);
72613
73010
  const servers = [...winner.entries()].map(([name, { entry, source, file, projectLocal }]) => {
@@ -73042,11 +73439,11 @@ function createMcpInteractiveTools(deps) {
73042
73439
 
73043
73440
  // src/mcp-servers/credentials.ts
73044
73441
  init_config_dir();
73045
- import path167 from "path";
73442
+ import path168 from "path";
73046
73443
  var FORGET_CODE_VERIFIER = { code_verifier: undefined };
73047
73444
  var CREDENTIALS_SCHEMA_VERSION = 1;
73048
73445
  function credentialsFile(configDir) {
73049
- return path167.join(configDir ?? ensureKeryxConfigDir(), "mcp-credentials.json");
73446
+ return path168.join(configDir ?? ensureKeryxConfigDir(), "mcp-credentials.json");
73050
73447
  }
73051
73448
  function credentialKey(serverName, serverUrl) {
73052
73449
  return `${serverName}:${serverUrl}`;
@@ -73945,7 +74342,7 @@ async function connectStdioMcpServer(argv, options, handshakeTimeoutMs, signal,
73945
74342
  }
73946
74343
 
73947
74344
  // src/mcp-servers/runtime.ts
73948
- import path170 from "path";
74345
+ import path171 from "path";
73949
74346
 
73950
74347
  // src/mcp-servers/manager.ts
73951
74348
  var DEFAULT_CONNECT_CONCURRENCY = 4;
@@ -74037,9 +74434,9 @@ async function closeServers(states) {
74037
74434
  // src/mcp-servers/trust.ts
74038
74435
  init_config_dir();
74039
74436
  import { createHash as createHash32 } from "crypto";
74040
- import path168 from "path";
74437
+ import path169 from "path";
74041
74438
  function trustFile(configDir) {
74042
- return path168.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers-trust.json");
74439
+ return path169.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers-trust.json");
74043
74440
  }
74044
74441
  function serverFingerprint(server) {
74045
74442
  const material = JSON.stringify({
@@ -74136,16 +74533,16 @@ function credentialSummary(raw) {
74136
74533
  // src/mcp-servers/store.ts
74137
74534
  init_config_dir();
74138
74535
  import { mkdirSync as mkdirSync6, writeFileSync as writeFileSync7 } from "fs";
74139
- import path169 from "path";
74536
+ import path170 from "path";
74140
74537
  var SERVER_NAME_PATTERN = /^[A-Za-z0-9_-]+$/;
74141
74538
  function userConfigFile(configDir) {
74142
- return path169.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers.json");
74539
+ return path170.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers.json");
74143
74540
  }
74144
74541
  function projectConfigFile(projectRoot) {
74145
- return path169.join(projectRoot, ".keryx", "mcp-servers.json");
74542
+ return path170.join(projectRoot, ".keryx", "mcp-servers.json");
74146
74543
  }
74147
74544
  function overlayFile(configDir) {
74148
- return path169.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers-disabled.json");
74545
+ return path170.join(configDir ?? ensureKeryxConfigDir(), "mcp-servers-disabled.json");
74149
74546
  }
74150
74547
  function readForWrite(file) {
74151
74548
  const read = readConfigFile(file);
@@ -74184,7 +74581,7 @@ function writeDocument(file, doc, scope) {
74184
74581
  writeOwnerOnlyFileAtomic(file, body);
74185
74582
  return;
74186
74583
  }
74187
- mkdirSync6(path169.dirname(file), { recursive: true });
74584
+ mkdirSync6(path170.dirname(file), { recursive: true });
74188
74585
  writeFileSync7(file, body, { encoding: "utf8" });
74189
74586
  }
74190
74587
  function addServer(options) {
@@ -74733,7 +75130,7 @@ async function defaultConnect(server, env, signal, kills, configDir) {
74733
75130
  function defaultServerCwd(server, fallbackCwd = process.cwd()) {
74734
75131
  if (server.source !== "project")
74735
75132
  return fallbackCwd;
74736
- return path170.dirname(path170.dirname(server.file));
75133
+ return path171.dirname(path171.dirname(server.file));
74737
75134
  }
74738
75135
 
74739
75136
  // src/commands/mcp-servers.ts
@@ -75347,12 +75744,12 @@ function positional3(args) {
75347
75744
 
75348
75745
  // src/commands/serve-mcp.ts
75349
75746
  init_args();
75350
- import path177 from "path";
75747
+ import path178 from "path";
75351
75748
 
75352
75749
  // src/mcp/discovery.ts
75353
75750
  init_fs();
75354
75751
  init_json();
75355
- import path171 from "path";
75752
+ import path172 from "path";
75356
75753
  var MODULE_MANIFEST_KEY = {
75357
75754
  gdgraph: "gdgraph",
75358
75755
  security: "security",
@@ -75414,7 +75811,7 @@ function buildDiscovery(manifest) {
75414
75811
  };
75415
75812
  }
75416
75813
  async function loadDiscovery(cwd) {
75417
- const manifestPath = path171.join(cwd, ".metaproject", "metaproject.json");
75814
+ const manifestPath = path172.join(cwd, ".metaproject", "metaproject.json");
75418
75815
  if (!await pathExists(manifestPath)) {
75419
75816
  return buildDiscovery({});
75420
75817
  }
@@ -75486,8 +75883,8 @@ init_workspace_service();
75486
75883
  init_store2();
75487
75884
  init_fs();
75488
75885
  import { createHash as createHash35, randomUUID as randomUUID17 } from "crypto";
75489
- import { appendFile as appendFile7, mkdir as mkdir58, open as open2, readFile as readFile94, rename as rename7, rm as rm12, stat as stat17, writeFile as writeFile50 } from "fs/promises";
75490
- import path172 from "path";
75886
+ import { appendFile as appendFile7, mkdir as mkdir59, open as open2, readFile as readFile94, rename as rename7, rm as rm12, stat as stat17, writeFile as writeFile50 } from "fs/promises";
75887
+ import path173 from "path";
75491
75888
 
75492
75889
  // src/sac/policy-experiment.ts
75493
75890
  import { createHash as createHash34, createHmac as createHmac2 } from "crypto";
@@ -75843,7 +76240,7 @@ function hasReportIntegrity(report) {
75843
76240
  return reportHashPattern.test(reportDigest) && sha2565(body) === reportDigest;
75844
76241
  }
75845
76242
  async function resolvePolicySelection(workspaceRoot, canonicalFallback) {
75846
- const configPath = path172.join(workspaceRoot, ...policyExperimentConfigPath);
76243
+ const configPath = path173.join(workspaceRoot, ...policyExperimentConfigPath);
75847
76244
  let configJson;
75848
76245
  try {
75849
76246
  const raw = await readFile94(configPath, "utf8").catch((error) => {
@@ -75945,7 +76342,7 @@ async function diagnosePolicyReadiness(workspaceRoot) {
75945
76342
  const integrityReady = steps.every((entry) => entry.step === "activation-flags" || entry.status === "pass");
75946
76343
  return Object.freeze({ configPresent, enabled, killSwitch, integrityReady, candidateWouldActivate: integrityReady && enabled && !killSwitch, steps: Object.freeze([...steps]) });
75947
76344
  };
75948
- const configPath = path172.join(workspaceRoot, ...policyExperimentConfigPath);
76345
+ const configPath = path173.join(workspaceRoot, ...policyExperimentConfigPath);
75949
76346
  let configJson;
75950
76347
  try {
75951
76348
  const raw = await readFile94(configPath, "utf8").catch((error) => {
@@ -76200,9 +76597,9 @@ class FwkReadService {
76200
76597
  const recordedAt = nowIso2(now);
76201
76598
  const id = `receipt-${randomUUID17().replace(/-/g, "").slice(0, 16)}`;
76202
76599
  const base = { schemaVersion: "1.0", id, workspaceId, actor, action, decision, recordedAt, cost: { tokens: 0, toolCalls: 1, elapsedMs: 0 }, contextAssembly: { traceRef: assembly.traceRef, configurationRevision: assembly.configurationRevision, selected: assembly.selected.map((id) => `./ids/${id}`), omittedOptional: assembly.omittedOptional.map((id) => `./ids/${id}`) }, policy: { ref: assembly.policyRef, revision: assembly.policyRevision }, ...action === "resource" ? { resourceRef: `./ids/${resourceId ?? "unknown"}` } : {} };
76203
- const ledger = path172.join(this.options.canonical.workspaceRoot, ".metaproject", "context-operations", "access-receipts.jsonl");
76204
- const checkpointPath = path172.join(path172.dirname(ledger), "access-receipts.checkpoint.json");
76205
- await mkdir58(path172.dirname(ledger), { recursive: true, mode: 448 });
76600
+ const ledger = path173.join(this.options.canonical.workspaceRoot, ".metaproject", "context-operations", "access-receipts.jsonl");
76601
+ const checkpointPath = path173.join(path173.dirname(ledger), "access-receipts.checkpoint.json");
76602
+ await mkdir59(path173.dirname(ledger), { recursive: true, mode: 448 });
76206
76603
  return withFileLock(`${ledger}.lock`, async () => {
76207
76604
  const state = await resolveLedgerState(ledger, checkpointPath, this.options.verifyReceiptLedger ?? verifyAccessReceiptLedger);
76208
76605
  const receipt = sealAccessReceipt(base, state.headHash);
@@ -76312,9 +76709,9 @@ init_store3();
76312
76709
  // src/sac/collaboration-service.ts
76313
76710
  init_workspace_service();
76314
76711
  init_sac();
76315
- import { appendFile as appendFile8, mkdir as mkdir59, readFile as readFile95 } from "fs/promises";
76712
+ import { appendFile as appendFile8, mkdir as mkdir60, readFile as readFile95 } from "fs/promises";
76316
76713
  import { randomUUID as randomUUID18 } from "crypto";
76317
- import path173 from "path";
76714
+ import path174 from "path";
76318
76715
 
76319
76716
  class CollaborationServiceError extends Error {
76320
76717
  code;
@@ -76330,7 +76727,7 @@ class CollaborationService {
76330
76727
  this.input = input;
76331
76728
  }
76332
76729
  file(id) {
76333
- return path173.join(this.input.workspaceRoot, ".metaproject", "workspaces", id, "activity.jsonl");
76730
+ return path174.join(this.input.workspaceRoot, ".metaproject", "workspaces", id, "activity.jsonl");
76334
76731
  }
76335
76732
  async overview(input) {
76336
76733
  const manifest = await this.input.workspaces.show(input);
@@ -76367,7 +76764,7 @@ class CollaborationService {
76367
76764
  occurredAt: (this.input.now ?? (() => new Date))().toISOString(),
76368
76765
  ...suppliedHandoff ? { handoff: { ...suppliedHandoff, from: actor.subject } } : {}
76369
76766
  });
76370
- await mkdir59(path173.dirname(this.file(manifest.id)), { recursive: true, mode: 448 });
76767
+ await mkdir60(path174.dirname(this.file(manifest.id)), { recursive: true, mode: 448 });
76371
76768
  await appendFile8(this.file(manifest.id), `${JSON.stringify(activity)}
76372
76769
  `, { mode: 384 });
76373
76770
  return normalizeCollaborationResult(activity);
@@ -76421,10 +76818,10 @@ init_workspace_service();
76421
76818
  // src/session/external-slate.ts
76422
76819
  init_fs();
76423
76820
  init_machine_wrap_up();
76424
- import { mkdir as mkdir60, readdir as readdir33, readFile as readFile96 } from "fs/promises";
76425
- import path174 from "path";
76821
+ import { mkdir as mkdir61, readdir as readdir33, readFile as readFile96 } from "fs/promises";
76822
+ import path175 from "path";
76426
76823
  function externalSlatesDir(cwd) {
76427
- return path174.join(cwd, ".keryx", "external-slates");
76824
+ return path175.join(cwd, ".keryx", "external-slates");
76428
76825
  }
76429
76826
  var EXTERNAL_SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
76430
76827
  function assertValidExternalSessionId(externalSessionId) {
@@ -76434,14 +76831,14 @@ function assertValidExternalSessionId(externalSessionId) {
76434
76831
  }
76435
76832
  function externalSlatePath(cwd, externalSessionId) {
76436
76833
  assertValidExternalSessionId(externalSessionId);
76437
- return path174.join(externalSlatesDir(cwd), `${externalSessionId}.json`);
76834
+ return path175.join(externalSlatesDir(cwd), `${externalSessionId}.json`);
76438
76835
  }
76439
76836
  function externalSlateLockPath(cwd, externalSessionId) {
76440
76837
  return `${externalSlatePath(cwd, externalSessionId)}.lock`;
76441
76838
  }
76442
76839
  function externalSlateEvidenceDir(cwd, externalSessionId) {
76443
76840
  assertValidExternalSessionId(externalSessionId);
76444
- return path174.join(externalSlatesDir(cwd), externalSessionId);
76841
+ return path175.join(externalSlatesDir(cwd), externalSessionId);
76445
76842
  }
76446
76843
  async function readExternalSlate(cwd, externalSessionId) {
76447
76844
  try {
@@ -76454,7 +76851,7 @@ async function readExternalSlate(cwd, externalSessionId) {
76454
76851
  }
76455
76852
  }
76456
76853
  async function writeExternalSlate(cwd, externalSessionId, update) {
76457
- await mkdir60(externalSlatesDir(cwd), { recursive: true });
76854
+ await mkdir61(externalSlatesDir(cwd), { recursive: true });
76458
76855
  return withFileLock(externalSlateLockPath(cwd, externalSessionId), async () => {
76459
76856
  const prev = await readExternalSlate(cwd, externalSessionId);
76460
76857
  const next = update(prev);
@@ -76480,7 +76877,7 @@ function isExternalSlateStale(slate, now = () => new Date) {
76480
76877
  return now().getTime() - lastWriteMs > DEFAULT_LOCK_STALE_MS;
76481
76878
  }
76482
76879
  async function closeExternalSlate(cwd, externalSessionId, trigger, now = () => new Date) {
76483
- await mkdir60(externalSlatesDir(cwd), { recursive: true });
76880
+ await mkdir61(externalSlatesDir(cwd), { recursive: true });
76484
76881
  return withFileLock(externalSlateLockPath(cwd, externalSessionId), async () => {
76485
76882
  const external = await readExternalSlate(cwd, externalSessionId);
76486
76883
  if (!external || external.closedAt !== undefined)
@@ -77086,7 +77483,7 @@ function buildToolRegistry() {
77086
77483
  // src/mcp/resources.ts
77087
77484
  init_fs();
77088
77485
  init_contained_read();
77089
- import path175 from "path";
77486
+ import path176 from "path";
77090
77487
  import { lstat as lstat5, readdir as readdir34, realpath as realpath9, stat as stat18 } from "fs/promises";
77091
77488
  var URI_PREFIX = "metaproject://";
77092
77489
  function mimeForPath(filePath) {
@@ -77099,13 +77496,13 @@ function mimeForPath(filePath) {
77099
77496
  return "text/plain";
77100
77497
  }
77101
77498
  function dataRoot3(cwd) {
77102
- return path175.join(cwd, ".metaproject", "data");
77499
+ return path176.join(cwd, ".metaproject", "data");
77103
77500
  }
77104
77501
  function wikiRoot(cwd) {
77105
- return path175.join(cwd, ".metaproject", "wiki");
77502
+ return path176.join(cwd, ".metaproject", "wiki");
77106
77503
  }
77107
77504
  function memoryRoot2(cwd) {
77108
- return path175.join(cwd, ".metaproject", "memory");
77505
+ return path176.join(cwd, ".metaproject", "memory");
77109
77506
  }
77110
77507
  var MAX_RESOURCE_BYTES = 8 * 1024 * 1024;
77111
77508
  async function walkFiles(root, ownerRoot = root, ancestors = new Set) {
@@ -77131,7 +77528,7 @@ async function walkFiles(root, ownerRoot = root, ancestors = new Set) {
77131
77528
  return [];
77132
77529
  }
77133
77530
  for (const entry of entries) {
77134
- const full = path175.join(root, entry.name);
77531
+ const full = path176.join(root, entry.name);
77135
77532
  const info = await stat18(full).catch(() => null);
77136
77533
  if (!info || !isPathInside(ownerReal, await realpath9(full).catch(() => "")))
77137
77534
  continue;
@@ -77159,9 +77556,9 @@ async function listArtifacts(cwd) {
77159
77556
  if (!moduleEntry.isDirectory()) {
77160
77557
  continue;
77161
77558
  }
77162
- const artifactsDir = path175.join(base, moduleEntry.name, "artifacts");
77559
+ const artifactsDir = path176.join(base, moduleEntry.name, "artifacts");
77163
77560
  for (const file of await walkFiles(artifactsDir)) {
77164
- const rel = toPosix(path175.relative(artifactsDir, file));
77561
+ const rel = toPosix(path176.relative(artifactsDir, file));
77165
77562
  const relPath = `${moduleEntry.name}/${rel}`;
77166
77563
  listings.push({
77167
77564
  uri: `${URI_PREFIX}artifacts/${relPath}`,
@@ -77175,7 +77572,7 @@ async function listArtifacts(cwd) {
77175
77572
  async function listUnderRoot(cwd, cls, root) {
77176
77573
  const listings = [];
77177
77574
  for (const file of await walkFiles(root)) {
77178
- const rel = toPosix(path175.relative(root, file));
77575
+ const rel = toPosix(path176.relative(root, file));
77179
77576
  listings.push({
77180
77577
  uri: `${URI_PREFIX}${cls}/${rel}`,
77181
77578
  name: rel,
@@ -77225,12 +77622,12 @@ function resolveConfined(cwd, cls, relPath) {
77225
77622
  if (moduleName.includes("..") || moduleName.length === 0) {
77226
77623
  return null;
77227
77624
  }
77228
- const root = path175.join(dataRoot3(cwd), moduleName, "artifacts");
77229
- const absolute = path175.resolve(root, rest);
77625
+ const root = path176.join(dataRoot3(cwd), moduleName, "artifacts");
77626
+ const absolute = path176.resolve(root, rest);
77230
77627
  return isPathInside(root, absolute) ? { root, absolute } : null;
77231
77628
  }
77232
77629
  const root = cls === "wiki" ? wikiRoot(cwd) : memoryRoot2(cwd);
77233
- const absolute = path175.resolve(root, relPath);
77630
+ const absolute = path176.resolve(root, relPath);
77234
77631
  return isPathInside(root, absolute) ? { root, absolute } : null;
77235
77632
  }
77236
77633
  async function readResource(cwd, roots, uri) {
@@ -77441,7 +77838,7 @@ async function serveMcpCommand(args = [], cwd = process.cwd()) {
77441
77838
  return;
77442
77839
  }
77443
77840
  const http = args.includes("--http");
77444
- const projectRoot = path177.resolve(resolveServeRoot(optionValue(args, "--cwd"), cwd, process.env));
77841
+ const projectRoot = path178.resolve(resolveServeRoot(optionValue(args, "--cwd"), cwd, process.env));
77445
77842
  try {
77446
77843
  await serveMcp({ cwd: projectRoot, http });
77447
77844
  } catch (error) {
@@ -77545,14 +77942,14 @@ function printMcpHelp() {
77545
77942
  // src/commands/status.ts
77546
77943
  init_fs();
77547
77944
  init_json();
77548
- import path178 from "path";
77945
+ import path179 from "path";
77549
77946
  async function statusCommand(args = []) {
77550
77947
  if (args.includes("--help") || args.includes("-h")) {
77551
77948
  printHelp16();
77552
77949
  return;
77553
77950
  }
77554
- const root = path178.join(process.cwd(), ".metaproject");
77555
- const manifestPath = path178.join(root, "metaproject.json");
77951
+ const root = path179.join(process.cwd(), ".metaproject");
77952
+ const manifestPath = path179.join(root, "metaproject.json");
77556
77953
  if (!await pathExists(root)) {
77557
77954
  console.log("Metaproject: not initialized");
77558
77955
  console.log("Run: keryx init");
@@ -77592,7 +77989,7 @@ Reports the workspace root and one enabled/disabled line per module. Use
77592
77989
 
77593
77990
  // src/commands/harness.ts
77594
77991
  import { readFileSync as readFileSync8, writeFileSync as writeFileSync8 } from "fs";
77595
- import path181 from "path";
77992
+ import path182 from "path";
77596
77993
  import { randomUUID as randomUUID20 } from "crypto";
77597
77994
 
77598
77995
  // src/security/harness-scan.ts
@@ -78665,7 +79062,7 @@ ${command.argv.join(" ")}`);
78665
79062
  }
78666
79063
 
78667
79064
  // src/harness/process/sandbox/profile.ts
78668
- import path179 from "path";
79065
+ import path180 from "path";
78669
79066
  var DEFAULT_SECRET_SUBPATHS = [
78670
79067
  ".ssh",
78671
79068
  ".gnupg",
@@ -78690,7 +79087,7 @@ function defaultReadDenyList(home) {
78690
79087
  if (!home) {
78691
79088
  return [];
78692
79089
  }
78693
- return DEFAULT_SECRET_SUBPATHS.map((sub) => path179.join(home, sub));
79090
+ return DEFAULT_SECRET_SUBPATHS.map((sub) => path180.join(home, sub));
78694
79091
  }
78695
79092
  function defaultSandboxProfile(cwd, tmpDir, home) {
78696
79093
  return {
@@ -78710,10 +79107,10 @@ function dedupe3(values) {
78710
79107
  init_config_dir();
78711
79108
  init_shell_config();
78712
79109
  import { existsSync as existsSync27 } from "fs";
78713
- import path180 from "path";
79110
+ import path181 from "path";
78714
79111
  var SECRET_KEY_PATTERN2 = /api[_-]?key|secret|token|password|credential/i;
78715
79112
  function sandboxConfigPath(dir) {
78716
- return path180.join(path180.dirname(shellConfigPath(dir)), "sandbox.json");
79113
+ return path181.join(path181.dirname(shellConfigPath(dir)), "sandbox.json");
78717
79114
  }
78718
79115
  function isShellDefault(v) {
78719
79116
  return v === "off" || v === "workspace" || v === "strict" || v === "1";
@@ -79039,7 +79436,7 @@ init_slate();
79039
79436
  init_paths();
79040
79437
  init_shell_config();
79041
79438
  import { realpathSync as realpathSync3 } from "fs";
79042
- import { homedir as homedir4, tmpdir as tmpdir2 } from "os";
79439
+ import { homedir as homedir5, tmpdir as tmpdir2 } from "os";
79043
79440
 
79044
79441
  // src/harness/extension/registry.ts
79045
79442
  function registerExtension(input) {
@@ -79297,7 +79694,7 @@ function canonicalPath(p) {
79297
79694
  }
79298
79695
  function buildDefaultShellAdapter(cwd, env, profileOverride) {
79299
79696
  const real = new RealProcessAdapter({ allowRealSubprocess: true });
79300
- let profile = profileOverride ?? defaultSandboxProfile(canonicalPath(cwd), canonicalPath(tmpdir2()), homedir4());
79697
+ let profile = profileOverride ?? defaultSandboxProfile(canonicalPath(cwd), canonicalPath(tmpdir2()), homedir5());
79301
79698
  if (profileOverride === undefined && env.KERYX_DANGEROUSLY_DISABLE_SANDBOX === "1") {
79302
79699
  profile = { ...profile, mode: "danger-full-access", required: false };
79303
79700
  }
@@ -79811,7 +80208,7 @@ async function harnessExec(args, deps) {
79811
80208
  let closeNetwork = async () => {};
79812
80209
  let netDecisions;
79813
80210
  if (restrictedDomains !== undefined && deps?.processAdapter === undefined) {
79814
- const baseProfile = defaultSandboxProfile(canonicalPath(cwd), canonicalPath(tmpdir2()), homedir4());
80211
+ const baseProfile = defaultSandboxProfile(canonicalPath(cwd), canonicalPath(tmpdir2()), homedir5());
79815
80212
  const net = await setupNetworkRun({
79816
80213
  ...baseProfile,
79817
80214
  network: "restricted",
@@ -79832,7 +80229,7 @@ async function harnessExec(args, deps) {
79832
80229
  env: commandEnv,
79833
80230
  cwd
79834
80231
  };
79835
- const worktreeRoot = path181.parse(path181.resolve(cwd, commandPath)).root || cwd;
80232
+ const worktreeRoot = path182.parse(path182.resolve(cwd, commandPath)).root || cwd;
79836
80233
  const budget = {
79837
80234
  reservationId: idSeq(),
79838
80235
  maxRuntimeMs: maxRuntimeMs ?? EXEC_DEFAULT_RUNTIME_MS
@@ -80433,7 +80830,7 @@ function createAskUserTool(ask) {
80433
80830
 
80434
80831
  // src/harness/process/shell-spawn.ts
80435
80832
  import { realpathSync as realpathSync5 } from "fs";
80436
- import { homedir as homedir5, tmpdir as tmpdir3 } from "os";
80833
+ import { homedir as homedir6, tmpdir as tmpdir3 } from "os";
80437
80834
  init_providers();
80438
80835
  function resolveShellSandboxMode(env, sandboxConfigDir) {
80439
80836
  if (env.KERYX_DANGEROUSLY_DISABLE_SANDBOX === "1")
@@ -80463,7 +80860,7 @@ function rootsFromEnv(name, env) {
80463
80860
  const raw = env[name];
80464
80861
  if (!raw)
80465
80862
  return [];
80466
- return raw.split(",").map((p) => p.trim()).filter((p) => p.length > 0).map((p) => canonical(p.startsWith("~/") ? p.replace(/^~/, homedir5()) : p));
80863
+ return raw.split(",").map((p) => p.trim()).filter((p) => p.length > 0).map((p) => canonical(p.startsWith("~/") ? p.replace(/^~/, homedir6()) : p));
80467
80864
  }
80468
80865
  function extraWritableRoots(env) {
80469
80866
  return rootsFromEnv("KERYX_SANDBOX_ALLOW_WRITE", env);
@@ -80472,7 +80869,7 @@ function extraReadDenyRoots(env) {
80472
80869
  return rootsFromEnv("KERYX_SANDBOX_READ_DENY", env);
80473
80870
  }
80474
80871
  function shellSandboxProfile(root, mode, env) {
80475
- const base = defaultSandboxProfile(canonical(root), canonical(tmpdir3()), canonical(homedir5()));
80872
+ const base = defaultSandboxProfile(canonical(root), canonical(tmpdir3()), canonical(homedir6()));
80476
80873
  const writableRoots = [...base.writableRoots, ...extraWritableRoots(env)];
80477
80874
  const readDenyList = [...base.readDenyList, ...extraReadDenyRoots(env)];
80478
80875
  const domains = resolveAllowedDomains(env, root);
@@ -81900,7 +82297,7 @@ class SandboxedWebTransport {
81900
82297
 
81901
82298
  // src/harness/web/web-worker-runner.ts
81902
82299
  import { existsSync as existsSync29 } from "fs";
81903
- import { homedir as homedir6 } from "os";
82300
+ import { homedir as homedir7 } from "os";
81904
82301
  var WORKER_TIMEOUT_MS = 1e4;
81905
82302
  var MAX_WORKER_OUTPUT_BYTES = 192000;
81906
82303
  var WORKER_SOURCE = String.raw`
@@ -81998,7 +82395,7 @@ class SystemWebWorkerRunner {
81998
82395
  platform;
81999
82396
  constructor(options = {}) {
82000
82397
  this.workspace = options.workspace ?? process.cwd();
82001
- this.home = options.home ?? homedir6();
82398
+ this.home = options.home ?? homedir7();
82002
82399
  this.platform = options.platform ?? process.platform;
82003
82400
  }
82004
82401
  async run(request, signal) {
@@ -82064,7 +82461,7 @@ class SystemWebWorkerRunner {
82064
82461
  }
82065
82462
  }
82066
82463
  function createSystemWebWorkerRunner() {
82067
- return new SystemWebWorkerRunner({ workspace: process.cwd(), home: homedir6() });
82464
+ return new SystemWebWorkerRunner({ workspace: process.cwd(), home: homedir7() });
82068
82465
  }
82069
82466
 
82070
82467
  // src/harness/tool/builtin/web-fetch-tool.ts
@@ -82216,7 +82613,7 @@ function workspaceReadTool(cwd) {
82216
82613
  // src/harness/tool/builtin/workspace-lifecycle-tool.ts
82217
82614
  import { randomUUID as randomUUID22 } from "crypto";
82218
82615
  import { writeFile as writeFile52 } from "fs/promises";
82219
- import path182 from "path";
82616
+ import path183 from "path";
82220
82617
  init_slate();
82221
82618
  init_store3();
82222
82619
  init_guard2();
@@ -82362,7 +82759,7 @@ function workspaceProposeTool(cwd, getSessionDir) {
82362
82759
  return { output: `workspace_propose: unrecognized 'kind' \u2014 expected one of: ${PROPOSAL_KINDS.join(", ")}`, isError: true };
82363
82760
  }
82364
82761
  const explicitSessionId = typeof input.sessionId === "string" && input.sessionId.length > 0 ? input.sessionId : undefined;
82365
- const sessionRef = explicitSessionId ?? path182.basename(getSessionDir() ?? "");
82762
+ const sessionRef = explicitSessionId ?? path183.basename(getSessionDir() ?? "");
82366
82763
  if (sessionRef.length === 0) {
82367
82764
  return { output: "workspace_propose: no 'sessionId' given and no active session in this run", isError: true };
82368
82765
  }
@@ -82540,7 +82937,7 @@ init_shell_config();
82540
82937
  init_command_risk();
82541
82938
  init_shell_syntax();
82542
82939
  import { existsSync as existsSync31 } from "fs";
82543
- import path183 from "path";
82940
+ import path184 from "path";
82544
82941
  import { createHash as createHash42 } from "crypto";
82545
82942
  var PREFIX_BANNED = new Set([
82546
82943
  "sh",
@@ -82745,7 +83142,7 @@ function emptyShellPermissions() {
82745
83142
  return { allow: [] };
82746
83143
  }
82747
83144
  function shellPermissionsPath(dir) {
82748
- return path183.join(path183.dirname(shellConfigPath(dir)), "permissions.json");
83145
+ return path184.join(path184.dirname(shellConfigPath(dir)), "permissions.json");
82749
83146
  }
82750
83147
  function loadShellPermissionsWithAudit(dir) {
82751
83148
  try {
@@ -82783,7 +83180,7 @@ function loadShellPermissions(dir) {
82783
83180
  function saveShellPermissions(perms, dir, options = {}) {
82784
83181
  try {
82785
83182
  const file = shellPermissionsPath(dir);
82786
- ensureKeryxConfigDir(path183.dirname(file));
83183
+ ensureKeryxConfigDir(path184.dirname(file));
82787
83184
  const cleaned = Array.from(new Set(perms.allow.map((p) => p.trim()).filter((p) => p.length > 0)));
82788
83185
  const body = {
82789
83186
  allow: options.skipValidation === true ? cleaned : cleaned.filter((p) => validateShellPattern(p).ok)
@@ -83222,12 +83619,12 @@ function remoteRequest(providerId, endpoint, query, key) {
83222
83619
  // src/lib/search-config.ts
83223
83620
  init_config_dir();
83224
83621
  import { existsSync as existsSync32 } from "fs";
83225
- import path184 from "path";
83622
+ import path185 from "path";
83226
83623
  function searchConfigPath(dir) {
83227
- return path184.join(keryxConfigDir(dir), "search-providers.json");
83624
+ return path185.join(keryxConfigDir(dir), "search-providers.json");
83228
83625
  }
83229
83626
  function searchCredentialPath(dir) {
83230
- return path184.join(keryxConfigDir(dir), "search-credentials.json");
83627
+ return path185.join(keryxConfigDir(dir), "search-credentials.json");
83231
83628
  }
83232
83629
  function readJson5(file) {
83233
83630
  try {
@@ -83356,7 +83753,7 @@ function createDefaultSearchProviderController(configDir) {
83356
83753
  import { createHash as createHash43, randomUUID as randomUUID24 } from "crypto";
83357
83754
  import { mkdtemp, rm as rm13 } from "fs/promises";
83358
83755
  import { tmpdir as tmpdir4 } from "os";
83359
- import path185 from "path";
83756
+ import path186 from "path";
83360
83757
  init_metaproject_adapter();
83361
83758
  init_ledger();
83362
83759
  init_orchestrate();
@@ -83619,8 +84016,8 @@ function createSpawnSubagentTool(deps) {
83619
84016
  let dispatchId = "";
83620
84017
  let closing = false;
83621
84018
  try {
83622
- ephemeralDir = await mkdtemp(path185.join(tmpdir4(), "keryx-subagent-slate-"));
83623
- dispatchId = path185.basename(ephemeralDir);
84019
+ ephemeralDir = await mkdtemp(path186.join(tmpdir4(), "keryx-subagent-slate-"));
84020
+ dispatchId = path186.basename(ephemeralDir);
83624
84021
  openedChildSlate = await openSlate({
83625
84022
  dir: ephemeralDir,
83626
84023
  cwd,
@@ -83861,9 +84258,9 @@ ${boundSummary(folded.text)}`,
83861
84258
  // src/harness/run-external-factory.ts
83862
84259
  import { randomUUID as randomUUID25 } from "crypto";
83863
84260
  import { execFile as execFile4 } from "child_process";
83864
- import { mkdir as mkdir61 } from "fs/promises";
84261
+ import { mkdir as mkdir62 } from "fs/promises";
83865
84262
  import { tmpdir as tmpdir6 } from "os";
83866
- import path187 from "path";
84263
+ import path188 from "path";
83867
84264
  import { promisify as promisify4 } from "util";
83868
84265
 
83869
84266
  // src/harness/child/git-worktree-port.ts
@@ -84115,7 +84512,7 @@ function readRuntimeBlock(dispatch) {
84115
84512
  // src/harness/external/runtime.ts
84116
84513
  import { mkdtemp as mkdtemp2, rm as rm14, writeFile as writeFile53 } from "fs/promises";
84117
84514
  import { tmpdir as tmpdir5 } from "os";
84118
- import path186 from "path";
84515
+ import path187 from "path";
84119
84516
 
84120
84517
  // src/harness/external/prompt.ts
84121
84518
  var EXTERNAL_RUNTIME_DIRECTIVE = [
@@ -85158,8 +85555,8 @@ async function prepareResultSchema() {
85158
85555
  try {
85159
85556
  const schema = await loadSchema("subagent-result");
85160
85557
  const schemaText = JSON.stringify(schema, null, 2);
85161
- schemaDir = await mkdtemp2(path186.join(tmpdir5(), "keryx-external-result-schema-"));
85162
- const schemaPath = path186.join(schemaDir, "subagent-result.schema.json");
85558
+ schemaDir = await mkdtemp2(path187.join(tmpdir5(), "keryx-external-result-schema-"));
85559
+ const schemaPath = path187.join(schemaDir, "subagent-result.schema.json");
85163
85560
  await writeFile53(schemaPath, schemaText, "utf8");
85164
85561
  return { ok: true, schema, schemaText, schemaPath, schemaDir };
85165
85562
  } catch (error) {
@@ -85386,7 +85783,7 @@ async function defaultWorkingDiff(cwd) {
85386
85783
  function ensuringParentDir(port, worktreesDir) {
85387
85784
  return {
85388
85785
  async create(worktreeId) {
85389
- await mkdir61(worktreesDir, { recursive: true });
85786
+ await mkdir62(worktreesDir, { recursive: true });
85390
85787
  return port.create(worktreeId);
85391
85788
  },
85392
85789
  remove: (worktreeId) => port.remove(worktreeId),
@@ -85416,7 +85813,7 @@ async function createRunExternal(options) {
85416
85813
  }
85417
85814
  const config = gate.config;
85418
85815
  const idSeq = options.idSeq ?? (() => randomUUID25());
85419
- const worktreesDir = options.worktreesDir ?? path187.join(tmpdir6(), "keryx-external-worktrees");
85816
+ const worktreesDir = options.worktreesDir ?? path188.join(tmpdir6(), "keryx-external-worktrees");
85420
85817
  const spawn = options.spawn ?? createBunSpawnPort();
85421
85818
  const worktree = options.worktree ?? ensuringParentDir(createGitWorktreePort({ repoRoot: options.cwd, worktreesDir }), worktreesDir);
85422
85819
  const readWorkingDiff = options.readWorkingDiff ?? (() => defaultWorkingDiff(options.cwd));
@@ -85611,12 +86008,15 @@ async function approveExternalSpawn(request) {
85611
86008
  }
85612
86009
  }
85613
86010
 
86011
+ // src/commands/shell.ts
86012
+ init_caller_session();
86013
+
85614
86014
  // src/lib/statusbar.ts
85615
- import { homedir as homedir7 } from "os";
86015
+ import { homedir as homedir8 } from "os";
85616
86016
  var ESC = "\x1B";
85617
86017
  var CSI = `${ESC}[`;
85618
86018
  function collapseHome(path) {
85619
- const home = homedir7();
86019
+ const home = homedir8();
85620
86020
  if (home.length > 0 && (path === home || path.startsWith(`${home}/`))) {
85621
86021
  return `~${path.slice(home.length)}`;
85622
86022
  }
@@ -85726,7 +86126,7 @@ class LiveMarkdownBlock {
85726
86126
  // src/tui/theme.ts
85727
86127
  init_config_dir();
85728
86128
  import { existsSync as existsSync33 } from "fs";
85729
- import path188 from "path";
86129
+ import path189 from "path";
85730
86130
  var THEME_NAMES = ["groknight", "grokday", "tokyonight", "keryx"];
85731
86131
  var THEME_IDS = ["auto", ...THEME_NAMES];
85732
86132
  var DEFAULT_THEME_ID = "groknight";
@@ -85856,7 +86256,7 @@ function onThemeChange(listener) {
85856
86256
  };
85857
86257
  }
85858
86258
  function tuiConfigPath(dir) {
85859
- return path188.join(keryxConfigDir(dir), "tui.json");
86259
+ return path189.join(keryxConfigDir(dir), "tui.json");
85860
86260
  }
85861
86261
  function loadPersistedThemeId(dir) {
85862
86262
  try {
@@ -85913,7 +86313,7 @@ init_workspace_service();
85913
86313
  init_service8();
85914
86314
  init_store2();
85915
86315
  init_fs();
85916
- import path189 from "path";
86316
+ import path190 from "path";
85917
86317
  import { readFile as readFile98 } from "fs/promises";
85918
86318
  var POSITIVE_INTEGER = /^[1-9][0-9]*$/;
85919
86319
  var DEFAULT_AUTO_GOAL_ROUNDS = 8;
@@ -85989,7 +86389,7 @@ function getFlowService() {
85989
86389
  async function autoProvisionFlow(cwd, goalText) {
85990
86390
  const service = getFlowService();
85991
86391
  const result = await service.init({ cwd, title: goalText });
85992
- const acFile = path189.join(cwd, result.dir, "acceptance-criteria.md");
86392
+ const acFile = path190.join(cwd, result.dir, "acceptance-criteria.md");
85993
86393
  await writeFileAtomic(acFile, [
85994
86394
  "# Acceptance Criteria",
85995
86395
  "",
@@ -87540,7 +87940,7 @@ init_proposal_lifecycle();
87540
87940
  init_workspace_service();
87541
87941
  import { randomUUID as randomUUID27 } from "crypto";
87542
87942
  import { readdir as readdir36, stat as stat19 } from "fs/promises";
87543
- import path190 from "path";
87943
+ import path191 from "path";
87544
87944
 
87545
87945
  // src/sac/lifecycle-flag.ts
87546
87946
  init_store();
@@ -87699,7 +88099,7 @@ async function readExternalUnboundCandidates(cwd) {
87699
88099
  }
87700
88100
  return groups;
87701
88101
  }, []));
87702
- const metaPath = path190.join(extDir, `${id}.json`);
88102
+ const metaPath = path191.join(extDir, `${id}.json`);
87703
88103
  if (await pathExists(unboundDismissedReceiptPath(metaPath)))
87704
88104
  continue;
87705
88105
  candidates.push({
@@ -87713,7 +88113,7 @@ async function readExternalUnboundCandidates(cwd) {
87713
88113
  return candidates;
87714
88114
  }
87715
88115
  async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
87716
- const evidenceDir = path190.join(externalSlatesDir(cwd), externalSessionId);
88116
+ const evidenceDir = path191.join(externalSlatesDir(cwd), externalSessionId);
87717
88117
  let entries;
87718
88118
  try {
87719
88119
  entries = (await readdir36(evidenceDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
@@ -87722,7 +88122,7 @@ async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
87722
88122
  }
87723
88123
  entries.sort();
87724
88124
  for (let i = entries.length - 1;i >= 0; i--) {
87725
- const evidencePath = path190.join(evidenceDir, entries[i]);
88125
+ const evidencePath = path191.join(evidenceDir, entries[i]);
87726
88126
  if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
87727
88127
  continue;
87728
88128
  const result = readConfigFile(evidencePath);
@@ -87765,7 +88165,7 @@ var SESSION_ATTRIBUTION_SLACK_MS = 5 * 60000;
87765
88165
  var MTIME_CLOCK_SKEW_TOLERANCE_MS = 5000;
87766
88166
  async function collectReceiptTargets(cwd, owner) {
87767
88167
  const targets = new Set;
87768
- const workspacesDir = path190.join(cwd, ".metaproject", "workspaces");
88168
+ const workspacesDir = path191.join(cwd, ".metaproject", "workspaces");
87769
88169
  let workspaceIds;
87770
88170
  try {
87771
88171
  workspaceIds = (await readdir36(workspacesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
@@ -87773,7 +88173,7 @@ async function collectReceiptTargets(cwd, owner) {
87773
88173
  return targets;
87774
88174
  }
87775
88175
  for (const workspaceId of workspaceIds) {
87776
- const receiptsDir = path190.join(workspacesDir, workspaceId, `${owner}-write-receipts`);
88176
+ const receiptsDir = path191.join(workspacesDir, workspaceId, `${owner}-write-receipts`);
87777
88177
  let files;
87778
88178
  try {
87779
88179
  files = (await readdir36(receiptsDir)).filter((name) => name.endsWith(".json"));
@@ -87781,7 +88181,7 @@ async function collectReceiptTargets(cwd, owner) {
87781
88181
  continue;
87782
88182
  }
87783
88183
  for (const file of files) {
87784
- const result = readConfigFile(path190.join(receiptsDir, file));
88184
+ const result = readConfigFile(path191.join(receiptsDir, file));
87785
88185
  if (!result.ok)
87786
88186
  continue;
87787
88187
  try {
@@ -87824,7 +88224,7 @@ async function findSkillFiles(dir) {
87824
88224
  }
87825
88225
  const found = [];
87826
88226
  for (const entry of entries) {
87827
- const full = path190.join(dir, entry.name);
88227
+ const full = path191.join(dir, entry.name);
87828
88228
  if (entry.isDirectory()) {
87829
88229
  found.push(...await findSkillFiles(full));
87830
88230
  } else if (entry.isFile() && entry.name === "SKILL.md") {
@@ -87883,10 +88283,10 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
87883
88283
  changedAt: attribution.changedAt
87884
88284
  });
87885
88285
  }
87886
- const sacSkillsDir = path190.join(cwd, ".metaproject", "project-skills", "sac");
88286
+ const sacSkillsDir = path191.join(cwd, ".metaproject", "project-skills", "sac");
87887
88287
  const skillFiles = await findSkillFiles(sacSkillsDir);
87888
88288
  for (const absolutePath of skillFiles) {
87889
- const targetRef = path190.relative(path190.join(cwd, ".metaproject"), absolutePath).split(path190.sep).join("/");
88289
+ const targetRef = path191.relative(path191.join(cwd, ".metaproject"), absolutePath).split(path191.sep).join("/");
87890
88290
  if (skillReceipts.has(targetRef))
87891
88291
  continue;
87892
88292
  const attribution = await attributeToSession(absolutePath, scoped);
@@ -87904,12 +88304,12 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
87904
88304
  return items;
87905
88305
  }
87906
88306
  async function isSlateEngaged(dir) {
87907
- if (await pathExists(path190.join(dir, "slate.json")))
88307
+ if (await pathExists(path191.join(dir, "slate.json")))
87908
88308
  return true;
87909
- if (await pathExists(path190.join(dir, "terminal-state.json")))
88309
+ if (await pathExists(path191.join(dir, "terminal-state.json")))
87910
88310
  return true;
87911
88311
  try {
87912
- const entries = await readdir36(path190.join(dir, "slate-archive"));
88312
+ const entries = await readdir36(path191.join(dir, "slate-archive"));
87913
88313
  return entries.length > 0;
87914
88314
  } catch {
87915
88315
  return false;
@@ -87923,7 +88323,7 @@ async function safeReadSlate(dir) {
87923
88323
  }
87924
88324
  }
87925
88325
  async function readTerminalState(dir) {
87926
- const result = readConfigFile(path190.join(dir, "terminal-state.json"));
88326
+ const result = readConfigFile(path191.join(dir, "terminal-state.json"));
87927
88327
  if (!result.ok) {
87928
88328
  return;
87929
88329
  }
@@ -87947,7 +88347,7 @@ async function resolveUnboundCandidateTarget(cwd, target) {
87947
88347
  if (session === undefined) {
87948
88348
  throw new Error(`No session or evidence path matches "${target}"`);
87949
88349
  }
87950
- const archiveDir = path190.join(sessionDir(session.projectPath, session.id), "slate-archive");
88350
+ const archiveDir = path191.join(sessionDir(session.projectPath, session.id), "slate-archive");
87951
88351
  let entries;
87952
88352
  try {
87953
88353
  entries = (await readdir36(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
@@ -87957,7 +88357,7 @@ async function resolveUnboundCandidateTarget(cwd, target) {
87957
88357
  if (entries.length === 0)
87958
88358
  throw new Error(`No unbound-candidate artifacts for session ${session.id}`);
87959
88359
  entries.sort();
87960
- return path190.join(archiveDir, entries[entries.length - 1]);
88360
+ return path191.join(archiveDir, entries[entries.length - 1]);
87961
88361
  }
87962
88362
  async function dismissUnboundByTarget(cwd, target, reason) {
87963
88363
  const evidencePath = await resolveUnboundCandidateTarget(cwd, target);
@@ -87983,7 +88383,7 @@ async function dismissUnboundCandidate(candidatePath, reason) {
87983
88383
  return { removed: candidatePath, receipt: receiptPath };
87984
88384
  }
87985
88385
  async function readNewestUnboundCandidate(dir) {
87986
- const archiveDir = path190.join(dir, "slate-archive");
88386
+ const archiveDir = path191.join(dir, "slate-archive");
87987
88387
  let entries;
87988
88388
  try {
87989
88389
  entries = (await readdir36(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
@@ -87992,7 +88392,7 @@ async function readNewestUnboundCandidate(dir) {
87992
88392
  }
87993
88393
  entries.sort();
87994
88394
  for (let i = entries.length - 1;i >= 0; i--) {
87995
- const evidencePath = path190.join(archiveDir, entries[i]);
88395
+ const evidencePath = path191.join(archiveDir, entries[i]);
87996
88396
  if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
87997
88397
  continue;
87998
88398
  const result = readConfigFile(evidencePath);
@@ -88022,7 +88422,7 @@ function isFailureOutcome(g) {
88022
88422
  return g.outcome === "error" || g.outcome === "no_credential" || g.outcome === "conflict";
88023
88423
  }
88024
88424
  async function readNewestWrapUpOutcome(dir) {
88025
- const archiveDir = path190.join(dir, "slate-archive");
88425
+ const archiveDir = path191.join(dir, "slate-archive");
88026
88426
  let entries;
88027
88427
  try {
88028
88428
  entries = (await readdir36(archiveDir)).filter((name) => name.endsWith("-wrap-up-outcome.json"));
@@ -88031,7 +88431,7 @@ async function readNewestWrapUpOutcome(dir) {
88031
88431
  }
88032
88432
  entries.sort();
88033
88433
  for (let i = entries.length - 1;i >= 0; i--) {
88034
- const evidencePath = path190.join(archiveDir, entries[i]);
88434
+ const evidencePath = path191.join(archiveDir, entries[i]);
88035
88435
  const result = readConfigFile(evidencePath);
88036
88436
  if (!result.ok) {
88037
88437
  continue;
@@ -91787,10 +92187,10 @@ function describeElicitationPrompt(tool, inputJson) {
91787
92187
  // src/lib/permission-mode-config.ts
91788
92188
  init_permission_mode();
91789
92189
  init_config_dir();
91790
- import path191 from "path";
92190
+ import path192 from "path";
91791
92191
  var EMPTY3 = { schemaVersion: 1, projects: {} };
91792
92192
  function permissionModeConfigPath(dir) {
91793
- return path191.join(keryxConfigDir(dir), "permission-mode.json");
92193
+ return path192.join(keryxConfigDir(dir), "permission-mode.json");
91794
92194
  }
91795
92195
  function withRegistryLock2(dir, fn) {
91796
92196
  return withFileLock2(`${permissionModeConfigPath(dir)}.lock`, fn, {
@@ -92308,7 +92708,7 @@ function showComposerChoice(otui, r, dock, request) {
92308
92708
  // src/lib/version-check.ts
92309
92709
  init_config_dir();
92310
92710
  init_fs();
92311
- import path192 from "path";
92711
+ import path193 from "path";
92312
92712
  var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
92313
92713
  var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
92314
92714
  var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
@@ -92510,7 +92910,7 @@ async function checkVersion(options) {
92510
92910
  const now = options.now ?? Date.now;
92511
92911
  const timestamp = now();
92512
92912
  const configDir = ensureKeryxConfigDir(options.cacheDir);
92513
- const cacheFile = path192.join(configDir, "version-check.json");
92913
+ const cacheFile = path193.join(configDir, "version-check.json");
92514
92914
  const cache = parseCache(cacheFile);
92515
92915
  if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
92516
92916
  return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
@@ -100895,6 +101295,7 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
100895
101295
  };
100896
101296
  const makeAgentDeps = async (sel, getSlateSession) => {
100897
101297
  const getSessionDir = () => getSlateSession()?.dir;
101298
+ exportCallerSession(sel.provider, sel.model);
100898
101299
  const agentProvider = tuiProviderFactory(sel.provider, sel.model, sel.baseUrl);
100899
101300
  let orient;
100900
101301
  try {
@@ -101095,6 +101496,7 @@ Example: keryx shell --provider ollama --model llama3.1:latest`);
101095
101496
  const cwdLabel = collapseHome(process.cwd());
101096
101497
  printHeader("keryx", `${provider}/${model}${baseUrl !== undefined ? ` (${baseUrl})` : ""}${modeLabel} \xB7 ${cwdLabel}`);
101097
101498
  if (agentMode) {
101499
+ exportCallerSession(provider, model);
101098
101500
  const agentProvider = baseFactory(provider, model, baseUrl);
101099
101501
  let orient;
101100
101502
  try {
@@ -101337,7 +101739,7 @@ Shell:
101337
101739
  init_fs();
101338
101740
  import { readFile as readFile99 } from "fs/promises";
101339
101741
  import { stdin } from "process";
101340
- import path193 from "path";
101742
+ import path194 from "path";
101341
101743
  var MODULES = [
101342
101744
  { name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
101343
101745
  { name: "gdctx", flag: "--no-gdctx", desc: "token-aware command/read output", defaultEnabled: true },
@@ -101383,8 +101785,8 @@ async function modulesCommand(args = []) {
101383
101785
  return;
101384
101786
  }
101385
101787
  const wantsJson = args.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
101386
- const metaprojectRoot = path193.join(process.cwd(), ".metaproject");
101387
- const manifestPath = path193.join(metaprojectRoot, "metaproject.json");
101788
+ const metaprojectRoot = path194.join(process.cwd(), ".metaproject");
101789
+ const manifestPath = path194.join(metaprojectRoot, "metaproject.json");
101388
101790
  if (!await pathExists(manifestPath)) {
101389
101791
  if (wantsJson) {
101390
101792
  console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
@@ -101515,9 +101917,9 @@ import {
101515
101917
  unlinkSync as unlinkSync3,
101516
101918
  writeFileSync as writeFileSync9
101517
101919
  } from "fs";
101518
- import path194 from "path";
101920
+ import path195 from "path";
101519
101921
  function serveCredentialPath(dir) {
101520
- return path194.join(keryxConfigDir(dir), "serve-credentials.json");
101922
+ return path195.join(keryxConfigDir(dir), "serve-credentials.json");
101521
101923
  }
101522
101924
  function constantTimeEqual(a, b) {
101523
101925
  const width = Math.max(a.length, b.length);
@@ -101751,22 +102153,22 @@ class AuthFailureThrottle {
101751
102153
  init_config_dir();
101752
102154
  import { createHash as createHash45 } from "crypto";
101753
102155
  import { existsSync as existsSync35, readdirSync as readdirSync3, rmSync as rmSync2 } from "fs";
101754
- import path195 from "path";
102156
+ import path196 from "path";
101755
102157
  var MAX_TURN_EVENTS = 1e4;
101756
102158
  function turnsRoot(dir) {
101757
- return path195.join(keryxConfigDir(dir), "turns");
102159
+ return path196.join(keryxConfigDir(dir), "turns");
101758
102160
  }
101759
102161
  function turnDir(turnId, dir) {
101760
- return path195.join(turnsRoot(dir), turnId);
102162
+ return path196.join(turnsRoot(dir), turnId);
101761
102163
  }
101762
102164
  function keyPath(project, idempotencyKey, dir) {
101763
102165
  const projectBytes = Buffer.byteLength(project, "utf8");
101764
102166
  const digest = createHash45("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
101765
- return path195.join(turnsRoot(dir), "keys", `${digest}.json`);
102167
+ return path196.join(turnsRoot(dir), "keys", `${digest}.json`);
101766
102168
  }
101767
102169
  function legacyKeyPath(idempotencyKey, dir) {
101768
102170
  const digest = createHash45("sha256").update(idempotencyKey, "utf8").digest("hex");
101769
- return path195.join(turnsRoot(dir), "keys", `${digest}.json`);
102171
+ return path196.join(turnsRoot(dir), "keys", `${digest}.json`);
101770
102172
  }
101771
102173
  function adoptLegacyClaim(project, idempotencyKey, dir) {
101772
102174
  const legacy = legacyKeyPath(idempotencyKey, dir);
@@ -101830,7 +102232,7 @@ function ensureTurnDir(turnId, dir) {
101830
102232
  }
101831
102233
  function createTurnRecord(record, dir) {
101832
102234
  ensureTurnDir(record.turnId, dir);
101833
- writeOwnerOnlyFile(path195.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
102235
+ writeOwnerOnlyFile(path196.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
101834
102236
  `);
101835
102237
  }
101836
102238
  function appendTurnEvent(event, dir, opts) {
@@ -101839,12 +102241,12 @@ function appendTurnEvent(event, dir, opts) {
101839
102241
  }
101840
102242
  const line = JSON.stringify(event);
101841
102243
  try {
101842
- appendOwnerOnlyLine(path195.join(turnDir(event.turnId, dir), "events.jsonl"), line);
102244
+ appendOwnerOnlyLine(path196.join(turnDir(event.turnId, dir), "events.jsonl"), line);
101843
102245
  } catch (error) {
101844
102246
  if (error?.code !== "ENOENT") {
101845
102247
  throw error;
101846
102248
  }
101847
- appendOwnerOnlyLine(path195.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
102249
+ appendOwnerOnlyLine(path196.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
101848
102250
  }
101849
102251
  return true;
101850
102252
  }
@@ -101852,7 +102254,7 @@ function readTurnEvents(turnId, after = -1, dir) {
101852
102254
  if (!isTurnId(turnId)) {
101853
102255
  return { ok: false, reason: "not-a-turn-id" };
101854
102256
  }
101855
- const read = readTurnFile(path195.join(turnDir(turnId, dir), "events.jsonl"));
102257
+ const read = readTurnFile(path196.join(turnDir(turnId, dir), "events.jsonl"));
101856
102258
  if (!read.ok) {
101857
102259
  if (isDefiniteAbsence2(read.reason)) {
101858
102260
  return { ok: true, value: [] };
@@ -101880,7 +102282,7 @@ function readTurnRecord(turnId, dir) {
101880
102282
  if (!isTurnId(turnId)) {
101881
102283
  return { ok: false, reason: "not-a-turn-id" };
101882
102284
  }
101883
- const read = readTurnFile(path195.join(turnDir(turnId, dir), "turn.json"));
102285
+ const read = readTurnFile(path196.join(turnDir(turnId, dir), "turn.json"));
101884
102286
  if (!read.ok) {
101885
102287
  return { ok: false, reason: read.reason };
101886
102288
  }
@@ -101899,7 +102301,7 @@ function finishTurn(turnId, result, dir) {
101899
102301
  if (!record.ok) {
101900
102302
  return false;
101901
102303
  }
101902
- writeOwnerOnlyFile(path195.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
102304
+ writeOwnerOnlyFile(path196.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
101903
102305
  `);
101904
102306
  return true;
101905
102307
  }
@@ -101945,7 +102347,7 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
101945
102347
 
101946
102348
  // src/lib/serve-turn.ts
101947
102349
  import { randomUUID as randomUUID31 } from "crypto";
101948
- import path196 from "path";
102350
+ import path197 from "path";
101949
102351
  init_service();
101950
102352
  var REMOTE_ORIGIN = "remote:http";
101951
102353
  var MAX_PROMPT_CHARS = 32000;
@@ -102014,9 +102416,9 @@ function isUuid(value) {
102014
102416
  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);
102015
102417
  }
102016
102418
  function resolveProject(declared, dir) {
102017
- const wanted = path196.resolve(declared);
102419
+ const wanted = path197.resolve(declared);
102018
102420
  for (const entry of listProjects(dir, () => {})) {
102019
- if (path196.resolve(entry.path) === wanted) {
102421
+ if (path197.resolve(entry.path) === wanted) {
102020
102422
  return { ok: true, project: entry.path };
102021
102423
  }
102022
102424
  }
@@ -103050,9 +103452,9 @@ function printHelp19() {
103050
103452
 
103051
103453
  // src/commands/update.ts
103052
103454
  import { spawn as spawn6 } from "child_process";
103053
- import { chmod as chmod4, mkdir as mkdir62, readFile as readFile100, readdir as readdir37, writeFile as writeFile54 } from "fs/promises";
103455
+ import { chmod as chmod4, mkdir as mkdir63, readFile as readFile100, readdir as readdir37, writeFile as writeFile54 } from "fs/promises";
103054
103456
  import { access as access4, constants as constants2, existsSync as existsSync36 } from "fs";
103055
- import path197 from "path";
103457
+ import path198 from "path";
103056
103458
  import { fileURLToPath as fileURLToPath8 } from "url";
103057
103459
  init_config2();
103058
103460
  init_config();
@@ -103069,8 +103471,8 @@ async function updateCommand(args = []) {
103069
103471
  return;
103070
103472
  }
103071
103473
  const projectRoot = process.cwd();
103072
- const metaprojectRoot = path197.join(projectRoot, ".metaproject");
103073
- banner("keryx update", `Refreshing the .metaproject workspace in ${path197.basename(projectRoot)}/`);
103474
+ const metaprojectRoot = path198.join(projectRoot, ".metaproject");
103475
+ banner("keryx update", `Refreshing the .metaproject workspace in ${path198.basename(projectRoot)}/`);
103074
103476
  if (!await pathExists(metaprojectRoot)) {
103075
103477
  console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
103076
103478
  console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
@@ -103143,7 +103545,7 @@ async function updateCommand(args = []) {
103143
103545
  nextSteps(steps);
103144
103546
  }
103145
103547
  async function previewServiceFiles(projectRoot, options) {
103146
- const metaprojectRoot = path197.join(projectRoot, ".metaproject");
103548
+ const metaprojectRoot = path198.join(projectRoot, ".metaproject");
103147
103549
  const manifestState = await readManifest5(metaprojectRoot);
103148
103550
  const manifest = manifestState.manifest;
103149
103551
  const enableTasks = moduleEnabled2(manifest, "tasks") || !options.noTasks;
@@ -103172,12 +103574,12 @@ async function previewServiceFiles(projectRoot, options) {
103172
103574
  });
103173
103575
  }
103174
103576
  async function refreshServiceFiles(projectRoot, options) {
103175
- const metaprojectRoot = path197.join(projectRoot, ".metaproject");
103577
+ const metaprojectRoot = path198.join(projectRoot, ".metaproject");
103176
103578
  const manifestState = await readManifest5(metaprojectRoot);
103177
103579
  const manifest = manifestState.manifest;
103178
103580
  const recoveredManifest = !manifestState.exists || !manifestState.valid;
103179
103581
  if (manifestState.migrated) {
103180
- await writeFile54(path197.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
103582
+ await writeFile54(path198.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
103181
103583
  `, "utf8");
103182
103584
  }
103183
103585
  const enableGdgraph = moduleEnabled2(manifest, "gdgraph");
@@ -103214,9 +103616,9 @@ async function refreshServiceFiles(projectRoot, options) {
103214
103616
  enableSecurity,
103215
103617
  enableSac
103216
103618
  });
103217
- await writeTextIfChanged4(path197.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
103218
- await writeTextIfChanged4(path197.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
103219
- await writeTextIfChanged4(path197.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
103619
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
103620
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
103621
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
103220
103622
  await writeRoutingEntrypointPair(metaprojectRoot, {
103221
103623
  enableGdgraph,
103222
103624
  enableGdctx,
@@ -103239,7 +103641,7 @@ async function refreshServiceFiles(projectRoot, options) {
103239
103641
  }
103240
103642
  }
103241
103643
  });
103242
- await writeTextIfChanged4(path197.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
103644
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
103243
103645
  enableGdgraph,
103244
103646
  enableGdctx,
103245
103647
  enableGdwiki,
@@ -103251,7 +103653,7 @@ async function refreshServiceFiles(projectRoot, options) {
103251
103653
  enableSecurity,
103252
103654
  data: dashboardData
103253
103655
  }));
103254
- await writeTextIfMissing4(path197.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
103656
+ await writeTextIfMissing4(path198.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
103255
103657
  enableGdgraph,
103256
103658
  enableGdctx,
103257
103659
  enableGdwiki,
@@ -103264,31 +103666,31 @@ async function refreshServiceFiles(projectRoot, options) {
103264
103666
  }));
103265
103667
  if (enableGdgraph) {
103266
103668
  await installGdgraphCoreScripts2(metaprojectRoot);
103267
- await writeTextIfChanged4(path197.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
103268
- await writeTextIfChanged4(path197.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
103269
- await writeTextIfChanged4(path197.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
103669
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
103670
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
103671
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
103270
103672
  await seedAssetsLock(metaprojectRoot);
103271
103673
  if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
103272
103674
  await installManagedHook2(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
103273
103675
  }
103274
103676
  }
103275
103677
  if (enableGdctx) {
103276
- await writeTextIfMissing4(path197.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
103277
- await writeTextIfChanged4(path197.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
103278
- await writeTextIfChanged4(path197.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
103279
- await writeTextIfChanged4(path197.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
103678
+ await writeTextIfMissing4(path198.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
103679
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
103680
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
103681
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
103280
103682
  }
103281
103683
  if (enableGdwiki) {
103282
- await writeTextIfMissing4(path197.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
103283
- await writeTextIfChanged4(path197.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
103284
- await writeTextIfChanged4(path197.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
103684
+ await writeTextIfMissing4(path198.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
103685
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
103686
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
103285
103687
  if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
103286
103688
  await installManagedHook2(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
103287
103689
  }
103288
103690
  }
103289
103691
  if (enableSac) {
103290
- await writeTextIfMissing4(path197.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
103291
- await writeTextIfChanged4(path197.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
103692
+ await writeTextIfMissing4(path198.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
103693
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
103292
103694
  }
103293
103695
  let gdskillsWarnings = [];
103294
103696
  let gdskillsNotices = [];
@@ -103301,25 +103703,25 @@ async function refreshServiceFiles(projectRoot, options) {
103301
103703
  }
103302
103704
  }
103303
103705
  if (enableHealth) {
103304
- await writeTextIfMissing4(path197.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
103305
- await writeTextIfChanged4(path197.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
103306
- await writeTextIfChanged4(path197.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
103307
- await writeTextIfChanged4(path197.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
103706
+ await writeTextIfMissing4(path198.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
103707
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
103708
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
103709
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
103308
103710
  if (manifest.modules?.health?.hooks?.gitPostCommit) {
103309
103711
  await installManagedHook2(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
103310
103712
  }
103311
103713
  }
103312
103714
  if (enableTesting) {
103313
- await writeTextIfMissing4(path197.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
103715
+ await writeTextIfMissing4(path198.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
103314
103716
  postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
103315
103717
  prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
103316
103718
  }));
103317
- await writeTextIfChanged4(path197.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
103318
- await writeTextIfChanged4(path197.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
103319
- await writeTextIfChanged4(path197.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
103719
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
103720
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
103721
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
103320
103722
  if (enableGdwiki) {
103321
- await writeTextIfMissing4(path197.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
103322
- await writeTextIfMissing4(path197.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
103723
+ await writeTextIfMissing4(path198.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
103724
+ await writeTextIfMissing4(path198.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
103323
103725
  }
103324
103726
  if (manifest.modules?.testing?.hooks?.gitPostCommit) {
103325
103727
  await installManagedHook2(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
@@ -103332,24 +103734,24 @@ async function refreshServiceFiles(projectRoot, options) {
103332
103734
  await installManagedHook2(projectRoot, "post-commit", "metaproject-dashboard-post-commit", renderMetaprojectDashboardPostCommitHook());
103333
103735
  }
103334
103736
  if (enableMemory) {
103335
- await writeTextIfMissing4(path197.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
103336
- await writeTextIfMissing4(path197.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
103337
- await writeTextIfChanged4(path197.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
103338
- await writeTextIfChanged4(path197.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
103339
- await writeTextIfChanged4(path197.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
103737
+ await writeTextIfMissing4(path198.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
103738
+ await writeTextIfMissing4(path198.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
103739
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
103740
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
103741
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
103340
103742
  }
103341
103743
  if (enableTasks) {
103342
- await writeTextIfChanged4(path197.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
103343
- await writeTextIfChanged4(path197.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
103344
- await writeTextIfChanged4(path197.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
103345
- await writeTextIfChanged4(path197.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
103346
- await writeTextIfChanged4(path197.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
103347
- await writeTextIfChanged4(path197.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
103744
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
103745
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
103746
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
103747
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
103748
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
103749
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
103348
103750
  }
103349
103751
  if (enableSecurity) {
103350
- await writeTextIfMissing4(path197.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
103351
- await writeTextIfChanged4(path197.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
103352
- await writeTextIfChanged4(path197.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
103752
+ await writeTextIfMissing4(path198.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
103753
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
103754
+ await writeTextIfChanged4(path198.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
103353
103755
  if (manifest.modules?.security?.hooks?.prePush) {
103354
103756
  await installManagedHook2(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
103355
103757
  }
@@ -103402,13 +103804,13 @@ async function refreshServiceFiles(projectRoot, options) {
103402
103804
  };
103403
103805
  }
103404
103806
  async function buildDashboard(projectRoot = process.cwd()) {
103405
- const metaprojectRoot = path197.join(projectRoot, ".metaproject");
103807
+ const metaprojectRoot = path198.join(projectRoot, ".metaproject");
103406
103808
  if (!await pathExists(metaprojectRoot)) {
103407
103809
  throw new Error("Metaproject is not initialized. Run: keryx init");
103408
103810
  }
103409
103811
  const manifest = (await readManifest5(metaprojectRoot)).manifest;
103410
103812
  const data = await collectDashboardData(metaprojectRoot);
103411
- const dashboardPath = path197.join(metaprojectRoot, "keryx-dashboard.html");
103813
+ const dashboardPath = path198.join(metaprojectRoot, "keryx-dashboard.html");
103412
103814
  await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
103413
103815
  enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
103414
103816
  enableGdctx: moduleEnabled2(manifest, "gdctx"),
@@ -103428,7 +103830,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
103428
103830
  if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
103429
103831
  return true;
103430
103832
  }
103431
- const hookPath = path197.join(projectRoot, ".git", "hooks", "post-commit");
103833
+ const hookPath = path198.join(projectRoot, ".git", "hooks", "post-commit");
103432
103834
  if (!await pathExists(hookPath)) {
103433
103835
  return false;
103434
103836
  }
@@ -103448,11 +103850,11 @@ async function collectDashboardData(metaprojectRoot) {
103448
103850
  if (testing) {
103449
103851
  data.testing = testing;
103450
103852
  }
103451
- const wiki = await collectMarkdownPages(path197.join(metaprojectRoot, "wiki"), "wiki");
103853
+ const wiki = await collectMarkdownPages(path198.join(metaprojectRoot, "wiki"), "wiki");
103452
103854
  if (wiki.length > 0) {
103453
103855
  data.wiki = { pages: wiki };
103454
103856
  }
103455
- const memory = await collectMarkdownPages(path197.join(metaprojectRoot, "memory"), "memory");
103857
+ const memory = await collectMarkdownPages(path198.join(metaprojectRoot, "memory"), "memory");
103456
103858
  if (memory.length > 0) {
103457
103859
  data.memory = { entries: memory };
103458
103860
  }
@@ -103467,7 +103869,7 @@ async function collectDashboardData(metaprojectRoot) {
103467
103869
  return data;
103468
103870
  }
103469
103871
  async function collectTasksDashboardData(metaprojectRoot) {
103470
- const flowsRoot = path197.join(metaprojectRoot, "flows");
103872
+ const flowsRoot = path198.join(metaprojectRoot, "flows");
103471
103873
  if (!await pathExists(flowsRoot)) {
103472
103874
  return null;
103473
103875
  }
@@ -103479,7 +103881,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
103479
103881
  }
103480
103882
  const flows = [];
103481
103883
  for (const dir of dirEntries) {
103482
- const flowPath = path197.join(flowsRoot, dir, "flow.json");
103884
+ const flowPath = path198.join(flowsRoot, dir, "flow.json");
103483
103885
  if (!await pathExists(flowPath)) {
103484
103886
  continue;
103485
103887
  }
@@ -103487,7 +103889,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
103487
103889
  const flow = JSON.parse(await readFile100(flowPath, "utf8"));
103488
103890
  const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
103489
103891
  let acTotal = 0;
103490
- const acPath = path197.join(flowsRoot, dir, "acceptance-criteria.md");
103892
+ const acPath = path198.join(flowsRoot, dir, "acceptance-criteria.md");
103491
103893
  if (await pathExists(acPath)) {
103492
103894
  const acContent = await readFile100(acPath, "utf8");
103493
103895
  acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
@@ -103541,7 +103943,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
103541
103943
  "data/testing/context.md"
103542
103944
  ];
103543
103945
  for (const href of staticHrefs) {
103544
- const filePath = path197.join(metaprojectRoot, ...href.split("/"));
103946
+ const filePath = path198.join(metaprojectRoot, ...href.split("/"));
103545
103947
  if (!await pathExists(filePath)) {
103546
103948
  continue;
103547
103949
  }
@@ -103558,7 +103960,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
103558
103960
  return docs;
103559
103961
  }
103560
103962
  async function collectHealthDashboardData(metaprojectRoot) {
103561
- const reportPath = path197.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
103963
+ const reportPath = path198.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
103562
103964
  if (!await pathExists(reportPath)) {
103563
103965
  return;
103564
103966
  }
@@ -103667,8 +104069,8 @@ function metricToScope(metric) {
103667
104069
  };
103668
104070
  }
103669
104071
  async function collectGraphDashboardData(metaprojectRoot) {
103670
- const nodesPath = path197.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
103671
- const edgesPath = path197.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
104072
+ const nodesPath = path198.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
104073
+ const edgesPath = path198.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
103672
104074
  if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
103673
104075
  return;
103674
104076
  }
@@ -103719,8 +104121,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
103719
104121
  };
103720
104122
  }
103721
104123
  async function collectTestingDashboardData(metaprojectRoot) {
103722
- const reportPath = path197.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
103723
- const contextPath = path197.join(metaprojectRoot, "data", "testing", "context.md");
104124
+ const reportPath = path198.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
104125
+ const contextPath = path198.join(metaprojectRoot, "data", "testing", "context.md");
103724
104126
  if (await pathExists(reportPath)) {
103725
104127
  const report = JSON.parse(await readFile100(reportPath, "utf8"));
103726
104128
  const totalTests = numberOrUndefined(report.total);
@@ -103749,7 +104151,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
103749
104151
  const files = await listMarkdownFiles(root);
103750
104152
  const pages = [];
103751
104153
  for (const filePath of files.slice(0, 40)) {
103752
- const relativePath = path197.relative(root, filePath).split(path197.sep).join("/");
104154
+ const relativePath = path198.relative(root, filePath).split(path198.sep).join("/");
103753
104155
  if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
103754
104156
  continue;
103755
104157
  }
@@ -103770,7 +104172,7 @@ async function listMarkdownFiles(root) {
103770
104172
  const entries = await readdir37(root, { withFileTypes: true });
103771
104173
  const files = [];
103772
104174
  for (const entry of entries) {
103773
- const fullPath = path197.join(root, entry.name);
104175
+ const fullPath = path198.join(root, entry.name);
103774
104176
  if (entry.isDirectory()) {
103775
104177
  files.push(...await listMarkdownFiles(fullPath));
103776
104178
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -103818,7 +104220,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
103818
104220
  const manifest = {
103819
104221
  schemaVersion: 1,
103820
104222
  standardVersion: STANDARD_VERSION,
103821
- name: `${path197.basename(path197.dirname(metaprojectRoot))}-metaproject`,
104223
+ name: `${path198.basename(path198.dirname(metaprojectRoot))}-metaproject`,
103822
104224
  createdBy: "keryx",
103823
104225
  profiles: computeProfiles(enabledModuleKeys),
103824
104226
  paths: {
@@ -103908,11 +104310,11 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
103908
104310
  metaproject: ".metaproject/index.md"
103909
104311
  }
103910
104312
  };
103911
- await writeFile54(path197.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
104313
+ await writeFile54(path198.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
103912
104314
  `, "utf8");
103913
104315
  }
103914
104316
  async function enableTasksInManifest(metaprojectRoot) {
103915
- const manifestPath = path197.join(metaprojectRoot, "metaproject.json");
104317
+ const manifestPath = path198.join(metaprojectRoot, "metaproject.json");
103916
104318
  if (!await pathExists(manifestPath)) {
103917
104319
  return;
103918
104320
  }
@@ -103935,7 +104337,7 @@ async function enableTasksInManifest(metaprojectRoot) {
103935
104337
  `, "utf8");
103936
104338
  }
103937
104339
  async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
103938
- const manifestPath = path197.join(metaprojectRoot, "metaproject.json");
104340
+ const manifestPath = path198.join(metaprojectRoot, "metaproject.json");
103939
104341
  if (!await pathExists(manifestPath)) {
103940
104342
  return;
103941
104343
  }
@@ -103971,80 +104373,80 @@ async function updateRuntime(projectRoot) {
103971
104373
  }
103972
104374
  }
103973
104375
  async function findRuntimeRoot(projectRoot) {
103974
- const projectRuntime = path197.join(projectRoot, ".metaproject", "runtime", "keryx");
103975
- if (await pathExists(path197.join(projectRuntime, ".git"))) {
104376
+ const projectRuntime = path198.join(projectRoot, ".metaproject", "runtime", "keryx");
104377
+ if (await pathExists(path198.join(projectRuntime, ".git"))) {
103976
104378
  return projectRuntime;
103977
104379
  }
103978
104380
  const home = process.env.HOME;
103979
104381
  if (!home) {
103980
104382
  return null;
103981
104383
  }
103982
- const globalRuntime = path197.join(home, ".keryx", "keryx");
103983
- if (await pathExists(path197.join(globalRuntime, ".git"))) {
104384
+ const globalRuntime = path198.join(home, ".keryx", "keryx");
104385
+ if (await pathExists(path198.join(globalRuntime, ".git"))) {
103984
104386
  return globalRuntime;
103985
104387
  }
103986
104388
  return null;
103987
104389
  }
103988
104390
  async function createServiceDirs(metaprojectRoot, modules) {
103989
104391
  const dirs = [
103990
- path197.join(metaprojectRoot, "core"),
103991
- path197.join(metaprojectRoot, "hooks", "post-update.d"),
103992
- path197.join(metaprojectRoot, "modules"),
103993
- path197.join(metaprojectRoot, "rules"),
103994
- path197.join(metaprojectRoot, "skills", "project-rules"),
104392
+ path198.join(metaprojectRoot, "core"),
104393
+ path198.join(metaprojectRoot, "hooks", "post-update.d"),
104394
+ path198.join(metaprojectRoot, "modules"),
104395
+ path198.join(metaprojectRoot, "rules"),
104396
+ path198.join(metaprojectRoot, "skills", "project-rules"),
103995
104397
  ...modules.enableGdgraph ? [
103996
- path197.join(metaprojectRoot, "core", "gdgraph"),
103997
- path197.join(metaprojectRoot, "skills", "gdgraph")
104398
+ path198.join(metaprojectRoot, "core", "gdgraph"),
104399
+ path198.join(metaprojectRoot, "skills", "gdgraph")
103998
104400
  ] : [],
103999
104401
  ...modules.enableGdctx ? [
104000
- path197.join(metaprojectRoot, "core", "gdctx"),
104001
- path197.join(metaprojectRoot, "skills", "gdctx")
104402
+ path198.join(metaprojectRoot, "core", "gdctx"),
104403
+ path198.join(metaprojectRoot, "skills", "gdctx")
104002
104404
  ] : [],
104003
104405
  ...modules.enableGdwiki ? [
104004
- path197.join(metaprojectRoot, "skills", "gdwiki"),
104005
- path197.join(metaprojectRoot, "wiki", "templates")
104406
+ path198.join(metaprojectRoot, "skills", "gdwiki"),
104407
+ path198.join(metaprojectRoot, "wiki", "templates")
104006
104408
  ] : [],
104007
104409
  ...modules.enableHealth ? [
104008
- path197.join(metaprojectRoot, "core", "health"),
104009
- path197.join(metaprojectRoot, "skills", "health")
104410
+ path198.join(metaprojectRoot, "core", "health"),
104411
+ path198.join(metaprojectRoot, "skills", "health")
104010
104412
  ] : [],
104011
104413
  ...modules.enableTesting ? [
104012
- path197.join(metaprojectRoot, "core", "testing"),
104013
- path197.join(metaprojectRoot, "skills", "testing")
104414
+ path198.join(metaprojectRoot, "core", "testing"),
104415
+ path198.join(metaprojectRoot, "skills", "testing")
104014
104416
  ] : [],
104015
104417
  ...modules.enableMemory ? [
104016
- path197.join(metaprojectRoot, "core", "memory"),
104017
- path197.join(metaprojectRoot, "skills", "memory"),
104018
- path197.join(metaprojectRoot, "memory", "templates")
104418
+ path198.join(metaprojectRoot, "core", "memory"),
104419
+ path198.join(metaprojectRoot, "skills", "memory"),
104420
+ path198.join(metaprojectRoot, "memory", "templates")
104019
104421
  ] : [],
104020
104422
  ...modules.enableTasks ? [
104021
- path197.join(metaprojectRoot, "flows"),
104022
- path197.join(metaprojectRoot, "skills", "flow")
104423
+ path198.join(metaprojectRoot, "flows"),
104424
+ path198.join(metaprojectRoot, "skills", "flow")
104023
104425
  ] : [],
104024
104426
  ...modules.enableSecurity ? [
104025
- path197.join(metaprojectRoot, "core", "security")
104427
+ path198.join(metaprojectRoot, "core", "security")
104026
104428
  ] : [],
104027
104429
  ...modules.enableSac ? [
104028
- path197.join(metaprojectRoot, "skills", "sac")
104430
+ path198.join(metaprojectRoot, "skills", "sac")
104029
104431
  ] : []
104030
104432
  ];
104031
- await Promise.all(dirs.map((dir) => mkdir62(dir, { recursive: true })));
104433
+ await Promise.all(dirs.map((dir) => mkdir63(dir, { recursive: true })));
104032
104434
  }
104033
104435
  async function installGdgraphCoreScripts2(metaprojectRoot) {
104034
- const gdgraphCoreRoot = path197.join(metaprojectRoot, "core", "gdgraph");
104035
- await mkdir62(gdgraphCoreRoot, { recursive: true });
104436
+ const gdgraphCoreRoot = path198.join(metaprojectRoot, "core", "gdgraph");
104437
+ await mkdir63(gdgraphCoreRoot, { recursive: true });
104036
104438
  for (const file of GDGRAPH_CORE_SOURCES) {
104037
- await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path197.join(gdgraphCoreRoot, file));
104439
+ await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path198.join(gdgraphCoreRoot, file));
104038
104440
  }
104039
- await writeTextIfChanged4(path197.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
104441
+ await writeTextIfChanged4(path198.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
104040
104442
  }
104041
104443
  async function installManagedHook2(projectRoot, hookName, blockId, content) {
104042
104444
  const hooksRoot = await resolveGitHooksRoot(projectRoot);
104043
104445
  if (!hooksRoot) {
104044
104446
  return;
104045
104447
  }
104046
- await mkdir62(hooksRoot, { recursive: true });
104047
- const hookPath = path197.join(hooksRoot, hookName);
104448
+ await mkdir63(hooksRoot, { recursive: true });
104449
+ const hookPath = path198.join(hooksRoot, hookName);
104048
104450
  const blockStart = `# keryx:${blockId}:begin`;
104049
104451
  const blockEnd = `# keryx:${blockId}:end`;
104050
104452
  const managedBlock = `${blockStart}
@@ -104065,7 +104467,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
104065
104467
  if (!hooksRoot) {
104066
104468
  return;
104067
104469
  }
104068
- const hookPath = path197.join(hooksRoot, hookName);
104470
+ const hookPath = path198.join(hooksRoot, hookName);
104069
104471
  if (!await pathExists(hookPath)) {
104070
104472
  return;
104071
104473
  }
@@ -104087,7 +104489,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
104087
104489
  if (!hooksRoot) {
104088
104490
  return false;
104089
104491
  }
104090
- const hookPath = path197.join(hooksRoot, "pre-push");
104492
+ const hookPath = path198.join(hooksRoot, "pre-push");
104091
104493
  if (!await pathExists(hookPath)) {
104092
104494
  return false;
104093
104495
  }
@@ -104102,7 +104504,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
104102
104504
  return (await readFile100(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
104103
104505
  }
104104
104506
  async function readManifest5(metaprojectRoot) {
104105
- const manifestPath = path197.join(metaprojectRoot, "metaproject.json");
104507
+ const manifestPath = path198.join(metaprojectRoot, "metaproject.json");
104106
104508
  if (!await pathExists(manifestPath)) {
104107
104509
  return {
104108
104510
  exists: false,
@@ -104171,7 +104573,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
104171
104573
  }
104172
104574
  async function anyPathExists(root, candidates) {
104173
104575
  for (const candidate of candidates) {
104174
- if (await pathExists(path197.join(root, candidate))) {
104576
+ if (await pathExists(path198.join(root, candidate))) {
104175
104577
  return true;
104176
104578
  }
104177
104579
  }
@@ -104194,13 +104596,13 @@ function parseUpdateArgs(args) {
104194
104596
  };
104195
104597
  }
104196
104598
  async function runPostUpdateHooks(projectRoot) {
104197
- const hooksDir = path197.join(projectRoot, ".metaproject", "hooks", "post-update.d");
104599
+ const hooksDir = path198.join(projectRoot, ".metaproject", "hooks", "post-update.d");
104198
104600
  if (!await pathExists(hooksDir)) {
104199
104601
  return;
104200
104602
  }
104201
104603
  const entries = (await readdir37(hooksDir)).sort();
104202
104604
  for (const entry of entries) {
104203
- const hookPath = path197.join(hooksDir, entry);
104605
+ const hookPath = path198.join(hooksDir, entry);
104204
104606
  try {
104205
104607
  await accessExecutable(hookPath);
104206
104608
  } catch {
@@ -104241,14 +104643,14 @@ async function writeTextIfChanged4(filePath, content) {
104241
104643
  if (await pathExists(filePath) && await readFile100(filePath, "utf8") === content) {
104242
104644
  return;
104243
104645
  }
104244
- await mkdir62(path197.dirname(filePath), { recursive: true });
104646
+ await mkdir63(path198.dirname(filePath), { recursive: true });
104245
104647
  await writeFile54(filePath, content, "utf8");
104246
104648
  }
104247
104649
  async function writeTextIfMissing4(filePath, content) {
104248
104650
  if (await pathExists(filePath)) {
104249
104651
  return;
104250
104652
  }
104251
- await mkdir62(path197.dirname(filePath), { recursive: true });
104653
+ await mkdir63(path198.dirname(filePath), { recursive: true });
104252
104654
  await writeFile54(filePath, content, "utf8");
104253
104655
  }
104254
104656
  async function copyFileIfChanged2(from, to) {
@@ -104256,7 +104658,7 @@ async function copyFileIfChanged2(from, to) {
104256
104658
  if (await pathExists(to) && await readFile100(to, "utf8") === next) {
104257
104659
  return;
104258
104660
  }
104259
- await mkdir62(path197.dirname(to), { recursive: true });
104661
+ await mkdir63(path198.dirname(to), { recursive: true });
104260
104662
  await writeFile54(to, next, "utf8");
104261
104663
  }
104262
104664
  function runtimeSourcePath2(relativePath) {
@@ -104265,7 +104667,7 @@ function runtimeSourcePath2(relativePath) {
104265
104667
  return directPath;
104266
104668
  }
104267
104669
  if (relativePath.startsWith("../")) {
104268
- const packagedSourcePath = path197.join(path197.dirname(fileURLToPath8(import.meta.url)), "..", "src", relativePath.slice(3));
104670
+ const packagedSourcePath = path198.join(path198.dirname(fileURLToPath8(import.meta.url)), "..", "src", relativePath.slice(3));
104269
104671
  if (existsSync36(packagedSourcePath)) {
104270
104672
  return packagedSourcePath;
104271
104673
  }
@@ -104300,7 +104702,7 @@ function printHelp20() {
104300
104702
 
104301
104703
  // src/commands/dashboard.ts
104302
104704
  import { spawn as spawn7 } from "child_process";
104303
- import path198 from "path";
104705
+ import path199 from "path";
104304
104706
  init_args();
104305
104707
  async function dashboardCommand(args = []) {
104306
104708
  const options = parseOptions(args);
@@ -104311,7 +104713,7 @@ async function dashboardCommand(args = []) {
104311
104713
  }
104312
104714
  if (subcommand === "build") {
104313
104715
  const result = await buildDashboard();
104314
- const rel = path198.relative(process.cwd(), result.path);
104716
+ const rel = path199.relative(process.cwd(), result.path);
104315
104717
  console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
104316
104718
  note(`Open it: keryx dashboard open`);
104317
104719
  return;
@@ -104319,7 +104721,7 @@ async function dashboardCommand(args = []) {
104319
104721
  if (subcommand === "open") {
104320
104722
  const result = await buildDashboard();
104321
104723
  await openFile(result.path);
104322
- const rel = path198.relative(process.cwd(), result.path);
104724
+ const rel = path199.relative(process.cwd(), result.path);
104323
104725
  console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
104324
104726
  return;
104325
104727
  }
@@ -104366,9 +104768,9 @@ function printHelp21() {
104366
104768
  import { readFileSync as readFileSync9 } from "fs";
104367
104769
 
104368
104770
  // src/agents/bootstrap.ts
104369
- import { mkdir as mkdir63, readFile as readFile101, writeFile as writeFile55 } from "fs/promises";
104370
- import { homedir as homedir8 } from "os";
104371
- import path199 from "path";
104771
+ import { mkdir as mkdir64, readFile as readFile101, writeFile as writeFile55 } from "fs/promises";
104772
+ import { homedir as homedir9 } from "os";
104773
+ import path200 from "path";
104372
104774
  init_fs();
104373
104775
  var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
104374
104776
  var AGENT_BOOTSTRAP_END = "<!-- /keryx:global-bootstrap -->";
@@ -104378,35 +104780,35 @@ var AGENT_BOOTSTRAP_RUNTIMES = [
104378
104780
  aliases: ["claude-code"],
104379
104781
  label: "Claude Code",
104380
104782
  fileName: "CLAUDE.md",
104381
- filePath: (homeRoot) => path199.join(homeRoot, ".claude", "CLAUDE.md")
104783
+ filePath: (homeRoot) => path200.join(homeRoot, ".claude", "CLAUDE.md")
104382
104784
  },
104383
104785
  {
104384
104786
  id: "opencode",
104385
104787
  aliases: ["open-code"],
104386
104788
  label: "OpenCode",
104387
104789
  fileName: "AGENTS.md",
104388
- filePath: (homeRoot) => path199.join(homeRoot, ".config", "opencode", "AGENTS.md")
104790
+ filePath: (homeRoot) => path200.join(homeRoot, ".config", "opencode", "AGENTS.md")
104389
104791
  },
104390
104792
  {
104391
104793
  id: "zcode",
104392
104794
  aliases: ["zed", "zed-code"],
104393
104795
  label: "ZCode",
104394
104796
  fileName: "AGENTS.md",
104395
- filePath: (homeRoot) => path199.join(homeRoot, ".zcode", "AGENTS.md")
104797
+ filePath: (homeRoot) => path200.join(homeRoot, ".zcode", "AGENTS.md")
104396
104798
  },
104397
104799
  {
104398
104800
  id: "codex",
104399
104801
  aliases: [],
104400
104802
  label: "Codex",
104401
104803
  fileName: "AGENTS.md",
104402
- filePath: (homeRoot) => path199.join(homeRoot, ".codex", "AGENTS.md")
104804
+ filePath: (homeRoot) => path200.join(homeRoot, ".codex", "AGENTS.md")
104403
104805
  },
104404
104806
  {
104405
104807
  id: "antigravity",
104406
104808
  aliases: ["antigravuty", "antigravity-code"],
104407
104809
  label: "Antigravity",
104408
104810
  fileName: "AGENTS.md",
104409
- filePath: (homeRoot) => path199.join(homeRoot, ".config", "antigravity", "AGENTS.md")
104811
+ filePath: (homeRoot) => path200.join(homeRoot, ".config", "antigravity", "AGENTS.md")
104410
104812
  }
104411
104813
  ];
104412
104814
  function agentBootstrapRuntimeIds() {
@@ -104436,7 +104838,7 @@ function resolveAgentBootstrapRuntimes(ids) {
104436
104838
  }
104437
104839
  return { runtimes, unknown };
104438
104840
  }
104439
- async function agentBootstrapStatus(runtime, homeRoot = homedir8()) {
104841
+ async function agentBootstrapStatus(runtime, homeRoot = homedir9()) {
104440
104842
  const filePath = runtime.filePath(homeRoot);
104441
104843
  const exists = await pathExists(filePath);
104442
104844
  const content = exists ? await readFile101(filePath, "utf8") : "";
@@ -104446,7 +104848,7 @@ async function agentBootstrapStatus(runtime, homeRoot = homedir8()) {
104446
104848
  return { runtime: runtime.id, label: runtime.label, filePath, exists, installed, current };
104447
104849
  }
104448
104850
  async function installAgentBootstrap(runtime, options = {}) {
104449
- const homeRoot = options.homeRoot ?? homedir8();
104851
+ const homeRoot = options.homeRoot ?? homedir9();
104450
104852
  const filePath = runtime.filePath(homeRoot);
104451
104853
  const exists = await pathExists(filePath);
104452
104854
  const current = exists ? await readFile101(filePath, "utf8") : "";
@@ -104454,14 +104856,14 @@ async function installAgentBootstrap(runtime, options = {}) {
104454
104856
  const dryRun = options.dryRun === true;
104455
104857
  const wrote = next !== current;
104456
104858
  if (wrote && !dryRun) {
104457
- await mkdir63(path199.dirname(filePath), { recursive: true });
104859
+ await mkdir64(path200.dirname(filePath), { recursive: true });
104458
104860
  await writeFile55(filePath, next, "utf8");
104459
104861
  }
104460
104862
  const status = dryRun ? statusFromContent(runtime, filePath, exists, next) : await agentBootstrapStatus(runtime, homeRoot);
104461
104863
  return { ...status, wrote, dryRun };
104462
104864
  }
104463
104865
  async function uninstallAgentBootstrap(runtime, options = {}) {
104464
- const homeRoot = options.homeRoot ?? homedir8();
104866
+ const homeRoot = options.homeRoot ?? homedir9();
104465
104867
  const filePath = runtime.filePath(homeRoot);
104466
104868
  const exists = await pathExists(filePath);
104467
104869
  const current = exists ? await readFile101(filePath, "utf8") : "";
@@ -104991,7 +105393,7 @@ function printBootstrapHelp() {
104991
105393
  // src/commands/metrics.ts
104992
105394
  init_args();
104993
105395
  import { readFile as readFile102 } from "fs/promises";
104994
- import path201 from "path";
105396
+ import path202 from "path";
104995
105397
 
104996
105398
  // src/metrics/benchmark.ts
104997
105399
  var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
@@ -105807,8 +106209,8 @@ function buildContainmentManifest(inputs, options = {}) {
105807
106209
  }
105808
106210
 
105809
106211
  // src/metrics/oracle-runner.ts
105810
- import { mkdir as mkdir64, writeFile as writeFile56 } from "fs/promises";
105811
- import path200 from "path";
106212
+ import { mkdir as mkdir65, writeFile as writeFile56 } from "fs/promises";
106213
+ import path201 from "path";
105812
106214
 
105813
106215
  // src/metrics/ir.ts
105814
106216
  function toIdSet(ids) {
@@ -106254,9 +106656,9 @@ function buildEvidenceBundle(input, options = {}) {
106254
106656
  async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
106255
106657
  const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
106256
106658
  const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
106257
- const dir = path200.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
106258
- await mkdir64(dir, { recursive: true });
106259
- const write = (name, value) => writeFile56(path200.join(dir, name), `${JSON.stringify(value, null, 2)}
106659
+ const dir = path201.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
106660
+ await mkdir65(dir, { recursive: true });
106661
+ const write = (name, value) => writeFile56(path201.join(dir, name), `${JSON.stringify(value, null, 2)}
106260
106662
  `, "utf8");
106261
106663
  await Promise.all([
106262
106664
  write("inputs.json", bundle.inputs),
@@ -106296,7 +106698,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
106296
106698
  console.log("# metrics status");
106297
106699
  console.log("");
106298
106700
  console.log(`root: ${root}`);
106299
- console.log(`enabled: ${await Bun.file(path201.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
106701
+ console.log(`enabled: ${await Bun.file(path202.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
106300
106702
  const latest = await readLatestPointer(root);
106301
106703
  console.log(`latest: ${latest.status}`);
106302
106704
  return;
@@ -106308,7 +106710,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
106308
106710
  process.exitCode = 1;
106309
106711
  return;
106310
106712
  }
106311
- const record = JSON.parse(await readFile102(path201.resolve(projectRoot, file), "utf8"));
106713
+ const record = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
106312
106714
  const result = validateRunRecord(record);
106313
106715
  console.log(result.valid ? "valid: yes" : "valid: no");
106314
106716
  for (const error of result.errors)
@@ -106333,7 +106735,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
106333
106735
  process.exitCode = 1;
106334
106736
  return;
106335
106737
  }
106336
- const file = path201.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
106738
+ const file = path202.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
106337
106739
  if (!await Bun.file(file).exists()) {
106338
106740
  console.error(`Run not found: ${runId}`);
106339
106741
  process.exitCode = 1;
@@ -106350,8 +106752,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
106350
106752
  process.exitCode = 1;
106351
106753
  return;
106352
106754
  }
106353
- const a = JSON.parse(await readFile102(path201.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
106354
- const b = JSON.parse(await readFile102(path201.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
106755
+ const a = JSON.parse(await readFile102(path202.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
106756
+ const b = JSON.parse(await readFile102(path202.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
106355
106757
  const comparison = compareExecutionRuns(a, b);
106356
106758
  console.log(stableJson(comparison));
106357
106759
  return;
@@ -106385,8 +106787,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
106385
106787
  return;
106386
106788
  }
106387
106789
  const template = createPairedBenchmarkTemplate(taskIds);
106388
- await Bun.write(path201.resolve(projectRoot, out), stableJson(template));
106389
- console.log(`manifest: ${path201.relative(projectRoot, path201.resolve(projectRoot, out))}`);
106790
+ await Bun.write(path202.resolve(projectRoot, out), stableJson(template));
106791
+ console.log(`manifest: ${path202.relative(projectRoot, path202.resolve(projectRoot, out))}`);
106390
106792
  return;
106391
106793
  }
106392
106794
  if (subcommand === "benchmark" && args[1] === "run") {
@@ -106400,7 +106802,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
106400
106802
  process.exitCode = 1;
106401
106803
  return;
106402
106804
  }
106403
- const raw = JSON.parse(await readFile102(path201.resolve(projectRoot, file), "utf8"));
106805
+ const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
106404
106806
  const input = Array.isArray(raw) ? raw : raw.runs ?? [];
106405
106807
  const result = validatePairedBenchmark(input);
106406
106808
  console.log(stableJson(result));
@@ -106412,7 +106814,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
106412
106814
  process.exitCode = 1;
106413
106815
  }
106414
106816
  async function loadAffectedSets(projectRoot, file) {
106415
- const raw = JSON.parse(await readFile102(path201.resolve(projectRoot, file), "utf8"));
106817
+ const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
106416
106818
  const map = new Map;
106417
106819
  for (const entry of raw.targets ?? []) {
106418
106820
  if (typeof entry.target === "string")
@@ -106483,7 +106885,7 @@ async function runHarnessLayer(projectRoot, args, ladder) {
106483
106885
  let tasks;
106484
106886
  let model;
106485
106887
  try {
106486
- const raw = JSON.parse(await readFile102(path201.resolve(projectRoot, resultsPath), "utf8"));
106888
+ const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, resultsPath), "utf8"));
106487
106889
  tasks = raw.tasks ?? [];
106488
106890
  model = raw.model;
106489
106891
  } catch (error) {
@@ -106514,7 +106916,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args, ladder) {
106514
106916
  let cases;
106515
106917
  let model;
106516
106918
  try {
106517
- const raw = JSON.parse(await readFile102(path201.resolve(projectRoot, resultsPath), "utf8"));
106919
+ const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, resultsPath), "utf8"));
106518
106920
  cases = raw.cases ?? [];
106519
106921
  model = raw.model;
106520
106922
  } catch (error) {
@@ -106539,7 +106941,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args, ladder) {
106539
106941
  let cases;
106540
106942
  let model;
106541
106943
  try {
106542
- const raw = JSON.parse(await readFile102(path201.resolve(projectRoot, resultsPath), "utf8"));
106944
+ const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, resultsPath), "utf8"));
106543
106945
  cases = raw.cases ?? [];
106544
106946
  model = raw.model;
106545
106947
  } catch (error) {
@@ -106564,7 +106966,7 @@ async function runSafetyContainmentLayer(projectRoot, args, ladder, caseClass) {
106564
106966
  let cases;
106565
106967
  let model;
106566
106968
  try {
106567
- const raw = JSON.parse(await readFile102(path201.resolve(projectRoot, resultsPath), "utf8"));
106969
+ const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, resultsPath), "utf8"));
106568
106970
  cases = raw.cases ?? [];
106569
106971
  model = raw.model;
106570
106972
  } catch (error) {
@@ -106626,12 +107028,12 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
106626
107028
  }
106627
107029
  const manifests = buildOracleManifestsByGold(inputs, { ladder });
106628
107030
  if (outDir) {
106629
- const resolvedOut = path201.resolve(projectRoot, outDir);
107031
+ const resolvedOut = path202.resolve(projectRoot, outDir);
106630
107032
  for (const input of inputs) {
106631
107033
  for (const named of input.golds) {
106632
107034
  const bundle = buildEvidenceBundle({ target: input.target, system: input.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
106633
- const dir = await persistEvidenceBundle(path201.join(resolvedOut, named.kind), bundle, ladder);
106634
- console.error(`bundle[${named.kind}]: ${path201.relative(projectRoot, dir)}`);
107035
+ const dir = await persistEvidenceBundle(path202.join(resolvedOut, named.kind), bundle, ladder);
107036
+ console.error(`bundle[${named.kind}]: ${path202.relative(projectRoot, dir)}`);
106635
107037
  }
106636
107038
  }
106637
107039
  }
@@ -106655,7 +107057,7 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
106655
107057
  return allValid;
106656
107058
  }
106657
107059
  async function loadCoverageMap2(projectRoot, file) {
106658
- const raw = JSON.parse(await readFile102(path201.resolve(projectRoot, file), "utf8"));
107060
+ const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
106659
107061
  return raw.coverageMap ?? {};
106660
107062
  }
106661
107063
  async function runTestingLayer(projectRoot, args, ladder) {
@@ -106692,7 +107094,7 @@ async function runTestingLayer(projectRoot, args, ladder) {
106692
107094
  return result.valid;
106693
107095
  }
106694
107096
  async function loadMemoryGoldK(projectRoot, file) {
106695
- const raw = JSON.parse(await readFile102(path201.resolve(projectRoot, file), "utf8"));
107097
+ const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
106696
107098
  return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
106697
107099
  }
106698
107100
  async function runMemoryLayer(projectRoot, args, ladder) {
@@ -106729,11 +107131,11 @@ async function runMemoryLayer(projectRoot, args, ladder) {
106729
107131
  return result.valid;
106730
107132
  }
106731
107133
  async function loadWikiGoldK(projectRoot, file) {
106732
- const raw = JSON.parse(await readFile102(path201.resolve(projectRoot, file), "utf8"));
107134
+ const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
106733
107135
  return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
106734
107136
  }
106735
107137
  async function loadWikiGroundedness(projectRoot, file) {
106736
- const raw = JSON.parse(await readFile102(path201.resolve(projectRoot, file), "utf8"));
107138
+ const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
106737
107139
  const map = new Map;
106738
107140
  for (const entry of raw.targets ?? []) {
106739
107141
  if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
@@ -106789,7 +107191,7 @@ async function runWikiLayer(projectRoot, args, ladder) {
106789
107191
  return result.valid;
106790
107192
  }
106791
107193
  async function loadGdctxFacts(projectRoot, file) {
106792
- const raw = JSON.parse(await readFile102(path201.resolve(projectRoot, file), "utf8"));
107194
+ const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, file), "utf8"));
106793
107195
  const inputs = [];
106794
107196
  for (const entry of raw.inputs ?? []) {
106795
107197
  if (typeof entry.input === "string") {
@@ -106827,7 +107229,7 @@ async function collect(projectRoot, args) {
106827
107229
  process.exitCode = 1;
106828
107230
  return;
106829
107231
  }
106830
- const raw = JSON.parse(await readFile102(path201.resolve(projectRoot, eventFile), "utf8"));
107232
+ const raw = JSON.parse(await readFile102(path202.resolve(projectRoot, eventFile), "utf8"));
106831
107233
  const events = Array.isArray(raw) ? raw : raw.events;
106832
107234
  const startedAt = optionValue(args, "--started-at") ?? events[0]?.timestamp_utc ?? new Date().toISOString();
106833
107235
  const finishedAt = optionValue(args, "--finished-at") ?? events.at(-1)?.timestamp_utc ?? startedAt;
@@ -106843,11 +107245,11 @@ async function collect(projectRoot, args) {
106843
107245
  parentRunId: optionValue(args, "--parent-run-id") ?? null
106844
107246
  });
106845
107247
  const result = await writeRunArtifacts(metricsRoot(projectRoot), record, { cwd: projectRoot });
106846
- console.log(`json: ${path201.relative(projectRoot, result.jsonPath)}`);
106847
- console.log(`markdown: ${path201.relative(projectRoot, result.markdownPath)}`);
107248
+ console.log(`json: ${path202.relative(projectRoot, result.jsonPath)}`);
107249
+ console.log(`markdown: ${path202.relative(projectRoot, result.markdownPath)}`);
106848
107250
  }
106849
107251
  function metricsRoot(projectRoot) {
106850
- return path201.join(projectRoot, ".metaproject", "data", "metrics");
107252
+ return path202.join(projectRoot, ".metaproject", "data", "metrics");
106851
107253
  }
106852
107254
  function printMetricsHelp() {
106853
107255
  console.log(`keryx metrics