@mrciphersmith/keryx 0.2.84 → 0.2.88

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
@@ -923,6 +923,7 @@ var init_providers = __esm(() => {
923
923
  label: "xAI (Grok)",
924
924
  baseUrl: "https://api.x.ai",
925
925
  envKey: "XAI_API_KEY",
926
+ streamUsage: true,
926
927
  models: ["grok-2-latest", "grok-2", "grok-beta"],
927
928
  note: "xAI \xB7 OpenAI-compatible"
928
929
  },
@@ -2343,6 +2344,7 @@ ${message.content}` });
2343
2344
  const payload = {
2344
2345
  model: request.modelId,
2345
2346
  stream: true,
2347
+ ...this.deps.grant?.streamUsage === true ? { stream_options: { include_usage: true } } : {},
2346
2348
  messages,
2347
2349
  ...request.tools !== undefined ? {
2348
2350
  tools: request.tools.map((tool) => ({
@@ -3696,6 +3698,7 @@ function makeProvider(name, _model, opts) {
3696
3698
  }
3697
3699
  const grant = {
3698
3700
  network: true,
3701
+ ...compat.streamUsage === true ? { streamUsage: true } : {},
3699
3702
  baseUrl: opts.baseUrl ?? resolveProviderBaseUrl(compat, env),
3700
3703
  ...compat.allowLoopback === true ? { allowLoopback: true } : {},
3701
3704
  ...compat.allowPrivateLan === true ? { allowPrivateLan: true } : {},
@@ -35094,6 +35097,7 @@ function createFlowService(deps) {
35094
35097
  acChecksum: null,
35095
35098
  acConfirmed: {},
35096
35099
  pr: { url: null },
35100
+ ...input.baseBranch === undefined ? {} : { baseBranch: input.baseBranch },
35097
35101
  tasks: DEFAULT_TASKS.map((task) => ({ ...task, status: "todo" })),
35098
35102
  history: [{ at: createdAt, event: "created" }]
35099
35103
  };
@@ -35302,6 +35306,10 @@ function createFlowService(deps) {
35302
35306
  if (!pr.isDraft) {
35303
35307
  detail += " (warning: PR is not a draft)";
35304
35308
  }
35309
+ if (flow.baseBranch === undefined && typeof pr.baseRefName === "string" && pr.baseRefName !== "") {
35310
+ flow.baseBranch = pr.baseRefName;
35311
+ detail += ` (base: ${pr.baseRefName})`;
35312
+ }
35305
35313
  } else {
35306
35314
  detail += " (tracker unavailable: existence not verified)";
35307
35315
  }
@@ -35353,6 +35361,7 @@ function createFlowService(deps) {
35353
35361
  detail: "tracker unavailable; verify PR checks manually"
35354
35362
  });
35355
35363
  }
35364
+ gates.push(await baseBranchCondition(cwd, flow, mergedCommit ?? undefined, flow.pr.url && deps.tracker && await deps.tracker.detect() ? (await deps.tracker.prStatus(flow.pr.url)).baseRefName : undefined, commitContainedIn));
35356
35365
  gates.push(taskGate(flow));
35357
35366
  try {
35358
35367
  gates.push(await reviewGate({
@@ -35657,6 +35666,70 @@ function buildIssueComment2(flow, gates) {
35657
35666
  ].join(`
35658
35667
  `);
35659
35668
  }
35669
+ async function baseBranchCondition(cwd, flow, mergedCommit, prBase, containedIn) {
35670
+ const recorded = flow.baseBranch;
35671
+ if (recorded === undefined || recorded.trim() === "") {
35672
+ return {
35673
+ name: "base-branch",
35674
+ status: "skipped",
35675
+ detail: "not recorded: this flow never named a base branch, so there is nothing to compare the merge against. Not a pass \u2014 `keryx flow init --base <branch>` or `flow implemented --pr <url>` records one."
35676
+ };
35677
+ }
35678
+ if (mergedCommit !== undefined) {
35679
+ const contained = await containedIn(cwd, mergedCommit, `origin/${recorded}`);
35680
+ if (contained === null) {
35681
+ return {
35682
+ name: "base-branch",
35683
+ status: "fail",
35684
+ detail: `unobserved: origin/${recorded} could not be resolved, so whether ${mergedCommit} landed there is unknown. Fetch the remote (\`git fetch origin ${recorded}\`) and re-run; an unresolvable base is not a passing one.`
35685
+ };
35686
+ }
35687
+ return contained ? {
35688
+ name: "base-branch",
35689
+ status: "pass",
35690
+ detail: `${mergedCommit} is contained in origin/${recorded}, the base this flow recorded`
35691
+ } : {
35692
+ name: "base-branch",
35693
+ status: "fail",
35694
+ detail: `violated: this flow recorded base ${recorded}, but ${mergedCommit} is not contained in origin/${recorded}. The merge landed somewhere else.`
35695
+ };
35696
+ }
35697
+ if (prBase === undefined || prBase === null || prBase === "") {
35698
+ return {
35699
+ name: "base-branch",
35700
+ status: "fail",
35701
+ detail: `unobserved: this flow recorded base ${recorded}, but the tracker did not report the pull request's base. An unread base is not a matching one.`
35702
+ };
35703
+ }
35704
+ return prBase === recorded ? {
35705
+ name: "base-branch",
35706
+ status: "pass",
35707
+ detail: `the pull request targets ${prBase}, the base this flow recorded`
35708
+ } : {
35709
+ name: "base-branch",
35710
+ status: "fail",
35711
+ detail: `violated: this flow recorded base ${recorded}, but the pull request now targets ${prBase}. It was retargeted after the base was recorded.`
35712
+ };
35713
+ }
35714
+ async function commitContainedIn(cwd, commit, ref) {
35715
+ if (!/^[0-9a-f]{7,64}$/i.test(commit)) {
35716
+ return null;
35717
+ }
35718
+ const resolved = Bun.spawn(["git", "rev-parse", "--verify", `${ref}^{commit}`], {
35719
+ cwd,
35720
+ stdout: "ignore",
35721
+ stderr: "ignore"
35722
+ });
35723
+ if (await resolved.exited !== 0) {
35724
+ return null;
35725
+ }
35726
+ const process2 = Bun.spawn(["git", "merge-base", "--is-ancestor", commit, ref], {
35727
+ cwd,
35728
+ stdout: "ignore",
35729
+ stderr: "ignore"
35730
+ });
35731
+ return await process2.exited === 0;
35732
+ }
35660
35733
  async function verifyCommitOnMain(cwd, commit) {
35661
35734
  if (!/^[0-9a-f]{7,64}$/i.test(commit)) {
35662
35735
  return { status: "fail", detail: "merged commit must be a hexadecimal Git commit id" };
@@ -35736,9 +35809,9 @@ var init_github = __esm(() => {
35736
35809
  },
35737
35810
  async prStatus(url) {
35738
35811
  try {
35739
- const view = await gh(["pr", "view", url, "--json", "isDraft,state,headRefOid"]);
35812
+ const view = await gh(["pr", "view", url, "--json", "isDraft,state,headRefOid,baseRefName"]);
35740
35813
  if (view.exitCode !== 0) {
35741
- return { exists: false, isDraft: false, checksGreen: null, headSha: null };
35814
+ return { exists: false, isDraft: false, checksGreen: null, headSha: null, baseRefName: null };
35742
35815
  }
35743
35816
  const parsed = JSON.parse(view.stdout);
35744
35817
  const checks = await gh(["pr", "checks", url]);
@@ -35746,10 +35819,11 @@ var init_github = __esm(() => {
35746
35819
  exists: true,
35747
35820
  isDraft: parsed.isDraft === true,
35748
35821
  checksGreen: checks.exitCode === 0,
35749
- headSha: typeof parsed.headRefOid === "string" && parsed.headRefOid !== "" ? parsed.headRefOid : null
35822
+ headSha: typeof parsed.headRefOid === "string" && parsed.headRefOid !== "" ? parsed.headRefOid : null,
35823
+ baseRefName: typeof parsed.baseRefName === "string" && parsed.baseRefName !== "" ? parsed.baseRefName : null
35750
35824
  };
35751
35825
  } catch {
35752
- return { exists: false, isDraft: false, checksGreen: null, headSha: null };
35826
+ return { exists: false, isDraft: false, checksGreen: null, headSha: null, baseRefName: null };
35753
35827
  }
35754
35828
  },
35755
35829
  async comment(ref, body) {
@@ -40091,9 +40165,9 @@ var init_memory = __esm(() => {
40091
40165
 
40092
40166
  // src/lib/serve-config.ts
40093
40167
  import { existsSync as existsSync25 } from "fs";
40094
- import path167 from "path";
40168
+ import path168 from "path";
40095
40169
  function serveConfigPath(dir) {
40096
- return path167.join(keryxConfigDir(dir), "serve.json");
40170
+ return path168.join(keryxConfigDir(dir), "serve.json");
40097
40171
  }
40098
40172
  function parseIpv4(value) {
40099
40173
  const parts = value.split(".");
@@ -43232,63 +43306,112 @@ var CONTRACTS = [
43232
43306
  {
43233
43307
  name: "agent-event",
43234
43308
  fileName: "agent-event.schema.json",
43235
- description: "Append-only lifecycle event emitted by orchestrators and subagents."
43309
+ description: "Append-only lifecycle event emitted by orchestrators and subagents.",
43310
+ enforcement: {
43311
+ kind: "none",
43312
+ reason: "Events are appended by whichever agent is running, in that agent's own runtime. No keryx process sits between the emitter and the log, so there is no point at which a malformed event could be refused rather than written."
43313
+ }
43236
43314
  },
43237
43315
  {
43238
43316
  name: "flow-orchestrator-input",
43239
43317
  fileName: "flow-orchestrator-input-contract.schema.json",
43240
43318
  description: "Dispatch payload handed to flow-orchestrator (request + base branch + completion outcome + constraints).",
43241
- sourcePath: "src/gdskills/bundled/skills/orchestration/flow-orchestrator/input-contract.schema.json"
43319
+ sourcePath: "src/gdskills/bundled/skills/orchestration/flow-orchestrator/input-contract.schema.json",
43320
+ enforcement: {
43321
+ kind: "none",
43322
+ reason: "This payload is handed from one AGENT to another. In a session driven by a host agent's own dispatch tool, no keryx process is on that path, so nothing can refuse a malformed dispatch. Same structural position as reviewer-input, recorded for the same reason. Registering it made `keryx skills contracts validate --schema flow-orchestrator-input` possible, which is a validator an agent must remember to run, not an enforcement."
43323
+ }
43242
43324
  },
43243
43325
  {
43244
43326
  name: "review-pr-feedback-input",
43245
43327
  fileName: "review-pr-feedback-input-contract.schema.json",
43246
43328
  description: "Request handed to review-pr-feedback (PR reference, --fix, and the operator confirmation --fix requires).",
43247
- sourcePath: "src/gdskills/bundled/skills/review/review-pr-feedback/input-contract.schema.json"
43329
+ sourcePath: "src/gdskills/bundled/skills/review/review-pr-feedback/input-contract.schema.json",
43330
+ enforcement: {
43331
+ kind: "none",
43332
+ reason: "The request is what an operator or a host agent hands the skill before any keryx command runs, so keryx is not yet in the path when the payload would have to be refused. This is the one where that hurts most: `--fix` merges third-party review comments into somebody else's pull request, and `operator_confirmed` is the fence. The conditional is expressed in the schema and `keryx skills contracts validate --schema review-pr-feedback-input` will apply it \u2014 but only when something invokes it."
43333
+ }
43248
43334
  },
43249
43335
  {
43250
43336
  name: "review-pr-feedback-output",
43251
43337
  fileName: "review-pr-feedback-output-contract.schema.json",
43252
43338
  description: "Result review-pr-feedback returns: verdict counts, the injection-screen record, and what the fix run merged.",
43253
- sourcePath: "src/gdskills/bundled/skills/review/review-pr-feedback/output-contract.schema.json"
43339
+ sourcePath: "src/gdskills/bundled/skills/review/review-pr-feedback/output-contract.schema.json",
43340
+ enforcement: {
43341
+ kind: "opt-in",
43342
+ module: "src/commands/review.ts",
43343
+ switchedOnBy: "keryx review comments reply --result <file>",
43344
+ refuses: "A result that contradicts itself, before the pass is built and before any network call: an analyze-mode run reporting a branch and a merge, or a record saying the injection screen never ran while claiming it excluded comments."
43345
+ }
43254
43346
  },
43255
43347
  {
43256
43348
  name: "job-orchestrator-state",
43257
43349
  fileName: "job-orchestrator-state.schema.json",
43258
43350
  description: "Persisted job package state (.metaproject/jobs/<name>/state.json), written by `keryx job`.",
43259
- sourcePath: "src/gdskills/bundled/skills/orchestration/job-orchestrator/state.schema.json"
43351
+ sourcePath: "src/gdskills/bundled/skills/orchestration/job-orchestrator/state.schema.json",
43352
+ enforcement: {
43353
+ kind: "production",
43354
+ module: "src/job/store.ts",
43355
+ refuses: "`writeJob` refuses to write a state file that does not satisfy the schema, so an invalid job package cannot reach disk."
43356
+ }
43260
43357
  },
43261
43358
  {
43262
43359
  name: "orchestrator-state",
43263
43360
  fileName: "orchestrator-state.schema.json",
43264
- description: "Persisted resumable orchestrator state."
43361
+ description: "Persisted resumable orchestrator state.",
43362
+ enforcement: {
43363
+ kind: "none",
43364
+ reason: "No keryx command reads or writes this file. It describes state an orchestrator agent persists for itself, in its own runtime, so there is no keryx-owned write path to refuse at. Contrast job-orchestrator-state, which looks similar and IS enforced for exactly one reason: `keryx job` writes that one."
43365
+ }
43265
43366
  },
43266
43367
  {
43267
43368
  name: "review-finding",
43268
43369
  fileName: "review-finding.schema.json",
43269
- description: "Normalized reviewer finding consumed by review-orchestrator and learning flows."
43370
+ description: "Normalized reviewer finding consumed by review-orchestrator and learning flows.",
43371
+ enforcement: {
43372
+ kind: "production",
43373
+ module: "src/review/managed.ts",
43374
+ refuses: "`createManagedReviewPackage` refuses to record findings that do not satisfy the schema, and the disposition sub-schema is applied the same way, so a review round cannot be written with a malformed finding or an unreadable outcome."
43375
+ }
43270
43376
  },
43271
43377
  {
43272
43378
  name: "subagent-dispatch",
43273
43379
  fileName: "subagent-dispatch.schema.json",
43274
- description: "Orchestrator-to-subagent dispatch payload."
43380
+ description: "Orchestrator-to-subagent dispatch payload.",
43381
+ enforcement: {
43382
+ kind: "none",
43383
+ reason: "The dispatch is built and consumed inside the harness without the canonical schema being loaded: `subagent-dispatch` appears only as a label on the extension metadata. Its sibling `subagent-result` IS validated, because the harness parses a child's reply back and has to decide whether it is well-formed; nothing performs the equivalent act on the way out."
43384
+ }
43275
43385
  },
43276
43386
  {
43277
43387
  name: "subagent-result",
43278
43388
  fileName: "subagent-result.schema.json",
43279
- description: "Subagent-to-orchestrator result payload."
43389
+ description: "Subagent-to-orchestrator result payload.",
43390
+ enforcement: {
43391
+ kind: "production",
43392
+ module: "src/harness/external/runtime.ts",
43393
+ refuses: "A child's reply that does not parse as a well-formed result is rejected when the harness reads it back, so a malformed result cannot be persisted as if the child had succeeded."
43394
+ }
43280
43395
  },
43281
43396
  {
43282
43397
  name: "task-implementer-input",
43283
43398
  fileName: "task-implementer-input-contract.schema.json",
43284
43399
  description: "Task request handed to task-implementer (task + workspace + automation), validated before dispatch.",
43285
- sourcePath: "src/gdskills/bundled/skills/orchestration/task-implementer/input-contract.schema.json"
43400
+ sourcePath: "src/gdskills/bundled/skills/orchestration/task-implementer/input-contract.schema.json",
43401
+ enforcement: {
43402
+ kind: "none",
43403
+ reason: "Dispatched agent-to-agent, like flow-orchestrator-input. The skill's Phase 1.4 lists five `ASSERT \u2026 \u2192 ABORT(\u2026)` refusals; registering the contract is what lets `keryx skills contracts validate` perform them, and nothing forces that call. The comment beside this registration has said so since it was written."
43404
+ }
43286
43405
  },
43287
43406
  {
43288
43407
  name: "task-implementer-output",
43289
43408
  fileName: "task-implementer-output-contract.schema.json",
43290
43409
  description: "JSON result task-implementer writes in Phase 6.1 before it emits its STATUS line.",
43291
- sourcePath: "src/gdskills/bundled/skills/orchestration/task-implementer/output-contract.schema.json"
43410
+ sourcePath: "src/gdskills/bundled/skills/orchestration/task-implementer/output-contract.schema.json",
43411
+ enforcement: {
43412
+ kind: "none",
43413
+ reason: "The skill writes this file itself, in its own runtime, and no keryx command reads it back. Contrast subagent-result, which is the same shape of thing and IS enforced for one reason: the harness reads that one back and must decide whether it is well-formed."
43414
+ }
43292
43415
  }
43293
43416
  ];
43294
43417
  function normalizeContractName(value) {
@@ -44483,6 +44606,13 @@ function renderMetaprojectGitignoreBlock() {
44483
44606
  .metaproject/data/security/raw/**
44484
44607
  .metaproject/data/security/artifacts/latest.md
44485
44608
  .metaproject/data/security/artifacts/latest.json
44609
+ # Forgetting/retention runtime state: the auto-sweep stamp is rewritten by the
44610
+ # gdctx write path on every call, not a project artifact. These lines were added
44611
+ # to this repository's .gitignore by hand INSIDE the managed block, where the
44612
+ # next \`keryx update\` regenerated them away \u2014 which is how they came to belong
44613
+ # here instead.
44614
+ .metaproject/data/forgetting/
44615
+ .metaproject/data/retention/
44486
44616
  .metaproject/reports/
44487
44617
  `;
44488
44618
  }
@@ -46155,7 +46285,33 @@ function renderGdgraphPostCommitHook() {
46155
46285
  return 0
46156
46286
  fi
46157
46287
 
46158
- if ! printf '%s\\n' "$changed_files" | grep -E '(^src/|^lib/|^app/|^packages/|^services/|^scripts/|^docs/|^\\.metaproject/(modules|skills|rules)/|package\\.json$|tsconfig.*\\.json$|bun\\.lockb?$|pnpm-lock\\.yaml$|yarn\\.lock$|package-lock\\.json$)' >/dev/null 2>&1; then
46288
+ # Two questions, because the graph is fed by two different things.
46289
+ #
46290
+ # The second one is here because the first one alone was wrong. It lists
46291
+ # directory prefixes \u2014 src/, lib/, app/ and so on \u2014 while the builder indexes
46292
+ # any source file anywhere except the directories it never walks. Everything
46293
+ # in between was indexed AND undetected: a commit to \`vscode-extension/src/\`,
46294
+ # to a source file at the repository root, or to any directory not on the
46295
+ # list left the graph stale and printed NOTHING. Every other path in this
46296
+ # function says something when the graph may be stale; that one returned 0 in
46297
+ # silence, which is the failure this hook exists to end.
46298
+ #
46299
+ # The prefix list is kept rather than replaced, so nothing that rebuilt
46300
+ # before stops rebuilding: it covers paths that change how the graph resolves
46301
+ # without being indexed themselves, such as docs/ and the metaproject rules.
46302
+ # The extension test only widens.
46303
+ #
46304
+ # The exclusion mirrors IGNORE_DIRS in src/gdgraph/build.ts. It has to: a
46305
+ # rebuild triggered by a file the builder never reads is a rebuild that
46306
+ # cannot change the answer, and every dependency bump would pay for it.
46307
+ if printf '%s\\n' "$changed_files" | grep -E '(^src/|^lib/|^app/|^packages/|^services/|^scripts/|^docs/|^\\.metaproject/(modules|skills|rules)/|package\\.json$|tsconfig.*\\.json$|bun\\.lockb?$|pnpm-lock\\.yaml$|yarn\\.lock$|package-lock\\.json$)' >/dev/null 2>&1; then
46308
+ :
46309
+ elif printf '%s\\n' "$changed_files" \\
46310
+ | grep -E '\\.(ts|tsx|js|jsx|java|py)$' \\
46311
+ | grep -Ev '(^|/)(\\.git|\\.claude|\\.metaproject|node_modules|\\.cache|\\.docusaurus|\\.next|\\.turbo|dist|build|coverage|generated|out|public|storybook-static)/' \\
46312
+ >/dev/null 2>&1; then
46313
+ :
46314
+ else
46159
46315
  return 0
46160
46316
  fi
46161
46317
 
@@ -47005,7 +47161,7 @@ import path43 from "path";
47005
47161
  // package.json
47006
47162
  var package_default = {
47007
47163
  name: "@mrciphersmith/keryx",
47008
- version: "0.2.84",
47164
+ version: "0.2.88",
47009
47165
  description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
47010
47166
  private: false,
47011
47167
  publishConfig: {
@@ -47059,6 +47215,7 @@ var package_default = {
47059
47215
  check: "bun run lint && bun run typecheck && bun run typecheck:scripts && bun test",
47060
47216
  "check:core": "bun run lint && bun run typecheck && bun run typecheck:scripts && bun run test:core",
47061
47217
  "check:doc-links": "bun scripts/check-doc-links.ts",
47218
+ "check:retired-spellings": "bun scripts/check-retired-cli-spellings.ts",
47062
47219
  "baseline:review-precision": "bun scripts/review-precision-baseline.ts",
47063
47220
  "test:guards": "bun test src/lib/config-dir.ast.test.ts src/lib/config-dir.readers.test.ts src/lib/production-graph.test.ts src/harness/policy/profiles.test.ts src/lib/serve-server.test.ts src/gdskills/agent-catalogue-xref.test.ts src/gdskills/enforcement-claims.test.ts",
47064
47221
  lint: "eslint ."
@@ -48494,7 +48651,7 @@ var MCP_MANAGED_KEY = "_keryxManaged";
48494
48651
  var MCP_MANAGED_SENTINEL = "mcp-client-config";
48495
48652
  var MCP_SERVER_NAME = "keryx";
48496
48653
  var MCP_SERVER_COMMAND = "keryx";
48497
- var MCP_SERVER_ARGS = ["mcp", "serve"];
48654
+ var MCP_SERVER_ARGS = ["serve-mcp"];
48498
48655
  var MCP_SDK_HINT = "bun add @modelcontextprotocol/sdk";
48499
48656
  function mcpServerEntry(projectRoot) {
48500
48657
  const args = projectRoot ? [...MCP_SERVER_ARGS, "--cwd", projectRoot] : [...MCP_SERVER_ARGS];
@@ -48812,12 +48969,12 @@ protocol adapter \u2014 it defines no new module logic.
48812
48969
 
48813
48970
  ## Commands
48814
48971
 
48815
- - \`keryx mcp serve\` \u2014 stdio JSON-RPC MCP server (default transport).
48816
- - \`keryx mcp serve --http\` \u2014 isolated HTTP/SSE opt-in (localhost only;
48972
+ - \`keryx serve-mcp\` \u2014 stdio JSON-RPC MCP server (default transport).
48973
+ - \`keryx serve-mcp --http\` \u2014 isolated HTTP/SSE opt-in (localhost only;
48817
48974
  requires \`http.enabled=true\` in this module's manifest entry).
48818
- - \`keryx mcp serve --cwd <project-root>\` \u2014 expose a specific project,
48975
+ - \`keryx serve-mcp --cwd <project-root>\` \u2014 expose a specific project,
48819
48976
  independent of the MCP client's launch directory.
48820
- - \`keryx mcp install --runtime <cursor|claude|opencode|vscode|generic|all> [--dry-run]\` \u2014
48977
+ - \`keryx integrate <cursor|claude|opencode|vscode|generic|all> [--dry-run]\` \u2014
48821
48978
  wire this project into an editor/agent: writes a project-local client
48822
48979
  config (cursor \u2192 \`.cursor/mcp.json\`, claude \u2192 \`.mcp.json\`, opencode \u2192
48823
48980
  \`opencode.json\`, vscode \u2192 \`.vscode/mcp.json\`) and sets
@@ -48827,17 +48984,17 @@ protocol adapter \u2014 it defines no new module logic.
48827
48984
  step; hand-editing a client config file directly is unnecessary and skips
48828
48985
  setting \`modules.mcp.enabled\`. \`all\` expands to cursor + claude +
48829
48986
  opencode; \`vscode\` is opt-in only (not bundled into \`all\`) \u2014 request it
48830
- explicitly with \`--runtime vscode\`.
48831
- - \`keryx mcp uninstall --runtime <cursor|claude|opencode|vscode|generic|all>\` \u2014
48987
+ explicitly as \`keryx integrate vscode\`.
48988
+ - \`keryx integrate --remove <cursor|claude|opencode|vscode|generic|all>\` \u2014
48832
48989
  remove the managed client config again.
48833
48990
  - **codex CLI**: not a \`--runtime\` here \u2014 codex's client config is a single
48834
48991
  GLOBAL \`~/.codex/config.toml\`, not a project-local file, and it already
48835
48992
  ships its own safe, native installer for it. Run
48836
- \`codex mcp add keryx -- keryx mcp serve --cwd <project-root>\` once
48993
+ \`codex mcp add keryx -- keryx serve-mcp --cwd <project-root>\` once
48837
48994
  (verified live: codex successfully discovers and calls this server's
48838
48995
  tools headlessly with \`codex exec --approve-for-me\`); \`codex mcp remove
48839
48996
  keryx\` to undo. \`modules.mcp.enabled=true\` still needs
48840
- \`keryx mcp install --runtime generic\` (or any other runtime) run once,
48997
+ \`keryx integrate generic\` (or any other editor) run once,
48841
48998
  since codex's own installer has no notion of the keryx manifest.
48842
48999
 
48843
49000
  ## Notes
@@ -48952,7 +49109,7 @@ async function installMcpClient(projectRoot, ids, options = {}) {
48952
49109
  const sdk = await probeMcpSdk();
48953
49110
  return { outcomes, unknown, manifest, sdk, dryRun };
48954
49111
  }
48955
- async function uninstallMcpClient(projectRoot, ids) {
49112
+ async function uninstallMcpClient(projectRoot, ids, options = {}) {
48956
49113
  const absoluteProjectRoot = path52.resolve(projectRoot);
48957
49114
  const { runtimes, unknown } = resolveMcpRuntimes(ids);
48958
49115
  const outcomes = [];
@@ -48969,7 +49126,9 @@ async function uninstallMcpClient(projectRoot, ids) {
48969
49126
  const settings = await readSettings2(file);
48970
49127
  const hadManaged = runtime.hasManaged(settings);
48971
49128
  const stripped = runtime.strip(settings);
48972
- await writeSettings2(file, stripped);
49129
+ if (options.dryRun !== true) {
49130
+ await writeSettings2(file, stripped);
49131
+ }
48973
49132
  outcomes.push({ id: runtime.id, filePath: file, removed: hadManaged });
48974
49133
  }
48975
49134
  return { outcomes, unknown };
@@ -49589,7 +49748,7 @@ async function initCommand(args) {
49589
49748
  }
49590
49749
  }
49591
49750
  if (!report.sdk.available) {
49592
- note(`Optional MCP SDK not found \u2014 install it to run \`keryx mcp serve\`: ${report.sdk.hint}`);
49751
+ note(`Optional MCP SDK not found \u2014 install it to run \`keryx serve-mcp\`: ${report.sdk.hint}`);
49593
49752
  }
49594
49753
  }
49595
49754
  const enabledModuleCount = [
@@ -49674,7 +49833,7 @@ async function initCommand(args) {
49674
49833
  steps.push(`After pulling changes, run ${style.cyan("keryx update")} to refresh service files.`);
49675
49834
  nextSteps(steps);
49676
49835
  if (!enableMcp && !options.yes) {
49677
- note(`Wire an editor/agent to the MCP server later with ${style.cyan("keryx mcp install")}.`);
49836
+ note(`Wire an editor/agent to the MCP server later with ${style.cyan("keryx integrate <editor>")}.`);
49678
49837
  }
49679
49838
  }
49680
49839
  function parseInitArgs(args) {
@@ -49929,7 +50088,7 @@ ${blockEnd}`;
49929
50088
  const existing = await pathExists(hookPath) ? await readFile23(hookPath, "utf8") : `#!/usr/bin/env sh
49930
50089
  `;
49931
50090
  const blockPattern = new RegExp(`${escapeRegExp2(blockStart)}[\\s\\S]*?${escapeRegExp2(blockEnd)}`);
49932
- const next = blockPattern.test(existing) ? existing.replace(blockPattern, managedBlock) : `${existing.trimEnd()}
50091
+ const next = blockPattern.test(existing) ? existing.replace(blockPattern, () => managedBlock) : `${existing.trimEnd()}
49933
50092
 
49934
50093
  ${managedBlock}
49935
50094
  `;
@@ -59686,12 +59845,16 @@ async function runInit(args) {
59686
59845
  cwd: process.cwd(),
59687
59846
  title: optionValue(args, "--title"),
59688
59847
  issue: optionValue(args, "--issue"),
59689
- slug: optionValue(args, "--slug")
59848
+ slug: optionValue(args, "--slug"),
59849
+ baseBranch: optionValue(args, "--base")
59690
59850
  });
59691
59851
  banner("flow init", `Created flow ${result.flow.id}`);
59692
59852
  console.log(` ${style.green(symbols.ok)} ${style.bold(result.flow.title)}`);
59693
59853
  note(result.dir);
59694
59854
  console.log(` status: ${flowStatusLabel(result.flow.status)}`);
59855
+ if (result.flow.baseBranch !== undefined) {
59856
+ console.log(` base: ${result.flow.baseBranch}`);
59857
+ }
59695
59858
  if (result.contextNotes.length > 0) {
59696
59859
  heading("Context collected");
59697
59860
  for (const contextNote of result.contextNotes) {
@@ -60095,7 +60258,7 @@ function requireId(args) {
60095
60258
  function printHelp10() {
60096
60259
  helpTitle("keryx flow", "agent-first managed work (flows)");
60097
60260
  helpUsage([
60098
- 'keryx flow init (--issue <url> | --title "<t>") [--slug <s>]',
60261
+ 'keryx flow init (--issue <url> | --title "<t>") [--slug <s>] [--base <branch>]',
60099
60262
  "keryx flow list",
60100
60263
  "keryx flow status <id>",
60101
60264
  "keryx flow freeze <id>",
@@ -64724,6 +64887,7 @@ var COMMENTS_REPLY_FLAGS = [
64724
64887
  "--pr",
64725
64888
  "--self",
64726
64889
  "--outcomes",
64890
+ "--result",
64727
64891
  "--sha",
64728
64892
  "--round",
64729
64893
  "--final",
@@ -65183,6 +65347,7 @@ async function runCommentsReply(args) {
65183
65347
  const round = parseNonNegativeInteger(optionValue(args, "--round"), "--round") ?? 1;
65184
65348
  const isFinal = args.includes("--final");
65185
65349
  const dryRun = args.includes("--dry-run");
65350
+ await refuseInvalidResult(optionValue(args, "--result"), optionValue(args, "--outcomes"));
65186
65351
  const cwd = process.cwd();
65187
65352
  const port = await resolvePort(args);
65188
65353
  const state = await readPrCommentState(cwd, repo, number);
@@ -65262,6 +65427,28 @@ async function resolveSelfLogin(args) {
65262
65427
  }
65263
65428
  return login;
65264
65429
  }
65430
+ async function refuseInvalidResult(source, outcomesSource) {
65431
+ if (source === undefined) {
65432
+ return;
65433
+ }
65434
+ if (source === "-" && outcomesSource === "-") {
65435
+ throw new Error("`--result -` and `--outcomes -` both read standard input, which can only be consumed once. Pass at least one of them as a file path.");
65436
+ }
65437
+ const raw = source === "-" ? await Bun.stdin.text() : await readFile87(source, "utf8");
65438
+ let parsed;
65439
+ try {
65440
+ parsed = JSON.parse(raw);
65441
+ } catch (error) {
65442
+ throw new Error(`\`--result ${source}\` is not JSON: ${error instanceof Error ? error.message : String(error)}. Nothing was posted.`);
65443
+ }
65444
+ const schema = await loadSchema("review-pr-feedback-output");
65445
+ const errors = await validateJson(parsed, schema);
65446
+ if (errors.length > 0) {
65447
+ throw new Error(`\`--result ${source}\` does not satisfy the review-pr-feedback-output contract, so nothing was posted:
65448
+ ${errors.map((error) => ` ${error.path}: ${error.message}`).join(`
65449
+ `)}`);
65450
+ }
65451
+ }
65265
65452
  async function readOutcomes(source) {
65266
65453
  if (source === undefined) {
65267
65454
  throw new Error("`--outcomes <file|->` is required: it carries one disposition and one reply sentence per collected comment. The judgement is the model's; this command only enforces the budget, the threading and the once-at-the-end rule.");
@@ -69187,12 +69374,109 @@ async function sandboxCommand(args = [], deps = {}) {
69187
69374
 
69188
69375
  // src/commands/mcp.ts
69189
69376
  init_args();
69190
- import path168 from "path";
69377
+
69378
+ // src/commands/integrate.ts
69379
+ import path162 from "path";
69380
+ var EDITOR_USAGE = `<cursor|claude|opencode|vscode|generic|all>`;
69381
+ var DEFAULT_EDITORS = ["all"];
69382
+ async function integrateCommand(args = [], cwd = process.cwd(), label) {
69383
+ if (args.includes("--help") || args.includes("-h")) {
69384
+ printIntegrateHelp();
69385
+ return;
69386
+ }
69387
+ const editors = parseEditors(args);
69388
+ if (args.includes("--remove")) {
69389
+ await removeIntegration(cwd, editors, args.includes("--dry-run"), label ?? "keryx integrate --remove");
69390
+ return;
69391
+ }
69392
+ await addIntegration(cwd, editors, args.includes("--dry-run"), label ?? "keryx integrate");
69393
+ }
69394
+ function parseEditors(args) {
69395
+ const named = args.filter((argument) => !argument.startsWith("-")).flatMap((argument) => argument.split(",")).map((argument) => argument.trim()).filter(Boolean);
69396
+ return named.length > 0 ? named : [...DEFAULT_EDITORS];
69397
+ }
69398
+ async function addIntegration(cwd, editors, dryRun, label) {
69399
+ const report = await installMcpClient(cwd, editors, { dryRun });
69400
+ if (report.unknown.length > 0) {
69401
+ console.error(`Unknown runtime(s): ${report.unknown.join(", ")}`);
69402
+ process.exitCode = 1;
69403
+ return;
69404
+ }
69405
+ heading(`${label}${dryRun ? " (dry run)" : ""}`);
69406
+ for (const outcome of report.outcomes) {
69407
+ if (outcome.filePath === null) {
69408
+ console.log(` ${style.cyan(symbols.arrow)} ${outcome.id} \u2014 paste this into your MCP client config:`);
69409
+ console.log(outcome.snippet ?? "");
69410
+ continue;
69411
+ }
69412
+ const rel = path162.relative(cwd, outcome.filePath);
69413
+ if (outcome.errors.length > 0) {
69414
+ for (const e of outcome.errors) {
69415
+ console.log(` ${style.red(symbols.cross)} ${e}`);
69416
+ }
69417
+ process.exitCode = 1;
69418
+ continue;
69419
+ }
69420
+ if (dryRun) {
69421
+ console.log(` ${style.cyan(symbols.arrow)} ${outcome.id} \u2192 would write ${rel}:`);
69422
+ console.log(outcome.snippet ?? "");
69423
+ } else {
69424
+ console.log(` ${style.green(symbols.ok)} ${outcome.id} \u2192 ${rel}`);
69425
+ }
69426
+ }
69427
+ if (report.manifest.message) {
69428
+ note(report.manifest.message);
69429
+ } else if (dryRun && report.manifest.changed) {
69430
+ note("would set modules.mcp.enabled=true in .metaproject/metaproject.json");
69431
+ } else if (report.manifest.changed) {
69432
+ note("set modules.mcp.enabled=true in .metaproject/metaproject.json");
69433
+ }
69434
+ if (!report.sdk.available) {
69435
+ note(`Optional MCP SDK not found \u2014 install it to run \`keryx serve-mcp\`: ${report.sdk.hint}`);
69436
+ }
69437
+ }
69438
+ async function removeIntegration(cwd, editors, dryRun, label) {
69439
+ const report = await uninstallMcpClient(cwd, editors, { dryRun });
69440
+ if (report.unknown.length > 0) {
69441
+ console.error(`Unknown runtime(s): ${report.unknown.join(", ")}`);
69442
+ process.exitCode = 1;
69443
+ return;
69444
+ }
69445
+ heading(label);
69446
+ for (const outcome of report.outcomes) {
69447
+ if (outcome.filePath === null) {
69448
+ console.log(` ${style.gray(symbols.off)} ${outcome.id} ${style.dim("no file to change")}`);
69449
+ continue;
69450
+ }
69451
+ const rel = path162.relative(cwd, outcome.filePath);
69452
+ 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")}`);
69453
+ }
69454
+ }
69455
+ function printIntegrateHelp() {
69456
+ helpTitle("keryx integrate", "wire this project into an editor or agent as an MCP server");
69457
+ helpUsage([
69458
+ `keryx integrate ${EDITOR_USAGE} # write the client config`,
69459
+ `keryx integrate --remove ${EDITOR_USAGE} # remove the managed keryx server`,
69460
+ `keryx integrate ${EDITOR_USAGE} --dry-run # print the planned change, write nothing`
69461
+ ]);
69462
+ helpOptions([
69463
+ { flag: "--remove", desc: "Remove the managed keryx server instead of writing it." },
69464
+ { flag: "--dry-run", desc: "Print the planned change and write nothing. Works with --remove too." }
69465
+ ]);
69466
+ heading("Notes");
69467
+ console.log(` ${style.dim(`Editors: ${mcpRuntimeIds().join(", ")}, or all (=cursor,claude,opencode). Comma-separated. Default: all.`)}`);
69468
+ console.log(` ${style.dim("Writes a project-local MCP client config (cursor \u2192 .cursor/mcp.json, claude \u2192 .mcp.json, opencode \u2192 opencode.json, vscode \u2192 .vscode/mcp.json), sets modules.mcp.enabled=true, and prints a snippet for `generic`. `vscode` is opt-in only \u2014 not included in `all`.")}`);
69469
+ console.log(` ${style.dim("Only probes the optional @modelcontextprotocol/sdk \u2014 it never installs it and never opens a network connection.")}`);
69470
+ }
69471
+
69472
+ // src/commands/serve-mcp.ts
69473
+ init_args();
69474
+ import path169 from "path";
69191
69475
 
69192
69476
  // src/mcp/discovery.ts
69193
69477
  init_fs();
69194
69478
  init_json();
69195
- import path162 from "path";
69479
+ import path163 from "path";
69196
69480
  var MODULE_MANIFEST_KEY = {
69197
69481
  gdgraph: "gdgraph",
69198
69482
  security: "security",
@@ -69254,7 +69538,7 @@ function buildDiscovery(manifest) {
69254
69538
  };
69255
69539
  }
69256
69540
  async function loadDiscovery(cwd) {
69257
- const manifestPath = path162.join(cwd, ".metaproject", "metaproject.json");
69541
+ const manifestPath = path163.join(cwd, ".metaproject", "metaproject.json");
69258
69542
  if (!await pathExists(manifestPath)) {
69259
69543
  return buildDiscovery({});
69260
69544
  }
@@ -69327,7 +69611,7 @@ init_store2();
69327
69611
  init_fs();
69328
69612
  import { createHash as createHash33, randomUUID as randomUUID17 } from "crypto";
69329
69613
  import { appendFile as appendFile7, mkdir as mkdir58, open as open2, readFile as readFile93, rename as rename7, rm as rm12, stat as stat16, writeFile as writeFile50 } from "fs/promises";
69330
- import path163 from "path";
69614
+ import path164 from "path";
69331
69615
 
69332
69616
  // src/sac/policy-experiment.ts
69333
69617
  import { createHash as createHash32, createHmac as createHmac2 } from "crypto";
@@ -69683,7 +69967,7 @@ function hasReportIntegrity(report) {
69683
69967
  return reportHashPattern.test(reportDigest) && sha2565(body) === reportDigest;
69684
69968
  }
69685
69969
  async function resolvePolicySelection(workspaceRoot, canonicalFallback) {
69686
- const configPath = path163.join(workspaceRoot, ...policyExperimentConfigPath);
69970
+ const configPath = path164.join(workspaceRoot, ...policyExperimentConfigPath);
69687
69971
  let configJson;
69688
69972
  try {
69689
69973
  const raw = await readFile93(configPath, "utf8").catch((error) => {
@@ -69785,7 +70069,7 @@ async function diagnosePolicyReadiness(workspaceRoot) {
69785
70069
  const integrityReady = steps.every((entry) => entry.step === "activation-flags" || entry.status === "pass");
69786
70070
  return Object.freeze({ configPresent, enabled, killSwitch, integrityReady, candidateWouldActivate: integrityReady && enabled && !killSwitch, steps: Object.freeze([...steps]) });
69787
70071
  };
69788
- const configPath = path163.join(workspaceRoot, ...policyExperimentConfigPath);
70072
+ const configPath = path164.join(workspaceRoot, ...policyExperimentConfigPath);
69789
70073
  let configJson;
69790
70074
  try {
69791
70075
  const raw = await readFile93(configPath, "utf8").catch((error) => {
@@ -70040,9 +70324,9 @@ class FwkReadService {
70040
70324
  const recordedAt = nowIso2(now);
70041
70325
  const id = `receipt-${randomUUID17().replace(/-/g, "").slice(0, 16)}`;
70042
70326
  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"}` } : {} };
70043
- const ledger = path163.join(this.options.canonical.workspaceRoot, ".metaproject", "context-operations", "access-receipts.jsonl");
70044
- const checkpointPath = path163.join(path163.dirname(ledger), "access-receipts.checkpoint.json");
70045
- await mkdir58(path163.dirname(ledger), { recursive: true, mode: 448 });
70327
+ const ledger = path164.join(this.options.canonical.workspaceRoot, ".metaproject", "context-operations", "access-receipts.jsonl");
70328
+ const checkpointPath = path164.join(path164.dirname(ledger), "access-receipts.checkpoint.json");
70329
+ await mkdir58(path164.dirname(ledger), { recursive: true, mode: 448 });
70046
70330
  return withFileLock2(`${ledger}.lock`, async () => {
70047
70331
  const state = await resolveLedgerState(ledger, checkpointPath, this.options.verifyReceiptLedger ?? verifyAccessReceiptLedger);
70048
70332
  const receipt = sealAccessReceipt(base, state.headHash);
@@ -70154,7 +70438,7 @@ init_workspace_service();
70154
70438
  init_sac();
70155
70439
  import { appendFile as appendFile8, mkdir as mkdir59, readFile as readFile94 } from "fs/promises";
70156
70440
  import { randomUUID as randomUUID18 } from "crypto";
70157
- import path164 from "path";
70441
+ import path165 from "path";
70158
70442
 
70159
70443
  class CollaborationServiceError extends Error {
70160
70444
  code;
@@ -70170,7 +70454,7 @@ class CollaborationService {
70170
70454
  this.input = input;
70171
70455
  }
70172
70456
  file(id) {
70173
- return path164.join(this.input.workspaceRoot, ".metaproject", "workspaces", id, "activity.jsonl");
70457
+ return path165.join(this.input.workspaceRoot, ".metaproject", "workspaces", id, "activity.jsonl");
70174
70458
  }
70175
70459
  async overview(input) {
70176
70460
  const manifest = await this.input.workspaces.show(input);
@@ -70207,7 +70491,7 @@ class CollaborationService {
70207
70491
  occurredAt: (this.input.now ?? (() => new Date))().toISOString(),
70208
70492
  ...suppliedHandoff ? { handoff: { ...suppliedHandoff, from: actor.subject } } : {}
70209
70493
  });
70210
- await mkdir59(path164.dirname(this.file(manifest.id)), { recursive: true, mode: 448 });
70494
+ await mkdir59(path165.dirname(this.file(manifest.id)), { recursive: true, mode: 448 });
70211
70495
  await appendFile8(this.file(manifest.id), `${JSON.stringify(activity)}
70212
70496
  `, { mode: 384 });
70213
70497
  return normalizeCollaborationResult(activity);
@@ -70262,9 +70546,9 @@ init_workspace_service();
70262
70546
  init_fs();
70263
70547
  init_machine_wrap_up();
70264
70548
  import { mkdir as mkdir60, readdir as readdir31, readFile as readFile95 } from "fs/promises";
70265
- import path165 from "path";
70549
+ import path166 from "path";
70266
70550
  function externalSlatesDir(cwd) {
70267
- return path165.join(cwd, ".keryx", "external-slates");
70551
+ return path166.join(cwd, ".keryx", "external-slates");
70268
70552
  }
70269
70553
  var EXTERNAL_SESSION_ID_PATTERN = /^[A-Za-z0-9_-]{1,128}$/;
70270
70554
  function assertValidExternalSessionId(externalSessionId) {
@@ -70274,14 +70558,14 @@ function assertValidExternalSessionId(externalSessionId) {
70274
70558
  }
70275
70559
  function externalSlatePath(cwd, externalSessionId) {
70276
70560
  assertValidExternalSessionId(externalSessionId);
70277
- return path165.join(externalSlatesDir(cwd), `${externalSessionId}.json`);
70561
+ return path166.join(externalSlatesDir(cwd), `${externalSessionId}.json`);
70278
70562
  }
70279
70563
  function externalSlateLockPath(cwd, externalSessionId) {
70280
70564
  return `${externalSlatePath(cwd, externalSessionId)}.lock`;
70281
70565
  }
70282
70566
  function externalSlateEvidenceDir(cwd, externalSessionId) {
70283
70567
  assertValidExternalSessionId(externalSessionId);
70284
- return path165.join(externalSlatesDir(cwd), externalSessionId);
70568
+ return path166.join(externalSlatesDir(cwd), externalSessionId);
70285
70569
  }
70286
70570
  async function readExternalSlate(cwd, externalSessionId) {
70287
70571
  try {
@@ -70926,7 +71210,7 @@ function buildToolRegistry() {
70926
71210
  // src/mcp/resources.ts
70927
71211
  init_fs();
70928
71212
  init_contained_read();
70929
- import path166 from "path";
71213
+ import path167 from "path";
70930
71214
  import { lstat as lstat4, readdir as readdir32, realpath as realpath7, stat as stat17 } from "fs/promises";
70931
71215
  var URI_PREFIX = "metaproject://";
70932
71216
  function mimeForPath(filePath) {
@@ -70939,13 +71223,13 @@ function mimeForPath(filePath) {
70939
71223
  return "text/plain";
70940
71224
  }
70941
71225
  function dataRoot3(cwd) {
70942
- return path166.join(cwd, ".metaproject", "data");
71226
+ return path167.join(cwd, ".metaproject", "data");
70943
71227
  }
70944
71228
  function wikiRoot(cwd) {
70945
- return path166.join(cwd, ".metaproject", "wiki");
71229
+ return path167.join(cwd, ".metaproject", "wiki");
70946
71230
  }
70947
71231
  function memoryRoot2(cwd) {
70948
- return path166.join(cwd, ".metaproject", "memory");
71232
+ return path167.join(cwd, ".metaproject", "memory");
70949
71233
  }
70950
71234
  var MAX_RESOURCE_BYTES = 8 * 1024 * 1024;
70951
71235
  async function walkFiles(root, ownerRoot = root, ancestors = new Set) {
@@ -70971,7 +71255,7 @@ async function walkFiles(root, ownerRoot = root, ancestors = new Set) {
70971
71255
  return [];
70972
71256
  }
70973
71257
  for (const entry of entries) {
70974
- const full = path166.join(root, entry.name);
71258
+ const full = path167.join(root, entry.name);
70975
71259
  const info = await stat17(full).catch(() => null);
70976
71260
  if (!info || !isPathInside(ownerReal, await realpath7(full).catch(() => "")))
70977
71261
  continue;
@@ -70999,9 +71283,9 @@ async function listArtifacts(cwd) {
70999
71283
  if (!moduleEntry.isDirectory()) {
71000
71284
  continue;
71001
71285
  }
71002
- const artifactsDir = path166.join(base, moduleEntry.name, "artifacts");
71286
+ const artifactsDir = path167.join(base, moduleEntry.name, "artifacts");
71003
71287
  for (const file of await walkFiles(artifactsDir)) {
71004
- const rel = toPosix(path166.relative(artifactsDir, file));
71288
+ const rel = toPosix(path167.relative(artifactsDir, file));
71005
71289
  const relPath = `${moduleEntry.name}/${rel}`;
71006
71290
  listings.push({
71007
71291
  uri: `${URI_PREFIX}artifacts/${relPath}`,
@@ -71015,7 +71299,7 @@ async function listArtifacts(cwd) {
71015
71299
  async function listUnderRoot(cwd, cls, root) {
71016
71300
  const listings = [];
71017
71301
  for (const file of await walkFiles(root)) {
71018
- const rel = toPosix(path166.relative(root, file));
71302
+ const rel = toPosix(path167.relative(root, file));
71019
71303
  listings.push({
71020
71304
  uri: `${URI_PREFIX}${cls}/${rel}`,
71021
71305
  name: rel,
@@ -71065,12 +71349,12 @@ function resolveConfined(cwd, cls, relPath) {
71065
71349
  if (moduleName.includes("..") || moduleName.length === 0) {
71066
71350
  return null;
71067
71351
  }
71068
- const root = path166.join(dataRoot3(cwd), moduleName, "artifacts");
71069
- const absolute = path166.resolve(root, rest);
71352
+ const root = path167.join(dataRoot3(cwd), moduleName, "artifacts");
71353
+ const absolute = path167.resolve(root, rest);
71070
71354
  return isPathInside(root, absolute) ? { root, absolute } : null;
71071
71355
  }
71072
71356
  const root = cls === "wiki" ? wikiRoot(cwd) : memoryRoot2(cwd);
71073
- const absolute = path166.resolve(root, relPath);
71357
+ const absolute = path167.resolve(root, relPath);
71074
71358
  return isPathInside(root, absolute) ? { root, absolute } : null;
71075
71359
  }
71076
71360
  async function readResource(cwd, roots, uri) {
@@ -71183,7 +71467,7 @@ class McpSdkMissingError extends Error {
71183
71467
  "Install it (it is an optional dependency):",
71184
71468
  " bun add @modelcontextprotocol/sdk",
71185
71469
  "",
71186
- "Then re-run `keryx mcp serve`. All other commands run without the SDK."
71470
+ "Then re-run `keryx serve-mcp`. All other commands run without the SDK."
71187
71471
  ].join(`
71188
71472
  `));
71189
71473
  this.name = "McpSdkMissingError";
@@ -71274,6 +71558,36 @@ function resolveServeRoot(explicitCwd, processCwd, env) {
71274
71558
  return processCwd;
71275
71559
  }
71276
71560
 
71561
+ // src/commands/serve-mcp.ts
71562
+ async function serveMcpCommand(args = [], cwd = process.cwd()) {
71563
+ if (args.includes("--help") || args.includes("-h")) {
71564
+ printServeMcpHelp();
71565
+ return;
71566
+ }
71567
+ const http = args.includes("--http");
71568
+ const projectRoot = path169.resolve(resolveServeRoot(optionValue(args, "--cwd"), cwd, process.env));
71569
+ try {
71570
+ await serveMcp({ cwd: projectRoot, http });
71571
+ } catch (error) {
71572
+ console.error(error instanceof Error ? error.message : String(error));
71573
+ process.exitCode = 1;
71574
+ }
71575
+ }
71576
+ function printServeMcpHelp() {
71577
+ helpTitle("keryx serve-mcp", "expose Metaproject services over the Model Context Protocol");
71578
+ helpUsage([
71579
+ "keryx serve-mcp [--cwd <project-root>] # stdio JSON-RPC MCP server (default)",
71580
+ "keryx serve-mcp --http [--cwd <project-root>] # HTTP/SSE opt-in (requires capabilities.http.enabled)"
71581
+ ]);
71582
+ helpOptions([
71583
+ { flag: "--http", desc: "Use the isolated HTTP/SSE transport (localhost only) instead of stdio." },
71584
+ { flag: "--cwd", desc: "Project root whose .metaproject workspace should be exposed. Defaults to the process cwd." }
71585
+ ]);
71586
+ heading("Notes");
71587
+ console.log(` ${style.dim("Requires the optional @modelcontextprotocol/sdk. Disabled by default (modules.mcp.enabled=false); `keryx integrate <editor>` turns it on and wires a client.")}`);
71588
+ console.log(` ${style.dim("`keryx mcp serve` is the retired spelling of this command. It still works and prints one deprecation line on stderr.")}`);
71589
+ }
71590
+
71277
71591
  // src/commands/mcp.ts
71278
71592
  async function mcpCommand(args = [], cwd = process.cwd()) {
71279
71593
  const subcommand = args[0];
@@ -71282,127 +71596,65 @@ async function mcpCommand(args = [], cwd = process.cwd()) {
71282
71596
  return;
71283
71597
  }
71284
71598
  if (subcommand === "install") {
71285
- await handleInstall2(cwd, args.slice(1));
71599
+ announceRename("`keryx mcp install --runtime <editor>`", "`keryx integrate <editor>`");
71600
+ await integrateCommand(toIntegrateArgs(args.slice(1)), cwd, "keryx mcp install");
71286
71601
  return;
71287
71602
  }
71288
71603
  if (subcommand === "uninstall") {
71289
- await handleUninstall2(cwd, args.slice(1));
71604
+ announceRename("`keryx mcp uninstall --runtime <editor>`", "`keryx integrate --remove <editor>`");
71605
+ await integrateCommand(["--remove", ...toIntegrateArgs(args.slice(1))], cwd, "keryx mcp uninstall");
71290
71606
  return;
71291
71607
  }
71292
71608
  if (!subcommand || subcommand === "serve") {
71293
- const http = args.includes("--http");
71294
- const projectRoot = path168.resolve(resolveServeRoot(optionValue(args, "--cwd"), cwd, process.env));
71295
- try {
71296
- await serveMcp({ cwd: projectRoot, http });
71297
- } catch (error) {
71298
- console.error(error instanceof Error ? error.message : String(error));
71299
- process.exitCode = 1;
71300
- }
71609
+ announceRename("`keryx mcp serve`", "`keryx serve-mcp`");
71610
+ await serveMcpCommand(args.slice(1), cwd);
71301
71611
  return;
71302
71612
  }
71303
71613
  console.error(`Unknown mcp command: ${subcommand}`);
71304
71614
  printMcpHelp();
71305
71615
  process.exitCode = 1;
71306
71616
  }
71307
- var RUNTIME_USAGE = `<cursor|claude|opencode|vscode|generic|all>`;
71308
- function parseRequestedRuntimes(args, fallback) {
71309
- const runtimeArg = optionValue(args, "--runtime") ?? fallback;
71310
- return runtimeArg.split(",").map((s) => s.trim()).filter(Boolean);
71311
- }
71312
- async function handleInstall2(cwd, args) {
71313
- const dryRun = args.includes("--dry-run");
71314
- const requested = parseRequestedRuntimes(args, "all");
71315
- const report = await installMcpClient(cwd, requested, { dryRun });
71316
- if (report.unknown.length > 0) {
71317
- console.error(`Unknown runtime(s): ${report.unknown.join(", ")}`);
71318
- process.exitCode = 1;
71319
- return;
71320
- }
71321
- heading(`keryx mcp install${dryRun ? " (dry run)" : ""}`);
71322
- for (const outcome of report.outcomes) {
71323
- if (outcome.filePath === null) {
71324
- console.log(` ${style.cyan(symbols.arrow)} ${outcome.id} \u2014 paste this into your MCP client config:`);
71325
- console.log(outcome.snippet ?? "");
71326
- continue;
71327
- }
71328
- const rel = path168.relative(cwd, outcome.filePath);
71329
- if (outcome.errors.length > 0) {
71330
- for (const e of outcome.errors) {
71331
- console.log(` ${style.red(symbols.cross)} ${e}`);
71332
- }
71333
- process.exitCode = 1;
71334
- continue;
71335
- }
71336
- if (dryRun) {
71337
- console.log(` ${style.cyan(symbols.arrow)} ${outcome.id} \u2192 would write ${rel}:`);
71338
- console.log(outcome.snippet ?? "");
71339
- } else {
71340
- console.log(` ${style.green(symbols.ok)} ${outcome.id} \u2192 ${rel}`);
71341
- }
71342
- }
71343
- if (report.manifest.message) {
71344
- note(report.manifest.message);
71345
- } else if (dryRun && report.manifest.changed) {
71346
- note("would set modules.mcp.enabled=true in .metaproject/metaproject.json");
71347
- } else if (report.manifest.changed) {
71348
- note("set modules.mcp.enabled=true in .metaproject/metaproject.json");
71349
- }
71350
- if (!report.sdk.available) {
71351
- note(`Optional MCP SDK not found \u2014 install it to run \`keryx mcp serve\`: ${report.sdk.hint}`);
71352
- }
71617
+ function announceRename(retired, replacement) {
71618
+ console.error(`${retired} is deprecated \u2014 use ${replacement} instead.`);
71353
71619
  }
71354
- async function handleUninstall2(cwd, args) {
71355
- const requested = parseRequestedRuntimes(args, "all");
71356
- const report = await uninstallMcpClient(cwd, requested);
71357
- if (report.unknown.length > 0) {
71358
- console.error(`Unknown runtime(s): ${report.unknown.join(", ")}`);
71359
- process.exitCode = 1;
71360
- return;
71361
- }
71362
- heading("keryx mcp uninstall");
71363
- for (const outcome of report.outcomes) {
71364
- if (outcome.filePath === null) {
71365
- console.log(` ${style.gray(symbols.off)} ${outcome.id} ${style.dim("no file to change")}`);
71366
- continue;
71367
- }
71368
- const rel = path168.relative(cwd, outcome.filePath);
71369
- console.log(` ${outcome.removed ? style.green(symbols.ok) : style.gray(symbols.off)} ${outcome.id} ${style.dim(outcome.removed ? `removed from ${rel}` : "nothing to remove")}`);
71370
- }
71620
+ function toIntegrateArgs(rest) {
71621
+ const runtimeArg = optionValue(rest, "--runtime");
71622
+ const editors = runtimeArg === undefined ? ["all"] : [runtimeArg];
71623
+ const flags = rest.filter((argument) => argument.startsWith("-") && argument !== "--runtime" && !argument.startsWith("--runtime="));
71624
+ return [...editors, ...flags];
71371
71625
  }
71372
71626
  function printMcpHelp() {
71373
- helpTitle("keryx mcp", "expose Metaproject services over the Model Context Protocol");
71627
+ helpTitle("keryx mcp", "retired spelling of the MCP publisher surface \u2014 see the replacements below");
71374
71628
  helpUsage([
71375
- "keryx mcp serve [--cwd <project-root>] # stdio JSON-RPC MCP server (default)",
71376
- "keryx mcp serve --http [--cwd <project-root>] # HTTP/SSE opt-in (requires capabilities.http.enabled)",
71377
- "keryx mcp # alias for `mcp serve`",
71378
- `keryx mcp install --runtime ${RUNTIME_USAGE} # wire this project into an editor/agent`,
71379
- `keryx mcp uninstall --runtime ${RUNTIME_USAGE} # remove the managed keryx server`
71629
+ "keryx serve-mcp [--http] [--cwd <project-root>] # replaces `keryx mcp serve`",
71630
+ `keryx integrate ${EDITOR_USAGE} # replaces \`keryx mcp install --runtime\``,
71631
+ `keryx integrate --remove ${EDITOR_USAGE} # replaces \`keryx mcp uninstall --runtime\``
71380
71632
  ]);
71381
71633
  helpOptions([
71382
- { flag: "--http", desc: "Use the isolated HTTP/SSE transport (localhost only) instead of stdio." },
71383
- { flag: "--cwd", desc: "Project root whose .metaproject workspace should be exposed. Defaults to the process cwd." },
71634
+ { flag: "--http", desc: "serve only: use the isolated HTTP/SSE transport (localhost only) instead of stdio." },
71635
+ { flag: "--cwd", desc: "serve only: project root whose .metaproject workspace should be exposed. Defaults to the process cwd." },
71384
71636
  {
71385
71637
  flag: "--runtime",
71386
- desc: `Target client(s) for install/uninstall: ${mcpRuntimeIds().join(", ")}, or all (=cursor,claude,opencode). Comma-separated. Default: all.`
71638
+ desc: `install/uninstall only: target client(s) \u2014 ${mcpRuntimeIds().join(", ")}, or all (=cursor,claude,opencode). Comma-separated. Default: all.`
71387
71639
  },
71388
71640
  { flag: "--dry-run", desc: "install only: print the planned change and write nothing." }
71389
71641
  ]);
71390
71642
  heading("Notes");
71391
- console.log(` ${style.dim("`install` writes a project-local MCP client config (cursor \u2192 .cursor/mcp.json, claude \u2192 .mcp.json, opencode \u2192 opencode.json, vscode \u2192 .vscode/mcp.json), sets modules.mcp.enabled=true, and prints a snippet for `generic`. `vscode` is opt-in only \u2014 not included in `all`.")}`);
71392
- console.log(` ${style.dim("Requires the optional @modelcontextprotocol/sdk to serve. Disabled by default (modules.mcp.enabled=false). `install` only probes the SDK \u2014 it never installs it or opens a network connection.")}`);
71643
+ console.log(` ${style.dim("Every `keryx mcp \u2026` invocation still works and still behaves identically; each prints one deprecation line on stderr naming its replacement.")}`);
71644
+ console.log(` ${style.dim("Run `keryx serve-mcp --help` or `keryx integrate --help` for the current surface.")}`);
71393
71645
  }
71394
71646
 
71395
71647
  // src/commands/status.ts
71396
71648
  init_fs();
71397
71649
  init_json();
71398
- import path169 from "path";
71650
+ import path170 from "path";
71399
71651
  async function statusCommand(args = []) {
71400
71652
  if (args.includes("--help") || args.includes("-h")) {
71401
71653
  printHelp16();
71402
71654
  return;
71403
71655
  }
71404
- const root = path169.join(process.cwd(), ".metaproject");
71405
- const manifestPath = path169.join(root, "metaproject.json");
71656
+ const root = path170.join(process.cwd(), ".metaproject");
71657
+ const manifestPath = path170.join(root, "metaproject.json");
71406
71658
  if (!await pathExists(root)) {
71407
71659
  console.log("Metaproject: not initialized");
71408
71660
  console.log("Run: keryx init");
@@ -71442,7 +71694,7 @@ Reports the workspace root and one enabled/disabled line per module. Use
71442
71694
 
71443
71695
  // src/commands/harness.ts
71444
71696
  import { readFileSync as readFileSync7, writeFileSync as writeFileSync7 } from "fs";
71445
- import path172 from "path";
71697
+ import path173 from "path";
71446
71698
  import { randomUUID as randomUUID20 } from "crypto";
71447
71699
 
71448
71700
  // src/security/harness-scan.ts
@@ -72515,7 +72767,7 @@ ${command.argv.join(" ")}`);
72515
72767
  }
72516
72768
 
72517
72769
  // src/harness/process/sandbox/profile.ts
72518
- import path170 from "path";
72770
+ import path171 from "path";
72519
72771
  var DEFAULT_SECRET_SUBPATHS = [
72520
72772
  ".ssh",
72521
72773
  ".gnupg",
@@ -72540,7 +72792,7 @@ function defaultReadDenyList(home) {
72540
72792
  if (!home) {
72541
72793
  return [];
72542
72794
  }
72543
- return DEFAULT_SECRET_SUBPATHS.map((sub) => path170.join(home, sub));
72795
+ return DEFAULT_SECRET_SUBPATHS.map((sub) => path171.join(home, sub));
72544
72796
  }
72545
72797
  function defaultSandboxProfile(cwd, tmpDir, home) {
72546
72798
  return {
@@ -72560,10 +72812,10 @@ function dedupe3(values) {
72560
72812
  init_config_dir();
72561
72813
  init_shell_config();
72562
72814
  import { existsSync as existsSync26 } from "fs";
72563
- import path171 from "path";
72815
+ import path172 from "path";
72564
72816
  var SECRET_KEY_PATTERN2 = /api[_-]?key|secret|token|password|credential/i;
72565
72817
  function sandboxConfigPath(dir) {
72566
- return path171.join(path171.dirname(shellConfigPath(dir)), "sandbox.json");
72818
+ return path172.join(path172.dirname(shellConfigPath(dir)), "sandbox.json");
72567
72819
  }
72568
72820
  function isShellDefault(v) {
72569
72821
  return v === "off" || v === "workspace" || v === "strict" || v === "1";
@@ -73682,7 +73934,7 @@ async function harnessExec(args, deps) {
73682
73934
  env: commandEnv,
73683
73935
  cwd
73684
73936
  };
73685
- const worktreeRoot = path172.parse(path172.resolve(cwd, commandPath)).root || cwd;
73937
+ const worktreeRoot = path173.parse(path173.resolve(cwd, commandPath)).root || cwd;
73686
73938
  const budget = {
73687
73939
  reservationId: idSeq(),
73688
73940
  maxRuntimeMs: maxRuntimeMs ?? EXEC_DEFAULT_RUNTIME_MS
@@ -73869,6 +74121,8 @@ function harnessWave(args, deps) {
73869
74121
  }
73870
74122
 
73871
74123
  // src/commands/shell.ts
74124
+ init_grants();
74125
+ init_providers();
73872
74126
  init_make_provider();
73873
74127
  init_orient();
73874
74128
  init_metaproject_adapter();
@@ -75544,7 +75798,7 @@ function workspaceReadTool(cwd) {
75544
75798
  // src/harness/tool/builtin/workspace-lifecycle-tool.ts
75545
75799
  import { randomUUID as randomUUID22 } from "crypto";
75546
75800
  import { writeFile as writeFile52 } from "fs/promises";
75547
- import path173 from "path";
75801
+ import path174 from "path";
75548
75802
  init_slate();
75549
75803
  init_store3();
75550
75804
  init_guard2();
@@ -75690,7 +75944,7 @@ function workspaceProposeTool(cwd, getSessionDir) {
75690
75944
  return { output: `workspace_propose: unrecognized 'kind' \u2014 expected one of: ${PROPOSAL_KINDS.join(", ")}`, isError: true };
75691
75945
  }
75692
75946
  const explicitSessionId = typeof input.sessionId === "string" && input.sessionId.length > 0 ? input.sessionId : undefined;
75693
- const sessionRef = explicitSessionId ?? path173.basename(getSessionDir() ?? "");
75947
+ const sessionRef = explicitSessionId ?? path174.basename(getSessionDir() ?? "");
75694
75948
  if (sessionRef.length === 0) {
75695
75949
  return { output: "workspace_propose: no 'sessionId' given and no active session in this run", isError: true };
75696
75950
  }
@@ -75780,7 +76034,7 @@ init_shell_config();
75780
76034
  init_command_risk();
75781
76035
  init_shell_syntax();
75782
76036
  import { existsSync as existsSync28 } from "fs";
75783
- import path174 from "path";
76037
+ import path175 from "path";
75784
76038
  import { createHash as createHash40 } from "crypto";
75785
76039
  var PREFIX_BANNED = new Set([
75786
76040
  "sh",
@@ -75985,7 +76239,7 @@ function emptyShellPermissions() {
75985
76239
  return { allow: [] };
75986
76240
  }
75987
76241
  function shellPermissionsPath(dir) {
75988
- return path174.join(path174.dirname(shellConfigPath(dir)), "permissions.json");
76242
+ return path175.join(path175.dirname(shellConfigPath(dir)), "permissions.json");
75989
76243
  }
75990
76244
  function loadShellPermissionsWithAudit(dir) {
75991
76245
  try {
@@ -76023,7 +76277,7 @@ function loadShellPermissions(dir) {
76023
76277
  function saveShellPermissions(perms, dir, options = {}) {
76024
76278
  try {
76025
76279
  const file = shellPermissionsPath(dir);
76026
- ensureKeryxConfigDir(path174.dirname(file));
76280
+ ensureKeryxConfigDir(path175.dirname(file));
76027
76281
  const cleaned = Array.from(new Set(perms.allow.map((p) => p.trim()).filter((p) => p.length > 0)));
76028
76282
  const body = {
76029
76283
  allow: options.skipValidation === true ? cleaned : cleaned.filter((p) => validateShellPattern(p).ok)
@@ -76340,12 +76594,12 @@ function remoteRequest(providerId, endpoint, query, key) {
76340
76594
  // src/lib/search-config.ts
76341
76595
  init_config_dir();
76342
76596
  import { existsSync as existsSync29 } from "fs";
76343
- import path175 from "path";
76597
+ import path176 from "path";
76344
76598
  function searchConfigPath(dir) {
76345
- return path175.join(keryxConfigDir(dir), "search-providers.json");
76599
+ return path176.join(keryxConfigDir(dir), "search-providers.json");
76346
76600
  }
76347
76601
  function searchCredentialPath(dir) {
76348
- return path175.join(keryxConfigDir(dir), "search-credentials.json");
76602
+ return path176.join(keryxConfigDir(dir), "search-credentials.json");
76349
76603
  }
76350
76604
  function readJson5(file) {
76351
76605
  try {
@@ -76474,7 +76728,7 @@ function createDefaultSearchProviderController(configDir) {
76474
76728
  import { createHash as createHash41, randomUUID as randomUUID24 } from "crypto";
76475
76729
  import { mkdtemp, rm as rm13 } from "fs/promises";
76476
76730
  import { tmpdir as tmpdir4 } from "os";
76477
- import path176 from "path";
76731
+ import path177 from "path";
76478
76732
  init_metaproject_adapter();
76479
76733
  init_ledger();
76480
76734
  init_orchestrate();
@@ -76737,8 +76991,8 @@ function createSpawnSubagentTool(deps) {
76737
76991
  let dispatchId = "";
76738
76992
  let closing = false;
76739
76993
  try {
76740
- ephemeralDir = await mkdtemp(path176.join(tmpdir4(), "keryx-subagent-slate-"));
76741
- dispatchId = path176.basename(ephemeralDir);
76994
+ ephemeralDir = await mkdtemp(path177.join(tmpdir4(), "keryx-subagent-slate-"));
76995
+ dispatchId = path177.basename(ephemeralDir);
76742
76996
  openedChildSlate = await openSlate({
76743
76997
  dir: ephemeralDir,
76744
76998
  cwd,
@@ -76981,7 +77235,7 @@ import { randomUUID as randomUUID25 } from "crypto";
76981
77235
  import { execFile as execFile4 } from "child_process";
76982
77236
  import { mkdir as mkdir61 } from "fs/promises";
76983
77237
  import { tmpdir as tmpdir6 } from "os";
76984
- import path178 from "path";
77238
+ import path179 from "path";
76985
77239
  import { promisify as promisify4 } from "util";
76986
77240
 
76987
77241
  // src/harness/child/git-worktree-port.ts
@@ -77280,7 +77534,7 @@ function canNestExternalChild(env, maxDepth) {
77280
77534
  // src/harness/external/runtime.ts
77281
77535
  import { mkdtemp as mkdtemp2, rm as rm14, writeFile as writeFile53 } from "fs/promises";
77282
77536
  import { tmpdir as tmpdir5 } from "os";
77283
- import path177 from "path";
77537
+ import path178 from "path";
77284
77538
 
77285
77539
  // src/harness/external/prompt.ts
77286
77540
  var EXTERNAL_RUNTIME_DIRECTIVE = [
@@ -78323,8 +78577,8 @@ async function prepareResultSchema() {
78323
78577
  try {
78324
78578
  const schema = await loadSchema("subagent-result");
78325
78579
  const schemaText = JSON.stringify(schema, null, 2);
78326
- schemaDir = await mkdtemp2(path177.join(tmpdir5(), "keryx-external-result-schema-"));
78327
- const schemaPath = path177.join(schemaDir, "subagent-result.schema.json");
78580
+ schemaDir = await mkdtemp2(path178.join(tmpdir5(), "keryx-external-result-schema-"));
78581
+ const schemaPath = path178.join(schemaDir, "subagent-result.schema.json");
78328
78582
  await writeFile53(schemaPath, schemaText, "utf8");
78329
78583
  return { ok: true, schema, schemaText, schemaPath, schemaDir };
78330
78584
  } catch (error) {
@@ -78581,7 +78835,7 @@ async function createRunExternal(options) {
78581
78835
  }
78582
78836
  const config = gate.config;
78583
78837
  const idSeq = options.idSeq ?? (() => randomUUID25());
78584
- const worktreesDir = options.worktreesDir ?? path178.join(tmpdir6(), "keryx-external-worktrees");
78838
+ const worktreesDir = options.worktreesDir ?? path179.join(tmpdir6(), "keryx-external-worktrees");
78585
78839
  const spawn = options.spawn ?? createBunSpawnPort();
78586
78840
  const worktree = options.worktree ?? ensuringParentDir(createGitWorktreePort({ repoRoot: options.cwd, worktreesDir }), worktreesDir);
78587
78841
  const readWorkingDiff = options.readWorkingDiff ?? (() => defaultWorkingDiff(options.cwd));
@@ -78888,7 +79142,7 @@ class LiveMarkdownBlock {
78888
79142
  // src/tui/theme.ts
78889
79143
  init_config_dir();
78890
79144
  import { existsSync as existsSync30 } from "fs";
78891
- import path179 from "path";
79145
+ import path180 from "path";
78892
79146
  var THEME_NAMES = ["groknight", "grokday", "tokyonight", "keryx"];
78893
79147
  var THEME_IDS = ["auto", ...THEME_NAMES];
78894
79148
  var DEFAULT_THEME_ID = "groknight";
@@ -79018,7 +79272,7 @@ function onThemeChange(listener) {
79018
79272
  };
79019
79273
  }
79020
79274
  function tuiConfigPath(dir) {
79021
- return path179.join(keryxConfigDir(dir), "tui.json");
79275
+ return path180.join(keryxConfigDir(dir), "tui.json");
79022
79276
  }
79023
79277
  function loadPersistedThemeId(dir) {
79024
79278
  try {
@@ -79075,7 +79329,7 @@ init_workspace_service();
79075
79329
  init_service8();
79076
79330
  init_store2();
79077
79331
  init_fs();
79078
- import path180 from "path";
79332
+ import path181 from "path";
79079
79333
  import { readFile as readFile97 } from "fs/promises";
79080
79334
  var POSITIVE_INTEGER = /^[1-9][0-9]*$/;
79081
79335
  var DEFAULT_AUTO_GOAL_ROUNDS = 8;
@@ -79151,7 +79405,7 @@ function getFlowService() {
79151
79405
  async function autoProvisionFlow(cwd, goalText) {
79152
79406
  const service = getFlowService();
79153
79407
  const result = await service.init({ cwd, title: goalText });
79154
- const acFile = path180.join(cwd, result.dir, "acceptance-criteria.md");
79408
+ const acFile = path181.join(cwd, result.dir, "acceptance-criteria.md");
79155
79409
  await writeFileAtomic(acFile, [
79156
79410
  "# Acceptance Criteria",
79157
79411
  "",
@@ -80702,7 +80956,7 @@ init_proposal_lifecycle();
80702
80956
  init_workspace_service();
80703
80957
  import { randomUUID as randomUUID27 } from "crypto";
80704
80958
  import { readdir as readdir34, stat as stat18 } from "fs/promises";
80705
- import path181 from "path";
80959
+ import path182 from "path";
80706
80960
 
80707
80961
  // src/sac/lifecycle-flag.ts
80708
80962
  init_store();
@@ -80861,7 +81115,7 @@ async function readExternalUnboundCandidates(cwd) {
80861
81115
  }
80862
81116
  return groups;
80863
81117
  }, []));
80864
- const metaPath = path181.join(extDir, `${id}.json`);
81118
+ const metaPath = path182.join(extDir, `${id}.json`);
80865
81119
  if (await pathExists(unboundDismissedReceiptPath(metaPath)))
80866
81120
  continue;
80867
81121
  candidates.push({
@@ -80875,7 +81129,7 @@ async function readExternalUnboundCandidates(cwd) {
80875
81129
  return candidates;
80876
81130
  }
80877
81131
  async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
80878
- const evidenceDir = path181.join(externalSlatesDir(cwd), externalSessionId);
81132
+ const evidenceDir = path182.join(externalSlatesDir(cwd), externalSessionId);
80879
81133
  let entries;
80880
81134
  try {
80881
81135
  entries = (await readdir34(evidenceDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
@@ -80884,7 +81138,7 @@ async function readNewestUnboundCandidateForExternal(cwd, externalSessionId) {
80884
81138
  }
80885
81139
  entries.sort();
80886
81140
  for (let i = entries.length - 1;i >= 0; i--) {
80887
- const evidencePath = path181.join(evidenceDir, entries[i]);
81141
+ const evidencePath = path182.join(evidenceDir, entries[i]);
80888
81142
  if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
80889
81143
  continue;
80890
81144
  const result = readConfigFile(evidencePath);
@@ -80927,7 +81181,7 @@ var SESSION_ATTRIBUTION_SLACK_MS = 5 * 60000;
80927
81181
  var MTIME_CLOCK_SKEW_TOLERANCE_MS = 5000;
80928
81182
  async function collectReceiptTargets(cwd, owner) {
80929
81183
  const targets = new Set;
80930
- const workspacesDir = path181.join(cwd, ".metaproject", "workspaces");
81184
+ const workspacesDir = path182.join(cwd, ".metaproject", "workspaces");
80931
81185
  let workspaceIds;
80932
81186
  try {
80933
81187
  workspaceIds = (await readdir34(workspacesDir, { withFileTypes: true })).filter((entry) => entry.isDirectory()).map((entry) => entry.name);
@@ -80935,7 +81189,7 @@ async function collectReceiptTargets(cwd, owner) {
80935
81189
  return targets;
80936
81190
  }
80937
81191
  for (const workspaceId of workspaceIds) {
80938
- const receiptsDir = path181.join(workspacesDir, workspaceId, `${owner}-write-receipts`);
81192
+ const receiptsDir = path182.join(workspacesDir, workspaceId, `${owner}-write-receipts`);
80939
81193
  let files;
80940
81194
  try {
80941
81195
  files = (await readdir34(receiptsDir)).filter((name) => name.endsWith(".json"));
@@ -80943,7 +81197,7 @@ async function collectReceiptTargets(cwd, owner) {
80943
81197
  continue;
80944
81198
  }
80945
81199
  for (const file of files) {
80946
- const result = readConfigFile(path181.join(receiptsDir, file));
81200
+ const result = readConfigFile(path182.join(receiptsDir, file));
80947
81201
  if (!result.ok)
80948
81202
  continue;
80949
81203
  try {
@@ -80986,7 +81240,7 @@ async function findSkillFiles(dir) {
80986
81240
  }
80987
81241
  const found = [];
80988
81242
  for (const entry of entries) {
80989
- const full = path181.join(dir, entry.name);
81243
+ const full = path182.join(dir, entry.name);
80990
81244
  if (entry.isDirectory()) {
80991
81245
  found.push(...await findSkillFiles(full));
80992
81246
  } else if (entry.isFile() && entry.name === "SKILL.md") {
@@ -81045,10 +81299,10 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
81045
81299
  changedAt: attribution.changedAt
81046
81300
  });
81047
81301
  }
81048
- const sacSkillsDir = path181.join(cwd, ".metaproject", "project-skills", "sac");
81302
+ const sacSkillsDir = path182.join(cwd, ".metaproject", "project-skills", "sac");
81049
81303
  const skillFiles = await findSkillFiles(sacSkillsDir);
81050
81304
  for (const absolutePath of skillFiles) {
81051
- const targetRef = path181.relative(path181.join(cwd, ".metaproject"), absolutePath).split(path181.sep).join("/");
81305
+ const targetRef = path182.relative(path182.join(cwd, ".metaproject"), absolutePath).split(path182.sep).join("/");
81052
81306
  if (skillReceipts.has(targetRef))
81053
81307
  continue;
81054
81308
  const attribution = await attributeToSession(absolutePath, scoped);
@@ -81066,12 +81320,12 @@ async function detectUnreviewedSacPathChanges(cwd, sessions) {
81066
81320
  return items;
81067
81321
  }
81068
81322
  async function isSlateEngaged(dir) {
81069
- if (await pathExists(path181.join(dir, "slate.json")))
81323
+ if (await pathExists(path182.join(dir, "slate.json")))
81070
81324
  return true;
81071
- if (await pathExists(path181.join(dir, "terminal-state.json")))
81325
+ if (await pathExists(path182.join(dir, "terminal-state.json")))
81072
81326
  return true;
81073
81327
  try {
81074
- const entries = await readdir34(path181.join(dir, "slate-archive"));
81328
+ const entries = await readdir34(path182.join(dir, "slate-archive"));
81075
81329
  return entries.length > 0;
81076
81330
  } catch {
81077
81331
  return false;
@@ -81085,7 +81339,7 @@ async function safeReadSlate(dir) {
81085
81339
  }
81086
81340
  }
81087
81341
  async function readTerminalState(dir) {
81088
- const result = readConfigFile(path181.join(dir, "terminal-state.json"));
81342
+ const result = readConfigFile(path182.join(dir, "terminal-state.json"));
81089
81343
  if (!result.ok) {
81090
81344
  return;
81091
81345
  }
@@ -81109,7 +81363,7 @@ async function resolveUnboundCandidateTarget(cwd, target) {
81109
81363
  if (session === undefined) {
81110
81364
  throw new Error(`No session or evidence path matches "${target}"`);
81111
81365
  }
81112
- const archiveDir = path181.join(sessionDir(session.projectPath, session.id), "slate-archive");
81366
+ const archiveDir = path182.join(sessionDir(session.projectPath, session.id), "slate-archive");
81113
81367
  let entries;
81114
81368
  try {
81115
81369
  entries = (await readdir34(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
@@ -81119,7 +81373,7 @@ async function resolveUnboundCandidateTarget(cwd, target) {
81119
81373
  if (entries.length === 0)
81120
81374
  throw new Error(`No unbound-candidate artifacts for session ${session.id}`);
81121
81375
  entries.sort();
81122
- return path181.join(archiveDir, entries[entries.length - 1]);
81376
+ return path182.join(archiveDir, entries[entries.length - 1]);
81123
81377
  }
81124
81378
  async function dismissUnboundByTarget(cwd, target, reason) {
81125
81379
  const evidencePath = await resolveUnboundCandidateTarget(cwd, target);
@@ -81145,7 +81399,7 @@ async function dismissUnboundCandidate(candidatePath, reason) {
81145
81399
  return { removed: candidatePath, receipt: receiptPath };
81146
81400
  }
81147
81401
  async function readNewestUnboundCandidate(dir) {
81148
- const archiveDir = path181.join(dir, "slate-archive");
81402
+ const archiveDir = path182.join(dir, "slate-archive");
81149
81403
  let entries;
81150
81404
  try {
81151
81405
  entries = (await readdir34(archiveDir)).filter((name) => name.endsWith("-unbound-candidate.json"));
@@ -81154,7 +81408,7 @@ async function readNewestUnboundCandidate(dir) {
81154
81408
  }
81155
81409
  entries.sort();
81156
81410
  for (let i = entries.length - 1;i >= 0; i--) {
81157
- const evidencePath = path181.join(archiveDir, entries[i]);
81411
+ const evidencePath = path182.join(archiveDir, entries[i]);
81158
81412
  if (await pathExists(unboundDismissedReceiptPath(evidencePath)))
81159
81413
  continue;
81160
81414
  const result = readConfigFile(evidencePath);
@@ -81184,7 +81438,7 @@ function isFailureOutcome(g) {
81184
81438
  return g.outcome === "error" || g.outcome === "no_credential" || g.outcome === "conflict";
81185
81439
  }
81186
81440
  async function readNewestWrapUpOutcome(dir) {
81187
- const archiveDir = path181.join(dir, "slate-archive");
81441
+ const archiveDir = path182.join(dir, "slate-archive");
81188
81442
  let entries;
81189
81443
  try {
81190
81444
  entries = (await readdir34(archiveDir)).filter((name) => name.endsWith("-wrap-up-outcome.json"));
@@ -81193,7 +81447,7 @@ async function readNewestWrapUpOutcome(dir) {
81193
81447
  }
81194
81448
  entries.sort();
81195
81449
  for (let i = entries.length - 1;i >= 0; i--) {
81196
- const evidencePath = path181.join(archiveDir, entries[i]);
81450
+ const evidencePath = path182.join(archiveDir, entries[i]);
81197
81451
  const result = readConfigFile(evidencePath);
81198
81452
  if (!result.ok) {
81199
81453
  continue;
@@ -82407,10 +82661,11 @@ var MCP_INSPECTOR_FOOTER = [
82407
82661
  { key: "\u2190/\u2192", label: "tabs" },
82408
82662
  { key: "esc", label: "close" }
82409
82663
  ];
82410
- var MCP_TOOLS_COMMAND = "/mcp";
82664
+ var MCP_TOOLS_COMMAND = "/integrations";
82665
+ var MCP_TOOLS_COMMAND_RETIRED = "/mcp";
82411
82666
  function isMcpToolsCommand(line) {
82412
82667
  const token = line.trim().split(/\s+/)[0] ?? "";
82413
- return token === MCP_TOOLS_COMMAND;
82668
+ return token === MCP_TOOLS_COMMAND || token === MCP_TOOLS_COMMAND_RETIRED;
82414
82669
  }
82415
82670
  var TOOLS_TAB_HEADER = "Built into keryx \u2014 not from an external MCP server (keryx doesn't consume MCP servers as a client yet).";
82416
82671
  var MCP_TAB_HEADER_1 = "Connects/disconnects ONLY keryx's own MCP server, one editor config at a time.";
@@ -83453,9 +83708,14 @@ var AGENT_SLASH_COMMANDS = [
83453
83708
  description: "Show project-wide items needing review (proposals, blocked sessions)",
83454
83709
  modes: AGENT_ONLY
83455
83710
  },
83711
+ {
83712
+ name: "/integrations",
83713
+ description: "Wire this project into an editor over MCP (keryx integrate)",
83714
+ modes: AGENT_ONLY
83715
+ },
83456
83716
  {
83457
83717
  name: "/mcp",
83458
- description: "Show available tools and MCP client connect status",
83718
+ description: "Deprecated alias of /integrations",
83459
83719
  modes: AGENT_ONLY
83460
83720
  },
83461
83721
  {
@@ -84649,10 +84909,10 @@ function describeElicitationPrompt(tool, inputJson) {
84649
84909
  // src/lib/permission-mode-config.ts
84650
84910
  init_permission_mode();
84651
84911
  init_config_dir();
84652
- import path182 from "path";
84912
+ import path183 from "path";
84653
84913
  var EMPTY3 = { schemaVersion: 1, projects: {} };
84654
84914
  function permissionModeConfigPath(dir) {
84655
- return path182.join(keryxConfigDir(dir), "permission-mode.json");
84915
+ return path183.join(keryxConfigDir(dir), "permission-mode.json");
84656
84916
  }
84657
84917
  function withRegistryLock2(dir, fn) {
84658
84918
  return withFileLock(`${permissionModeConfigPath(dir)}.lock`, fn, {
@@ -84820,6 +85080,20 @@ async function runAfterForegroundSettlement(owner, action) {
84820
85080
  action();
84821
85081
  }
84822
85082
  }
85083
+ async function forceForegroundQueueItem(owner, handoff, item, deps) {
85084
+ const ownsSettlement = handoff.enqueue(item);
85085
+ owner.cancel(deps.cancelReason ?? "queue item forced");
85086
+ deps.announce?.();
85087
+ if (!ownsSettlement) {
85088
+ return;
85089
+ }
85090
+ await runAfterForegroundSettlement(owner, () => {
85091
+ const next = handoff.takeAfterSettlement();
85092
+ if (next !== undefined) {
85093
+ deps.run(next);
85094
+ }
85095
+ });
85096
+ }
84823
85097
  function createForegroundAgentIoFacade(owner, token, io) {
84824
85098
  const accepts = () => owner.accepts(token);
84825
85099
  return {
@@ -85156,7 +85430,7 @@ function showComposerChoice(otui, r, dock, request) {
85156
85430
  // src/lib/version-check.ts
85157
85431
  init_config_dir();
85158
85432
  init_fs();
85159
- import path183 from "path";
85433
+ import path184 from "path";
85160
85434
  var REGISTRY_URL = "https://registry.npmjs.org/@mrciphersmith%2Fkeryx/latest";
85161
85435
  var FIXED_INSTALL_COMMAND = "npm install -g @mrciphersmith/keryx@latest";
85162
85436
  var RESPONSE_BODY_LIMIT_BYTES = 64 * 1024;
@@ -85358,7 +85632,7 @@ async function checkVersion(options) {
85358
85632
  const now = options.now ?? Date.now;
85359
85633
  const timestamp = now();
85360
85634
  const configDir = ensureKeryxConfigDir(options.cacheDir);
85361
- const cacheFile = path183.join(configDir, "version-check.json");
85635
+ const cacheFile = path184.join(configDir, "version-check.json");
85362
85636
  const cache = parseCache(cacheFile);
85363
85637
  if (cache?.latestVersion !== undefined && cache.successAt !== undefined && timestamp - cache.successAt >= 0 && timestamp - cache.successAt < SUCCESS_CACHE_TTL_MS) {
85364
85638
  return resultFor(options.currentVersion, current, cache.latestVersion, "cache");
@@ -90686,19 +90960,11 @@ Staying in the current session.
90686
90960
  return;
90687
90961
  mainQueue = removeMainQueueItem(mainQueue, index);
90688
90962
  paintMainQueue();
90689
- (async () => {
90690
- const waitsForSettlement = forceHandoff.enqueue(item);
90691
- foregroundOperation.cancel("queue item forced");
90692
- io.onSystem?.(`\u25C7 main turn interrupted \u2014 q${index + 1} will run next.
90693
- `);
90694
- if (!waitsForSettlement)
90695
- return;
90696
- await runAfterForegroundSettlement(foregroundOperation, () => {
90697
- const next = forceHandoff.takeAfterSettlement();
90698
- if (next !== undefined)
90699
- runLine(next.question);
90700
- });
90701
- })();
90963
+ forceForegroundQueueItem(foregroundOperation, forceHandoff, item, {
90964
+ announce: () => io.onSystem?.(`\u25C7 main turn interrupted \u2014 q${index + 1} will run next.
90965
+ `),
90966
+ run: (next) => runLine(next.question)
90967
+ });
90702
90968
  };
90703
90969
  const handleQueueNavKey = (key) => {
90704
90970
  if (!queueNavActive) {
@@ -92329,10 +92595,24 @@ function realMakeProvider(write) {
92329
92595
  }
92330
92596
  return makeProvider(name, model, {
92331
92597
  fetch: globalThis.fetch,
92332
- ...baseUrl !== undefined ? { baseUrl } : {}
92598
+ ...baseUrl !== undefined ? { baseUrl } : {},
92599
+ ...oauthCredentialsFor(name)
92333
92600
  });
92334
92601
  };
92335
92602
  }
92603
+ function oauthCredentialsFor(name) {
92604
+ const definition = providerByName(name);
92605
+ const envKey = definition?.envKey;
92606
+ if (envKey === undefined)
92607
+ return {};
92608
+ const fromEnv = process.env[envKey];
92609
+ if (fromEnv !== undefined && fromEnv.length > 0)
92610
+ return {};
92611
+ const grant = loadOAuthGrant(name);
92612
+ if (grant === undefined || grant.access.length === 0)
92613
+ return {};
92614
+ return { credentials: { ...process.env, [envKey]: grant.access } };
92615
+ }
92336
92616
  function realSelectProviderModel(baseUrl) {
92337
92617
  return async (io, opts) => {
92338
92618
  const detected = await detectProviders({
@@ -93738,7 +94018,7 @@ Shell:
93738
94018
  init_fs();
93739
94019
  import { readFile as readFile98 } from "fs/promises";
93740
94020
  import { stdin } from "process";
93741
- import path184 from "path";
94021
+ import path185 from "path";
93742
94022
  var MODULES = [
93743
94023
  { name: "gdgraph", flag: "--no-gdgraph", desc: "code graph, symbols, affected context", defaultEnabled: true },
93744
94024
  { name: "gdctx", flag: "--no-gdctx", desc: "token-aware command/read output", defaultEnabled: true },
@@ -93784,8 +94064,8 @@ async function modulesCommand(args = []) {
93784
94064
  return;
93785
94065
  }
93786
94066
  const wantsJson = args.includes("--json") && (sub === undefined || sub === "status" || sub === "list" || sub === "--json");
93787
- const metaprojectRoot = path184.join(process.cwd(), ".metaproject");
93788
- const manifestPath = path184.join(metaprojectRoot, "metaproject.json");
94067
+ const metaprojectRoot = path185.join(process.cwd(), ".metaproject");
94068
+ const manifestPath = path185.join(metaprojectRoot, "metaproject.json");
93789
94069
  if (!await pathExists(manifestPath)) {
93790
94070
  if (wantsJson) {
93791
94071
  console.log(JSON.stringify({ schemaVersion: 1, error: "not-initialized", modules: [] }, null, 2));
@@ -93916,9 +94196,9 @@ import {
93916
94196
  unlinkSync as unlinkSync3,
93917
94197
  writeFileSync as writeFileSync8
93918
94198
  } from "fs";
93919
- import path185 from "path";
94199
+ import path186 from "path";
93920
94200
  function serveCredentialPath(dir) {
93921
- return path185.join(keryxConfigDir(dir), "serve-credentials.json");
94201
+ return path186.join(keryxConfigDir(dir), "serve-credentials.json");
93922
94202
  }
93923
94203
  function constantTimeEqual(a, b) {
93924
94204
  const width = Math.max(a.length, b.length);
@@ -94152,22 +94432,22 @@ class AuthFailureThrottle {
94152
94432
  init_config_dir();
94153
94433
  import { createHash as createHash43 } from "crypto";
94154
94434
  import { existsSync as existsSync32, readdirSync as readdirSync3, rmSync as rmSync2 } from "fs";
94155
- import path186 from "path";
94435
+ import path187 from "path";
94156
94436
  var MAX_TURN_EVENTS = 1e4;
94157
94437
  function turnsRoot(dir) {
94158
- return path186.join(keryxConfigDir(dir), "turns");
94438
+ return path187.join(keryxConfigDir(dir), "turns");
94159
94439
  }
94160
94440
  function turnDir(turnId, dir) {
94161
- return path186.join(turnsRoot(dir), turnId);
94441
+ return path187.join(turnsRoot(dir), turnId);
94162
94442
  }
94163
94443
  function keyPath(project, idempotencyKey, dir) {
94164
94444
  const projectBytes = Buffer.byteLength(project, "utf8");
94165
94445
  const digest = createHash43("sha256").update(`${projectBytes}:${project}\x00${idempotencyKey}`, "utf8").digest("hex");
94166
- return path186.join(turnsRoot(dir), "keys", `${digest}.json`);
94446
+ return path187.join(turnsRoot(dir), "keys", `${digest}.json`);
94167
94447
  }
94168
94448
  function legacyKeyPath(idempotencyKey, dir) {
94169
94449
  const digest = createHash43("sha256").update(idempotencyKey, "utf8").digest("hex");
94170
- return path186.join(turnsRoot(dir), "keys", `${digest}.json`);
94450
+ return path187.join(turnsRoot(dir), "keys", `${digest}.json`);
94171
94451
  }
94172
94452
  function adoptLegacyClaim(project, idempotencyKey, dir) {
94173
94453
  const legacy = legacyKeyPath(idempotencyKey, dir);
@@ -94231,7 +94511,7 @@ function ensureTurnDir(turnId, dir) {
94231
94511
  }
94232
94512
  function createTurnRecord(record, dir) {
94233
94513
  ensureTurnDir(record.turnId, dir);
94234
- writeOwnerOnlyFile(path186.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
94514
+ writeOwnerOnlyFile(path187.join(turnDir(record.turnId, dir), "turn.json"), `${JSON.stringify(record, null, 2)}
94235
94515
  `);
94236
94516
  }
94237
94517
  function appendTurnEvent(event, dir, opts) {
@@ -94240,12 +94520,12 @@ function appendTurnEvent(event, dir, opts) {
94240
94520
  }
94241
94521
  const line = JSON.stringify(event);
94242
94522
  try {
94243
- appendOwnerOnlyLine(path186.join(turnDir(event.turnId, dir), "events.jsonl"), line);
94523
+ appendOwnerOnlyLine(path187.join(turnDir(event.turnId, dir), "events.jsonl"), line);
94244
94524
  } catch (error) {
94245
94525
  if (error?.code !== "ENOENT") {
94246
94526
  throw error;
94247
94527
  }
94248
- appendOwnerOnlyLine(path186.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
94528
+ appendOwnerOnlyLine(path187.join(ensureTurnDir(event.turnId, dir), "events.jsonl"), line);
94249
94529
  }
94250
94530
  return true;
94251
94531
  }
@@ -94253,7 +94533,7 @@ function readTurnEvents(turnId, after = -1, dir) {
94253
94533
  if (!isTurnId(turnId)) {
94254
94534
  return { ok: false, reason: "not-a-turn-id" };
94255
94535
  }
94256
- const read = readTurnFile(path186.join(turnDir(turnId, dir), "events.jsonl"));
94536
+ const read = readTurnFile(path187.join(turnDir(turnId, dir), "events.jsonl"));
94257
94537
  if (!read.ok) {
94258
94538
  if (isDefiniteAbsence2(read.reason)) {
94259
94539
  return { ok: true, value: [] };
@@ -94281,7 +94561,7 @@ function readTurnRecord(turnId, dir) {
94281
94561
  if (!isTurnId(turnId)) {
94282
94562
  return { ok: false, reason: "not-a-turn-id" };
94283
94563
  }
94284
- const read = readTurnFile(path186.join(turnDir(turnId, dir), "turn.json"));
94564
+ const read = readTurnFile(path187.join(turnDir(turnId, dir), "turn.json"));
94285
94565
  if (!read.ok) {
94286
94566
  return { ok: false, reason: read.reason };
94287
94567
  }
@@ -94300,7 +94580,7 @@ function finishTurn(turnId, result, dir) {
94300
94580
  if (!record.ok) {
94301
94581
  return false;
94302
94582
  }
94303
- writeOwnerOnlyFile(path186.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
94583
+ writeOwnerOnlyFile(path187.join(turnDir(turnId, dir), "turn.json"), `${JSON.stringify({ ...record.value, result }, null, 2)}
94304
94584
  `);
94305
94585
  return true;
94306
94586
  }
@@ -94346,7 +94626,7 @@ function releaseIdempotencyKey(project, idempotencyKey, turnId, dir) {
94346
94626
 
94347
94627
  // src/lib/serve-turn.ts
94348
94628
  import { randomUUID as randomUUID31 } from "crypto";
94349
- import path187 from "path";
94629
+ import path188 from "path";
94350
94630
  init_service();
94351
94631
  var REMOTE_ORIGIN = "remote:http";
94352
94632
  var MAX_PROMPT_CHARS = 32000;
@@ -94415,9 +94695,9 @@ function isUuid(value) {
94415
94695
  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);
94416
94696
  }
94417
94697
  function resolveProject(declared, dir) {
94418
- const wanted = path187.resolve(declared);
94698
+ const wanted = path188.resolve(declared);
94419
94699
  for (const entry of listProjects(dir, () => {})) {
94420
- if (path187.resolve(entry.path) === wanted) {
94700
+ if (path188.resolve(entry.path) === wanted) {
94421
94701
  return { ok: true, project: entry.path };
94422
94702
  }
94423
94703
  }
@@ -95453,7 +95733,7 @@ function printHelp19() {
95453
95733
  import { spawn as spawn6 } from "child_process";
95454
95734
  import { chmod as chmod4, mkdir as mkdir62, readFile as readFile99, readdir as readdir35, writeFile as writeFile54 } from "fs/promises";
95455
95735
  import { access as access4, constants as constants2, existsSync as existsSync33 } from "fs";
95456
- import path188 from "path";
95736
+ import path189 from "path";
95457
95737
  import { fileURLToPath as fileURLToPath7 } from "url";
95458
95738
  init_config();
95459
95739
  init_config2();
@@ -95470,8 +95750,8 @@ async function updateCommand(args = []) {
95470
95750
  return;
95471
95751
  }
95472
95752
  const projectRoot = process.cwd();
95473
- const metaprojectRoot = path188.join(projectRoot, ".metaproject");
95474
- banner("keryx update", `Refreshing the .metaproject workspace in ${path188.basename(projectRoot)}/`);
95753
+ const metaprojectRoot = path189.join(projectRoot, ".metaproject");
95754
+ banner("keryx update", `Refreshing the .metaproject workspace in ${path189.basename(projectRoot)}/`);
95475
95755
  if (!await pathExists(metaprojectRoot)) {
95476
95756
  console.log(` ${style.red(symbols.cross)} Metaproject is not initialized.`);
95477
95757
  console.log(` ${style.cyan(symbols.arrow)} Run ${style.cyan("keryx init")} first.`);
@@ -95532,7 +95812,7 @@ async function updateCommand(args = []) {
95532
95812
  nextSteps(steps);
95533
95813
  }
95534
95814
  async function previewServiceFiles(projectRoot, options) {
95535
- const metaprojectRoot = path188.join(projectRoot, ".metaproject");
95815
+ const metaprojectRoot = path189.join(projectRoot, ".metaproject");
95536
95816
  const manifestState = await readManifest5(metaprojectRoot);
95537
95817
  const manifest = manifestState.manifest;
95538
95818
  const enableTasks = moduleEnabled2(manifest, "tasks") || !options.noTasks;
@@ -95561,12 +95841,12 @@ async function previewServiceFiles(projectRoot, options) {
95561
95841
  });
95562
95842
  }
95563
95843
  async function refreshServiceFiles(projectRoot, options) {
95564
- const metaprojectRoot = path188.join(projectRoot, ".metaproject");
95844
+ const metaprojectRoot = path189.join(projectRoot, ".metaproject");
95565
95845
  const manifestState = await readManifest5(metaprojectRoot);
95566
95846
  const manifest = manifestState.manifest;
95567
95847
  const recoveredManifest = !manifestState.exists || !manifestState.valid;
95568
95848
  if (manifestState.migrated) {
95569
- await writeFile54(path188.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
95849
+ await writeFile54(path189.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
95570
95850
  `, "utf8");
95571
95851
  }
95572
95852
  const enableGdgraph = moduleEnabled2(manifest, "gdgraph");
@@ -95603,9 +95883,9 @@ async function refreshServiceFiles(projectRoot, options) {
95603
95883
  enableSecurity,
95604
95884
  enableSac
95605
95885
  });
95606
- await writeTextIfChanged4(path188.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
95607
- await writeTextIfChanged4(path188.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
95608
- await writeTextIfChanged4(path188.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
95886
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "core", "README.md"), renderMetaprojectCoreReadme());
95887
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "hooks", "README.md"), renderHooksReadme());
95888
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "skills", "project-rules", "README.md"), renderProjectRulesSkillReadme({ sources: ruleSources }));
95609
95889
  await writeRoutingEntrypointPair(metaprojectRoot, {
95610
95890
  enableGdgraph,
95611
95891
  enableGdctx,
@@ -95628,7 +95908,7 @@ async function refreshServiceFiles(projectRoot, options) {
95628
95908
  }
95629
95909
  }
95630
95910
  });
95631
- await writeTextIfChanged4(path188.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
95911
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "keryx-dashboard.html"), renderMetaprojectDashboardHtml({
95632
95912
  enableGdgraph,
95633
95913
  enableGdctx,
95634
95914
  enableGdwiki,
@@ -95640,7 +95920,7 @@ async function refreshServiceFiles(projectRoot, options) {
95640
95920
  enableSecurity,
95641
95921
  data: dashboardData
95642
95922
  }));
95643
- await writeTextIfMissing4(path188.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
95923
+ await writeTextIfMissing4(path189.join(metaprojectRoot, "README.md"), renderMetaprojectReadme({
95644
95924
  enableGdgraph,
95645
95925
  enableGdctx,
95646
95926
  enableGdwiki,
@@ -95653,31 +95933,31 @@ async function refreshServiceFiles(projectRoot, options) {
95653
95933
  }));
95654
95934
  if (enableGdgraph) {
95655
95935
  await installGdgraphCoreScripts2(metaprojectRoot);
95656
- await writeTextIfChanged4(path188.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
95657
- await writeTextIfChanged4(path188.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
95658
- await writeTextIfChanged4(path188.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
95936
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "modules", "gdgraph.md"), renderGdgraphManifest());
95937
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "core", "gdgraph", "README.md"), renderGdgraphCoreReadme());
95938
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "skills", "gdgraph", "SKILL.md"), renderGdgraphSkillReadme());
95659
95939
  await seedAssetsLock(metaprojectRoot);
95660
95940
  if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
95661
95941
  await installManagedHook2(projectRoot, "post-commit", "gdgraph-post-commit", renderGdgraphPostCommitHook());
95662
95942
  }
95663
95943
  }
95664
95944
  if (enableGdctx) {
95665
- await writeTextIfMissing4(path188.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
95666
- await writeTextIfChanged4(path188.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
95667
- await writeTextIfChanged4(path188.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
95668
- await writeTextIfChanged4(path188.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
95945
+ await writeTextIfMissing4(path189.join(metaprojectRoot, "gdctx.config.json"), renderGdctxConfig());
95946
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "modules", "gdctx.md"), renderGdctxManifest());
95947
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "core", "gdctx", "README.md"), renderGdctxCoreReadme());
95948
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "skills", "gdctx", "SKILL.md"), renderGdctxSkillReadme());
95669
95949
  }
95670
95950
  if (enableGdwiki) {
95671
- await writeTextIfMissing4(path188.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
95672
- await writeTextIfChanged4(path188.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
95673
- await writeTextIfChanged4(path188.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
95951
+ await writeTextIfMissing4(path189.join(metaprojectRoot, "wiki", "templates", "page.md"), renderWikiPageTemplate());
95952
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "modules", "gdwiki.md"), renderGdwikiManifest());
95953
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "skills", "gdwiki", "SKILL.md"), renderGdwikiSkillReadme());
95674
95954
  if (manifest.modules?.gdgraph?.hooks?.gitPostCommit) {
95675
95955
  await installManagedHook2(projectRoot, "post-commit", "gdwiki-post-commit", renderGdwikiPostCommitHook());
95676
95956
  }
95677
95957
  }
95678
95958
  if (enableSac) {
95679
- await writeTextIfMissing4(path188.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
95680
- await writeTextIfChanged4(path188.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
95959
+ await writeTextIfMissing4(path189.join(metaprojectRoot, "modules", "sac.md"), renderSacManifest());
95960
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "skills", "sac", "SKILL.md"), renderSacSkillReadme());
95681
95961
  }
95682
95962
  if (enableGdskills) {
95683
95963
  await installGdskills(metaprojectRoot, gdskillsProfile, { createDataDirs: false });
@@ -95686,25 +95966,25 @@ async function refreshServiceFiles(projectRoot, options) {
95686
95966
  }
95687
95967
  }
95688
95968
  if (enableHealth) {
95689
- await writeTextIfMissing4(path188.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
95690
- await writeTextIfChanged4(path188.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
95691
- await writeTextIfChanged4(path188.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
95692
- await writeTextIfChanged4(path188.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
95969
+ await writeTextIfMissing4(path189.join(metaprojectRoot, "health.config.json"), renderHealthConfig());
95970
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "modules", "health.md"), renderHealthManifest());
95971
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "core", "health", "README.md"), renderHealthCoreReadme());
95972
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "skills", "health", "SKILL.md"), renderHealthSkillReadme());
95693
95973
  if (manifest.modules?.health?.hooks?.gitPostCommit) {
95694
95974
  await installManagedHook2(projectRoot, "post-commit", "health-post-commit", renderHealthPostCommitHook());
95695
95975
  }
95696
95976
  }
95697
95977
  if (enableTesting) {
95698
- await writeTextIfMissing4(path188.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
95978
+ await writeTextIfMissing4(path189.join(metaprojectRoot, "testing.config.json"), renderTestingConfig({
95699
95979
  postCommitRefresh: Boolean(manifest.modules?.testing?.hooks?.gitPostCommit),
95700
95980
  prePushGate: Boolean(manifest.modules?.testing?.hooks?.prePush)
95701
95981
  }));
95702
- await writeTextIfChanged4(path188.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
95703
- await writeTextIfChanged4(path188.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
95704
- await writeTextIfChanged4(path188.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
95982
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "modules", "testing.md"), renderTestingManifest());
95983
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "core", "testing", "README.md"), renderTestingCoreReadme());
95984
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "skills", "testing", "SKILL.md"), renderTestingSkillReadme());
95705
95985
  if (enableGdwiki) {
95706
- await writeTextIfMissing4(path188.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
95707
- await writeTextIfMissing4(path188.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
95986
+ await writeTextIfMissing4(path189.join(metaprojectRoot, "wiki", "testing", "README.md"), renderTestingWikiReadme());
95987
+ await writeTextIfMissing4(path189.join(metaprojectRoot, "wiki", "testing", "conventions.md"), renderTestingWikiConventions());
95708
95988
  }
95709
95989
  if (manifest.modules?.testing?.hooks?.gitPostCommit) {
95710
95990
  await installManagedHook2(projectRoot, "post-commit", "testing-post-commit", renderTestingPostCommitHook());
@@ -95717,24 +95997,24 @@ async function refreshServiceFiles(projectRoot, options) {
95717
95997
  await installManagedHook2(projectRoot, "post-commit", "metaproject-dashboard-post-commit", renderMetaprojectDashboardPostCommitHook());
95718
95998
  }
95719
95999
  if (enableMemory) {
95720
- await writeTextIfMissing4(path188.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
95721
- await writeTextIfMissing4(path188.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
95722
- await writeTextIfChanged4(path188.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
95723
- await writeTextIfChanged4(path188.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
95724
- await writeTextIfChanged4(path188.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
96000
+ await writeTextIfMissing4(path189.join(metaprojectRoot, "memory.config.json"), renderMemoryConfig());
96001
+ await writeTextIfMissing4(path189.join(metaprojectRoot, "memory", "templates", "entry.md"), renderMemoryEntryTemplate());
96002
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "modules", "memory.md"), renderMemoryManifest());
96003
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "core", "memory", "README.md"), renderMemoryCoreReadme());
96004
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "skills", "memory", "SKILL.md"), renderMemorySkillReadme());
95725
96005
  }
95726
96006
  if (enableTasks) {
95727
- await writeTextIfChanged4(path188.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
95728
- await writeTextIfChanged4(path188.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
95729
- await writeTextIfChanged4(path188.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
95730
- await writeTextIfChanged4(path188.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
95731
- await writeTextIfChanged4(path188.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
95732
- await writeTextIfChanged4(path188.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
96007
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "flows", "README.md"), renderFlowsReadme());
96008
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "modules", "tasks.md"), renderTasksManifest());
96009
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "skills", "flow", "SKILL.md"), renderFlowSkillRouter());
96010
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "skills", "flow", "init.md"), renderFlowInitSkill());
96011
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "skills", "flow", "manage.md"), renderFlowManageSkill());
96012
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "skills", "flow", "complete.md"), renderFlowCompleteSkill());
95733
96013
  }
95734
96014
  if (enableSecurity) {
95735
- await writeTextIfMissing4(path188.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
95736
- await writeTextIfChanged4(path188.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
95737
- await writeTextIfChanged4(path188.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
96015
+ await writeTextIfMissing4(path189.join(metaprojectRoot, "security.config.json"), renderSecurityConfig());
96016
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "modules", "security.md"), renderSecurityManifest());
96017
+ await writeTextIfChanged4(path189.join(metaprojectRoot, "core", "security", "README.md"), renderSecurityCoreReadme());
95738
96018
  if (manifest.modules?.security?.hooks?.prePush) {
95739
96019
  await installManagedHook2(projectRoot, "pre-push", "security-pre-push", renderSecurityPrePushHook());
95740
96020
  }
@@ -95785,13 +96065,13 @@ async function refreshServiceFiles(projectRoot, options) {
95785
96065
  };
95786
96066
  }
95787
96067
  async function buildDashboard(projectRoot = process.cwd()) {
95788
- const metaprojectRoot = path188.join(projectRoot, ".metaproject");
96068
+ const metaprojectRoot = path189.join(projectRoot, ".metaproject");
95789
96069
  if (!await pathExists(metaprojectRoot)) {
95790
96070
  throw new Error("Metaproject is not initialized. Run: keryx init");
95791
96071
  }
95792
96072
  const manifest = (await readManifest5(metaprojectRoot)).manifest;
95793
96073
  const data = await collectDashboardData(metaprojectRoot);
95794
- const dashboardPath = path188.join(metaprojectRoot, "keryx-dashboard.html");
96074
+ const dashboardPath = path189.join(metaprojectRoot, "keryx-dashboard.html");
95795
96075
  await writeTextIfChanged4(dashboardPath, renderMetaprojectDashboardHtml({
95796
96076
  enableGdgraph: moduleEnabled2(manifest, "gdgraph"),
95797
96077
  enableGdctx: moduleEnabled2(manifest, "gdctx"),
@@ -95811,7 +96091,7 @@ async function shouldInstallDashboardPostCommitHook(projectRoot, manifest) {
95811
96091
  if (Object.values(modules).some((module) => Boolean(module.hooks?.gitPostCommit))) {
95812
96092
  return true;
95813
96093
  }
95814
- const hookPath = path188.join(projectRoot, ".git", "hooks", "post-commit");
96094
+ const hookPath = path189.join(projectRoot, ".git", "hooks", "post-commit");
95815
96095
  if (!await pathExists(hookPath)) {
95816
96096
  return false;
95817
96097
  }
@@ -95831,11 +96111,11 @@ async function collectDashboardData(metaprojectRoot) {
95831
96111
  if (testing) {
95832
96112
  data.testing = testing;
95833
96113
  }
95834
- const wiki = await collectMarkdownPages(path188.join(metaprojectRoot, "wiki"), "wiki");
96114
+ const wiki = await collectMarkdownPages(path189.join(metaprojectRoot, "wiki"), "wiki");
95835
96115
  if (wiki.length > 0) {
95836
96116
  data.wiki = { pages: wiki };
95837
96117
  }
95838
- const memory = await collectMarkdownPages(path188.join(metaprojectRoot, "memory"), "memory");
96118
+ const memory = await collectMarkdownPages(path189.join(metaprojectRoot, "memory"), "memory");
95839
96119
  if (memory.length > 0) {
95840
96120
  data.memory = { entries: memory };
95841
96121
  }
@@ -95850,7 +96130,7 @@ async function collectDashboardData(metaprojectRoot) {
95850
96130
  return data;
95851
96131
  }
95852
96132
  async function collectTasksDashboardData(metaprojectRoot) {
95853
- const flowsRoot = path188.join(metaprojectRoot, "flows");
96133
+ const flowsRoot = path189.join(metaprojectRoot, "flows");
95854
96134
  if (!await pathExists(flowsRoot)) {
95855
96135
  return null;
95856
96136
  }
@@ -95862,7 +96142,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
95862
96142
  }
95863
96143
  const flows = [];
95864
96144
  for (const dir of dirEntries) {
95865
- const flowPath = path188.join(flowsRoot, dir, "flow.json");
96145
+ const flowPath = path189.join(flowsRoot, dir, "flow.json");
95866
96146
  if (!await pathExists(flowPath)) {
95867
96147
  continue;
95868
96148
  }
@@ -95870,7 +96150,7 @@ async function collectTasksDashboardData(metaprojectRoot) {
95870
96150
  const flow = JSON.parse(await readFile99(flowPath, "utf8"));
95871
96151
  const tasks = Array.isArray(flow.tasks) ? flow.tasks : [];
95872
96152
  let acTotal = 0;
95873
- const acPath = path188.join(flowsRoot, dir, "acceptance-criteria.md");
96153
+ const acPath = path189.join(flowsRoot, dir, "acceptance-criteria.md");
95874
96154
  if (await pathExists(acPath)) {
95875
96155
  const acContent = await readFile99(acPath, "utf8");
95876
96156
  acTotal = (acContent.match(/^- AC\d+:/gm) ?? []).length;
@@ -95924,7 +96204,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
95924
96204
  "data/testing/context.md"
95925
96205
  ];
95926
96206
  for (const href of staticHrefs) {
95927
- const filePath = path188.join(metaprojectRoot, ...href.split("/"));
96207
+ const filePath = path189.join(metaprojectRoot, ...href.split("/"));
95928
96208
  if (!await pathExists(filePath)) {
95929
96209
  continue;
95930
96210
  }
@@ -95941,7 +96221,7 @@ async function collectDashboardDocs(metaprojectRoot, wiki, memory) {
95941
96221
  return docs;
95942
96222
  }
95943
96223
  async function collectHealthDashboardData(metaprojectRoot) {
95944
- const reportPath = path188.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
96224
+ const reportPath = path189.join(metaprojectRoot, "data", "health", "artifacts", "latest.json");
95945
96225
  if (!await pathExists(reportPath)) {
95946
96226
  return;
95947
96227
  }
@@ -96050,8 +96330,8 @@ function metricToScope(metric) {
96050
96330
  };
96051
96331
  }
96052
96332
  async function collectGraphDashboardData(metaprojectRoot) {
96053
- const nodesPath = path188.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
96054
- const edgesPath = path188.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
96333
+ const nodesPath = path189.join(metaprojectRoot, "data", "gdgraph", "storage", "nodes.jsonl");
96334
+ const edgesPath = path189.join(metaprojectRoot, "data", "gdgraph", "storage", "edges.jsonl");
96055
96335
  if (!await pathExists(nodesPath) || !await pathExists(edgesPath)) {
96056
96336
  return;
96057
96337
  }
@@ -96102,8 +96382,8 @@ async function collectGraphDashboardData(metaprojectRoot) {
96102
96382
  };
96103
96383
  }
96104
96384
  async function collectTestingDashboardData(metaprojectRoot) {
96105
- const reportPath = path188.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
96106
- const contextPath = path188.join(metaprojectRoot, "data", "testing", "context.md");
96385
+ const reportPath = path189.join(metaprojectRoot, "data", "testing", "artifacts", "latest.json");
96386
+ const contextPath = path189.join(metaprojectRoot, "data", "testing", "context.md");
96107
96387
  if (await pathExists(reportPath)) {
96108
96388
  const report = JSON.parse(await readFile99(reportPath, "utf8"));
96109
96389
  const totalTests = numberOrUndefined(report.total);
@@ -96132,7 +96412,7 @@ async function collectMarkdownPages(root, hrefPrefix) {
96132
96412
  const files = await listMarkdownFiles(root);
96133
96413
  const pages = [];
96134
96414
  for (const filePath of files.slice(0, 40)) {
96135
- const relativePath = path188.relative(root, filePath).split(path188.sep).join("/");
96415
+ const relativePath = path189.relative(root, filePath).split(path189.sep).join("/");
96136
96416
  if (relativePath === "index.md" || relativePath.startsWith("templates/")) {
96137
96417
  continue;
96138
96418
  }
@@ -96153,7 +96433,7 @@ async function listMarkdownFiles(root) {
96153
96433
  const entries = await readdir35(root, { withFileTypes: true });
96154
96434
  const files = [];
96155
96435
  for (const entry of entries) {
96156
- const fullPath = path188.join(root, entry.name);
96436
+ const fullPath = path189.join(root, entry.name);
96157
96437
  if (entry.isDirectory()) {
96158
96438
  files.push(...await listMarkdownFiles(fullPath));
96159
96439
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -96201,7 +96481,7 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
96201
96481
  const manifest = {
96202
96482
  schemaVersion: 1,
96203
96483
  standardVersion: STANDARD_VERSION,
96204
- name: `${path188.basename(path188.dirname(metaprojectRoot))}-metaproject`,
96484
+ name: `${path189.basename(path189.dirname(metaprojectRoot))}-metaproject`,
96205
96485
  createdBy: "keryx",
96206
96486
  profiles: computeProfiles(enabledModuleKeys),
96207
96487
  paths: {
@@ -96291,11 +96571,11 @@ async function writeRecoveredManifest(metaprojectRoot, modules) {
96291
96571
  metaproject: ".metaproject/index.md"
96292
96572
  }
96293
96573
  };
96294
- await writeFile54(path188.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
96574
+ await writeFile54(path189.join(metaprojectRoot, "metaproject.json"), `${JSON.stringify(manifest, null, 2)}
96295
96575
  `, "utf8");
96296
96576
  }
96297
96577
  async function enableTasksInManifest(metaprojectRoot) {
96298
- const manifestPath = path188.join(metaprojectRoot, "metaproject.json");
96578
+ const manifestPath = path189.join(metaprojectRoot, "metaproject.json");
96299
96579
  if (!await pathExists(manifestPath)) {
96300
96580
  return;
96301
96581
  }
@@ -96318,7 +96598,7 @@ async function enableTasksInManifest(metaprojectRoot) {
96318
96598
  `, "utf8");
96319
96599
  }
96320
96600
  async function updateManifestAgentEntrypoints(metaprojectRoot, ruleSources) {
96321
- const manifestPath = path188.join(metaprojectRoot, "metaproject.json");
96601
+ const manifestPath = path189.join(metaprojectRoot, "metaproject.json");
96322
96602
  if (!await pathExists(manifestPath)) {
96323
96603
  return;
96324
96604
  }
@@ -96354,72 +96634,72 @@ async function updateRuntime(projectRoot) {
96354
96634
  }
96355
96635
  }
96356
96636
  async function findRuntimeRoot(projectRoot) {
96357
- const projectRuntime = path188.join(projectRoot, ".metaproject", "runtime", "keryx");
96358
- if (await pathExists(path188.join(projectRuntime, ".git"))) {
96637
+ const projectRuntime = path189.join(projectRoot, ".metaproject", "runtime", "keryx");
96638
+ if (await pathExists(path189.join(projectRuntime, ".git"))) {
96359
96639
  return projectRuntime;
96360
96640
  }
96361
96641
  const home = process.env.HOME;
96362
96642
  if (!home) {
96363
96643
  return null;
96364
96644
  }
96365
- const globalRuntime = path188.join(home, ".keryx", "keryx");
96366
- if (await pathExists(path188.join(globalRuntime, ".git"))) {
96645
+ const globalRuntime = path189.join(home, ".keryx", "keryx");
96646
+ if (await pathExists(path189.join(globalRuntime, ".git"))) {
96367
96647
  return globalRuntime;
96368
96648
  }
96369
96649
  return null;
96370
96650
  }
96371
96651
  async function createServiceDirs(metaprojectRoot, modules) {
96372
96652
  const dirs = [
96373
- path188.join(metaprojectRoot, "core"),
96374
- path188.join(metaprojectRoot, "hooks", "post-update.d"),
96375
- path188.join(metaprojectRoot, "modules"),
96376
- path188.join(metaprojectRoot, "rules"),
96377
- path188.join(metaprojectRoot, "skills", "project-rules"),
96653
+ path189.join(metaprojectRoot, "core"),
96654
+ path189.join(metaprojectRoot, "hooks", "post-update.d"),
96655
+ path189.join(metaprojectRoot, "modules"),
96656
+ path189.join(metaprojectRoot, "rules"),
96657
+ path189.join(metaprojectRoot, "skills", "project-rules"),
96378
96658
  ...modules.enableGdgraph ? [
96379
- path188.join(metaprojectRoot, "core", "gdgraph"),
96380
- path188.join(metaprojectRoot, "skills", "gdgraph")
96659
+ path189.join(metaprojectRoot, "core", "gdgraph"),
96660
+ path189.join(metaprojectRoot, "skills", "gdgraph")
96381
96661
  ] : [],
96382
96662
  ...modules.enableGdctx ? [
96383
- path188.join(metaprojectRoot, "core", "gdctx"),
96384
- path188.join(metaprojectRoot, "skills", "gdctx")
96663
+ path189.join(metaprojectRoot, "core", "gdctx"),
96664
+ path189.join(metaprojectRoot, "skills", "gdctx")
96385
96665
  ] : [],
96386
96666
  ...modules.enableGdwiki ? [
96387
- path188.join(metaprojectRoot, "skills", "gdwiki"),
96388
- path188.join(metaprojectRoot, "wiki", "templates")
96667
+ path189.join(metaprojectRoot, "skills", "gdwiki"),
96668
+ path189.join(metaprojectRoot, "wiki", "templates")
96389
96669
  ] : [],
96390
96670
  ...modules.enableHealth ? [
96391
- path188.join(metaprojectRoot, "core", "health"),
96392
- path188.join(metaprojectRoot, "skills", "health")
96671
+ path189.join(metaprojectRoot, "core", "health"),
96672
+ path189.join(metaprojectRoot, "skills", "health")
96393
96673
  ] : [],
96394
96674
  ...modules.enableTesting ? [
96395
- path188.join(metaprojectRoot, "core", "testing"),
96396
- path188.join(metaprojectRoot, "skills", "testing")
96675
+ path189.join(metaprojectRoot, "core", "testing"),
96676
+ path189.join(metaprojectRoot, "skills", "testing")
96397
96677
  ] : [],
96398
96678
  ...modules.enableMemory ? [
96399
- path188.join(metaprojectRoot, "core", "memory"),
96400
- path188.join(metaprojectRoot, "skills", "memory"),
96401
- path188.join(metaprojectRoot, "memory", "templates")
96679
+ path189.join(metaprojectRoot, "core", "memory"),
96680
+ path189.join(metaprojectRoot, "skills", "memory"),
96681
+ path189.join(metaprojectRoot, "memory", "templates")
96402
96682
  ] : [],
96403
96683
  ...modules.enableTasks ? [
96404
- path188.join(metaprojectRoot, "flows"),
96405
- path188.join(metaprojectRoot, "skills", "flow")
96684
+ path189.join(metaprojectRoot, "flows"),
96685
+ path189.join(metaprojectRoot, "skills", "flow")
96406
96686
  ] : [],
96407
96687
  ...modules.enableSecurity ? [
96408
- path188.join(metaprojectRoot, "core", "security")
96688
+ path189.join(metaprojectRoot, "core", "security")
96409
96689
  ] : [],
96410
96690
  ...modules.enableSac ? [
96411
- path188.join(metaprojectRoot, "skills", "sac")
96691
+ path189.join(metaprojectRoot, "skills", "sac")
96412
96692
  ] : []
96413
96693
  ];
96414
96694
  await Promise.all(dirs.map((dir) => mkdir62(dir, { recursive: true })));
96415
96695
  }
96416
96696
  async function installGdgraphCoreScripts2(metaprojectRoot) {
96417
- const gdgraphCoreRoot = path188.join(metaprojectRoot, "core", "gdgraph");
96697
+ const gdgraphCoreRoot = path189.join(metaprojectRoot, "core", "gdgraph");
96418
96698
  await mkdir62(gdgraphCoreRoot, { recursive: true });
96419
96699
  for (const file of GDGRAPH_CORE_SOURCES) {
96420
- await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path188.join(gdgraphCoreRoot, file));
96700
+ await copyFileIfChanged2(runtimeSourcePath2(`../gdgraph/${file}`), path189.join(gdgraphCoreRoot, file));
96421
96701
  }
96422
- await writeTextIfChanged4(path188.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
96702
+ await writeTextIfChanged4(path189.join(gdgraphCoreRoot, "cli.ts"), renderGdgraphCoreCli());
96423
96703
  }
96424
96704
  async function installManagedHook2(projectRoot, hookName, blockId, content) {
96425
96705
  const hooksRoot = await resolveGitHooksRoot(projectRoot);
@@ -96427,7 +96707,7 @@ async function installManagedHook2(projectRoot, hookName, blockId, content) {
96427
96707
  return;
96428
96708
  }
96429
96709
  await mkdir62(hooksRoot, { recursive: true });
96430
- const hookPath = path188.join(hooksRoot, hookName);
96710
+ const hookPath = path189.join(hooksRoot, hookName);
96431
96711
  const blockStart = `# keryx:${blockId}:begin`;
96432
96712
  const blockEnd = `# keryx:${blockId}:end`;
96433
96713
  const managedBlock = `${blockStart}
@@ -96436,7 +96716,7 @@ ${blockEnd}`;
96436
96716
  const existing = await pathExists(hookPath) ? await readFile99(hookPath, "utf8") : `#!/usr/bin/env sh
96437
96717
  `;
96438
96718
  const blockPattern = new RegExp(`${escapeRegExp7(blockStart)}[\\s\\S]*?${escapeRegExp7(blockEnd)}`);
96439
- const next = blockPattern.test(existing) ? existing.replace(blockPattern, managedBlock) : `${existing.trimEnd()}
96719
+ const next = blockPattern.test(existing) ? existing.replace(blockPattern, () => managedBlock) : `${existing.trimEnd()}
96440
96720
 
96441
96721
  ${managedBlock}
96442
96722
  `;
@@ -96448,7 +96728,7 @@ async function removeManagedHook2(projectRoot, hookName, blockId) {
96448
96728
  if (!hooksRoot) {
96449
96729
  return;
96450
96730
  }
96451
- const hookPath = path188.join(hooksRoot, hookName);
96731
+ const hookPath = path189.join(hooksRoot, hookName);
96452
96732
  if (!await pathExists(hookPath)) {
96453
96733
  return;
96454
96734
  }
@@ -96470,7 +96750,7 @@ async function prePushHasSecurityBlock2(projectRoot) {
96470
96750
  if (!hooksRoot) {
96471
96751
  return false;
96472
96752
  }
96473
- const hookPath = path188.join(hooksRoot, "pre-push");
96753
+ const hookPath = path189.join(hooksRoot, "pre-push");
96474
96754
  if (!await pathExists(hookPath)) {
96475
96755
  return false;
96476
96756
  }
@@ -96485,7 +96765,7 @@ async function agentSettingsHasSecuritySentinel2(projectRoot) {
96485
96765
  return (await readFile99(file, "utf8")).includes(AGENT_HOOKS_SENTINEL);
96486
96766
  }
96487
96767
  async function readManifest5(metaprojectRoot) {
96488
- const manifestPath = path188.join(metaprojectRoot, "metaproject.json");
96768
+ const manifestPath = path189.join(metaprojectRoot, "metaproject.json");
96489
96769
  if (!await pathExists(manifestPath)) {
96490
96770
  return {
96491
96771
  exists: false,
@@ -96554,7 +96834,7 @@ async function inferManifestFromExistingMetaproject(metaprojectRoot) {
96554
96834
  }
96555
96835
  async function anyPathExists(root, candidates) {
96556
96836
  for (const candidate of candidates) {
96557
- if (await pathExists(path188.join(root, candidate))) {
96837
+ if (await pathExists(path189.join(root, candidate))) {
96558
96838
  return true;
96559
96839
  }
96560
96840
  }
@@ -96577,13 +96857,13 @@ function parseUpdateArgs(args) {
96577
96857
  };
96578
96858
  }
96579
96859
  async function runPostUpdateHooks(projectRoot) {
96580
- const hooksDir = path188.join(projectRoot, ".metaproject", "hooks", "post-update.d");
96860
+ const hooksDir = path189.join(projectRoot, ".metaproject", "hooks", "post-update.d");
96581
96861
  if (!await pathExists(hooksDir)) {
96582
96862
  return;
96583
96863
  }
96584
96864
  const entries = (await readdir35(hooksDir)).sort();
96585
96865
  for (const entry of entries) {
96586
- const hookPath = path188.join(hooksDir, entry);
96866
+ const hookPath = path189.join(hooksDir, entry);
96587
96867
  try {
96588
96868
  await accessExecutable(hookPath);
96589
96869
  } catch {
@@ -96624,14 +96904,14 @@ async function writeTextIfChanged4(filePath, content) {
96624
96904
  if (await pathExists(filePath) && await readFile99(filePath, "utf8") === content) {
96625
96905
  return;
96626
96906
  }
96627
- await mkdir62(path188.dirname(filePath), { recursive: true });
96907
+ await mkdir62(path189.dirname(filePath), { recursive: true });
96628
96908
  await writeFile54(filePath, content, "utf8");
96629
96909
  }
96630
96910
  async function writeTextIfMissing4(filePath, content) {
96631
96911
  if (await pathExists(filePath)) {
96632
96912
  return;
96633
96913
  }
96634
- await mkdir62(path188.dirname(filePath), { recursive: true });
96914
+ await mkdir62(path189.dirname(filePath), { recursive: true });
96635
96915
  await writeFile54(filePath, content, "utf8");
96636
96916
  }
96637
96917
  async function copyFileIfChanged2(from, to) {
@@ -96639,7 +96919,7 @@ async function copyFileIfChanged2(from, to) {
96639
96919
  if (await pathExists(to) && await readFile99(to, "utf8") === next) {
96640
96920
  return;
96641
96921
  }
96642
- await mkdir62(path188.dirname(to), { recursive: true });
96922
+ await mkdir62(path189.dirname(to), { recursive: true });
96643
96923
  await writeFile54(to, next, "utf8");
96644
96924
  }
96645
96925
  function runtimeSourcePath2(relativePath) {
@@ -96648,7 +96928,7 @@ function runtimeSourcePath2(relativePath) {
96648
96928
  return directPath;
96649
96929
  }
96650
96930
  if (relativePath.startsWith("../")) {
96651
- const packagedSourcePath = path188.join(path188.dirname(fileURLToPath7(import.meta.url)), "..", "src", relativePath.slice(3));
96931
+ const packagedSourcePath = path189.join(path189.dirname(fileURLToPath7(import.meta.url)), "..", "src", relativePath.slice(3));
96652
96932
  if (existsSync33(packagedSourcePath)) {
96653
96933
  return packagedSourcePath;
96654
96934
  }
@@ -96683,7 +96963,7 @@ function printHelp20() {
96683
96963
 
96684
96964
  // src/commands/dashboard.ts
96685
96965
  import { spawn as spawn7 } from "child_process";
96686
- import path189 from "path";
96966
+ import path190 from "path";
96687
96967
  init_args();
96688
96968
  async function dashboardCommand(args = []) {
96689
96969
  const options = parseOptions(args);
@@ -96694,7 +96974,7 @@ async function dashboardCommand(args = []) {
96694
96974
  }
96695
96975
  if (subcommand === "build") {
96696
96976
  const result = await buildDashboard();
96697
- const rel = path189.relative(process.cwd(), result.path);
96977
+ const rel = path190.relative(process.cwd(), result.path);
96698
96978
  console.log(` ${style.green(symbols.ok)} Dashboard built ${style.cyan(symbols.arrow)} ${style.cyan(rel)}`);
96699
96979
  note(`Open it: keryx dashboard open`);
96700
96980
  return;
@@ -96702,7 +96982,7 @@ async function dashboardCommand(args = []) {
96702
96982
  if (subcommand === "open") {
96703
96983
  const result = await buildDashboard();
96704
96984
  await openFile(result.path);
96705
- const rel = path189.relative(process.cwd(), result.path);
96985
+ const rel = path190.relative(process.cwd(), result.path);
96706
96986
  console.log(` ${style.green(symbols.ok)} Opened ${style.cyan(rel)}`);
96707
96987
  return;
96708
96988
  }
@@ -96751,7 +97031,7 @@ import { readFileSync as readFileSync8 } from "fs";
96751
97031
  // src/agents/bootstrap.ts
96752
97032
  import { mkdir as mkdir63, readFile as readFile100, writeFile as writeFile55 } from "fs/promises";
96753
97033
  import { homedir as homedir8 } from "os";
96754
- import path190 from "path";
97034
+ import path191 from "path";
96755
97035
  init_fs();
96756
97036
  var AGENT_BOOTSTRAP_START = "<!-- keryx:global-bootstrap -->";
96757
97037
  var AGENT_BOOTSTRAP_END = "<!-- /keryx:global-bootstrap -->";
@@ -96761,35 +97041,35 @@ var AGENT_BOOTSTRAP_RUNTIMES = [
96761
97041
  aliases: ["claude-code"],
96762
97042
  label: "Claude Code",
96763
97043
  fileName: "CLAUDE.md",
96764
- filePath: (homeRoot) => path190.join(homeRoot, ".claude", "CLAUDE.md")
97044
+ filePath: (homeRoot) => path191.join(homeRoot, ".claude", "CLAUDE.md")
96765
97045
  },
96766
97046
  {
96767
97047
  id: "opencode",
96768
97048
  aliases: ["open-code"],
96769
97049
  label: "OpenCode",
96770
97050
  fileName: "AGENTS.md",
96771
- filePath: (homeRoot) => path190.join(homeRoot, ".config", "opencode", "AGENTS.md")
97051
+ filePath: (homeRoot) => path191.join(homeRoot, ".config", "opencode", "AGENTS.md")
96772
97052
  },
96773
97053
  {
96774
97054
  id: "zcode",
96775
97055
  aliases: ["zed", "zed-code"],
96776
97056
  label: "ZCode",
96777
97057
  fileName: "AGENTS.md",
96778
- filePath: (homeRoot) => path190.join(homeRoot, ".zcode", "AGENTS.md")
97058
+ filePath: (homeRoot) => path191.join(homeRoot, ".zcode", "AGENTS.md")
96779
97059
  },
96780
97060
  {
96781
97061
  id: "codex",
96782
97062
  aliases: [],
96783
97063
  label: "Codex",
96784
97064
  fileName: "AGENTS.md",
96785
- filePath: (homeRoot) => path190.join(homeRoot, ".codex", "AGENTS.md")
97065
+ filePath: (homeRoot) => path191.join(homeRoot, ".codex", "AGENTS.md")
96786
97066
  },
96787
97067
  {
96788
97068
  id: "antigravity",
96789
97069
  aliases: ["antigravuty", "antigravity-code"],
96790
97070
  label: "Antigravity",
96791
97071
  fileName: "AGENTS.md",
96792
- filePath: (homeRoot) => path190.join(homeRoot, ".config", "antigravity", "AGENTS.md")
97072
+ filePath: (homeRoot) => path191.join(homeRoot, ".config", "antigravity", "AGENTS.md")
96793
97073
  }
96794
97074
  ];
96795
97075
  function agentBootstrapRuntimeIds() {
@@ -96837,7 +97117,7 @@ async function installAgentBootstrap(runtime, options = {}) {
96837
97117
  const dryRun = options.dryRun === true;
96838
97118
  const wrote = next !== current;
96839
97119
  if (wrote && !dryRun) {
96840
- await mkdir63(path190.dirname(filePath), { recursive: true });
97120
+ await mkdir63(path191.dirname(filePath), { recursive: true });
96841
97121
  await writeFile55(filePath, next, "utf8");
96842
97122
  }
96843
97123
  const status = dryRun ? statusFromContent(runtime, filePath, exists, next) : await agentBootstrapStatus(runtime, homeRoot);
@@ -97199,7 +97479,7 @@ function reduceAgents(events) {
97199
97479
 
97200
97480
  // src/commands/agents.ts
97201
97481
  init_args();
97202
- var RUNTIME_USAGE2 = "<claude|opencode|zcode|codex|antigravity|all>";
97482
+ var RUNTIME_USAGE = "<claude|opencode|zcode|codex|antigravity|all>";
97203
97483
  async function agentsCommand(args = []) {
97204
97484
  const subcommand = args[0];
97205
97485
  if (!subcommand || subcommand === "--help" || subcommand === "-h") {
@@ -97345,9 +97625,9 @@ async function bootstrapCommand(args) {
97345
97625
  function printAgentsHelp() {
97346
97626
  helpTitle("keryx agents", "manage agent bootstrap instructions and monitor a subagent fleet");
97347
97627
  helpUsage([
97348
- `keryx agents bootstrap status --runtime ${RUNTIME_USAGE2}`,
97349
- `keryx agents bootstrap install --runtime ${RUNTIME_USAGE2} [--dry-run]`,
97350
- `keryx agents bootstrap uninstall --runtime ${RUNTIME_USAGE2} [--dry-run]`,
97628
+ `keryx agents bootstrap status --runtime ${RUNTIME_USAGE}`,
97629
+ `keryx agents bootstrap install --runtime ${RUNTIME_USAGE} [--dry-run]`,
97630
+ `keryx agents bootstrap uninstall --runtime ${RUNTIME_USAGE} [--dry-run]`,
97351
97631
  "keryx agents bootstrap print",
97352
97632
  "keryx agents monitor <events-file> [--json]",
97353
97633
  "keryx agents external list [--json] [--no-probe]",
@@ -97357,9 +97637,9 @@ function printAgentsHelp() {
97357
97637
  function printBootstrapHelp() {
97358
97638
  helpTitle("keryx agents bootstrap", "install optional Metaproject routing into global agent entrypoints");
97359
97639
  helpUsage([
97360
- `keryx agents bootstrap status --runtime ${RUNTIME_USAGE2}`,
97361
- `keryx agents bootstrap install --runtime ${RUNTIME_USAGE2} [--dry-run]`,
97362
- `keryx agents bootstrap uninstall --runtime ${RUNTIME_USAGE2} [--dry-run]`,
97640
+ `keryx agents bootstrap status --runtime ${RUNTIME_USAGE}`,
97641
+ `keryx agents bootstrap install --runtime ${RUNTIME_USAGE} [--dry-run]`,
97642
+ `keryx agents bootstrap uninstall --runtime ${RUNTIME_USAGE} [--dry-run]`,
97363
97643
  "keryx agents bootstrap print"
97364
97644
  ]);
97365
97645
  helpOptions([
@@ -97374,7 +97654,7 @@ function printBootstrapHelp() {
97374
97654
  // src/commands/metrics.ts
97375
97655
  init_args();
97376
97656
  import { readFile as readFile101 } from "fs/promises";
97377
- import path192 from "path";
97657
+ import path193 from "path";
97378
97658
 
97379
97659
  // src/metrics/benchmark.ts
97380
97660
  var RELIABILITIES2 = new Set(["exact", "estimated", "unknown"]);
@@ -98191,7 +98471,7 @@ function buildContainmentManifest(inputs, options = {}) {
98191
98471
 
98192
98472
  // src/metrics/oracle-runner.ts
98193
98473
  import { mkdir as mkdir64, writeFile as writeFile56 } from "fs/promises";
98194
- import path191 from "path";
98474
+ import path192 from "path";
98195
98475
 
98196
98476
  // src/metrics/ir.ts
98197
98477
  function toIdSet(ids) {
@@ -98637,9 +98917,9 @@ function buildEvidenceBundle(input, options = {}) {
98637
98917
  async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
98638
98918
  const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
98639
98919
  const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
98640
- const dir = path191.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
98920
+ const dir = path192.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
98641
98921
  await mkdir64(dir, { recursive: true });
98642
- const write = (name, value) => writeFile56(path191.join(dir, name), `${JSON.stringify(value, null, 2)}
98922
+ const write = (name, value) => writeFile56(path192.join(dir, name), `${JSON.stringify(value, null, 2)}
98643
98923
  `, "utf8");
98644
98924
  await Promise.all([
98645
98925
  write("inputs.json", bundle.inputs),
@@ -98679,7 +98959,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
98679
98959
  console.log("# metrics status");
98680
98960
  console.log("");
98681
98961
  console.log(`root: ${root}`);
98682
- console.log(`enabled: ${await Bun.file(path192.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
98962
+ console.log(`enabled: ${await Bun.file(path193.join(projectRoot, ".metaproject", "metaproject.json")).exists() ? "yes" : "no"}`);
98683
98963
  const latest = await readLatestPointer(root);
98684
98964
  console.log(`latest: ${latest.status}`);
98685
98965
  return;
@@ -98691,7 +98971,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
98691
98971
  process.exitCode = 1;
98692
98972
  return;
98693
98973
  }
98694
- const record = JSON.parse(await readFile101(path192.resolve(projectRoot, file), "utf8"));
98974
+ const record = JSON.parse(await readFile101(path193.resolve(projectRoot, file), "utf8"));
98695
98975
  const result = validateRunRecord(record);
98696
98976
  console.log(result.valid ? "valid: yes" : "valid: no");
98697
98977
  for (const error of result.errors)
@@ -98716,7 +98996,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
98716
98996
  process.exitCode = 1;
98717
98997
  return;
98718
98998
  }
98719
- const file = path192.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
98999
+ const file = path193.join(metricsRoot(projectRoot), "runs", `${runId}.json`);
98720
99000
  if (!await Bun.file(file).exists()) {
98721
99001
  console.error(`Run not found: ${runId}`);
98722
99002
  process.exitCode = 1;
@@ -98733,8 +99013,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
98733
99013
  process.exitCode = 1;
98734
99014
  return;
98735
99015
  }
98736
- const a = JSON.parse(await readFile101(path192.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
98737
- const b = JSON.parse(await readFile101(path192.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
99016
+ const a = JSON.parse(await readFile101(path193.join(metricsRoot(projectRoot), "runs", `${runA}.json`), "utf8"));
99017
+ const b = JSON.parse(await readFile101(path193.join(metricsRoot(projectRoot), "runs", `${runB}.json`), "utf8"));
98738
99018
  const comparison = compareExecutionRuns(a, b);
98739
99019
  console.log(stableJson(comparison));
98740
99020
  return;
@@ -98768,8 +99048,8 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
98768
99048
  return;
98769
99049
  }
98770
99050
  const template = createPairedBenchmarkTemplate(taskIds);
98771
- await Bun.write(path192.resolve(projectRoot, out), stableJson(template));
98772
- console.log(`manifest: ${path192.relative(projectRoot, path192.resolve(projectRoot, out))}`);
99051
+ await Bun.write(path193.resolve(projectRoot, out), stableJson(template));
99052
+ console.log(`manifest: ${path193.relative(projectRoot, path193.resolve(projectRoot, out))}`);
98773
99053
  return;
98774
99054
  }
98775
99055
  if (subcommand === "benchmark" && args[1] === "run") {
@@ -98783,7 +99063,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
98783
99063
  process.exitCode = 1;
98784
99064
  return;
98785
99065
  }
98786
- const raw = JSON.parse(await readFile101(path192.resolve(projectRoot, file), "utf8"));
99066
+ const raw = JSON.parse(await readFile101(path193.resolve(projectRoot, file), "utf8"));
98787
99067
  const input = Array.isArray(raw) ? raw : raw.runs ?? [];
98788
99068
  const result = validatePairedBenchmark(input);
98789
99069
  console.log(stableJson(result));
@@ -98795,7 +99075,7 @@ async function metricsCommand(args = [], projectRoot = process.cwd()) {
98795
99075
  process.exitCode = 1;
98796
99076
  }
98797
99077
  async function loadAffectedSets(projectRoot, file) {
98798
- const raw = JSON.parse(await readFile101(path192.resolve(projectRoot, file), "utf8"));
99078
+ const raw = JSON.parse(await readFile101(path193.resolve(projectRoot, file), "utf8"));
98799
99079
  const map = new Map;
98800
99080
  for (const entry of raw.targets ?? []) {
98801
99081
  if (typeof entry.target === "string")
@@ -98866,7 +99146,7 @@ async function runHarnessLayer(projectRoot, args, ladder) {
98866
99146
  let tasks;
98867
99147
  let model;
98868
99148
  try {
98869
- const raw = JSON.parse(await readFile101(path192.resolve(projectRoot, resultsPath), "utf8"));
99149
+ const raw = JSON.parse(await readFile101(path193.resolve(projectRoot, resultsPath), "utf8"));
98870
99150
  tasks = raw.tasks ?? [];
98871
99151
  model = raw.model;
98872
99152
  } catch (error) {
@@ -98897,7 +99177,7 @@ async function runSafetyCompletionHonestyLayer(projectRoot, args, ladder) {
98897
99177
  let cases;
98898
99178
  let model;
98899
99179
  try {
98900
- const raw = JSON.parse(await readFile101(path192.resolve(projectRoot, resultsPath), "utf8"));
99180
+ const raw = JSON.parse(await readFile101(path193.resolve(projectRoot, resultsPath), "utf8"));
98901
99181
  cases = raw.cases ?? [];
98902
99182
  model = raw.model;
98903
99183
  } catch (error) {
@@ -98922,7 +99202,7 @@ async function runSafetyFalsePremiseLayer(projectRoot, args, ladder) {
98922
99202
  let cases;
98923
99203
  let model;
98924
99204
  try {
98925
- const raw = JSON.parse(await readFile101(path192.resolve(projectRoot, resultsPath), "utf8"));
99205
+ const raw = JSON.parse(await readFile101(path193.resolve(projectRoot, resultsPath), "utf8"));
98926
99206
  cases = raw.cases ?? [];
98927
99207
  model = raw.model;
98928
99208
  } catch (error) {
@@ -98947,7 +99227,7 @@ async function runSafetyContainmentLayer(projectRoot, args, ladder, caseClass) {
98947
99227
  let cases;
98948
99228
  let model;
98949
99229
  try {
98950
- const raw = JSON.parse(await readFile101(path192.resolve(projectRoot, resultsPath), "utf8"));
99230
+ const raw = JSON.parse(await readFile101(path193.resolve(projectRoot, resultsPath), "utf8"));
98951
99231
  cases = raw.cases ?? [];
98952
99232
  model = raw.model;
98953
99233
  } catch (error) {
@@ -99009,12 +99289,12 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
99009
99289
  }
99010
99290
  const manifests = buildOracleManifestsByGold(inputs, { ladder });
99011
99291
  if (outDir) {
99012
- const resolvedOut = path192.resolve(projectRoot, outDir);
99292
+ const resolvedOut = path193.resolve(projectRoot, outDir);
99013
99293
  for (const input of inputs) {
99014
99294
  for (const named of input.golds) {
99015
99295
  const bundle = buildEvidenceBundle({ target: input.target, system: input.system, gold: named.gold }, { ladder, goldReference: goldPathFor(named.kind), timestamp: new Date().toISOString() });
99016
- const dir = await persistEvidenceBundle(path192.join(resolvedOut, named.kind), bundle, ladder);
99017
- console.error(`bundle[${named.kind}]: ${path192.relative(projectRoot, dir)}`);
99296
+ const dir = await persistEvidenceBundle(path193.join(resolvedOut, named.kind), bundle, ladder);
99297
+ console.error(`bundle[${named.kind}]: ${path193.relative(projectRoot, dir)}`);
99018
99298
  }
99019
99299
  }
99020
99300
  }
@@ -99038,7 +99318,7 @@ async function runGdgraphLayer(projectRoot, args, ladder) {
99038
99318
  return allValid;
99039
99319
  }
99040
99320
  async function loadCoverageMap2(projectRoot, file) {
99041
- const raw = JSON.parse(await readFile101(path192.resolve(projectRoot, file), "utf8"));
99321
+ const raw = JSON.parse(await readFile101(path193.resolve(projectRoot, file), "utf8"));
99042
99322
  return raw.coverageMap ?? {};
99043
99323
  }
99044
99324
  async function runTestingLayer(projectRoot, args, ladder) {
@@ -99075,7 +99355,7 @@ async function runTestingLayer(projectRoot, args, ladder) {
99075
99355
  return result.valid;
99076
99356
  }
99077
99357
  async function loadMemoryGoldK(projectRoot, file) {
99078
- const raw = JSON.parse(await readFile101(path192.resolve(projectRoot, file), "utf8"));
99358
+ const raw = JSON.parse(await readFile101(path193.resolve(projectRoot, file), "utf8"));
99079
99359
  return typeof raw.k === "number" && raw.k > 0 ? raw.k : 3;
99080
99360
  }
99081
99361
  async function runMemoryLayer(projectRoot, args, ladder) {
@@ -99112,11 +99392,11 @@ async function runMemoryLayer(projectRoot, args, ladder) {
99112
99392
  return result.valid;
99113
99393
  }
99114
99394
  async function loadWikiGoldK(projectRoot, file) {
99115
- const raw = JSON.parse(await readFile101(path192.resolve(projectRoot, file), "utf8"));
99395
+ const raw = JSON.parse(await readFile101(path193.resolve(projectRoot, file), "utf8"));
99116
99396
  return typeof raw.k === "number" && raw.k > 0 ? raw.k : 5;
99117
99397
  }
99118
99398
  async function loadWikiGroundedness(projectRoot, file) {
99119
- const raw = JSON.parse(await readFile101(path192.resolve(projectRoot, file), "utf8"));
99399
+ const raw = JSON.parse(await readFile101(path193.resolve(projectRoot, file), "utf8"));
99120
99400
  const map = new Map;
99121
99401
  for (const entry of raw.targets ?? []) {
99122
99402
  if (typeof entry.target !== "string" || !Array.isArray(entry.scores) || entry.scores.length !== 3)
@@ -99172,7 +99452,7 @@ async function runWikiLayer(projectRoot, args, ladder) {
99172
99452
  return result.valid;
99173
99453
  }
99174
99454
  async function loadGdctxFacts(projectRoot, file) {
99175
- const raw = JSON.parse(await readFile101(path192.resolve(projectRoot, file), "utf8"));
99455
+ const raw = JSON.parse(await readFile101(path193.resolve(projectRoot, file), "utf8"));
99176
99456
  const inputs = [];
99177
99457
  for (const entry of raw.inputs ?? []) {
99178
99458
  if (typeof entry.input === "string") {
@@ -99210,7 +99490,7 @@ async function collect(projectRoot, args) {
99210
99490
  process.exitCode = 1;
99211
99491
  return;
99212
99492
  }
99213
- const raw = JSON.parse(await readFile101(path192.resolve(projectRoot, eventFile), "utf8"));
99493
+ const raw = JSON.parse(await readFile101(path193.resolve(projectRoot, eventFile), "utf8"));
99214
99494
  const events = Array.isArray(raw) ? raw : raw.events;
99215
99495
  const startedAt = optionValue(args, "--started-at") ?? events[0]?.timestamp_utc ?? new Date().toISOString();
99216
99496
  const finishedAt = optionValue(args, "--finished-at") ?? events.at(-1)?.timestamp_utc ?? startedAt;
@@ -99226,11 +99506,11 @@ async function collect(projectRoot, args) {
99226
99506
  parentRunId: optionValue(args, "--parent-run-id") ?? null
99227
99507
  });
99228
99508
  const result = await writeRunArtifacts(metricsRoot(projectRoot), record, { cwd: projectRoot });
99229
- console.log(`json: ${path192.relative(projectRoot, result.jsonPath)}`);
99230
- console.log(`markdown: ${path192.relative(projectRoot, result.markdownPath)}`);
99509
+ console.log(`json: ${path193.relative(projectRoot, result.jsonPath)}`);
99510
+ console.log(`markdown: ${path193.relative(projectRoot, result.markdownPath)}`);
99231
99511
  }
99232
99512
  function metricsRoot(projectRoot) {
99233
- return path192.join(projectRoot, ".metaproject", "data", "metrics");
99513
+ return path193.join(projectRoot, ".metaproject", "data", "metrics");
99234
99514
  }
99235
99515
  function printMetricsHelp() {
99236
99516
  console.log(`keryx metrics
@@ -100122,6 +100402,8 @@ var CLI_ROUTES = {
100122
100402
  commands: commandsCommand,
100123
100403
  security: securityCommand,
100124
100404
  sandbox: sandboxCommand,
100405
+ "serve-mcp": serveMcpCommand,
100406
+ integrate: integrateCommand,
100125
100407
  mcp: mcpCommand,
100126
100408
  harness: harnessCommand,
100127
100409
  shell: shellCommand,
@@ -100260,10 +100542,12 @@ Usage:
100260
100542
  keryx security incidents [--limit <n>]
100261
100543
  keryx security hooks install --runtime <claude|cursor|windsurf|generic-mcp|all>
100262
100544
  keryx security eval [--corpus <name|all>] [--with-model]
100263
- keryx mcp serve [--http] [--cwd <project-root>]
100545
+ keryx serve-mcp [--http] [--cwd <project-root>]
100546
+ keryx integrate [--remove] <cursor|claude|opencode|vscode|generic|all> [--dry-run]
100547
+ keryx mcp serve [--http] ... # retired: use keryx serve-mcp
100264
100548
  keryx workspace create --title <title> [--component <workspace-relative-ref>]
100265
100549
  keryx workspace list|show|add-resource
100266
- keryx mcp install|uninstall --runtime <cursor|claude|opencode|generic|all> [--dry-run]
100550
+ keryx mcp install|uninstall --runtime ... # retired: use keryx integrate
100267
100551
  keryx retention status [--json]
100268
100552
  keryx retention sweep [--apply] [--target <id>]... [--max-age-days <n>] [--max-bytes <n>] [--json]
100269
100553
  Bound gdctx raw/artifacts logs and owner write-conflict
@@ -100308,7 +100592,9 @@ Commands:
100308
100592
  commands Agent-callable command registry (intents, args, output, model usage)
100309
100593
  security Policy-based scanning, redaction, guardrails and audit reports
100310
100594
  sandbox Report OS sandbox launcher availability and the per-capability containment matrix
100311
- mcp Expose Metaproject services over the Model Context Protocol (opt-in)
100595
+ serve-mcp Expose Metaproject services over the Model Context Protocol (opt-in)
100596
+ integrate Wire this project into an editor or agent as an MCP server
100597
+ mcp Retired spelling of serve-mcp / integrate; still works, names its replacement
100312
100598
  metrics Provenance-aware execution observability: run records, baselines, benchmarks
100313
100599
  workspace Shared Agent Context: workspaces, FWK reads, propose/review (module sac)
100314
100600
  retention Bound stores that grow without bound (gdctx raw/artifacts, owner write-conflict sidecars)