@kontextmind/kxm 0.7.104 → 0.7.106

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.
@@ -2987,7 +2987,7 @@ var require_compile = __commonJS({
2987
2987
  const schOrFunc = root.refs[ref];
2988
2988
  if (schOrFunc)
2989
2989
  return schOrFunc;
2990
- let _sch = resolve35.call(this, root, ref);
2990
+ let _sch = resolve34.call(this, root, ref);
2991
2991
  if (_sch === void 0) {
2992
2992
  const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
2993
2993
  const { schemaId } = this.opts;
@@ -3014,7 +3014,7 @@ var require_compile = __commonJS({
3014
3014
  function sameSchemaEnv(s1, s2) {
3015
3015
  return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
3016
3016
  }
3017
- function resolve35(root, ref) {
3017
+ function resolve34(root, ref) {
3018
3018
  let sch;
3019
3019
  while (typeof (sch = this.refs[ref]) == "string")
3020
3020
  ref = sch;
@@ -3835,11 +3835,11 @@ var require_fast_uri = __commonJS({
3835
3835
  normalizeString(uri, options);
3836
3836
  } else if (typeof uri === "object") {
3837
3837
  uri = /** @type {T} */
3838
- parse12(serialize(uri, options), options);
3838
+ parse11(serialize(uri, options), options);
3839
3839
  }
3840
3840
  return uri;
3841
3841
  }
3842
- function resolve35(baseURI, relativeURI, options) {
3842
+ function resolve34(baseURI, relativeURI, options) {
3843
3843
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3844
3844
  const {
3845
3845
  parsed: baseParsed,
@@ -3875,8 +3875,8 @@ var require_fast_uri = __commonJS({
3875
3875
  function resolveComponent(base, relative8, options, skipNormalization) {
3876
3876
  const target = {};
3877
3877
  if (!skipNormalization) {
3878
- base = parse12(serialize(base, options), options);
3879
- relative8 = parse12(serialize(relative8, options), options);
3878
+ base = parse11(serialize(base, options), options);
3879
+ relative8 = parse11(serialize(relative8, options), options);
3880
3880
  }
3881
3881
  options = options || {};
3882
3882
  if (!options.tolerant && relative8.scheme) {
@@ -4168,7 +4168,7 @@ var require_fast_uri = __commonJS({
4168
4168
  }
4169
4169
  return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
4170
4170
  }
4171
- function parse12(uri, opts) {
4171
+ function parse11(uri, opts) {
4172
4172
  return parseWithStatus(uri, opts).parsed;
4173
4173
  }
4174
4174
  function normalizeString(uri, opts) {
@@ -4201,11 +4201,11 @@ var require_fast_uri = __commonJS({
4201
4201
  var fastUri = {
4202
4202
  SCHEMES,
4203
4203
  normalize,
4204
- resolve: resolve35,
4204
+ resolve: resolve34,
4205
4205
  resolveComponent,
4206
4206
  equal,
4207
4207
  serialize,
4208
- parse: parse12
4208
+ parse: parse11
4209
4209
  };
4210
4210
  module.exports = fastUri;
4211
4211
  module.exports.default = fastUri;
@@ -14678,7 +14678,7 @@ var require_public_api = __commonJS({
14678
14678
  }
14679
14679
  return doc;
14680
14680
  }
14681
- function parse12(src, reviver, options) {
14681
+ function parse11(src, reviver, options) {
14682
14682
  let _reviver = void 0;
14683
14683
  if (typeof reviver === "function") {
14684
14684
  _reviver = reviver;
@@ -14719,7 +14719,7 @@ var require_public_api = __commonJS({
14719
14719
  return value.toString(options);
14720
14720
  return new Document.Document(value, _replacer, options).toString(options);
14721
14721
  }
14722
- exports.parse = parse12;
14722
+ exports.parse = parse11;
14723
14723
  exports.parseAllDocuments = parseAllDocuments;
14724
14724
  exports.parseDocument = parseDocument2;
14725
14725
  exports.stringify = stringify12;
@@ -14779,7 +14779,7 @@ var require_dist = __commonJS({
14779
14779
  });
14780
14780
 
14781
14781
  // plugins/kxm/src/cli.ts
14782
- import { resolve as resolve34 } from "node:path";
14782
+ import { resolve as resolve33 } from "node:path";
14783
14783
  import { fileURLToPath as fileURLToPath4 } from "node:url";
14784
14784
 
14785
14785
  // node_modules/commander/lib/error.js
@@ -19751,7 +19751,7 @@ var HubClient = class {
19751
19751
  if (signal?.aborted) throw new MeshWaitError("aborted", messageId);
19752
19752
  const message = await this.getMessage(messageId);
19753
19753
  if (["replied", "cancelled", "expired", "error"].includes(message.status)) return message;
19754
- await new Promise((resolve35, reject) => {
19754
+ await new Promise((resolve34, reject) => {
19755
19755
  const onAbort = () => {
19756
19756
  signal?.removeEventListener("abort", onAbort);
19757
19757
  clearTimeout(timer);
@@ -19759,7 +19759,7 @@ var HubClient = class {
19759
19759
  };
19760
19760
  const timer = setTimeout(() => {
19761
19761
  signal?.removeEventListener("abort", onAbort);
19762
- resolve35();
19762
+ resolve34();
19763
19763
  }, Math.min(500, Math.max(1, deadline - Date.now())));
19764
19764
  signal?.addEventListener("abort", onAbort, { once: true });
19765
19765
  if (signal?.aborted) onAbort();
@@ -19814,7 +19814,7 @@ var HubClient = class {
19814
19814
  } catch (error) {
19815
19815
  if (this.stopped || error instanceof Error && error.name === "AbortError") return;
19816
19816
  }
19817
- if (!this.stopped) await new Promise((resolve35) => setTimeout(resolve35, this.options.reconnectMs));
19817
+ if (!this.stopped) await new Promise((resolve34) => setTimeout(resolve34, this.options.reconnectMs));
19818
19818
  }
19819
19819
  }
19820
19820
  headers(includeIdentity = true) {
@@ -21350,7 +21350,7 @@ function defaultSpawn(command, args, options) {
21350
21350
  if (options.signal?.aborted) {
21351
21351
  return Promise.resolve({ stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") });
21352
21352
  }
21353
- return new Promise((resolve35) => {
21353
+ return new Promise((resolve34) => {
21354
21354
  const stdout = [];
21355
21355
  const stderr = [];
21356
21356
  let outputBytes = 0;
@@ -21381,7 +21381,7 @@ function defaultSpawn(command, args, options) {
21381
21381
  const started = Boolean(child?.pid);
21382
21382
  if (started && !observedChildExit) error ??= new Error("process_exit_unobserved");
21383
21383
  const unverifiedDescendants = stopping || started && !observedChildExit;
21384
- resolve35({
21384
+ resolve34({
21385
21385
  stdout: Buffer.concat(stdout).toString("utf8"),
21386
21386
  stderr: Buffer.concat(stderr).toString("utf8"),
21387
21387
  code,
@@ -21790,6 +21790,12 @@ var WRITER_ONESHOT_ARGS = Object.freeze({
21790
21790
  pi: Object.freeze(["-a", "--no-extensions", "--no-skills", "--no-prompt-templates", "--no-session"]),
21791
21791
  grok: Object.freeze(["--always-approve", "--no-subagents", "--disable-web-search"])
21792
21792
  });
21793
+ function oneShotWriterArgs(harness) {
21794
+ return Object.hasOwn(WRITER_ONESHOT_ARGS, harness) ? WRITER_ONESHOT_ARGS[harness] : void 0;
21795
+ }
21796
+ function oneShotPermissionArgs(harness, permission) {
21797
+ return permission === "edit" ? oneShotWriterArgs(harness) : oneShotReadOnlyArgs(harness);
21798
+ }
21793
21799
  var BUILTIN_HARNESSES = Object.freeze([
21794
21800
  {
21795
21801
  id: "pi",
@@ -22167,7 +22173,7 @@ function probeEntry(entry, runCommand, timeoutMs, platform, probe = {}) {
22167
22173
  return {
22168
22174
  id: entry.id,
22169
22175
  label: entry.label,
22170
- default: entry.default,
22176
+ default: entry.id === (probe.defaultHarness ?? DEFAULT_HARNESS),
22171
22177
  mode: entry.mode,
22172
22178
  detected,
22173
22179
  authenticated,
@@ -22188,7 +22194,7 @@ function probeHarnesses(options = {}) {
22188
22194
  const runCommand = options.runCommand ?? defaultRunner(options.env ?? process.env);
22189
22195
  const platform = options.platform ?? process.platform;
22190
22196
  return {
22191
- defaultHarness: DEFAULT_HARNESS,
22197
+ defaultHarness: options.defaultHarness ?? DEFAULT_HARNESS,
22192
22198
  harnesses: BUILTIN_HARNESSES.map((entry) => probeEntry(entry, runCommand, timeoutMs, platform, options))
22193
22199
  };
22194
22200
  }
@@ -22432,7 +22438,7 @@ function formatHarnessInventory(inventory) {
22432
22438
  ].join(" ");
22433
22439
  });
22434
22440
  return [
22435
- `default harness: ${inventory.defaultHarness} (omit agent harness: to use headless Pi)`,
22441
+ `default harness: ${inventory.defaultHarness} (used when an agent omits harness:)`,
22436
22442
  "enable/disable = Git YAML (.kxm/agents, .kxm/models) or the harness's own plugin CLI",
22437
22443
  "governed kxm skills are not auto-updated",
22438
22444
  header,
@@ -23094,8 +23100,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
23094
23100
  const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
23095
23101
  if (maxWriteRepositories > writable) issues.push(issue2("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
23096
23102
  }
23097
- const join51 = objectValue(step.join);
23098
- const minimumPassed = join51 && typeof join51.minimumPassed === "number" ? join51.minimumPassed : void 0;
23103
+ const join52 = objectValue(step.join);
23104
+ const minimumPassed = join52 && typeof join52.minimumPassed === "number" ? join52.minimumPassed : void 0;
23099
23105
  if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue2("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
23100
23106
  const distinctBy = names(assignment?.distinctBy);
23101
23107
  if (distinctBy.length > 0) {
@@ -23240,7 +23246,7 @@ function validateModelReferences(models, issues) {
23240
23246
  };
23241
23247
  for (const id of models.keys()) walk(id, []);
23242
23248
  }
23243
- function validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, projectRoot) {
23249
+ function validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, projectRoot, writerRole) {
23244
23250
  const issues = [];
23245
23251
  const entries = valuesOf(project.value, "repositories").map((candidate) => objectValue(candidate)).filter((candidate) => Boolean(candidate));
23246
23252
  const repositoryIds = /* @__PURE__ */ new Set();
@@ -23311,9 +23317,9 @@ function validateBundle(project, repositories, agents, models, workflows, enviro
23311
23317
  if (projectRoot) {
23312
23318
  const writerRolePath = join6(projectRoot, ".kxm", "roles", "writer.yaml");
23313
23319
  const implementerAgent = agents.get("implementer") ?? agents.get("writer");
23314
- if (existsSync6(writerRolePath) && implementerAgent) {
23320
+ if ((writerRole !== void 0 || existsSync6(writerRolePath)) && implementerAgent) {
23315
23321
  try {
23316
- const rawRole = parseRestrictedYaml2(readFileSync5(writerRolePath, "utf8"));
23322
+ const rawRole = parseRestrictedYaml2(writerRole ?? readFileSync5(writerRolePath, "utf8"));
23317
23323
  const roleObj = objectValue(rawRole);
23318
23324
  const rosterEntries = valuesOf(roleObj ?? {}, "roster").map((candidate) => objectValue(candidate)).filter((entry) => Boolean(entry));
23319
23325
  const enabledRosterModels = rosterEntries.filter((entry) => entry.enabled !== false).map((entry) => stringValue(entry.model)).filter((m2) => Boolean(m2));
@@ -23384,15 +23390,23 @@ function loadKxmProject(projectRoot, options = {}) {
23384
23390
  return loadProjectBundle(projectRoot, options);
23385
23391
  }
23386
23392
  function kxmWorkflowWriteIssues(projectRoot, workflowId, document, options = {}) {
23393
+ return candidateIssues(projectRoot, options, { kind: "workflow", id: workflowId, document });
23394
+ }
23395
+ function kxmRoleWriteIssues(projectRoot, roleId, document, options = {}) {
23396
+ return candidateIssues(projectRoot, options, { kind: "role", id: roleId, document });
23397
+ }
23398
+ function candidateIssues(projectRoot, options, candidate) {
23387
23399
  try {
23388
- loadProjectBundle(projectRoot, options, { id: workflowId, document });
23400
+ loadProjectBundle(projectRoot, options, candidate);
23389
23401
  return [];
23390
23402
  } catch (error) {
23391
23403
  if (error instanceof KxmConfigError) return error.issues;
23392
23404
  throw error;
23393
23405
  }
23394
23406
  }
23395
- function loadProjectBundle(projectRoot, options, workflowCandidate) {
23407
+ function loadProjectBundle(projectRoot, options, candidate) {
23408
+ const workflowCandidate = candidate?.kind === "workflow" ? candidate : void 0;
23409
+ const writerRole = candidate?.kind === "role" && candidate.id.toLocaleLowerCase("en-US") === "writer" ? candidate.document : void 0;
23396
23410
  assertNoRegisteredGates(options);
23397
23411
  const root = resolve3(projectRoot);
23398
23412
  const legacyPresent = legacyConfigFilesAt(root);
@@ -23415,7 +23429,7 @@ function loadProjectBundle(projectRoot, options, workflowCandidate) {
23415
23429
  earlyIssues.push(issue2("semantic", "template_provenance_project_mismatch", ".kxm/template-provenance.yaml", "template provenance belongs to a different project identity"));
23416
23430
  }
23417
23431
  }
23418
- const declaredRepositoryIds = new Set(valuesOf(project.value, "repositories").map((candidate) => stringValue(objectValue(candidate)?.id)).filter((candidate) => candidate !== void 0));
23432
+ const declaredRepositoryIds = new Set(valuesOf(project.value, "repositories").map((candidate2) => stringValue(objectValue(candidate2)?.id)).filter((candidate2) => candidate2 !== void 0));
23419
23433
  for (const repositoryId of Object.keys(options.repositoryBindings ?? {}).sort(compareCodeUnits3)) {
23420
23434
  if (!resourceIdentifier(repositoryId)) {
23421
23435
  earlyIssues.push(issue2("path", "repository_binding_id_invalid", ".kxm/project.yaml", `host-local binding identity ${repositoryId} is invalid`));
@@ -23431,7 +23445,7 @@ function loadProjectBundle(projectRoot, options, workflowCandidate) {
23431
23445
  const { id, document } = workflowCandidate;
23432
23446
  const logicalPath = `.kxm/workflows/${id}.yaml`;
23433
23447
  if (!resourceIdentifier(id)) fail2("path", "resource_id_invalid", logicalPath, `filename-derived identity ${id} is invalid or platform-reserved`);
23434
- const collision = [...workflows.keys()].find((candidate) => candidate.toLocaleLowerCase("en-US") === id.toLocaleLowerCase("en-US"));
23448
+ const collision = [...workflows.keys()].find((candidate2) => candidate2.toLocaleLowerCase("en-US") === id.toLocaleLowerCase("en-US"));
23435
23449
  if (collision) fail2("path", "resource_id_collision", logicalPath, `${id} case-folds to existing ${collision}`);
23436
23450
  workflows.set(id, { kind: "workflow", id, file: join6(root, logicalPath), logicalPath, value: resourceValue(registry, document, logicalPath, "workflow") });
23437
23451
  }
@@ -23450,8 +23464,8 @@ function loadProjectBundle(projectRoot, options, workflowCandidate) {
23450
23464
  }
23451
23465
  }
23452
23466
  const seenBindings = /* @__PURE__ */ new Map();
23453
- for (const candidate of valuesOf(project.value, "repositories")) {
23454
- const entry = objectValue(candidate);
23467
+ for (const candidate2 of valuesOf(project.value, "repositories")) {
23468
+ const entry = objectValue(candidate2);
23455
23469
  const repositoryId = entry && stringValue(entry.id);
23456
23470
  const pathHint = entry && stringValue(entry.pathHint);
23457
23471
  const role = entry && stringValue(entry.role);
@@ -23548,7 +23562,7 @@ function loadProjectBundle(projectRoot, options, workflowCandidate) {
23548
23562
  }
23549
23563
  }
23550
23564
  if (loadIssues.length > 0) throw new KxmConfigError(loadIssues);
23551
- const issues = validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, root);
23565
+ const issues = validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry, root, writerRole);
23552
23566
  if (issues.length > 0) throw new KxmConfigError(issues);
23553
23567
  const resources = [project, ...repositories.values(), ...agents.values(), ...models.values(), ...workflows.values(), ...environments, ...gateRegistry ? [gateRegistry] : []].sort((left, right) => compareCodeUnits3(left.logicalPath, right.logicalPath));
23554
23568
  return {
@@ -23659,9 +23673,9 @@ function planKxmInitialization(start = process.cwd(), options = {}) {
23659
23673
 
23660
23674
  // plugins/kxm/src/runtime-supervisor.ts
23661
23675
  import { spawn as spawn2 } from "node:child_process";
23662
- import { createHash as createHash13, createHmac as createHmac2, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
23663
- import { chmodSync as chmodSync4, existsSync as existsSync16, lstatSync as lstatSync6, mkdirSync as mkdirSync11, readFileSync as readFileSync14, renameSync as renameSync5, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "node:fs";
23664
- import { dirname as dirname11, isAbsolute as isAbsolute7, join as join16 } from "node:path";
23676
+ import { createHash as createHash14, createHmac as createHmac2, randomBytes as randomBytes2, timingSafeEqual as timingSafeEqual2 } from "node:crypto";
23677
+ import { chmodSync as chmodSync4, existsSync as existsSync17, lstatSync as lstatSync6, mkdirSync as mkdirSync11, readFileSync as readFileSync15, renameSync as renameSync5, rmSync as rmSync5, writeFileSync as writeFileSync12 } from "node:fs";
23678
+ import { dirname as dirname11, isAbsolute as isAbsolute7, join as join17 } from "node:path";
23665
23679
 
23666
23680
  // plugins/kxm/src/runtime-store.ts
23667
23681
  import { existsSync as existsSync10, lstatSync as lstatSync4, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync6 } from "node:fs";
@@ -25062,6 +25076,9 @@ var EVENT_STORE_TABLES = {
25062
25076
  };
25063
25077
  var KXM_EVENT_STORE_TABLE_NAMES = Object.keys(EVENT_STORE_TABLES).sort();
25064
25078
 
25079
+ // plugins/kxm/src/engine-plan.ts
25080
+ import { createHash as createHash11 } from "node:crypto";
25081
+
25065
25082
  // plugins/kxm/src/engine-compile.ts
25066
25083
  var KXM_COMPILED_WORKFLOW_SCHEMA = "kxm.compiled-workflow.v1";
25067
25084
  var WORKFLOW_SCHEMA = "kxm.workflow.v1";
@@ -25210,7 +25227,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
25210
25227
  const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
25211
25228
  const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
25212
25229
  const assignments = compileAssignments(step, id, agent, sink);
25213
- const join51 = compileJoin(step, id, sink);
25230
+ const join52 = compileJoin(step, id, sink);
25214
25231
  const requiredEvidence = compileEvidence(step, id, sink);
25215
25232
  const transitions = compileTransitions(step, id, index, stepIndex, sink);
25216
25233
  const outcomes = Object.keys(transitions).sort(compareCodeUnits4);
@@ -25238,7 +25255,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
25238
25255
  transitions: orderedTransitions,
25239
25256
  requiresPlanHash: requirePlanHash.includes(id),
25240
25257
  assignments,
25241
- join: join51
25258
+ join: join52
25242
25259
  };
25243
25260
  if (kind === "agent" || kind === "moa") {
25244
25261
  if (!agent) return void 0;
@@ -25288,15 +25305,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
25288
25305
  };
25289
25306
  }
25290
25307
  function compileJoin(step, stepId, sink) {
25291
- const join51 = objectValue2(step.join);
25292
- if (!join51) return { strategy: "all" };
25293
- const declared = stringValue2(join51.strategy);
25308
+ const join52 = objectValue2(step.join);
25309
+ if (!join52) return { strategy: "all" };
25310
+ const declared = stringValue2(join52.strategy);
25294
25311
  const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
25295
- const minimumPassed = compileOptionalCount(join51.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
25312
+ const minimumPassed = compileOptionalCount(join52.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
25296
25313
  const compiled = {
25297
25314
  strategy,
25298
25315
  ...minimumPassed !== void 0 ? { minimumPassed } : {},
25299
- ...typeof join51.cancelRemaining === "boolean" ? { cancelRemaining: join51.cancelRemaining } : {}
25316
+ ...typeof join52.cancelRemaining === "boolean" ? { cancelRemaining: join52.cancelRemaining } : {}
25300
25317
  };
25301
25318
  return compiled;
25302
25319
  }
@@ -25565,7 +25582,110 @@ function pushIssue(sink, issue3) {
25565
25582
  sink.issues.push({ workflowId: sink.workflowId, ...issue3 });
25566
25583
  }
25567
25584
 
25585
+ // plugins/kxm/src/engine-plan.ts
25586
+ function kxmSha256(input) {
25587
+ return `sha256:${createHash11("sha256").update(input, "utf8").digest("hex")}`;
25588
+ }
25589
+ function parseGateDefinition(raw, gateId, runId) {
25590
+ const value = asObject(raw, runId, `gates.definitions.${gateId}`);
25591
+ const kind = asString(value.kind, runId, `gates.definitions.${gateId}.kind`);
25592
+ if (kind === "command") {
25593
+ assertExactKeys(value, ["kind", "argv", "timeoutMs"], ["cwd"], runId, `gates.definitions.${gateId}`);
25594
+ const argv = asStringArray(value.argv, runId, `gates.definitions.${gateId}.argv`);
25595
+ if (argv.length < 1 || argv.length > 64) {
25596
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.argv length is invalid`);
25597
+ }
25598
+ for (const [index, entry] of argv.entries()) {
25599
+ if (entry.length === 0 || entry.length > 4096 || entry.includes("\0")) {
25600
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.argv[${index}] is invalid`);
25601
+ }
25602
+ }
25603
+ const executable = argv[0];
25604
+ if (executable === "." || executable === ".." || executable.includes("\\") || !executable.startsWith("/") && executable.includes("/")) {
25605
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId} argv[0] must be a bare executable or absolute POSIX path`);
25606
+ }
25607
+ const timeoutMs = value.timeoutMs;
25608
+ if (typeof timeoutMs !== "number" || !Number.isInteger(timeoutMs) || timeoutMs < 1 || timeoutMs > 2147483647) {
25609
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.timeoutMs is invalid`);
25610
+ }
25611
+ if (value.cwd !== void 0 && value.cwd !== "control") {
25612
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.cwd is invalid`);
25613
+ }
25614
+ return {
25615
+ kind: "command",
25616
+ argv,
25617
+ timeoutMs,
25618
+ ...value.cwd === "control" ? { cwd: "control" } : {}
25619
+ };
25620
+ }
25621
+ if (kind === "artifacts-exist") {
25622
+ assertExactKeys(value, ["kind", "paths"], [], runId, `gates.definitions.${gateId}`);
25623
+ const paths = asStringArray(value.paths, runId, `gates.definitions.${gateId}.paths`);
25624
+ if (paths.length < 1 || paths.length > 64) {
25625
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.paths length is invalid`);
25626
+ }
25627
+ if (new Set(paths).size !== paths.length) {
25628
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId}.paths are not unique`);
25629
+ }
25630
+ for (const path4 of paths) {
25631
+ if (path4 === "." || !kxmPortablePath(path4) || path4.startsWith("/") || path4.includes("\\")) {
25632
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId} path is not a portable relative path`);
25633
+ }
25634
+ }
25635
+ return { kind: "artifacts-exist", paths };
25636
+ }
25637
+ if (kind === "reserved") {
25638
+ assertExactKeys(value, ["kind"], [], runId, `gates.definitions.${gateId}`);
25639
+ return { kind: "reserved" };
25640
+ }
25641
+ throw runtimeError("run_plan_corrupt", runId, `gates.definitions.${gateId} kind is invalid`);
25642
+ }
25643
+ function asObject(value, runId, label) {
25644
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
25645
+ throw runtimeError("run_plan_corrupt", runId, `${label} is not an object`);
25646
+ }
25647
+ return value;
25648
+ }
25649
+ function assertExactKeys(value, required, optional, runId, label) {
25650
+ const allowed = /* @__PURE__ */ new Set([...required, ...optional]);
25651
+ for (const key of required) {
25652
+ if (!(key in value)) throw runtimeError("run_plan_corrupt", runId, `${label} is missing ${key}`);
25653
+ }
25654
+ for (const key of Object.keys(value)) {
25655
+ if (!allowed.has(key)) throw runtimeError("run_plan_corrupt", runId, `${label} has unexpected key ${key}`);
25656
+ }
25657
+ }
25658
+ function asString(value, runId, label) {
25659
+ if (typeof value !== "string" || value.length === 0) {
25660
+ throw runtimeError("run_plan_corrupt", runId, `${label} is not a non-empty string`);
25661
+ }
25662
+ return value;
25663
+ }
25664
+ function asStringArray(value, runId, label) {
25665
+ if (!Array.isArray(value) || value.some((entry) => typeof entry !== "string")) {
25666
+ throw runtimeError("run_plan_corrupt", runId, `${label} is not a string array`);
25667
+ }
25668
+ return [...value];
25669
+ }
25670
+
25671
+ // plugins/kxm/src/gate-hash.ts
25672
+ function gateRegistryHash(registryValue) {
25673
+ return kxmSha256(kxmCanonicalJson(registryValue));
25674
+ }
25675
+
25568
25676
  // plugins/kxm/src/runtime-service.ts
25677
+ function objectValue3(value) {
25678
+ return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
25679
+ }
25680
+ function kxmProjectAdmissionLimits(bundle) {
25681
+ const limits = objectValue3(bundle.project.value.limits);
25682
+ const maxConcurrentRuns = typeof limits?.maxConcurrentRuns === "number" && Number.isInteger(limits.maxConcurrentRuns) && limits.maxConcurrentRuns >= 1 ? limits.maxConcurrentRuns : 1;
25683
+ return {
25684
+ maxConcurrentRuns,
25685
+ ...typeof limits?.maxRunDurationMs === "number" ? { maxRunDurationMs: limits.maxRunDurationMs } : {},
25686
+ ...typeof limits?.maxAgentTimeMs === "number" ? { maxAgentTimeMs: limits.maxAgentTimeMs } : {}
25687
+ };
25688
+ }
25569
25689
  var RUNTIME_EPOCH_NS = process.hrtime.bigint();
25570
25690
 
25571
25691
  // plugins/kxm/src/oneshot-evidence.ts
@@ -25575,7 +25695,7 @@ var RECORD_LIMIT = 16 * 1024 * 1024;
25575
25695
 
25576
25696
  // plugins/kxm/src/prices.ts
25577
25697
  var import_yaml3 = __toESM(require_dist(), 1);
25578
- import { createHash as createHash11 } from "node:crypto";
25698
+ import { createHash as createHash12 } from "node:crypto";
25579
25699
  import { existsSync as existsSync11, readFileSync as readFileSync9, statSync, writeFileSync as writeFileSync7 } from "node:fs";
25580
25700
  import { join as join11 } from "node:path";
25581
25701
 
@@ -25638,7 +25758,7 @@ function hashPriceCatalog(catalog) {
25638
25758
  }))
25639
25759
  })).sort((a, b2) => a.id.localeCompare(b2.id))
25640
25760
  };
25641
- return createHash11("sha256").update(JSON.stringify(canonical2), "utf8").digest("hex");
25761
+ return createHash12("sha256").update(JSON.stringify(canonical2), "utf8").digest("hex");
25642
25762
  }
25643
25763
  function parsePriceCatalog(text) {
25644
25764
  const parsed = (0, import_yaml3.parse)(text);
@@ -25776,6 +25896,8 @@ function acknowledgePriceCatalog(projectRoot, now = /* @__PURE__ */ new Date())
25776
25896
 
25777
25897
  // plugins/kxm/src/engine.ts
25778
25898
  var import_yaml7 = __toESM(require_dist(), 1);
25899
+ import { existsSync as existsSync15, readFileSync as readFileSync13 } from "node:fs";
25900
+ import { join as join15 } from "node:path";
25779
25901
 
25780
25902
  // plugins/kxm/src/routes.ts
25781
25903
  var import_yaml4 = __toESM(require_dist(), 1);
@@ -25835,6 +25957,10 @@ function listRoleBindings(root) {
25835
25957
  }
25836
25958
  return result;
25837
25959
  }
25960
+ function isRouteAdmitted(root, modelId2) {
25961
+ const policy = loadRoutePolicy(root);
25962
+ return policy.admitted.includes(modelId2) && !policy.disabled.includes(modelId2);
25963
+ }
25838
25964
  function listInventoryModels(root) {
25839
25965
  const path4 = join12(root, ".kxm", "models", "inventory.yaml");
25840
25966
  if (!existsSync12(path4)) return [];
@@ -26120,7 +26246,7 @@ function syncHarnessMemory(repoRoot, options = {}) {
26120
26246
 
26121
26247
  // plugins/kxm/src/skills.ts
26122
26248
  var import_yaml6 = __toESM(require_dist(), 1);
26123
- import { createHash as createHash12 } from "node:crypto";
26249
+ import { createHash as createHash13 } from "node:crypto";
26124
26250
  import { existsSync as existsSync14, mkdirSync as mkdirSync9, readdirSync as readdirSync5, readFileSync as readFileSync12, renameSync as renameSync3, rmSync as rmSync3, statSync as statSync2, writeFileSync as writeFileSync10 } from "node:fs";
26125
26251
  import { dirname as dirname9, join as join14 } from "node:path";
26126
26252
  var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
@@ -26145,7 +26271,7 @@ var SkillLifecycleError = class extends Error {
26145
26271
  }
26146
26272
  };
26147
26273
  function skillContentSha256(content) {
26148
- return createHash12("sha256").update(content, "utf8").digest("hex");
26274
+ return createHash13("sha256").update(content, "utf8").digest("hex");
26149
26275
  }
26150
26276
  function skillIdFor(name, contentSha256) {
26151
26277
  const slug = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
@@ -27159,13 +27285,418 @@ function formatRoutingReport(report2, options = {}) {
27159
27285
  }
27160
27286
 
27161
27287
  // plugins/kxm/src/engine.ts
27288
+ function resolveProducerRoute(projectRoot, step, agentId) {
27289
+ let agentModel;
27290
+ const agentFile = join15(projectRoot, ".kxm", "agents", `${agentId}.yaml`);
27291
+ if (existsSync15(agentFile)) {
27292
+ try {
27293
+ const parsed = (0, import_yaml7.parse)(readFileSync13(agentFile, "utf8"));
27294
+ if (typeof parsed?.model === "string") {
27295
+ agentModel = parsed.model;
27296
+ } else if (parsed?.model && typeof parsed.model === "object" && !Array.isArray(parsed.model)) {
27297
+ const declared = parsed.model;
27298
+ const providerOk = typeof declared.provider === "string" && declared.provider.length > 0 && !declared.provider.includes("/");
27299
+ const modelOk = typeof declared.model === "string" && declared.model.length > 0 && !declared.model.startsWith("/") && !declared.model.endsWith("/");
27300
+ if (providerOk && modelOk) {
27301
+ agentModel = `${declared.provider}/${declared.model}`;
27302
+ } else {
27303
+ return {
27304
+ error: {
27305
+ reason: "step_unsupported",
27306
+ field: "model",
27307
+ detail: "producer_route_unsupported: invalid model declaration"
27308
+ }
27309
+ };
27310
+ }
27311
+ }
27312
+ } catch {
27313
+ }
27314
+ }
27315
+ let selector = typeof step.model === "string" ? step.model : agentModel;
27316
+ if (!selector) {
27317
+ if (agentId === "implementer") {
27318
+ selector = "xai/grok-4.6";
27319
+ }
27320
+ }
27321
+ if (!selector) {
27322
+ return {
27323
+ error: {
27324
+ reason: "step_unsupported",
27325
+ field: "model",
27326
+ detail: "producer_route_unsupported: agent or step has no model declared"
27327
+ }
27328
+ };
27329
+ }
27330
+ const slash = selector.indexOf("/");
27331
+ if (slash <= 0 || slash === selector.length - 1) {
27332
+ return {
27333
+ error: {
27334
+ reason: "step_unsupported",
27335
+ field: "model",
27336
+ detail: `producer_route_unsupported: invalid selector '${selector}'`
27337
+ }
27338
+ };
27339
+ }
27340
+ if (!isRouteAdmitted(projectRoot, selector)) {
27341
+ return {
27342
+ error: {
27343
+ reason: "step_unsupported",
27344
+ field: "model",
27345
+ detail: `producer_route_unsupported: model '${selector}' is not admitted`
27346
+ }
27347
+ };
27348
+ }
27349
+ const role = agentId === "implementer" ? "writer" : agentId;
27350
+ const roleBindings = listRoleBindings(projectRoot);
27351
+ const roster = roleBindings[role];
27352
+ if (roster && !roster.includes(selector)) {
27353
+ return {
27354
+ error: {
27355
+ reason: "step_unsupported",
27356
+ field: "model",
27357
+ detail: `producer_route_unsupported: model '${selector}' not in role '${role}' roster`
27358
+ }
27359
+ };
27360
+ }
27361
+ const provider = selector.slice(0, slash);
27362
+ const model = selector.slice(slash + 1);
27363
+ return { provider, model, selector };
27364
+ }
27162
27365
  var ENGINE_ROUTING_METADATA_KEYS = /* @__PURE__ */ new Set(["workflowId", "askSha256", "objectiveSha256", "stepWrites"]);
27163
27366
  var MAX_PRODUCER_ROUTING_METADATA_FIELDS = MAX_PROVIDER_METADATA_FIELDS - ENGINE_ROUTING_METADATA_KEYS.size;
27367
+ function kxmLiveRunPrerequisites(bundle, workflowId, projectRoot) {
27368
+ const workflow = bundle.workflows.get(workflowId);
27369
+ if (!workflow) throw runtimeError("run_workflow_unknown", workflowId, `workflow ${workflowId} does not exist in this project`);
27370
+ const plan = compileKxmWorkflow({ id: workflowId, value: workflow.value, logicalPath: workflow.logicalPath });
27371
+ const envelope = { plan, projectLimits: kxmProjectAdmissionLimits(bundle), gates: pinnedGatesForCompiledPlan(plan, bundle, projectRoot) };
27372
+ const prerequisites = [];
27373
+ const limit = unsupportedLimit(envelope);
27374
+ if (limit) prerequisites.push(limit);
27375
+ const visited = /* @__PURE__ */ new Set();
27376
+ const pending = [plan.entryStepId];
27377
+ while (pending.length > 0) {
27378
+ const stepId = pending.pop();
27379
+ if (visited.has(stepId)) continue;
27380
+ visited.add(stepId);
27381
+ const step = plan.steps[stepId];
27382
+ for (const transition2 of Object.values(step.transitions)) {
27383
+ if (transition2.to === "step") pending.push(transition2.target);
27384
+ }
27385
+ const unsupported = step.kind === "gate" ? unsupportedGateStep(plan, step, envelope, { projectRoot }) ?? starterGatePrerequisite(step, envelope.gates, projectRoot) : unsupportedStep(plan, step, "oneshot");
27386
+ if (unsupported) {
27387
+ prerequisites.push({ ...unsupported, stepId });
27388
+ continue;
27389
+ }
27390
+ if (step.kind !== "agent" && step.kind !== "moa") continue;
27391
+ const agentIds = step.assignments.allowedAgents.length > 0 ? step.assignments.allowedAgents : [step.agent];
27392
+ for (const agentId of agentIds) {
27393
+ const agent = bundle.agents.get(agentId);
27394
+ const harness = String(agent?.value.harness ?? bundle.project.value.defaultHarness ?? "pi");
27395
+ const permission = Object.values(step.repositories).includes("write") ? "edit" : "read-only";
27396
+ if (!BUILTIN_HARNESSES.some((entry) => entry.id === harness && entry.oneShot) || !oneShotPermissionArgs(harness, permission)) {
27397
+ prerequisites.push({ reason: "step_unsupported", stepId, field: "harness", detail: `${agentId}: ${harness} has no audited ${permission} one-shot profile; use a supported workflow or execute this work directly in ${harness}, without substituting another harness` });
27398
+ continue;
27399
+ }
27400
+ const route = resolveProducerRoute(projectRoot, step, agentId);
27401
+ if ("error" in route) {
27402
+ prerequisites.push({ ...route.error, stepId, detail: `${agentId}: ${route.error.detail}; configure its model in .kxm/agents/${agentId}.yaml and admit the installed model with kxm routes admit --model <provider/model>` });
27403
+ continue;
27404
+ }
27405
+ const writeRefusal = unsupportedLiveWrite(projectRoot, step, agentId, route.selector, envelope.projectLimits.maxConcurrentRuns);
27406
+ if (writeRefusal) {
27407
+ prerequisites.push({ ...writeRefusal, stepId });
27408
+ continue;
27409
+ }
27410
+ const compatible = validateHarnessModelPair(harness, route);
27411
+ if (!compatible.valid) {
27412
+ prerequisites.push({ reason: "step_unsupported", stepId, field: "model", detail: `${agentId}: ${compatible.message ?? `${harness} cannot run ${route.selector}`}; configure a model supported by ${harness} in .kxm/agents/${agentId}.yaml` });
27413
+ }
27414
+ }
27415
+ }
27416
+ return prerequisites;
27417
+ }
27418
+ function starterGatePrerequisite(step, gates, projectRoot) {
27419
+ const definition = gates.definitions[step.gate];
27420
+ if (!definition || definition.kind !== "command" || definition.argv.length !== 2 || definition.argv[0] !== "npm" || definition.argv[1] !== "test") return void 0;
27421
+ let testScript;
27422
+ try {
27423
+ const manifest = JSON.parse(readFileSync13(join15(projectRoot, "package.json"), "utf8"));
27424
+ if (manifest && typeof manifest === "object" && "scripts" in manifest) {
27425
+ const scripts = manifest.scripts;
27426
+ if (scripts && typeof scripts === "object" && "test" in scripts) testScript = scripts.test;
27427
+ }
27428
+ } catch {
27429
+ }
27430
+ if (typeof testScript !== "string" || testScript.trim().length === 0) {
27431
+ return { reason: "gate_unsupported", field: `gates.${step.gate}.argv`, detail: `gate ${step.gate} runs npm test but this repository has no readable package.json with scripts.test; configure .kxm/gates.yaml gates.${step.gate}.argv for the repository's actual test runner before driving this workflow` };
27432
+ }
27433
+ return void 0;
27434
+ }
27435
+ function unsupportedLimit(envelope) {
27436
+ if (envelope.plan.limits.maxAgentTimeMs !== void 0) {
27437
+ return { reason: "limit_unsupported", field: "limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
27438
+ }
27439
+ if (envelope.projectLimits.maxAgentTimeMs !== void 0) {
27440
+ return { reason: "limit_unsupported", field: "project.limits.maxAgentTimeMs", detail: "agent-time budget enforcement is not available in this slice" };
27441
+ }
27442
+ return void 0;
27443
+ }
27444
+ function unsupportedStep(plan, step, producerId) {
27445
+ if (step.kind === "gate") throw runtimeError("run_plan_corrupt", plan.workflowId, `unsupportedStep called on gate without envelope; use unsupportedGateStep instead`);
27446
+ if (step.kind !== "agent" && step.kind !== "moa" && step.kind !== "approval" && step.kind !== "wait") {
27447
+ return { reason: "step_unsupported", field: "kind", detail: `step kind ${step.kind} is not executed in this slice` };
27448
+ }
27449
+ if (step.assignments.maxAttemptsPerAssignment > 2) {
27450
+ return { reason: "step_unsupported", field: "assignments.maxAttemptsPerAssignment", detail: "only up to two physical attempts are supported in this slice" };
27451
+ }
27452
+ if (step.join.strategy !== "all" && step.join.strategy !== "all-settled") {
27453
+ return { reason: "step_unsupported", field: "join.strategy", detail: "only join all or all-settled is supported" };
27454
+ }
27455
+ if (step.join.strategy === "all" && step.join.minimumPassed !== void 0) {
27456
+ return { reason: "step_unsupported", field: "join.minimumPassed", detail: "minimumPassed requires join strategy all-settled" };
27457
+ }
27458
+ if (step.join.cancelRemaining) {
27459
+ return { reason: "step_unsupported", field: "join.cancelRemaining", detail: "cancelRemaining is not executed in this slice" };
27460
+ }
27461
+ if (step.assignments.distinctBy.some((d2) => d2 !== "provider")) {
27462
+ return { reason: "step_unsupported", field: "assignments.distinctBy", detail: "only distinctBy provider is supported in this slice" };
27463
+ }
27464
+ if (step.assignments.maxWriteRepositories !== void 0 && step.assignments.maxWriteRepositories > 1) {
27465
+ return { reason: "step_unsupported", field: "assignments.maxWriteRepositories", detail: "at most one write repository is supported in this slice" };
27466
+ }
27467
+ for (const evidence of step.requiredEvidence) {
27468
+ if (!evidence || !["artifact", "assignment-result", "gate", "approval", "receipt"].includes(evidence.kind)) {
27469
+ return { reason: "step_unsupported", field: "requiredEvidence", detail: `evidence kind '${evidence?.kind}' is not supported in this slice` };
27470
+ }
27471
+ }
27472
+ if (step.timeoutMs !== void 0 && step.timeoutMs <= 0) {
27473
+ return { reason: "step_unsupported", field: "timeoutMs", detail: "timeoutMs must be positive" };
27474
+ }
27475
+ if (step.safeSpeculation) return { reason: "step_unsupported", field: "safeSpeculation", detail: "speculation is not executed in this slice" };
27476
+ if (step.tools) return { reason: "step_unsupported", field: "tools", detail: "tools are not executed in this slice" };
27477
+ if (step.secrets.length > 0) return { reason: "step_unsupported", field: "secrets", detail: "secrets are not executed in this slice" };
27478
+ for (const [repoId, access] of Object.entries(step.repositories)) {
27479
+ if (access !== "read" && access !== "write" && access !== "none") {
27480
+ return { reason: "step_unsupported", field: "repositories", detail: `invalid repository access '${access}' on ${repoId}` };
27481
+ }
27482
+ }
27483
+ return void 0;
27484
+ }
27485
+ function readYamlRecord(path4) {
27486
+ if (!existsSync15(path4)) return void 0;
27487
+ try {
27488
+ const parsed = (0, import_yaml7.parse)(readFileSync13(path4, "utf8"));
27489
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
27490
+ } catch {
27491
+ return void 0;
27492
+ }
27493
+ return void 0;
27494
+ }
27495
+ function agentHarness(projectRoot, agentId) {
27496
+ const harness = readYamlRecord(join15(projectRoot, ".kxm", "agents", `${agentId}.yaml`))?.harness;
27497
+ return typeof harness === "string" && harness.length > 0 ? harness : void 0;
27498
+ }
27499
+ function projectDefaultHarness(projectRoot) {
27500
+ const harness = readYamlRecord(join15(projectRoot, ".kxm", "project.yaml"))?.defaultHarness;
27501
+ return typeof harness === "string" && harness.length > 0 ? harness : "pi";
27502
+ }
27503
+ function unsupportedLiveWrite(projectRoot, step, agentId, selector, maxConcurrentRuns) {
27504
+ if (!Object.values(step.repositories).some((access) => access === "write")) return void 0;
27505
+ if (step.assignments.maximum !== 1) {
27506
+ return {
27507
+ reason: "step_unsupported",
27508
+ field: "assignments.maximum",
27509
+ detail: "live write steps run a single assignment; the checkout witness cannot attribute edits between writers"
27510
+ };
27511
+ }
27512
+ if (maxConcurrentRuns !== 1) {
27513
+ return {
27514
+ reason: "step_unsupported",
27515
+ field: "limits.maxConcurrentRuns",
27516
+ detail: "live write steps require limits.maxConcurrentRuns of 1; concurrent runs share one checkout"
27517
+ };
27518
+ }
27519
+ const harness = agentHarness(projectRoot, agentId) ?? projectDefaultHarness(projectRoot);
27520
+ if (!oneShotWriterArgs(harness)) {
27521
+ return {
27522
+ reason: "step_unsupported",
27523
+ field: "repositories",
27524
+ detail: `live write steps require an audited writer profile; ${harness} has none`
27525
+ };
27526
+ }
27527
+ const rosterPath = join15(projectRoot, ".kxm", "roster.yaml");
27528
+ if (!existsSync15(rosterPath)) return void 0;
27529
+ const roster = readYamlRecord(rosterPath);
27530
+ if (!roster || roster.schema !== "kxm.developer-roster.v1") {
27531
+ return { reason: "step_unsupported", field: "model", detail: "live write steps require a readable kxm.developer-roster.v1" };
27532
+ }
27533
+ const routes = roster.routes;
27534
+ const lineup = roster.lineup;
27535
+ const writerIds = lineup && typeof lineup === "object" && !Array.isArray(lineup) ? lineup.writer : void 0;
27536
+ if (!routes || typeof routes !== "object" || Array.isArray(routes) || !Array.isArray(writerIds)) {
27537
+ return { reason: "step_unsupported", field: "model", detail: "developer roster has no writer lineup" };
27538
+ }
27539
+ const allowed = writerIds.some((id) => {
27540
+ if (typeof id !== "string") return false;
27541
+ const route = routes[id];
27542
+ if (!route || typeof route !== "object" || Array.isArray(route)) return false;
27543
+ const record = route;
27544
+ if (record.harness !== harness || record.status !== "admitted") return false;
27545
+ if (!Array.isArray(record.permissions) || !record.permissions.includes("edit")) return false;
27546
+ const model = typeof record.model === "string" ? record.model : "";
27547
+ const vendor = typeof record.vendor === "string" ? record.vendor : "";
27548
+ return model === selector || vendor.length > 0 && `${vendor}/${model}` === selector;
27549
+ });
27550
+ if (!allowed) {
27551
+ return {
27552
+ reason: "step_unsupported",
27553
+ field: "model",
27554
+ detail: `live write route ${selector} on ${harness} is not on the developer roster writer lineup`
27555
+ };
27556
+ }
27557
+ return void 0;
27558
+ }
27559
+ function unsupportedGateStep(plan, step, envelope, context) {
27560
+ if (!step.gate) return { reason: "step_unsupported", field: "gate", detail: "gate step is missing gate id" };
27561
+ if (envelope.gates.registry === null) {
27562
+ return { reason: "step_unsupported", field: "gate", detail: `step ${step.id} refers to a gate but registry is null` };
27563
+ }
27564
+ const definition = envelope.gates.definitions[step.gate];
27565
+ if (!definition) {
27566
+ return { reason: "step_unsupported", field: "gate", detail: `gate definition ${step.gate} not found in registry` };
27567
+ }
27568
+ if (definition.kind === "reserved") {
27569
+ return { reason: "gate_unsupported", field: "gate", detail: `gate ${step.gate} is reserved and cannot be executed` };
27570
+ }
27571
+ if (step.assignments.allowedAgents && step.assignments.allowedAgents.length > 0) {
27572
+ return { reason: "step_unsupported", field: "assignments.allowedAgents", detail: "gate steps do not support allowedAgents" };
27573
+ }
27574
+ if (step.assignments.minimum !== 1 || step.assignments.target !== 1 || step.assignments.maximum !== 1) {
27575
+ return { reason: "step_unsupported", field: "assignments", detail: "gate steps require exactly 1 agent assignment" };
27576
+ }
27577
+ if (step.assignments.maxParallel !== 1) {
27578
+ return { reason: "step_unsupported", field: "assignments.maxParallel", detail: "gate steps require maxParallel of 1" };
27579
+ }
27580
+ if (step.assignments.maxAttemptsPerAssignment !== 1) {
27581
+ return { reason: "step_unsupported", field: "assignments.maxAttemptsPerAssignment", detail: "gate steps require maxAttemptsPerAssignment of 1" };
27582
+ }
27583
+ if (step.assignments.distinctBy && step.assignments.distinctBy.length > 0) {
27584
+ return { reason: "step_unsupported", field: "assignments.distinctBy", detail: "gate steps do not support distinctBy" };
27585
+ }
27586
+ if (step.assignments.maxWriteRepositories) {
27587
+ return { reason: "step_unsupported", field: "assignments.maxWriteRepositories", detail: "gate steps do not support maxWriteRepositories" };
27588
+ }
27589
+ if (step.join.strategy !== "all") {
27590
+ return { reason: "step_unsupported", field: "join", detail: `gate steps require join strategy 'all', not '${step.join.strategy}'` };
27591
+ }
27592
+ if (step.join.minimumPassed !== void 0) {
27593
+ return { reason: "step_unsupported", field: "join", detail: "gate steps do not support minimumPassed" };
27594
+ }
27595
+ if (step.join.cancelRemaining) {
27596
+ return { reason: "step_unsupported", field: "join", detail: "gate steps do not support cancelRemaining" };
27597
+ }
27598
+ for (const evidence of step.requiredEvidence) {
27599
+ if (!evidence || !["gate", "artifact", "receipt", "assignment-result", "approval"].includes(evidence.kind)) {
27600
+ return { reason: "step_unsupported", field: "requiredEvidence", detail: `gate steps do not support evidence kind '${evidence?.kind}'` };
27601
+ }
27602
+ }
27603
+ if (step.safeSpeculation) {
27604
+ return { reason: "step_unsupported", field: "safeSpeculation", detail: "gate steps do not support safeSpeculation" };
27605
+ }
27606
+ if (step.tools) {
27607
+ return { reason: "step_unsupported", field: "tools", detail: "gate steps do not support tools" };
27608
+ }
27609
+ if (step.secrets && step.secrets.length > 0) {
27610
+ return { reason: "step_unsupported", field: "secrets", detail: "gate steps do not support secrets" };
27611
+ }
27612
+ if (step.model) {
27613
+ return { reason: "step_unsupported", field: "model", detail: "gate steps do not support model" };
27614
+ }
27615
+ if (plan.reproOracle?.stageId === step.id) {
27616
+ return { reason: "step_unsupported", field: "reproOracle", detail: "gate steps cannot be repro oracle" };
27617
+ }
27618
+ if (plan.planHash?.stageId === step.id) {
27619
+ return { reason: "step_unsupported", field: "planHash", detail: "gate steps cannot be plan hash oracle" };
27620
+ }
27621
+ if (step.timeoutMs !== void 0) {
27622
+ if (definition.kind === "artifacts-exist") {
27623
+ return { reason: "step_unsupported", field: "timeoutMs", detail: "artifacts-exist gates do not support timeoutMs" };
27624
+ }
27625
+ if (definition.kind === "command" && definition.timeoutMs !== void 0 && step.timeoutMs < definition.timeoutMs) {
27626
+ return { reason: "step_unsupported", field: "timeoutMs", detail: "command step timeoutMs narrower than the registry timeout is refused" };
27627
+ }
27628
+ }
27629
+ const repositories = step.repositories;
27630
+ if (!repositories || Object.keys(repositories).length === 0) {
27631
+ return { reason: "step_unsupported", field: "repositories", detail: "gate steps require repository declarations" };
27632
+ }
27633
+ if (definition.kind === "artifacts-exist") {
27634
+ const controlAccess = repositories.control;
27635
+ if (controlAccess !== "read" && controlAccess !== "write") {
27636
+ return { reason: "step_unsupported", field: "repositories", detail: "artifacts-exist gates require control repository access as read or write" };
27637
+ }
27638
+ for (const [repoId, access] of Object.entries(repositories)) {
27639
+ if (repoId !== "control" && access !== "none") {
27640
+ return { reason: "step_unsupported", field: "repositories", detail: "artifacts-exist gates require other repositories to have access 'none'" };
27641
+ }
27642
+ }
27643
+ } else if (definition.kind === "command") {
27644
+ for (const [repoId, access] of Object.entries(repositories)) {
27645
+ if (access !== "read" && access !== "write" && access !== "none") {
27646
+ return { reason: "step_unsupported", field: "repositories", detail: `invalid repository access '${access}' on ${repoId}` };
27647
+ }
27648
+ }
27649
+ const hasWrite = Object.values(repositories).some((access) => access === "write");
27650
+ if (!hasWrite) {
27651
+ return { reason: "step_unsupported", field: "repositories", detail: "command gates require at least one repository with write access" };
27652
+ }
27653
+ }
27654
+ const expectPass = step.expect === "pass";
27655
+ if (expectPass) {
27656
+ if (!step.outcomes.includes("passed")) {
27657
+ return { reason: "gate_outcome_undeclared", field: "outcomes", detail: "step expects pass but missing required outcome passed; spell it as 'passed'" };
27658
+ }
27659
+ if (!step.outcomes.includes("implementation-failure") && !step.outcomes.includes("failed")) {
27660
+ return { reason: "gate_outcome_undeclared", field: "outcomes", detail: "step expects pass but missing required failure outcome; declare implementation-failure or failed" };
27661
+ }
27662
+ } else {
27663
+ if (!step.outcomes.includes("repro-missing")) {
27664
+ return { reason: "gate_outcome_undeclared", field: "outcomes", detail: "step expects fail but missing required outcome repro-missing; spell it as 'repro-missing'" };
27665
+ }
27666
+ if (!step.outcomes.includes("implementation-failure") && !step.outcomes.includes("failed")) {
27667
+ return { reason: "gate_outcome_undeclared", field: "outcomes", detail: "step expects fail but missing required failure outcome; declare implementation-failure or failed" };
27668
+ }
27669
+ }
27670
+ if (context && projectRuntimeKey(context.projectRoot) !== envelope.gates.controlRoot.projectKey) {
27671
+ throw runtimeError("run_owner_mismatch", plan.workflowId, `gate control root does not match project`);
27672
+ }
27673
+ if (definition.kind === "artifacts-exist" && step.expect === "fail") {
27674
+ return { reason: "step_unsupported", field: "expect", detail: "artifacts-exist gates cannot expect fail" };
27675
+ }
27676
+ return void 0;
27677
+ }
27678
+ function pinnedGatesForCompiledPlan(plan, bundle, projectRoot) {
27679
+ const referenced = /* @__PURE__ */ new Set();
27680
+ for (const step of Object.values(plan.steps)) {
27681
+ if (step.kind === "gate") referenced.add(step.gate);
27682
+ }
27683
+ const definitions = /* @__PURE__ */ Object.create(null);
27684
+ const registryValue = bundle.gateRegistry?.value;
27685
+ const gatesObject = registryValue && typeof registryValue.gates === "object" && registryValue.gates && !Array.isArray(registryValue.gates) ? registryValue.gates : {};
27686
+ for (const gateId of referenced) {
27687
+ definitions[gateId] = parseGateDefinition(gatesObject[gateId], gateId, plan.workflowId);
27688
+ }
27689
+ return {
27690
+ registry: referenced.size > 0 && bundle.gateRegistry ? { schema: "kxm.gate-registry.v1", hash: gateRegistryHash(bundle.gateRegistry.value) } : bundle.gateRegistry && referenced.size === 0 ? { schema: "kxm.gate-registry.v1", hash: gateRegistryHash(bundle.gateRegistry.value) } : null,
27691
+ definitions,
27692
+ controlRoot: { repositoryId: "control", projectKey: projectRuntimeKey(projectRoot) }
27693
+ };
27694
+ }
27164
27695
 
27165
27696
  // plugins/kxm/src/hub-binding.ts
27166
- import { existsSync as existsSync15, mkdirSync as mkdirSync10, readFileSync as readFileSync13, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "node:fs";
27697
+ import { existsSync as existsSync16, mkdirSync as mkdirSync10, readFileSync as readFileSync14, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync11 } from "node:fs";
27167
27698
  import { homedir as homedir4 } from "node:os";
27168
- import { dirname as dirname10, isAbsolute as isAbsolute6, join as join15, resolve as resolve11 } from "node:path";
27699
+ import { dirname as dirname10, isAbsolute as isAbsolute6, join as join16, resolve as resolve11 } from "node:path";
27169
27700
  var HUB_BINDING_SCHEMA = "kxm.hub-binding.v1";
27170
27701
  var HUB_HEALTH_PROBE_MS = 300;
27171
27702
  var HubBindingError = class extends Error {
@@ -27182,16 +27713,16 @@ function resolveUserStateRoot2(env) {
27182
27713
  }
27183
27714
  if (process.platform === "win32") {
27184
27715
  const localAppData = env.LOCALAPPDATA?.trim();
27185
- const base2 = localAppData && isAbsolute6(localAppData) ? localAppData : join15(homedir4(), "AppData", "Local");
27716
+ const base2 = localAppData && isAbsolute6(localAppData) ? localAppData : join16(homedir4(), "AppData", "Local");
27186
27717
  return resolve11(base2, "KXM");
27187
27718
  }
27188
27719
  if (process.platform === "darwin") return resolve11(homedir4(), "Library", "Application Support", "KXM");
27189
27720
  const xdgState = env.XDG_STATE_HOME?.trim();
27190
- const base = xdgState && isAbsolute6(xdgState) ? xdgState : join15(homedir4(), ".local", "state");
27721
+ const base = xdgState && isAbsolute6(xdgState) ? xdgState : join16(homedir4(), ".local", "state");
27191
27722
  return resolve11(base, "kxm");
27192
27723
  }
27193
27724
  function hubBindingFile(env = process.env) {
27194
- return join15(resolveUserStateRoot2(env), "hub-binding.json");
27725
+ return join16(resolveUserStateRoot2(env), "hub-binding.json");
27195
27726
  }
27196
27727
  function validateHubUrl(raw) {
27197
27728
  let parsed;
@@ -27227,10 +27758,10 @@ function isAbortError(error) {
27227
27758
  }
27228
27759
  function readHubBinding(env = process.env) {
27229
27760
  const file = hubBindingFile(env);
27230
- if (!existsSync15(file)) return void 0;
27761
+ if (!existsSync16(file)) return void 0;
27231
27762
  let parsed;
27232
27763
  try {
27233
- parsed = JSON.parse(readFileSync13(file, "utf8"));
27764
+ parsed = JSON.parse(readFileSync14(file, "utf8"));
27234
27765
  } catch {
27235
27766
  throw new HubBindingError(`malformed hub binding at ${file}`);
27236
27767
  }
@@ -27253,7 +27784,7 @@ function readHubBinding(env = process.env) {
27253
27784
  function writeHubBinding(record, env = process.env) {
27254
27785
  const file = hubBindingFile(env);
27255
27786
  mkdirSync10(dirname10(file), { recursive: true, mode: 448 });
27256
- const temporary = join15(dirname10(file), `.hub-binding-${process.pid}.tmp`);
27787
+ const temporary = join16(dirname10(file), `.hub-binding-${process.pid}.tmp`);
27257
27788
  try {
27258
27789
  writeFileSync11(temporary, `${JSON.stringify(record, null, 2)}
27259
27790
  `, { encoding: "utf8", mode: 384 });
@@ -27302,7 +27833,7 @@ var DEFAULT_LOG_MAX_BYTES = 10 * 1024 * 1024;
27302
27833
 
27303
27834
  // plugins/kxm/src/runtime-supervisor.ts
27304
27835
  function kxmSupervisorTokenFile(paths) {
27305
- return join16(paths.runtimeDir, "supervisor.token");
27836
+ return join17(paths.runtimeDir, "supervisor.token");
27306
27837
  }
27307
27838
  function readKxmSupervisorToken(paths) {
27308
27839
  const file = kxmSupervisorTokenFile(paths);
@@ -27311,7 +27842,7 @@ function readKxmSupervisorToken(paths) {
27311
27842
  if (stat.isSymbolicLink() || !stat.isFile()) {
27312
27843
  throw runtimeError("runtime_path_invalid", file, "supervisor token file must be a regular file, not a link");
27313
27844
  }
27314
- const token = readFileSync14(file, "utf8").trim();
27845
+ const token = readFileSync15(file, "utf8").trim();
27315
27846
  return token.length >= 32 ? token : void 0;
27316
27847
  }
27317
27848
  function processAlive(pid) {
@@ -27325,11 +27856,11 @@ function processAlive(pid) {
27325
27856
  var HEARTBEAT_STALE_MS = 15e3;
27326
27857
  var SUPERVISOR_ERROR_MAX_AGE_MS = 3e4;
27327
27858
  function supervisorErrorFile(paths) {
27328
- return join16(paths.runtimeDir, "supervisor.error");
27859
+ return join17(paths.runtimeDir, "supervisor.error");
27329
27860
  }
27330
27861
  function clearSupervisorError(paths) {
27331
27862
  const file = supervisorErrorFile(paths);
27332
- if (existsSync16(file)) rmSync5(file, { force: true });
27863
+ if (existsSync17(file)) rmSync5(file, { force: true });
27333
27864
  }
27334
27865
  function readRecentSupervisorError(paths) {
27335
27866
  const file = supervisorErrorFile(paths);
@@ -27338,7 +27869,7 @@ function readRecentSupervisorError(paths) {
27338
27869
  const ageMs = Date.now() - stat.mtimeMs;
27339
27870
  if (ageMs > SUPERVISOR_ERROR_MAX_AGE_MS) return void 0;
27340
27871
  try {
27341
- return readFileSync14(file, "utf8").trim();
27872
+ return readFileSync15(file, "utf8").trim();
27342
27873
  } catch {
27343
27874
  return void 0;
27344
27875
  }
@@ -27359,7 +27890,7 @@ function supervisorStatusOf(record) {
27359
27890
  };
27360
27891
  }
27361
27892
  function kxmSupervisorStatus(paths, options = {}) {
27362
- if (!existsSync16(paths.registryDb)) return { running: false };
27893
+ if (!existsSync17(paths.registryDb)) return { running: false };
27363
27894
  if (options.readOnly) {
27364
27895
  const database = openReadOnlyDatabase(paths.registryDb);
27365
27896
  try {
@@ -27425,7 +27956,7 @@ async function ensureKxmSupervisor(options = {}) {
27425
27956
  throw runtimeError("runtime_supervisor_unreachable", paths.registryDb, `runtime supervisor pid ${status.pid} is registered as running but cannot be probed`);
27426
27957
  }
27427
27958
  clearSupervisorError(paths);
27428
- const scriptPath = join16(findKxmRepoRoot(import.meta.url), "scripts", "kxm-runtime-supervisor.mjs");
27959
+ const scriptPath = join17(findKxmRepoRoot(import.meta.url), "scripts", "kxm-runtime-supervisor.mjs");
27429
27960
  const spawnImpl = options.spawnImpl ?? ((script, env) => {
27430
27961
  const child = spawn2(process.execPath, [script], {
27431
27962
  detached: true,
@@ -27489,11 +28020,11 @@ function handoffSuffix(handoff) {
27489
28020
 
27490
28021
  // plugins/kxm/src/cli/types.ts
27491
28022
  import { spawn as spawn3 } from "node:child_process";
27492
- import { join as join18, resolve as resolve12 } from "node:path";
28023
+ import { join as join19, resolve as resolve12 } from "node:path";
27493
28024
 
27494
28025
  // plugins/kxm/src/telemetry.ts
27495
- import { appendFileSync, mkdirSync as mkdirSync12, readFileSync as readFileSync15 } from "node:fs";
27496
- import { dirname as dirname12, join as join17 } from "node:path";
28026
+ import { appendFileSync, mkdirSync as mkdirSync12, readFileSync as readFileSync16 } from "node:fs";
28027
+ import { dirname as dirname12, join as join18 } from "node:path";
27497
28028
  var TELEMETRY_SCHEMA = "kxm.telemetry.v1";
27498
28029
  function inferImprovementTarget(input) {
27499
28030
  const explicit = input.env?.KXM_IMPROVE_TARGET?.trim();
@@ -27508,7 +28039,7 @@ function appendTelemetry(path4, event) {
27508
28039
  `, { encoding: "utf8", mode: 384 });
27509
28040
  }
27510
28041
  function telemetryPath(logsDir) {
27511
- return join17(logsDir, "telemetry.jsonl");
28042
+ return join18(logsDir, "telemetry.jsonl");
27512
28043
  }
27513
28044
  function makeTelemetryEvent(input) {
27514
28045
  return {
@@ -27523,7 +28054,7 @@ function makeTelemetryEvent(input) {
27523
28054
  function readRoutingRecords(path4) {
27524
28055
  const records = [];
27525
28056
  try {
27526
- const raw = readFileSync15(path4, "utf8");
28057
+ const raw = readFileSync16(path4, "utf8");
27527
28058
  for (const line of raw.split(/\r?\n/)) {
27528
28059
  if (!line.trim()) continue;
27529
28060
  try {
@@ -27602,7 +28133,7 @@ function workerResult(worker, payload) {
27602
28133
  // plugins/kxm/src/cli/types.ts
27603
28134
  function spawnScript(scriptName, extraEnv = {}) {
27604
28135
  return new Promise((resolveExit) => {
27605
- const child = spawn3(process.execPath, [join18(findKxmRepoRoot(import.meta.url), "scripts", scriptName)], {
28136
+ const child = spawn3(process.execPath, [join19(findKxmRepoRoot(import.meta.url), "scripts", scriptName)], {
27606
28137
  stdio: "inherit",
27607
28138
  env: { ...process.env, ...extraEnv }
27608
28139
  });
@@ -27703,10 +28234,10 @@ function workspaceDirs(cwd, workspaceFlag, env) {
27703
28234
  return {
27704
28235
  workdir,
27705
28236
  workspace,
27706
- config: derive ? join18(workspace, "config") : resolve12(workdir, env.KXM_CONFIG_DIR?.trim() || join18(workspace, "config")),
27707
- logs: derive ? join18(workspace, "logs") : resolve12(workdir, env.KXM_LOGS_DIR?.trim() || join18(workspace, "logs")),
27708
- assets: derive ? join18(workspace, "assets") : resolve12(workdir, env.KXM_ASSETS_DIR?.trim() || join18(workspace, "assets")),
27709
- state: derive ? join18(workspace, "state") : resolve12(workdir, env.KXM_STATE_DIR?.trim() || join18(workspace, "state"))
28237
+ config: derive ? join19(workspace, "config") : resolve12(workdir, env.KXM_CONFIG_DIR?.trim() || join19(workspace, "config")),
28238
+ logs: derive ? join19(workspace, "logs") : resolve12(workdir, env.KXM_LOGS_DIR?.trim() || join19(workspace, "logs")),
28239
+ assets: derive ? join19(workspace, "assets") : resolve12(workdir, env.KXM_ASSETS_DIR?.trim() || join19(workspace, "assets")),
28240
+ state: derive ? join19(workspace, "state") : resolve12(workdir, env.KXM_STATE_DIR?.trim() || join19(workspace, "state"))
27710
28241
  };
27711
28242
  }
27712
28243
  function maskEnvName(name) {
@@ -27774,20 +28305,20 @@ function workspaceEnv(runtime) {
27774
28305
  // plugins/kxm/src/cli/roles.ts
27775
28306
  var import_yaml10 = __toESM(require_dist(), 1);
27776
28307
  import { randomUUID as randomUUID5 } from "node:crypto";
27777
- import { existsSync as existsSync19, readFileSync as readFileSync18 } from "node:fs";
27778
- import { join as join21, resolve as resolve15 } from "node:path";
28308
+ import { existsSync as existsSync20, readFileSync as readFileSync19 } from "node:fs";
28309
+ import { join as join22, resolve as resolve15 } from "node:path";
27779
28310
  import { createInterface } from "node:readline";
27780
28311
 
27781
28312
  // plugins/kxm/src/role.ts
27782
28313
  var import_yaml9 = __toESM(require_dist(), 1);
27783
- import { existsSync as existsSync18, mkdirSync as mkdirSync14, readdirSync as readdirSync6, readFileSync as readFileSync17, rmSync as rmSync6, writeFileSync as writeFileSync14 } from "node:fs";
27784
- import { join as join20 } from "node:path";
28314
+ import { existsSync as existsSync19, mkdirSync as mkdirSync14, readdirSync as readdirSync6, readFileSync as readFileSync18, rmSync as rmSync6, writeFileSync as writeFileSync14 } from "node:fs";
28315
+ import { join as join21 } from "node:path";
27785
28316
 
27786
28317
  // plugins/kxm/src/config.ts
27787
28318
  var import_yaml8 = __toESM(require_dist(), 1);
27788
- import { existsSync as existsSync17, mkdirSync as mkdirSync13, readFileSync as readFileSync16, writeFileSync as writeFileSync13 } from "node:fs";
28319
+ import { existsSync as existsSync18, mkdirSync as mkdirSync13, readFileSync as readFileSync17, writeFileSync as writeFileSync13 } from "node:fs";
27789
28320
  import { homedir as homedir5 } from "node:os";
27790
- import { dirname as dirname13, join as join19, resolve as resolve13 } from "node:path";
28321
+ import { dirname as dirname13, join as join20, resolve as resolve13 } from "node:path";
27791
28322
  var KXM_CONFIG_SCHEMA = "kxm.config.v1";
27792
28323
  var IMPROVEMENT_PROMOTION_POLICIES = ["manual_pr", "critic_quorum", "auto_threshold"];
27793
28324
  var DEFAULT_KXM_CONFIG = {
@@ -27842,7 +28373,7 @@ var DEFAULT_KXM_CONFIG = {
27842
28373
  };
27843
28374
  function userConfigDirectory(overrideDir) {
27844
28375
  if (overrideDir) return resolve13(overrideDir);
27845
- return resolve13(process.env.KXM_USER_CONFIG_DIR?.trim() || join19(homedir5(), ".config", "kxm"));
28376
+ return resolve13(process.env.KXM_USER_CONFIG_DIR?.trim() || join20(homedir5(), ".config", "kxm"));
27846
28377
  }
27847
28378
  function repoConfigDirectory(repoRoot) {
27848
28379
  return resolve13(repoRoot, ".kxm");
@@ -27889,14 +28420,14 @@ function normalizeImprovementConfig(raw) {
27889
28420
  }
27890
28421
  function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
27891
28422
  const userDir = userConfigDirectory(options.userConfigDir);
27892
- const userConfigFile = join19(userDir, "config.yaml");
28423
+ const userConfigFile = join20(userDir, "config.yaml");
27893
28424
  const repoDir = repoConfigDirectory(repoRoot);
27894
- const repoConfigFile = join19(repoDir, "config.yaml");
28425
+ const repoConfigFile = join20(repoDir, "config.yaml");
27895
28426
  let userRaw = {};
27896
28427
  let userLoadedPath;
27897
- if (existsSync17(userConfigFile)) {
28428
+ if (existsSync18(userConfigFile)) {
27898
28429
  try {
27899
- const text = readFileSync16(userConfigFile, "utf8");
28430
+ const text = readFileSync17(userConfigFile, "utf8");
27900
28431
  userRaw = (0, import_yaml8.parse)(text) ?? {};
27901
28432
  userLoadedPath = userConfigFile;
27902
28433
  } catch (error) {
@@ -27905,9 +28436,9 @@ function loadKxmConfig(repoRoot = process.cwd(), options = {}) {
27905
28436
  }
27906
28437
  let repoRaw = {};
27907
28438
  let repoLoadedPath;
27908
- if (existsSync17(repoConfigFile)) {
28439
+ if (existsSync18(repoConfigFile)) {
27909
28440
  try {
27910
- const text = readFileSync16(repoConfigFile, "utf8");
28441
+ const text = readFileSync17(repoConfigFile, "utf8");
27911
28442
  repoRaw = (0, import_yaml8.parse)(text) ?? {};
27912
28443
  repoLoadedPath = repoConfigFile;
27913
28444
  } catch (error) {
@@ -27943,7 +28474,7 @@ function getKxmConfigValue(config, keyPath) {
27943
28474
  return current;
27944
28475
  }
27945
28476
  function kxmConfigFileForScope(repoRoot, scope, userConfigDir) {
27946
- return scope === "user" ? join19(userConfigDirectory(userConfigDir), "config.yaml") : join19(repoConfigDirectory(repoRoot), "config.yaml");
28477
+ return scope === "user" ? join20(userConfigDirectory(userConfigDir), "config.yaml") : join20(repoConfigDirectory(repoRoot), "config.yaml");
27947
28478
  }
27948
28479
  function configKeyParts(keyPath) {
27949
28480
  const parts = keyPath.split(".");
@@ -27954,9 +28485,9 @@ function configKeyParts(keyPath) {
27954
28485
  }
27955
28486
  function readConfigFile(targetFile2) {
27956
28487
  let existing = {};
27957
- if (existsSync17(targetFile2)) {
28488
+ if (existsSync18(targetFile2)) {
27958
28489
  try {
27959
- existing = (0, import_yaml8.parse)(readFileSync16(targetFile2, "utf8")) ?? {};
28490
+ existing = (0, import_yaml8.parse)(readFileSync17(targetFile2, "utf8")) ?? {};
27960
28491
  } catch {
27961
28492
  existing = {};
27962
28493
  }
@@ -28074,21 +28605,21 @@ var DEFAULT_ROLES = {
28074
28605
  };
28075
28606
  function rolesDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
28076
28607
  if (scope === "global") {
28077
- return join20(userConfigDirectory(userConfigDir), "roles");
28608
+ return join21(userConfigDirectory(userConfigDir), "roles");
28078
28609
  }
28079
- return join20(repoConfigDirectory(repoRoot), "roles");
28610
+ return join21(repoConfigDirectory(repoRoot), "roles");
28080
28611
  }
28081
28612
  function ensureRolesDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
28082
28613
  const dir = rolesDirectory(scope, repoRoot, userConfigDir);
28083
- if (!existsSync18(dir)) {
28614
+ if (!existsSync19(dir)) {
28084
28615
  mkdirSync14(dir, { recursive: true });
28085
28616
  }
28086
28617
  return dir;
28087
28618
  }
28088
28619
  function parseRoleFile(filePath) {
28089
- if (!existsSync18(filePath)) return void 0;
28620
+ if (!existsSync19(filePath)) return void 0;
28090
28621
  try {
28091
- const raw = readFileSync17(filePath, "utf8");
28622
+ const raw = readFileSync18(filePath, "utf8");
28092
28623
  const parsed = (0, import_yaml9.parse)(raw);
28093
28624
  if (!parsed || typeof parsed !== "object" || parsed.schema !== KXM_ROLE_SCHEMA) {
28094
28625
  return void 0;
@@ -28115,10 +28646,10 @@ function listRoles(options = {}) {
28115
28646
  const globalDir = rolesDirectory("global", repoRoot, options.userConfigDir);
28116
28647
  const localDir = rolesDirectory("local", repoRoot, options.userConfigDir);
28117
28648
  const localRoles = /* @__PURE__ */ new Map();
28118
- if (scopeFilter !== "global" && existsSync18(localDir)) {
28649
+ if (scopeFilter !== "global" && existsSync19(localDir)) {
28119
28650
  for (const entry of readdirSync6(localDir)) {
28120
28651
  if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
28121
- const filePath = join20(localDir, entry);
28652
+ const filePath = join21(localDir, entry);
28122
28653
  const parsed = parseRoleFile(filePath);
28123
28654
  if (parsed) {
28124
28655
  localRoles.set(parsed.id, { role: parsed, filePath });
@@ -28127,10 +28658,10 @@ function listRoles(options = {}) {
28127
28658
  }
28128
28659
  }
28129
28660
  const globalRoles = /* @__PURE__ */ new Map();
28130
- if (scopeFilter !== "local" && existsSync18(globalDir)) {
28661
+ if (scopeFilter !== "local" && existsSync19(globalDir)) {
28131
28662
  for (const entry of readdirSync6(globalDir)) {
28132
28663
  if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
28133
- const filePath = join20(globalDir, entry);
28664
+ const filePath = join21(globalDir, entry);
28134
28665
  const parsed = parseRoleFile(filePath);
28135
28666
  if (parsed) {
28136
28667
  globalRoles.set(parsed.id, { role: parsed, filePath });
@@ -28178,13 +28709,13 @@ function getRole(roleId, options = {}) {
28178
28709
  const repoRoot = options.repoRoot ?? process.cwd();
28179
28710
  if (scope !== "global") {
28180
28711
  const localDir = rolesDirectory("local", repoRoot, options.userConfigDir);
28181
- const localFile = join20(localDir, `${roleId}.yaml`);
28712
+ const localFile = join21(localDir, `${roleId}.yaml`);
28182
28713
  const parsed = parseRoleFile(localFile);
28183
28714
  if (parsed) return { role: parsed, scope: "local", filePath: localFile };
28184
28715
  }
28185
28716
  if (scope !== "local") {
28186
28717
  const globalDir = rolesDirectory("global", repoRoot, options.userConfigDir);
28187
- const globalFile = join20(globalDir, `${roleId}.yaml`);
28718
+ const globalFile = join21(globalDir, `${roleId}.yaml`);
28188
28719
  const parsed = parseRoleFile(globalFile);
28189
28720
  if (parsed) return { role: parsed, scope: "global", filePath: globalFile };
28190
28721
  }
@@ -28194,8 +28725,8 @@ function addRole(role, options = {}) {
28194
28725
  const scope = options.scope ?? "local";
28195
28726
  const repoRoot = options.repoRoot ?? process.cwd();
28196
28727
  const dir = options.dryRun ? rolesDirectory(scope, repoRoot, options.userConfigDir) : ensureRolesDirectory(scope, repoRoot, options.userConfigDir);
28197
- const filePath = join20(dir, `${role.id}.yaml`);
28198
- if (existsSync18(filePath) && !options.overwrite) {
28728
+ const filePath = join21(dir, `${role.id}.yaml`);
28729
+ if (existsSync19(filePath) && !options.overwrite) {
28199
28730
  throw new Error(`role_already_exists: role '${role.id}' already exists at ${filePath}`);
28200
28731
  }
28201
28732
  if (!options.dryRun) writeFileSync14(filePath, (0, import_yaml9.stringify)(role), "utf8");
@@ -28205,8 +28736,8 @@ function removeRole(roleId, options = {}) {
28205
28736
  const scope = options.scope ?? "local";
28206
28737
  const repoRoot = options.repoRoot ?? process.cwd();
28207
28738
  const dir = rolesDirectory(scope, repoRoot, options.userConfigDir);
28208
- const filePath = join20(dir, `${roleId}.yaml`);
28209
- if (!existsSync18(filePath)) {
28739
+ const filePath = join21(dir, `${roleId}.yaml`);
28740
+ if (!existsSync19(filePath)) {
28210
28741
  throw new Error(`role_not_found: role '${roleId}' not found in ${scope} directory (${filePath})`);
28211
28742
  }
28212
28743
  if (options.dryRun) return { id: roleId, removed: false, filePath, scope };
@@ -28228,7 +28759,7 @@ function modifyRole(roleId, updates, options = {}) {
28228
28759
  const scope = options.scope ?? target.scope;
28229
28760
  const repoRoot = options.repoRoot ?? process.cwd();
28230
28761
  const dir = options.dryRun ? rolesDirectory(scope, repoRoot, options.userConfigDir) : ensureRolesDirectory(scope, repoRoot, options.userConfigDir);
28231
- const filePath = join20(dir, `${roleId}.yaml`);
28762
+ const filePath = join21(dir, `${roleId}.yaml`);
28232
28763
  if (!options.dryRun) writeFileSync14(filePath, (0, import_yaml9.stringify)(updated), "utf8");
28233
28764
  return { id: roleId, role: updated, filePath, scope };
28234
28765
  }
@@ -28277,18 +28808,18 @@ var DEFAULT_ROLE_SEATS = {
28277
28808
  };
28278
28809
  function findExistingRoleHostsFile(scope, repoRoot = process.cwd(), userConfigDir) {
28279
28810
  const dir = scope === "global" ? userConfigDirectory(userConfigDir) : repoConfigDirectory(repoRoot);
28280
- const yamlPath = join20(dir, "role-hosts.yaml");
28281
- const ymlPath = join20(dir, "role-hosts.yml");
28282
- const jsonPath = join20(dir, "role-hosts.json");
28283
- if (existsSync18(yamlPath)) return { filePath: yamlPath, exists: true, format: "yaml" };
28284
- if (existsSync18(ymlPath)) return { filePath: ymlPath, exists: true, format: "yaml" };
28285
- if (existsSync18(jsonPath)) return { filePath: jsonPath, exists: true, format: "json" };
28811
+ const yamlPath = join21(dir, "role-hosts.yaml");
28812
+ const ymlPath = join21(dir, "role-hosts.yml");
28813
+ const jsonPath = join21(dir, "role-hosts.json");
28814
+ if (existsSync19(yamlPath)) return { filePath: yamlPath, exists: true, format: "yaml" };
28815
+ if (existsSync19(ymlPath)) return { filePath: ymlPath, exists: true, format: "yaml" };
28816
+ if (existsSync19(jsonPath)) return { filePath: jsonPath, exists: true, format: "json" };
28286
28817
  return { filePath: yamlPath, exists: false, format: "yaml" };
28287
28818
  }
28288
28819
  function parseRoleHostsFile(filePath) {
28289
- if (!existsSync18(filePath)) return void 0;
28820
+ if (!existsSync19(filePath)) return void 0;
28290
28821
  try {
28291
- const raw = readFileSync17(filePath, "utf8");
28822
+ const raw = readFileSync18(filePath, "utf8");
28292
28823
  const parsed = (0, import_yaml9.parse)(raw);
28293
28824
  if (!parsed || typeof parsed !== "object") return void 0;
28294
28825
  if (parsed.schema && parsed.schema !== KXM_ROLE_HOSTS_SCHEMA) return void 0;
@@ -28361,9 +28892,9 @@ function saveRoleHostsConfig(config, options = {}) {
28361
28892
  const repoRoot = options.repoRoot ?? process.cwd();
28362
28893
  const dir = scope === "global" ? userConfigDirectory(options.userConfigDir) : repoConfigDirectory(repoRoot);
28363
28894
  const format = options.format ?? "yaml";
28364
- const filePath = join20(dir, format === "json" ? "role-hosts.json" : "role-hosts.yaml");
28895
+ const filePath = join21(dir, format === "json" ? "role-hosts.json" : "role-hosts.yaml");
28365
28896
  if (options.dryRun) return { filePath, scope };
28366
- if (!existsSync18(dir)) {
28897
+ if (!existsSync19(dir)) {
28367
28898
  mkdirSync14(dir, { recursive: true });
28368
28899
  }
28369
28900
  const payload = {
@@ -28551,6 +29082,7 @@ async function cmdRoleGet(runtime, roleId, options) {
28551
29082
  }
28552
29083
  async function cmdRoleAdd(runtime, roleId, options) {
28553
29084
  const scope = options.scope ?? "local";
29085
+ let base;
28554
29086
  if (!roleId || options.pick) {
28555
29087
  const candidates = Object.values(DEFAULT_ROLES).map((r) => ({
28556
29088
  id: r.id,
@@ -28561,8 +29093,9 @@ async function cmdRoleAdd(runtime, roleId, options) {
28561
29093
  if (scope === "local") {
28562
29094
  const globalRoles = listRoles({ scope: "global", userConfigDir: runtime.env.KXM_USER_CONFIG_DIR });
28563
29095
  for (const gr of globalRoles) {
28564
- if (!candidates.some((c) => c.id === gr.id)) {
28565
- candidates.push({ id: gr.id, description: gr.description, label: "global", payload: gr });
29096
+ const definition = parseRoleFile(gr.filePath);
29097
+ if (definition && !candidates.some((c) => c.id === gr.id)) {
29098
+ candidates.push({ id: gr.id, description: gr.description, label: "global", payload: definition });
28566
29099
  }
28567
29100
  }
28568
29101
  }
@@ -28574,63 +29107,62 @@ async function cmdRoleAdd(runtime, roleId, options) {
28574
29107
  }
28575
29108
  } else {
28576
29109
  roleId = picked.id;
28577
- if (!options.file && picked.payload && DEFAULT_ROLES[picked.id]) {
28578
- const base = DEFAULT_ROLES[picked.id];
28579
- const roleDef2 = {
28580
- ...base,
28581
- description: options.description || base.description,
28582
- skills: options.skills ? options.skills.split(",").map((s) => s.trim()).filter(Boolean) : base.skills,
28583
- roster: options.model ? [{ harness: options.harness ?? "pi", model: options.model }] : base.roster
28584
- };
28585
- try {
28586
- const res = addRole(roleDef2, {
28587
- scope,
28588
- repoRoot: runtime.cwd,
28589
- userConfigDir: runtime.env.KXM_USER_CONFIG_DIR,
28590
- overwrite: options.overwrite,
28591
- dryRun: runtime.dryRun
28592
- });
28593
- if (runtime.dryRun) {
28594
- printPlan(runtime, { command: "role add", roleId, ...res }, [{ action: "write", target: res.filePath }], `add role '${roleId}' to ${res.scope}`);
28595
- return 0;
28596
- }
28597
- print(
28598
- runtime.io,
28599
- runtime.json,
28600
- { ok: true, command: "role add", roleId, ...res },
28601
- `Added role '${roleId}' to ${res.scope} (${res.filePath})
28602
- `
28603
- );
28604
- return 0;
28605
- } catch (err) {
28606
- runtime.io.stderr(`role add failed: ${err.message}
28607
- `);
28608
- return 1;
28609
- }
28610
- }
29110
+ base = picked.payload;
28611
29111
  }
28612
29112
  }
29113
+ const skills = options.skills ? options.skills.split(",").map((s) => s.trim()).filter(Boolean) : void 0;
29114
+ const roster = options.model ? [{ harness: options.harness ?? "pi", model: options.model }] : void 0;
28613
29115
  let roleDef;
28614
29116
  if (options.file) {
28615
29117
  const filePath = resolve15(runtime.cwd, options.file);
28616
- const content = readFileSync18(filePath, "utf8");
29118
+ const content = readFileSync19(filePath, "utf8");
28617
29119
  roleDef = (0, import_yaml10.parse)(content);
28618
29120
  roleDef.id = roleId;
29121
+ } else if (base) {
29122
+ roleDef = {
29123
+ ...base,
29124
+ description: options.description || base.description,
29125
+ skills: skills ?? base.skills,
29126
+ roster: roster ?? base.roster
29127
+ };
28619
29128
  } else {
28620
- const skills = options.skills ? options.skills.split(",").map((s) => s.trim()).filter(Boolean) : [];
28621
- const roster = options.model ? [{ harness: options.harness ?? "pi", model: options.model }] : [];
28622
29129
  roleDef = {
28623
29130
  schema: "kxm.role.v1",
28624
29131
  id: roleId,
28625
29132
  description: options.description || `Role ${roleId}`,
28626
- skills,
28627
- roster
29133
+ skills: skills ?? [],
29134
+ roster: roster ?? []
28628
29135
  };
28629
29136
  }
29137
+ let repoRoot = runtime.cwd;
29138
+ if (scope === "local") {
29139
+ const projectRoot = discoverKxmProjectRoot(runtime.cwd);
29140
+ if (!projectRoot) {
29141
+ print(
29142
+ runtime.io,
29143
+ runtime.json,
29144
+ { ok: false, command: "role add", error: "project_not_found" },
29145
+ "role add failed: local roles belong to a KXM project; run kxm init at the repository root, or pass --scope global"
29146
+ );
29147
+ return 2;
29148
+ }
29149
+ const issues = kxmRoleWriteIssues(projectRoot, roleDef.id, (0, import_yaml10.stringify)(roleDef));
29150
+ if (issues.length > 0) {
29151
+ print(
29152
+ runtime.io,
29153
+ runtime.json,
29154
+ { ok: false, command: "role add", error: "role_invalid", issues },
29155
+ `role add failed: with this role the project would not load, so nothing was written
29156
+ ${issues.map((entry) => ` ${entry.file}: ${entry.code}: ${entry.message}`).join("\n")}`
29157
+ );
29158
+ return 2;
29159
+ }
29160
+ repoRoot = projectRoot;
29161
+ }
28630
29162
  try {
28631
29163
  const res = addRole(roleDef, {
28632
29164
  scope,
28633
- repoRoot: runtime.cwd,
29165
+ repoRoot,
28634
29166
  userConfigDir: runtime.env.KXM_USER_CONFIG_DIR,
28635
29167
  overwrite: options.overwrite,
28636
29168
  dryRun: runtime.dryRun
@@ -28942,8 +29474,8 @@ async function cmdRoleResume(runtime, runId, ruling) {
28942
29474
  return 1;
28943
29475
  }
28944
29476
  }
28945
- const dbPath = join21(runtime.cwd, ".kxm", "state", "kxm.db");
28946
- if (existsSync19(dbPath)) {
29477
+ const dbPath = join22(runtime.cwd, ".kxm", "state", "kxm.db");
29478
+ if (existsSync20(dbPath)) {
28947
29479
  try {
28948
29480
  const database = runtime.dryRun ? openReadOnlyDatabase(dbPath) : new DatabaseSync(dbPath);
28949
29481
  try {
@@ -29015,8 +29547,8 @@ async function cmdRoleResume(runtime, runId, ruling) {
29015
29547
  // plugins/kxm/src/cli/workflows.ts
29016
29548
  var import_yaml12 = __toESM(require_dist(), 1);
29017
29549
  import { randomUUID as randomUUID7 } from "node:crypto";
29018
- import { existsSync as existsSync21, readFileSync as readFileSync20 } from "node:fs";
29019
- import { join as join23, resolve as resolve18 } from "node:path";
29550
+ import { existsSync as existsSync22, readFileSync as readFileSync21 } from "node:fs";
29551
+ import { join as join24, resolve as resolve17 } from "node:path";
29020
29552
 
29021
29553
  // plugins/kxm/src/retrospective.ts
29022
29554
  import { mkdirSync as mkdirSync15, renameSync as renameSync6, writeFileSync as writeFileSync15 } from "node:fs";
@@ -29344,7 +29876,7 @@ async function watchGithubChecks(input) {
29344
29876
  }
29345
29877
  const fetchImpl = input.fetchImpl ?? fetch;
29346
29878
  const now = input.now ?? Date.now;
29347
- const sleep = input.sleep ?? ((ms) => new Promise((resolve35) => setTimeout(resolve35, ms)));
29879
+ const sleep = input.sleep ?? ((ms) => new Promise((resolve34) => setTimeout(resolve34, ms)));
29348
29880
  const deadline = now() + input.timeoutMs;
29349
29881
  const headers = {
29350
29882
  authorization: `Bearer ${token}`,
@@ -29443,8 +29975,22 @@ async function watchGithubChecks(input) {
29443
29975
 
29444
29976
  // plugins/kxm/src/workflow-manager.ts
29445
29977
  var import_yaml11 = __toESM(require_dist(), 1);
29446
- import { existsSync as existsSync20, mkdirSync as mkdirSync16, readdirSync as readdirSync7, readFileSync as readFileSync19, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "node:fs";
29447
- import { join as join22 } from "node:path";
29978
+ import { existsSync as existsSync21, mkdirSync as mkdirSync16, readdirSync as readdirSync7, readFileSync as readFileSync20, rmSync as rmSync7, writeFileSync as writeFileSync16 } from "node:fs";
29979
+ import { join as join23 } from "node:path";
29980
+ function assertWorkflowId(workflowId) {
29981
+ if (!kxmResourceIdentifier(workflowId)) {
29982
+ throw new Error(`workflow_id_invalid: '${workflowId}' must be a flat lowercase slug of at most 64 characters, starting with a letter and using letters, digits, single hyphens or underscores; paths and platform-reserved names are not allowed (use 'bug-fix', not 'software-engineering/bug-fix')`);
29983
+ }
29984
+ }
29985
+ var workflowSchemaRegistry;
29986
+ function workflowPayload(workflowId, content, filePath) {
29987
+ const payload = typeof content === "string" ? content : (0, import_yaml11.stringify)(content);
29988
+ const value = parseRestrictedYaml2(payload, filePath);
29989
+ const issues = (workflowSchemaRegistry ??= new KxmSchemaRegistry()).validate("workflow", value, filePath);
29990
+ if (issues.length > 0) throw new KxmConfigError(issues);
29991
+ compileKxmWorkflow({ id: workflowId, value, logicalPath: filePath });
29992
+ return payload;
29993
+ }
29448
29994
  var completed = () => ({ target: "$terminal", terminalStatus: "completed" });
29449
29995
  var failed = () => ({ target: "$terminal", terminalStatus: "failed" });
29450
29996
  function verifyStep(retryStep) {
@@ -29581,24 +30127,22 @@ function scaffoldWorkflowDefinition(description) {
29581
30127
  var DEFAULT_WORKFLOW_TEMPLATE = WORKFLOW_TEMPLATES["implement-and-verify"];
29582
30128
  function workflowsDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
29583
30129
  if (scope === "global") {
29584
- return join22(userConfigDirectory(userConfigDir), "workflows");
30130
+ return join23(userConfigDirectory(userConfigDir), "workflows");
29585
30131
  }
29586
- return join22(repoConfigDirectory(repoRoot), "workflows");
30132
+ return join23(repoConfigDirectory(repoRoot), "workflows");
29587
30133
  }
29588
30134
  function ensureWorkflowsDirectory(scope, repoRoot = process.cwd(), userConfigDir) {
29589
30135
  const dir = workflowsDirectory(scope, repoRoot, userConfigDir);
29590
- if (!existsSync20(dir)) {
30136
+ if (!existsSync21(dir)) {
29591
30137
  mkdirSync16(dir, { recursive: true });
29592
30138
  }
29593
30139
  return dir;
29594
30140
  }
29595
30141
  function parseWorkflowFile(filePath) {
29596
- if (!existsSync20(filePath)) return void 0;
30142
+ if (!existsSync21(filePath)) return void 0;
29597
30143
  try {
29598
- const raw = readFileSync19(filePath, "utf8");
29599
- const parsed = (0, import_yaml11.parse)(raw);
29600
- if (!parsed || typeof parsed !== "object") return void 0;
29601
- return parsed;
30144
+ const raw = readFileSync20(filePath, "utf8");
30145
+ return parseRestrictedYaml2(raw, filePath);
29602
30146
  } catch {
29603
30147
  return void 0;
29604
30148
  }
@@ -29609,26 +30153,26 @@ function listWorkflowDefinitions(options = {}) {
29609
30153
  const globalDir = workflowsDirectory("global", repoRoot, options.userConfigDir);
29610
30154
  const localDir = workflowsDirectory("local", repoRoot, options.userConfigDir);
29611
30155
  const localDefs = /* @__PURE__ */ new Map();
29612
- if (scopeFilter !== "global" && existsSync20(localDir)) {
30156
+ if (scopeFilter !== "global" && existsSync21(localDir)) {
29613
30157
  for (const entry of readdirSync7(localDir)) {
29614
- if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
29615
- const filePath = join22(localDir, entry);
30158
+ const id = entry.endsWith(".yaml") ? entry.slice(0, -5) : entry.endsWith(".yml") ? entry.slice(0, -4) : void 0;
30159
+ if (id && kxmResourceIdentifier(id)) {
30160
+ const filePath = join23(localDir, entry);
29616
30161
  const def = parseWorkflowFile(filePath);
29617
30162
  if (def) {
29618
- const id = def.id || entry.replace(/\.ya?ml$/i, "");
29619
30163
  localDefs.set(id, { def, filePath });
29620
30164
  }
29621
30165
  }
29622
30166
  }
29623
30167
  }
29624
30168
  const globalDefs = /* @__PURE__ */ new Map();
29625
- if (scopeFilter !== "local" && existsSync20(globalDir)) {
30169
+ if (scopeFilter !== "local" && existsSync21(globalDir)) {
29626
30170
  for (const entry of readdirSync7(globalDir)) {
29627
- if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
29628
- const filePath = join22(globalDir, entry);
30171
+ const id = entry.endsWith(".yaml") ? entry.slice(0, -5) : entry.endsWith(".yml") ? entry.slice(0, -4) : void 0;
30172
+ if (id && kxmResourceIdentifier(id)) {
30173
+ const filePath = join23(globalDir, entry);
29629
30174
  const def = parseWorkflowFile(filePath);
29630
30175
  if (def) {
29631
- const id = def.id || entry.replace(/\.ya?ml$/i, "");
29632
30176
  globalDefs.set(id, { def, filePath });
29633
30177
  }
29634
30178
  }
@@ -29639,7 +30183,7 @@ function listWorkflowDefinitions(options = {}) {
29639
30183
  const steps = Array.isArray(def.steps) ? def.steps : [];
29640
30184
  for (const step of steps) {
29641
30185
  if (step && typeof step === "object") {
29642
- const role = step.role || step.agent;
30186
+ const role = step.agent;
29643
30187
  if (typeof role === "string" && !roles.includes(role)) {
29644
30188
  roles.push(role);
29645
30189
  }
@@ -29677,40 +30221,46 @@ function listWorkflowDefinitions(options = {}) {
29677
30221
  return result;
29678
30222
  }
29679
30223
  function getWorkflowDefinition(workflowId, options = {}) {
30224
+ assertWorkflowId(workflowId);
29680
30225
  const scope = options.scope ?? "all";
29681
30226
  const repoRoot = options.repoRoot ?? process.cwd();
29682
30227
  if (scope !== "global") {
29683
30228
  const localDir = workflowsDirectory("local", repoRoot, options.userConfigDir);
29684
- const localFile = join22(localDir, `${workflowId}.yaml`);
30229
+ const localFile = join23(localDir, `${workflowId}.yaml`);
29685
30230
  const parsed = parseWorkflowFile(localFile);
29686
30231
  if (parsed) return { workflow: parsed, scope: "local", filePath: localFile };
29687
30232
  }
29688
30233
  if (scope !== "local") {
29689
30234
  const globalDir = workflowsDirectory("global", repoRoot, options.userConfigDir);
29690
- const globalFile = join22(globalDir, `${workflowId}.yaml`);
30235
+ const globalFile = join23(globalDir, `${workflowId}.yaml`);
29691
30236
  const parsed = parseWorkflowFile(globalFile);
29692
30237
  if (parsed) return { workflow: parsed, scope: "global", filePath: globalFile };
29693
30238
  }
29694
30239
  return void 0;
29695
30240
  }
29696
30241
  function addWorkflowDefinition(workflowId, content = DEFAULT_WORKFLOW_TEMPLATE, options = {}) {
30242
+ assertWorkflowId(workflowId);
29697
30243
  const scope = options.scope ?? "local";
29698
30244
  const repoRoot = options.repoRoot ?? process.cwd();
29699
- const dir = options.dryRun ? workflowsDirectory(scope, repoRoot, options.userConfigDir) : ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
29700
- const filePath = join22(dir, `${workflowId}.yaml`);
29701
- if (existsSync20(filePath) && !options.overwrite) {
30245
+ const dir = workflowsDirectory(scope, repoRoot, options.userConfigDir);
30246
+ const filePath = join23(dir, `${workflowId}.yaml`);
30247
+ if (existsSync21(filePath) && !options.overwrite) {
29702
30248
  throw new Error(`workflow_already_exists: workflow '${workflowId}' already exists at ${filePath}`);
29703
30249
  }
29704
- const payload = typeof content === "string" ? content : (0, import_yaml11.stringify)(content);
29705
- if (!options.dryRun) writeFileSync16(filePath, payload, "utf8");
30250
+ const payload = workflowPayload(workflowId, content, filePath);
30251
+ if (!options.dryRun) {
30252
+ ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
30253
+ writeFileSync16(filePath, payload, "utf8");
30254
+ }
29706
30255
  return { id: workflowId, filePath, scope };
29707
30256
  }
29708
30257
  function removeWorkflowDefinition(workflowId, options = {}) {
30258
+ assertWorkflowId(workflowId);
29709
30259
  const scope = options.scope ?? "local";
29710
30260
  const repoRoot = options.repoRoot ?? process.cwd();
29711
30261
  const dir = workflowsDirectory(scope, repoRoot, options.userConfigDir);
29712
- const filePath = join22(dir, `${workflowId}.yaml`);
29713
- if (!existsSync20(filePath)) {
30262
+ const filePath = join23(dir, `${workflowId}.yaml`);
30263
+ if (!existsSync21(filePath)) {
29714
30264
  throw new Error(`workflow_not_found: workflow '${workflowId}' not found in ${scope} directory (${filePath})`);
29715
30265
  }
29716
30266
  if (options.dryRun) return { id: workflowId, removed: false, filePath, scope };
@@ -29728,16 +30278,20 @@ function modifyWorkflowDefinition(workflowId, updates, options = {}) {
29728
30278
  };
29729
30279
  const scope = options.scope ?? target.scope;
29730
30280
  const repoRoot = options.repoRoot ?? process.cwd();
29731
- const dir = options.dryRun ? workflowsDirectory(scope, repoRoot, options.userConfigDir) : ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
29732
- const filePath = join22(dir, `${workflowId}.yaml`);
29733
- if (!options.dryRun) writeFileSync16(filePath, (0, import_yaml11.stringify)(updated), "utf8");
30281
+ const dir = workflowsDirectory(scope, repoRoot, options.userConfigDir);
30282
+ const filePath = join23(dir, `${workflowId}.yaml`);
30283
+ const payload = workflowPayload(workflowId, updated, filePath);
30284
+ if (!options.dryRun) {
30285
+ ensureWorkflowsDirectory(scope, repoRoot, options.userConfigDir);
30286
+ writeFileSync16(filePath, payload, "utf8");
30287
+ }
29734
30288
  return { id: workflowId, workflow: updated, filePath, scope };
29735
30289
  }
29736
30290
 
29737
30291
  // plugins/kxm/src/cli/workflows.ts
29738
30292
  var CLI_NAME = "kxm";
29739
30293
  function localWorkflowSnapshot(dataPath, runId) {
29740
- if (!existsSync21(dataPath)) throw new Error("state_database_not_found");
30294
+ if (!existsSync22(dataPath)) throw new Error("state_database_not_found");
29741
30295
  const database = openReadOnlyDatabase(dataPath);
29742
30296
  try {
29743
30297
  const rows = runId ? database.prepare("SELECT record FROM workflow_runs WHERE id = ?").all(runId) : database.prepare("SELECT record FROM workflow_runs ORDER BY rowid DESC LIMIT 200").all();
@@ -29817,7 +30371,7 @@ function activeWorkflowDefinition(runtime, definitionId) {
29817
30371
  let raw;
29818
30372
  if (file) {
29819
30373
  try {
29820
- raw = readFileSync20(resolve18(runtime.cwd, file), "utf8");
30374
+ raw = readFileSync21(resolve17(runtime.cwd, file), "utf8");
29821
30375
  } catch {
29822
30376
  throw new Error("workflow definition file is unavailable");
29823
30377
  }
@@ -29865,60 +30419,56 @@ async function cmdWorkflowDefinitions(runtime, options) {
29865
30419
  }
29866
30420
  async function cmdWorkflowAdd(runtime, workflowId, options) {
29867
30421
  const scope = options.scope ?? "local";
29868
- const refuse = (error, text) => {
29869
- print(runtime.io, runtime.json, { ok: false, command: "workflow add", error }, `workflow add failed: ${text}`);
29870
- return 2;
30422
+ const refuse = (error, message, code = 2) => {
30423
+ print(runtime.io, runtime.json, { ok: false, command: "workflow add", error, message }, `workflow add failed: ${message}`);
30424
+ return code;
29871
30425
  };
29872
- let content;
29873
- if (options.template !== void 0) {
29874
- if (options.file !== void 0 || options.pick !== void 0) {
29875
- return refuse("workflow_add_conflict", "--template cannot be combined with --file or --pick");
29876
- }
29877
- if (!workflowId) return refuse("workflow_id_required", "usage: kxm workflow add <workflowId> --template <name>");
29878
- const template = Object.hasOwn(WORKFLOW_TEMPLATES, options.template) ? WORKFLOW_TEMPLATES[options.template] : void 0;
29879
- if (!template) {
29880
- return refuse("workflow_template_unknown", `unknown template ${options.template}; choose ${Object.keys(WORKFLOW_TEMPLATES).join(", ")}`);
29881
- }
29882
- content = { ...template, ...options.description ? { description: options.description } : {} };
29883
- } else if (!workflowId || options.pick) {
29884
- const candidates = Object.entries(WORKFLOW_TEMPLATES).map(([id, tmpl]) => ({
29885
- id,
29886
- description: String(tmpl.description ?? id),
29887
- label: "template",
29888
- payload: tmpl
29889
- }));
29890
- if (scope === "local") {
29891
- const globalDefs = listWorkflowDefinitions({ scope: "global", userConfigDir: runtime.env.KXM_USER_CONFIG_DIR });
29892
- for (const gd of globalDefs) {
29893
- const definition = parseWorkflowFile(gd.filePath);
29894
- if (definition && !candidates.some((c) => c.id === gd.id)) {
29895
- candidates.push({ id: gd.id, description: gd.description, label: "global", payload: definition });
30426
+ try {
30427
+ let content;
30428
+ if (options.template !== void 0) {
30429
+ if (options.file !== void 0 || options.pick !== void 0) {
30430
+ return refuse("workflow_add_conflict", "--template cannot be combined with --file or --pick");
30431
+ }
30432
+ if (!workflowId) return refuse("workflow_id_required", "usage: kxm workflow add <workflowId> --template <name>");
30433
+ const template = Object.hasOwn(WORKFLOW_TEMPLATES, options.template) ? WORKFLOW_TEMPLATES[options.template] : void 0;
30434
+ if (!template) {
30435
+ return refuse("workflow_template_unknown", `unknown template ${options.template}; choose ${Object.keys(WORKFLOW_TEMPLATES).join(", ")}`);
30436
+ }
30437
+ content = { ...template, ...options.description !== void 0 ? { description: options.description } : {} };
30438
+ } else if (!workflowId || options.pick) {
30439
+ const candidates = Object.entries(WORKFLOW_TEMPLATES).map(([id, tmpl]) => ({
30440
+ id,
30441
+ description: String(tmpl.description ?? id),
30442
+ label: "template",
30443
+ payload: tmpl
30444
+ }));
30445
+ if (scope === "local") {
30446
+ const globalDefs = listWorkflowDefinitions({ scope: "global", userConfigDir: runtime.env.KXM_USER_CONFIG_DIR });
30447
+ for (const gd of globalDefs) {
30448
+ if (!candidates.some((c) => c.id === gd.id)) {
30449
+ candidates.push({ id: gd.id, description: gd.description, label: "global", payload: gd.filePath });
30450
+ }
29896
30451
  }
29897
30452
  }
29898
- }
29899
- const picked = await resolvePickItem(runtime.io, `Select a workflow template to add (${scope})`, candidates, options.pick, runtime.env);
29900
- if (!picked) {
29901
- if (!workflowId) {
29902
- runtime.io.stderr("workflow add failed: missing workflowId or pick selection\n");
29903
- return 1;
30453
+ const picked = await resolvePickItem(runtime.io, `Select a workflow template to add (${scope})`, candidates, options.pick, runtime.env);
30454
+ if (!picked) {
30455
+ return refuse("workflow_id_required", "provide a workflowId or select an available workflow with --pick <id>", 1);
29904
30456
  }
29905
- } else {
29906
- workflowId = picked.id;
29907
- if (!options.file && picked.payload) {
29908
- content = {
29909
- ...picked.payload,
29910
- ...options.description ? { description: options.description } : {}
29911
- };
30457
+ workflowId ??= picked.id;
30458
+ if (options.file === void 0) {
30459
+ const pickedContent = typeof picked.payload === "string" ? readFileSync21(picked.payload, "utf8") : picked.payload;
30460
+ content = options.description !== void 0 ? {
30461
+ ...typeof pickedContent === "string" ? parseRestrictedYaml2(pickedContent, String(picked.payload)) : pickedContent,
30462
+ description: options.description
30463
+ } : pickedContent;
29912
30464
  }
29913
30465
  }
29914
- }
29915
- if (options.file) {
29916
- const filePath = resolve18(runtime.cwd, options.file);
29917
- content = readFileSync20(filePath, "utf8");
29918
- } else if (!content) {
29919
- content = scaffoldWorkflowDefinition(options.description || `Workflow ${workflowId}`);
29920
- }
29921
- try {
30466
+ if (options.file !== void 0) {
30467
+ const filePath = resolve17(runtime.cwd, options.file);
30468
+ content = readFileSync21(filePath, "utf8");
30469
+ } else if (!content) {
30470
+ content = scaffoldWorkflowDefinition(options.description || `Workflow ${workflowId}`);
30471
+ }
29922
30472
  const document = typeof content === "string" ? content : (0, import_yaml12.stringify)(content);
29923
30473
  let repoRoot = runtime.cwd;
29924
30474
  if (scope === "local") {
@@ -29931,7 +30481,7 @@ async function cmdWorkflowAdd(runtime, workflowId, options) {
29931
30481
  print(
29932
30482
  runtime.io,
29933
30483
  runtime.json,
29934
- { ok: false, command: "workflow add", error: "workflow_invalid", issues },
30484
+ { ok: false, command: "workflow add", error: "workflow_invalid", message: "with this workflow the project would not load, so nothing was written", issues },
29935
30485
  `workflow add failed: with this workflow the project would not load, so nothing was written
29936
30486
  ${issues.map((entry) => ` ${entry.file}: ${entry.code}: ${entry.message}`).join("\n")}`
29937
30487
  );
@@ -29959,9 +30509,7 @@ ${issues.map((entry) => ` ${entry.file}: ${entry.code}: ${entry.message}`).join
29959
30509
  );
29960
30510
  return 0;
29961
30511
  } catch (err) {
29962
- runtime.io.stderr(`workflow add failed: ${err.message}
29963
- `);
29964
- return 1;
30512
+ return refuse("workflow_add_failed", err instanceof Error ? err.message : String(err), 1);
29965
30513
  }
29966
30514
  }
29967
30515
  async function cmdWorkflowRemove(runtime, workflowId, options) {
@@ -30093,7 +30641,7 @@ async function cmdWorkflowStart(runtime, definitionIdArg, options) {
30093
30641
  }
30094
30642
  let payload;
30095
30643
  try {
30096
- const raw = payloadFlag.startsWith("@") ? readFileSync20(resolve18(runtime.cwd, payloadFlag.slice(1)), "utf8") : payloadFlag;
30644
+ const raw = payloadFlag.startsWith("@") ? readFileSync21(resolve17(runtime.cwd, payloadFlag.slice(1)), "utf8") : payloadFlag;
30097
30645
  const value = JSON.parse(raw);
30098
30646
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("object required");
30099
30647
  payload = value;
@@ -30160,7 +30708,7 @@ async function cmdWorkflowDegrade(runtime, runId, stageId, options) {
30160
30708
  }
30161
30709
  }
30162
30710
  async function cmdWorkflowInspect(runtime, action, runId) {
30163
- const dataPath = resolve18(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join23(runtime.dirs.state, "kxm.db"));
30711
+ const dataPath = resolve17(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join24(runtime.dirs.state, "kxm.db"));
30164
30712
  if (action === "get" && !runId) {
30165
30713
  runtime.io.stderr(`Usage: ${CLI_NAME} workflow get <runId>
30166
30714
  `);
@@ -30329,14 +30877,14 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
30329
30877
  return 2;
30330
30878
  }
30331
30879
  const snapshotFlag = String(options.input || "");
30332
- const snapshotPath = snapshotFlag ? resolve18(runtime.cwd, snapshotFlag) : "";
30880
+ const snapshotPath = snapshotFlag ? resolve17(runtime.cwd, snapshotFlag) : "";
30333
30881
  let snapshot;
30334
30882
  try {
30335
30883
  if (snapshotPath) {
30336
- if (!existsSync21(snapshotPath)) throw new Error("snapshot_missing");
30337
- snapshot = JSON.parse(readFileSync20(snapshotPath, "utf8"));
30884
+ if (!existsSync22(snapshotPath)) throw new Error("snapshot_missing");
30885
+ snapshot = JSON.parse(readFileSync21(snapshotPath, "utf8"));
30338
30886
  } else {
30339
- const dataPath = resolve18(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join23(runtime.dirs.state, "kxm.db"));
30887
+ const dataPath = resolve17(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join24(runtime.dirs.state, "kxm.db"));
30340
30888
  const local = localWorkflowSnapshot(dataPath, runId);
30341
30889
  if (!local.runs[0]) throw new Error("workflow_not_found");
30342
30890
  snapshot = { run: local.runs[0], journal: local.journal };
@@ -30348,8 +30896,8 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
30348
30896
  return 1;
30349
30897
  }
30350
30898
  const doc = buildRetrospective(snapshot.run, snapshot.journal);
30351
- const outDir = resolve18(runtime.cwd, String(options.outDir || join23(runtime.dirs.assets, "retrospectives")));
30352
- const assetsRoot = resolve18(runtime.dirs.assets);
30899
+ const outDir = resolve17(runtime.cwd, String(options.outDir || join24(runtime.dirs.assets, "retrospectives")));
30900
+ const assetsRoot = resolve17(runtime.dirs.assets);
30353
30901
  const assetsPrefix = `${assetsRoot}${process.platform === "win32" ? "\\" : "/"}`;
30354
30902
  if (outDir !== assetsRoot && !outDir.startsWith(assetsPrefix)) {
30355
30903
  print(runtime.io, runtime.json, { ok: false, command: "retrospective export", error: "output_outside_workspace_assets" }, "retrospectives must stay under the workspace assets directory");
@@ -30366,133 +30914,174 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
30366
30914
 
30367
30915
  // plugins/kxm/src/cli/tasks.ts
30368
30916
  var import_yaml15 = __toESM(require_dist(), 1);
30369
- import { existsSync as existsSync27, readFileSync as readFileSync26, readdirSync as readdirSync10 } from "node:fs";
30370
- import { basename as basename7, join as join31, resolve as resolve24 } from "node:path";
30917
+ import { existsSync as existsSync28, readFileSync as readFileSync27, readdirSync as readdirSync10 } from "node:fs";
30918
+ import { basename as basename7, join as join32, resolve as resolve23 } from "node:path";
30371
30919
 
30372
30920
  // plugins/kxm/src/suggest.ts
30373
30921
  var WORKFLOW_PATTERNS = [
30374
30922
  {
30375
- id: "software-engineering/bug-fix",
30923
+ id: "bug-fix",
30924
+ template: "implement-and-verify",
30376
30925
  area: "software-engineering",
30377
30926
  keywords: ["fix", "bug", "flaky", "failure", "timeout", "error", "repro", "crash", "broken", "hang"],
30378
- skills: ["kxm-workflow", "kxm-runs", "kxm-context-memory"],
30379
- defaultCommand: (p) => `kxm run software-engineering/bug-fix "${p}"`
30927
+ skills: ["kxm-workflow", "kxm-runs", "kxm-context-memory"]
30380
30928
  },
30381
30929
  {
30382
- id: "software-engineering/feature-implementation",
30930
+ id: "feature-implementation",
30931
+ template: "implement-and-verify",
30383
30932
  area: "software-engineering",
30384
30933
  keywords: ["feature", "implement", "add", "build", "create", "develop", "support", "endpoint", "ui", "tui"],
30385
- skills: ["kxm-workflow", "kxm-peer", "kxm-context-memory"],
30386
- defaultCommand: (p) => `kxm run software-engineering/feature-implementation "${p}"`
30934
+ skills: ["kxm-workflow", "kxm-peer", "kxm-context-memory"]
30387
30935
  },
30388
30936
  {
30389
- id: "software-engineering/refactoring",
30937
+ id: "refactoring",
30938
+ template: "dual-critic-review",
30390
30939
  area: "software-engineering",
30391
30940
  keywords: ["refactor", "cleanup", "reorganize", "modularize", "deduplicate", "split", "simplify", "deprecate"],
30392
- skills: ["kxm-workflow", "kxm-runs"],
30393
- defaultCommand: (p) => `kxm run software-engineering/refactoring "${p}"`
30941
+ skills: ["kxm-workflow", "kxm-runs"]
30394
30942
  },
30395
30943
  {
30396
- id: "security-reliability/vulnerability-remediation",
30944
+ id: "vulnerability-remediation",
30945
+ template: "dual-critic-review",
30397
30946
  area: "security-reliability",
30398
30947
  keywords: ["cve", "vulnerability", "security", "exploit", "sanitize", "leak", "secret", "injection", "redact", "auth"],
30399
- skills: ["kxm-workflow", "kxm-definitions"],
30400
- defaultCommand: (p) => `kxm run security-reliability/vulnerability-remediation "${p}"`
30948
+ skills: ["kxm-workflow", "kxm-definitions"]
30401
30949
  },
30402
30950
  {
30403
- id: "security-reliability/reliability-hardening",
30951
+ id: "reliability-hardening",
30952
+ template: "dual-critic-review",
30404
30953
  area: "security-reliability",
30405
30954
  keywords: ["idempotency", "retry", "circuit-breaker", "cas", "lock", "concurrency", "deadlock", "race", "crash-recovery"],
30406
- skills: ["kxm-workflow", "kxm-peer"],
30407
- defaultCommand: (p) => `kxm run security-reliability/reliability-hardening "${p}"`
30955
+ skills: ["kxm-workflow", "kxm-peer"]
30408
30956
  },
30409
30957
  {
30410
- id: "data-analytics/pipeline-migration",
30958
+ id: "pipeline-migration",
30959
+ template: "implement-and-verify",
30411
30960
  area: "data-analytics",
30412
30961
  keywords: ["database", "sqlite", "migration", "pipeline", "schema", "transform", "table", "wal", "foreign", "cascading"],
30413
- skills: ["kxm-runs", "kxm-context-memory"],
30414
- defaultCommand: (p) => `kxm run data-analytics/pipeline-migration "${p}"`
30962
+ skills: ["kxm-runs", "kxm-context-memory"]
30415
30963
  },
30416
30964
  {
30417
- id: "research-strategy/architecture-spike",
30965
+ id: "architecture-spike",
30966
+ template: "spec-and-plan",
30418
30967
  area: "research-strategy",
30419
30968
  keywords: ["spike", "investigate", "prototype", "research", "feasibility", "benchmark", "explore", "evaluate"],
30420
- skills: ["kxm-session", "kxm-context-memory", "kxm-routing-improve"],
30421
- defaultCommand: (p) => `kxm run research-strategy/architecture-spike "${p}"`
30969
+ skills: ["kxm-session", "kxm-context-memory", "kxm-routing-improve"]
30422
30970
  }
30423
30971
  ];
30424
30972
  function suggestWorkflowAndRoles(prompt, options = {}) {
30425
30973
  const normalized = prompt.toLowerCase();
30426
30974
  const words = normalized.split(/\W+/).filter(Boolean);
30427
- let bestPattern = WORKFLOW_PATTERNS[0];
30428
- let bestScore = -1;
30429
- const matchedReasons = [];
30975
+ const claudeOnly = /\bclaude(?:\s+code)?(?:[\s-]+only|\s+exclusively)\b|\b(?:only|exclusively)(?:\s+(?:use|using|with|via))?\s+claude(?:\s+code)?\b/i.test(prompt);
30976
+ let bestPattern = WORKFLOW_PATTERNS[1];
30977
+ let bestScore = 0;
30978
+ const reasons = [];
30430
30979
  for (const pattern of WORKFLOW_PATTERNS) {
30431
- let score = 0;
30432
- const matchedWords = [];
30433
- for (const keyword of pattern.keywords) {
30434
- if (words.includes(keyword) || normalized.includes(keyword)) {
30435
- score += 1;
30436
- matchedWords.push(keyword);
30437
- }
30438
- }
30439
- if (score > bestScore) {
30440
- bestScore = score;
30980
+ const matchedWords = pattern.keywords.filter((keyword) => words.includes(keyword) || normalized.includes(keyword));
30981
+ if (matchedWords.length > bestScore) {
30982
+ bestScore = matchedWords.length;
30441
30983
  bestPattern = pattern;
30442
- matchedReasons.length = 0;
30443
- matchedReasons.push(`Matched keywords: ${matchedWords.join(", ")}`);
30984
+ reasons.length = 0;
30985
+ reasons.push(`Matched keywords: ${matchedWords.join(", ")}`);
30444
30986
  }
30445
30987
  }
30446
- if (bestScore <= 0) {
30447
- bestPattern = WORKFLOW_PATTERNS[1];
30448
- matchedReasons.push("Default fallback: general feature implementation workflow");
30449
- }
30450
- const activeHarnesses = new Set(
30451
- (options.availableHarnesses ?? []).filter((h) => h.auth === "active" || h.auth === "ready" || h.auth === "configured").map((h) => h.harness.toLowerCase())
30452
- );
30453
- const hasClaude = activeHarnesses.size === 0 || activeHarnesses.has("claude");
30454
- const hasGrok = activeHarnesses.size === 0 || activeHarnesses.has("grok");
30455
- const hasCodex = activeHarnesses.size === 0 || activeHarnesses.has("codex");
30456
- const planner = hasClaude ? { harness: "claude", model: "fable", role: "planner" } : { harness: "codex", model: "gpt-5.6-sol", role: "planner" };
30457
- const writer = hasGrok ? { harness: "grok", model: "grok-4.6", role: "writer" } : activeHarnesses.has("agy") ? { harness: "agy", model: "gemini-2.5-pro", role: "writer" } : { harness: "pi", model: "qwen/qwen3-coder-plus", role: "writer" };
30458
- const critics = [
30459
- { harness: "claude", model: "fable", role: "reviewer-arch" },
30460
- { harness: "codex", model: "gpt-5.6-sol", role: "reviewer-cli" }
30461
- ];
30462
- return {
30988
+ if (bestScore === 0) reasons.push("Default fallback: general feature implementation workflow");
30989
+ if (claudeOnly) reasons.push("Explicit Claude-only constraint; other harnesses are excluded");
30990
+ const template = WORKFLOW_TEMPLATES[bestPattern.template];
30991
+ const steps = template.steps;
30992
+ const base = {
30463
30993
  workflowId: bestPattern.id,
30994
+ template: bestPattern.template,
30464
30995
  area: bestPattern.area,
30465
30996
  confidence: bestScore > 0 ? Math.min(1, 0.5 + bestScore * 0.15) : 0.5,
30466
- reasons: matchedReasons,
30997
+ reasons,
30467
30998
  suggestedSkills: bestPattern.skills,
30468
- roles: {
30469
- planner,
30470
- writer,
30471
- critics,
30472
- verifier: { kind: "witness", command: "npm run verify" }
30473
- },
30474
- suggestedCommand: bestPattern.defaultCommand(prompt.replace(/"/g, '\\"'))
30999
+ suggestedCommand: `kxm workflow add ${bestPattern.id} --template ${bestPattern.template}`
31000
+ };
31001
+ const writeStep = steps.find((step) => step.kind === "agent" && Object.values(step.repositories ?? {}).includes("write"));
31002
+ const available = (options.availableHarnesses ?? []).filter((entry) => BUILTIN_HARNESS_IDS.includes(entry.id) && entry.detected && entry.authenticated === true && entry.dispatch?.supported === true && entry.dispatch.status === "yes" && (!claudeOnly || entry.id === "claude"));
31003
+ const selected = available.find((entry) => entry.id === "claude") ?? available[0];
31004
+ if (!selected) {
31005
+ const relevant = (options.availableHarnesses ?? []).filter((entry) => !claudeOnly || entry.id === "claude");
31006
+ const detail = relevant.map((entry) => `${entry.id}: ${entry.dispatch?.reason ?? (!entry.detected ? "not_detected" : entry.authenticated !== true ? "authentication_unverified" : "dispatch_unavailable")}`).join("; ");
31007
+ return {
31008
+ ...base,
31009
+ roles: [],
31010
+ execution: {
31011
+ supported: false,
31012
+ error: "harness_unavailable",
31013
+ reason: `${claudeOnly ? "Claude-only execution requires Claude Code" : "Execution requires a supported harness"} detected, authenticated, and ready for dispatch. ${detail || "No verified harness inventory is available."}`,
31014
+ nextSteps: claudeOnly ? ["Install Claude Code if missing, authenticate with claude auth login, then confirm claude auth status and kxm harness list. Other harnesses will not be substituted."] : ["Install and authenticate a supported harness, then run kxm harness list to check detection, authentication, and dispatch readiness. Pi requires a configured model/provider auth context."]
31015
+ }
31016
+ };
31017
+ }
31018
+ if (writeStep && !oneShotWriterArgs(selected.id)) {
31019
+ return {
31020
+ ...base,
31021
+ roles: [],
31022
+ execution: {
31023
+ supported: false,
31024
+ error: "live_write_unsupported",
31025
+ reason: `${bestPattern.template} requires repository writes at step ${writeStep.id}, but the selected ${selected.id} harness has no audited live writer profile. No other harness will be substituted.`,
31026
+ nextSteps: [
31027
+ `Run the implementation directly in ${claudeOnly ? "Claude Code" : selected.id} in the target worktree, and run the repository's actual verification command.`,
31028
+ `For read-only KXM planning, try kxm suggest "Investigate an architecture spike${claudeOnly ? " using Claude only" : ""}"; the shipped spec-and-plan template does not implement changes.`,
31029
+ "Installing a definition or creating a run does not execute it. Simulation is not evidence of a bug fix or workflow completion."
31030
+ ]
31031
+ }
31032
+ };
31033
+ }
31034
+ const roles = [];
31035
+ for (const step of steps) {
31036
+ if (!step.agent) continue;
31037
+ const role = roles.find((entry) => entry.agent === step.agent);
31038
+ if (role) role.role += `, ${step.id}`;
31039
+ else roles.push({ agent: step.agent, harness: selected.id, role: step.id });
31040
+ }
31041
+ const shell = process.platform === "win32" ? "powershell" : "posix";
31042
+ const quotedPrompt = shell === "powershell" ? `'${prompt.replace(/['\u2018-\u201b]/g, "$&$&")}'` : `'${prompt.replace(/'/g, "'\\''")}'`;
31043
+ return {
31044
+ ...base,
31045
+ roles,
31046
+ execution: {
31047
+ supported: true,
31048
+ prerequisites: [
31049
+ "Run kxm init in the target repository if needed, then install the exact template with the suggested command. If that workflow ID already exists, stop and review it; the commands below apply only to a newly installed template, not an existing definition with potentially different agents or permissions.",
31050
+ ...roles.map((role) => `Set harness: ${role.harness} and model: <your authenticated compatible model selector> in .kxm/agents/${role.agent}.yaml. Admit that exact selector in .kxm/routes.yaml and ensure it is not disabled; this recommendation does not choose a model or change routing.`),
31051
+ ...writeStep ? [
31052
+ `Keep each writer step at assignments.maximum: 1 and set limits.maxConcurrentRuns: 1 in .kxm/project.yaml; the live ${selected.id} writer profile requires a lone writer in the checkout.`,
31053
+ `If .kxm/roster.yaml exists, it must be readable kxm.developer-roster.v1 with a lineup.writer route matching harness: ${selected.id} and the selected writer model (model or vendor/model), status: admitted, and permissions containing edit.`,
31054
+ "Configure the test gate in .kxm/gates.yaml with kind: command and argv for this repository's actual verification command. A scaffold/example command or simulation is not evidence that the implementation works."
31055
+ ] : [],
31056
+ `Validate the installed workflow with kxm gate validate --file .kxm/workflows/${bestPattern.id}.yaml and the project configuration with kxm init --dry-run before creating a run.`
31057
+ ],
31058
+ shell,
31059
+ createCommand: `kxm run ${bestPattern.id} -- ${quotedPrompt}`,
31060
+ driveCommand: "kxm runs drive <runId> --wait",
31061
+ statusCommand: "kxm runs status <runId>",
31062
+ receiptCommand: "kxm runs receipt <runId>"
31063
+ }
30475
31064
  };
30476
31065
  }
30477
31066
 
30478
31067
  // plugins/kxm/src/task-manager.ts
30479
31068
  var import_yaml13 = __toESM(require_dist(), 1);
30480
31069
  import { randomUUID as randomUUID8 } from "node:crypto";
30481
- import { existsSync as existsSync22, mkdirSync as mkdirSync17, readFileSync as readFileSync21, readdirSync as readdirSync8, writeFileSync as writeFileSync17 } from "node:fs";
30482
- import { join as join24, resolve as resolve19 } from "node:path";
31070
+ import { existsSync as existsSync23, mkdirSync as mkdirSync17, readFileSync as readFileSync22, readdirSync as readdirSync8, writeFileSync as writeFileSync17 } from "node:fs";
31071
+ import { join as join25, resolve as resolve18 } from "node:path";
30483
31072
  var GOAL_SCHEMA = "kxm.goal.v1";
30484
31073
  var TASK_SCHEMA = "kxm.task.v1";
30485
31074
  function goalsDirectory(repoRoot) {
30486
- return resolve19(repoRoot, ".kxm", "goals");
31075
+ return resolve18(repoRoot, ".kxm", "goals");
30487
31076
  }
30488
31077
  function tasksDirectory(repoRoot) {
30489
- return resolve19(repoRoot, ".kxm", "tasks");
31078
+ return resolve18(repoRoot, ".kxm", "tasks");
30490
31079
  }
30491
31080
  function goalFilePath(repoRoot, goalId) {
30492
- return join24(goalsDirectory(repoRoot), `${goalId}.yaml`);
31081
+ return join25(goalsDirectory(repoRoot), `${goalId}.yaml`);
30493
31082
  }
30494
31083
  function taskFilePath(repoRoot, taskId) {
30495
- return join24(tasksDirectory(repoRoot), `${taskId}.yaml`);
31084
+ return join25(tasksDirectory(repoRoot), `${taskId}.yaml`);
30496
31085
  }
30497
31086
  function createGoal(repoRoot, input, options = {}) {
30498
31087
  const id = `goal_${randomUUID8().replaceAll("-", "").slice(0, 12)}`;
@@ -30515,13 +31104,13 @@ function createGoal(repoRoot, input, options = {}) {
30515
31104
  }
30516
31105
  function listGoals(repoRoot) {
30517
31106
  const dir = goalsDirectory(repoRoot);
30518
- if (!existsSync22(dir)) return [];
31107
+ if (!existsSync23(dir)) return [];
30519
31108
  const goals = [];
30520
31109
  const entries = readdirSync8(dir, { withFileTypes: true });
30521
31110
  for (const entry of entries) {
30522
31111
  if (entry.isFile() && (entry.name.endsWith(".yaml") || entry.name.endsWith(".yml"))) {
30523
31112
  try {
30524
- const text = readFileSync21(join24(dir, entry.name), "utf8");
31113
+ const text = readFileSync22(join25(dir, entry.name), "utf8");
30525
31114
  const doc = (0, import_yaml13.parse)(text);
30526
31115
  if (doc && doc.schema === GOAL_SCHEMA) goals.push(doc);
30527
31116
  } catch {
@@ -30559,13 +31148,13 @@ function createTask(repoRoot, input, options = {}) {
30559
31148
  }
30560
31149
  function listTasks(repoRoot, options = {}) {
30561
31150
  const dir = tasksDirectory(repoRoot);
30562
- if (!existsSync22(dir)) return [];
31151
+ if (!existsSync23(dir)) return [];
30563
31152
  const tasks = [];
30564
31153
  const entries = readdirSync8(dir, { withFileTypes: true });
30565
31154
  for (const entry of entries) {
30566
31155
  if (entry.isFile() && (entry.name.endsWith(".yaml") || entry.name.endsWith(".yml"))) {
30567
31156
  try {
30568
- const text = readFileSync21(join24(dir, entry.name), "utf8");
31157
+ const text = readFileSync22(join25(dir, entry.name), "utf8");
30569
31158
  const doc = (0, import_yaml13.parse)(text);
30570
31159
  if (doc && doc.schema === TASK_SCHEMA) {
30571
31160
  if (options.goalId && doc.goalId !== options.goalId) continue;
@@ -30580,27 +31169,16 @@ function listTasks(repoRoot, options = {}) {
30580
31169
  }
30581
31170
  function getTask(repoRoot, taskId) {
30582
31171
  const dir = tasksDirectory(repoRoot);
30583
- const filePath = join24(dir, `${taskId}.yaml`);
30584
- if (!existsSync22(filePath)) return void 0;
31172
+ const filePath = join25(dir, `${taskId}.yaml`);
31173
+ if (!existsSync23(filePath)) return void 0;
30585
31174
  try {
30586
- const text = readFileSync21(filePath, "utf8");
31175
+ const text = readFileSync22(filePath, "utf8");
30587
31176
  const doc = (0, import_yaml13.parse)(text);
30588
31177
  return doc && doc.schema === TASK_SCHEMA ? doc : void 0;
30589
31178
  } catch {
30590
31179
  return void 0;
30591
31180
  }
30592
31181
  }
30593
- function updateTaskStatus(repoRoot, taskId, status, options = {}) {
30594
- const task = getTask(repoRoot, taskId);
30595
- if (!task) {
30596
- throw new Error(`Task ${taskId} not found`);
30597
- }
30598
- task.status = status;
30599
- if (options.workflowRunId) task.workflowRunId = options.workflowRunId;
30600
- task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
30601
- if (!options.dryRun) writeFileSync17(taskFilePath(repoRoot, taskId), (0, import_yaml13.stringify)(task).trim() + "\n", "utf8");
30602
- return task;
30603
- }
30604
31182
  function syncTaskWithTracker(repoRoot, taskId, options = {}) {
30605
31183
  const task = getTask(repoRoot, taskId);
30606
31184
  if (!task) {
@@ -31380,13 +31958,13 @@ function createStudioServer(options = {}) {
31380
31958
  }
31381
31959
 
31382
31960
  // plugins/kxm/src/cli/project.ts
31383
- import { existsSync as existsSync26 } from "node:fs";
31384
- import { dirname as dirname18, join as join30, resolve as resolve23 } from "node:path";
31961
+ import { existsSync as existsSync27 } from "node:fs";
31962
+ import { dirname as dirname17, join as join31, resolve as resolve22 } from "node:path";
31385
31963
  import { createInterface as createInterface2 } from "node:readline";
31386
31964
 
31387
31965
  // plugins/kxm/src/hub-autostart.ts
31388
- import { closeSync as closeSync2, mkdirSync as mkdirSync18, openSync as openSync2, readFileSync as readFileSync22 } from "node:fs";
31389
- import { join as join25, resolve as resolve20 } from "node:path";
31966
+ import { closeSync as closeSync2, mkdirSync as mkdirSync18, openSync as openSync2, readFileSync as readFileSync23 } from "node:fs";
31967
+ import { join as join26, resolve as resolve19 } from "node:path";
31390
31968
  function defaultProcessExists(pid) {
31391
31969
  try {
31392
31970
  process.kill(pid, 0);
@@ -31398,7 +31976,7 @@ function defaultProcessExists(pid) {
31398
31976
  function readLiveHubClaim(stateDir, processExists3 = defaultProcessExists) {
31399
31977
  let parsed;
31400
31978
  try {
31401
- parsed = JSON.parse(readFileSync22(join25(stateDir, "hub.pid"), "utf8"));
31979
+ parsed = JSON.parse(readFileSync23(join26(stateDir, "hub.pid"), "utf8"));
31402
31980
  } catch {
31403
31981
  return void 0;
31404
31982
  }
@@ -31416,7 +31994,7 @@ function readLiveHubClaim(stateDir, processExists3 = defaultProcessExists) {
31416
31994
  // plugins/kxm/src/model-inventory.ts
31417
31995
  var import_yaml14 = __toESM(require_dist(), 1);
31418
31996
  import { mkdirSync as mkdirSync19, writeFileSync as writeFileSync18 } from "node:fs";
31419
- import { join as join26 } from "node:path";
31997
+ import { join as join27 } from "node:path";
31420
31998
  var OR_URL = "https://openrouter.ai/api/v1/models";
31421
31999
  var NOUS_URL = "https://inference-api.nousresearch.com/v1/models";
31422
32000
  function price(value) {
@@ -31502,41 +32080,41 @@ async function refreshModelInventory(options) {
31502
32080
  sources.nous = { url: nousUrl, ok: !nous.error, ...nous.error ? { error: nous.error } : {} };
31503
32081
  for (const item of nous.data) add(item, "nous", "discount");
31504
32082
  const inventory = { schema: "kxm.model-inventory.v1", fetchedAt, currency: "USD", sources, models: [...byId.values()].sort((a, b2) => a.id.localeCompare(b2.id)) };
31505
- mkdirSync19(join26(options.outputRoot, ".kxm", "models"), { recursive: true });
31506
- writeFileSync18(join26(options.outputRoot, ".kxm", "models", "inventory.yaml"), (0, import_yaml14.stringify)(inventory), "utf8");
32083
+ mkdirSync19(join27(options.outputRoot, ".kxm", "models"), { recursive: true });
32084
+ writeFileSync18(join27(options.outputRoot, ".kxm", "models", "inventory.yaml"), (0, import_yaml14.stringify)(inventory), "utf8");
31507
32085
  return inventory;
31508
32086
  }
31509
32087
 
31510
32088
  // plugins/kxm/src/init.ts
31511
32089
  import { randomUUID as randomUUID11 } from "node:crypto";
31512
- import { existsSync as existsSync25 } from "node:fs";
31513
- import { basename as basename6, join as join29 } from "node:path";
32090
+ import { existsSync as existsSync26 } from "node:fs";
32091
+ import { basename as basename6, join as join30 } from "node:path";
31514
32092
 
31515
32093
  // plugins/kxm/src/repair.ts
31516
32094
  import { randomUUID as randomUUID10 } from "node:crypto";
31517
32095
  import {
31518
32096
  chmodSync as chmodSync5,
31519
32097
  closeSync as closeSync3,
31520
- existsSync as existsSync24,
32098
+ existsSync as existsSync25,
31521
32099
  fsyncSync as fsyncSync2,
31522
32100
  linkSync,
31523
32101
  lstatSync as lstatSync7,
31524
32102
  mkdirSync as mkdirSync21,
31525
32103
  openSync as openSync3,
31526
- readFileSync as readFileSync24,
32104
+ readFileSync as readFileSync25,
31527
32105
  readdirSync as readdirSync9,
31528
32106
  renameSync as renameSync7,
31529
32107
  rmSync as rmSync9,
31530
32108
  writeFileSync as writeFileSync20
31531
32109
  } from "node:fs";
31532
- import { dirname as dirname17, isAbsolute as isAbsolute8, join as join28, relative as relative5, resolve as resolve22 } from "node:path";
32110
+ import { dirname as dirname16, isAbsolute as isAbsolute8, join as join29, relative as relative5, resolve as resolve21 } from "node:path";
31533
32111
 
31534
32112
  // plugins/kxm/src/permission.ts
31535
32113
  import { spawnSync as spawnSync4 } from "node:child_process";
31536
- import { createHash as createHash14 } from "node:crypto";
31537
- import { existsSync as existsSync23, mkdtempSync, mkdirSync as mkdirSync20, readFileSync as readFileSync23, rmSync as rmSync8, writeFileSync as writeFileSync19 } from "node:fs";
32114
+ import { createHash as createHash15 } from "node:crypto";
32115
+ import { existsSync as existsSync24, mkdtempSync, mkdirSync as mkdirSync20, readFileSync as readFileSync24, rmSync as rmSync8, writeFileSync as writeFileSync19 } from "node:fs";
31538
32116
  import { tmpdir } from "node:os";
31539
- import { dirname as dirname16, join as join27, resolve as resolve21, sep as sep4 } from "node:path";
32117
+ import { dirname as dirname15, join as join28, resolve as resolve20, sep as sep4 } from "node:path";
31540
32118
  var PROSE_FIELDS = {
31541
32119
  project: /* @__PURE__ */ new Set(["name", "description"]),
31542
32120
  repository: /* @__PURE__ */ new Set(["description"]),
@@ -31552,7 +32130,7 @@ var UNTRACKED_RANK = { "tracked-only": 0, ask: 1, bounded: 2 };
31552
32130
  function canonical(value) {
31553
32131
  return kxmCanonicalJson(value === void 0 ? null : value);
31554
32132
  }
31555
- function asObject(value) {
32133
+ function asObject2(value) {
31556
32134
  return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
31557
32135
  }
31558
32136
  function valuesOf3(value, key) {
@@ -31568,12 +32146,12 @@ function pointerEscape(segment) {
31568
32146
  function secretGrantKeys(grants) {
31569
32147
  const counts = /* @__PURE__ */ new Map();
31570
32148
  for (const grant of grants) {
31571
- const ref = stringValue3(asObject(grant)?.ref);
32149
+ const ref = stringValue3(asObject2(grant)?.ref);
31572
32150
  if (ref) counts.set(ref, (counts.get(ref) ?? 0) + 1);
31573
32151
  }
31574
32152
  const keys = /* @__PURE__ */ new Map();
31575
32153
  for (const [index, grant] of grants.entries()) {
31576
- const ref = stringValue3(asObject(grant)?.ref);
32154
+ const ref = stringValue3(asObject2(grant)?.ref);
31577
32155
  keys.set(index, ref && counts.get(ref) === 1 ? `by-ref/${pointerEscape(ref)}` : String(index));
31578
32156
  }
31579
32157
  return keys;
@@ -31593,21 +32171,21 @@ function kxmAuthorityEntries(resource) {
31593
32171
  const declarations = valuesOf3(value, "repositories");
31594
32172
  const declarationIds = /* @__PURE__ */ new Map();
31595
32173
  for (const entry of declarations) {
31596
- const id = stringValue3(asObject(entry)?.id);
32174
+ const id = stringValue3(asObject2(entry)?.id);
31597
32175
  if (id) declarationIds.set(id, (declarationIds.get(id) ?? 0) + 1);
31598
32176
  }
31599
32177
  for (const [index, repository] of declarations.entries()) {
31600
- const entry = asObject(repository);
32178
+ const entry = asObject2(repository);
31601
32179
  if (!entry) continue;
31602
32180
  const id = stringValue3(entry.id);
31603
32181
  const key = id && declarationIds.get(id) === 1 ? `by-id/${pointerEscape(id)}` : String(index);
31604
32182
  push(`/repositories/${key}`, "resource-shape", entry);
31605
32183
  }
31606
- const workspace = asObject(value.workspace);
32184
+ const workspace = asObject2(value.workspace);
31607
32185
  if (workspace) push("/workspace", "snapshot-policy", workspace);
31608
- const sync = asObject(value.sync);
32186
+ const sync = asObject2(value.sync);
31609
32187
  if (sync) push("/sync", "sync-policy", sync);
31610
- const limits = asObject(value.limits);
32188
+ const limits = asObject2(value.limits);
31611
32189
  if (limits) push("/limits", "budget", limits);
31612
32190
  break;
31613
32191
  }
@@ -31625,7 +32203,7 @@ function kxmAuthorityEntries(resource) {
31625
32203
  push("/harness", "harness", value.harness ?? "pi");
31626
32204
  push("/tools", "tools", value.tools);
31627
32205
  push("/defaultRepositoryAccess", "repository-access", value.defaultRepositoryAccess);
31628
- const repositories = asObject(value.repositories);
32206
+ const repositories = asObject2(value.repositories);
31629
32207
  for (const repositoryId of Object.keys(repositories ?? {}).sort()) {
31630
32208
  push(`/repositories/${pointerEscape(repositoryId)}`, "repository-access", repositories?.[repositoryId]);
31631
32209
  }
@@ -31635,7 +32213,7 @@ function kxmAuthorityEntries(resource) {
31635
32213
  }
31636
32214
  push("/network", "network", value.network);
31637
32215
  push("/resultSchema", "delivery", value.resultSchema);
31638
- const session = asObject(value.session);
32216
+ const session = asObject2(value.session);
31639
32217
  if (session) push("/session", "resource-shape", session);
31640
32218
  break;
31641
32219
  }
@@ -31647,15 +32225,15 @@ function kxmAuthorityEntries(resource) {
31647
32225
  push("/capabilities", "model", value.capabilities);
31648
32226
  push("/priority", "model", value.priority);
31649
32227
  push("/fallbacks", "model", value.fallbacks);
31650
- const limits = asObject(value.limits);
32228
+ const limits = asObject2(value.limits);
31651
32229
  if (limits) push("/limits", "budget", limits);
31652
32230
  break;
31653
32231
  }
31654
32232
  case "environment": {
31655
32233
  push("/path", "environment", value.path);
31656
- const envValues = asObject(value.values);
32234
+ const envValues = asObject2(value.values);
31657
32235
  for (const key of Object.keys(envValues ?? {}).sort()) {
31658
- const entry = asObject(envValues?.[key]);
32236
+ const entry = asObject2(envValues?.[key]);
31659
32237
  const projected = entry ? { ...entry, value: entry.value === void 0 ? void 0 : "<redacted>" } : envValues?.[key];
31660
32238
  push(`/values/${pointerEscape(key)}`, "environment", projected);
31661
32239
  }
@@ -31667,9 +32245,9 @@ function kxmAuthorityEntries(resource) {
31667
32245
  }
31668
32246
  case "gate-registry": {
31669
32247
  push("/schema", "resource-shape", value.schema);
31670
- const gates = asObject(value.gates) ?? {};
32248
+ const gates = asObject2(value.gates) ?? {};
31671
32249
  for (const id of Object.keys(gates).sort()) {
31672
- const definition = asObject(gates[id]);
32250
+ const definition = asObject2(gates[id]);
31673
32251
  const path4 = `/gates/${pointerEscape(id)}`;
31674
32252
  if (definition?.kind === "command") {
31675
32253
  const { timeoutMs, ...authority } = definition;
@@ -31681,13 +32259,13 @@ function kxmAuthorityEntries(resource) {
31681
32259
  }
31682
32260
  case "workflow": {
31683
32261
  push("/coordinator", "resource-shape", value.coordinator);
31684
- const limits = asObject(value.limits);
32262
+ const limits = asObject2(value.limits);
31685
32263
  if (limits) push("/limits", "budget", limits);
31686
32264
  if (value.reproOracle !== void 0) push("/reproOracle", "evidence-quorum", value.reproOracle);
31687
32265
  if (value.planHash !== void 0) push("/planHash", "evidence-quorum", value.planHash);
31688
32266
  if (value.requirePlanHash !== void 0) push("/requirePlanHash", "evidence-quorum", value.requirePlanHash);
31689
32267
  for (const [index, stepEntry] of valuesOf3(value, "steps").entries()) {
31690
- const step = asObject(stepEntry);
32268
+ const step = asObject2(stepEntry);
31691
32269
  if (!step) continue;
31692
32270
  const stepPath = `/steps/${index}`;
31693
32271
  push(`${stepPath}/id`, "resource-shape", step.id);
@@ -31699,7 +32277,7 @@ function kxmAuthorityEntries(resource) {
31699
32277
  push(`${stepPath}/model`, "model", step.model);
31700
32278
  push(`${stepPath}/maxAttempts`, "budget", step.maxAttempts);
31701
32279
  push(`${stepPath}/timeoutMs`, "budget", step.timeoutMs);
31702
- const repositories = asObject(step.repositories);
32280
+ const repositories = asObject2(step.repositories);
31703
32281
  for (const repositoryId of Object.keys(repositories ?? {}).sort()) {
31704
32282
  push(`${stepPath}/repositories/${pointerEscape(repositoryId)}`, "repository-access", repositories?.[repositoryId]);
31705
32283
  }
@@ -31729,7 +32307,7 @@ function kxmProseEntries(resource) {
31729
32307
  }
31730
32308
  if (resource.kind === "workflow") {
31731
32309
  for (const [index, stepEntry] of valuesOf3(resource.value, "steps").entries()) {
31732
- const step = asObject(stepEntry);
32310
+ const step = asObject2(stepEntry);
31733
32311
  if (!step) continue;
31734
32312
  for (const field of ["description", "instructions"]) {
31735
32313
  if (step[field] !== void 0) entries.set(`/steps/${index}/${field}`, canonical(step[field]));
@@ -31739,7 +32317,7 @@ function kxmProseEntries(resource) {
31739
32317
  return entries;
31740
32318
  }
31741
32319
  function valueHash(canonicalValue) {
31742
- return `sha256:${createHash14("sha256").update(canonicalValue, "utf8").digest("hex")}`;
32320
+ return `sha256:${createHash15("sha256").update(canonicalValue, "utf8").digest("hex")}`;
31743
32321
  }
31744
32322
  function rankDirection(rank, baseValue, candidateValue) {
31745
32323
  const base = JSON.parse(baseValue);
@@ -31760,8 +32338,8 @@ function numericBoundDirection(baseValue, candidateValue) {
31760
32338
  return "neutral";
31761
32339
  }
31762
32340
  function budgetDirection(baseValue, candidateValue) {
31763
- const base = asObject(JSON.parse(baseValue));
31764
- const candidate = asObject(JSON.parse(candidateValue));
32341
+ const base = asObject2(JSON.parse(baseValue));
32342
+ const candidate = asObject2(JSON.parse(candidateValue));
31765
32343
  if (!base || !candidate) return "expansion";
31766
32344
  let sawExpansion = false;
31767
32345
  let sawNarrowing = false;
@@ -31774,8 +32352,8 @@ function budgetDirection(baseValue, candidateValue) {
31774
32352
  return sawNarrowing ? "narrowing" : "neutral";
31775
32353
  }
31776
32354
  function quorumDirection(baseValue, candidateValue) {
31777
- const base = asObject(JSON.parse(baseValue));
31778
- const candidate = asObject(JSON.parse(candidateValue));
32355
+ const base = asObject2(JSON.parse(baseValue));
32356
+ const candidate = asObject2(JSON.parse(candidateValue));
31779
32357
  if (!base || !candidate) return "expansion";
31780
32358
  if (base.strategy !== void 0 || candidate.strategy !== void 0) return "expansion";
31781
32359
  if (base.stageId !== void 0 || candidate.stageId !== void 0) return "expansion";
@@ -31789,13 +32367,13 @@ function quorumDirection(baseValue, candidateValue) {
31789
32367
  const CLASSIFIED = /* @__PURE__ */ new Set(["key", "kind", "minimum", "reusableAcrossAttempts", "strategy", "stageId", "evidenceKey"]);
31790
32368
  const CLASSIFIED_POLICY = /* @__PURE__ */ new Set(["minimumProducers", "eligibleAgents", "degradation"]);
31791
32369
  const residualOf = (value) => {
31792
- const parsed = asObject(JSON.parse(value));
32370
+ const parsed = asObject2(JSON.parse(value));
31793
32371
  if (!parsed) return null;
31794
32372
  const residual = {};
31795
32373
  for (const [key, entry] of Object.entries(parsed)) {
31796
32374
  if (CLASSIFIED.has(key)) continue;
31797
32375
  if (key === "producerPolicy") {
31798
- const policy = asObject(entry);
32376
+ const policy = asObject2(entry);
31799
32377
  if (!policy) {
31800
32378
  residual[key] = entry;
31801
32379
  continue;
@@ -31808,8 +32386,8 @@ function quorumDirection(baseValue, candidateValue) {
31808
32386
  return residual;
31809
32387
  };
31810
32388
  if (kxmCanonicalJson(residualOf(baseValue)) !== kxmCanonicalJson(residualOf(candidateValue))) return "expansion";
31811
- const basePolicy = asObject(base.producerPolicy);
31812
- const candidatePolicy = asObject(candidate.producerPolicy);
32389
+ const basePolicy = asObject2(base.producerPolicy);
32390
+ const candidatePolicy = asObject2(candidate.producerPolicy);
31813
32391
  if (!basePolicy && !candidatePolicy) return sawNarrowing ? "narrowing" : "neutral";
31814
32392
  if (!basePolicy || !candidatePolicy) return "expansion";
31815
32393
  const basePolicyMinimum = typeof basePolicy.minimumProducers === "number" ? basePolicy.minimumProducers : 1;
@@ -31818,8 +32396,8 @@ function quorumDirection(baseValue, candidateValue) {
31818
32396
  const baseEligible = new Set((Array.isArray(basePolicy.eligibleAgents) ? basePolicy.eligibleAgents : []).map(String));
31819
32397
  const candidateEligible = new Set((Array.isArray(candidatePolicy.eligibleAgents) ? candidatePolicy.eligibleAgents : []).map(String));
31820
32398
  for (const agent of baseEligible) if (!candidateEligible.has(agent)) return "expansion";
31821
- const baseDegradation = asObject(basePolicy.degradation);
31822
- const candidateDegradation = asObject(candidatePolicy.degradation);
32399
+ const baseDegradation = asObject2(basePolicy.degradation);
32400
+ const candidateDegradation = asObject2(candidatePolicy.degradation);
31823
32401
  if (!baseDegradation && candidateDegradation) return "expansion";
31824
32402
  if (baseDegradation && candidateDegradation) {
31825
32403
  const baseFloor = typeof baseDegradation.minimumProducers === "number" ? baseDegradation.minimumProducers : 1;
@@ -31833,11 +32411,11 @@ function quorumDirection(baseValue, candidateValue) {
31833
32411
  return sawNarrowing ? "narrowing" : "neutral";
31834
32412
  }
31835
32413
  function snapshotDirection(baseValue, candidateValue) {
31836
- const base = asObject(JSON.parse(baseValue));
31837
- const candidate = asObject(JSON.parse(candidateValue));
32414
+ const base = asObject2(JSON.parse(baseValue));
32415
+ const candidate = asObject2(JSON.parse(candidateValue));
31838
32416
  if (!base || !candidate) return "expansion";
31839
- const baseSnapshot = asObject(base.dirtySnapshot);
31840
- const candidateSnapshot = asObject(candidate.dirtySnapshot);
32417
+ const baseSnapshot = asObject2(base.dirtySnapshot);
32418
+ const candidateSnapshot = asObject2(candidate.dirtySnapshot);
31841
32419
  if (!baseSnapshot || !candidateSnapshot) return "expansion";
31842
32420
  const untracked = rankDirection(UNTRACKED_RANK, canonical(baseSnapshot.untracked), canonical(candidateSnapshot.untracked));
31843
32421
  if (untracked === "expansion") return "expansion";
@@ -31891,8 +32469,8 @@ function classifyChange(field, baseValue, candidateValue, context) {
31891
32469
  case "snapshot-policy":
31892
32470
  return snapshotDirection(baseValue, candidateValue);
31893
32471
  case "secrets": {
31894
- const base = asObject(JSON.parse(baseValue));
31895
- const candidate = asObject(JSON.parse(candidateValue));
32472
+ const base = asObject2(JSON.parse(baseValue));
32473
+ const candidate = asObject2(JSON.parse(candidateValue));
31896
32474
  if (base && candidate && base.ref === candidate.ref) {
31897
32475
  const { required: _b, ...baseRest } = base;
31898
32476
  const { required: _c, ...candidateRest } = candidate;
@@ -32057,9 +32635,9 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
32057
32635
  const tree = resolveTreeRevision(root, revision);
32058
32636
  const listing = gitBuffer(root, ["ls-tree", "-r", "-z", "--name-only", tree]).toString("utf8");
32059
32637
  const paths = listing.split("\0").filter((line) => line.length > 0 && (line.startsWith(".kxm/") || line.includes("/.kxm/")));
32060
- const shadow = mkdtempSync(join27(tmpdir(), "kxm-permission-base-"));
32061
- const shadowResolved = resolve21(shadow);
32062
- const shadowMembersDir = `.kxm-shadow-members-${createHash14("sha256").update(shadowResolved, "utf8").digest("hex").slice(0, 8)}`;
32638
+ const shadow = mkdtempSync(join28(tmpdir(), "kxm-permission-base-"));
32639
+ const shadowResolved = resolve20(shadow);
32640
+ const shadowMembersDir = `.kxm-shadow-members-${createHash15("sha256").update(shadowResolved, "utf8").digest("hex").slice(0, 8)}`;
32063
32641
  try {
32064
32642
  for (const path4 of paths) {
32065
32643
  const segments = path4.split("/");
@@ -32071,8 +32649,8 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
32071
32649
  message: "Git tree entry contains a traversal or non-portable segment"
32072
32650
  }]);
32073
32651
  }
32074
- const absolute = join27(shadowResolved, ...segments);
32075
- const resolved = resolve21(absolute);
32652
+ const absolute = join28(shadowResolved, ...segments);
32653
+ const resolved = resolve20(absolute);
32076
32654
  if (!resolved.startsWith(`${shadowResolved}${sep4}`)) {
32077
32655
  throw new KxmConfigError([{
32078
32656
  phase: "path",
@@ -32082,11 +32660,11 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
32082
32660
  }]);
32083
32661
  }
32084
32662
  const content = gitBuffer(root, ["cat-file", "blob", `${tree}:${path4}`]);
32085
- mkdirSync20(dirname16(absolute), { recursive: true });
32663
+ mkdirSync20(dirname15(absolute), { recursive: true });
32086
32664
  writeFileSync19(absolute, content);
32087
32665
  }
32088
32666
  initShadowGitRoot(shadowResolved);
32089
- const baseProjectFile = join27(shadowResolved, ".kxm", "project.yaml");
32667
+ const baseProjectFile = join28(shadowResolved, ".kxm", "project.yaml");
32090
32668
  let baseProject;
32091
32669
  try {
32092
32670
  baseProject = loadBaseProjectDeclarations(baseProjectFile);
@@ -32116,8 +32694,8 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
32116
32694
  }]);
32117
32695
  }
32118
32696
  const boundPath = options.repositoryBindings?.[member.repositoryId];
32119
- const memberWorktree = boundPath ? resolve21(boundPath) : member.pathHint ? resolve21(root, ...member.pathHint.split("/")) : void 0;
32120
- if (!memberWorktree || !existsSync23(memberWorktree)) {
32697
+ const memberWorktree = boundPath ? resolve20(boundPath) : member.pathHint ? resolve20(root, ...member.pathHint.split("/")) : void 0;
32698
+ if (!memberWorktree || !existsSync24(memberWorktree)) {
32121
32699
  if (member.required === false) continue;
32122
32700
  throw new KxmConfigError([{
32123
32701
  phase: "discovery",
@@ -32164,8 +32742,8 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
32164
32742
  const memberTree = git(memberWorktree, ["rev-parse", "--verify", "--quiet", `${pinnedCommit}^{tree}`]).trim();
32165
32743
  const memberListing = gitBuffer(memberWorktree, ["ls-tree", "-r", "-z", "--name-only", memberTree]).toString("utf8");
32166
32744
  const memberPaths = memberListing.split("\0").filter((line) => line.length > 0 && (line.startsWith(".kxm/") || line.includes("/.kxm/")));
32167
- const memberShadowRoot = join27(shadowResolved, shadowMembersDir, member.repositoryId);
32168
- if (!resolve21(memberShadowRoot).startsWith(`${resolve21(shadowResolved, shadowMembersDir)}${sep4}`)) {
32745
+ const memberShadowRoot = join28(shadowResolved, shadowMembersDir, member.repositoryId);
32746
+ if (!resolve20(memberShadowRoot).startsWith(`${resolve20(shadowResolved, shadowMembersDir)}${sep4}`)) {
32169
32747
  throw new KxmConfigError([{
32170
32748
  phase: "path",
32171
32749
  code: "git_tree_path_invalid",
@@ -32178,13 +32756,13 @@ function loadKxmProjectAtRevision(root, revision, options = {}) {
32178
32756
  if (segments.some((segment) => segment === "" || segment === "." || segment === ".." || /[\\:]/.test(segment))) {
32179
32757
  throw new KxmConfigError([{ phase: "path", code: "git_tree_path_invalid", file: path4, message: "member Git tree entry contains a traversal or non-portable segment" }]);
32180
32758
  }
32181
- const absolute = join27(memberShadowRoot, ...segments);
32182
- const resolved = resolve21(absolute);
32183
- if (!resolved.startsWith(`${resolve21(memberShadowRoot)}${sep4}`)) {
32759
+ const absolute = join28(memberShadowRoot, ...segments);
32760
+ const resolved = resolve20(absolute);
32761
+ if (!resolved.startsWith(`${resolve20(memberShadowRoot)}${sep4}`)) {
32184
32762
  throw new KxmConfigError([{ phase: "path", code: "git_tree_path_invalid", file: path4, message: "member Git tree entry escapes the shadow root" }]);
32185
32763
  }
32186
32764
  const content = gitBuffer(memberWorktree, ["cat-file", "blob", `${memberTree}:${path4}`]);
32187
- mkdirSync20(dirname16(absolute), { recursive: true });
32765
+ mkdirSync20(dirname15(absolute), { recursive: true });
32188
32766
  writeFileSync19(absolute, content);
32189
32767
  }
32190
32768
  initShadowGitRoot(memberShadowRoot);
@@ -32206,8 +32784,8 @@ function portableMemberPathHint(pathHint) {
32206
32784
  return pathHint !== "." && kxmPortablePath(pathHint);
32207
32785
  }
32208
32786
  function loadBaseProjectDeclarations(projectFile) {
32209
- if (!existsSync23(projectFile)) return [];
32210
- const value = parseRestrictedYaml2(readFileSync23(projectFile, "utf8"), ".kxm/project.yaml");
32787
+ if (!existsSync24(projectFile)) return [];
32788
+ const value = parseRestrictedYaml2(readFileSync24(projectFile, "utf8"), ".kxm/project.yaml");
32211
32789
  const repositories = Array.isArray(value?.repositories) ? value.repositories : [];
32212
32790
  const members = [];
32213
32791
  for (const entry of repositories) {
@@ -32310,7 +32888,7 @@ function writeDurableNew(file, bytes, mode) {
32310
32888
  } finally {
32311
32889
  if (descriptor !== void 0) closeSync3(descriptor);
32312
32890
  }
32313
- syncDirectory2(dirname17(file));
32891
+ syncDirectory2(dirname16(file));
32314
32892
  }
32315
32893
  function writeDurable(file, bytes, mode) {
32316
32894
  let descriptor;
@@ -32327,27 +32905,27 @@ function writeDurable(file, bytes, mode) {
32327
32905
  } finally {
32328
32906
  if (descriptor !== void 0) closeSync3(descriptor);
32329
32907
  }
32330
- syncDirectory2(dirname17(file));
32908
+ syncDirectory2(dirname16(file));
32331
32909
  }
32332
32910
  function sameHostPath3(left, right) {
32333
- const first = resolve22(left);
32334
- const second = resolve22(right);
32911
+ const first = resolve21(left);
32912
+ const second = resolve21(right);
32335
32913
  return process.platform === "win32" ? first.toLocaleLowerCase("en-US") === second.toLocaleLowerCase("en-US") : first === second;
32336
32914
  }
32337
32915
  function transactionRoot(projectRoot) {
32338
- return join28(resolve22(projectRoot), TRANSACTION_NAME);
32916
+ return join29(resolve21(projectRoot), TRANSACTION_NAME);
32339
32917
  }
32340
32918
  function operationFile(projectRoot) {
32341
- return join28(transactionRoot(projectRoot), OPERATION_FILE);
32919
+ return join29(transactionRoot(projectRoot), OPERATION_FILE);
32342
32920
  }
32343
32921
  function targetFile(projectRoot, portablePath2) {
32344
- return join28(transactionRoot(projectRoot), "targets", ...portablePath2.split("/"));
32922
+ return join29(transactionRoot(projectRoot), "targets", ...portablePath2.split("/"));
32345
32923
  }
32346
32924
  function shadowRoot(projectRoot) {
32347
- return join28(transactionRoot(projectRoot), "shadow");
32925
+ return join29(transactionRoot(projectRoot), "shadow");
32348
32926
  }
32349
32927
  function backupFile(projectRoot, portablePath2) {
32350
- return join28(transactionRoot(projectRoot), "backups", ...portablePath2.split("/"));
32928
+ return join29(transactionRoot(projectRoot), "backups", ...portablePath2.split("/"));
32351
32929
  }
32352
32930
  function portableManagedPath(path4) {
32353
32931
  return path4.startsWith(".kxm/") && path4 !== KXM_TEMPLATE_PROVENANCE_PATH && path4.length <= 1024 && !path4.includes("\\") && !isAbsolute8(path4) && PORTABLE_PATH.test(path4) && !/[<>:"|?*]/.test(path4);
@@ -32356,24 +32934,24 @@ function readRegularBounded(file, label) {
32356
32934
  const stat = lstatSync7(file);
32357
32935
  if (stat.isSymbolicLink() || !stat.isFile()) fail3("repair_file_invalid", label, "managed file must be a regular file, not a link or directory", "path");
32358
32936
  if (stat.size > MAX_MANAGED_FILE_BYTES) fail3("repair_file_too_large", label, `managed file exceeds ${MAX_MANAGED_FILE_BYTES} bytes`, "parse");
32359
- return readFileSync24(file);
32937
+ return readFileSync25(file);
32360
32938
  }
32361
32939
  function readOptionalManaged(projectRoot, path4) {
32362
- const file = join28(projectRoot, ...path4.split("/"));
32363
- if (!existsSync24(file)) return void 0;
32364
- let parent = dirname17(file);
32365
- const boundary = resolve22(projectRoot);
32940
+ const file = join29(projectRoot, ...path4.split("/"));
32941
+ if (!existsSync25(file)) return void 0;
32942
+ let parent = dirname16(file);
32943
+ const boundary = resolve21(projectRoot);
32366
32944
  while (parent !== boundary) {
32367
32945
  const stat = lstatSync7(parent);
32368
32946
  if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_parent_invalid", path4, "managed file parents must be regular directories", "path");
32369
- parent = dirname17(parent);
32947
+ parent = dirname16(parent);
32370
32948
  }
32371
32949
  return readRegularBounded(file, path4);
32372
32950
  }
32373
32951
  function readProjectAndProvenance(projectRoot, schemasDir) {
32374
- const projectFile = join28(projectRoot, ".kxm", "project.yaml");
32375
- const provenanceFile = join28(projectRoot, ...KXM_TEMPLATE_PROVENANCE_PATH.split("/"));
32376
- if (!existsSync24(projectFile) || !existsSync24(provenanceFile)) return void 0;
32952
+ const projectFile = join29(projectRoot, ".kxm", "project.yaml");
32953
+ const provenanceFile = join29(projectRoot, ...KXM_TEMPLATE_PROVENANCE_PATH.split("/"));
32954
+ if (!existsSync25(projectFile) || !existsSync25(provenanceFile)) return void 0;
32377
32955
  const projectBytes = readOptionalManaged(projectRoot, ".kxm/project.yaml");
32378
32956
  const provenanceBytes = readOptionalManaged(projectRoot, KXM_TEMPLATE_PROVENANCE_PATH);
32379
32957
  if (!projectBytes || !provenanceBytes) return void 0;
@@ -32409,7 +32987,7 @@ function readProjectAndProvenance(projectRoot, schemasDir) {
32409
32987
  }
32410
32988
  function planKxmTemplateRepair(projectRoot, options = {}) {
32411
32989
  assertNoRegisteredGates(options);
32412
- const root = resolve22(projectRoot);
32990
+ const root = resolve21(projectRoot);
32413
32991
  const source = readProjectAndProvenance(root, options.schemasDir);
32414
32992
  if (!source) return void 0;
32415
32993
  const target = renderKxmTemplate(
@@ -32537,7 +33115,7 @@ function operationPlanSha(operation) {
32537
33115
  function writeOperation(projectRoot, operation) {
32538
33116
  const root = transactionRoot(projectRoot);
32539
33117
  const file = operationFile(projectRoot);
32540
- const temporary = join28(root, `.operation-${operation.operationId}-${randomUUID10()}.tmp`);
33118
+ const temporary = join29(root, `.operation-${operation.operationId}-${randomUUID10()}.tmp`);
32541
33119
  try {
32542
33120
  writeDurableNew(temporary, Buffer.from(`${JSON.stringify(operation, null, 2)}
32543
33121
  `, "utf8"), 384);
@@ -32554,11 +33132,11 @@ function updatePhase(projectRoot, operation, phase) {
32554
33132
  }
32555
33133
  function readOperation(projectRoot, schemasDir) {
32556
33134
  const root = transactionRoot(projectRoot);
32557
- if (!existsSync24(root)) return void 0;
33135
+ if (!existsSync25(root)) return void 0;
32558
33136
  const stat = lstatSync7(root);
32559
33137
  if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("init_transaction_invalid", TRANSACTION_NAME, "initialization transaction must be a regular directory", "path");
32560
33138
  const file = operationFile(projectRoot);
32561
- if (!existsSync24(file)) {
33139
+ if (!existsSync25(file)) {
32562
33140
  const entries = readdirSync9(root, { withFileTypes: true });
32563
33141
  const recoverable = entries.every((entry) => entry.isFile() && /^\.operation-op_[a-f0-9]{32}-[a-f0-9-]{36}\.tmp$/.test(entry.name));
32564
33142
  if (recoverable) return void 0;
@@ -32586,15 +33164,15 @@ function readOperation(projectRoot, schemasDir) {
32586
33164
  return operation;
32587
33165
  }
32588
33166
  function hasKxmInitTransaction(projectRoot) {
32589
- return existsSync24(transactionRoot(projectRoot));
33167
+ return existsSync25(transactionRoot(projectRoot));
32590
33168
  }
32591
33169
  function readTarget(projectRoot, path4) {
32592
- return readOptionalManaged(join28(transactionRoot(projectRoot), "targets"), path4);
33170
+ return readOptionalManaged(join29(transactionRoot(projectRoot), "targets"), path4);
32593
33171
  }
32594
33172
  function writeTarget(projectRoot, path4, bytes) {
32595
33173
  if (bytes.byteLength > MAX_MANAGED_FILE_BYTES) fail3("template_target_too_large", path4, `target exceeds ${MAX_MANAGED_FILE_BYTES} bytes`);
32596
33174
  const file = targetFile(projectRoot, path4);
32597
- mkdirSync21(dirname17(file), { recursive: true, mode: 448 });
33175
+ mkdirSync21(dirname16(file), { recursive: true, mode: 448 });
32598
33176
  writeDurableNew(file, bytes, 384);
32599
33177
  }
32600
33178
  function writeAndVerifyBackups(projectRoot, operation) {
@@ -32606,7 +33184,7 @@ function writeAndVerifyBackups(projectRoot, operation) {
32606
33184
  fail3("repair_preimage_changed", entry.path, "destination changed before transaction preparation; local bytes were preserved");
32607
33185
  }
32608
33186
  const file = backupFile(projectRoot, entry.path);
32609
- mkdirSync21(dirname17(file), { recursive: true, mode: 448 });
33187
+ mkdirSync21(dirname16(file), { recursive: true, mode: 448 });
32610
33188
  writeDurableNew(file, current, 384);
32611
33189
  }
32612
33190
  }
@@ -32621,7 +33199,7 @@ function writeKnownBackups(projectRoot, operation) {
32621
33199
  fail3("init_transaction_backup_source_invalid", entry.path, "supported source template cannot reproduce the pinned preimage");
32622
33200
  }
32623
33201
  const file = backupFile(projectRoot, entry.path);
32624
- mkdirSync21(dirname17(file), { recursive: true, mode: 448 });
33202
+ mkdirSync21(dirname16(file), { recursive: true, mode: 448 });
32625
33203
  writeDurableNew(file, bytes, 384);
32626
33204
  }
32627
33205
  }
@@ -32629,7 +33207,7 @@ function verifyBackups(projectRoot, operation) {
32629
33207
  if (operation.kind !== "repair") return;
32630
33208
  for (const entry of operation.files) {
32631
33209
  if (entry.observedSha256 === null) continue;
32632
- const bytes = readOptionalManaged(join28(transactionRoot(projectRoot), "backups"), entry.path);
33210
+ const bytes = readOptionalManaged(join29(transactionRoot(projectRoot), "backups"), entry.path);
32633
33211
  if (!bytes || kxmContentSha256(bytes) !== entry.observedSha256) {
32634
33212
  fail3("init_transaction_backup_invalid", entry.path, "pinned preimage backup is missing or corrupt");
32635
33213
  }
@@ -32663,10 +33241,10 @@ function sourceConfigFiles(projectRoot) {
32663
33241
  ".kxm/repo/repo.yaml",
32664
33242
  ".kxm/repo/env.yaml"
32665
33243
  ];
32666
- const files = fixed.filter((path4) => existsSync24(join28(projectRoot, ...path4.split("/"))));
33244
+ const files = fixed.filter((path4) => existsSync25(join29(projectRoot, ...path4.split("/"))));
32667
33245
  for (const directory of ["agents", "models", "workflows"]) {
32668
- const absolute = join28(projectRoot, ".kxm", directory);
32669
- if (!existsSync24(absolute)) continue;
33246
+ const absolute = join29(projectRoot, ".kxm", directory);
33247
+ if (!existsSync25(absolute)) continue;
32670
33248
  const stat = lstatSync7(absolute);
32671
33249
  if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_directory_invalid", `.kxm/${directory}`, "configuration directory must be regular", "path");
32672
33250
  for (const entry of readdirSync9(absolute, { withFileTypes: true })) {
@@ -32687,8 +33265,8 @@ function absoluteMemberBindings(projectRoot, project, explicit = {}) {
32687
33265
  const repository = candidate;
32688
33266
  if (repository.role !== "member" || typeof repository.id !== "string" || Object.hasOwn(bindings, repository.id)) continue;
32689
33267
  if (typeof repository.pathHint === "string") {
32690
- const candidate2 = resolve22(projectRoot, ...repository.pathHint.split("/"));
32691
- if (existsSync24(candidate2)) bindings[repository.id] = candidate2;
33268
+ const candidate2 = resolve21(projectRoot, ...repository.pathHint.split("/"));
33269
+ if (existsSync25(candidate2)) bindings[repository.id] = candidate2;
32692
33270
  }
32693
33271
  }
32694
33272
  return bindings;
@@ -32702,9 +33280,9 @@ function prepareShadow(projectRoot, operation, options) {
32702
33280
  const bytes = readOptionalManaged(projectRoot, path4);
32703
33281
  if (!bytes) fail3("repair_snapshot_file_missing", path4, "configuration changed during shadow preparation");
32704
33282
  total += bytes.byteLength;
32705
- const source = join28(projectRoot, ...path4.split("/"));
32706
- const destination = join28(shadow, ...path4.split("/"));
32707
- mkdirSync21(dirname17(destination), { recursive: true, mode: 448 });
33283
+ const source = join29(projectRoot, ...path4.split("/"));
33284
+ const destination = join29(shadow, ...path4.split("/"));
33285
+ mkdirSync21(dirname16(destination), { recursive: true, mode: 448 });
32708
33286
  writeDurableNew(destination, bytes, lstatSync7(source).mode & 511);
32709
33287
  if (total > MAX_MANAGED_TOTAL_BYTES) fail3("repair_snapshot_too_large", ".kxm", `configuration snapshot exceeds ${MAX_MANAGED_TOTAL_BYTES} bytes`, "parse");
32710
33288
  }
@@ -32712,12 +33290,12 @@ function prepareShadow(projectRoot, operation, options) {
32712
33290
  for (const entry of operation.files) {
32713
33291
  const bytes = readTarget(projectRoot, entry.path);
32714
33292
  if (!bytes) fail3("init_transaction_target_missing", entry.path, "pinned target artifact is missing");
32715
- const destination = join28(shadow, ...entry.path.split("/"));
32716
- mkdirSync21(dirname17(destination), { recursive: true, mode: 448 });
32717
- const mode = existsSync24(destination) ? lstatSync7(destination).mode & 511 : 420;
33293
+ const destination = join29(shadow, ...entry.path.split("/"));
33294
+ mkdirSync21(dirname16(destination), { recursive: true, mode: 448 });
33295
+ const mode = existsSync25(destination) ? lstatSync7(destination).mode & 511 : 420;
32718
33296
  writeDurable(destination, bytes, mode);
32719
33297
  }
32720
- const project = parseRestrictedYaml2(readFileSync24(join28(shadow, ".kxm", "project.yaml")), ".kxm/project.yaml");
33298
+ const project = parseRestrictedYaml2(readFileSync25(join29(shadow, ".kxm", "project.yaml")), ".kxm/project.yaml");
32721
33299
  const bound = absoluteMemberBindings(projectRoot, project, options.repositoryBindings);
32722
33300
  loadKxmProject(shadow, { ...loaderOptions(options), repositoryBindings: bound });
32723
33301
  const provenance = readProjectAndProvenance(shadow, options.schemasDir)?.provenance;
@@ -32726,7 +33304,7 @@ function prepareShadow(projectRoot, operation, options) {
32726
33304
  }
32727
33305
  }
32728
33306
  function createOperation(projectRoot, kind, rendered, files, sourceTemplateRevision) {
32729
- const root = resolve22(projectRoot);
33307
+ const root = resolve21(projectRoot);
32730
33308
  const operationWithoutHash = {
32731
33309
  schema: "kxm.init-operation.v1",
32732
33310
  operationId: `op_${randomUUID10().replaceAll("-", "")}`,
@@ -32755,7 +33333,7 @@ function prepareOperation(projectRoot, operation, rendered, options) {
32755
33333
  const transaction = transactionRoot(projectRoot);
32756
33334
  validateOperationRecord(operation, options.schemasDir);
32757
33335
  validateOperationIntent(projectRoot, operation);
32758
- if (existsSync24(transaction)) fail3("init_transaction_exists", TRANSACTION_NAME, "an initialization transaction already exists and must be resumed");
33336
+ if (existsSync25(transaction)) fail3("init_transaction_exists", TRANSACTION_NAME, "an initialization transaction already exists and must be resumed");
32759
33337
  mkdirSync21(transaction, { mode: 448 });
32760
33338
  syncDirectory2(projectRoot);
32761
33339
  writeOperation(projectRoot, operation);
@@ -32777,25 +33355,25 @@ function prepareOperation(projectRoot, operation, rendered, options) {
32777
33355
  }
32778
33356
  }
32779
33357
  function ensureDestinationParents(projectRoot, path4) {
32780
- const boundary = resolve22(projectRoot);
33358
+ const boundary = resolve21(projectRoot);
32781
33359
  const segments = path4.split("/").slice(0, -1);
32782
33360
  let current = boundary;
32783
33361
  for (const segment of segments) {
32784
- current = join28(current, segment);
32785
- if (!existsSync24(current)) mkdirSync21(current, { mode: 448 });
33362
+ current = join29(current, segment);
33363
+ if (!existsSync25(current)) mkdirSync21(current, { mode: 448 });
32786
33364
  const stat = lstatSync7(current);
32787
33365
  if (stat.isSymbolicLink() || !stat.isDirectory()) fail3("repair_destination_parent_invalid", path4, "destination parent must be a regular directory", "path");
32788
33366
  }
32789
33367
  }
32790
33368
  function destinationSha(projectRoot, path4) {
32791
- const file = join28(projectRoot, ...path4.split("/"));
32792
- if (!existsSync24(file)) return void 0;
33369
+ const file = join29(projectRoot, ...path4.split("/"));
33370
+ if (!existsSync25(file)) return void 0;
32793
33371
  return kxmContentSha256(readRegularBounded(file, path4));
32794
33372
  }
32795
33373
  function requireConditionalLinkSupport(directory, operationId, path4) {
32796
33374
  const nonce = randomUUID10();
32797
- const source = join28(directory, `.kxm-repair-${operationId}-${nonce}.link-source`);
32798
- const target = join28(directory, `.kxm-repair-${operationId}-${nonce}.link-target`);
33375
+ const source = join29(directory, `.kxm-repair-${operationId}-${nonce}.link-source`);
33376
+ const target = join29(directory, `.kxm-repair-${operationId}-${nonce}.link-target`);
32799
33377
  try {
32800
33378
  writeDurableNew(source, Buffer.from("kxm-link-probe\n", "utf8"), 384);
32801
33379
  try {
@@ -32810,19 +33388,19 @@ function requireConditionalLinkSupport(directory, operationId, path4) {
32810
33388
  }
32811
33389
  }
32812
33390
  function atomicInstallTarget(projectRoot, operation, entry) {
32813
- const destination = join28(projectRoot, ...entry.path.split("/"));
33391
+ const destination = join29(projectRoot, ...entry.path.split("/"));
32814
33392
  ensureDestinationParents(projectRoot, entry.path);
32815
- const directory = dirname17(destination);
33393
+ const directory = dirname16(destination);
32816
33394
  const temporaryPrefix = `.kxm-repair-${operation.operationId}-`;
32817
33395
  const pathKey = kxmContentSha256(entry.path).slice("sha256:".length, "sha256:".length + 16);
32818
- const displaced = join28(directory, `${temporaryPrefix}${pathKey}.preimage`);
32819
- if (existsSync24(displaced)) {
33396
+ const displaced = join29(directory, `${temporaryPrefix}${pathKey}.preimage`);
33397
+ if (existsSync25(displaced)) {
32820
33398
  const stat = lstatSync7(displaced);
32821
33399
  if (stat.isSymbolicLink() || !stat.isFile()) fail3("repair_preimage_temporary_invalid", entry.path, "stale displaced preimage is not a regular file", "path");
32822
- if (entry.observedSha256 === null || kxmContentSha256(readFileSync24(displaced)) !== entry.observedSha256) {
33400
+ if (entry.observedSha256 === null || kxmContentSha256(readFileSync25(displaced)) !== entry.observedSha256) {
32823
33401
  fail3("repair_preimage_temporary_mismatch", entry.path, "stale displaced preimage does not match the pinned operation");
32824
33402
  }
32825
- if (!existsSync24(destination)) {
33403
+ if (!existsSync25(destination)) {
32826
33404
  try {
32827
33405
  linkSync(displaced, destination);
32828
33406
  } catch {
@@ -32837,7 +33415,7 @@ function atomicInstallTarget(projectRoot, operation, entry) {
32837
33415
  const owned = candidate.name.startsWith(temporaryPrefix) && (candidate.name.endsWith(".tmp") || candidate.name.endsWith(".link-source") || candidate.name.endsWith(".link-target"));
32838
33416
  if (!owned) continue;
32839
33417
  if (!candidate.isFile()) fail3("repair_temporary_invalid", entry.path, "stale operation temporary is not a regular file", "path");
32840
- rmSync9(join28(directory, candidate.name), { force: true });
33418
+ rmSync9(join29(directory, candidate.name), { force: true });
32841
33419
  }
32842
33420
  const current = destinationSha(projectRoot, entry.path);
32843
33421
  if (current === entry.targetSha256) return;
@@ -32847,15 +33425,15 @@ function atomicInstallTarget(projectRoot, operation, entry) {
32847
33425
  requireConditionalLinkSupport(directory, operation.operationId, entry.path);
32848
33426
  const bytes = readTarget(projectRoot, entry.path);
32849
33427
  if (!bytes) fail3("init_transaction_target_missing", entry.path, "pinned target artifact is missing");
32850
- const temporary = join28(directory, `${temporaryPrefix}${randomUUID10()}.tmp`);
32851
- const mode = existsSync24(destination) ? lstatSync7(destination).mode & 511 : 420;
33428
+ const temporary = join29(directory, `${temporaryPrefix}${randomUUID10()}.tmp`);
33429
+ const mode = existsSync25(destination) ? lstatSync7(destination).mode & 511 : 420;
32852
33430
  try {
32853
33431
  writeDurableNew(temporary, bytes, mode);
32854
33432
  if (entry.observedSha256 !== null) {
32855
33433
  renameSync7(destination, displaced);
32856
33434
  syncDirectory2(directory);
32857
- if (kxmContentSha256(readFileSync24(displaced)) !== entry.observedSha256) {
32858
- if (!existsSync24(destination)) {
33435
+ if (kxmContentSha256(readFileSync25(displaced)) !== entry.observedSha256) {
33436
+ if (!existsSync25(destination)) {
32859
33437
  try {
32860
33438
  linkSync(displaced, destination);
32861
33439
  } catch {
@@ -32871,7 +33449,7 @@ function atomicInstallTarget(projectRoot, operation, entry) {
32871
33449
  try {
32872
33450
  linkSync(temporary, destination);
32873
33451
  } catch {
32874
- if (!existsSync24(destination) && existsSync24(displaced)) {
33452
+ if (!existsSync25(destination) && existsSync25(displaced)) {
32875
33453
  try {
32876
33454
  linkSync(displaced, destination);
32877
33455
  } catch {
@@ -32893,12 +33471,12 @@ function atomicInstallTarget(projectRoot, operation, entry) {
32893
33471
  if (destinationSha(projectRoot, entry.path) !== entry.targetSha256) fail3("repair_install_verification_failed", entry.path, "installed target hash does not match the pinned operation");
32894
33472
  }
32895
33473
  function installedCreateMatches(projectRoot, operation) {
32896
- const configRoot = join28(projectRoot, ".kxm");
32897
- if (!existsSync24(configRoot)) return false;
33474
+ const configRoot = join29(projectRoot, ".kxm");
33475
+ if (!existsSync25(configRoot)) return false;
32898
33476
  const actual = [];
32899
33477
  const visit2 = (directory) => {
32900
33478
  for (const entry of readdirSync9(directory, { withFileTypes: true })) {
32901
- const absolute = join28(directory, entry.name);
33479
+ const absolute = join29(directory, entry.name);
32902
33480
  if (entry.isSymbolicLink()) fail3("create_resume_link", relativeConfigPath(projectRoot, absolute), "created configuration must not contain links", "path");
32903
33481
  if (entry.isDirectory()) visit2(absolute);
32904
33482
  else if (entry.isFile()) actual.push(relativeConfigPath(projectRoot, absolute));
@@ -32911,15 +33489,15 @@ function installedCreateMatches(projectRoot, operation) {
32911
33489
  return actual.length === expected.length && actual.every((path4, index) => path4 === expected[index]) && operation.files.every((entry) => destinationSha(projectRoot, entry.path) === entry.targetSha256);
32912
33490
  }
32913
33491
  function relativeConfigPath(projectRoot, file) {
32914
- return relative5(resolve22(projectRoot), file).replaceAll("\\", "/");
33492
+ return relative5(resolve21(projectRoot), file).replaceAll("\\", "/");
32915
33493
  }
32916
33494
  function syncTreeDirectories(root) {
32917
- if (!existsSync24(root)) return;
33495
+ if (!existsSync25(root)) return;
32918
33496
  const directories = [];
32919
33497
  const visit2 = (directory) => {
32920
33498
  directories.push(directory);
32921
33499
  for (const entry of readdirSync9(directory, { withFileTypes: true })) {
32922
- if (entry.isDirectory()) visit2(join28(directory, entry.name));
33500
+ if (entry.isDirectory()) visit2(join29(directory, entry.name));
32923
33501
  }
32924
33502
  };
32925
33503
  visit2(root);
@@ -32933,7 +33511,7 @@ function rebuildCreateShadow(projectRoot, operation, options) {
32933
33511
  if (stat.isSymbolicLink() || !stat.isDirectory() || !installedCreateMatches(shadow, operation)) {
32934
33512
  fail3("create_shadow_invalid", TRANSACTION_NAME, "reconstructed create shadow does not exactly match pinned target files", "path");
32935
33513
  }
32936
- syncTreeDirectories(join28(shadow, ".kxm"));
33514
+ syncTreeDirectories(join29(shadow, ".kxm"));
32937
33515
  }
32938
33516
  function applyOperation(projectRoot, original, options, resumed) {
32939
33517
  assertNoRegisteredGates(options);
@@ -32943,10 +33521,10 @@ function applyOperation(projectRoot, original, options, resumed) {
32943
33521
  verifyBackups(projectRoot, operation);
32944
33522
  operation = updatePhase(projectRoot, operation, "applying");
32945
33523
  if (operation.kind === "create") {
32946
- const destination = join28(projectRoot, ".kxm");
32947
- if (!existsSync24(destination)) {
33524
+ const destination = join29(projectRoot, ".kxm");
33525
+ if (!existsSync25(destination)) {
32948
33526
  rebuildCreateShadow(projectRoot, operation, effectiveOptions);
32949
- renameSync7(join28(shadowRoot(projectRoot), ".kxm"), destination);
33527
+ renameSync7(join29(shadowRoot(projectRoot), ".kxm"), destination);
32950
33528
  syncDirectory2(projectRoot);
32951
33529
  } else if (!installedCreateMatches(projectRoot, operation)) {
32952
33530
  fail3("create_resume_conflict", ".kxm", "existing project configuration does not match the pinned create operation");
@@ -32973,7 +33551,7 @@ function applyOperation(projectRoot, original, options, resumed) {
32973
33551
  operation = updatePhase(projectRoot, operation, "verified");
32974
33552
  if (options.testFaultAt === "verified") throw new Error("injected init fault after verification");
32975
33553
  const files = operation.files.map((entry) => entry.path);
32976
- syncTreeDirectories(join28(projectRoot, ".kxm"));
33554
+ syncTreeDirectories(join29(projectRoot, ".kxm"));
32977
33555
  return { kind: operation.kind, resumed, bundle, files };
32978
33556
  }
32979
33557
  function prepareAndApplyKxmCreate(projectRoot, rendered, options = {}) {
@@ -33088,7 +33666,7 @@ function finishPreparingOperation(projectRoot, operation, options) {
33088
33666
  const rendered = rendererForOperation(operation);
33089
33667
  if (!rendered) fail3("init_transaction_renderer_unavailable", TRANSACTION_NAME, "this binary cannot reproduce the pinned pre-application target");
33090
33668
  for (const directory of ["targets", "backups", "shadow"]) {
33091
- rmSync9(join28(transactionRoot(projectRoot), directory), { recursive: true, force: true });
33669
+ rmSync9(join29(transactionRoot(projectRoot), directory), { recursive: true, force: true });
33092
33670
  }
33093
33671
  for (const entry of operation.files) {
33094
33672
  const bytes = rendered.files.get(entry.path);
@@ -33107,7 +33685,7 @@ function resumeKxmInitTransaction(projectRoot, options = {}) {
33107
33685
  let operation = readOperation(projectRoot, options.schemasDir);
33108
33686
  if (!operation) {
33109
33687
  const root = transactionRoot(projectRoot);
33110
- if (existsSync24(root)) {
33688
+ if (existsSync25(root)) {
33111
33689
  rmSync9(root, { recursive: true, force: true });
33112
33690
  syncDirectory2(projectRoot);
33113
33691
  }
@@ -33123,7 +33701,7 @@ function commitKxmInitTransaction(projectRoot, schemasDir) {
33123
33701
  fail3("init_transaction_not_verified", TRANSACTION_NAME, "initialization transaction cannot commit before installed state is verified");
33124
33702
  }
33125
33703
  const transaction = transactionRoot(projectRoot);
33126
- const retired = join28(projectRoot, `${TRANSACTION_NAME}-cleanup-${operation.operationId}-${randomUUID10()}`);
33704
+ const retired = join29(projectRoot, `${TRANSACTION_NAME}-cleanup-${operation.operationId}-${randomUUID10()}`);
33127
33705
  renameSync7(transaction, retired);
33128
33706
  syncDirectory2(projectRoot);
33129
33707
  rmSync9(retired, { recursive: true, force: true });
@@ -33232,7 +33810,7 @@ function validateBindingIdentities(bundle, persisted, explicit) {
33232
33810
  }
33233
33811
  function initializeKxmProjectAtGitRoot(start, gitRoot, options, mutationLock) {
33234
33812
  const storeOptions = bindingStoreOptions(options);
33235
- const persisted = existsSync25(join29(gitRoot, ".kxm", "project.yaml")) ? readKxmLocalBindings(gitRoot, storeOptions) : void 0;
33813
+ const persisted = existsSync26(join30(gitRoot, ".kxm", "project.yaml")) ? readKxmLocalBindings(gitRoot, storeOptions) : void 0;
33236
33814
  const repositoryBindings = Object.fromEntries([
33237
33815
  ...Object.entries(persisted?.repositories ?? {}),
33238
33816
  ...Object.entries(options.repositoryBindings ?? {})
@@ -33420,7 +33998,7 @@ function initializeKxmProjectAtGitRoot(start, gitRoot, options, mutationLock) {
33420
33998
  const files = [...rendered.files.keys()];
33421
33999
  if (options.dryRun) return { action: "planned", plan, projectRoot: gitRoot, files };
33422
34000
  if (!mutationLock) throw new Error("project mutation lock is required to create configuration");
33423
- if (existsSync25(join29(gitRoot, ".kxm"))) {
34001
+ if (existsSync26(join30(gitRoot, ".kxm"))) {
33424
34002
  throw new KxmConfigError([initIssue("workspace_changed", ".kxm", "workspace changed after planning; existing .kxm state was not overwritten")]);
33425
34003
  }
33426
34004
  const created = prepareAndApplyKxmCreate(gitRoot, rendered, transactionOptions);
@@ -33457,7 +34035,7 @@ function hubSourceReason(status) {
33457
34035
  if (status === 404) return "hub_not_found";
33458
34036
  return `hub_http_${status}`;
33459
34037
  }
33460
- function asString(value) {
34038
+ function asString2(value) {
33461
34039
  return typeof value === "string" && value.length > 0 ? value : void 0;
33462
34040
  }
33463
34041
  function isObject(value) {
@@ -33505,37 +34083,37 @@ async function assembleTenantStatus(input) {
33505
34083
  project: body.project,
33506
34084
  fetchedAt: body.fetchedAt,
33507
34085
  agents: body.agents.filter(isObject).map((agent) => ({
33508
- id: asString(agent.id) ?? "",
33509
- name: asString(agent.name) ?? "",
34086
+ id: asString2(agent.id) ?? "",
34087
+ name: asString2(agent.name) ?? "",
33510
34088
  online: agent.online === true
33511
34089
  })),
33512
34090
  openMessageTotal: body.openMessageTotal,
33513
34091
  runTotal: body.runTotal,
33514
34092
  runs: runRows.map((run) => {
33515
34093
  const stages = Array.isArray(run.stages) ? run.stages.filter(isObject).map((stage) => ({
33516
- id: asString(stage.id) ?? "",
33517
- status: asString(stage.status) ?? "unknown",
34094
+ id: asString2(stage.id) ?? "",
34095
+ status: asString2(stage.status) ?? "unknown",
33518
34096
  ...typeof stage.attempts === "number" ? { attempts: stage.attempts } : {}
33519
34097
  })) : void 0;
33520
34098
  const done = stages?.filter((stage) => stage.status === "passed" || stage.status === "failed" || stage.status === "warning").length;
33521
34099
  return {
33522
- id: asString(run.id) ?? "",
33523
- status: asString(run.status) ?? "unknown",
33524
- definitionId: asString(run.definitionId) ?? "",
33525
- ...asString(run.currentStage) ? { currentStage: asString(run.currentStage) } : {},
33526
- ...asString(run.targetAgentName) ? { targetAgentName: asString(run.targetAgentName) } : {},
33527
- ...asString(run.updatedAt) ? { updatedAt: asString(run.updatedAt) } : {},
34100
+ id: asString2(run.id) ?? "",
34101
+ status: asString2(run.status) ?? "unknown",
34102
+ definitionId: asString2(run.definitionId) ?? "",
34103
+ ...asString2(run.currentStage) ? { currentStage: asString2(run.currentStage) } : {},
34104
+ ...asString2(run.targetAgentName) ? { targetAgentName: asString2(run.targetAgentName) } : {},
34105
+ ...asString2(run.updatedAt) ? { updatedAt: asString2(run.updatedAt) } : {},
33528
34106
  ...stages !== void 0 ? { progress: { done: done ?? 0, total: stages.length }, stages } : {},
33529
34107
  source: "hub-projection"
33530
34108
  };
33531
34109
  }),
33532
34110
  plans: (Array.isArray(body.plans) ? body.plans : []).filter(isObject).map((plan) => ({
33533
- id: asString(plan.id) ?? "",
33534
- runId: asString(plan.runId) ?? "",
33535
- summary: asString(plan.summary) ?? "",
33536
- createdAt: asString(plan.createdAt) ?? "",
33537
- ...asString(plan.stageId) ? { stageId: asString(plan.stageId) } : {},
33538
- ...asString(plan.severity) ? { severity: asString(plan.severity) } : {}
34111
+ id: asString2(plan.id) ?? "",
34112
+ runId: asString2(plan.runId) ?? "",
34113
+ summary: asString2(plan.summary) ?? "",
34114
+ createdAt: asString2(plan.createdAt) ?? "",
34115
+ ...asString2(plan.stageId) ? { stageId: asString2(plan.stageId) } : {},
34116
+ ...asString2(plan.severity) ? { severity: asString2(plan.severity) } : {}
33539
34117
  }))
33540
34118
  };
33541
34119
  return { state: "ok", observedAt: attemptedAt, value };
@@ -33676,6 +34254,11 @@ async function cmdKxmInit(runtime, options, postHooks) {
33676
34254
  localStateRoot: kxmUserStateRoot({ env: runtime.env }),
33677
34255
  dryRun: runtime.dryRun
33678
34256
  });
34257
+ const starterGuidance = initialized.action === "created" || initialized.action === "planned" && initialized.plan.mode === "create" ? [
34258
+ "defaultHarness: pi and the npm test gate are generic starter settings, not repository detection.",
34259
+ "For Claude, set defaultHarness: claude in .kxm/project.yaml, update any explicit harness overrides in .kxm/agents/*.yaml, and configure compatible agent models.",
34260
+ "For .NET or other non-npm repositories, set gates.test.argv in .kxm/gates.yaml to the repository's actual test runner before driving a workflow."
34261
+ ] : [];
33679
34262
  const payload = {
33680
34263
  ok: initialized.action !== "planned" || runtime.dryRun,
33681
34264
  command: "init",
@@ -33688,10 +34271,11 @@ async function cmdKxmInit(runtime, options, postHooks) {
33688
34271
  ...initialized.repairPlan === void 0 ? {} : { repairPlan: initialized.repairPlan },
33689
34272
  ...initialized.resumePending === void 0 ? {} : { resumePending: initialized.resumePending },
33690
34273
  ...initialized.transactionKind === void 0 ? {} : { transactionKind: initialized.transactionKind },
33691
- plannedOnly: initialized.action === "planned"
34274
+ plannedOnly: initialized.action === "planned",
34275
+ ...starterGuidance.length > 0 ? { guidance: starterGuidance } : {}
33692
34276
  };
33693
34277
  const finishInit = (code, text) => {
33694
- print(runtime.io, runtime.json, payload, text);
34278
+ print(runtime.io, runtime.json, payload, [text, ...starterGuidance].join("\n"));
33695
34279
  return code;
33696
34280
  };
33697
34281
  if (initialized.action === "created") {
@@ -33745,7 +34329,7 @@ async function cmdBackup(runtime, options) {
33745
34329
  projectRoot: backupProjectRoot(runtime),
33746
34330
  env: runtime.env,
33747
34331
  ...options.allProjects === true ? { allProjects: true } : {},
33748
- ...options.out ? { outDir: resolve23(runtime.cwd, options.out) } : {}
34332
+ ...options.out ? { outDir: resolve22(runtime.cwd, options.out) } : {}
33749
34333
  };
33750
34334
  if (runtime.dryRun) {
33751
34335
  const plan = planBackup(backupOptions);
@@ -33753,8 +34337,8 @@ async function cmdBackup(runtime, options) {
33753
34337
  runtime,
33754
34338
  { command: "backup", scope: plan.scope, outDir: plan.outDir, stores: plan.stores, files: plan.files },
33755
34339
  [
33756
- ...[...plan.stores, ...plan.files].map((entry) => ({ action: "write", target: join30(plan.outDir, entry.backupFile) })),
33757
- { action: "write", target: join30(plan.outDir, "manifest.json") }
34340
+ ...[...plan.stores, ...plan.files].map((entry) => ({ action: "write", target: join31(plan.outDir, entry.backupFile) })),
34341
+ { action: "write", target: join31(plan.outDir, "manifest.json") }
33758
34342
  ],
33759
34343
  `back up ${plan.stores.length} store(s) and ${plan.files.length} file(s) (${plan.scope} scope) to ${plan.outDir} (sources are not opened, so their WAL is not checkpointed)`
33760
34344
  );
@@ -33773,7 +34357,7 @@ async function cmdBackup(runtime, options) {
33773
34357
  complete ? `Created SQLite backup with ${manifest.stores.length} store(s) (${manifest.scope ?? "project"} scope):` : `Backup is incomplete (${manifest.omitted?.length ?? 0} omitted); not ok:`,
33774
34358
  ...manifest.stores.map((s) => ` - ${s.storeId}: ${s.sourcePath} -> ${s.backupFile} (schema v${s.schemaVersion}, ${s.bytes} bytes, sha256 ${s.sha256.slice(0, 12)}...)`),
33775
34359
  ...(manifest.omitted ?? []).map((id) => ` - omitted ${id}`),
33776
- `Manifest: ${join30(outDir, "manifest.json")}`
34360
+ `Manifest: ${join31(outDir, "manifest.json")}`
33777
34361
  ].join("\n");
33778
34362
  print(runtime.io, runtime.json, payload, summary);
33779
34363
  return complete ? 0 : 1;
@@ -33807,7 +34391,7 @@ function assertRestoreTargetsStopped(runtime, plan) {
33807
34391
  }
33808
34392
  for (const store of plan.stores) {
33809
34393
  if (store.storeId !== "hub-store") continue;
33810
- const claim = readLiveHubClaim(dirname18(store.targetPath));
34394
+ const claim = readLiveHubClaim(dirname17(store.targetPath));
33811
34395
  if (claim) {
33812
34396
  throw databaseError(
33813
34397
  "restore_hub_running",
@@ -33819,7 +34403,7 @@ function assertRestoreTargetsStopped(runtime, plan) {
33819
34403
  }
33820
34404
  async function cmdRestore(runtime, manifestArg, options = {}) {
33821
34405
  try {
33822
- const plan = planRestore(resolve23(runtime.cwd, manifestArg), {
34406
+ const plan = planRestore(resolve22(runtime.cwd, manifestArg), {
33823
34407
  projectRoot: backupProjectRoot(runtime),
33824
34408
  env: runtime.env,
33825
34409
  ...options.allProjects === true ? { allProjects: true } : {}
@@ -33839,7 +34423,7 @@ async function cmdRestore(runtime, manifestArg, options = {}) {
33839
34423
  [
33840
34424
  ...plan.stores.flatMap((store) => [
33841
34425
  { action: "write", target: store.targetPath },
33842
- ...[`${store.targetPath}-wal`, `${store.targetPath}-shm`].filter((file) => existsSync26(file)).map((file) => ({ action: "delete", target: file }))
34426
+ ...[`${store.targetPath}-wal`, `${store.targetPath}-shm`].filter((file) => existsSync27(file)).map((file) => ({ action: "delete", target: file }))
33843
34427
  ]),
33844
34428
  ...plan.files.map((file) => ({ action: "write", target: file.targetPath }))
33845
34429
  ],
@@ -33920,11 +34504,17 @@ async function readSupervisor(runtime, command) {
33920
34504
  const attached = await attachKxmSupervisor({ env: runtime.env });
33921
34505
  return attached ?? refuseDryRun(runtime.io, runtime.json, command, "the Runtime supervisor is not running and --dry-run will not start it");
33922
34506
  }
33923
- var RUN_ENGINE_PHASE = "pre-3a";
33924
- function runEngineNotice(runId) {
33925
- return `drive it model-free: kxm runs drive ${runId} --simulated --wait (or cancel: kxm runs cancel ${runId})`;
34507
+ function runEngineNotice(runId, defaultHarness, prerequisites) {
34508
+ return [
34509
+ `No steps executed. Project default harness: ${defaultHarness}; per-agent harness settings take precedence.`,
34510
+ ...prerequisites.map((item) => `Live prerequisite${item.stepId ? ` (${item.stepId})` : ""}: ${item.detail}`),
34511
+ "Live execution uses one-shot harness calls; no hub or Pi worker is required. Check installation/authentication with kxm harness list.",
34512
+ `${prerequisites.length > 0 ? "Resolve the prerequisites above, then execute" : "Execute"}: kxm runs drive ${runId} --wait`,
34513
+ `Inspect: kxm runs status ${runId} --json; receipt: kxm runs receipt ${runId} --json; cancel: kxm runs cancel ${runId}`,
34514
+ "These are local Runtime runs, not webhook workflows; use kxm runs, not kxm workflow get."
34515
+ ].join("\n");
33926
34516
  }
33927
- function resolveKxmRunTarget(runtime, workflow) {
34517
+ function resolveKxmRunTarget(runtime, workflow, forTask = false) {
33928
34518
  if (runtime.workspaceFlag !== void 0) {
33929
34519
  print(runtime.io, runtime.json, {
33930
34520
  ok: false,
@@ -33933,7 +34523,7 @@ function resolveKxmRunTarget(runtime, workflow) {
33933
34523
  }, "kxm run discovers the authoritative project from the current directory; --workspace is not supported");
33934
34524
  return 2;
33935
34525
  }
33936
- if (!workflow) {
34526
+ if (!workflow && !forTask) {
33937
34527
  print(runtime.io, runtime.json, { ok: false, command: "run", error: "workflow_required" }, "usage: kxm run <workflow> [prompt]");
33938
34528
  return 2;
33939
34529
  }
@@ -33943,15 +34533,30 @@ function resolveKxmRunTarget(runtime, workflow) {
33943
34533
  return 1;
33944
34534
  }
33945
34535
  const bundle = loadKxmProject(projectRoot, {});
33946
- if (!bundle.workflows.has(workflow)) {
33947
- print(runtime.io, runtime.json, { ok: false, command: "run", error: "run_workflow_unknown", workflow }, `workflow ${workflow} does not exist in this project`);
34536
+ const workflowId = workflow ?? String(bundle.project.value.defaultWorkflow ?? "default");
34537
+ if (!bundle.workflows.has(workflowId)) {
34538
+ print(runtime.io, runtime.json, { ok: false, command: "run", error: "run_workflow_unknown", workflow: workflowId }, `workflow ${workflowId} does not exist in this project`);
33948
34539
  return 1;
33949
34540
  }
33950
- return { projectRoot, workflowId: workflow, configRevision: bundle.configRevision };
34541
+ const defaultHarness = String(bundle.project.value.defaultHarness ?? "pi");
34542
+ const prerequisites = kxmLiveRunPrerequisites(bundle, workflowId, projectRoot);
34543
+ if (forTask && prerequisites.length > 0) {
34544
+ print(runtime.io, runtime.json, {
34545
+ ok: false,
34546
+ command: "task run",
34547
+ error: "run_execution_unavailable",
34548
+ workflowId,
34549
+ defaultHarness,
34550
+ execution: { status: "not_started", mode: "live", prerequisites }
34551
+ }, `task run refused; no run created or task changed (default harness: ${defaultHarness}).
34552
+ ${prerequisites.map((item) => `${item.stepId ?? workflowId}: ${item.detail}`).join("\n")}`);
34553
+ return 1;
34554
+ }
34555
+ return { projectRoot, workflowId, configRevision: bundle.configRevision, defaultHarness, prerequisites };
33951
34556
  }
33952
- async function cmdKxmRun(runtime, workflow, promptParts) {
34557
+ async function cmdKxmRun(runtime, workflow, promptParts, forTask = false) {
33953
34558
  try {
33954
- const target = resolveKxmRunTarget(runtime, workflow);
34559
+ const target = resolveKxmRunTarget(runtime, workflow, forTask);
33955
34560
  if (typeof target === "number") return target;
33956
34561
  const { projectRoot } = target;
33957
34562
  if (runtime.dryRun) {
@@ -33974,12 +34579,24 @@ async function cmdKxmRun(runtime, workflow, promptParts) {
33974
34579
  print(runtime.io, runtime.json, {
33975
34580
  ok: true,
33976
34581
  command: "run",
33977
- phase: RUN_ENGINE_PHASE,
34582
+ execution: {
34583
+ status: "not_started",
34584
+ mode: "live",
34585
+ defaultHarness: target.defaultHarness,
34586
+ authentication: "not_checked",
34587
+ prerequisites: target.prerequisites,
34588
+ nextSteps: {
34589
+ harnesses: "kxm harness list",
34590
+ drive: `kxm runs drive ${run.runId} --wait`,
34591
+ status: `kxm runs status ${run.runId} --json`,
34592
+ receipt: `kxm runs receipt ${run.runId} --json`
34593
+ }
34594
+ },
33978
34595
  idempotent: acceptance.idempotent === true,
33979
34596
  run,
33980
34597
  supervisor: { runtimeId: supervisor.runtimeId, port: supervisor.port, started: supervisor.started }
33981
34598
  }, `run ${run.status}: ${run.runId} (home ${run.homeRuntimeId.slice(0, 12)}\u2026, config ${run.configRevision.slice(0, 19)}\u2026)
33982
- ${runEngineNotice(run.runId)}`);
34599
+ ${runEngineNotice(run.runId, target.defaultHarness, target.prerequisites)}`);
33983
34600
  return 0;
33984
34601
  } catch (error) {
33985
34602
  if (error instanceof KxmConfigError) {
@@ -34063,7 +34680,7 @@ function parseDriveWaitTimeoutMs(raw) {
34063
34680
  return value;
34064
34681
  }
34065
34682
  function sleepMs(ms) {
34066
- return new Promise((resolve35) => setTimeout(resolve35, ms));
34683
+ return new Promise((resolve34) => setTimeout(resolve34, ms));
34067
34684
  }
34068
34685
  function driveWaitCompleted(drive) {
34069
34686
  return drive?.verified === true && drive.receipt?.settlement?.kind === "terminal" && drive.receipt.settlement.status === "completed";
@@ -34260,9 +34877,20 @@ async function cmdTenantStatus(runtime) {
34260
34877
  }
34261
34878
  }
34262
34879
  async function cmdHarnessList(runtime) {
34263
- const inventory = await probeHarnessesAsync({ env: runtime.env });
34264
- print(runtime.io, runtime.json, { ok: true, command: "harness list", ...inventory }, formatHarnessInventory(inventory));
34265
- return 0;
34880
+ try {
34881
+ const projectRoot = discoverKxmProjectRoot(runtime.cwd);
34882
+ const defaultHarness = projectRoot ? String(loadKxmProject(projectRoot).project.value.defaultHarness ?? "pi") : void 0;
34883
+ const inventory = await probeHarnessesAsync({ env: runtime.env, defaultHarness });
34884
+ print(runtime.io, runtime.json, { ok: true, command: "harness list", ...inventory }, formatHarnessInventory(inventory));
34885
+ return 0;
34886
+ } catch (error) {
34887
+ if (error instanceof KxmConfigError) {
34888
+ print(runtime.io, runtime.json, { ok: false, command: "harness list", error: "harness_list_failed", issues: error.issues }, `harness list failed: ${error.message}`);
34889
+ return 1;
34890
+ }
34891
+ print(runtime.io, runtime.json, { ok: false, command: "harness list", error: "harness_list_io_failed" }, "harness list failed because a local operation did not complete");
34892
+ return 1;
34893
+ }
34266
34894
  }
34267
34895
  async function selectInventoryModel(runtime, requested) {
34268
34896
  const models = listInventoryModels(runtime.dirs.workdir);
@@ -34270,7 +34898,7 @@ async function selectInventoryModel(runtime, requested) {
34270
34898
  if (runtime.json || !process.stdin.isTTY || models.length === 0) return void 0;
34271
34899
  runtime.io.stdout(models.slice(0, 100).map((model, index2) => `${index2 + 1}. ${model}`).join("\n") + "\nSelect model number: ");
34272
34900
  const rl = createInterface2({ input: process.stdin, output: process.stderr });
34273
- const answer = await new Promise((resolve35) => rl.once("line", resolve35));
34901
+ const answer = await new Promise((resolve34) => rl.once("line", resolve34));
34274
34902
  rl.close();
34275
34903
  const index = Number.parseInt(answer.trim(), 10) - 1;
34276
34904
  return Number.isInteger(index) && index >= 0 && index < models.length ? models[index] : void 0;
@@ -34296,7 +34924,7 @@ async function cmdModelsScreen(runtime) {
34296
34924
  return 2;
34297
34925
  }
34298
34926
  const rl = createInterface2({ input: process.stdin, output: process.stderr });
34299
- const ask = (q2) => new Promise((resolve35) => rl.question(q2, resolve35));
34927
+ const ask = (q2) => new Promise((resolve34) => rl.question(q2, resolve34));
34300
34928
  try {
34301
34929
  while (true) {
34302
34930
  const policy = loadRoutePolicy(runtime.dirs.workdir);
@@ -34473,39 +35101,64 @@ async function cmdKxmRunReceipt(runtime, runId, options = {}) {
34473
35101
  }
34474
35102
 
34475
35103
  // plugins/kxm/src/cli/tasks.ts
34476
- async function cmdSuggest(runtime, promptParts) {
35104
+ async function cmdSuggest(runtime, promptParts, probeHarnesses2 = probeHarnessesAsync) {
34477
35105
  try {
34478
35106
  const prompt = promptParts.join(" ").trim();
34479
35107
  if (!prompt) {
34480
- runtime.io.stderr("prompt must be non-empty\n");
35108
+ print(runtime.io, runtime.json, { ok: false, command: "suggest", error: "prompt_required" }, "prompt must be non-empty");
34481
35109
  return 2;
34482
35110
  }
34483
- const inventory = await probeHarnessesAsync({ env: runtime.env });
34484
- const availableHarnesses = inventory.harnesses.map((h) => ({
34485
- harness: h.id,
34486
- auth: h.authenticated === true ? "authenticated" : "unauthenticated"
34487
- }));
34488
- const suggestion = suggestWorkflowAndRoles(prompt, { availableHarnesses });
35111
+ const inventory = runtime.dryRun ? void 0 : await probeHarnesses2({ env: runtime.env });
35112
+ const suggestion = suggestWorkflowAndRoles(prompt, { availableHarnesses: inventory?.harnesses });
35113
+ if (suggestion.execution.supported && [".yaml", ".yml"].some((extension) => existsSync28(join32(runtime.cwd, ".kxm", "workflows", `${suggestion.workflowId}${extension}`)))) {
35114
+ suggestion.roles = [];
35115
+ suggestion.execution = {
35116
+ supported: false,
35117
+ error: "workflow_already_exists",
35118
+ reason: `Workflow ${suggestion.workflowId} already exists; its agents and permissions may differ from the recommended template.`,
35119
+ nextSteps: ["Review the existing definition and every agent's harness/model before running it, or install the recommended template under a fresh flat ID. No existing workflow is overwritten or recommended for execution."]
35120
+ };
35121
+ }
35122
+ const execution = suggestion.execution;
34489
35123
  const text = [
34490
35124
  `Suggested Workflow: ${suggestion.workflowId} (${suggestion.area})`,
35125
+ `Template: ${suggestion.template}`,
34491
35126
  `Confidence: ${(suggestion.confidence * 100).toFixed(0)}%`,
34492
35127
  `Reasons: ${suggestion.reasons.join("; ")}`,
34493
35128
  `Suggested Skills: ${suggestion.suggestedSkills.join(", ") || "none"}`,
34494
- `Roles:`,
34495
- ` Planner: ${suggestion.roles.planner.harness} (${suggestion.roles.planner.model})`,
34496
- ` Writer: ${suggestion.roles.writer.harness} (${suggestion.roles.writer.model})`,
34497
- ` Critics: ${suggestion.roles.critics.map((c) => `${c.harness}:${c.model}`).join(", ")}`,
34498
- ` Verifier: ${suggestion.roles.verifier.command}`,
34499
- ``,
34500
- `Execute with:`,
34501
- ` ${suggestion.suggestedCommand}`
35129
+ "",
35130
+ "Install definition only (requires kxm init; does not execute):",
35131
+ ` ${suggestion.suggestedCommand}`,
35132
+ "",
35133
+ ...execution.supported ? [
35134
+ "Suggested agent routing (not applied):",
35135
+ ...suggestion.roles.map((role) => ` ${role.agent}: ${role.harness} (${role.role}; use a configured compatible model)`),
35136
+ "Prerequisites:",
35137
+ ...execution.prerequisites.map((step) => ` ${step}`),
35138
+ `Create a run only (${execution.shell}; does not execute steps):`,
35139
+ ` ${execution.createCommand}`,
35140
+ "Then drive the returned run ID with live calls and inspect its result:",
35141
+ ` ${execution.driveCommand}`,
35142
+ ` ${execution.statusCommand}`,
35143
+ ` ${execution.receiptCommand}`
35144
+ ] : [
35145
+ `Execution unavailable: ${execution.reason}`,
35146
+ ...execution.nextSteps.map((step) => ` ${step}`),
35147
+ ...runtime.dryRun ? ["Harness authentication was not probed during --dry-run."] : []
35148
+ ]
34502
35149
  ].join("\n");
34503
- print(runtime.io, runtime.json, { ok: true, command: "suggest", prompt, ...suggestion }, text);
34504
- return 0;
35150
+ print(runtime.io, runtime.json, {
35151
+ ok: execution.supported,
35152
+ command: "suggest",
35153
+ prompt,
35154
+ ...suggestion,
35155
+ ...runtime.dryRun ? { dryRun: true } : {},
35156
+ ...!execution.supported ? { error: execution.error } : {}
35157
+ }, text);
35158
+ return execution.supported ? 0 : 1;
34505
35159
  } catch (error) {
34506
35160
  const message = error instanceof Error ? error.message : String(error);
34507
- runtime.io.stderr(`suggest failed: ${message}
34508
- `);
35161
+ print(runtime.io, runtime.json, { ok: false, command: "suggest", error: "suggest_failed", detail: message }, `suggest failed: ${message}`);
34509
35162
  return 1;
34510
35163
  }
34511
35164
  }
@@ -34625,27 +35278,21 @@ async function cmdTaskRun(runtime, taskId) {
34625
35278
  `);
34626
35279
  return 1;
34627
35280
  }
34628
- const workflow = task.assignedWorkflow ?? "default";
35281
+ const workflow = task.assignedWorkflow;
34629
35282
  if (runtime.dryRun) {
34630
- const target = resolveKxmRunTarget(runtime, workflow);
35283
+ const target = resolveKxmRunTarget(runtime, workflow, true);
34631
35284
  if (typeof target === "number") return target;
34632
- const started = updateTaskStatus(runtime.cwd, taskId, "in_progress", { dryRun: true });
34633
35285
  printPlan(
34634
35286
  runtime,
34635
- { command: "task run", taskId, ...target, status: started.status },
35287
+ { command: "task run", taskId, ...target, status: task.status, execution: { status: "not_started", mode: "live" } },
34636
35288
  [
34637
- { action: "request", target: "POST kxm-runtime /v1/runs (starts the Runtime supervisor if it is not running)" },
34638
- { action: "write", target: taskFilePath(runtime.cwd, taskId) }
35289
+ { action: "request", target: "POST kxm-runtime /v1/runs (starts the Runtime supervisor if it is not running)" }
34639
35290
  ],
34640
- `run workflow ${workflow} for task ${taskId}, then mark it ${started.status}`
35291
+ `create workflow ${target.workflowId} for task ${taskId}; task status stays ${task.status} until work actually starts`
34641
35292
  );
34642
35293
  return 0;
34643
35294
  }
34644
- const exitCode = await cmdKxmRun(runtime, workflow, [task.objective]);
34645
- if (exitCode === 0) {
34646
- updateTaskStatus(runtime.cwd, taskId, "in_progress");
34647
- }
34648
- return exitCode;
35295
+ return await cmdKxmRun(runtime, workflow, [task.objective], true);
34649
35296
  } catch (error) {
34650
35297
  const message = error instanceof Error ? error.message : String(error);
34651
35298
  runtime.io.stderr(`task run failed: ${message}
@@ -34679,12 +35326,12 @@ async function cmdStudioLayout(runtime, workflowPath) {
34679
35326
  const projectRoot = discoverKxmProjectRoot(runtime.cwd) ?? runtime.cwd;
34680
35327
  let filePath = workflowPath;
34681
35328
  if (!filePath) {
34682
- filePath = resolve24(projectRoot, ".kxm", "workflows", "default.yaml");
35329
+ filePath = resolve23(projectRoot, ".kxm", "workflows", "default.yaml");
34683
35330
  }
34684
35331
  let yamlContent;
34685
35332
  let workflowId = "default";
34686
- if (existsSync27(filePath)) {
34687
- yamlContent = readFileSync26(filePath, "utf8");
35333
+ if (existsSync28(filePath)) {
35334
+ yamlContent = readFileSync27(filePath, "utf8");
34688
35335
  } else {
34689
35336
  yamlContent = `schema: kxm.workflow.v1
34690
35337
  description: Feature implementation workflow
@@ -34757,15 +35404,15 @@ async function cmdStudioServe(runtime, options) {
34757
35404
  sessionToken,
34758
35405
  planProvider: () => {
34759
35406
  try {
34760
- const wfDir = join31(runtime.cwd, ".kxm", "workflows");
34761
- const defaultPath = join31(wfDir, "default.yaml");
34762
- let targetPath = existsSync27(defaultPath) ? defaultPath : void 0;
34763
- if (!targetPath && existsSync27(wfDir)) {
35407
+ const wfDir = join32(runtime.cwd, ".kxm", "workflows");
35408
+ const defaultPath = join32(wfDir, "default.yaml");
35409
+ let targetPath = existsSync28(defaultPath) ? defaultPath : void 0;
35410
+ if (!targetPath && existsSync28(wfDir)) {
34764
35411
  const yml = readdirSync10(wfDir).find((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
34765
- if (yml) targetPath = join31(wfDir, yml);
35412
+ if (yml) targetPath = join32(wfDir, yml);
34766
35413
  }
34767
- if (targetPath && existsSync27(targetPath)) {
34768
- const raw = readFileSync26(targetPath, "utf8");
35414
+ if (targetPath && existsSync28(targetPath)) {
35415
+ const raw = readFileSync27(targetPath, "utf8");
34769
35416
  const parsed = (0, import_yaml15.parse)(raw);
34770
35417
  const wfId = basename7(targetPath).replace(/\.(yaml|yml)$/, "");
34771
35418
  return compileKxmWorkflow({ id: wfId, value: parsed });
@@ -34815,16 +35462,16 @@ Press Ctrl+C to stop.
34815
35462
  }
34816
35463
 
34817
35464
  // plugins/kxm/src/cli/context-skills.ts
34818
- import { readFileSync as readFileSync27 } from "node:fs";
34819
- import { join as join33, relative as relative6, resolve as resolve25 } from "node:path";
35465
+ import { readFileSync as readFileSync28 } from "node:fs";
35466
+ import { join as join34, relative as relative6, resolve as resolve24 } from "node:path";
34820
35467
 
34821
35468
  // plugins/kxm/src/wiki.ts
34822
35469
  import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync21 } from "node:fs";
34823
- import { join as join32 } from "node:path";
35470
+ import { join as join33 } from "node:path";
34824
35471
  function writeCompiledWiki(root, wiki) {
34825
35472
  const written = [];
34826
35473
  for (const [relativePath, content] of [...wiki.pages].sort(([left], [right]) => left.localeCompare(right))) {
34827
- const absolute = join32(root, relativePath);
35474
+ const absolute = join33(root, relativePath);
34828
35475
  mkdirSync23(absolute.slice(0, absolute.lastIndexOf("/")), { recursive: true });
34829
35476
  writeFileSync21(absolute, content);
34830
35477
  written.push(relativePath);
@@ -34976,7 +35623,7 @@ async function cmdContextWikiCompile(runtime, project, options) {
34976
35623
  printPlan(
34977
35624
  runtime,
34978
35625
  { command: "context wiki-compile", project, pages: compiled.audit.pages, openContradictions: compiled.audit.contradictions, outDir },
34979
- compiled.pages.map((page) => ({ action: "write", target: resolve25(outDir, page.path) })),
35626
+ compiled.pages.map((page) => ({ action: "write", target: resolve24(outDir, page.path) })),
34980
35627
  `write ${compiled.pages.length} compiled wiki page(s) to ${outDir}`
34981
35628
  );
34982
35629
  return 0;
@@ -35011,7 +35658,7 @@ function skillStateFromFlag(value) {
35011
35658
  throw new Error(`invalid skill state ${value}`);
35012
35659
  }
35013
35660
  function skillsRoot(runtime) {
35014
- return join33(runtime.dirs.workdir, ".kxm", "skills");
35661
+ return join34(runtime.dirs.workdir, ".kxm", "skills");
35015
35662
  }
35016
35663
  function csv(value) {
35017
35664
  if (!value) return void 0;
@@ -35028,7 +35675,7 @@ async function cmdSkillsCreate(runtime, options) {
35028
35675
  const metadata = lifecycle.create({
35029
35676
  name: options.name,
35030
35677
  description: options.description ?? "",
35031
- content: readFileSync27(options.file, "utf8"),
35678
+ content: readFileSync28(options.file, "utf8"),
35032
35679
  createdBy: options.createdBy,
35033
35680
  sources: {
35034
35681
  runIds: csv(options.run) ?? [],
@@ -35190,7 +35837,7 @@ async function cmdMemorySync(runtime) {
35190
35837
  printPlan(
35191
35838
  runtime,
35192
35839
  { command: "memory sync", ...result },
35193
- result.updated.map((file) => ({ action: "write", target: join33(runtime.cwd, file) })),
35840
+ result.updated.map((file) => ({ action: "write", target: join34(runtime.cwd, file) })),
35194
35841
  "regenerate the project memory block in the instruction files this project already has"
35195
35842
  );
35196
35843
  return 0;
@@ -35218,8 +35865,8 @@ ${lines.join("\n")}`
35218
35865
 
35219
35866
  // plugins/kxm/src/cli/hub.ts
35220
35867
  import { randomUUID as randomUUID13 } from "node:crypto";
35221
- import { existsSync as existsSync33, mkdirSync as mkdirSync27, readFileSync as readFileSync33, readdirSync as readdirSync12, rmSync as rmSync11, writeFileSync as writeFileSync24 } from "node:fs";
35222
- import { join as join43, resolve as resolve28 } from "node:path";
35868
+ import { existsSync as existsSync34, mkdirSync as mkdirSync27, readFileSync as readFileSync34, readdirSync as readdirSync12, rmSync as rmSync11, writeFileSync as writeFileSync24 } from "node:fs";
35869
+ import { join as join44, resolve as resolve27 } from "node:path";
35223
35870
 
35224
35871
  // node_modules/marked/lib/marked.esm.js
35225
35872
  function M() {
@@ -39381,10 +40028,10 @@ var TuiBase = class _TuiBase extends Container {
39381
40028
  * @returns Promise containing the parsed RGB color, or undefined if it times out or fails to parse.
39382
40029
  */
39383
40030
  queryTerminalBackgroundColor({ timeoutMs }) {
39384
- return new Promise((resolve35) => {
40031
+ return new Promise((resolve34) => {
39385
40032
  const query = {
39386
40033
  settled: false,
39387
- resolve: resolve35,
40034
+ resolve: resolve34,
39388
40035
  timer: void 0
39389
40036
  };
39390
40037
  query.timer = setTimeout(() => {
@@ -39407,7 +40054,7 @@ var TuiBase = class _TuiBase extends Container {
39407
40054
  * `CSI ? 997 ; 1 n` for dark or `CSI ? 997 ; 2 n` for light.
39408
40055
  */
39409
40056
  queryTerminalColorScheme({ timeoutMs }) {
39410
- return new Promise((resolve35) => {
40057
+ return new Promise((resolve34) => {
39411
40058
  let settled = false;
39412
40059
  let timer;
39413
40060
  let unsubscribe = () => {
@@ -39421,7 +40068,7 @@ var TuiBase = class _TuiBase extends Container {
39421
40068
  timer = void 0;
39422
40069
  }
39423
40070
  unsubscribe();
39424
- resolve35(scheme);
40071
+ resolve34(scheme);
39425
40072
  };
39426
40073
  unsubscribe = this.onTerminalColorSchemeChange(settle);
39427
40074
  timer = setTimeout(() => settle(void 0), timeoutMs);
@@ -41152,19 +41799,19 @@ import * as path2 from "node:path";
41152
41799
 
41153
41800
  // node_modules/@earendil-works/pi-tui/dist/native-module-path.js
41154
41801
  import { createRequire as createRequire2 } from "node:module";
41155
- import { dirname as dirname19, join as join34 } from "node:path";
41802
+ import { dirname as dirname18, join as join35 } from "node:path";
41156
41803
  import { fileURLToPath as fileURLToPath2 } from "node:url";
41157
41804
  var moduleRequire = createRequire2(import.meta.url);
41158
41805
  var TUI_PACKAGE_NAME = "@earendil-works/pi-tui";
41159
41806
  function getNativeModuleCandidates(nativePath, options = {}) {
41160
- const moduleDir = dirname19(fileURLToPath2(options.moduleUrl ?? import.meta.url));
41807
+ const moduleDir = dirname18(fileURLToPath2(options.moduleUrl ?? import.meta.url));
41161
41808
  const candidates = [];
41162
41809
  try {
41163
41810
  const packageEntry = (options.resolvePackage ?? moduleRequire.resolve)(TUI_PACKAGE_NAME);
41164
- candidates.push(join34(dirname19(packageEntry), "..", nativePath));
41811
+ candidates.push(join35(dirname18(packageEntry), "..", nativePath));
41165
41812
  } catch {
41166
41813
  }
41167
- candidates.push(join34(moduleDir, "..", nativePath), join34(moduleDir, nativePath), join34(dirname19(options.execPath ?? process.execPath), nativePath));
41814
+ candidates.push(join35(moduleDir, "..", nativePath), join35(moduleDir, nativePath), join35(dirname18(options.execPath ?? process.execPath), nativePath));
41168
41815
  return Array.from(new Set(candidates));
41169
41816
  }
41170
41817
 
@@ -41505,7 +42152,7 @@ var ProcessTerminal = class {
41505
42152
  break;
41506
42153
  if (now - lastDataTime >= idleMs)
41507
42154
  break;
41508
- await new Promise((resolve35) => setTimeout(resolve35, Math.min(idleMs, timeLeft)));
42155
+ await new Promise((resolve34) => setTimeout(resolve34, Math.min(idleMs, timeLeft)));
41509
42156
  }
41510
42157
  } finally {
41511
42158
  process.stdin.removeListener("data", onData);
@@ -43621,7 +44268,7 @@ var MAX_RENDER_WRITE_CHARS = 1024 * 1024;
43621
44268
 
43622
44269
  // plugins/kxm/src/tui.ts
43623
44270
  import { mkdirSync as mkdirSync24 } from "node:fs";
43624
- import { join as join38, resolve as resolve27, dirname as dirname20 } from "node:path";
44271
+ import { join as join39, resolve as resolve26, dirname as dirname19 } from "node:path";
43625
44272
  import { spawnSync as spawnSync5 } from "node:child_process";
43626
44273
 
43627
44274
  // packages/core/tui/src/types/surface.ts
@@ -43762,9 +44409,9 @@ var SHARED_EFFECT_KINDS = Object.freeze([
43762
44409
  ]);
43763
44410
 
43764
44411
  // plugins/kxm/src/local-snapshot.ts
43765
- import { existsSync as existsSync28, readdirSync as readdirSync11, readFileSync as readFileSync28 } from "node:fs";
44412
+ import { existsSync as existsSync29, readdirSync as readdirSync11, readFileSync as readFileSync29 } from "node:fs";
43766
44413
  import { homedir as homedir6 } from "node:os";
43767
- import { isAbsolute as isAbsolute9, join as join37, resolve as resolve26 } from "node:path";
44414
+ import { isAbsolute as isAbsolute9, join as join38, resolve as resolve25 } from "node:path";
43768
44415
  var DEFAULT_BUSY_TIMEOUT_MS = 5e3;
43769
44416
  var RUNTIME_PROJECT_KEY = /^[A-Za-z0-9_-][A-Za-z0-9._-]*$/;
43770
44417
  function processExists2(pid) {
@@ -43889,36 +44536,36 @@ function readOpenMessageMetadata(database, where = OPEN_MESSAGE_WHERE, params =
43889
44536
  return messages;
43890
44537
  }
43891
44538
  function resolveKxmSnapshotPaths(cwd, env = process.env) {
43892
- const stateDir = env.KXM_STATE_DIR?.trim() || join37(cwd, ".kxm", "state");
44539
+ const stateDir = env.KXM_STATE_DIR?.trim() || join38(cwd, ".kxm", "state");
43893
44540
  const configured = env.KXM_DATA_PATH?.trim();
43894
- const dataPath = configured ? resolve26(cwd, configured) : join37(stateDir, "kxm.db");
44541
+ const dataPath = configured ? resolve25(cwd, configured) : join38(stateDir, "kxm.db");
43895
44542
  return { dataPath, stateDir };
43896
44543
  }
43897
44544
  function resolveKxmStateRoot(stateDir, options) {
43898
- if (options?.kxmStateRoot && existsSync28(options.kxmStateRoot)) {
43899
- return resolve26(options.kxmStateRoot);
44545
+ if (options?.kxmStateRoot && existsSync29(options.kxmStateRoot)) {
44546
+ return resolve25(options.kxmStateRoot);
43900
44547
  }
43901
- if (existsSync28(join37(stateDir, "runtime", "registry.db")) || existsSync28(join37(stateDir, "runtime", "projects"))) {
44548
+ if (existsSync29(join38(stateDir, "runtime", "registry.db")) || existsSync29(join38(stateDir, "runtime", "projects"))) {
43902
44549
  return stateDir;
43903
44550
  }
43904
44551
  const env = options?.env ?? process.env;
43905
44552
  const explicit = env.KXM_STATE_HOME?.trim() || env.KXM_USER_STATE_DIR?.trim() || env.KXM_STATE_ROOT?.trim();
43906
- if (explicit && isAbsolute9(explicit) && existsSync28(resolve26(explicit))) {
43907
- return resolve26(explicit);
44553
+ if (explicit && isAbsolute9(explicit) && existsSync29(resolve25(explicit))) {
44554
+ return resolve25(explicit);
43908
44555
  }
43909
44556
  let base;
43910
44557
  if (process.platform === "win32") {
43911
44558
  const localAppData = env.LOCALAPPDATA?.trim();
43912
- base = localAppData && isAbsolute9(localAppData) ? localAppData : join37(homedir6(), "AppData", "Local");
43913
- base = resolve26(base, "KXM");
44559
+ base = localAppData && isAbsolute9(localAppData) ? localAppData : join38(homedir6(), "AppData", "Local");
44560
+ base = resolve25(base, "KXM");
43914
44561
  } else if (process.platform === "darwin") {
43915
- base = resolve26(homedir6(), "Library", "Application Support", "KXM");
44562
+ base = resolve25(homedir6(), "Library", "Application Support", "KXM");
43916
44563
  } else {
43917
44564
  const xdgState = env.XDG_STATE_HOME?.trim();
43918
- base = xdgState && isAbsolute9(xdgState) ? xdgState : join37(homedir6(), ".local", "state");
43919
- base = resolve26(base, "kxm");
44565
+ base = xdgState && isAbsolute9(xdgState) ? xdgState : join38(homedir6(), ".local", "state");
44566
+ base = resolve25(base, "kxm");
43920
44567
  }
43921
- if (existsSync28(base)) return base;
44568
+ if (existsSync29(base)) return base;
43922
44569
  return void 0;
43923
44570
  }
43924
44571
  function readRuntimeRuns(eventDb, projectId) {
@@ -43951,7 +44598,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
43951
44598
  let legacyRuns = [];
43952
44599
  let legacyRunTotal = 0;
43953
44600
  let plans = [];
43954
- if (existsSync28(dataPath)) {
44601
+ if (existsSync29(dataPath)) {
43955
44602
  hasLegacy = true;
43956
44603
  const database = openReadOnlyDatabase(dataPath);
43957
44604
  try {
@@ -43982,11 +44629,11 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
43982
44629
  let kxmRunTotal = 0;
43983
44630
  const kxmStateRoot = scope && !scope.runtimeProjectId ? void 0 : resolveKxmStateRoot(stateDir, options);
43984
44631
  if (kxmStateRoot) {
43985
- const runtimeDir = join37(kxmStateRoot, "runtime");
43986
- const registryDbPath = join37(runtimeDir, "registry.db");
43987
- const projectsDir = join37(runtimeDir, "projects");
44632
+ const runtimeDir = join38(kxmStateRoot, "runtime");
44633
+ const registryDbPath = join38(runtimeDir, "registry.db");
44634
+ const projectsDir = join38(runtimeDir, "projects");
43988
44635
  const projectKeys = /* @__PURE__ */ new Set();
43989
- if (existsSync28(registryDbPath)) {
44636
+ if (existsSync29(registryDbPath)) {
43990
44637
  hasKxm = true;
43991
44638
  try {
43992
44639
  const regDb = openReadOnlyDatabase(registryDbPath);
@@ -44002,7 +44649,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
44002
44649
  } catch {
44003
44650
  }
44004
44651
  }
44005
- if (!scope && existsSync28(projectsDir)) {
44652
+ if (!scope && existsSync29(projectsDir)) {
44006
44653
  try {
44007
44654
  for (const entry of readdirSync11(projectsDir, { withFileTypes: true })) {
44008
44655
  if (entry.isDirectory()) {
@@ -44014,8 +44661,8 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
44014
44661
  }
44015
44662
  for (const key of projectKeys) {
44016
44663
  if (scope && !RUNTIME_PROJECT_KEY.test(key)) continue;
44017
- const eventDbPath = join37(projectsDir, key, "run-events.db");
44018
- if (existsSync28(eventDbPath)) {
44664
+ const eventDbPath = join38(projectsDir, key, "run-events.db");
44665
+ if (existsSync29(eventDbPath)) {
44019
44666
  hasKxm = true;
44020
44667
  try {
44021
44668
  const eventDb = openReadOnlyDatabase(eventDbPath);
@@ -44033,10 +44680,10 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
44033
44680
  }
44034
44681
  }
44035
44682
  const pids = [];
44036
- if (existsSync28(stateDir)) {
44683
+ if (existsSync29(stateDir)) {
44037
44684
  for (const file of readdirSync11(stateDir).filter((name) => name.endsWith(".pid"))) {
44038
44685
  try {
44039
- const record = JSON.parse(readFileSync28(join37(stateDir, file), "utf8"));
44686
+ const record = JSON.parse(readFileSync29(join38(stateDir, file), "utf8"));
44040
44687
  pids.push({
44041
44688
  file,
44042
44689
  ...record.role ? { role: record.role } : {},
@@ -44075,8 +44722,8 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
44075
44722
  } else {
44076
44723
  source = "legacy";
44077
44724
  }
44078
- const telemetryFile = join37(stateDir, "telemetry.jsonl");
44079
- const spend = existsSync28(telemetryFile) ? readRoutingRecords(telemetryFile) : [];
44725
+ const telemetryFile = join38(stateDir, "telemetry.jsonl");
44726
+ const spend = existsSync29(telemetryFile) ? readRoutingRecords(telemetryFile) : [];
44080
44727
  return {
44081
44728
  source,
44082
44729
  agents: agents.sort((a, b2) => Number(b2.online) - Number(a.online) || a.name.localeCompare(b2.name)),
@@ -44210,11 +44857,11 @@ function spawnDegradeWorktree(repoRoot, runId, options) {
44210
44857
  const cleanRunId = runId.replace(/^run_/, "");
44211
44858
  const desc = options?.description ? slugifyBranchPart(options.description, 30) : "degrade";
44212
44859
  const branchName = `kxm/run-${cleanRunId}-${desc}`;
44213
- const relativeWtPath = join38(".kxm", "worktrees", `run-${cleanRunId}-${desc}`);
44214
- const absoluteWtPath = resolve27(repoRoot, relativeWtPath);
44860
+ const relativeWtPath = join39(".kxm", "worktrees", `run-${cleanRunId}-${desc}`);
44861
+ const absoluteWtPath = resolve26(repoRoot, relativeWtPath);
44215
44862
  const jumpCommand = `cd "${absoluteWtPath}"`;
44216
44863
  try {
44217
- mkdirSync24(dirname20(absoluteWtPath), { recursive: true });
44864
+ mkdirSync24(dirname19(absoluteWtPath), { recursive: true });
44218
44865
  const branchCheck = runner("git", ["rev-parse", "--verify", `refs/heads/${branchName}`]);
44219
44866
  let wtRes;
44220
44867
  if (branchCheck.status === 0) {
@@ -44612,11 +45259,11 @@ async function readJson(response) {
44612
45259
  }
44613
45260
  async function waitForReconnect(signal, milliseconds = 1e3) {
44614
45261
  if (signal.aborted) return;
44615
- await new Promise((resolve35) => {
45262
+ await new Promise((resolve34) => {
44616
45263
  const done = () => {
44617
45264
  clearTimeout(timer);
44618
45265
  signal.removeEventListener("abort", done);
44619
- resolve35();
45266
+ resolve34();
44620
45267
  };
44621
45268
  const timer = setTimeout(done, milliseconds);
44622
45269
  timer.unref();
@@ -44979,8 +45626,8 @@ async function runMeshTui(input) {
44979
45626
  // plugins/kxm/src/session-work.ts
44980
45627
  import { spawnSync as spawnSync6 } from "node:child_process";
44981
45628
  import { randomUUID as randomUUID12 } from "node:crypto";
44982
- import { existsSync as existsSync29, mkdirSync as mkdirSync25, readFileSync as readFileSync29, renameSync as renameSync8, writeFileSync as writeFileSync22 } from "node:fs";
44983
- import { join as join39 } from "node:path";
45629
+ import { existsSync as existsSync30, mkdirSync as mkdirSync25, readFileSync as readFileSync30, renameSync as renameSync8, writeFileSync as writeFileSync22 } from "node:fs";
45630
+ import { join as join40 } from "node:path";
44984
45631
  var MAX_SESSION_BRIEF_TASKS = 5;
44985
45632
  var MAX_SESSION_BRIEF_PLANS = 5;
44986
45633
  var SESSION_BRIEF_SCHEMA = "kxm.session-brief.v1";
@@ -45137,10 +45784,10 @@ function buildSessionBrief(snapshot, current, hub, ship, updateLatest, cost, ses
45137
45784
  };
45138
45785
  }
45139
45786
  function readCachedSessionBrief(stateDir) {
45140
- const file = join39(stateDir, "session-brief.json");
45787
+ const file = join40(stateDir, "session-brief.json");
45141
45788
  try {
45142
- if (!existsSync29(file)) return void 0;
45143
- const raw = readFileSync29(file, "utf8");
45789
+ if (!existsSync30(file)) return void 0;
45790
+ const raw = readFileSync30(file, "utf8");
45144
45791
  const parsed = JSON.parse(raw);
45145
45792
  if (parsed && parsed.schema === "kxm.session-brief.v1" && typeof parsed.generatedAt === "string") {
45146
45793
  const ageMs = Date.now() - Date.parse(parsed.generatedAt);
@@ -45156,7 +45803,7 @@ function readCachedSessionBrief(stateDir) {
45156
45803
  function writeCachedSessionBrief(stateDir, brief) {
45157
45804
  try {
45158
45805
  mkdirSync25(stateDir, { recursive: true, mode: 448 });
45159
- const file = join39(stateDir, "session-brief.json");
45806
+ const file = join40(stateDir, "session-brief.json");
45160
45807
  const tmp = `${file}.tmp.${randomUUID12().slice(0, 8)}`;
45161
45808
  writeFileSync22(tmp, JSON.stringify(brief, null, 2), { encoding: "utf8", mode: 384 });
45162
45809
  renameSync8(tmp, file);
@@ -45165,8 +45812,8 @@ function writeCachedSessionBrief(stateDir, brief) {
45165
45812
  }
45166
45813
  function estimateSessionCost(stateDir) {
45167
45814
  try {
45168
- const telemetryFile = join39(stateDir, "telemetry.jsonl");
45169
- if (!existsSync29(telemetryFile)) return void 0;
45815
+ const telemetryFile = join40(stateDir, "telemetry.jsonl");
45816
+ if (!existsSync30(telemetryFile)) return void 0;
45170
45817
  const records = readRoutingRecords(telemetryFile);
45171
45818
  if (records.length === 0) return void 0;
45172
45819
  let totalCost = 0;
@@ -45334,11 +45981,11 @@ function formatSessionBriefText(brief) {
45334
45981
  }
45335
45982
 
45336
45983
  // plugins/kxm/src/kxm-install-kind.ts
45337
- import { existsSync as existsSync30, readFileSync as readFileSync30 } from "node:fs";
45338
- import { dirname as dirname21, join as join40 } from "node:path";
45984
+ import { existsSync as existsSync31, readFileSync as readFileSync31 } from "node:fs";
45985
+ import { dirname as dirname20, join as join41 } from "node:path";
45339
45986
  function packageIsKxm(root) {
45340
45987
  try {
45341
- const pkg = JSON.parse(readFileSync30(join40(root, "package.json"), "utf8"));
45988
+ const pkg = JSON.parse(readFileSync31(join41(root, "package.json"), "utf8"));
45342
45989
  return pkg.name === "@kontextmind/kxm";
45343
45990
  } catch {
45344
45991
  return false;
@@ -45387,12 +46034,12 @@ function report(kind, root) {
45387
46034
  return { kind, root, instruction: installKindInstruction(kind, root) };
45388
46035
  }
45389
46036
  function classifyInstallRoot(probe) {
45390
- const piGitRoot = join40(probe.homeDir, ".pi", "agent", "git");
46037
+ const piGitRoot = join41(probe.homeDir, ".pi", "agent", "git");
45391
46038
  if (isUnder(probe.repoRoot, piGitRoot, probe.platform)) return report("pi-git", probe.repoRoot);
45392
- const claudeHome = probe.env.CLAUDE_CONFIG_DIR?.trim() || join40(probe.homeDir, ".claude");
45393
- const pluginsRoot = join40(claudeHome, "plugins");
46039
+ const claudeHome = probe.env.CLAUDE_CONFIG_DIR?.trim() || join41(probe.homeDir, ".claude");
46040
+ const pluginsRoot = join41(claudeHome, "plugins");
45394
46041
  if (isUnder(probe.moduleDir, pluginsRoot, probe.platform)) return report("claude-marketplace", probe.moduleDir);
45395
- const hasGit = existsSync30(join40(probe.repoRoot, ".git"));
46042
+ const hasGit = existsSync31(join41(probe.repoRoot, ".git"));
45396
46043
  if (hasGit && packageIsKxm(probe.repoRoot)) return report("source", probe.repoRoot);
45397
46044
  if (isNpmPackageLayout(probe.repoRoot, probe.platform) && packageIsKxm(probe.repoRoot) && !hasGit) {
45398
46045
  return report("npm-package", probe.repoRoot);
@@ -45406,21 +46053,21 @@ function resolveInstallKind(probe, npmGlobalRoot) {
45406
46053
  if (typeof globalRoot !== "string" || globalRoot.trim() === "") {
45407
46054
  return report("unknown", classified.root);
45408
46055
  }
45409
- const expected = dirname21(dirname21(classified.root));
46056
+ const expected = dirname20(dirname20(classified.root));
45410
46057
  if (sameHostPath2(globalRoot.trim(), expected, probe.platform)) return report("npm-global", classified.root);
45411
46058
  return report("npm-local", classified.root);
45412
46059
  }
45413
46060
 
45414
46061
  // plugins/kxm/src/kxm-update-config.ts
45415
46062
  var import_yaml16 = __toESM(require_dist(), 1);
45416
- import { existsSync as existsSync31, readFileSync as readFileSync31 } from "node:fs";
45417
- import { join as join41 } from "node:path";
46063
+ import { existsSync as existsSync32, readFileSync as readFileSync32 } from "node:fs";
46064
+ import { join as join42 } from "node:path";
45418
46065
  function loadKxmUpdateConfig(env = process.env) {
45419
- const path4 = join41(kxmUserStateRoot({ env }), "update.yaml");
45420
- if (!existsSync31(path4)) return { schema: KXM_UPDATE_SCHEMA, auto: false, source: "github" };
46066
+ const path4 = join42(kxmUserStateRoot({ env }), "update.yaml");
46067
+ if (!existsSync32(path4)) return { schema: KXM_UPDATE_SCHEMA, auto: false, source: "github" };
45421
46068
  let parsed;
45422
46069
  try {
45423
- parsed = (0, import_yaml16.parse)(readFileSync31(path4, "utf8"));
46070
+ parsed = (0, import_yaml16.parse)(readFileSync32(path4, "utf8"));
45424
46071
  } catch {
45425
46072
  throw new KxmUpdateConfigError("update.yaml is not valid YAML");
45426
46073
  }
@@ -45446,8 +46093,8 @@ function loadKxmUpdateConfig(env = process.env) {
45446
46093
  }
45447
46094
 
45448
46095
  // plugins/kxm/src/session.ts
45449
- import { existsSync as existsSync32, mkdirSync as mkdirSync26, readFileSync as readFileSync32, writeFileSync as writeFileSync23 } from "node:fs";
45450
- import { join as join42 } from "node:path";
46096
+ import { existsSync as existsSync33, mkdirSync as mkdirSync26, readFileSync as readFileSync33, writeFileSync as writeFileSync23 } from "node:fs";
46097
+ import { join as join43 } from "node:path";
45451
46098
  var SESSION_SCHEMA = "kxm.session.v1";
45452
46099
  var SessionConfigError = class extends Error {
45453
46100
  code = "session_config_invalid";
@@ -45457,12 +46104,12 @@ var SessionConfigError = class extends Error {
45457
46104
  }
45458
46105
  };
45459
46106
  function workflowAssetDirs(assetsDir, workflowId) {
45460
- const root = join42(assetsDir, "workflows", workflowId);
45461
- return [root, join42(root, "inputs"), join42(root, "outputs"), join42(root, "generated")];
46107
+ const root = join43(assetsDir, "workflows", workflowId);
46108
+ return [root, join43(root, "inputs"), join43(root, "outputs"), join43(root, "generated")];
45462
46109
  }
45463
46110
  function sessionAssetDirs(assetsDir, sessionId) {
45464
- const root = join42(assetsDir, "sessions", sessionId);
45465
- return [root, join42(root, "inputs"), join42(root, "outputs")];
46111
+ const root = join43(assetsDir, "sessions", sessionId);
46112
+ return [root, join43(root, "inputs"), join43(root, "outputs")];
45466
46113
  }
45467
46114
  function rosterNames(configDir) {
45468
46115
  return [...loadRosterMap(configDir).values()].map((row) => String(row.name));
@@ -45512,7 +46159,7 @@ function workerFromRosterRow(name, row, project) {
45512
46159
  function loadRosterMap(configDir) {
45513
46160
  const byName = /* @__PURE__ */ new Map();
45514
46161
  for (const [file, key] of [["agents.json", "agents"], ["gates.json", "gates"]]) {
45515
- const path4 = join42(configDir, file);
46162
+ const path4 = join43(configDir, file);
45516
46163
  for (const row of loadRoster(path4, key)) {
45517
46164
  const name = String(row.name).trim();
45518
46165
  const lowered = name.toLowerCase();
@@ -45526,10 +46173,10 @@ function loadRosterMap(configDir) {
45526
46173
  return byName;
45527
46174
  }
45528
46175
  function loadRoster(path4, key) {
45529
- if (!existsSync32(path4)) return [];
46176
+ if (!existsSync33(path4)) return [];
45530
46177
  let parsed;
45531
46178
  try {
45532
- parsed = JSON.parse(readFileSync32(path4, "utf8"));
46179
+ parsed = JSON.parse(readFileSync33(path4, "utf8"));
45533
46180
  } catch (error) {
45534
46181
  throw new SessionConfigError(
45535
46182
  `${key} roster at ${path4} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
@@ -45554,7 +46201,7 @@ function loadRoster(path4, key) {
45554
46201
  });
45555
46202
  }
45556
46203
  function createSession(input) {
45557
- const assetDir = input.mode === "workflow" && input.workflowId ? join42("assets", "workflows", input.workflowId) : join42("assets", "sessions", input.id);
46204
+ const assetDir = input.mode === "workflow" && input.workflowId ? join43("assets", "workflows", input.workflowId) : join43("assets", "sessions", input.id);
45558
46205
  return {
45559
46206
  schema: SESSION_SCHEMA,
45560
46207
  id: input.id,
@@ -45567,8 +46214,8 @@ function createSession(input) {
45567
46214
  };
45568
46215
  }
45569
46216
  function writeSession(assetsDir, session, dryRun = false) {
45570
- const dir = join42(assetsDir, "sessions", session.id);
45571
- const path4 = join42(dir, "session.json");
46217
+ const dir = join43(assetsDir, "sessions", session.id);
46218
+ const path4 = join43(dir, "session.json");
45572
46219
  if (!dryRun) {
45573
46220
  mkdirSync26(dir, { recursive: true });
45574
46221
  writeFileSync23(path4, `${JSON.stringify(session, null, 2)}
@@ -45579,7 +46226,7 @@ function writeSession(assetsDir, session, dryRun = false) {
45579
46226
 
45580
46227
  // plugins/kxm/src/cli/hub.ts
45581
46228
  import { fileURLToPath as fileURLToPath3 } from "node:url";
45582
- import { dirname as dirname22 } from "node:path";
46229
+ import { dirname as dirname21 } from "node:path";
45583
46230
  import { homedir as homedir7 } from "node:os";
45584
46231
  async function hubGet(url, fetchImpl) {
45585
46232
  try {
@@ -45597,7 +46244,7 @@ async function hubGet(url, fetchImpl) {
45597
46244
  }
45598
46245
  function installProbeFrom(runtime) {
45599
46246
  const partial = runtime.io.installProbe ?? {};
45600
- const moduleDir = partial.moduleDir ?? dirname22(fileURLToPath3(import.meta.url));
46247
+ const moduleDir = partial.moduleDir ?? dirname21(fileURLToPath3(import.meta.url));
45601
46248
  return {
45602
46249
  moduleDir,
45603
46250
  // Classify the install that is **running**, never the directory the caller
@@ -45618,13 +46265,13 @@ function moduleInstallRoot() {
45618
46265
  try {
45619
46266
  return findKxmRepoRoot(import.meta.url);
45620
46267
  } catch {
45621
- return dirname22(fileURLToPath3(import.meta.url));
46268
+ return dirname21(fileURLToPath3(import.meta.url));
45622
46269
  }
45623
46270
  }
45624
46271
  function warnIgnoredProjectUpdateYaml(runtime) {
45625
- const projectFile = join43(runtime.dirs.workspace, "update.yaml");
45626
- if (!existsSync33(projectFile)) return;
45627
- const userFile = join43(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
46272
+ const projectFile = join44(runtime.dirs.workspace, "update.yaml");
46273
+ if (!existsSync34(projectFile)) return;
46274
+ const userFile = join44(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
45628
46275
  runtime.io.stderr(`kxm: ignoring .kxm/update.yaml in ${runtime.dirs.workdir}; update settings are read only from ${userFile}
45629
46276
  `);
45630
46277
  }
@@ -45672,7 +46319,7 @@ async function cmdDash(runtime, options = {}) {
45672
46319
  return 2;
45673
46320
  }
45674
46321
  const screen = requested;
45675
- const dataPath = resolve28(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join43(runtime.dirs.state, "kxm.db"));
46322
+ const dataPath = resolve27(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join44(runtime.dirs.state, "kxm.db"));
45676
46323
  if (runtime.dryRun) {
45677
46324
  print(runtime.io, runtime.json, {
45678
46325
  ok: true,
@@ -45718,7 +46365,7 @@ async function cmdHub(runtime) {
45718
46365
  config = loadKxmUpdateConfig(runtime.env);
45719
46366
  } catch (error) {
45720
46367
  if (error instanceof KxmUpdateConfigError) {
45721
- const yamlPath = join43(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
46368
+ const yamlPath = join44(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
45722
46369
  runtime.io.stderr(`kxm: ${error.message}; update check skipped; fix or remove ${yamlPath}
45723
46370
  `);
45724
46371
  } else {
@@ -45905,7 +46552,7 @@ async function cmdWorker(runtime, options) {
45905
46552
  return await (runtime.io.spawnWorker ?? ((launchEnv) => spawnScript("kxm-worker.mjs", launchEnv)))(extraEnv);
45906
46553
  }
45907
46554
  async function cmdStop(runtime, waitMsFlag) {
45908
- const pids = existsSync33(runtime.dirs.state) ? readdirSync12(runtime.dirs.state).filter((name) => name.endsWith(".pid")) : [];
46555
+ const pids = existsSync34(runtime.dirs.state) ? readdirSync12(runtime.dirs.state).filter((name) => name.endsWith(".pid")) : [];
45909
46556
  if (runtime.dryRun) {
45910
46557
  print(runtime.io, runtime.json, { ok: true, command: "stop", dryRun: true, pidFiles: pids }, "would signal pid files");
45911
46558
  return 0;
@@ -45920,7 +46567,7 @@ async function cmdStop(runtime, waitMsFlag) {
45920
46567
  const orphans = [];
45921
46568
  for (const file of pids) {
45922
46569
  try {
45923
- const record = JSON.parse(readFileSync33(join43(runtime.dirs.state, file), "utf8"));
46570
+ const record = JSON.parse(readFileSync34(join44(runtime.dirs.state, file), "utf8"));
45924
46571
  const expectedControl = file === "hub.pid" ? "hub.stop" : file.startsWith("worker-") ? `${file.slice(0, -4)}.stop` : void 0;
45925
46572
  const expectedRole = file === "hub.pid" ? "hub" : file.startsWith("worker-") ? "worker" : void 0;
45926
46573
  if (record.version !== 1 || !Number.isInteger(record.pid) || record.pid <= 0 || !record.startedAt || !expectedControl || record.controlFile !== expectedControl || record.role !== expectedRole) {
@@ -45940,7 +46587,7 @@ async function cmdStop(runtime, waitMsFlag) {
45940
46587
  ignored.push(file);
45941
46588
  continue;
45942
46589
  }
45943
- writeFileSync24(join43(runtime.dirs.state, record.controlFile), `${JSON.stringify({ startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {}, requestedAt: (/* @__PURE__ */ new Date()).toISOString() })}
46590
+ writeFileSync24(join44(runtime.dirs.state, record.controlFile), `${JSON.stringify({ startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {}, requestedAt: (/* @__PURE__ */ new Date()).toISOString() })}
45944
46591
  `, { encoding: "utf8", mode: 384 });
45945
46592
  requested.push(file);
45946
46593
  records.set(file, { pid: record.pid, startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {} });
@@ -45958,7 +46605,7 @@ async function cmdStop(runtime, waitMsFlag) {
45958
46605
  while (Date.now() <= deadline && stopped.size < requested.length) {
45959
46606
  for (const [file, record] of records) {
45960
46607
  try {
45961
- const current = JSON.parse(readFileSync33(join43(runtime.dirs.state, file), "utf8"));
46608
+ const current = JSON.parse(readFileSync34(join44(runtime.dirs.state, file), "utf8"));
45962
46609
  if (current.pid !== record.pid || current.startedAt !== record.startedAt || current.generation !== record.generation || !processExists(record.pid)) stopped.add(file);
45963
46610
  } catch {
45964
46611
  stopped.add(file);
@@ -45971,7 +46618,7 @@ async function cmdStop(runtime, waitMsFlag) {
45971
46618
  for (const file of orphans) {
45972
46619
  while (Date.now() <= deadlineOrphans) {
45973
46620
  try {
45974
- const current = JSON.parse(readFileSync33(join43(runtime.dirs.state, file), "utf8"));
46621
+ const current = JSON.parse(readFileSync34(join44(runtime.dirs.state, file), "utf8"));
45975
46622
  if (!Number.isInteger(current.serverPid) || !processExists(current.serverPid)) break;
45976
46623
  } catch {
45977
46624
  break;
@@ -45979,14 +46626,14 @@ async function cmdStop(runtime, waitMsFlag) {
45979
46626
  await (runtime.io.sleep ?? ((ms) => new Promise((resolveSleep) => setTimeout(resolveSleep, ms))))(100);
45980
46627
  }
45981
46628
  try {
45982
- const current = JSON.parse(readFileSync33(join43(runtime.dirs.state, file), "utf8"));
46629
+ const current = JSON.parse(readFileSync34(join44(runtime.dirs.state, file), "utf8"));
45983
46630
  if (Number.isInteger(current.serverPid) && processExists(current.serverPid)) {
45984
46631
  try {
45985
46632
  process.kill(current.serverPid, "SIGKILL");
45986
46633
  } catch {
45987
46634
  }
45988
46635
  }
45989
- rmSync11(join43(runtime.dirs.state, file), { force: true });
46636
+ rmSync11(join44(runtime.dirs.state, file), { force: true });
45990
46637
  } catch {
45991
46638
  }
45992
46639
  stopped.add(file);
@@ -45997,11 +46644,11 @@ async function cmdStop(runtime, waitMsFlag) {
45997
46644
  }
45998
46645
  async function cmdSessionStatus(runtime) {
45999
46646
  const stateDir = runtime.dirs.state;
46000
- const names3 = existsSync33(stateDir) ? readdirSync12(stateDir) : [];
46647
+ const names3 = existsSync34(stateDir) ? readdirSync12(stateDir) : [];
46001
46648
  const claims = [];
46002
46649
  for (const file of names3.filter((name) => name.endsWith(".pid"))) {
46003
46650
  try {
46004
- const record = JSON.parse(readFileSync33(join43(stateDir, file), "utf8"));
46651
+ const record = JSON.parse(readFileSync34(join44(stateDir, file), "utf8"));
46005
46652
  claims.push({
46006
46653
  file,
46007
46654
  role: record.role,
@@ -46016,7 +46663,7 @@ async function cmdSessionStatus(runtime) {
46016
46663
  const recoveries = [];
46017
46664
  for (const file of names3.filter((name) => name.startsWith("worker-recovery-") && name.endsWith(".json"))) {
46018
46665
  try {
46019
- const envelope = JSON.parse(readFileSync33(join43(stateDir, file), "utf8"));
46666
+ const envelope = JSON.parse(readFileSync34(join44(stateDir, file), "utf8"));
46020
46667
  recoveries.push({
46021
46668
  file,
46022
46669
  reason: envelope.reason,
@@ -46042,7 +46689,7 @@ async function cmdSessionStatus(runtime) {
46042
46689
  async function cmdAuthToken(runtime, options = {}) {
46043
46690
  const tokenFile = sessionTokenPath(runtime.env.KXM_USER_CONFIG_DIR);
46044
46691
  if (options.clear && runtime.dryRun) {
46045
- const present = existsSync33(tokenFile);
46692
+ const present = existsSync34(tokenFile);
46046
46693
  printPlan(runtime, { command: "auth token", cleared: present }, present ? [{ action: "delete", target: tokenFile }] : [], present ? "clear the session token from disk" : "no session token file to clear");
46047
46694
  return 0;
46048
46695
  }
@@ -46131,7 +46778,7 @@ async function cmdSessionBrief(runtime, options = {}) {
46131
46778
  print(runtime.io, runtime.json, { ok: true, command: "session brief", sessionToken }, sessionToken);
46132
46779
  return 0;
46133
46780
  }
46134
- const dataPath = resolve28(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join43(runtime.dirs.state, "kxm.db"));
46781
+ const dataPath = resolve27(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join44(runtime.dirs.state, "kxm.db"));
46135
46782
  const env = {
46136
46783
  ...runtime.env,
46137
46784
  KXM_STATE_DIR: runtime.dirs.state,
@@ -46157,7 +46804,7 @@ async function cmdSessionBrief(runtime, options = {}) {
46157
46804
  writeCache: !runtime.dryRun
46158
46805
  });
46159
46806
  if (runtime.dryRun) {
46160
- if (!readCachedSessionBrief(runtime.dirs.state)) planned.push({ action: "write", target: join43(runtime.dirs.state, "session-brief.json") });
46807
+ if (!readCachedSessionBrief(runtime.dirs.state)) planned.push({ action: "write", target: join44(runtime.dirs.state, "session-brief.json") });
46161
46808
  print(runtime.io, runtime.json, { ...brief, dryRun: true, planned }, [
46162
46809
  options.status ? brief.statusLine : formatSessionBriefText(brief),
46163
46810
  ...planned.map((change) => `dry run: would ${change.action} ${change.target}`)
@@ -46232,16 +46879,16 @@ async function cmdSessionStart(runtime, options) {
46232
46879
 
46233
46880
  // plugins/kxm/src/cli/system.ts
46234
46881
  import { spawnSync as spawnSync8 } from "node:child_process";
46235
- import { createHash as createHash16 } from "node:crypto";
46236
- import { existsSync as existsSync40, mkdtempSync as mkdtempSync3, readFileSync as readFileSync38, rmSync as rmSync13 } from "node:fs";
46882
+ import { createHash as createHash17 } from "node:crypto";
46883
+ import { existsSync as existsSync41, mkdtempSync as mkdtempSync3, readFileSync as readFileSync39, rmSync as rmSync13 } from "node:fs";
46237
46884
  import { tmpdir as tmpdir2 } from "node:os";
46238
- import { join as join49, resolve as resolve33 } from "node:path";
46885
+ import { basename as basename10, extname as extname3, join as join50, resolve as resolve32 } from "node:path";
46239
46886
  import { createInterface as createInterface3 } from "node:readline";
46240
46887
 
46241
46888
  // plugins/kxm/src/improve.ts
46242
- import { createHash as createHash15 } from "node:crypto";
46243
- import { existsSync as existsSync34, mkdirSync as mkdirSync28, writeFileSync as writeFileSync25 } from "node:fs";
46244
- import { join as join44, relative as relative7, resolve as resolve29 } from "node:path";
46889
+ import { createHash as createHash16 } from "node:crypto";
46890
+ import { existsSync as existsSync35, mkdirSync as mkdirSync28, writeFileSync as writeFileSync25 } from "node:fs";
46891
+ import { join as join45, relative as relative7, resolve as resolve28 } from "node:path";
46245
46892
  var CANDIDATE_SCHEMA = "kxm.candidate.v1";
46246
46893
  var IMPROVEMENT_REPORT_SCHEMA = "kxm.improvement-report.v2";
46247
46894
  function classifyCandidateKind(stepId, agentRole) {
@@ -46456,7 +47103,7 @@ function groupRoutingRecords(records, options = {}) {
46456
47103
  }
46457
47104
  const candidateKind = isCandidate ? classifyCandidateKind(group.stepId, group.agentRole) : void 0;
46458
47105
  const safeSlug = group.stepId.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 16) || "step";
46459
- const keyHash = createHash15("sha256").update(`${group.workflowHash}:${group.stepId}:${group.agentRole}:${group.promptHash}`).digest("hex").slice(0, 10);
47106
+ const keyHash = createHash16("sha256").update(`${group.workflowHash}:${group.stepId}:${group.agentRole}:${group.promptHash}`).digest("hex").slice(0, 10);
46460
47107
  const candidateId = isCandidate && candidateKind ? `cand_${candidateKind.replace(/-/g, "_")}_${safeSlug}_${keyHash}` : void 0;
46461
47108
  keyed.push({
46462
47109
  key: group.key,
@@ -46496,8 +47143,8 @@ function groupRoutingRecords(records, options = {}) {
46496
47143
  }).map((entry) => entry.row);
46497
47144
  }
46498
47145
  function buildImprovementReport(records, options = {}) {
46499
- const projectRoot = options.projectRoot ? resolve29(options.projectRoot) : process.cwd();
46500
- const candidatesDir = options.candidatesDir ? resolve29(options.candidatesDir) : join44(projectRoot, ".kxm", "candidates");
47146
+ const projectRoot = options.projectRoot ? resolve28(options.projectRoot) : process.cwd();
47147
+ const candidatesDir = options.candidatesDir ? resolve28(options.candidatesDir) : join45(projectRoot, ".kxm", "candidates");
46501
47148
  const promotionPolicy = options.promotionPolicy ?? "manual_pr";
46502
47149
  const groups = groupRoutingRecords(records, options);
46503
47150
  const candidates = [];
@@ -46511,7 +47158,7 @@ function buildImprovementReport(records, options = {}) {
46511
47158
  group.workflowId
46512
47159
  );
46513
47160
  const diffFileName = `${group.candidateId}.diff`;
46514
- const diffFilePath = join44(candidatesDir, diffFileName);
47161
+ const diffFilePath = join45(candidatesDir, diffFileName);
46515
47162
  const relDiffPath = relative7(projectRoot, diffFilePath).replace(/\\/g, "/");
46516
47163
  const candidate = {
46517
47164
  schema: CANDIDATE_SCHEMA,
@@ -46533,11 +47180,11 @@ function buildImprovementReport(records, options = {}) {
46533
47180
  createdAt: nowIso()
46534
47181
  };
46535
47182
  if (!options.dryRun) {
46536
- if (!existsSync34(candidatesDir)) {
47183
+ if (!existsSync35(candidatesDir)) {
46537
47184
  mkdirSync28(candidatesDir, { recursive: true });
46538
47185
  }
46539
47186
  writeFileSync25(diffFilePath, diff, "utf8");
46540
- const jsonFilePath = join44(candidatesDir, `${group.candidateId}.json`);
47187
+ const jsonFilePath = join45(candidatesDir, `${group.candidateId}.json`);
46541
47188
  writeFileSync25(jsonFilePath, JSON.stringify(candidate, null, 2) + "\n", "utf8");
46542
47189
  }
46543
47190
  candidates.push(candidate);
@@ -46566,7 +47213,7 @@ function buildImprovementReport(records, options = {}) {
46566
47213
  }
46567
47214
  function writeImprovementReport(improvementsDir, report2, dryRun = false) {
46568
47215
  const stamp = report2.createdAt.replace(/[:.]/g, "-");
46569
- const path4 = join44(improvementsDir, `${stamp}.json`);
47216
+ const path4 = join45(improvementsDir, `${stamp}.json`);
46570
47217
  if (!dryRun) {
46571
47218
  mkdirSync28(improvementsDir, { recursive: true });
46572
47219
  writeFileSync25(path4, `${JSON.stringify(report2, null, 2)}
@@ -46645,8 +47292,8 @@ function evaluatePromotionPolicy(candidate, policy = "manual_pr", options = {})
46645
47292
  }
46646
47293
 
46647
47294
  // plugins/kxm/src/improve-sources.ts
46648
- import { existsSync as existsSync35 } from "node:fs";
46649
- import { resolve as resolve30 } from "node:path";
47295
+ import { existsSync as existsSync36 } from "node:fs";
47296
+ import { resolve as resolve29 } from "node:path";
46650
47297
  var ENGINE_EVENTS_SQL = "SELECT run_id, sequence, event_type, payload FROM events WHERE event_type IN ('routing.attempt.recorded','step.entered','run.status_changed') ORDER BY run_id, sequence";
46651
47298
  var SIMULATED_HARNESS = "driver-simulated";
46652
47299
  function parsePayload(text) {
@@ -46668,7 +47315,7 @@ function readEngineRoutingRecords(path4) {
46668
47315
  undecided: 0,
46669
47316
  duplicatesDropped: 0
46670
47317
  };
46671
- if (!existsSync35(path4)) return { records: [], source };
47318
+ if (!existsSync36(path4)) return { records: [], source };
46672
47319
  source.exists = true;
46673
47320
  let rows;
46674
47321
  let database;
@@ -46744,7 +47391,7 @@ function readEngineRoutingRecords(path4) {
46744
47391
  };
46745
47392
  }
46746
47393
  function readJsonlSource(kind, path4) {
46747
- const exists = existsSync35(path4);
47394
+ const exists = existsSync36(path4);
46748
47395
  const records = exists ? readRoutingRecords(path4).map((entry) => entry.routing) : [];
46749
47396
  return { records, source: { kind, path: path4, exists, records: records.length, duplicatesDropped: 0 } };
46750
47397
  }
@@ -46757,7 +47404,7 @@ function loadRoutingSources(options) {
46757
47404
  const projectRoot = discoverKxmProjectRoot(options.cwd);
46758
47405
  const reads = [];
46759
47406
  if (options.file !== void 0) {
46760
- reads.push(readJsonlSource("file", resolve30(options.cwd, options.file)));
47407
+ reads.push(readJsonlSource("file", resolve29(options.cwd, options.file)));
46761
47408
  } else {
46762
47409
  if (projectRoot !== void 0) reads.push(readEngineRoutingRecords(kxmProjectRunEventsPath(projectRoot, options.env)));
46763
47410
  reads.push(readJsonlSource("telemetry", telemetryPath(options.logsDir)));
@@ -46788,8 +47435,8 @@ function loadRoutingSources(options) {
46788
47435
 
46789
47436
  // plugins/kxm/src/modes.ts
46790
47437
  var import_yaml17 = __toESM(require_dist(), 1);
46791
- import { existsSync as existsSync36, readFileSync as readFileSync35 } from "node:fs";
46792
- import { join as join45 } from "node:path";
47438
+ import { existsSync as existsSync37, readFileSync as readFileSync36 } from "node:fs";
47439
+ import { join as join46 } from "node:path";
46793
47440
  var DEFAULT_MODES_CONFIG = Object.freeze({
46794
47441
  schema: "kxm.modes.v1",
46795
47442
  majorModes: {
@@ -46852,12 +47499,12 @@ function loadModesConfig(projectRoot) {
46852
47499
  if (!projectRoot) {
46853
47500
  return DEFAULT_MODES_CONFIG;
46854
47501
  }
46855
- const modesPath = join45(projectRoot, ".kxm", "modes.yaml");
46856
- if (!existsSync36(modesPath)) {
47502
+ const modesPath = join46(projectRoot, ".kxm", "modes.yaml");
47503
+ if (!existsSync37(modesPath)) {
46857
47504
  return DEFAULT_MODES_CONFIG;
46858
47505
  }
46859
47506
  try {
46860
- const raw = readFileSync35(modesPath, "utf8");
47507
+ const raw = readFileSync36(modesPath, "utf8");
46861
47508
  const parsed = (0, import_yaml17.parse)(raw);
46862
47509
  if (parsed && parsed.schema === "kxm.modes.v1" && parsed.majorModes) {
46863
47510
  return {
@@ -46943,11 +47590,11 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
46943
47590
  estimatedTokens: estimateTokens(baseSystemPromptChars)
46944
47591
  });
46945
47592
  for (const relPath of resolved.contextFiles) {
46946
- const fullPath = join45(projectRoot, relPath);
47593
+ const fullPath = join46(projectRoot, relPath);
46947
47594
  let chars = 0;
46948
- if (existsSync36(fullPath)) {
47595
+ if (existsSync37(fullPath)) {
46949
47596
  try {
46950
- chars = readFileSync35(fullPath, "utf8").length;
47597
+ chars = readFileSync36(fullPath, "utf8").length;
46951
47598
  } catch {
46952
47599
  chars = 0;
46953
47600
  }
@@ -47079,9 +47726,9 @@ ${divider}
47079
47726
 
47080
47727
  // plugins/kxm/src/ssh-remote.ts
47081
47728
  import { spawnSync as spawnSync7 } from "node:child_process";
47082
- import { existsSync as existsSync37, mkdirSync as mkdirSync29, readFileSync as readFileSync36, readdirSync as readdirSync13, rmSync as rmSync12, statSync as statSync5 } from "node:fs";
47729
+ import { existsSync as existsSync38, mkdirSync as mkdirSync29, readFileSync as readFileSync37, readdirSync as readdirSync13, rmSync as rmSync12, statSync as statSync5 } from "node:fs";
47083
47730
  import { homedir as homedir8 } from "node:os";
47084
- import { join as join46, resolve as resolve31 } from "node:path";
47731
+ import { join as join47, resolve as resolve30 } from "node:path";
47085
47732
  var MAX_SSH_OUTPUT_BYTES = 50 * 1024;
47086
47733
  var MAX_SSH_OUTPUT_LINES = 2e3;
47087
47734
  var DEFAULT_SOCKET_DIR = ".kxm/run/ssh-sockets";
@@ -47107,12 +47754,12 @@ function truncateSshOutput(raw) {
47107
47754
  return { text, truncated };
47108
47755
  }
47109
47756
  function parseSshConfig(configPath) {
47110
- const targetPath = configPath ?? join46(homedir8(), ".ssh", "config");
47111
- if (!existsSync37(targetPath)) {
47757
+ const targetPath = configPath ?? join47(homedir8(), ".ssh", "config");
47758
+ if (!existsSync38(targetPath)) {
47112
47759
  return [];
47113
47760
  }
47114
47761
  try {
47115
- const content = readFileSync36(targetPath, "utf-8");
47762
+ const content = readFileSync37(targetPath, "utf-8");
47116
47763
  const lines = content.split("\n");
47117
47764
  const hosts = [];
47118
47765
  let currentHosts = [];
@@ -47189,15 +47836,15 @@ function resolveSshHostG(host, execFn = spawnSync7) {
47189
47836
  }
47190
47837
  }
47191
47838
  function ensureSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
47192
- const resolved = resolve31(socketDir);
47193
- if (!existsSync37(resolved)) {
47839
+ const resolved = resolve30(socketDir);
47840
+ if (!existsSync38(resolved)) {
47194
47841
  mkdirSync29(resolved, { recursive: true, mode: 448 });
47195
47842
  }
47196
47843
  return resolved;
47197
47844
  }
47198
47845
  function buildSshArgs(options) {
47199
47846
  const socketDir = ensureSocketDir(options.socketDir ?? DEFAULT_SOCKET_DIR);
47200
- const controlPath = join46(socketDir, "%C");
47847
+ const controlPath = join47(socketDir, "%C");
47201
47848
  const persist = options.controlPersist ?? DEFAULT_CONTROL_PERSIST;
47202
47849
  const args = [
47203
47850
  "-o",
@@ -47222,7 +47869,7 @@ function buildSshArgs(options) {
47222
47869
  }
47223
47870
  function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync7) {
47224
47871
  const resolvedDir = ensureSocketDir(socketDir);
47225
- const controlPath = join46(resolvedDir, "%C");
47872
+ const controlPath = join47(resolvedDir, "%C");
47226
47873
  try {
47227
47874
  const result = execFn("ssh", ["-O", "check", "-o", `ControlPath=${controlPath}`, host], {
47228
47875
  encoding: "utf-8"
@@ -47234,7 +47881,7 @@ function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawn
47234
47881
  }
47235
47882
  function closeControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync7) {
47236
47883
  const resolvedDir = ensureSocketDir(socketDir);
47237
- const controlPath = join46(resolvedDir, "%C");
47884
+ const controlPath = join47(resolvedDir, "%C");
47238
47885
  try {
47239
47886
  const result = execFn("ssh", ["-O", "stop", "-o", `ControlPath=${controlPath}`, host], {
47240
47887
  encoding: "utf-8"
@@ -47679,9 +48326,9 @@ complete -c kxm -n "__fish_seen_subcommand_from goal" -a "create list get"
47679
48326
  }
47680
48327
 
47681
48328
  // plugins/kxm/src/completion-install.ts
47682
- import { existsSync as existsSync38, mkdirSync as mkdirSync30, readFileSync as readFileSync37, writeFileSync as writeFileSync26 } from "node:fs";
48329
+ import { existsSync as existsSync39, mkdirSync as mkdirSync30, readFileSync as readFileSync38, writeFileSync as writeFileSync26 } from "node:fs";
47683
48330
  import { homedir as homedir9 } from "node:os";
47684
- import { basename as basename9, delimiter, dirname as dirname24, isAbsolute as isAbsolute10, join as join47, resolve as resolve32 } from "node:path";
48331
+ import { basename as basename9, delimiter, dirname as dirname23, isAbsolute as isAbsolute10, join as join48, resolve as resolve31 } from "node:path";
47685
48332
  var COMPLETION_MARKER = "# kxm completion";
47686
48333
  var PATH_MARKER = "# kxm path";
47687
48334
  function detectShell(env = process.env, platform = process.platform) {
@@ -47697,31 +48344,31 @@ function effectiveHome(options) {
47697
48344
  const env = options.env ?? process.env;
47698
48345
  if (options.homeDir) return options.homeDir;
47699
48346
  const envHome = env.HOME?.trim() ?? env.USERPROFILE?.trim();
47700
- return envHome && envHome.length > 0 ? resolve32(envHome) : homedir9();
48347
+ return envHome && envHome.length > 0 ? resolve31(envHome) : homedir9();
47701
48348
  }
47702
48349
  function completionScriptPath(shell, options = {}) {
47703
48350
  const env = options.env ?? process.env;
47704
48351
  const explicit = options.configDir ?? env.KXM_USER_CONFIG_DIR?.trim();
47705
- const base = explicit && explicit.length > 0 ? resolve32(explicit) : resolve32(effectiveHome(options), ".config", "kxm");
47706
- return join47(base, "completions", `kxm.${shell}`);
48352
+ const base = explicit && explicit.length > 0 ? resolve31(explicit) : resolve31(effectiveHome(options), ".config", "kxm");
48353
+ return join48(base, "completions", `kxm.${shell}`);
47707
48354
  }
47708
48355
  function bashRcCandidate(options) {
47709
48356
  const home = effectiveHome(options);
47710
- const candidates = [join47(home, ".bashrc"), join47(home, ".bash_profile")];
47711
- const existing = candidates.find((candidate) => existsSync38(candidate));
48357
+ const candidates = [join48(home, ".bashrc"), join48(home, ".bash_profile")];
48358
+ const existing = candidates.find((candidate) => existsSync39(candidate));
47712
48359
  return existing ?? candidates[0];
47713
48360
  }
47714
48361
  function zshRcCandidate(options) {
47715
48362
  const env = options.env ?? process.env;
47716
48363
  const home = effectiveHome(options);
47717
- if (env.ZDOTDIR?.trim()) return join47(resolve32(env.ZDOTDIR.trim()), ".zshrc");
47718
- return join47(home, ".zshrc");
48364
+ if (env.ZDOTDIR?.trim()) return join48(resolve31(env.ZDOTDIR.trim()), ".zshrc");
48365
+ return join48(home, ".zshrc");
47719
48366
  }
47720
48367
  function fishCompletionTarget(shell, options) {
47721
48368
  const env = options.env ?? process.env;
47722
48369
  const home = effectiveHome(options);
47723
- if (env.XDG_CONFIG_HOME?.trim()) return join47(resolve32(env.XDG_CONFIG_HOME.trim()), "fish", "completions", "kxm.fish");
47724
- return join47(home, ".config", "fish", "completions", "kxm.fish");
48370
+ if (env.XDG_CONFIG_HOME?.trim()) return join48(resolve31(env.XDG_CONFIG_HOME.trim()), "fish", "completions", "kxm.fish");
48371
+ return join48(home, ".config", "fish", "completions", "kxm.fish");
47725
48372
  }
47726
48373
  function completionRcTarget(shell, options = {}) {
47727
48374
  if (shell === "fish") {
@@ -47753,16 +48400,16 @@ function installShellCompletion(shellInput, options = {}) {
47753
48400
  const scriptPath = completionScriptPath(shell, options);
47754
48401
  const { rcFile } = completionRcTarget(shell, options);
47755
48402
  const script = generateShellCompletion(shell);
47756
- const existingScript = existsSync38(scriptPath);
47757
- const existingScriptMatches = existingScript && readFileSync37(scriptPath, "utf8") === script;
48403
+ const existingScript = existsSync39(scriptPath);
48404
+ const existingScriptMatches = existingScript && readFileSync38(scriptPath, "utf8") === script;
47758
48405
  let rcModified = false;
47759
48406
  let alreadyInstalled = false;
47760
48407
  if (shell === "fish") {
47761
48408
  const fishTarget = fishCompletionTarget(shell, options);
47762
- const fishInstalled = existsSync38(fishTarget) && readFileSync37(fishTarget, "utf8") === script;
48409
+ const fishInstalled = existsSync39(fishTarget) && readFileSync38(fishTarget, "utf8") === script;
47763
48410
  alreadyInstalled = fishInstalled;
47764
48411
  if (!options.dryRun && (!fishInstalled || options.overwrite)) {
47765
- mkdirSync30(resolve32(fishTarget, ".."), { recursive: true });
48412
+ mkdirSync30(resolve31(fishTarget, ".."), { recursive: true });
47766
48413
  writeFileSync26(fishTarget, script, { encoding: "utf8" });
47767
48414
  }
47768
48415
  return {
@@ -47776,12 +48423,12 @@ function installShellCompletion(shellInput, options = {}) {
47776
48423
  }
47777
48424
  if (rcFile) {
47778
48425
  const line = sourceLine(shell, scriptPath);
47779
- const rcContent = existsSync38(rcFile) ? readFileSync37(rcFile, "utf8") : "";
48426
+ const rcContent = existsSync39(rcFile) ? readFileSync38(rcFile, "utf8") : "";
47780
48427
  alreadyInstalled = rcContent.includes(line) || rcContent.includes(`${COMPLETION_MARKER}`);
47781
48428
  }
47782
48429
  if (!options.dryRun) {
47783
48430
  if (!existingScriptMatches || options.overwrite) {
47784
- mkdirSync30(resolve32(scriptPath, ".."), { recursive: true });
48431
+ mkdirSync30(resolve31(scriptPath, ".."), { recursive: true });
47785
48432
  writeFileSync26(scriptPath, script, { encoding: "utf8" });
47786
48433
  }
47787
48434
  if (rcFile && !alreadyInstalled) {
@@ -47791,8 +48438,8 @@ function installShellCompletion(shellInput, options = {}) {
47791
48438
  ${marker}
47792
48439
  ${line}
47793
48440
  `;
47794
- mkdirSync30(resolve32(rcFile, ".."), { recursive: true });
47795
- writeFileSync26(rcFile, (existsSync38(rcFile) ? readFileSync37(rcFile, "utf8") : "") + stanza, { encoding: "utf8" });
48441
+ mkdirSync30(resolve31(rcFile, ".."), { recursive: true });
48442
+ writeFileSync26(rcFile, (existsSync39(rcFile) ? readFileSync38(rcFile, "utf8") : "") + stanza, { encoding: "utf8" });
47796
48443
  rcModified = true;
47797
48444
  }
47798
48445
  }
@@ -47808,14 +48455,14 @@ ${line}
47808
48455
  function kxmBinDir(env = process.env) {
47809
48456
  const argv1 = env.KXM_ENTRY ?? process.argv[1];
47810
48457
  if (argv1 && isAbsolute10(argv1)) {
47811
- const dir = dirname24(argv1);
47812
- if (existsSync38(join47(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
48458
+ const dir = dirname23(argv1);
48459
+ if (existsSync39(join48(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
47813
48460
  }
47814
48461
  const pathEnv = env.PATH ?? "";
47815
48462
  for (const part of pathEnv.split(delimiter)) {
47816
48463
  if (!part) continue;
47817
- const candidate = resolve32(part, process.platform === "win32" ? "kxm.cmd" : "kxm");
47818
- if (existsSync38(candidate)) return resolve32(part);
48464
+ const candidate = resolve31(part, process.platform === "win32" ? "kxm.cmd" : "kxm");
48465
+ if (existsSync39(candidate)) return resolve31(part);
47819
48466
  }
47820
48467
  return void 0;
47821
48468
  }
@@ -47833,10 +48480,10 @@ function installPathEntry(shellInput, options = {}) {
47833
48480
  }
47834
48481
  const { rcFile } = completionRcTarget(shell, options);
47835
48482
  const line = pathLine(binDir);
47836
- const rcContent = existsSync38(rcFile ?? "") ? readFileSync37(rcFile, "utf8") : "";
48483
+ const rcContent = existsSync39(rcFile ?? "") ? readFileSync38(rcFile, "utf8") : "";
47837
48484
  const alreadyInstalled = rcContent.includes(binDir) || (options.env?.PATH ?? process.env.PATH ?? "").split(delimiter).includes(binDir);
47838
48485
  if (!options.dryRun && rcFile && !alreadyInstalled) {
47839
- mkdirSync30(dirname24(rcFile), { recursive: true });
48486
+ mkdirSync30(dirname23(rcFile), { recursive: true });
47840
48487
  writeFileSync26(rcFile, rcContent + `
47841
48488
  ${PATH_MARKER}
47842
48489
  ${line}
@@ -47847,8 +48494,8 @@ ${line}
47847
48494
 
47848
48495
  // plugins/kxm/src/init-guide-setup.ts
47849
48496
  var import_yaml18 = __toESM(require_dist(), 1);
47850
- import { existsSync as existsSync39, mkdirSync as mkdirSync31, writeFileSync as writeFileSync27 } from "node:fs";
47851
- import { dirname as dirname25, join as join48 } from "node:path";
48497
+ import { existsSync as existsSync40, mkdirSync as mkdirSync31, writeFileSync as writeFileSync27 } from "node:fs";
48498
+ import { dirname as dirname24, join as join49 } from "node:path";
47852
48499
  var ADMITTED_GUIDE_BINDINGS = Object.freeze({
47853
48500
  "anthropic/claude-fable-5.1": { harness: "claude", provider: "anthropic", model: "fable" },
47854
48501
  "anthropic/fable": { harness: "claude", provider: "anthropic", model: "fable" },
@@ -48239,13 +48886,13 @@ function renderGuideSetupFiles(projectRoot, plan) {
48239
48886
  const stage = roleStages.get(role);
48240
48887
  if (!stage) continue;
48241
48888
  files.push({
48242
- path: join48(projectRoot, ".kxm", "agents", `${role}.yaml`),
48889
+ path: join49(projectRoot, ".kxm", "agents", `${role}.yaml`),
48243
48890
  content: (0, import_yaml18.stringify)(agentDocument(role, stage, binding))
48244
48891
  });
48245
48892
  }
48246
48893
  for (const workflow of plan.workflows) {
48247
48894
  files.push({
48248
- path: join48(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
48895
+ path: join49(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
48249
48896
  content: (0, import_yaml18.stringify)(workflowDocument(workflow))
48250
48897
  });
48251
48898
  }
@@ -48262,8 +48909,8 @@ function mergeGuideRouteAdmission(projectRoot, plan) {
48262
48909
  }
48263
48910
  if (added.length === 0) return added;
48264
48911
  policy.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
48265
- const path4 = join48(projectRoot, ".kxm", "routes.yaml");
48266
- mkdirSync31(dirname25(path4), { recursive: true });
48912
+ const path4 = join49(projectRoot, ".kxm", "routes.yaml");
48913
+ mkdirSync31(dirname24(path4), { recursive: true });
48267
48914
  writeFileSync27(path4, (0, import_yaml18.stringify)(policy), "utf8");
48268
48915
  return added;
48269
48916
  }
@@ -48271,11 +48918,11 @@ function writeGuideSetupFiles(files) {
48271
48918
  const written = [];
48272
48919
  const existed = [];
48273
48920
  for (const file of files) {
48274
- if (existsSync39(file.path)) {
48921
+ if (existsSync40(file.path)) {
48275
48922
  existed.push(file.path);
48276
48923
  continue;
48277
48924
  }
48278
- mkdirSync31(dirname25(file.path), { recursive: true });
48925
+ mkdirSync31(dirname24(file.path), { recursive: true });
48279
48926
  writeFileSync27(file.path, file.content, "utf8");
48280
48927
  written.push(file.path);
48281
48928
  }
@@ -48339,7 +48986,7 @@ function applyKxmPackageUpdate(runtime, notice) {
48339
48986
  detail: `release v${notice.latest} has no sha256 digest for ${name}; refusing to install`
48340
48987
  };
48341
48988
  }
48342
- const releaseDir = mkdtempSync3(join49(tmpdir2(), "kxm-pkg-update-"));
48989
+ const releaseDir = mkdtempSync3(join50(tmpdir2(), "kxm-pkg-update-"));
48343
48990
  try {
48344
48991
  const planned = planKxmPackageUpdate(notice.source, notice.latest, releaseDir, notice.asset);
48345
48992
  if (runtime.dryRun) {
@@ -48348,7 +48995,7 @@ function applyKxmPackageUpdate(runtime, notice) {
48348
48995
  for (const step of planned) {
48349
48996
  if (step.kind === "verify") {
48350
48997
  if (!verifyReleaseAssetDigest(step.path, step.sha256)) {
48351
- const actual = existsSync40(step.path) ? createHash16("sha256").update(readFileSync38(step.path)).digest("hex") : "missing";
48998
+ const actual = existsSync41(step.path) ? createHash17("sha256").update(readFileSync39(step.path)).digest("hex") : "missing";
48352
48999
  return {
48353
49000
  ok: false,
48354
49001
  error: "release_digest_mismatch",
@@ -48625,13 +49272,27 @@ async function cmdValidate(runtime, fileFlag) {
48625
49272
  return 2;
48626
49273
  }
48627
49274
  const selectedFile = explicitFile || configuredFile;
48628
- const file = selectedFile ? resolve33(runtime.cwd, selectedFile) : void 0;
48629
- if (file && !existsSync40(file)) {
49275
+ const file = selectedFile ? resolve32(runtime.cwd, selectedFile) : void 0;
49276
+ if (file && !existsSync41(file)) {
48630
49277
  printWorker(runtime, worker, { ok: false, command: "validate", error: "file_not_found", file }, `workflow file not found: ${file}`);
48631
49278
  return 1;
48632
49279
  }
48633
49280
  try {
48634
- const raw = file ? readFileSync38(file, "utf8") : inline;
49281
+ const raw = file ? readFileSync39(file, "utf8") : inline;
49282
+ if (explicitFile && file && !raw.trimStart().startsWith("[")) {
49283
+ const value = parseRestrictedYaml2(raw, file);
49284
+ const issues = new KxmSchemaRegistry().validate("workflow", value, file);
49285
+ if (issues.length > 0) throw new KxmConfigError(issues);
49286
+ const id = basename10(file, extname3(file));
49287
+ compileKxmWorkflow({ id, value, logicalPath: file });
49288
+ printWorker(
49289
+ runtime,
49290
+ worker,
49291
+ { ok: true, command: "validate", source: "file", file, workflows: [{ id, schema: value.schema }], warnings: [] },
49292
+ `validated local workflow ${id} (schema and transitions); kxm init validates project references`
49293
+ );
49294
+ return 0;
49295
+ }
48635
49296
  const warnings = [];
48636
49297
  const definitions = parseWorkflowDefinitions(raw, runtime.env, (message) => warnings.push(message));
48637
49298
  const secretEnvs = definitions.map((definition) => ({
@@ -48656,7 +49317,7 @@ async function cmdValidate(runtime, fileFlag) {
48656
49317
  }
48657
49318
  async function cmdArtifactsExist(runtime, pathFlag) {
48658
49319
  const worker = gateOf(runtime, "artifacts-exist");
48659
- const checked = verifyArtifactExists(runtime.dirs.assets, resolve33(runtime.cwd, pathFlag));
49320
+ const checked = verifyArtifactExists(runtime.dirs.assets, resolve32(runtime.cwd, pathFlag));
48660
49321
  if (!checked.ok) {
48661
49322
  printWorker(
48662
49323
  runtime,
@@ -48712,7 +49373,7 @@ async function cmdImprove(runtime, options = {}) {
48712
49373
  print(runtime.io, runtime.json, { ok: false, command: "improve", error: "config_invalid", detail: message }, `config_invalid: ${message}`);
48713
49374
  return 1;
48714
49375
  }
48715
- const candidatesDir = options.outDir ? resolve33(runtime.cwd, options.outDir) : join49(root, ".kxm", "candidates");
49376
+ const candidatesDir = options.outDir ? resolve32(runtime.cwd, options.outDir) : join50(root, ".kxm", "candidates");
48716
49377
  const report2 = buildImprovementReport(loaded.records, {
48717
49378
  candidatesDir,
48718
49379
  projectRoot: root,
@@ -48721,7 +49382,7 @@ async function cmdImprove(runtime, options = {}) {
48721
49382
  autoThreshold: config.improvement.autoThreshold,
48722
49383
  halfLifeDays: config.improvement.telemetryHalfLifeDays
48723
49384
  });
48724
- const reportDir = join49(runtime.dirs.assets, "improvements");
49385
+ const reportDir = join50(runtime.dirs.assets, "improvements");
48725
49386
  const reportPath = writeImprovementReport(reportDir, report2, runtime.dryRun);
48726
49387
  const text = [
48727
49388
  "Sources:",
@@ -48865,9 +49526,9 @@ async function maybeOfferCompletionInstall(runtime) {
48865
49526
  if (!pathNeeded) {
48866
49527
  const scriptPath = completionScriptPath(shell, { env: runtime.env, configDir: runtime.env.KXM_USER_CONFIG_DIR });
48867
49528
  const { rcFile } = completionRcTarget(shell, { env: runtime.env });
48868
- if (rcFile && existsSync40(rcFile)) {
49529
+ if (rcFile && existsSync41(rcFile)) {
48869
49530
  try {
48870
- if (readFileSync38(rcFile, "utf8").includes(scriptPath)) return;
49531
+ if (readFileSync39(rcFile, "utf8").includes(scriptPath)) return;
48871
49532
  } catch {
48872
49533
  }
48873
49534
  }
@@ -49030,7 +49691,7 @@ async function cmdRoutingReport(runtime, options) {
49030
49691
  let catalog;
49031
49692
  if (includeEquivalentListCost) {
49032
49693
  try {
49033
- const pricesPath = options.prices ? resolve33(runtime.cwd, options.prices) : join49(runtime.dirs.workspace, "prices.yaml");
49694
+ const pricesPath = options.prices ? resolve32(runtime.cwd, options.prices) : join50(runtime.dirs.workspace, "prices.yaml");
49034
49695
  catalog = loadPriceCatalog(pricesPath);
49035
49696
  } catch {
49036
49697
  }
@@ -49414,7 +50075,7 @@ function createProgram(ctx, result) {
49414
50075
  addGlobalOptions(program2.command("restore <manifest>").description("Restore this project's SQLite stores from a verified backup manifest; refuses while the Runtime or hub is running")).option("--all-projects", "Also restore the shared Runtime registry and other projects' event stores the backup holds").action(async function restoreAction(manifest, options) {
49415
50076
  result.code = await cmdRestore(runtimeFrom(ctx, this), manifest, options);
49416
50077
  });
49417
- addGlobalOptions(program2.command("run").description("Create a KXM run (offline-first; kxm runs drive <runId> --simulated executes it model-free)").argument("[workflow]", "Workflow id to run").argument("[prompt...]", "Run prompt (events keep its hash; the full text is kept in a local 0600 sidecar file)").action(async function runAction(workflow2, promptParts) {
50078
+ addGlobalOptions(program2.command("run").description("Create a KXM run without executing steps; follow its prerequisites, then kxm runs drive <runId> --wait").argument("[workflow]", "Workflow id to run").argument("[prompt...]", "Run prompt (events keep its hash; the full text is kept in a local 0600 sidecar file)").action(async function runAction(workflow2, promptParts) {
49418
50079
  result.code = await cmdKxmRun(runtimeFrom(ctx, this), workflow2, promptParts);
49419
50080
  }));
49420
50081
  const runCmd = addGlobalOptions(program2.command("runs").description("Inspect KXM runs"));
@@ -49879,7 +50540,7 @@ async function runCli(argv, env = process.env, io = { stdout: (text) => process.
49879
50540
  throw error;
49880
50541
  }
49881
50542
  }
49882
- if (process.argv[1] && resolve34(process.argv[1]) === fileURLToPath4(import.meta.url)) {
50543
+ if (process.argv[1] && resolve33(process.argv[1]) === fileURLToPath4(import.meta.url)) {
49883
50544
  const code = await runCli(process.argv.slice(2));
49884
50545
  process.exitCode = code;
49885
50546
  }