@mrciphersmith/keryx 0.3.89 → 0.3.90

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
@@ -21105,7 +21105,7 @@ var package_default;
21105
21105
  var init_package = __esm(() => {
21106
21106
  package_default = {
21107
21107
  name: "@mrciphersmith/keryx",
21108
- version: "0.3.89",
21108
+ version: "0.3.90",
21109
21109
  description: "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
21110
21110
  private: false,
21111
21111
  publishConfig: {
@@ -98346,7 +98346,7 @@ var init_registry3 = __esm(() => {
98346
98346
  // src/harness/tool/builtin/spawn-subagent-tool.ts
98347
98347
  import { createHash as createHash64, randomUUID as randomUUID26 } from "crypto";
98348
98348
  import { execFile as execFile7 } from "child_process";
98349
- import { lstat as lstat15, mkdtemp as mkdtemp2, readFile as readFile108, realpath as realpath19, rm as rm15, stat as stat27 } from "fs/promises";
98349
+ import { lstat as lstat15, mkdir as mkdir56, mkdtemp as mkdtemp2, readFile as readFile108, realpath as realpath19, rm as rm15, stat as stat27, writeFile as writeFile36 } from "fs/promises";
98350
98350
  import { tmpdir as tmpdir6 } from "os";
98351
98351
  import path222 from "path";
98352
98352
  import { promisify as promisify5 } from "util";
@@ -98413,6 +98413,57 @@ ${blocks.join(`
98413
98413
 
98414
98414
  `)}` };
98415
98415
  }
98416
+ async function resolveSaveResultPath(root, target) {
98417
+ if (target === undefined)
98418
+ return { ok: true, abs: undefined };
98419
+ if (typeof target !== "string" || target.trim() === "") {
98420
+ return { ok: false, reason: "spawn_subagent save_result_to must be a non-empty project-relative path" };
98421
+ }
98422
+ const abs = confineToRoot(root, target);
98423
+ if (abs === null)
98424
+ return { ok: false, reason: `spawn_subagent save_result_to path escapes the project root: ${target}` };
98425
+ if (CONTEXT_FILE_DENYLIST.test(path222.relative(root, abs).split(path222.sep).join("/"))) {
98426
+ return { ok: false, reason: `spawn_subagent save_result_to refuses a credential or VCS-internal path: ${target}` };
98427
+ }
98428
+ try {
98429
+ const realRoot = await realpath19(root);
98430
+ let probe = abs;
98431
+ for (;; ) {
98432
+ try {
98433
+ const real = await realpath19(probe);
98434
+ const rel = path222.relative(realRoot, real);
98435
+ if (rel.startsWith("..") || path222.isAbsolute(rel)) {
98436
+ return { ok: false, reason: `spawn_subagent save_result_to resolves outside the project root: ${target}` };
98437
+ }
98438
+ if (probe === abs && !(await stat27(abs)).isFile()) {
98439
+ return { ok: false, reason: `spawn_subagent save_result_to is not a regular file: ${target}` };
98440
+ }
98441
+ break;
98442
+ } catch (err) {
98443
+ if (err.code !== "ENOENT")
98444
+ throw err;
98445
+ const parent = path222.dirname(probe);
98446
+ if (parent === probe)
98447
+ throw err;
98448
+ probe = parent;
98449
+ }
98450
+ }
98451
+ } catch {
98452
+ return { ok: false, reason: `spawn_subagent save_result_to cannot be used: ${target}` };
98453
+ }
98454
+ return { ok: true, abs };
98455
+ }
98456
+ async function saveChildResult(abs, text) {
98457
+ try {
98458
+ await mkdir56(path222.dirname(abs), { recursive: true });
98459
+ await writeFile36(abs, text.endsWith(`
98460
+ `) ? text : `${text}
98461
+ `, "utf8");
98462
+ return;
98463
+ } catch (err) {
98464
+ return err.message;
98465
+ }
98466
+ }
98416
98467
  function resolveSubagentMaxToolCalls(env = process.env) {
98417
98468
  const n = parseIntEnvVar(env, ENV_SUBAGENT_MAX_TOOL_CALLS);
98418
98469
  return n === undefined || n <= 0 ? undefined : n;
@@ -98618,7 +98669,7 @@ function createSpawnSubagentTool(deps) {
98618
98669
  return {
98619
98670
  definition: {
98620
98671
  name: "spawn_subagent",
98621
- description: "Spawn a bounded subagent to work on a focused subtask in parallel-safe isolation " + "(MAE multi-agent). Use for independent investigations, reviews, or research while " + "you continue the main plan. Input: { task: string, mode?: 'read_only'|'general', " + "label?: string, max_tool_calls?: integer, max_rounds?: integer, context_files?: string[] }. " + "max_tool_calls is ADVISORY: it never stops the child, it only sets when the child is warned " + "(from 80% of it) to return its result; a hard tool-call cap exists only when the operator " + `configures ${ENV_SUBAGENT_MAX_TOOL_CALLS}. max_rounds limits model rounds (default 40, capped ` + "at 200). A child that hits its round budget or the operator's call cap gets one final round to " + "submit a partial result, and the output's first line is then 'status: BudgetExhausted (...)'. " + "External runtimes cannot accept these limits. " + "Default mode is read_only (no shell). " + "Returns the child's summary. Prefer one clear task per spawn; do not spawn for " + "trivial questions (answer yourself). Optionally accepts a 'model_tier' " + "(light|standard|deep) to size the child's model against your own, and a 'runtime' " + "block to delegate the child to an external vendor coding CLI instead of running it " + "in-process. Optional 'cwd' runs the child's tools against a different directory than " + "yours (e.g. a review worktree checked out at a PR head) \u2014 accepted only when it is your " + "own project root, a descendant of it, or one of its git worktrees; a relative path " + "resolves against your own cwd. Not supported for external (runtime.kind='external') children.",
98672
+ description: "Spawn a bounded subagent to work on a focused subtask in parallel-safe isolation " + "(MAE multi-agent). Use for independent investigations, reviews, or research while " + "you continue the main plan. Input: { task: string, mode?: 'read_only'|'general', " + "label?: string, max_tool_calls?: integer, max_rounds?: integer, context_files?: string[], save_result_to?: string }. " + "max_tool_calls is ADVISORY: it never stops the child, it only sets when the child is warned " + "(from 80% of it) to return its result; a hard tool-call cap exists only when the operator " + `configures ${ENV_SUBAGENT_MAX_TOOL_CALLS}. max_rounds limits model rounds (default 40, capped ` + "at 200). A child that hits its round budget or the operator's call cap gets one final round to " + "submit a partial result, and the output's first line is then 'status: BudgetExhausted (...)'. " + "External runtimes cannot accept these limits. " + "Default mode is read_only (no shell). " + "Returns the child's summary. Prefer one clear task per spawn; do not spawn for " + "trivial questions (answer yourself). Optionally accepts a 'model_tier' " + "(light|standard|deep) to size the child's model against your own, and a 'runtime' " + "block to delegate the child to an external vendor coding CLI instead of running it " + "in-process. Optional 'cwd' runs the child's tools against a different directory than " + "yours (e.g. a review worktree checked out at a PR head) \u2014 accepted only when it is your " + "own project root, a descendant of it, or one of its git worktrees; a relative path " + "resolves against your own cwd. Not supported for external (runtime.kind='external') children.",
98622
98673
  inputSchema: {
98623
98674
  type: "object",
98624
98675
  properties: {
@@ -98640,6 +98691,10 @@ function createSpawnSubagentTool(deps) {
98640
98691
  items: { type: "string" },
98641
98692
  description: "Project-relative files (e.g. review slices) the host copies into the child's task, so " + "you do not paste them yourself. Resolved against your own project root (not 'cwd'); native " + "children only; credential files (.env*, keys) are refused. Up to 40 files and 400000 bytes in total."
98642
98693
  },
98694
+ save_result_to: {
98695
+ type: "string",
98696
+ description: "Project-relative file the host writes the child's complete final text to when it ends, " + "so the result survives your own context (e.g. .metaproject/data/review/raw/<reviewer>.txt). " + "Native children only; credential and VCS paths are refused."
98697
+ },
98643
98698
  max_rounds: {
98644
98699
  type: "integer",
98645
98700
  minimum: 1,
@@ -98718,6 +98773,17 @@ function createSpawnSubagentTool(deps) {
98718
98773
  if (!attached.ok) {
98719
98774
  return { status: "Error", output: attached.reason, isError: true };
98720
98775
  }
98776
+ if (input.save_result_to !== undefined && isExternalRuntimeRequest) {
98777
+ return {
98778
+ status: "Error",
98779
+ output: "spawn_subagent save_result_to is not supported for external (runtime.kind='external') children.",
98780
+ isError: true
98781
+ };
98782
+ }
98783
+ const saveTarget = await resolveSaveResultPath(deps.cwd, input.save_result_to);
98784
+ if (!saveTarget.ok) {
98785
+ return { status: "Error", output: saveTarget.reason, isError: true };
98786
+ }
98721
98787
  const task = taskText;
98722
98788
  const labelRaw = typeof input.label === "string" ? input.label.trim() : "";
98723
98789
  childSeq += 1;
@@ -99191,9 +99257,11 @@ ${boundSummary(foldedPartial)}` : ""),
99191
99257
  ` + `MAE reservation: rounds\u2264${maxRounds} ` + (configuredMaxToolCalls === undefined ? "" : `calls\u2264${configuredMaxToolCalls} `) + (advisoryToolCalls === undefined ? "" : `calls~${advisoryToolCalls}(advisory) `) + `runtime\u2264${spawned.reservation.maxRuntimeMs}ms children=${ledger.childCount}
99192
99258
  ` + (tierRecord === undefined ? "" : `${tierRecord}
99193
99259
  `);
99260
+ const saveNote = saveTarget.abs === undefined ? "" : `
99261
+ [full result ${await saveChildResult(saveTarget.abs, raw) === undefined ? "saved to" : "could NOT be saved to"} ${String(input.save_result_to)}]`;
99194
99262
  if (status === "Completed") {
99195
99263
  return { status, isError, output: `${header}--- summary ---
99196
- ${boundSummary(folded.text)}` };
99264
+ ${boundSummary(folded.text)}${saveNote}` };
99197
99265
  }
99198
99266
  const statusLine = formatStoppedStatusLine(status, turnResult);
99199
99267
  const submitted = turnResult?.submittedResult;
@@ -99208,7 +99276,7 @@ ${raw}`).text;
99208
99276
  status,
99209
99277
  isError,
99210
99278
  output: `${statusLine}
99211
- ${header}${boundedResult}`,
99279
+ ${header}${boundedResult}${saveNote}`,
99212
99280
  partial: boundedResult
99213
99281
  };
99214
99282
  } catch (cause) {
@@ -100926,7 +100994,7 @@ var init_dispatch2 = __esm(() => {
100926
100994
 
100927
100995
  // src/harness/external/acp-fs.ts
100928
100996
  import { constants as constants2, realpathSync as realpathSync15, statSync as statSync12 } from "fs";
100929
- import { mkdir as mkdir56, open as open10, readFile as readFile109 } from "fs/promises";
100997
+ import { mkdir as mkdir57, open as open10, readFile as readFile109 } from "fs/promises";
100930
100998
  import path225 from "path";
100931
100999
  function refusedMethodError(method, reason) {
100932
101000
  return new AcpError(JSON_RPC_ERROR_CODES.methodNotFound, `${method} refused: ${reason}`, { reason });
@@ -100985,7 +101053,7 @@ async function writeTextFileInWorktree(worktree, target, content, requested, hoo
100985
101053
  }
100986
101054
  await hooks.beforeWrite?.();
100987
101055
  const parent = path225.dirname(target);
100988
- await mkdir56(parent, { recursive: true });
101056
+ await mkdir57(parent, { recursive: true });
100989
101057
  const rootReal = realpathSync15(worktree);
100990
101058
  const parentReal = realpathSync15(parent);
100991
101059
  const rel = path225.relative(rootReal, parentReal);
@@ -103087,7 +103155,7 @@ var init_codec2 = __esm(() => {
103087
103155
  });
103088
103156
 
103089
103157
  // src/harness/external/runtime.ts
103090
- import { mkdtemp as mkdtemp4, rm as rm17, writeFile as writeFile36 } from "fs/promises";
103158
+ import { mkdtemp as mkdtemp4, rm as rm17, writeFile as writeFile37 } from "fs/promises";
103091
103159
  import { tmpdir as tmpdir8 } from "os";
103092
103160
  import path227 from "path";
103093
103161
  function refuse2(status, output) {
@@ -103100,7 +103168,7 @@ async function prepareResultSchema() {
103100
103168
  const schemaText = JSON.stringify(schema, null, 2);
103101
103169
  schemaDir = await mkdtemp4(path227.join(tmpdir8(), "keryx-external-result-schema-"));
103102
103170
  const schemaPath = path227.join(schemaDir, "subagent-result.schema.json");
103103
- await writeFile36(schemaPath, schemaText, "utf8");
103171
+ await writeFile37(schemaPath, schemaText, "utf8");
103104
103172
  return { ok: true, schema, schemaText, schemaPath, schemaDir };
103105
103173
  } catch (error) {
103106
103174
  if (schemaDir !== undefined) {
@@ -103209,7 +103277,7 @@ async function runExternalChild(input, deps) {
103209
103277
  if (codexStrict) {
103210
103278
  resultSchemaPathForCodec = path227.join(schemaDir, STRICT_SCHEMA_FILE);
103211
103279
  try {
103212
- await writeFile36(resultSchemaPathForCodec, JSON.stringify(toStrictOutputSchema(bundledResultSchema)), "utf8");
103280
+ await writeFile37(resultSchemaPathForCodec, JSON.stringify(toStrictOutputSchema(bundledResultSchema)), "utf8");
103213
103281
  } catch (error) {
103214
103282
  await rm17(schemaDir, { recursive: true, force: true }).catch(() => {
103215
103283
  return;
@@ -103475,7 +103543,7 @@ var init_runtime2 = __esm(() => {
103475
103543
  // src/harness/run-external-factory.ts
103476
103544
  import { randomUUID as randomUUID27 } from "crypto";
103477
103545
  import { execFile as execFile10 } from "child_process";
103478
- import { mkdir as mkdir57 } from "fs/promises";
103546
+ import { mkdir as mkdir58 } from "fs/promises";
103479
103547
  import { tmpdir as tmpdir9 } from "os";
103480
103548
  import path228 from "path";
103481
103549
  import { promisify as promisify8 } from "util";
@@ -103505,7 +103573,7 @@ async function defaultWorkingDiff(cwd) {
103505
103573
  function ensuringParentDir(port, worktreesDir) {
103506
103574
  return {
103507
103575
  async create(worktreeId) {
103508
- await mkdir57(worktreesDir, { recursive: true });
103576
+ await mkdir58(worktreesDir, { recursive: true });
103509
103577
  return port.create(worktreeId);
103510
103578
  },
103511
103579
  remove: (worktreeId) => port.remove(worktreeId),
@@ -117740,7 +117808,7 @@ var init_repair = __esm(() => {
117740
117808
  });
117741
117809
 
117742
117810
  // src/review/managed.ts
117743
- import { mkdir as mkdir58, readFile as readFile121, readdir as readdir39, realpath as realpath20, rm as rm18, stat as stat29 } from "fs/promises";
117811
+ import { mkdir as mkdir59, readFile as readFile121, readdir as readdir39, realpath as realpath20, rm as rm18, stat as stat29 } from "fs/promises";
117744
117812
  import path252 from "path";
117745
117813
  function reviewsRoot(cwd) {
117746
117814
  return path252.join(cwd, ".metaproject", "reviews");
@@ -117876,7 +117944,7 @@ async function createManagedReviewPackage(input) {
117876
117944
  if (contractErrors.length > 0) {
117877
117945
  throw new Error(`Refusing to record findings that do not satisfy review-finding.schema.json: ${contractErrors.map((error) => `${error.path} ${error.message}`).join("; ")}`);
117878
117946
  }
117879
- await mkdir58(packageDir, { recursive: true });
117947
+ await mkdir59(packageDir, { recursive: true });
117880
117948
  await writeFileAtomic(path252.join(packageDir, "research.json"), `${JSON.stringify(input.research ?? PENDING_RESEARCH, null, 2)}
117881
117949
  `);
117882
117950
  await writeFileAtomic(path252.join(packageDir, "manifest.json"), `${JSON.stringify(manifest, null, 2)}
@@ -125399,7 +125467,7 @@ var init_aggregate = __esm(() => {
125399
125467
  });
125400
125468
 
125401
125469
  // src/governance/report.ts
125402
- import { mkdir as mkdir59 } from "fs/promises";
125470
+ import { mkdir as mkdir60 } from "fs/promises";
125403
125471
  import path261 from "path";
125404
125472
  function governanceDataRoot(cwd) {
125405
125473
  return path261.join(cwd, ".metaproject", "data", "governance");
@@ -125585,7 +125653,7 @@ function renderTriggerSpendLine(spend) {
125585
125653
  }
125586
125654
  async function writeGovernanceArtifacts(cwd, report) {
125587
125655
  const { markdown, json } = artifactPaths(cwd);
125588
- await mkdir59(path261.dirname(markdown), { recursive: true });
125656
+ await mkdir60(path261.dirname(markdown), { recursive: true });
125589
125657
  await writeFileAtomic(markdown, renderGovernanceMarkdown(report));
125590
125658
  await writeFileAtomic(json, `${JSON.stringify(report, null, 2)}
125591
125659
  `);
@@ -128773,7 +128841,7 @@ var init_granted_json_ids = __esm(() => {
128773
128841
  // src/commands/trigger-agent-task.ts
128774
128842
  import { execFile as execFile11 } from "child_process";
128775
128843
  import { randomUUID as randomUUID35 } from "crypto";
128776
- import { mkdtemp as mkdtemp5, mkdir as mkdir60, readdir as readdir42, rm as rm19, writeFile as writeFile37 } from "fs/promises";
128844
+ import { mkdtemp as mkdtemp5, mkdir as mkdir61, readdir as readdir42, rm as rm19, writeFile as writeFile38 } from "fs/promises";
128777
128845
  import { homedir as homedir12, tmpdir as tmpdir11 } from "os";
128778
128846
  import path268 from "path";
128779
128847
  function scrubGrantedOutput(text, env, secrets = [], json = false) {
@@ -128944,9 +129012,9 @@ async function runLocked(projectRoot, entry, action, deps, runId, now) {
128944
129012
  const workdir = path268.join(scratch, "work");
128945
129013
  const scratchHome = path268.join(scratch, "home");
128946
129014
  const grantedCwd = path268.join(scratch, "granted-cwd");
128947
- await mkdir60(workdir, { recursive: true });
128948
- await mkdir60(scratchHome, { recursive: true });
128949
- await mkdir60(grantedCwd, { recursive: true, mode: 448 });
129015
+ await mkdir61(workdir, { recursive: true });
129016
+ await mkdir61(scratchHome, { recursive: true });
129017
+ await mkdir61(grantedCwd, { recursive: true, mode: 448 });
128950
129018
  const cleanup = async () => {
128951
129019
  await rm19(scratch, { recursive: true, force: true }).catch(() => {});
128952
129020
  };
@@ -129114,9 +129182,9 @@ async function runLocked(projectRoot, entry, action, deps, runId, now) {
129114
129182
  try {
129115
129183
  await ensureTriggerDataIgnored(projectRoot);
129116
129184
  const dir = path268.join(triggerReportsDir(projectRoot), entry.name);
129117
- await mkdir60(dir, { recursive: true });
129185
+ await mkdir61(dir, { recursive: true });
129118
129186
  const file = path268.join(dir, `${runId}.md`);
129119
- await writeFile37(file, renderReport3({ entry, action, runId, at: now().toISOString(), outcome, why, cost, grantedCalls, denials, sandboxNote, summary, networkDecisions }), "utf8");
129187
+ await writeFile38(file, renderReport3({ entry, action, runId, at: now().toISOString(), outcome, why, cost, grantedCalls, denials, sandboxNote, summary, networkDecisions }), "utf8");
129120
129188
  await pruneReports(dir, action.report.keep);
129121
129189
  reportPath = path268.relative(projectRoot, file);
129122
129190
  } catch (error) {
@@ -129663,18 +129731,18 @@ var init_types10 = __esm(() => {
129663
129731
 
129664
129732
  // src/intake/store.ts
129665
129733
  import { createHash as createHash70 } from "crypto";
129666
- import { appendFile as appendFile14, mkdir as mkdir61, readFile as readFile128, writeFile as writeFile38 } from "fs/promises";
129734
+ import { appendFile as appendFile14, mkdir as mkdir62, readFile as readFile128, writeFile as writeFile39 } from "fs/promises";
129667
129735
  import path272 from "path";
129668
129736
  async function ensureIntakeDataIgnored(root) {
129669
129737
  const dir = intakeDataDir(root);
129670
- await mkdir61(dir, { recursive: true });
129738
+ await mkdir62(dir, { recursive: true });
129671
129739
  const file = path272.join(dir, ".gitignore");
129672
129740
  try {
129673
129741
  await readFile128(file, "utf8");
129674
129742
  } catch (error) {
129675
129743
  if (!isNotFound(error))
129676
129744
  throw error;
129677
- await writeFile38(file, `# Work intake: the card registry, the ledger and the poll state. Never committed.
129745
+ await writeFile39(file, `# Work intake: the card registry, the ledger and the poll state. Never committed.
129678
129746
  *
129679
129747
  !.gitignore
129680
129748
  `, "utf8");
@@ -130815,7 +130883,7 @@ var init_events = __esm(() => {
130815
130883
 
130816
130884
  // src/intake/poll.ts
130817
130885
  import { randomUUID as randomUUID36 } from "crypto";
130818
- import { mkdir as mkdir62, mkdtemp as mkdtemp6, rm as rm21, writeFile as writeFile39 } from "fs/promises";
130886
+ import { mkdir as mkdir63, mkdtemp as mkdtemp6, rm as rm21, writeFile as writeFile40 } from "fs/promises";
130819
130887
  import path275 from "path";
130820
130888
  function intakePollDue(config, state, now) {
130821
130889
  if (!config.enabled || state.paused || config.repos.length === 0)
@@ -130924,7 +130992,7 @@ async function pollLocked(root, config, deps, runId, now) {
130924
130992
  try {
130925
130993
  if (scratch !== undefined) {
130926
130994
  const cwd = path275.join(scratch, "granted-cwd");
130927
- await mkdir62(cwd, { recursive: true, mode: 448 });
130995
+ await mkdir63(cwd, { recursive: true, mode: 448 });
130928
130996
  const gh = await verifyGh(root, env);
130929
130997
  if (!gh.ok)
130930
130998
  failures.push({ source: "gh", detail: `refusing to run: ${gh.reason}` });
@@ -131113,9 +131181,9 @@ async function writeReport2(root, r) {
131113
131181
  try {
131114
131182
  await ensureIntakeDataIgnored(root);
131115
131183
  const dir = intakeReportsDir(root);
131116
- await mkdir62(dir, { recursive: true });
131184
+ await mkdir63(dir, { recursive: true });
131117
131185
  const file = path275.join(dir, `${r.runId}.md`);
131118
- await writeFile39(file, scrubGrantedOutput(lines.join(`
131186
+ await writeFile40(file, scrubGrantedOutput(lines.join(`
131119
131187
  `), r.env), "utf8");
131120
131188
  await pruneReports(dir, REPORTS_KEEP);
131121
131189
  return path275.relative(root, file);
@@ -132412,7 +132480,7 @@ var init_decisions_surface = __esm(() => {
132412
132480
  });
132413
132481
 
132414
132482
  // src/scheduler/research-sync-job.ts
132415
- import { mkdir as mkdir63, readFile as readFile131, readdir as readdir44, rm as rm22, writeFile as writeFile40 } from "fs/promises";
132483
+ import { mkdir as mkdir64, readFile as readFile131, readdir as readdir44, rm as rm22, writeFile as writeFile41 } from "fs/promises";
132416
132484
  import path276 from "path";
132417
132485
  function researchSyncDataDir(projectRoot) {
132418
132486
  return path276.join(projectRoot, ".metaproject", "data", "research-sync");
@@ -132451,14 +132519,14 @@ async function writeFired2(projectRoot, state) {
132451
132519
  }
132452
132520
  async function ensureIgnored(projectRoot) {
132453
132521
  const dir = researchSyncDataDir(projectRoot);
132454
- await mkdir63(dir, { recursive: true });
132522
+ await mkdir64(dir, { recursive: true });
132455
132523
  const file = path276.join(dir, ".gitignore");
132456
132524
  try {
132457
132525
  await readFile131(file, "utf8");
132458
132526
  } catch (error) {
132459
132527
  if (!isNotFound(error))
132460
132528
  throw error;
132461
- await writeFile40(file, `# Daily research sync: the entry and the day it last ran. Never staged.
132529
+ await writeFile41(file, `# Daily research sync: the entry and the day it last ran. Never staged.
132462
132530
  *
132463
132531
  !.gitignore
132464
132532
  `, "utf8");
@@ -132490,7 +132558,7 @@ async function claimFiles(projectRoot) {
132490
132558
  }
132491
132559
  async function claimDay(projectRoot, day, at) {
132492
132560
  try {
132493
- await writeFile40(researchSyncClaimPath(projectRoot, day), `${JSON.stringify({ pid: process.pid, claimedAt: at.toISOString() })}
132561
+ await writeFile41(researchSyncClaimPath(projectRoot, day), `${JSON.stringify({ pid: process.pid, claimedAt: at.toISOString() })}
132494
132562
  `, { flag: "wx" });
132495
132563
  } catch (error) {
132496
132564
  if (error.code === "EEXIST")
@@ -132688,7 +132756,7 @@ var STATUS_TITLE = "# \u0422\u0435\u043A\u0443\u0449\u0435\u0435 \u0441\u043E\u0
132688
132756
 
132689
132757
  // src/commands/research-sync.ts
132690
132758
  import { execFile as execFile12 } from "child_process";
132691
- import { mkdir as mkdir64, mkdtemp as mkdtemp7, readFile as readFile132, rename as rename12, rm as rm23, writeFile as writeFile41 } from "fs/promises";
132759
+ import { mkdir as mkdir65, mkdtemp as mkdtemp7, readFile as readFile132, rename as rename12, rm as rm23, writeFile as writeFile42 } from "fs/promises";
132692
132760
  import { tmpdir as tmpdir12 } from "os";
132693
132761
  import path277 from "path";
132694
132762
  import { promisify as promisify9 } from "util";
@@ -132723,7 +132791,7 @@ async function runCounts(root) {
132723
132791
  const gitDir = (await tool("git", ["rev-parse", "--absolute-git-dir"], { cwd: root }, "git rev-parse")).trim();
132724
132792
  const archive = path277.join(work, "flows.tar");
132725
132793
  const tree = path277.join(work, "tree");
132726
- await mkdir64(tree);
132794
+ await mkdir65(tree);
132727
132795
  await tool("git", ["archive", "--format=tar", `--output=${archive}`, "HEAD", ".metaproject/flows"], { cwd: root }, "git archive");
132728
132796
  await tool("tar", ["-xf", archive, "-C", tree], { cwd: work }, "tar extract");
132729
132797
  const python = process.platform === "win32" ? "python" : "python3";
@@ -132755,7 +132823,7 @@ function withoutTime(counts) {
132755
132823
  async function writeAtomic(file, content) {
132756
132824
  const temp = `${file}.tmp-${process.pid}`;
132757
132825
  try {
132758
- await writeFile41(temp, content, "utf8");
132826
+ await writeFile42(temp, content, "utf8");
132759
132827
  await rename12(temp, file);
132760
132828
  } catch (error) {
132761
132829
  await rm23(temp, { force: true });
@@ -132808,7 +132876,7 @@ async function recordFailure(root, runAt, reason) {
132808
132876
  await writeIfChanged(file, text);
132809
132877
  }
132810
132878
  async function runResearchSync(input) {
132811
- const deps = { now: () => new Date, runCounts, headHash, writeStaged: (file, content) => writeFile41(file, content, "utf8"), renameStaged: rename12, ...input };
132879
+ const deps = { now: () => new Date, runCounts, headHash, writeStaged: (file, content) => writeFile42(file, content, "utf8"), renameStaged: rename12, ...input };
132812
132880
  const { root } = deps;
132813
132881
  const runAt = deps.now();
132814
132882
  let computed;
@@ -132855,7 +132923,7 @@ async function runResearchSync(input) {
132855
132923
  if (item.previous === null)
132856
132924
  await rm23(item.file, { force: true });
132857
132925
  else
132858
- await writeFile41(item.file, item.previous, "utf8");
132926
+ await writeFile42(item.file, item.previous, "utf8");
132859
132927
  } catch {}
132860
132928
  }
132861
132929
  changed.length = 0;
@@ -146613,7 +146681,7 @@ var init_jev_edit_guard_inspector = __esm(() => {
146613
146681
  // src/review/jev-edit-guard-log.ts
146614
146682
  import { appendFile as appendFile16, readFile as readFile154 } from "fs/promises";
146615
146683
  import path296 from "path";
146616
- import { mkdir as mkdir65 } from "fs/promises";
146684
+ import { mkdir as mkdir66 } from "fs/promises";
146617
146685
  function isEditGuardLogStatus(value) {
146618
146686
  return typeof value === "string" && EDIT_GUARD_LOG_STATUSES.includes(value);
146619
146687
  }
@@ -146667,7 +146735,7 @@ async function readEditGuardLogRecords(cwd) {
146667
146735
  }
146668
146736
  async function appendEditGuardLog(cwd, record) {
146669
146737
  const file = path296.join(cwd, EDIT_GUARD_LOG_PATH);
146670
- await mkdir65(path296.dirname(file), { recursive: true });
146738
+ await mkdir66(path296.dirname(file), { recursive: true });
146671
146739
  await appendFile16(file, `${JSON.stringify(record)}
146672
146740
  `, { mode: 384 });
146673
146741
  }
@@ -155309,7 +155377,7 @@ var init_execution_plan_panel = __esm(() => {
155309
155377
  });
155310
155378
 
155311
155379
  // src/assets/pull.ts
155312
- import { mkdir as mkdir66, writeFile as writeFile42 } from "fs/promises";
155380
+ import { mkdir as mkdir67, writeFile as writeFile43 } from "fs/promises";
155313
155381
  import path301 from "path";
155314
155382
  async function pullAsset(id, lock, options = {}) {
155315
155383
  const entry = lock.assets[id];
@@ -155328,8 +155396,8 @@ async function pullAsset(id, lock, options = {}) {
155328
155396
  }
155329
155397
  const destDir = options.destDir ?? assetCacheDir();
155330
155398
  const destPath = path301.join(destDir, id);
155331
- await mkdir66(destDir, { recursive: true });
155332
- await writeFile42(destPath, bytes);
155399
+ await mkdir67(destDir, { recursive: true });
155400
+ await writeFile43(destPath, bytes);
155333
155401
  return { path: destPath, sha256: actual, verified: true };
155334
155402
  }
155335
155403
  var defaultFetcher = async (url) => {
@@ -156269,7 +156337,7 @@ var init_diff = __esm(() => {
156269
156337
  });
156270
156338
 
156271
156339
  // src/sync/hooks.ts
156272
- import { chmod as chmod11, mkdir as mkdir67, readFile as readFile157, writeFile as writeFile43 } from "fs/promises";
156340
+ import { chmod as chmod11, mkdir as mkdir68, readFile as readFile157, writeFile as writeFile44 } from "fs/promises";
156273
156341
  import path303 from "path";
156274
156342
  function escapeRegExp8(value) {
156275
156343
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -156292,7 +156360,7 @@ async function writeManagedHook(projectRoot, hookName) {
156292
156360
  if (!hooksRoot) {
156293
156361
  return false;
156294
156362
  }
156295
- await mkdir67(hooksRoot, { recursive: true });
156363
+ await mkdir68(hooksRoot, { recursive: true });
156296
156364
  const hookPath = path303.join(hooksRoot, hookName);
156297
156365
  const start = `# keryx:${BLOCK_ID}:begin`;
156298
156366
  const end = `# keryx:${BLOCK_ID}:end`;
@@ -156306,7 +156374,7 @@ ${end}`;
156306
156374
 
156307
156375
  ${block}
156308
156376
  `;
156309
- await writeFile43(hookPath, next, "utf8");
156377
+ await writeFile44(hookPath, next, "utf8");
156310
156378
  await chmod11(hookPath, 493);
156311
156379
  return true;
156312
156380
  }
@@ -156326,7 +156394,7 @@ async function stripManagedHook(projectRoot, hookName) {
156326
156394
  if (!pattern.test(existing)) {
156327
156395
  return false;
156328
156396
  }
156329
- await writeFile43(hookPath, `${existing.replace(pattern, `
156397
+ await writeFile44(hookPath, `${existing.replace(pattern, `
156330
156398
  `).trimEnd()}
156331
156399
  `, "utf8");
156332
156400
  return true;
@@ -157612,7 +157680,7 @@ var init_digest_summary = __esm(() => {
157612
157680
 
157613
157681
  // src/scheduler/digest-run.ts
157614
157682
  import { randomUUID as randomUUID41 } from "crypto";
157615
- import { mkdir as mkdir68, mkdtemp as mkdtemp10, rm as rm26, writeFile as writeFile44 } from "fs/promises";
157683
+ import { mkdir as mkdir69, mkdtemp as mkdtemp10, rm as rm26, writeFile as writeFile45 } from "fs/promises";
157616
157684
  import path305 from "path";
157617
157685
  function scrubDigestReportLocator(locator, env) {
157618
157686
  const scrubbed = scrubGrantedOutput(locator, env);
@@ -157684,7 +157752,7 @@ async function runLocked2(projectRoot, entry, action, deps, runId, now) {
157684
157752
  }
157685
157753
  const scratch = await mkdtemp10(path305.join(parent.dir, `${entry.name.replace(/[^A-Za-z0-9._-]/g, "-")}-`));
157686
157754
  const grantedCwd = path305.join(scratch, "granted-cwd");
157687
- await mkdir68(grantedCwd, { recursive: true, mode: 448 });
157755
+ await mkdir69(grantedCwd, { recursive: true, mode: 448 });
157688
157756
  const limits = startLimits({ maxSeconds: dispatch.maxSeconds, memoryLimitMb: digest.memoryLimitMb }, { ...deps.armTimeout !== undefined ? { armTimeout: deps.armTimeout } : {}, ...deps.limits ?? {} });
157689
157757
  let crashed;
157690
157758
  let collected = { items: [], failures: [], failedSources: new Set, truncatedSources: new Set, raw: [], calls: [], stopped: false };
@@ -157771,9 +157839,9 @@ async function runLocked2(projectRoot, entry, action, deps, runId, now) {
157771
157839
  try {
157772
157840
  await ensureTriggerDataIgnored(projectRoot);
157773
157841
  const dir = path305.join(triggerReportsDir(projectRoot), entry.name);
157774
- await mkdir68(dir, { recursive: true });
157842
+ await mkdir69(dir, { recursive: true });
157775
157843
  const file = path305.join(dir, `${runId}.md`);
157776
- await writeFile44(file, renderDigestReport({
157844
+ await writeFile45(file, renderDigestReport({
157777
157845
  entry,
157778
157846
  runId,
157779
157847
  at: startedAt.toISOString(),
@@ -174232,7 +174300,7 @@ var init_sweep = __esm(() => {
174232
174300
  });
174233
174301
 
174234
174302
  // src/retention/auto-sweep.ts
174235
- import { mkdir as mkdir69, readFile as readFile167, rm as rm27, stat as stat35, writeFile as writeFile45 } from "fs/promises";
174303
+ import { mkdir as mkdir70, readFile as readFile167, rm as rm27, stat as stat35, writeFile as writeFile46 } from "fs/promises";
174236
174304
  import { randomUUID as randomUUID43 } from "crypto";
174237
174305
  import path322 from "path";
174238
174306
  function autoSweepStampPath(cwd) {
@@ -174285,7 +174353,7 @@ async function acquireClaim(claimPath, staleMs) {
174285
174353
  const nonce = `${process.pid}:${randomUUID43()}`;
174286
174354
  for (let attempt = 0;attempt < 2; attempt += 1) {
174287
174355
  try {
174288
- await writeFile45(claimPath, nonce, { flag: "wx", mode: 384 });
174356
+ await writeFile46(claimPath, nonce, { flag: "wx", mode: 384 });
174289
174357
  return {
174290
174358
  release: async () => {
174291
174359
  try {
@@ -174328,7 +174396,7 @@ async function maybeAutoSweepGdctx(cwd, options = {}) {
174328
174396
  }
174329
174397
  const stamp = autoSweepStampPath(cwd);
174330
174398
  try {
174331
- await mkdir69(path322.dirname(stamp), { recursive: true });
174399
+ await mkdir70(path322.dirname(stamp), { recursive: true });
174332
174400
  } catch {
174333
174401
  return { ran: false, reason: "stamp-unwritable" };
174334
174402
  }
@@ -174341,7 +174409,7 @@ async function maybeAutoSweepGdctx(cwd, options = {}) {
174341
174409
  return { ran: false, reason: "throttled" };
174342
174410
  }
174343
174411
  try {
174344
- await writeFile45(stamp, `${JSON.stringify({ startedAtMs: now, completedAtMs: null }, null, 2)}
174412
+ await writeFile46(stamp, `${JSON.stringify({ startedAtMs: now, completedAtMs: null }, null, 2)}
174345
174413
  `, "utf8");
174346
174414
  } catch {
174347
174415
  return { ran: false, reason: "stamp-unwritable" };
@@ -174351,7 +174419,7 @@ async function maybeAutoSweepGdctx(cwd, options = {}) {
174351
174419
  now
174352
174420
  });
174353
174421
  const completedAtMs = options.now ?? Date.now();
174354
- await writeFile45(stamp, `${JSON.stringify({ startedAtMs: now, completedAtMs, lastRunAtMs: completedAtMs }, null, 2)}
174422
+ await writeFile46(stamp, `${JSON.stringify({ startedAtMs: now, completedAtMs, lastRunAtMs: completedAtMs }, null, 2)}
174355
174423
  `, "utf8").catch(() => {
174356
174424
  return;
174357
174425
  });
@@ -174371,7 +174439,7 @@ var init_auto_sweep = __esm(() => {
174371
174439
 
174372
174440
  // src/ctx/artifact-id.ts
174373
174441
  import { randomBytes as randomBytes14 } from "crypto";
174374
- import { mkdir as mkdir70, readFile as readFile168, rm as rm28, writeFile as writeFile46 } from "fs/promises";
174442
+ import { mkdir as mkdir71, readFile as readFile168, rm as rm28, writeFile as writeFile47 } from "fs/promises";
174375
174443
  import path323 from "path";
174376
174444
  function pinnedClock() {
174377
174445
  const pin = process.env.KERYX_CTX_CLOCK_PIN_MS;
@@ -174397,7 +174465,7 @@ function isAlreadyExists(error) {
174397
174465
  }
174398
174466
  async function claim(file) {
174399
174467
  try {
174400
- await writeFile46(file, "", { encoding: "utf8", flag: "wx" });
174468
+ await writeFile47(file, "", { encoding: "utf8", flag: "wx" });
174401
174469
  return true;
174402
174470
  } catch (error) {
174403
174471
  if (isAlreadyExists(error)) {
@@ -174410,8 +174478,8 @@ async function reserveArtifact(options) {
174410
174478
  const now = options.now ?? pinnedClock() ?? (() => new Date);
174411
174479
  const token = options.token ?? defaultToken;
174412
174480
  const maxAttempts = options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS;
174413
- await mkdir70(options.rawDir, { recursive: true });
174414
- await mkdir70(options.artifactsDir, { recursive: true });
174481
+ await mkdir71(options.rawDir, { recursive: true });
174482
+ await mkdir71(options.artifactsDir, { recursive: true });
174415
174483
  for (let attempt = 0;attempt < maxAttempts; attempt += 1) {
174416
174484
  const id = artifactIdCandidate(options.kind, now(), token());
174417
174485
  const rawPath = path323.join(options.rawDir, `${id}.log`);
@@ -174584,7 +174652,7 @@ var init_structured = __esm(() => {
174584
174652
  });
174585
174653
 
174586
174654
  // src/commands/ctx.ts
174587
- import { readFile as readFile169, writeFile as writeFile47 } from "fs/promises";
174655
+ import { readFile as readFile169, writeFile as writeFile48 } from "fs/promises";
174588
174656
  import path324 from "path";
174589
174657
  function structuredBudgetBytes(config) {
174590
174658
  return (config.compactHeadLines + config.compactTailLines) * 40;
@@ -175023,8 +175091,8 @@ async function writeArtifact({
175023
175091
  ${JSON.stringify(artifact, null, 2)}
175024
175092
  \`\`\`
175025
175093
  `;
175026
- await writeFile47(rawPath, raw, "utf8");
175027
- await writeFile47(summaryPath, summaryWithMeta, "utf8");
175094
+ await writeFile48(rawPath, raw, "utf8");
175095
+ await writeFile48(summaryPath, summaryWithMeta, "utf8");
175028
175096
  await writeFileAtomic(latestRawPath, raw);
175029
175097
  await writeFileAtomic(latestSummaryPath, summaryWithMeta);
175030
175098
  await maybeAutoSweepGdctx(projectRoot).catch(() => {
@@ -175920,7 +175988,7 @@ function readLiteral(source, start, quote) {
175920
175988
  }
175921
175989
 
175922
175990
  // src/wiki/freshness/queue.ts
175923
- import { readFile as readFile170, rename as rename13, stat as stat36, unlink as unlink3, writeFile as writeFile48 } from "fs/promises";
175991
+ import { readFile as readFile170, rename as rename13, stat as stat36, unlink as unlink3, writeFile as writeFile49 } from "fs/promises";
175924
175992
  import path325 from "path";
175925
175993
  function queuePath(cwd) {
175926
175994
  return path325.join(cwd, ".metaproject", "data", "wiki", QUEUE_FILE);
@@ -176504,7 +176572,7 @@ var init_report6 = __esm(() => {
176504
176572
  });
176505
176573
 
176506
176574
  // src/wiki/freshness/run.ts
176507
- import { mkdir as mkdir71, readFile as readFile172, writeFile as writeFile49 } from "fs/promises";
176575
+ import { mkdir as mkdir72, readFile as readFile172, writeFile as writeFile50 } from "fs/promises";
176508
176576
  import path326 from "path";
176509
176577
  function freshnessDir(cwd) {
176510
176578
  return path326.join(cwd, ".metaproject", "data", "wiki", "freshness");
@@ -176550,10 +176618,10 @@ async function runFreshness(input) {
176550
176618
  }
176551
176619
  async function persist(cwd, report) {
176552
176620
  const dir = freshnessDir(cwd);
176553
- await mkdir71(dir, { recursive: true });
176554
- await writeFile49(path326.join(dir, "latest.json"), `${JSON.stringify(report, null, 2)}
176621
+ await mkdir72(dir, { recursive: true });
176622
+ await writeFile50(path326.join(dir, "latest.json"), `${JSON.stringify(report, null, 2)}
176555
176623
  `, "utf8");
176556
- await writeFile49(path326.join(dir, "latest.md"), renderMarkdown2(report), "utf8");
176624
+ await writeFile50(path326.join(dir, "latest.md"), renderMarkdown2(report), "utf8");
176557
176625
  }
176558
176626
  async function collectChanges(cwd, git, fromRev) {
176559
176627
  const status = await git(cwd, ["diff", "--name-status", "-M", fromRev, "HEAD"]);
@@ -180546,7 +180614,7 @@ var init_manifest4 = __esm(() => {
180546
180614
  });
180547
180615
 
180548
180616
  // src/gdskills/sync.ts
180549
- import { copyFile as copyFile3, mkdir as mkdir72, readdir as readdir55, writeFile as writeFile50 } from "fs/promises";
180617
+ import { copyFile as copyFile3, mkdir as mkdir73, readdir as readdir55, writeFile as writeFile51 } from "fs/promises";
180550
180618
  import path340 from "path";
180551
180619
  function isGlobalSyncRuntime(runtime) {
180552
180620
  return Object.hasOwn(GLOBAL_SKILL_SYNC_TARGETS, runtime);
@@ -180617,12 +180685,12 @@ async function syncRuntimeSkillsToRoot(projectRoot, options) {
180617
180685
  const manifestPath = path340.join(targetRoot, "keryx-sync-manifest.json");
180618
180686
  const syncedSkills = skillDirs.map((skillDir) => path340.basename(skillDir)).sort();
180619
180687
  if (!options.dryRun) {
180620
- await mkdir72(targetRoot, { recursive: true });
180688
+ await mkdir73(targetRoot, { recursive: true });
180621
180689
  for (const skillDir of skillDirs) {
180622
180690
  const targetSkillDir = path340.join(targetRoot, path340.basename(skillDir));
180623
180691
  await copyDirectory(skillDir, targetSkillDir);
180624
180692
  }
180625
- await writeFile50(manifestPath, `${JSON.stringify({
180693
+ await writeFile51(manifestPath, `${JSON.stringify({
180626
180694
  schemaVersion: 1,
180627
180695
  runtime: options.runtime,
180628
180696
  sourceRoot: toPosix(path340.relative(projectRoot, sourceRoot)),
@@ -180687,7 +180755,7 @@ async function plannedSyncFiles(projectRoot, skillDirs, sourceRoot, targetRoot)
180687
180755
  async function copyDirectory(sourceDir, targetDir) {
180688
180756
  for (const filePath of await listFiles3(sourceDir)) {
180689
180757
  const targetPath = path340.join(targetDir, path340.relative(sourceDir, filePath));
180690
- await mkdir72(path340.dirname(targetPath), { recursive: true });
180758
+ await mkdir73(path340.dirname(targetPath), { recursive: true });
180691
180759
  await copyFile3(filePath, targetPath);
180692
180760
  }
180693
180761
  }
@@ -182830,7 +182898,7 @@ var init_remove_skill = __esm(() => {
182830
182898
  });
182831
182899
 
182832
182900
  // src/gdskills/verify.ts
182833
- import { mkdir as mkdir73, readFile as readFile181, writeFile as writeFile51 } from "fs/promises";
182901
+ import { mkdir as mkdir74, readFile as readFile181, writeFile as writeFile52 } from "fs/promises";
182834
182902
  import path345 from "path";
182835
182903
  async function verifyProjectSkill(projectRoot, options) {
182836
182904
  const metaprojectRoot = path345.join(projectRoot, ".metaproject");
@@ -182876,11 +182944,11 @@ async function verifyProjectSkill(projectRoot, options) {
182876
182944
  dryRun: options.dryRun === true
182877
182945
  };
182878
182946
  if (!options.dryRun) {
182879
- await mkdir73(path345.dirname(reportPath), { recursive: true });
182880
- await writeFile51(reportPath, `${JSON.stringify(report, null, 2)}
182947
+ await mkdir74(path345.dirname(reportPath), { recursive: true });
182948
+ await writeFile52(reportPath, `${JSON.stringify(report, null, 2)}
182881
182949
  `, "utf8");
182882
- await writeFile51(path345.join(resolved.packageRoot, "verification.md"), renderVerificationMarkdown(report), "utf8");
182883
- await writeFile51(skillMdPath, updateLastVerified(skillMd, verifiedAt), "utf8");
182950
+ await writeFile52(path345.join(resolved.packageRoot, "verification.md"), renderVerificationMarkdown(report), "utf8");
182951
+ await writeFile52(skillMdPath, updateLastVerified(skillMd, verifiedAt), "utf8");
182884
182952
  }
182885
182953
  return report;
182886
182954
  }
@@ -185586,7 +185654,7 @@ var init_plans = __esm(() => {
185586
185654
  });
185587
185655
 
185588
185656
  // src/job/store.ts
185589
- import { appendFile as appendFile17, mkdir as mkdir74, readFile as readFile184, readdir as readdir61 } from "fs/promises";
185657
+ import { appendFile as appendFile17, mkdir as mkdir75, readFile as readFile184, readdir as readdir61 } from "fs/promises";
185590
185658
  import path348 from "path";
185591
185659
  function jobsRoot(cwd) {
185592
185660
  return path348.join(cwd, ".metaproject", "jobs");
@@ -185631,7 +185699,7 @@ async function writeJob(cwd, name, state) {
185631
185699
  `));
185632
185700
  }
185633
185701
  const file = jobStatePath(cwd, name);
185634
- await mkdir74(path348.dirname(file), { recursive: true });
185702
+ await mkdir75(path348.dirname(file), { recursive: true });
185635
185703
  await writeFileAtomic(file, `${JSON.stringify(state, null, 2)}
185636
185704
  `);
185637
185705
  }
@@ -185670,7 +185738,7 @@ var init_store10 = __esm(() => {
185670
185738
  });
185671
185739
 
185672
185740
  // src/job/service.ts
185673
- import { copyFile as copyFile4, mkdir as mkdir75 } from "fs/promises";
185741
+ import { copyFile as copyFile4, mkdir as mkdir76 } from "fs/promises";
185674
185742
  import path349 from "path";
185675
185743
  function createJobService(deps) {
185676
185744
  const now = () => deps.now().toISOString();
@@ -185713,7 +185781,7 @@ function createJobService(deps) {
185713
185781
  if (await pathExists(absolute)) {
185714
185782
  throw new Error(`Job package already exists: .metaproject/jobs/${name}. ` + `Run: keryx job status ${name}`);
185715
185783
  }
185716
- await mkdir75(absolute, { recursive: true });
185784
+ await mkdir76(absolute, { recursive: true });
185717
185785
  const createdAt = now();
185718
185786
  const state = {
185719
185787
  phase: "PLAN",
@@ -187383,7 +187451,7 @@ var init_retry_plan = __esm(() => {
187383
187451
 
187384
187452
  // src/commands/review-slice.ts
187385
187453
  import { existsSync as existsSync58, readFileSync as readFileSync28, statSync as statSync20 } from "fs";
187386
- import { mkdir as mkdir76, readFile as readFile188, readdir as readdir63, rm as rm29 } from "fs/promises";
187454
+ import { mkdir as mkdir77, readFile as readFile188, readdir as readdir63, rm as rm29 } from "fs/promises";
187387
187455
  import path351 from "path";
187388
187456
  function wantsHelp2(args) {
187389
187457
  return args.includes("--help") || args.includes("-h");
@@ -187441,7 +187509,7 @@ Attempts are remembered in ${RETRY_STATE_NAME} beside the manifest (--state to m
187441
187509
  `);
187442
187510
  }
187443
187511
  async function writeSliceFiles(dir, slices) {
187444
- await mkdir76(dir, { recursive: true });
187512
+ await mkdir77(dir, { recursive: true });
187445
187513
  for (const slice of slices) {
187446
187514
  await writeFileAtomic(path351.join(dir, slice.path), slice.text);
187447
187515
  }
@@ -187460,7 +187528,7 @@ async function runSlice(args, deps) {
187460
187528
  const outDir = path351.resolve(optionValue(args, "--out") ?? DEFAULT_SLICE_DIR);
187461
187529
  const diff = diffFile !== undefined ? await deps.readDiffSource(diffFile) : await deps.gitDiff(ref, contextLines);
187462
187530
  const { manifest, slices } = buildSlices(sliceSourceFromDiff(diff, { contextLines }), { maxBytes });
187463
- await mkdir76(outDir, { recursive: true });
187531
+ await mkdir77(outDir, { recursive: true });
187464
187532
  for (const entry of await readdir63(outDir)) {
187465
187533
  if (/^slice-\d+\.diff$/.test(entry)) {
187466
187534
  await rm29(path351.join(outDir, entry), { force: true });
@@ -188935,7 +189003,7 @@ var init_floor = __esm(() => {
188935
189003
  });
188936
189004
 
188937
189005
  // src/commands/review.ts
188938
- import { mkdir as mkdir77, readdir as readdir64, readFile as readFile192 } from "fs/promises";
189006
+ import { mkdir as mkdir78, readdir as readdir64, readFile as readFile192 } from "fs/promises";
188939
189007
  import path356, { join as join11 } from "path";
188940
189008
  function reviewerList(args) {
188941
189009
  return (optionValue(args, "--reviewers") ?? "").split(",").map((item) => item.trim()).filter(Boolean);
@@ -190245,7 +190313,7 @@ async function runLearn(args) {
190245
190313
  const sourcePath = learningSourcePath(cwd, config.repo, number);
190246
190314
  const recordPath = learningRecordPath(cwd, config.repo, number);
190247
190315
  const relativeSourcePath = toPosix(path356.relative(cwd, sourcePath));
190248
- await mkdir77(path356.dirname(sourcePath), { recursive: true });
190316
+ await mkdir78(path356.dirname(sourcePath), { recursive: true });
190249
190317
  await writeFileAtomic(sourcePath, renderLearningSource(selection));
190250
190318
  await writeFileAtomic(recordPath, renderLearningRecord({
190251
190319
  repo: config.repo,
@@ -191876,7 +191944,7 @@ function classifyBaselineStatuses(baseline, pr) {
191876
191944
  }
191877
191945
 
191878
191946
  // src/commands/standard.ts
191879
- import { mkdir as mkdir78, writeFile as writeFile52 } from "fs/promises";
191947
+ import { mkdir as mkdir79, writeFile as writeFile53 } from "fs/promises";
191880
191948
  import path359 from "path";
191881
191949
  async function standardCommand(args = [], projectRoot = process.cwd()) {
191882
191950
  const subcommand = args[0];
@@ -191987,8 +192055,8 @@ async function handleEmit(projectRoot, args) {
191987
192055
  if (args.includes("--stdout")) {
191988
192056
  process.stdout.write(result.content);
191989
192057
  } else {
191990
- await mkdir78(path359.dirname(result.path), { recursive: true });
191991
- await writeFile52(result.path, result.content, "utf8");
192058
+ await mkdir79(path359.dirname(result.path), { recursive: true });
192059
+ await writeFile53(result.path, result.content, "utf8");
191992
192060
  heading("keryx standard emit llms");
191993
192061
  if (problems.length === 0) {
191994
192062
  console.log(` ${style.green(symbols.ok)} wrote ${path359.relative(projectRoot, result.path)} (valid llms.txt)`);
@@ -194732,7 +194800,7 @@ var init_security_impact_evidence = __esm(() => {
194732
194800
  });
194733
194801
 
194734
194802
  // src/commands/security.ts
194735
- import { mkdir as mkdir79, readdir as readdir66, readFile as readFile195, writeFile as writeFile53 } from "fs/promises";
194803
+ import { mkdir as mkdir80, readdir as readdir66, readFile as readFile195, writeFile as writeFile54 } from "fs/promises";
194736
194804
  import path363 from "path";
194737
194805
  import { fileURLToPath as fileURLToPath10 } from "url";
194738
194806
  async function securityCommand(args = [], cwd = process.cwd()) {
@@ -195015,8 +195083,8 @@ async function handleScanMcp(cwd, args) {
195015
195083
  const { manifest } = extractManifestAndBaseline(read.value, {});
195016
195084
  const baseline = { schemaVersion: 1, tools: buildMcpBaseline(manifest) };
195017
195085
  const outPath = mcpBaselinePath(cwd);
195018
- await mkdir79(path363.dirname(outPath), { recursive: true });
195019
- await writeFile53(outPath, `${JSON.stringify(baseline, null, 2)}
195086
+ await mkdir80(path363.dirname(outPath), { recursive: true });
195087
+ await writeFile54(outPath, `${JSON.stringify(baseline, null, 2)}
195020
195088
  `, "utf8");
195021
195089
  if (asJson) {
195022
195090
  console.log(JSON.stringify({ pinned: Object.keys(baseline.tools).length, path: outPath }, null, 2));
@@ -195138,7 +195206,7 @@ async function handleRedact(cwd, args) {
195138
195206
  source
195139
195207
  });
195140
195208
  if (out) {
195141
- await writeFile53(out, redacted, "utf8");
195209
+ await writeFile54(out, redacted, "utf8");
195142
195210
  heading("keryx security redact");
195143
195211
  console.log(` ${style.green(symbols.ok)} redacted ${findings.length} span(s) \u2192 ${out}`);
195144
195212
  } else {
@@ -196399,7 +196467,7 @@ var init_metaproject_tools2 = __esm(() => {
196399
196467
  });
196400
196468
 
196401
196469
  // src/mcp/tools.ts
196402
- import { readFile as readFile196, realpath as realpath24, writeFile as writeFile54 } from "fs/promises";
196470
+ import { readFile as readFile196, realpath as realpath24, writeFile as writeFile55 } from "fs/promises";
196403
196471
  import path365 from "path";
196404
196472
  import { randomUUID as randomUUID44 } from "crypto";
196405
196473
  function sacWorkspaceService(cwd) {
@@ -196545,7 +196613,7 @@ function buildToolRegistry() {
196545
196613
  const wrapUp = await wrapUpAuthority.issue({ actor, source: "session", sourceRef: sessionEvidenceRef(workspaceId, session.id) });
196546
196614
  const proposal = await service.create({ request: undefined, requestCorrelationId, workspaceId, id: `proposal-${randomUUID44().replace(/-/g, "").slice(0, 16)}`, proposalRevision, kind, wrapUp });
196547
196615
  if (note)
196548
- await writeFile54(proposalNotePath(cwd, workspaceId, proposal.id), note, "utf8");
196616
+ await writeFile55(proposalNotePath(cwd, workspaceId, proposal.id), note, "utf8");
196549
196617
  return normalizeProposalLifecycleResult(proposal);
196550
196618
  }
196551
196619
  },
@@ -213287,7 +213355,7 @@ var PROBE_TIMEOUT_MS = 1e4;
213287
213355
 
213288
213356
  // src/commands/agents-external.ts
213289
213357
  import { randomUUID as randomUUID56 } from "crypto";
213290
- import { mkdir as mkdir80 } from "fs/promises";
213358
+ import { mkdir as mkdir81 } from "fs/promises";
213291
213359
  import { tmpdir as tmpdir16 } from "os";
213292
213360
  import path386 from "path";
213293
213361
  import { createInterface as createInterface4 } from "readline";
@@ -213560,7 +213628,7 @@ async function runCommand4(args, deps, log) {
213560
213628
  const worktreesDir = path386.join(tmpdir16(), "keryx-external-worktrees");
213561
213629
  const worktree = seams.worktree ?? createGitWorktreePort({ repoRoot: cwd, worktreesDir });
213562
213630
  if (seams.worktree === undefined)
213563
- await mkdir80(worktreesDir, { recursive: true });
213631
+ await mkdir81(worktreesDir, { recursive: true });
213564
213632
  const detect = seams.detect === null ? undefined : seams.detect ?? createVersionProbe();
213565
213633
  const runInput = {
213566
213634
  runtime: {
@@ -215535,7 +215603,7 @@ function factPreservation(rawFacts, compactFacts) {
215535
215603
  }
215536
215604
 
215537
215605
  // src/metrics/oracle-runner.ts
215538
- import { mkdir as mkdir81, writeFile as writeFile55 } from "fs/promises";
215606
+ import { mkdir as mkdir82, writeFile as writeFile56 } from "fs/promises";
215539
215607
  import path388 from "path";
215540
215608
  function oracleTaskId(target) {
215541
215609
  return `metastore:gdgraph-affected:${target}`;
@@ -215858,8 +215926,8 @@ async function persistEvidenceBundle(outDir, bundle, ladder = "metastore") {
215858
215926
  const safeTarget = bundle.target.replace(/[^A-Za-z0-9._/-]/g, "_");
215859
215927
  const safeCase = bundle.caseId.replace(/[^A-Za-z0-9._-]/g, "_");
215860
215928
  const dir = path388.join(outDir, "bench", ladder, safeTarget, safeCase, bundle.variant, String(bundle.seed));
215861
- await mkdir81(dir, { recursive: true });
215862
- const write = (name, value) => writeFile55(path388.join(dir, name), `${JSON.stringify(value, null, 2)}
215929
+ await mkdir82(dir, { recursive: true });
215930
+ const write = (name, value) => writeFile56(path388.join(dir, name), `${JSON.stringify(value, null, 2)}
215863
215931
  `, "utf8");
215864
215932
  await Promise.all([
215865
215933
  write("inputs.json", bundle.inputs),
@@ -216583,7 +216651,7 @@ function formatFwkExplain(result) {
216583
216651
 
216584
216652
  // src/commands/workspace.ts
216585
216653
  import { randomUUID as randomUUID57 } from "crypto";
216586
- import { writeFile as writeFile56 } from "fs/promises";
216654
+ import { writeFile as writeFile57 } from "fs/promises";
216587
216655
  function service8() {
216588
216656
  return new WorkspaceService({
216589
216657
  workspaceRoot: process.cwd(),
@@ -216724,7 +216792,7 @@ async function workspaceCommand(args) {
216724
216792
  const wrapUp = await wrapUpAuthority.issue({ actor, source: "session", sourceRef: sessionEvidenceRef(workspaceId, session.id) });
216725
216793
  const proposal = await service.create({ request: undefined, requestCorrelationId, workspaceId, id: proposalId, proposalRevision, kind, wrapUp });
216726
216794
  if (persistedNote)
216727
- await writeFile56(notePath, persistedNote, "utf8");
216795
+ await writeFile57(notePath, persistedNote, "utf8");
216728
216796
  console.log(JSON.stringify(normalizeProposalLifecycleResult(proposal), null, 2));
216729
216797
  return;
216730
216798
  }
@@ -221044,7 +221112,7 @@ var init_learned_pattern_schema = __esm(() => {
221044
221112
 
221045
221113
  // src/bundle/export.ts
221046
221114
  import { execFileSync as execFileSync3 } from "child_process";
221047
- import { mkdir as mkdir82, readdir as readdir71, readFile as readFile205, writeFile as writeFile57, lstat as lstat24 } from "fs/promises";
221115
+ import { mkdir as mkdir83, readdir as readdir71, readFile as readFile205, writeFile as writeFile58, lstat as lstat24 } from "fs/promises";
221048
221116
  import { existsSync as existsSync62 } from "fs";
221049
221117
  import path395 from "path";
221050
221118
  function globToRegExp5(glob) {
@@ -221279,12 +221347,12 @@ async function outDirIsEmpty(out) {
221279
221347
  }
221280
221348
  }
221281
221349
  async function writeDirectoryBundle(out, manifestBytes, files) {
221282
- await mkdir82(out, { recursive: true });
221283
- await writeFile57(path395.join(out, "bundle.json"), manifestBytes);
221350
+ await mkdir83(out, { recursive: true });
221351
+ await writeFile58(path395.join(out, "bundle.json"), manifestBytes);
221284
221352
  for (const [rel, bytes] of files) {
221285
221353
  const abs = path395.join(out, ...rel.split("/"));
221286
- await mkdir82(path395.dirname(abs), { recursive: true });
221287
- await writeFile57(abs, bytes);
221354
+ await mkdir83(path395.dirname(abs), { recursive: true });
221355
+ await writeFile58(abs, bytes);
221288
221356
  }
221289
221357
  }
221290
221358
  async function exportBundle(opts) {
@@ -221417,8 +221485,8 @@ async function exportBundle(opts) {
221417
221485
  const archive = buildBundleArchive(files, manifestBytes);
221418
221486
  if (!archive.ok)
221419
221487
  return { ok: false, refusals: [archive.refusal] };
221420
- await mkdir82(path395.dirname(opts.out), { recursive: true });
221421
- await writeFile57(opts.out, archive.value);
221488
+ await mkdir83(path395.dirname(opts.out), { recursive: true });
221489
+ await writeFile58(opts.out, archive.value);
221422
221490
  } else {
221423
221491
  if (existsSync62(opts.out) && !await outDirIsEmpty(opts.out)) {
221424
221492
  return { ok: false, refusals: [{ reason: BUNDLE_REFUSAL.notABundle, message: `${opts.out} already exists and is not empty` }] };
@@ -221601,7 +221669,7 @@ var init_applied_state = __esm(() => {
221601
221669
  });
221602
221670
 
221603
221671
  // src/lib/private-dir.ts
221604
- import { lstat as lstat25, mkdir as mkdir83, open as open11, readFile as readFile207 } from "fs/promises";
221672
+ import { lstat as lstat25, mkdir as mkdir84, open as open11, readFile as readFile207 } from "fs/promises";
221605
221673
  import path397 from "path";
221606
221674
  async function checkPrivateDirGitignore(dir, root) {
221607
221675
  if (root !== undefined) {
@@ -221691,7 +221759,7 @@ async function ensurePrivateDirGitignore(dir, root) {
221691
221759
  return check;
221692
221760
  }
221693
221761
  const gitignorePath = path397.join(dir, ".gitignore");
221694
- await mkdir83(dir, { recursive: true });
221762
+ await mkdir84(dir, { recursive: true });
221695
221763
  try {
221696
221764
  const handle = await open11(gitignorePath, "wx");
221697
221765
  try {
@@ -222106,7 +222174,7 @@ var init_plan2 = __esm(() => {
222106
222174
  });
222107
222175
 
222108
222176
  // src/bundle/audit.ts
222109
- import { mkdtemp as mkdtemp11, mkdir as mkdir84, rm as rm30, writeFile as writeFile58 } from "fs/promises";
222177
+ import { mkdtemp as mkdtemp11, mkdir as mkdir85, rm as rm30, writeFile as writeFile59 } from "fs/promises";
222110
222178
  import { tmpdir as tmpdir17 } from "os";
222111
222179
  import path398 from "path";
222112
222180
  function entriesToStage(plan) {
@@ -222123,8 +222191,8 @@ async function auditBundlePlan(plan, opts = {}) {
222123
222191
  try {
222124
222192
  for (const entry of toStage) {
222125
222193
  const abs = path398.join(stagingDir, ...entry.path.split("/"));
222126
- await mkdir84(path398.dirname(abs), { recursive: true });
222127
- await writeFile58(abs, entry.bytes);
222194
+ await mkdir85(path398.dirname(abs), { recursive: true });
222195
+ await writeFile59(abs, entry.bytes);
222128
222196
  }
222129
222197
  } catch (err) {
222130
222198
  return {
@@ -222157,7 +222225,7 @@ var init_audit = __esm(() => {
222157
222225
  });
222158
222226
 
222159
222227
  // src/bundle/apply.ts
222160
- import { mkdir as mkdir85, rmdir as rmdir2, stat as stat42 } from "fs/promises";
222228
+ import { mkdir as mkdir86, rmdir as rmdir2, stat as stat42 } from "fs/promises";
222161
222229
  import path399 from "path";
222162
222230
  function writableEntries(plan) {
222163
222231
  return plan.entries.filter((e) => e.bucket === "new" || e.bucket === "update" || e.bucket === "conflict" && e.forced);
@@ -222326,7 +222394,7 @@ async function mkdirRecordingCreated(dir) {
222326
222394
  }
222327
222395
  }
222328
222396
  if (missing.length > 0) {
222329
- await mkdir85(dir, { recursive: true });
222397
+ await mkdir86(dir, { recursive: true });
222330
222398
  }
222331
222399
  return missing;
222332
222400
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrciphersmith/keryx",
3
- "version": "0.3.89",
3
+ "version": "0.3.90",
4
4
  "description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -85,7 +85,7 @@ A reviewer on this host cannot open a path: its file tool reads inside its worki
85
85
  1. **Slice first.** `keryx review slice --ref <base> --out <dir>` after `keryx review scope`. It writes `slice-NN.diff` files of at most 150000 bytes each and a `manifest.json`; ledgers (csv, lockfiles, snapshots, generated files) are listed under `omissions` with a reason — list them in the report as not reviewed, never as clean.
86
86
  2. **Slices travel by `context_files`.** Write the slices under the project (the default `--out`, `.metaproject/data/review/slices`, already is) and pass each reviewer its slice paths in `spawn_subagent`'s `context_files`: the host copies the text into that reviewer's task, so you never paste or retype a slice. Put the `slices` ids in its reviewer-input. A dispatch that names a git range but no slice and no `context_files` leaves the reviewer with nothing to read — that is `BLOCKED`, not a review.
87
87
  3. **Check before dispatch.** `keryx review dispatch-check --payload <file> --manifest <dir>/manifest.json` for the wave; exit 1 means a payload names no slice, is over the ceiling, or breaks `reviewer-input.schema.json`. Fix it; do not dispatch around it.
88
- 4. **One batch.** Dispatch the whole wave — up to 10 reviewers — in one message, not one at a time. Give each reviewer `max_rounds` of at least 40 (the default); a reviewer that spends its rounds reading the skill and the schema returns nothing. **Incomplete means retry once, then Not run.** On `INCOMPLETE` or `BLOCKED`, `keryx review retry-plan --manifest <dir>/manifest.json --result <result.json>` decides: it cuts the assigned slices in half and names the one retry, and after that prints the `- **Not run:** …` line to paste into the report. An unfinished reviewer is never counted as a clean pass.
88
+ 4. **One batch.** Dispatch the whole wave — up to 10 reviewers — in one message, not one at a time. Give each reviewer `max_rounds` of at least 40 (the default); a reviewer that spends its rounds reading the skill and the schema returns nothing. **Incomplete means retry once, then Not run.** On `INCOMPLETE` or `BLOCKED`, `keryx review retry-plan --manifest <dir>/manifest.json --result <result.json>` decides: it cuts the assigned slices in half and names the one retry, and after that prints the `- **Not run:** …` line to paste into the report. An unfinished reviewer is never counted as a clean pass. **Persist every result.** Pass each reviewer `save_result_to: .metaproject/data/review/raw/<reviewer>.txt`: the host writes its full final text there, so `review complete` has raw evidence even after the transcript is cut. Build `research.json` from those files — the ledger fields from the detail file plus one more key, `dispatch: {version:1, selected:[names], unresolvedRules:[], runs:[{reviewer, executionId:<its sub:… id>, status:"complete", scopeComplete:true, rawEvidence:<saved path>, ruleEvidence:[<rule paths>], executionRequired:false, executionReason:<why>}]}` — and ingest once with `--research`, into the package `review start` created. A refused `review complete` names what is missing: fix that with a command and run it again; it is never `blocked`. `--head` wants a SHA (`git rev-parse HEAD`), not `HEAD`.
89
89
  5. **Never print a scope or a slice.** The tool output of one call is cut at about 20 KB and `keryx review scope` of a large PR prints hundreds of KB. Run it as `keryx review scope --ref <base> --json > .metaproject/data/review/scope.json` and read the counts from the file (`keryx ctx read`, or `jq`); take `<base>` from `git symbolic-ref refs/remotes/origin/HEAD`, not a guessed `origin/main`. Create `.metaproject/data/review/` first (`mkdir -p`) — a missing directory is a command to run, not a blocker. **Machine artifacts by direct write.** Save slices, payloads and results by redirection or `keryx ctx run --raw -- <command>`; the summarized form of `keryx ctx run` drops the bytes a later step parses.
90
90
 
91
91
  ## Control