@kontextmind/kxm 0.7.53 → 0.7.55

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.
@@ -164,10 +164,10 @@ var require_code = __commonJS({
164
164
  function interpolate(x2) {
165
165
  return typeof x2 == "number" || typeof x2 == "boolean" || x2 === null ? x2 : safeStringify(Array.isArray(x2) ? x2.join(",") : x2);
166
166
  }
167
- function stringify11(x2) {
167
+ function stringify10(x2) {
168
168
  return new _Code(safeStringify(x2));
169
169
  }
170
- exports.stringify = stringify11;
170
+ exports.stringify = stringify10;
171
171
  function safeStringify(x2) {
172
172
  return JSON.stringify(x2).replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
173
173
  }
@@ -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 = resolve31.call(this, root, ref);
2990
+ let _sch = resolve30.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 resolve31(root, ref) {
3017
+ function resolve30(root, ref) {
3018
3018
  let sch;
3019
3019
  while (typeof (sch = this.refs[ref]) == "string")
3020
3020
  ref = sch;
@@ -3839,7 +3839,7 @@ var require_fast_uri = __commonJS({
3839
3839
  }
3840
3840
  return uri;
3841
3841
  }
3842
- function resolve31(baseURI, relativeURI, options) {
3842
+ function resolve30(baseURI, relativeURI, options) {
3843
3843
  const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
3844
3844
  const {
3845
3845
  parsed: baseParsed,
@@ -4201,7 +4201,7 @@ var require_fast_uri = __commonJS({
4201
4201
  var fastUri = {
4202
4202
  SCHEMES,
4203
4203
  normalize,
4204
- resolve: resolve31,
4204
+ resolve: resolve30,
4205
4205
  resolveComponent,
4206
4206
  equal,
4207
4207
  serialize,
@@ -8918,7 +8918,7 @@ var require_stringify = __commonJS({
8918
8918
  props.push(doc.directives.tagString(tag));
8919
8919
  return props.join(" ");
8920
8920
  }
8921
- function stringify11(item, ctx, onComment, onChompKeep) {
8921
+ function stringify10(item, ctx, onComment, onChompKeep) {
8922
8922
  if (identity.isPair(item))
8923
8923
  return item.toString(ctx, onComment, onChompKeep);
8924
8924
  if (identity.isAlias(item)) {
@@ -8947,7 +8947,7 @@ var require_stringify = __commonJS({
8947
8947
  ${ctx.indent}${str}`;
8948
8948
  }
8949
8949
  exports.createStringifyContext = createStringifyContext;
8950
- exports.stringify = stringify11;
8950
+ exports.stringify = stringify10;
8951
8951
  }
8952
8952
  });
8953
8953
 
@@ -8957,7 +8957,7 @@ var require_stringifyPair = __commonJS({
8957
8957
  "use strict";
8958
8958
  var identity = require_identity();
8959
8959
  var Scalar = require_Scalar();
8960
- var stringify11 = require_stringify();
8960
+ var stringify10 = require_stringify();
8961
8961
  var stringifyComment = require_stringifyComment();
8962
8962
  function stringifyPair({ key, value }, ctx, onComment, onChompKeep) {
8963
8963
  const { allNullValues, doc, indent, indentStep, options: { commentString, indentSeq, simpleKeys } } = ctx;
@@ -8979,7 +8979,7 @@ var require_stringifyPair = __commonJS({
8979
8979
  });
8980
8980
  let keyCommentDone = false;
8981
8981
  let chompKeep = false;
8982
- let str = stringify11.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
8982
+ let str = stringify10.stringify(key, ctx, () => keyCommentDone = true, () => chompKeep = true);
8983
8983
  if (!explicitKey && !ctx.inFlow && str.length > 1024) {
8984
8984
  if (simpleKeys)
8985
8985
  throw new Error("With simple keys, single line scalar must not span more than 1024 characters");
@@ -9031,7 +9031,7 @@ ${indent}:`;
9031
9031
  ctx.indent = ctx.indent.substring(2);
9032
9032
  }
9033
9033
  let valueCommentDone = false;
9034
- const valueStr = stringify11.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
9034
+ const valueStr = stringify10.stringify(value, ctx, () => valueCommentDone = true, () => chompKeep = true);
9035
9035
  let ws = " ";
9036
9036
  if (keyComment || vsb || vcb) {
9037
9037
  ws = vsb ? "\n" : "";
@@ -9172,7 +9172,7 @@ var require_addPairToJSMap = __commonJS({
9172
9172
  "use strict";
9173
9173
  var log = require_log();
9174
9174
  var merge = require_merge();
9175
- var stringify11 = require_stringify();
9175
+ var stringify10 = require_stringify();
9176
9176
  var identity = require_identity();
9177
9177
  var toJS = require_toJS();
9178
9178
  function addPairToJSMap(ctx, map, { key, value }) {
@@ -9208,7 +9208,7 @@ var require_addPairToJSMap = __commonJS({
9208
9208
  if (typeof jsKey !== "object")
9209
9209
  return String(jsKey);
9210
9210
  if (identity.isNode(key) && ctx?.doc) {
9211
- const strCtx = stringify11.createStringifyContext(ctx.doc, {});
9211
+ const strCtx = stringify10.createStringifyContext(ctx.doc, {});
9212
9212
  strCtx.anchors = /* @__PURE__ */ new Set();
9213
9213
  for (const node of ctx.anchors.keys())
9214
9214
  strCtx.anchors.add(node.anchor);
@@ -9275,12 +9275,12 @@ var require_stringifyCollection = __commonJS({
9275
9275
  "node_modules/yaml/dist/stringify/stringifyCollection.js"(exports) {
9276
9276
  "use strict";
9277
9277
  var identity = require_identity();
9278
- var stringify11 = require_stringify();
9278
+ var stringify10 = require_stringify();
9279
9279
  var stringifyComment = require_stringifyComment();
9280
9280
  function stringifyCollection(collection, ctx, options) {
9281
9281
  const flow = ctx.inFlow ?? collection.flow;
9282
- const stringify12 = flow ? stringifyFlowCollection : stringifyBlockCollection;
9283
- return stringify12(collection, ctx, options);
9282
+ const stringify11 = flow ? stringifyFlowCollection : stringifyBlockCollection;
9283
+ return stringify11(collection, ctx, options);
9284
9284
  }
9285
9285
  function stringifyBlockCollection({ comment, items }, ctx, { blockItemPrefix, flowChars, itemIndent, onChompKeep, onComment }) {
9286
9286
  const { indent, options: { commentString } } = ctx;
@@ -9305,7 +9305,7 @@ var require_stringifyCollection = __commonJS({
9305
9305
  }
9306
9306
  }
9307
9307
  chompKeep = false;
9308
- let str2 = stringify11.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
9308
+ let str2 = stringify10.stringify(item, itemCtx, () => comment2 = null, () => chompKeep = true);
9309
9309
  if (comment2)
9310
9310
  str2 += stringifyComment.lineComment(str2, itemIndent, commentString(comment2));
9311
9311
  if (chompKeep && comment2)
@@ -9372,7 +9372,7 @@ ${indent}${line}` : "\n";
9372
9372
  }
9373
9373
  if (comment)
9374
9374
  reqNewline = true;
9375
- let str = stringify11.stringify(item, itemCtx, () => comment = null);
9375
+ let str = stringify10.stringify(item, itemCtx, () => comment = null);
9376
9376
  reqNewline || (reqNewline = lines.length > linesAtValue || str.includes("\n"));
9377
9377
  if (i < items.length - 1) {
9378
9378
  str += ",";
@@ -10733,7 +10733,7 @@ var require_stringifyDocument = __commonJS({
10733
10733
  "node_modules/yaml/dist/stringify/stringifyDocument.js"(exports) {
10734
10734
  "use strict";
10735
10735
  var identity = require_identity();
10736
- var stringify11 = require_stringify();
10736
+ var stringify10 = require_stringify();
10737
10737
  var stringifyComment = require_stringifyComment();
10738
10738
  function stringifyDocument(doc, options) {
10739
10739
  const lines = [];
@@ -10748,7 +10748,7 @@ var require_stringifyDocument = __commonJS({
10748
10748
  }
10749
10749
  if (hasDirectives)
10750
10750
  lines.push("---");
10751
- const ctx = stringify11.createStringifyContext(doc, options);
10751
+ const ctx = stringify10.createStringifyContext(doc, options);
10752
10752
  const { commentString } = ctx.options;
10753
10753
  if (doc.commentBefore) {
10754
10754
  if (lines.length !== 1)
@@ -10770,7 +10770,7 @@ var require_stringifyDocument = __commonJS({
10770
10770
  contentComment = doc.contents.comment;
10771
10771
  }
10772
10772
  const onChompKeep = contentComment ? void 0 : () => chompKeep = true;
10773
- let body = stringify11.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
10773
+ let body = stringify10.stringify(doc.contents, ctx, () => contentComment = null, onChompKeep);
10774
10774
  if (contentComment)
10775
10775
  body += stringifyComment.lineComment(body, "", commentString(contentComment));
10776
10776
  if ((body[0] === "|" || body[0] === ">") && lines[lines.length - 1] === "---") {
@@ -10778,7 +10778,7 @@ var require_stringifyDocument = __commonJS({
10778
10778
  } else
10779
10779
  lines.push(body);
10780
10780
  } else {
10781
- lines.push(stringify11.stringify(doc.contents, ctx));
10781
+ lines.push(stringify10.stringify(doc.contents, ctx));
10782
10782
  }
10783
10783
  if (doc.directives?.docEnd) {
10784
10784
  if (doc.comment) {
@@ -12914,7 +12914,7 @@ var require_cst_scalar = __commonJS({
12914
12914
  var require_cst_stringify = __commonJS({
12915
12915
  "node_modules/yaml/dist/parse/cst-stringify.js"(exports) {
12916
12916
  "use strict";
12917
- var stringify11 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
12917
+ var stringify10 = (cst) => "type" in cst ? stringifyToken(cst) : stringifyItem(cst);
12918
12918
  function stringifyToken(token) {
12919
12919
  switch (token.type) {
12920
12920
  case "block-scalar": {
@@ -12967,7 +12967,7 @@ var require_cst_stringify = __commonJS({
12967
12967
  res += stringifyToken(value);
12968
12968
  return res;
12969
12969
  }
12970
- exports.stringify = stringify11;
12970
+ exports.stringify = stringify10;
12971
12971
  }
12972
12972
  });
12973
12973
 
@@ -14697,7 +14697,7 @@ var require_public_api = __commonJS({
14697
14697
  }
14698
14698
  return doc.toJS(Object.assign({ reviver: _reviver }, options));
14699
14699
  }
14700
- function stringify11(value, replacer, options) {
14700
+ function stringify10(value, replacer, options) {
14701
14701
  let _replacer = null;
14702
14702
  if (typeof replacer === "function" || Array.isArray(replacer)) {
14703
14703
  _replacer = replacer;
@@ -14722,7 +14722,7 @@ var require_public_api = __commonJS({
14722
14722
  exports.parse = parse12;
14723
14723
  exports.parseAllDocuments = parseAllDocuments;
14724
14724
  exports.parseDocument = parseDocument2;
14725
- exports.stringify = stringify11;
14725
+ exports.stringify = stringify10;
14726
14726
  }
14727
14727
  });
14728
14728
 
@@ -14779,7 +14779,7 @@ var require_dist = __commonJS({
14779
14779
  });
14780
14780
 
14781
14781
  // plugins/kxm/src/cli.ts
14782
- import { resolve as resolve30 } from "node:path";
14782
+ import { resolve as resolve29 } from "node:path";
14783
14783
  import { fileURLToPath as fileURLToPath4 } from "node:url";
14784
14784
 
14785
14785
  // node_modules/commander/lib/error.js
@@ -19446,7 +19446,7 @@ var HubClient = class {
19446
19446
  if (signal?.aborted) throw new MeshWaitError("aborted", messageId);
19447
19447
  const message = await this.getMessage(messageId);
19448
19448
  if (["replied", "cancelled", "expired", "error"].includes(message.status)) return message;
19449
- await new Promise((resolve31, reject) => {
19449
+ await new Promise((resolve30, reject) => {
19450
19450
  const onAbort = () => {
19451
19451
  signal?.removeEventListener("abort", onAbort);
19452
19452
  clearTimeout(timer);
@@ -19454,7 +19454,7 @@ var HubClient = class {
19454
19454
  };
19455
19455
  const timer = setTimeout(() => {
19456
19456
  signal?.removeEventListener("abort", onAbort);
19457
- resolve31();
19457
+ resolve30();
19458
19458
  }, Math.min(500, Math.max(1, deadline - Date.now())));
19459
19459
  signal?.addEventListener("abort", onAbort, { once: true });
19460
19460
  if (signal?.aborted) onAbort();
@@ -19509,7 +19509,7 @@ var HubClient = class {
19509
19509
  } catch (error) {
19510
19510
  if (this.stopped || error instanceof Error && error.name === "AbortError") return;
19511
19511
  }
19512
- if (!this.stopped) await new Promise((resolve31) => setTimeout(resolve31, this.options.reconnectMs));
19512
+ if (!this.stopped) await new Promise((resolve30) => setTimeout(resolve30, this.options.reconnectMs));
19513
19513
  }
19514
19514
  }
19515
19515
  headers(includeIdentity = true) {
@@ -20960,7 +20960,7 @@ function defaultSpawn(command, args, options) {
20960
20960
  if (options.signal?.aborted) {
20961
20961
  return Promise.resolve({ stdout: "", stderr: "", code: null, started: false, observedChildExit: false, error: new Error("process_aborted") });
20962
20962
  }
20963
- return new Promise((resolve31) => {
20963
+ return new Promise((resolve30) => {
20964
20964
  const stdout = [];
20965
20965
  const stderr = [];
20966
20966
  let outputBytes = 0;
@@ -20991,7 +20991,7 @@ function defaultSpawn(command, args, options) {
20991
20991
  const started = Boolean(child?.pid);
20992
20992
  if (started && !observedChildExit) error ??= new Error("process_exit_unobserved");
20993
20993
  const unverifiedDescendants = stopping || started && !observedChildExit;
20994
- resolve31({
20994
+ resolve30({
20995
20995
  stdout: Buffer.concat(stdout).toString("utf8"),
20996
20996
  stderr: Buffer.concat(stderr).toString("utf8"),
20997
20997
  code,
@@ -22060,9 +22060,6 @@ var KxmSchemaRegistry = class {
22060
22060
  localBindingsValidator;
22061
22061
  templateProvenanceValidator;
22062
22062
  initOperationValidator;
22063
- migrationPlanValidator;
22064
- migrationDecisionValidator;
22065
- migrationReceiptValidator;
22066
22063
  permissionDiffValidator;
22067
22064
  runEventValidator;
22068
22065
  driveReceiptValidator;
@@ -22079,9 +22076,6 @@ var KxmSchemaRegistry = class {
22079
22076
  const localBindingsFile = "local-repository-bindings.schema.json";
22080
22077
  const templateProvenanceFile = "template-provenance.schema.json";
22081
22078
  const initOperationFile = "init-operation.schema.json";
22082
- const migrationPlanFile = "migration-plan.schema.json";
22083
- const migrationDecisionFile = "migration-decision.schema.json";
22084
- const migrationReceiptFile = "migration-receipt.schema.json";
22085
22079
  const permissionDiffFile = "permission-diff.schema.json";
22086
22080
  const runEventFile = "run-event.schema.json";
22087
22081
  const driveReceiptFile = "drive-receipt.schema.json";
@@ -22090,9 +22084,6 @@ var KxmSchemaRegistry = class {
22090
22084
  this.ajv.addSchema(readJsonObject(join6(this.schemasDir, localBindingsFile)));
22091
22085
  this.ajv.addSchema(readJsonObject(join6(this.schemasDir, templateProvenanceFile)));
22092
22086
  this.ajv.addSchema(readJsonObject(join6(this.schemasDir, initOperationFile)));
22093
- this.ajv.addSchema(readJsonObject(join6(this.schemasDir, migrationPlanFile)));
22094
- this.ajv.addSchema(readJsonObject(join6(this.schemasDir, migrationDecisionFile)));
22095
- this.ajv.addSchema(readJsonObject(join6(this.schemasDir, migrationReceiptFile)));
22096
22087
  this.ajv.addSchema(readJsonObject(join6(this.schemasDir, permissionDiffFile)));
22097
22088
  this.ajv.addSchema(readJsonObject(join6(this.schemasDir, runEventFile)));
22098
22089
  this.ajv.addSchema(readJsonObject(join6(this.schemasDir, driveReceiptFile)));
@@ -22106,9 +22097,6 @@ var KxmSchemaRegistry = class {
22106
22097
  const localBindingsValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${localBindingsFile}`);
22107
22098
  const templateProvenanceValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${templateProvenanceFile}`);
22108
22099
  const initOperationValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${initOperationFile}`);
22109
- const migrationPlanValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${migrationPlanFile}`);
22110
- const migrationDecisionValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${migrationDecisionFile}`);
22111
- const migrationReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${migrationReceiptFile}`);
22112
22100
  const permissionDiffValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${permissionDiffFile}`);
22113
22101
  const runEventValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${runEventFile}`);
22114
22102
  const driveReceiptValidator = this.ajv.getSchema(`https://schemas.kxm.dev/${driveReceiptFile}`);
@@ -22117,9 +22105,6 @@ var KxmSchemaRegistry = class {
22117
22105
  if (!localBindingsValidator) throw new Error(`schema did not compile: ${localBindingsFile}`);
22118
22106
  if (!templateProvenanceValidator) throw new Error(`schema did not compile: ${templateProvenanceFile}`);
22119
22107
  if (!initOperationValidator) throw new Error(`schema did not compile: ${initOperationFile}`);
22120
- if (!migrationPlanValidator) throw new Error(`schema did not compile: ${migrationPlanFile}`);
22121
- if (!migrationDecisionValidator) throw new Error(`schema did not compile: ${migrationDecisionFile}`);
22122
- if (!migrationReceiptValidator) throw new Error(`schema did not compile: ${migrationReceiptFile}`);
22123
22108
  if (!permissionDiffValidator) throw new Error(`schema did not compile: ${permissionDiffFile}`);
22124
22109
  if (!runEventValidator) throw new Error(`schema did not compile: ${runEventFile}`);
22125
22110
  if (!driveReceiptValidator) throw new Error(`schema did not compile: ${driveReceiptFile}`);
@@ -22128,9 +22113,6 @@ var KxmSchemaRegistry = class {
22128
22113
  this.localBindingsValidator = localBindingsValidator;
22129
22114
  this.templateProvenanceValidator = templateProvenanceValidator;
22130
22115
  this.initOperationValidator = initOperationValidator;
22131
- this.migrationPlanValidator = migrationPlanValidator;
22132
- this.migrationDecisionValidator = migrationDecisionValidator;
22133
- this.migrationReceiptValidator = migrationReceiptValidator;
22134
22116
  this.permissionDiffValidator = permissionDiffValidator;
22135
22117
  this.runEventValidator = runEventValidator;
22136
22118
  this.driveReceiptValidator = driveReceiptValidator;
@@ -22156,15 +22138,6 @@ var KxmSchemaRegistry = class {
22156
22138
  validateInitOperation(value, file) {
22157
22139
  return this.validateAuxiliary(value, file, "kxm.init-operation.v1", this.initOperationValidator);
22158
22140
  }
22159
- validateMigrationPlan(value, file) {
22160
- return this.validateAuxiliary(value, file, "kxm.migration-plan.v1", this.migrationPlanValidator);
22161
- }
22162
- validateMigrationDecision(value, file) {
22163
- return this.validateAuxiliary(value, file, "kxm.migration-decision.v1", this.migrationDecisionValidator);
22164
- }
22165
- validateMigrationReceipt(value, file) {
22166
- return this.validateAuxiliary(value, file, "kxm.migration-receipt.v1", this.migrationReceiptValidator);
22167
- }
22168
22141
  validatePermissionDiff(value, file) {
22169
22142
  return this.validateAuxiliary(value, file, "kxm.permission-diff.v1", this.permissionDiffValidator);
22170
22143
  }
@@ -22516,35 +22489,6 @@ function validateToolPolicy(resource, policy, label, issues) {
22516
22489
  if (allowed.has(tool)) issues.push(issue2("semantic", "tool_policy_contradiction", resource.logicalPath, `${label} both allows and denies ${tool}`));
22517
22490
  }
22518
22491
  }
22519
- function validateKxmResources(resources, options = {}) {
22520
- const make = (logicalPath, kind, id, value) => ({
22521
- kind,
22522
- ...id === void 0 ? {} : { id },
22523
- file: logicalPath,
22524
- logicalPath,
22525
- value
22526
- });
22527
- assertNoRegisteredGates(options);
22528
- let gateRegistry;
22529
- let project;
22530
- const repositories = /* @__PURE__ */ new Map();
22531
- const agents = /* @__PURE__ */ new Map();
22532
- const models = /* @__PURE__ */ new Map();
22533
- const workflows = /* @__PURE__ */ new Map();
22534
- const environments = [];
22535
- for (const [logicalPath, resource] of resources) {
22536
- const made = make(logicalPath, resource.kind, resource.id, resource.value);
22537
- if (resource.kind === "project") project = made;
22538
- else if (resource.kind === "repository" && resource.id) repositories.set(resource.id, made);
22539
- else if (resource.kind === "agent" && resource.id) agents.set(resource.id, made);
22540
- else if (resource.kind === "model" && resource.id) models.set(resource.id, made);
22541
- else if (resource.kind === "workflow" && resource.id) workflows.set(resource.id, made);
22542
- else if (resource.kind === "environment") environments.push(made);
22543
- else if (resource.kind === "gate-registry") gateRegistry = made;
22544
- }
22545
- if (!project) return [issue2("discovery", "project_definition_missing", ".kxm/project.yaml", "resource set has no project definition")];
22546
- return validateBundle(project, repositories, agents, models, workflows, environments, options, gateRegistry);
22547
- }
22548
22492
  function validateAgentScope(agent, step, repositories, file, stepId, issues) {
22549
22493
  validateToolScope(agent, step, file, stepId, issues);
22550
22494
  const agentRepositories = objectValue(agent.value.repositories) ?? {};
@@ -22667,8 +22611,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
22667
22611
  const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
22668
22612
  if (maxWriteRepositories > writable) issues.push(issue2("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
22669
22613
  }
22670
- const join50 = objectValue(step.join);
22671
- const minimumPassed = join50 && typeof join50.minimumPassed === "number" ? join50.minimumPassed : void 0;
22614
+ const join49 = objectValue(step.join);
22615
+ const minimumPassed = join49 && typeof join49.minimumPassed === "number" ? join49.minimumPassed : void 0;
22672
22616
  if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue2("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
22673
22617
  const distinctBy = names(assignment?.distinctBy);
22674
22618
  if (distinctBy.length > 0) {
@@ -22956,11 +22900,14 @@ function assertNoRegisteredGates(options) {
22956
22900
  function loadKxmProject(projectRoot, options = {}) {
22957
22901
  assertNoRegisteredGates(options);
22958
22902
  const root = resolve3(projectRoot);
22959
- let migrationReceipt;
22960
- if (legacyConfigFilesAt(root).length > 0 && options.allowUnreceiptedLegacyConfig !== true) {
22961
- const receiptCheck = readKxmMigrationReceipt(root, options);
22962
- if (receiptCheck.issues.length > 0) throw new KxmConfigError(receiptCheck.issues);
22963
- migrationReceipt = receiptCheck.receipt;
22903
+ const legacyPresent = legacyConfigFilesAt(root);
22904
+ if (legacyPresent.length > 0) {
22905
+ throw new KxmConfigError(legacyPresent.map((file) => issue2(
22906
+ "semantic",
22907
+ "legacy_state_unsupported",
22908
+ file,
22909
+ "legacy JSON configuration is present and this build does not migrate it: delete these files once their YAML replacements exist in .kxm/, or initialise a fresh project"
22910
+ )));
22964
22911
  }
22965
22912
  const registry = new KxmSchemaRegistry(options.schemasDir);
22966
22913
  const project = readResource(registry, root, join6(root, ".kxm", "project.yaml"), ".kxm/project.yaml", "project");
@@ -22973,9 +22920,6 @@ function loadKxmProject(projectRoot, options = {}) {
22973
22920
  earlyIssues.push(issue2("semantic", "template_provenance_project_mismatch", ".kxm/template-provenance.yaml", "template provenance belongs to a different project identity"));
22974
22921
  }
22975
22922
  }
22976
- if (migrationReceipt && stringValue(migrationReceipt.projectId) !== stringValue(project.value.id)) {
22977
- earlyIssues.push(issue2("semantic", "migration_project_mismatch", KXM_MIGRATION_RECEIPT_PATH, "migration receipt belongs to a different project identity"));
22978
- }
22979
22923
  const declaredRepositoryIds = new Set(valuesOf(project.value, "repositories").map((candidate) => stringValue(objectValue(candidate)?.id)).filter((candidate) => candidate !== void 0));
22980
22924
  for (const repositoryId of Object.keys(options.repositoryBindings ?? {}).sort(compareCodeUnits3)) {
22981
22925
  if (!resourceIdentifier(repositoryId)) {
@@ -23052,6 +22996,15 @@ function loadKxmProject(projectRoot, options = {}) {
23052
22996
  continue;
23053
22997
  }
23054
22998
  }
22999
+ const memberLegacy = binding === root ? [] : legacyConfigFilesAt(binding);
23000
+ if (memberLegacy.length > 0) {
23001
+ throw new KxmConfigError(memberLegacy.map((file) => issue2(
23002
+ "semantic",
23003
+ "legacy_state_unsupported",
23004
+ `${repositoryId}/${file}`,
23005
+ `repository ${repositoryId} holds legacy JSON configuration and this build does not migrate it: delete those files once their YAML replacements exist, or bind a clean worktree`
23006
+ )));
23007
+ }
23055
23008
  const foldedBinding = canonicalHostPath(binding).toLocaleLowerCase("en-US");
23056
23009
  const priorBinding = seenBindings.get(foldedBinding);
23057
23010
  if (priorBinding && priorBinding !== repositoryId) {
@@ -23105,50 +23058,10 @@ function loadKxmProject(projectRoot, options = {}) {
23105
23058
  environments,
23106
23059
  ...gateRegistry ? { gateRegistry } : {},
23107
23060
  ...templateProvenance === void 0 ? {} : { templateProvenance },
23108
- ...migrationReceipt === void 0 ? {} : { migrationReceipt },
23109
23061
  resources,
23110
23062
  configRevision: bundleRevision(resources)
23111
23063
  };
23112
23064
  }
23113
- function verifyKxmMigrationReceiptTarget(root, receipt, resources, configRevision) {
23114
- const issues = [];
23115
- if (receipt.configRevision !== configRevision) {
23116
- issues.push(issue2("semantic", "migration_target_changed", KXM_MIGRATION_RECEIPT_PATH, "target configuration revision no longer matches the migration receipt"));
23117
- }
23118
- const declared = /* @__PURE__ */ new Map();
23119
- for (const candidate of Array.isArray(receipt.resources) ? receipt.resources : []) {
23120
- if (candidate && typeof candidate === "object" && !Array.isArray(candidate)) {
23121
- const record = candidate;
23122
- if (typeof record.path === "string") declared.set(record.path, record);
23123
- }
23124
- }
23125
- const installedPaths = /* @__PURE__ */ new Set();
23126
- for (const resource of resources) {
23127
- const relative7 = resource.file.startsWith(root) ? relativePortable(root, resource.file) : void 0;
23128
- if (!relative7) continue;
23129
- installedPaths.add(relative7);
23130
- const record = declared.get(relative7);
23131
- if (!record) {
23132
- issues.push(issue2("semantic", "migration_target_unreceipted", relative7, "installed resource is not covered by the migration receipt"));
23133
- continue;
23134
- }
23135
- const current = hashFileRecord(root, relative7);
23136
- if (!current || current.sha256 !== record.sha256 || current.bytes !== record.bytes) {
23137
- issues.push(issue2("semantic", "migration_target_modified", relative7, "installed resource bytes differ from the migration receipt"));
23138
- }
23139
- }
23140
- for (const path4 of declared.keys()) {
23141
- if (!installedPaths.has(path4)) {
23142
- issues.push(issue2("semantic", "migration_target_missing", path4, "receipt-covered resource is not part of the validated configuration"));
23143
- }
23144
- }
23145
- return sortIssues2(issues);
23146
- }
23147
- function relativePortable(root, absolute) {
23148
- const rel = relative(root, absolute);
23149
- if (!rel || isAbsolute2(rel) || rel === ".." || rel.startsWith(`..${sep}`)) return void 0;
23150
- return rel.split(sep).join("/");
23151
- }
23152
23065
  function legacyInputsAt(root) {
23153
23066
  const candidates = [
23154
23067
  ".kxm/config/agents.json",
@@ -23158,7 +23071,6 @@ function legacyInputsAt(root) {
23158
23071
  ];
23159
23072
  return candidates.filter((candidate) => existsSync6(join6(root, ...candidate.split("/"))));
23160
23073
  }
23161
- var KXM_MIGRATION_RECEIPT_PATH = ".kxm/migration-receipt.yaml";
23162
23074
  var LEGACY_CONFIG_FILES = [".kxm/config/agents.json", ".kxm/config/gates.json"];
23163
23075
  function legacyConfigFilesAt(root) {
23164
23076
  const files = [];
@@ -23184,66 +23096,6 @@ function legacyConfigFilesAt(root) {
23184
23096
  }
23185
23097
  return files;
23186
23098
  }
23187
- function hashFileRecord(root, relativePath) {
23188
- const absolute = join6(root, ...relativePath.split("/"));
23189
- if (!existsSync6(absolute)) return void 0;
23190
- const bytes = readFileSync5(absolute);
23191
- return {
23192
- path: relativePath,
23193
- sha256: `sha256:${createHash6("sha256").update(bytes).digest("hex")}`,
23194
- bytes: bytes.byteLength
23195
- };
23196
- }
23197
- function migrationReceiptSelfHash(receipt) {
23198
- const { receiptSha256: _ignored, ...unsigned } = receipt;
23199
- return `sha256:${createHash6("sha256").update(kxmCanonicalJson(unsigned), "utf8").digest("hex")}`;
23200
- }
23201
- function readKxmMigrationReceipt(root, options = {}) {
23202
- const receiptPath = join6(root, ...KXM_MIGRATION_RECEIPT_PATH.split("/"));
23203
- const legacyFiles = legacyConfigFilesAt(root);
23204
- if (!existsSync6(receiptPath)) {
23205
- return {
23206
- issues: legacyFiles.map((file) => issue2("semantic", "legacy_kxm_conflict", file, "legacy and KXM configuration cannot coexist before an accepted migration receipt"))
23207
- };
23208
- }
23209
- const receiptStat = lstatSync(receiptPath);
23210
- if (receiptStat.isSymbolicLink() || !receiptStat.isFile()) {
23211
- return { issues: [issue2("path", "migration_receipt_invalid", KXM_MIGRATION_RECEIPT_PATH, "migration receipt must be a regular file, not a link")] };
23212
- }
23213
- let receipt;
23214
- try {
23215
- receipt = parseRestrictedYaml2(readFileSync5(receiptPath), KXM_MIGRATION_RECEIPT_PATH);
23216
- } catch (error) {
23217
- if (error instanceof KxmConfigError) return { issues: [...error.issues] };
23218
- throw error;
23219
- }
23220
- const registry = new KxmSchemaRegistry(options.schemasDir);
23221
- const schemaIssues = registry.validateMigrationReceipt(receipt, KXM_MIGRATION_RECEIPT_PATH);
23222
- if (schemaIssues.length > 0) return { issues: schemaIssues };
23223
- if (receipt.receiptSha256 !== migrationReceiptSelfHash(receipt)) {
23224
- return { issues: [issue2("semantic", "migration_receipt_hash_mismatch", KXM_MIGRATION_RECEIPT_PATH, "receipt self-hash does not match its content")] };
23225
- }
23226
- const sources = (Array.isArray(receipt.sources) ? receipt.sources : []).map((candidate) => candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate.path : void 0).filter((candidate) => typeof candidate === "string").sort(compareCodeUnits3);
23227
- const issues = [];
23228
- const declared = new Set(sources);
23229
- for (const file of legacyFiles) {
23230
- if (!declared.has(file)) {
23231
- issues.push(issue2("semantic", "migration_source_unmigrated", file, "legacy configuration file is not covered by the migration receipt"));
23232
- continue;
23233
- }
23234
- const record = (Array.isArray(receipt.sources) ? receipt.sources : []).map((candidate) => candidate && typeof candidate === "object" && !Array.isArray(candidate) ? candidate : void 0).find((candidate) => candidate?.path === file);
23235
- const current = hashFileRecord(root, file);
23236
- if (!current || current.sha256 !== record?.sha256 || current.bytes !== record?.bytes) {
23237
- issues.push(issue2("semantic", "migration_source_changed", file, "legacy configuration changed after the migration receipt was issued"));
23238
- }
23239
- }
23240
- for (const file of sources) {
23241
- if (!legacyFiles.includes(file)) {
23242
- issues.push(issue2("semantic", "migration_source_missing", file, "receipt covers a legacy configuration file that no longer exists"));
23243
- }
23244
- }
23245
- return issues.length > 0 ? { issues } : { receipt, issues: [] };
23246
- }
23247
23099
  function discoverLegacyRoot(start) {
23248
23100
  let initial = resolve3(start);
23249
23101
  if (existsSync6(initial) && !lstatSync(initial).isDirectory()) initial = dirname4(initial);
@@ -23260,16 +23112,32 @@ function planKxmInitialization(start = process.cwd(), options = {}) {
23260
23112
  return { mode: "ready", inspectedFrom, projectRoot, changesRequired: false, issues: [], legacyInputs: [], configRevision: bundle.configRevision };
23261
23113
  } catch (error) {
23262
23114
  if (!(error instanceof KxmConfigError)) throw error;
23115
+ const legacyIssues = error.issues.filter((candidate) => candidate.code === "legacy_state_unsupported");
23263
23116
  const legacyInputs = legacyInputsAt(projectRoot);
23264
- if (legacyInputs.length > 0) {
23265
- return { mode: "migrate", inspectedFrom, projectRoot, legacyRoot: projectRoot, changesRequired: true, issues: error.issues, legacyInputs };
23117
+ if (legacyInputs.length > 0 || legacyIssues.length > 0) {
23118
+ return {
23119
+ mode: "legacy",
23120
+ inspectedFrom,
23121
+ projectRoot,
23122
+ legacyRoot: projectRoot,
23123
+ changesRequired: true,
23124
+ issues: [...error.issues, ...legacyInputs.length > 0 ? [issue2("discovery", "legacy_state_unsupported", ".kxm", "legacy configuration is present and this build does not migrate it: initialise a fresh project directory and carry over the YAML definitions you want to keep")] : []],
23125
+ legacyInputs: [.../* @__PURE__ */ new Set([...legacyInputs, ...legacyIssues.map((candidate) => candidate.file)])]
23126
+ };
23266
23127
  }
23267
23128
  return { mode: "repair", inspectedFrom, projectRoot, changesRequired: true, issues: error.issues, legacyInputs: [] };
23268
23129
  }
23269
23130
  }
23270
23131
  const legacy = discoverLegacyRoot(inspectedFrom);
23271
23132
  if (legacy) {
23272
- return { mode: "migrate", inspectedFrom, legacyRoot: legacy.root, changesRequired: true, issues: [], legacyInputs: legacy.inputs };
23133
+ return {
23134
+ mode: "legacy",
23135
+ inspectedFrom,
23136
+ legacyRoot: legacy.root,
23137
+ changesRequired: true,
23138
+ issues: [issue2("discovery", "legacy_state_unsupported", legacy.root, "legacy KXM/Mesh configuration discovered; this build does not migrate it \u2014 initialise a fresh project directory and copy the YAML definitions you want to keep")],
23139
+ legacyInputs: legacy.inputs
23140
+ };
23273
23141
  }
23274
23142
  const gitRoot = discoverGitRoot(inspectedFrom);
23275
23143
  const candidateRoot = gitRoot ?? inspectedFrom;
@@ -23770,7 +23638,7 @@ function openDatabase(file, description, spec) {
23770
23638
  throw databaseError(
23771
23639
  "runtime_schema_outdated",
23772
23640
  file,
23773
- `${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file (or re-run \`kxm init\`) to start fresh \u2014 upgrading old state in place is deliberately unsupported`
23641
+ `${description} is schema version ${version}; this build requires ${spec.version}. Delete the state file to start fresh and let its owning process recreate it (\`kxm hub start\` for hub state, the Runtime for registry/event stores); \`kxm init\` is project-only and rebuilds no database \u2014 upgrading old state in place is deliberately unsupported`
23774
23642
  );
23775
23643
  }
23776
23644
  if (spec.tables) {
@@ -24532,7 +24400,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
24532
24400
  const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
24533
24401
  const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
24534
24402
  const assignments = compileAssignments(step, id, agent, sink);
24535
- const join50 = compileJoin(step, id, sink);
24403
+ const join49 = compileJoin(step, id, sink);
24536
24404
  const requiredEvidence = compileEvidence(step, id, sink);
24537
24405
  const transitions = compileTransitions(step, id, index, stepIndex, sink);
24538
24406
  const outcomes = Object.keys(transitions).sort(compareCodeUnits4);
@@ -24560,7 +24428,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
24560
24428
  transitions: orderedTransitions,
24561
24429
  requiresPlanHash: requirePlanHash.includes(id),
24562
24430
  assignments,
24563
- join: join50
24431
+ join: join49
24564
24432
  };
24565
24433
  if (kind === "agent" || kind === "moa") {
24566
24434
  if (!agent) return void 0;
@@ -24610,15 +24478,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
24610
24478
  };
24611
24479
  }
24612
24480
  function compileJoin(step, stepId, sink) {
24613
- const join50 = objectValue2(step.join);
24614
- if (!join50) return { strategy: "all" };
24615
- const declared = stringValue2(join50.strategy);
24481
+ const join49 = objectValue2(step.join);
24482
+ if (!join49) return { strategy: "all" };
24483
+ const declared = stringValue2(join49.strategy);
24616
24484
  const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
24617
- const minimumPassed = compileOptionalCount(join50.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
24485
+ const minimumPassed = compileOptionalCount(join49.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
24618
24486
  const compiled = {
24619
24487
  strategy,
24620
24488
  ...minimumPassed !== void 0 ? { minimumPassed } : {},
24621
- ...typeof join50.cancelRemaining === "boolean" ? { cancelRemaining: join50.cancelRemaining } : {}
24489
+ ...typeof join49.cancelRemaining === "boolean" ? { cancelRemaining: join49.cancelRemaining } : {}
24622
24490
  };
24623
24491
  return compiled;
24624
24492
  }
@@ -27847,7 +27715,7 @@ async function watchGithubChecks(input) {
27847
27715
  }
27848
27716
  const fetchImpl = input.fetchImpl ?? fetch;
27849
27717
  const now = input.now ?? Date.now;
27850
- const sleep = input.sleep ?? ((ms) => new Promise((resolve31) => setTimeout(resolve31, ms)));
27718
+ const sleep = input.sleep ?? ((ms) => new Promise((resolve30) => setTimeout(resolve30, ms)));
27851
27719
  const deadline = now() + input.timeoutMs;
27852
27720
  const headers = {
27853
27721
  authorization: `Bearer ${token}`,
@@ -28842,9 +28710,9 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
28842
28710
  }
28843
28711
 
28844
28712
  // plugins/kxm/src/cli/tasks.ts
28845
- var import_yaml13 = __toESM(require_dist(), 1);
28846
- import { existsSync as existsSync24, readFileSync as readFileSync23, readdirSync as readdirSync8 } from "node:fs";
28847
- import { basename as basename6, join as join28, resolve as resolve21 } from "node:path";
28713
+ var import_yaml12 = __toESM(require_dist(), 1);
28714
+ import { existsSync as existsSync23, readFileSync as readFileSync22, readdirSync as readdirSync8 } from "node:fs";
28715
+ import { basename as basename6, join as join27, resolve as resolve20 } from "node:path";
28848
28716
 
28849
28717
  // plugins/kxm/src/suggest.ts
28850
28718
  var WORKFLOW_PATTERNS = [
@@ -29854,7 +29722,7 @@ function createStudioServer(options = {}) {
29854
29722
  }
29855
29723
 
29856
29724
  // plugins/kxm/src/cli/project.ts
29857
- import { join as join27, resolve as resolve20 } from "node:path";
29725
+ import { join as join26, resolve as resolve19 } from "node:path";
29858
29726
  import { createInterface as createInterface2 } from "node:readline";
29859
29727
 
29860
29728
  // plugins/kxm/src/model-inventory.ts
@@ -31683,17 +31551,20 @@ function initializeKxmProjectAtGitRoot(start, gitRoot, options, mutationLock) {
31683
31551
  ]);
31684
31552
  const loaderOptions2 = configOptions(options, repositoryBindings);
31685
31553
  const transactionOptions = repairOptions(options, repositoryBindings);
31554
+ const plan = planKxmInitialization(start, loaderOptions2);
31555
+ if (plan.mode === "legacy") {
31556
+ return { action: "planned", plan, ...plan.projectRoot ? { projectRoot: plan.projectRoot } : {}, files: [] };
31557
+ }
31686
31558
  if (hasKxmInitTransaction(gitRoot)) {
31687
31559
  const operation = inspectKxmInitTransaction(gitRoot, options.schemasDir);
31688
- const plan2 = planKxmInitialization(start, loaderOptions2);
31689
31560
  if (!operation) {
31690
- if (options.dryRun) return { action: "planned", plan: plan2, projectRoot: gitRoot, resumePending: true, files: [] };
31561
+ if (options.dryRun) return { action: "planned", plan, projectRoot: gitRoot, resumePending: true, files: [] };
31691
31562
  if (!mutationLock) throw new Error("project mutation lock is required to clean an empty transaction");
31692
31563
  resumeKxmInitTransaction(gitRoot, transactionOptions);
31693
31564
  } else if (options.dryRun) {
31694
31565
  return {
31695
31566
  action: "planned",
31696
- plan: plan2,
31567
+ plan,
31697
31568
  projectRoot: gitRoot,
31698
31569
  resumePending: true,
31699
31570
  transactionKind: operation.kind,
@@ -31742,10 +31613,6 @@ function initializeKxmProjectAtGitRoot(start, gitRoot, options, mutationLock) {
31742
31613
  };
31743
31614
  }
31744
31615
  }
31745
- const plan = planKxmInitialization(start, loaderOptions2);
31746
- if (plan.mode === "migrate") {
31747
- return { action: "planned", plan, ...plan.projectRoot ? { projectRoot: plan.projectRoot } : {}, files: [] };
31748
- }
31749
31616
  if (plan.mode === "repair") {
31750
31617
  const projectRoot = plan.projectRoot ?? gitRoot;
31751
31618
  let templateRepair;
@@ -31894,1486 +31761,6 @@ function initializeKxmProject(start = process.cwd(), options = {}) {
31894
31761
  return withKxmLocalBindingLock(gitRoot, storeOptions, (lock) => initializeKxmProjectAtGitRoot(start, gitRoot, options, lock));
31895
31762
  }
31896
31763
 
31897
- // plugins/kxm/src/migrate.ts
31898
- var import_yaml12 = __toESM(require_dist(), 1);
31899
- import { createHash as createHash13 } from "node:crypto";
31900
- import { chmodSync as chmodSync6, closeSync as closeSync3, existsSync as existsSync23, fsyncSync as fsyncSync3, lstatSync as lstatSync8, mkdirSync as mkdirSync19, openSync as openSync3, readFileSync as readFileSync22, renameSync as renameSync7, rmSync as rmSync9, writeFileSync as writeFileSync18 } from "node:fs";
31901
- import { join as join26 } from "node:path";
31902
- var MAX_LEGACY_JSON_BYTES = 1024 * 1024;
31903
- var MAX_LEGACY_JSON_DEPTH = 32;
31904
- var MAX_LEGACY_JSON_NODES = 32768;
31905
- var MAX_LEGACY_SOURCES = 64;
31906
- function migrateIssue(phase, code, file, message) {
31907
- return { phase, code, file, message };
31908
- }
31909
- function migrateFail(code, file, message) {
31910
- throw new KxmConfigError([migrateIssue("parse", code, file, message)]);
31911
- }
31912
- function parseLegacyJson(text, label) {
31913
- const bytes = Buffer.byteLength(text, "utf8");
31914
- if (bytes > MAX_LEGACY_JSON_BYTES) migrateFail("document_too_large", label, `document exceeds ${MAX_LEGACY_JSON_BYTES} bytes`);
31915
- const state = { nodes: 0 };
31916
- const parseNode = (value, path4, depth) => {
31917
- state.nodes += 1;
31918
- if (state.nodes > MAX_LEGACY_JSON_NODES) migrateFail("node_limit", label, `document exceeds ${MAX_LEGACY_JSON_NODES} nodes`);
31919
- if (depth > MAX_LEGACY_JSON_DEPTH) migrateFail("depth_limit", label, `document exceeds nesting depth ${MAX_LEGACY_JSON_DEPTH}`);
31920
- if (value === null || typeof value === "boolean" || typeof value === "string") return value;
31921
- if (typeof value === "number") {
31922
- if (!Number.isFinite(value)) migrateFail("non_json_number", label, `${path4} is not a finite number`);
31923
- return value;
31924
- }
31925
- if (Array.isArray(value)) {
31926
- return value.map((entry, index) => parseNode(entry, `${path4}[${index}]`, depth + 1));
31927
- }
31928
- if (value && typeof value === "object") {
31929
- const record = {};
31930
- for (const [key, entry] of Object.entries(value)) record[key] = parseNode(entry, `${path4}.${key}`, depth + 1);
31931
- return record;
31932
- }
31933
- migrateFail("non_json_value", label, `${path4} is not JSON-compatible`);
31934
- };
31935
- let raw;
31936
- try {
31937
- raw = JSON.parse(text);
31938
- } catch (error) {
31939
- migrateFail("invalid_json", label, error instanceof Error ? error.message : String(error));
31940
- }
31941
- detectDuplicateKeys(text, label);
31942
- return parseNode(raw, "$", 0);
31943
- }
31944
- function detectDuplicateKeys(text, label) {
31945
- const stack = [];
31946
- let index = 0;
31947
- const length = text.length;
31948
- const skipWhitespace = () => {
31949
- while (index < length && /[\s]/.test(text[index])) index += 1;
31950
- };
31951
- const parseString = () => {
31952
- let result = "";
31953
- index += 1;
31954
- while (index < length) {
31955
- const char = text[index];
31956
- if (char === '"') {
31957
- index += 1;
31958
- return result;
31959
- }
31960
- if (char === "\\") {
31961
- const escape2 = text[index + 1];
31962
- if (escape2 === "u") {
31963
- result += String.fromCharCode(Number.parseInt(text.slice(index + 2, index + 6), 16));
31964
- index += 6;
31965
- } else {
31966
- const escapes = { '"': '"', "\\": "\\", "/": "/", b: "\b", f: "\f", n: "\n", r: "\r", t: " " };
31967
- result += escapes[escape2] ?? escape2;
31968
- index += 2;
31969
- }
31970
- } else {
31971
- result += char;
31972
- index += 1;
31973
- }
31974
- }
31975
- migrateFail("invalid_json", label, "unterminated string");
31976
- };
31977
- const skipValue = () => {
31978
- skipWhitespace();
31979
- const char = text[index];
31980
- if (char === '"') {
31981
- parseString();
31982
- return;
31983
- }
31984
- if (char === "{" || char === "[") {
31985
- parseContainer();
31986
- return;
31987
- }
31988
- while (index < length && !/[\s,\]}]/.test(text[index])) index += 1;
31989
- };
31990
- const parseContainer = () => {
31991
- const char = text[index];
31992
- if (stack.length > MAX_LEGACY_JSON_DEPTH) migrateFail("depth_limit", label, `document exceeds nesting depth ${MAX_LEGACY_JSON_DEPTH}`);
31993
- if (char === "{") {
31994
- const keys = /* @__PURE__ */ new Set();
31995
- stack.push(keys);
31996
- index += 1;
31997
- skipWhitespace();
31998
- if (text[index] === "}") {
31999
- index += 1;
32000
- stack.pop();
32001
- return;
32002
- }
32003
- for (; ; ) {
32004
- skipWhitespace();
32005
- if (text[index] !== '"') migrateFail("invalid_json", label, `expected object key at byte ${index}`);
32006
- const key = parseString();
32007
- if (key === "__proto__" || key === "constructor" || key === "prototype") {
32008
- migrateFail("forbidden_key", label, `key ${JSON.stringify(key)} is forbidden`);
32009
- }
32010
- const frame = stack[stack.length - 1];
32011
- if (frame instanceof Set) {
32012
- if (frame.has(key)) migrateFail("duplicate_key", label, `duplicate key ${JSON.stringify(key)}`);
32013
- frame.add(key);
32014
- }
32015
- skipWhitespace();
32016
- if (text[index] !== ":") migrateFail("invalid_json", label, `expected ':' at byte ${index}`);
32017
- index += 1;
32018
- skipValue();
32019
- skipWhitespace();
32020
- if (text[index] === ",") {
32021
- index += 1;
32022
- continue;
32023
- }
32024
- if (text[index] === "}") {
32025
- index += 1;
32026
- stack.pop();
32027
- return;
32028
- }
32029
- migrateFail("invalid_json", label, `expected ',' or '}' at byte ${index}`);
32030
- }
32031
- }
32032
- if (char === "[") {
32033
- stack.push("array");
32034
- index += 1;
32035
- skipWhitespace();
32036
- if (text[index] === "]") {
32037
- index += 1;
32038
- stack.pop();
32039
- return;
32040
- }
32041
- for (; ; ) {
32042
- skipValue();
32043
- skipWhitespace();
32044
- if (text[index] === ",") {
32045
- index += 1;
32046
- continue;
32047
- }
32048
- if (text[index] === "]") {
32049
- index += 1;
32050
- stack.pop();
32051
- return;
32052
- }
32053
- migrateFail("invalid_json", label, `expected ',' or ']' at byte ${index}`);
32054
- }
32055
- }
32056
- };
32057
- skipValue();
32058
- skipWhitespace();
32059
- if (index !== length) migrateFail("invalid_json", label, `trailing content at byte ${index}`);
32060
- }
32061
- function sha256Of(input) {
32062
- return `sha256:${createHash13("sha256").update(input).digest("hex")}`;
32063
- }
32064
- function readKxmLegacySources(root) {
32065
- const files = legacyConfigFilesAt(root);
32066
- if (files.length > MAX_LEGACY_SOURCES) {
32067
- throw new KxmConfigError([migrateIssue("discovery", "legacy_source_limit", ".kxm/config", `legacy configuration exceeds ${MAX_LEGACY_SOURCES} files`)]);
32068
- }
32069
- return files.map((path4) => {
32070
- const absolute = join26(root, ...path4.split("/"));
32071
- const stat = lstatSync8(absolute);
32072
- if (stat.isSymbolicLink() || !stat.isFile()) {
32073
- throw new KxmConfigError([migrateIssue("path", "legacy_source_invalid", path4, "legacy configuration must be a regular file, not a link")]);
32074
- }
32075
- const buffer = readFileSync22(absolute);
32076
- let text;
32077
- try {
32078
- text = new TextDecoder("utf-8", { fatal: true }).decode(buffer);
32079
- } catch {
32080
- throw new KxmConfigError([migrateIssue("parse", "invalid_utf8", path4, "document is not valid UTF-8")]);
32081
- }
32082
- return {
32083
- path: path4,
32084
- sha256: sha256Of(buffer),
32085
- bytes: buffer.byteLength,
32086
- value: parseLegacyJson(text, path4)
32087
- };
32088
- });
32089
- }
32090
- function legacySourceDigest(sources) {
32091
- return sha256Of(kxmCanonicalJson(sources.map((source) => ({ path: source.path, sha256: source.sha256, bytes: source.bytes }))));
32092
- }
32093
- var IDENTIFIER2 = /^[a-z][a-z0-9]*(?:[-_][a-z0-9]+)*$/;
32094
- var KNOWN_AGENT_FIELDS = /* @__PURE__ */ new Set(["name", "kind", "driver", "model", "thinking", "purpose"]);
32095
- var KNOWN_GATE_FIELDS = /* @__PURE__ */ new Set(["name", "kind", "driver", "purpose"]);
32096
- var KNOWN_WORKFLOW_FIELDS = /* @__PURE__ */ new Set([
32097
- "id",
32098
- "source",
32099
- "project",
32100
- "target",
32101
- "secret",
32102
- "secretEnv",
32103
- "signalSecret",
32104
- "signalSecretEnv",
32105
- "event",
32106
- "filter",
32107
- "delivery",
32108
- "ttlMs",
32109
- "promptTemplate",
32110
- "stages",
32111
- "maxTransitions",
32112
- "reproOracle",
32113
- "planHash",
32114
- "requirePlanHash"
32115
- ]);
32116
- var KNOWN_STAGE_FIELDS = /* @__PURE__ */ new Set(["id", "label", "instructions", "requiredEvidence", "maxAttempts", "area", "evidencePolicies", "on", "maxTransitions"]);
32117
- var KNOWN_POLICY_FIELDS = /* @__PURE__ */ new Set(["kind", "minProducers", "eligibleAgents", "acceptedStatuses", "degradation"]);
32118
- var KNOWN_TRANSITION_FIELDS = /* @__PURE__ */ new Set(["target", "maxTransitions"]);
32119
- function compareCodeUnits6(left, right) {
32120
- return left < right ? -1 : left > right ? 1 : 0;
32121
- }
32122
- function asObject2(value) {
32123
- return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
32124
- }
32125
- function asString(value) {
32126
- return typeof value === "string" ? value : void 0;
32127
- }
32128
- function normalizeIdentifier(raw) {
32129
- return raw.toLocaleLowerCase("en-US").replace(/[^a-z0-9_-]+/g, "-").replace(/[-_]{2,}/g, "-").replace(/^[-_]+/, "").replace(/[-_]+$/, "").replace(/^(?=[0-9])/, "id-");
32130
- }
32131
- function pointerEscape2(segment) {
32132
- const escaped = segment.replace(/~/g, "~0").replace(/\//g, "~1");
32133
- if (escaped.length <= 200) return escaped;
32134
- const digest = createHash13("sha256").update(segment, "utf8").digest("hex").slice(0, 8);
32135
- return `${escaped.slice(0, 180)}~digest-${digest}`;
32136
- }
32137
- function elide(raw, max = 120) {
32138
- return raw.length <= max ? raw : `${raw.slice(0, max - 15)}\u2026[${raw.length}]`;
32139
- }
32140
- function keyFragment(raw) {
32141
- const full = normalizeIdentifier(raw) || "unnamed";
32142
- const normalized = full.length > 96 ? `${full.slice(0, 96)}-${full.length}` : full;
32143
- const digest = createHash13("sha256").update(raw, "utf8").digest("hex").slice(0, 8);
32144
- return `${normalized}-${digest}`;
32145
- }
32146
- function recordUnmapped(plan, sourcePath, pointer, value, sensitive) {
32147
- const digestInput = sensitive ? { redacted: true, bytes: Buffer.byteLength(kxmCanonicalJson(value === void 0 ? null : value), "utf8") } : value === void 0 ? null : value;
32148
- plan.unmapped.push({
32149
- sourcePath,
32150
- jsonPointer: pointer,
32151
- valueSha256: sha256Of(kxmCanonicalJson(digestInput)),
32152
- sensitive
32153
- });
32154
- }
32155
- function recordUnknownFields(plan, sourcePath, pointer, record, known) {
32156
- for (const key of Object.keys(record).sort(compareCodeUnits6)) {
32157
- if (!known.has(key)) recordUnmapped(plan, sourcePath, `${pointer}/${pointerEscape2(key)}`, record[key], false);
32158
- }
32159
- }
32160
- function mapIdentity(plan, sourcePath, raw, kind, taken, decisions, dropPointer, dropValue) {
32161
- const normalized = normalizeIdentifier(raw);
32162
- const usable = (candidate) => IDENTIFIER2.test(candidate) && candidate.length <= 64;
32163
- const adopt = (candidate) => {
32164
- const foldedCandidate = candidate.toLocaleLowerCase("en-US");
32165
- const priorCandidate = taken.get(foldedCandidate);
32166
- if (priorCandidate !== void 0 && priorCandidate !== raw) return { outcome: "taken" };
32167
- if (priorCandidate === raw) return { outcome: "duplicate" };
32168
- taken.set(foldedCandidate, raw);
32169
- plan.renames.push({ from: elide(raw), to: candidate, kind });
32170
- return { outcome: "adopted", id: candidate };
32171
- };
32172
- const reportDuplicate = () => {
32173
- const decided = resolveDecision(
32174
- plan,
32175
- `identity-duplicate:${kind}:${keyFragment(raw)}`,
32176
- "unsupported",
32177
- sourcePath,
32178
- `${kind} name ${JSON.stringify(elide(raw, 200))} is declared more than once; confirm the duplicate definition is dropped`,
32179
- ["drop"],
32180
- decisions
32181
- );
32182
- recordUnmapped(plan, sourcePath, dropPointer ?? `/duplicates/${pointerEscape2(raw)}`, dropValue ?? raw, false);
32183
- return decided === "drop" ? { status: "dropped" } : { status: "pending" };
32184
- };
32185
- if (!normalized || !usable(normalized)) {
32186
- const supplied = resolveDecision(
32187
- plan,
32188
- `identity:${kind}:${keyFragment(raw)}`,
32189
- "identity",
32190
- sourcePath,
32191
- `${kind} name ${JSON.stringify(elide(raw, 200))} cannot be normalized to a KXM identifier; supply a decision with the target identifier`,
32192
- ["<identifier>"],
32193
- decisions
32194
- );
32195
- if (typeof supplied === "string" && usable(supplied)) {
32196
- const adopted = adopt(supplied);
32197
- if (adopted.outcome === "adopted") return { status: "adopted", id: adopted.id };
32198
- if (adopted.outcome === "duplicate") return reportDuplicate();
32199
- plan.ambiguities.push({
32200
- key: `identity:${kind}:${keyFragment(raw)}`,
32201
- category: "identity",
32202
- sourcePath,
32203
- message: `decision-supplied identifier ${JSON.stringify(elide(supplied, 200))} for ${kind} ${JSON.stringify(elide(raw, 200))} collides with another migrated identity`,
32204
- allowedValues: ["<identifier>"]
32205
- });
32206
- }
32207
- return { status: "pending" };
32208
- }
32209
- const folded = normalized.toLocaleLowerCase("en-US");
32210
- const prior = taken.get(folded);
32211
- if (prior && prior !== raw) {
32212
- const supplied = resolveDecision(
32213
- plan,
32214
- `identity-collision:${kind}:${folded}-${keyFragment(raw)}`,
32215
- "identity",
32216
- sourcePath,
32217
- `${kind} names ${JSON.stringify(elide(prior, 200))} and ${JSON.stringify(elide(raw, 200))} collide after normalization; supply a distinct identifier for ${JSON.stringify(elide(raw, 200))}`,
32218
- ["<identifier>"],
32219
- decisions
32220
- );
32221
- if (typeof supplied === "string" && usable(supplied)) {
32222
- const adopted = adopt(supplied);
32223
- if (adopted.outcome === "adopted") return { status: "adopted", id: adopted.id };
32224
- if (adopted.outcome === "duplicate") return reportDuplicate();
32225
- plan.ambiguities.push({
32226
- key: `identity-collision:${kind}:${folded}-${keyFragment(raw)}`,
32227
- category: "identity",
32228
- sourcePath,
32229
- message: `decision-supplied identifier ${JSON.stringify(elide(supplied, 200))} for ${kind} ${JSON.stringify(elide(raw, 200))} collides with another migrated identity`,
32230
- allowedValues: ["<identifier>"]
32231
- });
32232
- }
32233
- return { status: "pending" };
32234
- }
32235
- if (prior === raw) return reportDuplicate();
32236
- taken.set(folded, raw);
32237
- if (normalized !== raw && !plan.renames.some((rename) => rename.kind === kind && rename.from === raw)) {
32238
- plan.renames.push({ from: elide(raw), to: normalized, kind });
32239
- }
32240
- return { status: "adopted", id: normalized };
32241
- }
32242
- function decisionValueAllowed(key, value, allowed) {
32243
- if (value === void 0) return false;
32244
- if (allowed.some((candidate) => candidate === "<identifier>")) {
32245
- return typeof value === "string" && IDENTIFIER2.test(value) && value.length <= 64;
32246
- }
32247
- if (allowed.some((candidate) => candidate === "<provider/model>")) {
32248
- if (typeof value !== "string") return false;
32249
- const separator = value.indexOf("/");
32250
- const provider = separator > 0 ? normalizeIdentifier(value.slice(0, separator)) : "";
32251
- const modelId2 = separator > 0 ? value.slice(separator + 1) : "";
32252
- return IDENTIFIER2.test(provider) && provider.length <= 64 && modelId2.length >= 1 && modelId2.length <= 200;
32253
- }
32254
- if (key.startsWith("backedge:")) {
32255
- return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 100;
32256
- }
32257
- if (key.startsWith("budget:")) {
32258
- return typeof value === "number" && Number.isInteger(value) && value >= 1 && value <= 1e3;
32259
- }
32260
- if (key.startsWith("coordinator:") || key.startsWith("producer:")) {
32261
- if (allowed.some((candidate) => candidate === value)) return true;
32262
- return allowed.length >= 64 && typeof value === "string" && IDENTIFIER2.test(value) && value.length <= 64;
32263
- }
32264
- return allowed.some((candidate) => candidate === value);
32265
- }
32266
- function resolveDecision(plan, key, category, sourcePath, message, allowedValues, decisions) {
32267
- plan.decisionKeys.add(key);
32268
- plan.decisionAllowedValues.set(key, allowedValues);
32269
- if (Object.hasOwn(decisions, key)) {
32270
- const value = decisions[key];
32271
- if (decisionValueAllowed(key, value, allowedValues)) return value;
32272
- if (!plan.invalidDecisions.includes(key)) plan.invalidDecisions.push(key);
32273
- }
32274
- if (!plan.ambiguities.some((ambiguity) => ambiguity.key === key)) {
32275
- plan.ambiguities.push({ key, category, sourcePath, message, allowedValues });
32276
- }
32277
- return void 0;
32278
- }
32279
- function convertAgentsRoster(plan, source, decisions, agentNames) {
32280
- const agents = /* @__PURE__ */ new Map();
32281
- const models = /* @__PURE__ */ new Map();
32282
- const rawNames = /* @__PURE__ */ new Map();
32283
- const pendingRawNames = /* @__PURE__ */ new Set();
32284
- const modelNames = /* @__PURE__ */ new Map();
32285
- let identityPending = false;
32286
- const root = asObject2(source.value);
32287
- if (!root) {
32288
- resolveDecision(
32289
- plan,
32290
- `unsupported:${source.path}:/`,
32291
- "unsupported",
32292
- source.path,
32293
- "agents.json must be a JSON object with an agents array; confirm it is dropped from conversion",
32294
- ["drop"],
32295
- decisions
32296
- );
32297
- return { agents, models, rawNames, pendingRawNames, identityPending };
32298
- }
32299
- recordUnknownFields(plan, source.path, "", root, /* @__PURE__ */ new Set(["schema", "project", "host", "agents", "ownership"]));
32300
- if (root.ownership !== void 0) recordUnmapped(plan, source.path, "/ownership", root.ownership, false);
32301
- if (root.host !== void 0) recordUnmapped(plan, source.path, "/host", root.host, false);
32302
- if (root.project !== void 0) recordUnmapped(plan, source.path, "/project", root.project, false);
32303
- if (root.schema !== void 0 && root.schema !== "kxm.agents.v1") recordUnmapped(plan, source.path, "/schema", root.schema, false);
32304
- const list = Array.isArray(root.agents) ? root.agents : [];
32305
- for (const [index, entry] of list.entries()) {
32306
- const pointer = `/agents/${index}`;
32307
- const record = asObject2(entry);
32308
- if (!record) {
32309
- recordUnmapped(plan, source.path, pointer, entry, false);
32310
- continue;
32311
- }
32312
- recordUnknownFields(plan, source.path, pointer, record, KNOWN_AGENT_FIELDS);
32313
- const name = asString(record.name);
32314
- const kind = asString(record.kind) ?? "agent";
32315
- const driver = asString(record.driver) ?? "ai";
32316
- if (!name || kind !== "agent" || driver !== "ai") {
32317
- resolveDecision(
32318
- plan,
32319
- `unsupported:${source.path}:${pointer}`,
32320
- "unsupported",
32321
- source.path,
32322
- `roster entry ${index} (${name === void 0 ? "unnamed" : elide(name, 200)}) has kind ${elide(kind, 64)}/driver ${elide(driver, 64)}; only agent/ai entries convert losslessly \u2014 confirm the entry is dropped`,
32323
- ["drop"],
32324
- decisions
32325
- );
32326
- recordUnmapped(plan, source.path, pointer, record, false);
32327
- continue;
32328
- }
32329
- resolveDecision(
32330
- plan,
32331
- `permission:${keyFragment(name)}`,
32332
- "permission",
32333
- source.path,
32334
- `approve the narrowed tool/repository/network ceiling for agent ${elide(name, 200)}`,
32335
- ["approve"],
32336
- decisions
32337
- );
32338
- const agentIdentity = mapIdentity(plan, source.path, name, "agent", agentNames, decisions, pointer, record);
32339
- if (agentIdentity.status === "pending") {
32340
- identityPending = true;
32341
- pendingRawNames.add(name);
32342
- }
32343
- if (agentIdentity.status !== "adopted") continue;
32344
- const id = agentIdentity.id;
32345
- const agent = {
32346
- schema: "kxm.agent.v1",
32347
- purpose: asString(record.purpose) ?? `Migrated legacy agent ${elide(name, 200)}.`
32348
- };
32349
- const model = asString(record.model);
32350
- if (model) {
32351
- const applyModel = (candidate) => {
32352
- const separator = candidate.indexOf("/");
32353
- if (separator <= 0) return false;
32354
- const provider = normalizeIdentifier(candidate.slice(0, separator));
32355
- const modelId2 = candidate.slice(separator + 1);
32356
- if (IDENTIFIER2.test(provider) && provider.length <= 64 && modelId2.length >= 1 && modelId2.length <= 200) {
32357
- agent.model = { provider, model: modelId2 };
32358
- return true;
32359
- }
32360
- return false;
32361
- };
32362
- if (!applyModel(model)) {
32363
- const supplied = resolveDecision(
32364
- plan,
32365
- `model:${keyFragment(name)}`,
32366
- "identity",
32367
- source.path,
32368
- `agent ${elide(name, 200)} model ${JSON.stringify(elide(model, 200))} does not map to a provider identifier + model id pair; supply <provider>/<model>`,
32369
- ["<provider/model>"],
32370
- decisions
32371
- );
32372
- if (typeof supplied === "string" && supplied !== model) {
32373
- applyModel(supplied);
32374
- } else if (typeof supplied !== "string") {
32375
- recordUnmapped(plan, source.path, `${pointer}/model`, model, false);
32376
- }
32377
- }
32378
- }
32379
- const thinking = asString(record.thinking);
32380
- if (thinking) {
32381
- const selector = asObject2(agent.model);
32382
- if (selector && typeof selector.profile !== "string") {
32383
- const profileIdentity = mapIdentity(plan, source.path, `${id}-primary`, "model", modelNames, decisions);
32384
- const profileId = profileIdentity.status === "adopted" ? profileIdentity.id : void 0;
32385
- if (profileId) {
32386
- const profile = {
32387
- schema: "kxm.model.v1",
32388
- provider: selector.provider,
32389
- model: selector.model,
32390
- thinking
32391
- };
32392
- models.set(profileId, profile);
32393
- agent.model = { profile: profileId };
32394
- }
32395
- } else {
32396
- recordUnmapped(plan, source.path, `${pointer}/thinking`, thinking, false);
32397
- }
32398
- }
32399
- agent.tools = { preset: "workspace-writer" };
32400
- agent.defaultRepositoryAccess = "none";
32401
- agent.network = "provider-only";
32402
- plan.permissionChanges.push({
32403
- sourcePath: source.path,
32404
- targetPath: `.kxm/agents/${id}.yaml`,
32405
- direction: "unknown",
32406
- summary: `agent ${elide(name, 200)}: legacy workers used launch-time tool flags the converter cannot observe; migrated ceiling is workspace-writer tools, no repository access, provider-only network`,
32407
- decisionKey: `permission:${keyFragment(name)}`
32408
- });
32409
- agents.set(id, agent);
32410
- if (!rawNames.has(name)) rawNames.set(name, id);
32411
- }
32412
- return { agents, models, rawNames, pendingRawNames, identityPending };
32413
- }
32414
- var IMPLEMENTED_GATES = /* @__PURE__ */ new Set(["test", "scm-delivery"]);
32415
- function convertGatesRoster(plan, source, decisions) {
32416
- const root = asObject2(source.value);
32417
- if (!root) {
32418
- resolveDecision(
32419
- plan,
32420
- `unsupported:${source.path}:/`,
32421
- "unsupported",
32422
- source.path,
32423
- "gates.json must be a JSON object with a gates array; confirm it is dropped from conversion",
32424
- ["drop"],
32425
- decisions
32426
- );
32427
- return;
32428
- }
32429
- recordUnknownFields(plan, source.path, "", root, /* @__PURE__ */ new Set(["schema", "gates"]));
32430
- if (root.schema !== void 0 && root.schema !== "kxm.gates.v1") recordUnmapped(plan, source.path, "/schema", root.schema, false);
32431
- const list = Array.isArray(root.gates) ? root.gates : [];
32432
- for (const [index, entry] of list.entries()) {
32433
- const pointer = `/gates/${index}`;
32434
- const record = asObject2(entry);
32435
- if (!record) {
32436
- recordUnmapped(plan, source.path, pointer, entry, false);
32437
- continue;
32438
- }
32439
- recordUnknownFields(plan, source.path, pointer, record, KNOWN_GATE_FIELDS);
32440
- const name = asString(record.name) ?? `gate-${index}`;
32441
- if (!IMPLEMENTED_GATES.has(name)) {
32442
- resolveDecision(
32443
- plan,
32444
- `gate:${keyFragment(name)}`,
32445
- "unsupported",
32446
- source.path,
32447
- `gate ${JSON.stringify(elide(name, 200))} has no registered deterministic runner in KXM; it is preserved in the migration report only`,
32448
- ["report-only"],
32449
- decisions
32450
- );
32451
- recordUnmapped(plan, source.path, pointer, entry, false);
32452
- }
32453
- }
32454
- }
32455
- function mapStageRef(context, raw) {
32456
- return context.renamesByKind.get(raw) ?? normalizeIdentifier(raw);
32457
- }
32458
- function convertTransition(plan, source, context, stageId, stageKey, outcome, rawOutcome, rawValue, decisions, stagePointer) {
32459
- const pointer = `${stagePointer}/on/${pointerEscape2(rawOutcome)}`;
32460
- const rule = typeof rawValue === "string" ? { target: rawValue } : asObject2(rawValue);
32461
- if (!rule) {
32462
- recordUnmapped(plan, source.path, pointer, rawValue, false);
32463
- return void 0;
32464
- }
32465
- if (typeof rawValue !== "string") recordUnknownFields(plan, source.path, pointer, rule, KNOWN_TRANSITION_FIELDS);
32466
- const target = asString(rule.target);
32467
- if (!target) {
32468
- recordUnmapped(plan, source.path, pointer, rawValue, false);
32469
- return void 0;
32470
- }
32471
- const sourceIndex = context.stageIds.indexOf(stageId);
32472
- if (target === "$terminal") {
32473
- const chosen = resolveDecision(
32474
- plan,
32475
- `terminal:${context.workflowKey}:${stageKey}:${outcome}`,
32476
- "terminal-status",
32477
- source.path,
32478
- `legacy transition ${context.workflowId}/${stageId} outcome ${outcome} ends the run; choose the KXM terminal status (legacy semantics completed the run even on failure outcomes)`,
32479
- outcome === "passed" ? ["completed"] : ["completed", "failed", "cancelled"],
32480
- decisions
32481
- );
32482
- const status = typeof chosen === "string" && ["completed", "failed", "cancelled"].includes(chosen) ? chosen : "completed";
32483
- const objectRule2 = asObject2(rawValue);
32484
- const result = { target: "$terminal", terminalStatus: status };
32485
- if (typeof objectRule2?.maxTransitions === "number") {
32486
- if (Number.isInteger(objectRule2.maxTransitions) && objectRule2.maxTransitions >= 1 && objectRule2.maxTransitions <= 100) {
32487
- result.maxTransitions = objectRule2.maxTransitions;
32488
- } else {
32489
- recordUnmapped(plan, source.path, `${pointer}/maxTransitions`, objectRule2.maxTransitions, false);
32490
- }
32491
- }
32492
- return result;
32493
- }
32494
- const targetId = mapStageRef(context, target);
32495
- const targetIndex = context.stageIds.indexOf(targetId);
32496
- if (targetIndex < 0) {
32497
- resolveDecision(
32498
- plan,
32499
- `transition-target:${context.workflowKey}:${stageKey}:${outcome}`,
32500
- "identity",
32501
- source.path,
32502
- `transition target ${JSON.stringify(elide(target, 200))} is not a stage of workflow ${context.workflowId}; confirm the edge is dropped`,
32503
- ["drop"],
32504
- decisions
32505
- );
32506
- recordUnmapped(plan, source.path, pointer, rawValue, false);
32507
- return void 0;
32508
- }
32509
- const objectRule = asObject2(rawValue);
32510
- let explicitBudget;
32511
- if (typeof objectRule?.maxTransitions === "number") {
32512
- if (Number.isInteger(objectRule.maxTransitions) && objectRule.maxTransitions >= 1 && objectRule.maxTransitions <= 100) {
32513
- explicitBudget = objectRule.maxTransitions;
32514
- } else {
32515
- recordUnmapped(plan, source.path, `${pointer}/maxTransitions`, objectRule.maxTransitions, false);
32516
- }
32517
- }
32518
- const isBackEdge = targetIndex <= sourceIndex;
32519
- if (isBackEdge && explicitBudget === void 0) {
32520
- const chosen = resolveDecision(
32521
- plan,
32522
- `backedge:${context.workflowKey}:${stageKey}:${outcome}`,
32523
- "transition-budget",
32524
- source.path,
32525
- `back-edge ${context.workflowId}/${stageId} -> ${targetId} (outcome ${outcome}) has no per-edge budget in legacy; KXM requires maxTransitions (1-100)`,
32526
- [2, 3, 4, 5],
32527
- decisions
32528
- );
32529
- const budget = typeof chosen === "number" && Number.isInteger(chosen) && chosen >= 1 && chosen <= 100 ? chosen : 2;
32530
- return { target: targetId, maxTransitions: budget };
32531
- }
32532
- if (explicitBudget !== void 0) return { target: targetId, maxTransitions: explicitBudget };
32533
- return targetId;
32534
- }
32535
- function convertWorkflowFile(plan, source, decisions, agentIds, agentRawNames, pendingAgentRawNames, rosterIdentityPending, workflowNames) {
32536
- const workflows = /* @__PURE__ */ new Map();
32537
- const list = Array.isArray(source.value) ? source.value : [source.value];
32538
- for (const [definitionIndex, entry] of list.entries()) {
32539
- const definition = asObject2(entry);
32540
- const basePointer = Array.isArray(source.value) ? `/${definitionIndex}` : "";
32541
- if (!definition) {
32542
- recordUnmapped(plan, source.path, basePointer || "/", entry, false);
32543
- continue;
32544
- }
32545
- recordUnknownFields(plan, source.path, basePointer, definition, KNOWN_WORKFLOW_FIELDS);
32546
- const fileStem = source.path.slice(source.path.lastIndexOf("/") + 1).replace(/\.json$/u, "");
32547
- const rawId = asString(definition.id) ?? `${fileStem}-${definitionIndex}`;
32548
- const workflowIdentity = mapIdentity(plan, source.path, rawId, "workflow", workflowNames, decisions, basePointer || "/", definition);
32549
- if (workflowIdentity.status === "dropped") continue;
32550
- const workflowId = workflowIdentity.status === "adopted" ? workflowIdentity.id : `preview-${(normalizeIdentifier(rawId) || "unnamed").slice(0, 46).replace(/[-_]+$/u, "")}-${createHash13("sha256").update(`${rawId}#${source.path}#${definitionIndex}`, "utf8").digest("hex").slice(0, 8)}`;
32551
- const workflowKey = keyFragment(rawId);
32552
- for (const field of ["source", "project", "event", "filter", "delivery", "ttlMs", "promptTemplate"]) {
32553
- if (definition[field] !== void 0) recordUnmapped(plan, source.path, `${basePointer}/${field}`, definition[field], false);
32554
- }
32555
- for (const field of ["secret", "signalSecret", "secretEnv", "signalSecretEnv"]) {
32556
- if (definition[field] !== void 0) {
32557
- recordUnmapped(plan, source.path, `${basePointer}/${field}`, definition[field], true);
32558
- resolveDecision(
32559
- plan,
32560
- `secret:${workflowKey}:${field}`,
32561
- "secret-reference",
32562
- source.path,
32563
- `workflow ${workflowId} field ${field} configures a webhook secret; KXM workflows never embed secrets \u2014 confirm the field is dropped and re-granted through scoped secret references when the trigger surface lands`,
32564
- ["drop"],
32565
- decisions
32566
- );
32567
- }
32568
- }
32569
- const rawTarget = asString(definition.target);
32570
- const normalizedTarget = rawTarget ? normalizeIdentifier(rawTarget) : void 0;
32571
- let stepAgent = rawTarget !== void 0 ? agentRawNames.get(rawTarget) : void 0;
32572
- if (!stepAgent && rawTarget !== void 0 && pendingAgentRawNames.has(rawTarget)) {
32573
- stepAgent = void 0;
32574
- } else if (!stepAgent && rawTarget !== void 0) {
32575
- const foldedTarget = rawTarget.toLocaleLowerCase("en-US");
32576
- for (const [rawName, id] of agentRawNames) {
32577
- if (rawName.toLocaleLowerCase("en-US") === foldedTarget) {
32578
- stepAgent = id;
32579
- break;
32580
- }
32581
- }
32582
- }
32583
- if (!stepAgent && agentRawNames.size === 0 && normalizedTarget && agentIds.has(normalizedTarget)) {
32584
- stepAgent = normalizedTarget;
32585
- }
32586
- const targetIsPendingAgent = rawTarget !== void 0 && [...pendingAgentRawNames].some((pending) => pending === rawTarget || pending.toLocaleLowerCase("en-US") === rawTarget.toLocaleLowerCase("en-US"));
32587
- if (!stepAgent && targetIsPendingAgent) {
32588
- stepAgent = normalizedTarget && IDENTIFIER2.test(normalizedTarget) && normalizedTarget.length <= 64 ? normalizedTarget : "coordinator";
32589
- }
32590
- if (!stepAgent) {
32591
- if (agentIds.size === 0 && rosterIdentityPending) {
32592
- stepAgent = normalizedTarget && IDENTIFIER2.test(normalizedTarget) && normalizedTarget.length <= 64 ? normalizedTarget : "coordinator";
32593
- } else if (agentIds.size === 0) {
32594
- const dropped = resolveDecision(
32595
- plan,
32596
- `coordinator:${workflowKey}`,
32597
- "identity",
32598
- source.path,
32599
- `workflow ${workflowId} target ${JSON.stringify(rawTarget === void 0 ? "" : elide(rawTarget, 200))} matches no migrated agent and the roster is empty; confirm the workflow is dropped`,
32600
- ["drop"],
32601
- decisions
32602
- );
32603
- if (dropped === "drop") continue;
32604
- stepAgent = normalizedTarget && IDENTIFIER2.test(normalizedTarget) && normalizedTarget.length <= 64 ? normalizedTarget : "coordinator";
32605
- } else {
32606
- const chosen = resolveDecision(
32607
- plan,
32608
- `coordinator:${workflowKey}`,
32609
- "identity",
32610
- source.path,
32611
- `workflow ${workflowId} target ${JSON.stringify(rawTarget === void 0 ? "" : elide(rawTarget, 200))} is not a migrated agent; choose the roster agent that executes its steps`,
32612
- [...agentIds.keys()].sort(compareCodeUnits6).slice(0, 64),
32613
- decisions
32614
- );
32615
- if (typeof chosen === "string" && agentIds.has(chosen)) {
32616
- stepAgent = chosen;
32617
- if (rawTarget) plan.renames.push({ from: elide(rawTarget), to: chosen, kind: "agent" });
32618
- } else {
32619
- if (chosen !== void 0 && !plan.invalidDecisions.includes(`coordinator:${workflowKey}`)) {
32620
- plan.invalidDecisions.push(`coordinator:${workflowKey}`);
32621
- }
32622
- if (chosen !== void 0 && !plan.ambiguities.some((ambiguity) => ambiguity.key === `coordinator:${workflowKey}`)) {
32623
- plan.ambiguities.push({
32624
- key: `coordinator:${workflowKey}`,
32625
- category: "identity",
32626
- sourcePath: source.path,
32627
- message: `decision value ${JSON.stringify(typeof chosen === "string" ? elide(chosen, 200) : chosen)} for workflow ${workflowId} is not a migrated agent`,
32628
- allowedValues: [...agentIds.keys()].sort(compareCodeUnits6).slice(0, 64)
32629
- });
32630
- }
32631
- stepAgent = normalizedTarget && IDENTIFIER2.test(normalizedTarget) && normalizedTarget.length <= 64 ? normalizedTarget : "coordinator";
32632
- }
32633
- }
32634
- }
32635
- const rawStages = Array.isArray(definition.stages) ? definition.stages : [];
32636
- const stageIds = [];
32637
- const stageRename = /* @__PURE__ */ new Map();
32638
- const stepIdByIndex = /* @__PURE__ */ new Map();
32639
- const stageKeyByRaw = /* @__PURE__ */ new Map();
32640
- const droppedStageIndexes = /* @__PURE__ */ new Set();
32641
- const takenSteps = /* @__PURE__ */ new Map();
32642
- for (const [stageIndex, stageEntry] of rawStages.entries()) {
32643
- const stage = asObject2(stageEntry);
32644
- if (!stage) {
32645
- recordUnmapped(plan, source.path, `${basePointer}/stages/${stageIndex}`, stageEntry, false);
32646
- droppedStageIndexes.add(stageIndex);
32647
- continue;
32648
- }
32649
- const rawStageId = asString(stage.id);
32650
- if (!rawStageId) {
32651
- recordUnmapped(plan, source.path, `${basePointer}/stages/${stageIndex}`, stage, false);
32652
- droppedStageIndexes.add(stageIndex);
32653
- continue;
32654
- }
32655
- const stepIdentity = mapIdentity(plan, source.path, rawStageId, "step", takenSteps, decisions, `${basePointer}/stages/${stageIndex}`, stage);
32656
- if (stepIdentity.status === "dropped") {
32657
- droppedStageIndexes.add(stageIndex);
32658
- continue;
32659
- }
32660
- const stepId = stepIdentity.status === "adopted" ? stepIdentity.id : `preview-${(normalizeIdentifier(rawStageId) || "unnamed").slice(0, 46).replace(/[-_]+$/u, "")}-${createHash13("sha256").update(`${rawStageId}#${stageIndex}`, "utf8").digest("hex").slice(0, 8)}`;
32661
- const stepKey = keyFragment(rawStageId);
32662
- stageKeyByRaw.set(rawStageId, stepKey);
32663
- if (!asString(stage.instructions)) {
32664
- resolveDecision(
32665
- plan,
32666
- `instructions:${workflowKey}:${stepKey}`,
32667
- "unsupported",
32668
- source.path,
32669
- `stage ${stepId} has no instructions; KXM agent steps require executable instructions \u2014 confirm the stage is dropped`,
32670
- ["drop"],
32671
- decisions
32672
- );
32673
- recordUnmapped(plan, source.path, `${basePointer}/stages/${stageIndex}`, stage, false);
32674
- droppedStageIndexes.add(stageIndex);
32675
- continue;
32676
- }
32677
- if (!stageRename.has(rawStageId)) stageRename.set(rawStageId, stepId);
32678
- stepIdByIndex.set(stageIndex, stepId);
32679
- stageIds.push(stepId);
32680
- }
32681
- const context = { workflowId, workflowKey, stageIds, renamesByKind: stageRename };
32682
- const steps = [];
32683
- for (const [stageIndex, stageEntry] of rawStages.entries()) {
32684
- if (droppedStageIndexes.has(stageIndex)) continue;
32685
- const stage = asObject2(stageEntry);
32686
- if (!stage) continue;
32687
- const rawStageId = asString(stage.id);
32688
- if (!rawStageId) continue;
32689
- const stepId = stepIdByIndex.get(stageIndex);
32690
- if (!stepId) continue;
32691
- const stepKey = stageKeyByRaw.get(rawStageId) ?? keyFragment(rawStageId);
32692
- const pointer = `${basePointer}/stages/${stageIndex}`;
32693
- recordUnknownFields(plan, source.path, pointer, stage, KNOWN_STAGE_FIELDS);
32694
- const label = asString(stage.label);
32695
- const instructions = asString(stage.instructions);
32696
- if (!instructions) continue;
32697
- const step = {
32698
- id: stepId,
32699
- kind: "agent",
32700
- agent: stepAgent
32701
- };
32702
- if (label) step.description = label;
32703
- let maxAttempts = 3;
32704
- if (typeof stage.maxAttempts === "number" && Number.isInteger(stage.maxAttempts) && stage.maxAttempts >= 1 && stage.maxAttempts <= 20) {
32705
- maxAttempts = stage.maxAttempts;
32706
- } else if (stage.maxAttempts !== void 0) {
32707
- recordUnmapped(plan, source.path, `${pointer}/maxAttempts`, stage.maxAttempts, false);
32708
- }
32709
- step.maxAttempts = maxAttempts;
32710
- step.instructions = instructions;
32711
- const requiredEvidence = [];
32712
- const policyProducers = /* @__PURE__ */ new Set();
32713
- let policyProducerMinimum = 0;
32714
- const rawEvidence = Array.isArray(stage.requiredEvidence) ? stage.requiredEvidence : [];
32715
- const seenKeys = /* @__PURE__ */ new Set();
32716
- for (const [evidenceIndex, evidenceEntry] of rawEvidence.entries()) {
32717
- const rawKey = asString(evidenceEntry);
32718
- if (!rawKey) {
32719
- recordUnmapped(plan, source.path, `${pointer}/requiredEvidence/${evidenceIndex}`, evidenceEntry, false);
32720
- continue;
32721
- }
32722
- const key = normalizeIdentifier(rawKey);
32723
- if (!IDENTIFIER2.test(key) || key.length > 64) {
32724
- recordUnmapped(plan, source.path, `${pointer}/requiredEvidence/${evidenceIndex}`, evidenceEntry, false);
32725
- continue;
32726
- }
32727
- if (seenKeys.has(key)) {
32728
- recordUnmapped(plan, source.path, `${pointer}/requiredEvidence/${evidenceIndex}`, evidenceEntry, false);
32729
- continue;
32730
- }
32731
- if (key !== rawKey) plan.renames.push({ from: elide(rawKey), to: key, kind: "evidence" });
32732
- seenKeys.add(key);
32733
- requiredEvidence.push({ key, kind: "assignment-result" });
32734
- }
32735
- const policies = asObject2(stage.evidencePolicies);
32736
- if (policies) {
32737
- const seenPolicies = /* @__PURE__ */ new Set();
32738
- for (const [rawPolicyKey, policyEntry] of Object.entries(policies).sort(([left], [right]) => compareCodeUnits6(left, right))) {
32739
- const policy = asObject2(policyEntry);
32740
- const policyKey = normalizeIdentifier(rawPolicyKey);
32741
- if (seenPolicies.has(policyKey)) {
32742
- recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}`, policyEntry, false);
32743
- continue;
32744
- }
32745
- seenPolicies.add(policyKey);
32746
- const requirement = requiredEvidence.find((candidate) => candidate.key === policyKey);
32747
- if (!policy || !requirement) {
32748
- recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}`, policyEntry, false);
32749
- continue;
32750
- }
32751
- recordUnknownFields(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}`, policy, KNOWN_POLICY_FIELDS);
32752
- let minProducers = 1;
32753
- if (typeof policy.minProducers === "number" && Number.isInteger(policy.minProducers) && policy.minProducers >= 1 && policy.minProducers <= 16) {
32754
- minProducers = policy.minProducers;
32755
- } else if (policy.minProducers !== void 0) {
32756
- recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}/minProducers`, policy.minProducers, false);
32757
- }
32758
- if (policy.eligibleAgents !== void 0 && !Array.isArray(policy.eligibleAgents)) {
32759
- recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}/eligibleAgents`, policy.eligibleAgents, false);
32760
- }
32761
- const eligible = Array.isArray(policy.eligibleAgents) ? policy.eligibleAgents : [];
32762
- const eligibleIds = [];
32763
- for (const [eligibleIndex, candidate] of eligible.entries()) {
32764
- const rawName = asString(candidate);
32765
- if (!rawName) {
32766
- recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}/eligibleAgents/${eligibleIndex}`, candidate, false);
32767
- continue;
32768
- }
32769
- const rawBound = pendingAgentRawNames.has(rawName) ? void 0 : agentRawNames.get(rawName) ?? [...agentRawNames].find(([candidateName]) => candidateName.toLocaleLowerCase("en-US") === rawName.toLocaleLowerCase("en-US"))?.[1];
32770
- if (rawBound) {
32771
- if (!eligibleIds.includes(rawBound)) eligibleIds.push(rawBound);
32772
- continue;
32773
- }
32774
- if ([...pendingAgentRawNames].some((pending) => pending === rawName || pending.toLocaleLowerCase("en-US") === rawName.toLocaleLowerCase("en-US"))) {
32775
- const previewId = normalizeIdentifier(rawName);
32776
- if (IDENTIFIER2.test(previewId) && previewId.length <= 64 && !eligibleIds.includes(previewId)) {
32777
- eligibleIds.push(previewId);
32778
- }
32779
- continue;
32780
- }
32781
- const normalized = normalizeIdentifier(rawName);
32782
- if (agentRawNames.size === 0 && agentIds.has(normalized)) {
32783
- if (!eligibleIds.includes(normalized)) eligibleIds.push(normalized);
32784
- continue;
32785
- }
32786
- if (agentIds.size === 0) {
32787
- recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}/eligibleAgents/${eligibleIndex}`, candidate, false);
32788
- continue;
32789
- }
32790
- const mapped = resolveDecision(
32791
- plan,
32792
- `producer:${workflowKey}:${stepKey}:${keyFragment(rawName)}`,
32793
- "evidence-policy",
32794
- source.path,
32795
- `evidence policy on ${stepId}.${policyKey} names producer ${JSON.stringify(elide(rawName, 200))} which is not a migrated agent; choose the roster agent that takes this producer role`,
32796
- [...agentIds.keys()].sort(compareCodeUnits6).slice(0, 64),
32797
- decisions
32798
- );
32799
- if (typeof mapped === "string" && agentIds.has(mapped)) {
32800
- plan.renames.push({ from: elide(rawName), to: mapped, kind: "agent" });
32801
- if (!eligibleIds.includes(mapped)) eligibleIds.push(mapped);
32802
- } else if (mapped !== void 0) {
32803
- if (!plan.invalidDecisions.includes(`producer:${workflowKey}:${stepKey}:${keyFragment(rawName)}`)) {
32804
- plan.invalidDecisions.push(`producer:${workflowKey}:${stepKey}:${keyFragment(rawName)}`);
32805
- }
32806
- if (!plan.ambiguities.some((ambiguity) => ambiguity.key === `producer:${workflowKey}:${stepKey}:${keyFragment(rawName)}`)) {
32807
- plan.ambiguities.push({
32808
- key: `producer:${workflowKey}:${stepKey}:${keyFragment(rawName)}`,
32809
- category: "evidence-policy",
32810
- sourcePath: source.path,
32811
- message: `decision value ${JSON.stringify(typeof mapped === "string" ? elide(mapped, 200) : mapped)} for producer ${JSON.stringify(elide(rawName, 200))} is not a migrated agent`,
32812
- allowedValues: [...agentIds.keys()].sort(compareCodeUnits6).slice(0, 64)
32813
- });
32814
- }
32815
- }
32816
- }
32817
- const acceptedStatusesMalformed = policy.acceptedStatuses !== void 0 && !Array.isArray(policy.acceptedStatuses);
32818
- if (acceptedStatusesMalformed) {
32819
- recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}/acceptedStatuses`, policy.acceptedStatuses, false);
32820
- }
32821
- const accepted = acceptedStatusesMalformed ? [] : Array.isArray(policy.acceptedStatuses) ? policy.acceptedStatuses : ["replied"];
32822
- if (Array.isArray(policy.acceptedStatuses) && policy.acceptedStatuses.length === 0) {
32823
- recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}/acceptedStatuses`, policy.acceptedStatuses, false);
32824
- }
32825
- let acceptedStatuses = accepted.length > 0 && accepted.every((status) => status === "replied") ? ["passed"] : void 0;
32826
- if (!acceptedStatuses) {
32827
- const chosen = resolveDecision(
32828
- plan,
32829
- `statuses:${workflowKey}:${stepKey}:${policyKey}`,
32830
- "evidence-policy",
32831
- source.path,
32832
- acceptedStatusesMalformed ? `evidence policy ${policyKey} acceptedStatuses is malformed (not an array) and was preserved in the report; confirm mapping the policy to KXM 'passed'` : `evidence policy ${policyKey} accepted statuses ${JSON.stringify(elide(accepted.map((candidate) => String(candidate)).join(","), 200))} cannot map losslessly; confirm mapping to KXM 'passed'`,
32833
- ["passed"],
32834
- decisions
32835
- );
32836
- if (chosen === "passed") acceptedStatuses = ["passed"];
32837
- }
32838
- if (acceptedStatuses) {
32839
- plan.permissionChanges.push({
32840
- sourcePath: source.path,
32841
- targetPath: `.kxm/workflows/${workflowId}.yaml`,
32842
- direction: "narrowing",
32843
- summary: `evidence ${policyKey} on ${stepId}: legacy peer-reply policy (status 'replied') migrates to assignment-result producer policy requiring 'passed'`,
32844
- decisionKey: `evidence:${workflowKey}:${stepKey}:${policyKey}`
32845
- });
32846
- plan.permissionChanges.push({
32847
- sourcePath: source.path,
32848
- targetPath: `.kxm/workflows/${workflowId}.yaml`,
32849
- direction: "expansion",
32850
- summary: `step ${stepId}: assignment pool widens from the single legacy coordinator to coordinator plus evidence producers so KXM producer evidence stays in scope`,
32851
- decisionKey: `evidence:${workflowKey}:${stepKey}:${policyKey}`
32852
- });
32853
- resolveDecision(
32854
- plan,
32855
- `evidence:${workflowKey}:${stepKey}:${policyKey}`,
32856
- "evidence-policy",
32857
- source.path,
32858
- `approve strengthening ${stepId}.${policyKey} from 'peer replied' to 'peer assignment passed' and widening the step assignment pool to include the producers`,
32859
- ["approve"],
32860
- decisions
32861
- );
32862
- }
32863
- if (eligibleIds.length > 0 && acceptedStatuses) {
32864
- requirement.kind = "assignment-result";
32865
- requirement.producerPolicy = {
32866
- minimumProducers: minProducers,
32867
- eligibleAgents: eligibleIds,
32868
- acceptedStatuses
32869
- };
32870
- for (const producer of eligibleIds) policyProducers.add(producer);
32871
- policyProducerMinimum = Math.max(policyProducerMinimum, minProducers);
32872
- const degradation = asObject2(policy.degradation);
32873
- if (degradation && degradation.minProducers !== void 0) {
32874
- if (typeof degradation.minProducers === "number" && Number.isInteger(degradation.minProducers) && degradation.minProducers >= 1 && degradation.minProducers <= 15 && degradation.minProducers < minProducers) {
32875
- requirement.producerPolicy.degradation = { minimumProducers: degradation.minProducers };
32876
- } else {
32877
- recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}/degradation`, policy.degradation, false);
32878
- }
32879
- }
32880
- } else {
32881
- recordUnmapped(plan, source.path, `${pointer}/evidencePolicies/${pointerEscape2(rawPolicyKey)}`, policyEntry, false);
32882
- }
32883
- }
32884
- }
32885
- if (requiredEvidence.length > 0) step.requiredEvidence = requiredEvidence;
32886
- if (policyProducers.size > 0) {
32887
- const pool = [stepAgent, ...[...policyProducers].filter((candidate) => candidate !== stepAgent).sort(compareCodeUnits6)];
32888
- step.assignments = {
32889
- allowedAgents: pool,
32890
- minimum: 1,
32891
- target: Math.max(1, policyProducerMinimum),
32892
- maximum: pool.length,
32893
- maxParallel: pool.length
32894
- };
32895
- }
32896
- const on = asObject2(stage.on);
32897
- const transitions = {};
32898
- if (on) {
32899
- const seenOutcomes = /* @__PURE__ */ new Set();
32900
- for (const [rawOutcome, rawTransition] of Object.entries(on).sort(([left], [right]) => compareCodeUnits6(left, right))) {
32901
- const outcome = normalizeIdentifier(rawOutcome);
32902
- if (!IDENTIFIER2.test(outcome) || outcome.length > 64) {
32903
- recordUnmapped(plan, source.path, `${pointer}/on/${pointerEscape2(rawOutcome)}`, rawTransition, false);
32904
- continue;
32905
- }
32906
- if (seenOutcomes.has(outcome)) {
32907
- recordUnmapped(plan, source.path, `${pointer}/on/${pointerEscape2(rawOutcome)}`, rawTransition, false);
32908
- continue;
32909
- }
32910
- seenOutcomes.add(outcome);
32911
- if (outcome !== rawOutcome) plan.renames.push({ from: elide(rawOutcome), to: outcome, kind: "outcome" });
32912
- const converted = convertTransition(plan, source, context, stepId, stepKey, outcome, rawOutcome, rawTransition, decisions, pointer);
32913
- if (converted !== void 0) transitions[outcome] = converted;
32914
- }
32915
- if (transitions.passed === void 0) {
32916
- const nextId = stageIds[stageIds.indexOf(stepId) + 1];
32917
- transitions.passed = nextId ?? { target: "$terminal", terminalStatus: "completed" };
32918
- }
32919
- } else {
32920
- const nextId = stageIds[stageIds.indexOf(stepId) + 1];
32921
- transitions.passed = nextId ?? { target: "$terminal", terminalStatus: "completed" };
32922
- }
32923
- step.on = transitions;
32924
- if (typeof stage.maxTransitions === "number") {
32925
- recordUnmapped(plan, source.path, `${pointer}/maxTransitions`, stage.maxTransitions, false);
32926
- }
32927
- if (stage.area !== void 0) recordUnmapped(plan, source.path, `${pointer}/area`, stage.area, false);
32928
- steps.push(step);
32929
- }
32930
- if (steps.length === 0) {
32931
- resolveDecision(
32932
- plan,
32933
- `stages:${workflowKey}`,
32934
- "unsupported",
32935
- source.path,
32936
- `workflow ${workflowId} produced no convertible steps; confirm it is dropped`,
32937
- ["drop"],
32938
- decisions
32939
- );
32940
- continue;
32941
- }
32942
- const workflow = {
32943
- schema: "kxm.workflow.v1",
32944
- coordinator: stepAgent,
32945
- steps
32946
- };
32947
- if (typeof definition.maxTransitions === "number" && Number.isInteger(definition.maxTransitions) && definition.maxTransitions >= 1 && definition.maxTransitions <= 1e3) {
32948
- workflow.limits = { maxTransitions: definition.maxTransitions };
32949
- } else if (definition.maxTransitions !== void 0) {
32950
- recordUnmapped(plan, source.path, `${basePointer}/maxTransitions`, definition.maxTransitions, false);
32951
- }
32952
- if (workflow.limits === void 0) {
32953
- const hasBackEdge = steps.some((step) => Object.values(asObject2(step.on) ?? {}).some((rule) => {
32954
- const target = typeof rule === "string" ? rule : asObject2(rule)?.target;
32955
- return typeof target === "string" && target !== "$terminal" && stageIds.indexOf(target) <= stageIds.indexOf(String(step.id));
32956
- }));
32957
- if (hasBackEdge) {
32958
- const chosen = resolveDecision(
32959
- plan,
32960
- `budget:${workflowKey}`,
32961
- "transition-budget",
32962
- source.path,
32963
- `workflow ${workflowId} has back-edges but no legacy global maxTransitions; KXM requires limits.maxTransitions (1-1000)`,
32964
- [28],
32965
- decisions
32966
- );
32967
- workflow.limits = { maxTransitions: typeof chosen === "number" && Number.isInteger(chosen) && chosen >= 1 && chosen <= 1e3 ? chosen : 28 };
32968
- }
32969
- }
32970
- for (const oracleField of ["reproOracle", "planHash"]) {
32971
- const oracle = asObject2(definition[oracleField]);
32972
- if (!oracle) continue;
32973
- const oracleStage = asString(oracle.stageId);
32974
- const oracleEvidence = asString(oracle.evidenceKey);
32975
- if (!oracleStage || !oracleEvidence) {
32976
- recordUnmapped(plan, source.path, `${basePointer}/${oracleField}`, definition[oracleField], false);
32977
- continue;
32978
- }
32979
- const mappedStage = context.renamesByKind.get(oracleStage);
32980
- if (!mappedStage) {
32981
- if (!context.stageIds.includes(normalizeIdentifier(oracleStage))) {
32982
- recordUnmapped(plan, source.path, `${basePointer}/${oracleField}`, definition[oracleField], false);
32983
- continue;
32984
- }
32985
- }
32986
- workflow[oracleField] = {
32987
- stageId: mappedStage ?? normalizeIdentifier(oracleStage),
32988
- evidenceKey: normalizeIdentifier(oracleEvidence)
32989
- };
32990
- }
32991
- if (Array.isArray(definition.requirePlanHash)) {
32992
- const mapped = [];
32993
- for (const [requireIndex, candidate] of definition.requirePlanHash.entries()) {
32994
- const rawStage = asString(candidate);
32995
- if (!rawStage) {
32996
- recordUnmapped(plan, source.path, `${basePointer}/requirePlanHash/${requireIndex}`, candidate, false);
32997
- continue;
32998
- }
32999
- const mappedStage = context.renamesByKind.get(rawStage);
33000
- if (!mappedStage && !context.stageIds.includes(normalizeIdentifier(rawStage))) {
33001
- recordUnmapped(plan, source.path, `${basePointer}/requirePlanHash/${requireIndex}`, candidate, false);
33002
- continue;
33003
- }
33004
- mapped.push(mappedStage ?? normalizeIdentifier(rawStage));
33005
- }
33006
- if (mapped.length > 0) workflow.requirePlanHash = [...new Set(mapped)].sort(compareCodeUnits6);
33007
- }
33008
- workflows.set(workflowId, workflow);
33009
- }
33010
- return workflows;
33011
- }
33012
- var PROJECT_ID2 = /^prj_[A-Za-z0-9][A-Za-z0-9_-]{5,127}$/;
33013
- function sortedRecord(map, prefix) {
33014
- return new Map([...map.entries()].sort(([left], [right]) => compareCodeUnits6(left, right)).map(([key, value]) => [`${prefix}${key}.yaml`, value]));
33015
- }
33016
- function migrationGitRoot(start) {
33017
- const root = discoverGitRoot(start);
33018
- if (!root) {
33019
- throw new KxmConfigError([migrateIssue("discovery", "git_root_required", ".", "kxm migrate must run inside the authoritative Git worktree")]);
33020
- }
33021
- return root;
33022
- }
33023
- function planKxmMigration(projectRoot, options = {}) {
33024
- const root = migrationGitRoot(projectRoot);
33025
- const registry = new KxmSchemaRegistry(options.schemasDir);
33026
- const sources = readKxmLegacySources(root);
33027
- if (sources.length === 0) {
33028
- throw new KxmConfigError([migrateIssue("discovery", "legacy_sources_missing", ".kxm/config", "no legacy configuration files found to migrate")]);
33029
- }
33030
- const decisions = options.decisions ?? {};
33031
- const plan = { ambiguities: [], invalidDecisions: [], decisionKeys: /* @__PURE__ */ new Set(), decisionAllowedValues: /* @__PURE__ */ new Map(), unmapped: [], renames: [], permissionChanges: [] };
33032
- const agentNames = /* @__PURE__ */ new Map();
33033
- const workflowNames = /* @__PURE__ */ new Map();
33034
- const agentsSource = sources.find((source) => source.path === ".kxm/config/agents.json");
33035
- const gatesSource = sources.find((source) => source.path === ".kxm/config/gates.json");
33036
- const converted = agentsSource ? convertAgentsRoster(plan, agentsSource, decisions, agentNames) : { agents: /* @__PURE__ */ new Map(), models: /* @__PURE__ */ new Map(), rawNames: /* @__PURE__ */ new Map(), pendingRawNames: /* @__PURE__ */ new Set(), identityPending: false };
33037
- if (!agentsSource) {
33038
- resolveDecision(
33039
- plan,
33040
- "agents-missing",
33041
- "unsupported",
33042
- ".kxm/config/agents.json",
33043
- "no agents.json roster; workflows convert against an empty agent set \u2014 confirm conversion continues",
33044
- ["continue"],
33045
- decisions
33046
- );
33047
- }
33048
- if (gatesSource) convertGatesRoster(plan, gatesSource, decisions);
33049
- const workflows = /* @__PURE__ */ new Map();
33050
- for (const source of sources) {
33051
- if (!source.path.startsWith(".kxm/config/workflows/")) continue;
33052
- const convertedWorkflows = convertWorkflowFile(
33053
- plan,
33054
- source,
33055
- decisions,
33056
- converted.agents,
33057
- converted.rawNames,
33058
- converted.pendingRawNames,
33059
- converted.identityPending,
33060
- workflowNames
33061
- );
33062
- for (const [id, workflow] of convertedWorkflows) workflows.set(id, workflow);
33063
- }
33064
- const projectId = options.projectId ?? `prj_mig${sha256Of(legacySourceDigest(sources)).slice(7, 39)}`;
33065
- if (!PROJECT_ID2.test(projectId)) {
33066
- throw new KxmConfigError([migrateIssue("semantic", "project_id_invalid", ".kxm/project.yaml", "project ID must satisfy the kxm.project.v1 opaque ID grammar and use the prj_ prefix")]);
33067
- }
33068
- const projectName = options.projectName ?? "Migrated legacy project";
33069
- if (projectName.length < 1 || projectName.length > 120 || /[\u0000\r\n]/.test(projectName)) {
33070
- throw new KxmConfigError([migrateIssue("semantic", "project_name_invalid", ".kxm/project.yaml", "project name must contain 1-120 characters on one line")]);
33071
- }
33072
- const resources = /* @__PURE__ */ new Map();
33073
- resources.set(".kxm/project.yaml", {
33074
- schema: "kxm.project.v1",
33075
- id: projectId,
33076
- name: projectName,
33077
- ...workflows.size > 0 ? { defaultWorkflow: [...workflows.keys()].sort(compareCodeUnits6)[0] } : {},
33078
- defaultExecutor: "local",
33079
- repositories: [{ id: "control", role: "control", required: true, pathHint: "." }],
33080
- workspace: {
33081
- dirtySnapshot: {
33082
- untracked: "ask",
33083
- dirtySubmodules: "fail"
33084
- }
33085
- }
33086
- });
33087
- resources.set(".kxm/repo/repo.yaml", {
33088
- schema: "kxm.repository.v1",
33089
- projectId,
33090
- repositoryId: "control",
33091
- description: "Authoritative project configuration and repository content.",
33092
- defaultAccess: "write"
33093
- });
33094
- for (const [path4, value] of sortedRecord(converted.agents, ".kxm/agents/")) resources.set(path4, value);
33095
- for (const [path4, value] of sortedRecord(converted.models, ".kxm/models/")) resources.set(path4, value);
33096
- for (const [path4, value] of sortedRecord(workflows, ".kxm/workflows/")) resources.set(path4, value);
33097
- const schemaIssues = [];
33098
- const kindOf = (path4) => path4 === ".kxm/project.yaml" ? "project" : path4.endsWith("repo.yaml") ? "repository" : path4.startsWith(".kxm/agents/") ? "agent" : path4.startsWith(".kxm/models/") ? "model" : "workflow";
33099
- const idOf = (path4) => path4 === ".kxm/project.yaml" || path4.endsWith("repo.yaml") ? void 0 : path4.slice(path4.lastIndexOf("/") + 1, -".yaml".length);
33100
- const semanticResources = /* @__PURE__ */ new Map();
33101
- for (const [path4, value] of [...resources.entries()].sort(([left], [right]) => compareCodeUnits6(left, right))) {
33102
- const kind = kindOf(path4);
33103
- schemaIssues.push(...registry.validate(kind, value, path4));
33104
- const id = idOf(path4);
33105
- semanticResources.set(path4, kind === "repository" ? { kind, id: "control", value } : { kind, ...id === void 0 ? {} : { id }, value });
33106
- }
33107
- if (schemaIssues.length > 0) throw new KxmConfigError(schemaIssues);
33108
- if (plan.ambiguities.length === 0) {
33109
- const semanticIssues = validateKxmResources(semanticResources, options);
33110
- if (semanticIssues.length > 0) throw new KxmConfigError(semanticIssues);
33111
- }
33112
- const planRecord = {
33113
- schema: "kxm.migration-plan.v1",
33114
- projectId,
33115
- projectName,
33116
- sourceDigest: legacySourceDigest(sources),
33117
- sources: sources.map((source) => ({ path: source.path, sha256: source.sha256, bytes: source.bytes })),
33118
- resources: [...resources.keys()].sort(compareCodeUnits6).map((path4) => {
33119
- const bytes = Buffer.from((0, import_yaml12.stringify)(resources.get(path4), { lineWidth: 0 }), "utf8");
33120
- return { path: path4, sha256: sha256Of(bytes), bytes: bytes.byteLength };
33121
- }),
33122
- ambiguities: [...plan.ambiguities].sort((left, right) => compareCodeUnits6(left.key, right.key)).map((ambiguity) => ({
33123
- key: ambiguity.key,
33124
- category: ambiguity.category,
33125
- sourcePath: ambiguity.sourcePath,
33126
- message: ambiguity.message,
33127
- allowedValues: [...ambiguity.allowedValues]
33128
- })),
33129
- unmapped: [...plan.unmapped].sort((left, right) => compareCodeUnits6(left.sourcePath, right.sourcePath) || compareCodeUnits6(left.jsonPointer, right.jsonPointer)).map((entry) => ({ ...entry })),
33130
- renames: [...new Map(plan.renames.map((rename) => [JSON.stringify([rename.kind, rename.from, rename.to]), rename])).values()].sort((left, right) => compareCodeUnits6(left.kind, right.kind) || compareCodeUnits6(left.from, right.from)).map((entry) => ({ ...entry })),
33131
- permissionChanges: [...plan.permissionChanges].sort((left, right) => compareCodeUnits6(left.decisionKey, right.decisionKey)).map((entry) => ({ ...entry })),
33132
- canApply: plan.ambiguities.length === 0
33133
- };
33134
- const planIssues = registry.validateMigrationPlan(planRecord, "kxm.migration-plan.v1");
33135
- if (planIssues.length > 0) throw new KxmConfigError(planIssues);
33136
- return {
33137
- plan: planRecord,
33138
- resources,
33139
- ambiguities: plan.ambiguities,
33140
- decisionKeys: plan.decisionKeys,
33141
- decisionAllowedValues: plan.decisionAllowedValues,
33142
- invalidDecisions: plan.invalidDecisions,
33143
- decisions,
33144
- sources
33145
- };
33146
- }
33147
- function readKxmMigrationDecisions(path4, schemasDir) {
33148
- if (!existsSync23(path4)) {
33149
- throw new KxmConfigError([migrateIssue("discovery", "decisions_missing", path4, "migration decisions file does not exist")]);
33150
- }
33151
- const registry = new KxmSchemaRegistry(schemasDir);
33152
- const value = parseRestrictedYaml2(readFileSync22(path4), path4);
33153
- const issues = registry.validateMigrationDecision(value, path4);
33154
- if (issues.length > 0) throw new KxmConfigError(issues);
33155
- return value;
33156
- }
33157
- function resolutionsOf(decisions) {
33158
- const resolutions = decisions.resolutions;
33159
- if (!resolutions || typeof resolutions !== "object" || Array.isArray(resolutions)) return {};
33160
- return { ...resolutions };
33161
- }
33162
- function syncDirectory3(path4) {
33163
- let descriptor;
33164
- try {
33165
- descriptor = openSync3(path4, "r");
33166
- fsyncSync3(descriptor);
33167
- } catch (error) {
33168
- const code = error.code;
33169
- if (process.platform !== "win32" && code !== "EINVAL" && code !== "ENOTSUP" && code !== "EISDIR") throw error;
33170
- } finally {
33171
- if (descriptor !== void 0) closeSync3(descriptor);
33172
- }
33173
- }
33174
- function writeDurable2(path4, content) {
33175
- const temp = `${path4}.kxm-migration-tmp-${process.pid}-${createHash13("sha256").update(`${path4}${Date.now()}${Math.random()}`, "utf8").digest("hex").slice(0, 12)}`;
33176
- let created = false;
33177
- try {
33178
- const descriptor = openSync3(temp, "wx", 384);
33179
- created = true;
33180
- try {
33181
- writeFileSync18(descriptor, content);
33182
- fsyncSync3(descriptor);
33183
- } finally {
33184
- closeSync3(descriptor);
33185
- }
33186
- renameSync7(temp, path4);
33187
- try {
33188
- chmodSync6(path4, 384);
33189
- } catch {
33190
- }
33191
- syncDirectory3(join26(path4, ".."));
33192
- } finally {
33193
- if (created && existsSync23(temp)) rmSync9(temp, { force: true });
33194
- }
33195
- }
33196
- function applyKxmMigration(projectRoot, options = {}) {
33197
- const root = migrationGitRoot(projectRoot);
33198
- const storeOptions = {
33199
- ...options.localStateRoot === void 0 ? {} : { stateRoot: options.localStateRoot },
33200
- ...options.schemasDir === void 0 ? {} : { schemasDir: options.schemasDir }
33201
- };
33202
- const execute = (lock) => {
33203
- const receiptAbsolute = join26(root, ...KXM_MIGRATION_RECEIPT_PATH.split("/"));
33204
- if (existsSync23(receiptAbsolute)) {
33205
- const stat = lstatSync8(receiptAbsolute);
33206
- if (stat.isSymbolicLink() || !stat.isFile()) {
33207
- throw new KxmConfigError([migrateIssue("path", "migration_receipt_invalid", KXM_MIGRATION_RECEIPT_PATH, "migration receipt must be a regular file, not a link")]);
33208
- }
33209
- const existing = loadKxmProject(root, options);
33210
- return {
33211
- action: "already-migrated",
33212
- projectRoot: root,
33213
- receiptPath: KXM_MIGRATION_RECEIPT_PATH,
33214
- configRevision: existing.configRevision,
33215
- files: []
33216
- };
33217
- }
33218
- const decisionsRecord = options.decisionsFile ? readKxmMigrationDecisions(options.decisionsFile, options.schemasDir) : void 0;
33219
- const decisions = decisionsRecord ? resolutionsOf(decisionsRecord) : options.decisions ?? {};
33220
- const first = planKxmMigration(root, { ...options, decisions });
33221
- if (decisionsRecord) {
33222
- if (decisionsRecord.projectId !== first.plan.projectId) {
33223
- throw new KxmConfigError([migrateIssue("semantic", "decision_project_mismatch", options.decisionsFile ?? "<decisions>", "decisions file projectId does not match the recomputed plan")]);
33224
- }
33225
- if (decisionsRecord.projectName !== first.plan.projectName) {
33226
- throw new KxmConfigError([migrateIssue("semantic", "decision_project_mismatch", options.decisionsFile ?? "<decisions>", "decisions file projectName does not match the recomputed plan")]);
33227
- }
33228
- if (decisionsRecord.sourceDigest !== first.plan.sourceDigest) {
33229
- throw new KxmConfigError([migrateIssue("semantic", "decision_source_changed", options.decisionsFile ?? "<decisions>", "legacy sources changed after the decisions file was reviewed")]);
33230
- }
33231
- }
33232
- if (Object.keys(decisions).length > 0) {
33233
- const universe = planKxmMigration(root, { ...options, decisions: {} });
33234
- const unknown = [];
33235
- const invalid = [];
33236
- for (const [key, value] of Object.entries(decisions)) {
33237
- if (first.decisionKeys.has(key)) continue;
33238
- const universeAllowed = universe.decisionAllowedValues.get(key);
33239
- if (!universeAllowed) {
33240
- unknown.push(key);
33241
- } else if (!decisionValueAllowed(key, value, universeAllowed)) {
33242
- invalid.push(key);
33243
- }
33244
- }
33245
- if (unknown.length > 0) {
33246
- throw new KxmConfigError(unknown.sort(compareCodeUnits6).map((key) => migrateIssue("semantic", "decision_unknown", key, "decision key does not match any current ambiguity")));
33247
- }
33248
- const allInvalid = [.../* @__PURE__ */ new Set([...invalid, ...first.invalidDecisions])].sort(compareCodeUnits6);
33249
- if (allInvalid.length > 0) {
33250
- throw new KxmConfigError(allInvalid.map((key) => migrateIssue("semantic", "decision_value_invalid", key, "decision value is not one of the plan's allowed values")));
33251
- }
33252
- }
33253
- if (first.ambiguities.length > 0) {
33254
- return { action: "planned", projectRoot: root, files: [], plan: first.plan };
33255
- }
33256
- const files = [...first.resources.entries()].sort(([left], [right]) => compareCodeUnits6(left, right)).map(([path4, value]) => ({ path: path4, bytes: Buffer.from((0, import_yaml12.stringify)(value, { lineWidth: 0 }), "utf8") }));
33257
- const collisions = files.filter((file) => existsSync23(join26(root, ...file.path.split("/"))));
33258
- if (collisions.length > 0) {
33259
- throw new KxmConfigError(collisions.map((file) => migrateIssue("semantic", "migration_target_exists", file.path, "target resource already exists; refusing to overwrite")));
33260
- }
33261
- const parentIssues = [];
33262
- for (const file of files) {
33263
- const segments = file.path.split("/");
33264
- let current = root;
33265
- for (const segment of segments.slice(0, -1)) {
33266
- current = join26(current, segment);
33267
- if (!existsSync23(current)) continue;
33268
- const stat = lstatSync8(current);
33269
- if (stat.isSymbolicLink() || !stat.isDirectory()) {
33270
- parentIssues.push(migrateIssue("path", "migration_target_parent_invalid", file.path, `migration target parent ${segment} must be a regular directory, not a link`));
33271
- break;
33272
- }
33273
- }
33274
- }
33275
- if (parentIssues.length > 0) throw new KxmConfigError(parentIssues);
33276
- if (options.dryRun) {
33277
- return { action: "planned", projectRoot: root, files: files.map((file) => file.path), plan: first.plan };
33278
- }
33279
- if (!lock) throw new Error("project mutation lock is required to apply a migration");
33280
- const written = [];
33281
- try {
33282
- for (const file of files) {
33283
- const absolute = join26(root, ...file.path.split("/"));
33284
- mkdirSync19(join26(absolute, ".."), { recursive: true });
33285
- written.push(absolute);
33286
- writeDurable2(absolute, file.bytes);
33287
- }
33288
- const bundle = loadKxmProject(root, { ...options, allowUnreceiptedLegacyConfig: true });
33289
- const receipt = {
33290
- schema: "kxm.migration-receipt.v1",
33291
- migrationId: `mig_${sha256Of(`${first.plan.sourceDigest}
33292
- ${bundle.configRevision}`).slice(7, 39)}`,
33293
- projectId: String(first.plan.projectId),
33294
- sourceDigest: String(first.plan.sourceDigest),
33295
- decisionDigest: sha256Of(kxmCanonicalJson(decisionsRecord ?? { schema: "kxm.migration-decision.v1", projectId: first.plan.projectId, projectName: first.plan.projectName, sourceDigest: first.plan.sourceDigest, resolutions: decisions })),
33296
- configRevision: bundle.configRevision,
33297
- sources: first.plan.sources,
33298
- resources: files.map((file) => ({ path: file.path, sha256: sha256Of(file.bytes), bytes: file.bytes.byteLength }))
33299
- };
33300
- receipt.receiptSha256 = migrationReceiptSelfHash(receipt);
33301
- const registry = new KxmSchemaRegistry(options.schemasDir);
33302
- const receiptIssues = registry.validateMigrationReceipt(receipt, KXM_MIGRATION_RECEIPT_PATH);
33303
- if (receiptIssues.length > 0) throw new KxmConfigError(receiptIssues);
33304
- written.push(receiptAbsolute);
33305
- writeDurable2(receiptAbsolute, Buffer.from((0, import_yaml12.stringify)(receipt, { lineWidth: 0 }), "utf8"));
33306
- const verified = loadKxmProject(root, options);
33307
- return {
33308
- action: "applied",
33309
- projectRoot: root,
33310
- receiptPath: KXM_MIGRATION_RECEIPT_PATH,
33311
- configRevision: verified.configRevision,
33312
- files: files.map((file) => file.path),
33313
- plan: first.plan
33314
- };
33315
- } catch (error) {
33316
- for (const absolute of written.reverse()) {
33317
- try {
33318
- rmSync9(absolute, { force: true });
33319
- } catch {
33320
- }
33321
- }
33322
- throw error;
33323
- }
33324
- };
33325
- if (options.dryRun) return execute(void 0);
33326
- return withKxmLocalBindingLock(root, storeOptions, (lock) => execute(lock));
33327
- }
33328
- function verifyKxmMigration(projectRoot, options = {}) {
33329
- const root = migrationGitRoot(projectRoot);
33330
- const receiptAbsolute = join26(root, ...KXM_MIGRATION_RECEIPT_PATH.split("/"));
33331
- if (!existsSync23(receiptAbsolute)) {
33332
- return {
33333
- ok: false,
33334
- projectRoot: root,
33335
- receiptPath: KXM_MIGRATION_RECEIPT_PATH,
33336
- issues: [migrateIssue("discovery", "migration_receipt_missing", KXM_MIGRATION_RECEIPT_PATH, "no migration receipt exists")]
33337
- };
33338
- }
33339
- try {
33340
- const bundle = loadKxmProject(root, options);
33341
- const receipt = bundle.migrationReceipt;
33342
- if (!receipt) {
33343
- if (legacyConfigFilesAt(root).length === 0) {
33344
- const direct = readKxmMigrationReceipt(root, options);
33345
- return {
33346
- ok: false,
33347
- projectRoot: root,
33348
- receiptPath: KXM_MIGRATION_RECEIPT_PATH,
33349
- configRevision: bundle.configRevision,
33350
- issues: direct.issues.length > 0 ? direct.issues : [migrateIssue("semantic", "migration_receipt_invalid", KXM_MIGRATION_RECEIPT_PATH, "receipt did not validate")]
33351
- };
33352
- }
33353
- return {
33354
- ok: false,
33355
- projectRoot: root,
33356
- receiptPath: KXM_MIGRATION_RECEIPT_PATH,
33357
- configRevision: bundle.configRevision,
33358
- issues: [migrateIssue("semantic", "migration_receipt_invalid", KXM_MIGRATION_RECEIPT_PATH, "receipt did not validate during project load")]
33359
- };
33360
- }
33361
- const targetIssues = verifyKxmMigrationReceiptTarget(root, receipt, bundle.resources, bundle.configRevision);
33362
- return {
33363
- ok: targetIssues.length === 0,
33364
- projectRoot: root,
33365
- receiptPath: KXM_MIGRATION_RECEIPT_PATH,
33366
- configRevision: bundle.configRevision,
33367
- issues: targetIssues
33368
- };
33369
- } catch (error) {
33370
- if (error instanceof KxmConfigError) {
33371
- return { ok: false, projectRoot: root, receiptPath: KXM_MIGRATION_RECEIPT_PATH, issues: error.issues };
33372
- }
33373
- throw error;
33374
- }
33375
- }
33376
-
33377
31764
  // plugins/kxm/src/cli/project.ts
33378
31765
  var kxmDriveCliSeams = {};
33379
31766
  function initPlanPayload(plan) {
@@ -33471,7 +31858,7 @@ async function cmdKxmInit(runtime, options, postHooks) {
33471
31858
  if (runtime.dryRun) {
33472
31859
  return finishInit(0, `init plan: ${initialized.plan.mode}`);
33473
31860
  }
33474
- const next = initialized.plan.mode === "migrate" ? "legacy state requires reviewed migration; conversion is not available in this implementation slice" : initialized.repairPlan?.issues.length ? "managed-template repair is blocked by conflicts or authority changes; local files were preserved" : "partial or provenance-free KXM state requires explicit repair; no files were overwritten";
31861
+ const next = initialized.plan.mode === "legacy" ? "legacy state is not migrated by this build: initialise a fresh project directory and copy the YAML definitions you want to keep" : initialized.repairPlan?.issues.length ? "managed-template repair is blocked by conflicts or authority changes; local files were preserved" : "partial or provenance-free KXM state requires explicit repair; no files were overwritten";
33475
31862
  return finishInit(1, next);
33476
31863
  } catch (error) {
33477
31864
  if (error instanceof KxmConfigError) {
@@ -33491,141 +31878,11 @@ async function cmdKxmInit(runtime, options, postHooks) {
33491
31878
  return 1;
33492
31879
  }
33493
31880
  }
33494
- async function cmdKxmMigratePlan(runtime) {
33495
- if (runtime.workspaceFlag !== void 0) {
33496
- print(runtime.io, runtime.json, {
33497
- ok: false,
33498
- command: "migrate plan",
33499
- error: "workspace_option_unsupported"
33500
- }, "kxm migrate discovers the authoritative Git root from the current directory; --workspace is not supported");
33501
- return 2;
33502
- }
33503
- try {
33504
- const result = planKxmMigration(runtime.cwd, {});
33505
- const ambiguities = result.plan.ambiguities ?? [];
33506
- const unmapped = result.plan.unmapped ?? [];
33507
- const payload = {
33508
- ok: result.plan.canApply === true,
33509
- command: "migrate plan",
33510
- plan: result.plan,
33511
- plannedOnly: result.plan.canApply !== true
33512
- };
33513
- if (result.plan.canApply === true) {
33514
- print(runtime.io, runtime.json, payload, `migration plan: ${ambiguities.length} ambiguities, ${unmapped.length} preserved fields; ready to apply`);
33515
- return 0;
33516
- }
33517
- print(
33518
- runtime.io,
33519
- runtime.json,
33520
- payload,
33521
- `migration plan requires ${ambiguities.length} reviewed decision(s):
33522
- ${ambiguities.map((candidate) => ` - ${candidate.key}: ${candidate.message}`).join("\n")}`
33523
- );
33524
- return 1;
33525
- } catch (error) {
33526
- if (error instanceof KxmConfigError) {
33527
- print(runtime.io, runtime.json, { ok: false, command: "migrate plan", error: "migration_plan_failed", issues: error.issues }, `migration plan failed: ${error.message}`);
33528
- return 1;
33529
- }
33530
- print(runtime.io, runtime.json, { ok: false, command: "migrate plan", error: "migration_plan_io_failed" }, "migration plan failed because a local filesystem operation did not complete");
33531
- return 1;
33532
- }
33533
- }
33534
- async function cmdKxmMigrateApply(runtime, options) {
33535
- if (runtime.workspaceFlag !== void 0) {
33536
- print(runtime.io, runtime.json, {
33537
- ok: false,
33538
- command: "migrate apply",
33539
- error: "workspace_option_unsupported"
33540
- }, "kxm migrate discovers the authoritative Git root from the current directory; --workspace is not supported");
33541
- return 2;
33542
- }
33543
- try {
33544
- const result = applyKxmMigration(runtime.cwd, {
33545
- ...options.decisions?.trim() ? { decisionsFile: options.decisions.trim() } : {},
33546
- ...options.projectId?.trim() ? { projectId: options.projectId.trim() } : {},
33547
- ...options.name?.trim() ? { projectName: options.name.trim() } : {},
33548
- localStateRoot: kxmUserStateRoot({ env: runtime.env }),
33549
- dryRun: runtime.dryRun
33550
- });
33551
- const payload = {
33552
- ok: result.action !== "planned" || runtime.dryRun === true && result.plan?.canApply === true,
33553
- command: "migrate apply",
33554
- action: result.action,
33555
- files: result.files,
33556
- ...result.configRevision ? { configRevision: result.configRevision } : {},
33557
- ...result.receiptPath ? { receiptPath: result.receiptPath } : {},
33558
- plannedOnly: result.action === "planned"
33559
- };
33560
- if (result.action === "applied") {
33561
- print(runtime.io, runtime.json, payload, `migration applied: ${result.files.length} resources installed, receipt at ${result.receiptPath ?? ""}`);
33562
- return 0;
33563
- }
33564
- if (result.action === "already-migrated") {
33565
- print(runtime.io, runtime.json, payload, "migration receipt already exists; nothing to apply");
33566
- return 0;
33567
- }
33568
- if (runtime.dryRun && result.plan?.canApply === true) {
33569
- print(runtime.io, runtime.json, payload, `migration dry run: ${result.files.length} resources would be installed`);
33570
- return 0;
33571
- }
33572
- const ambiguities = result.plan?.ambiguities ?? [];
33573
- print(
33574
- runtime.io,
33575
- runtime.json,
33576
- { ...payload, plan: result.plan },
33577
- `migration blocked by ${ambiguities.length} unresolved decision(s); review 'kxm migrate plan' and pass --decisions:
33578
- ${ambiguities.map((candidate) => ` - ${candidate.key}: ${candidate.message}`).join("\n")}`
33579
- );
33580
- return 1;
33581
- } catch (error) {
33582
- if (error instanceof KxmConfigError) {
33583
- print(runtime.io, runtime.json, { ok: false, command: "migrate apply", error: "migration_apply_failed", issues: error.issues }, `migration apply failed: ${error.message}`);
33584
- return 1;
33585
- }
33586
- print(runtime.io, runtime.json, { ok: false, command: "migrate apply", error: "migration_apply_io_failed" }, "migration apply failed because a local filesystem operation did not complete");
33587
- return 1;
33588
- }
33589
- }
33590
- async function cmdKxmMigrateVerify(runtime) {
33591
- if (runtime.workspaceFlag !== void 0) {
33592
- print(runtime.io, runtime.json, {
33593
- ok: false,
33594
- command: "migrate verify",
33595
- error: "workspace_option_unsupported"
33596
- }, "kxm migrate discovers the authoritative Git root from the current directory; --workspace is not supported");
33597
- return 2;
33598
- }
33599
- let result;
33600
- try {
33601
- result = verifyKxmMigration(runtime.cwd, {});
33602
- } catch (error) {
33603
- if (error instanceof KxmConfigError) {
33604
- print(runtime.io, runtime.json, { ok: false, command: "migrate verify", error: "migration_verify_failed", issues: error.issues }, `migration verification failed: ${error.message}`);
33605
- return 1;
33606
- }
33607
- print(runtime.io, runtime.json, { ok: false, command: "migrate verify", error: "migration_verify_io_failed" }, "migration verification failed because a local filesystem operation did not complete");
33608
- return 1;
33609
- }
33610
- const payload = {
33611
- ok: result.ok,
33612
- command: "migrate verify",
33613
- ...result.configRevision ? { configRevision: result.configRevision } : {},
33614
- issues: result.issues
33615
- };
33616
- if (result.ok) {
33617
- print(runtime.io, runtime.json, payload, `migration receipt verified: legacy sources unchanged, target bundle matches ${result.configRevision ?? ""}`);
33618
- return 0;
33619
- }
33620
- print(runtime.io, runtime.json, payload, `migration verification failed:
33621
- ${result.issues.map((issue3) => ` - ${issue3.file}: ${issue3.code}: ${issue3.message}`).join("\n")}`);
33622
- return 1;
33623
- }
33624
31881
  async function cmdBackup(runtime, options) {
33625
31882
  try {
33626
31883
  const { manifest, outDir } = createBackup({
33627
31884
  projectRoot: runtime.cwd,
33628
- ...options.out ? { outDir: resolve20(runtime.cwd, options.out) } : {}
31885
+ ...options.out ? { outDir: resolve19(runtime.cwd, options.out) } : {}
33629
31886
  });
33630
31887
  const payload = {
33631
31888
  ok: true,
@@ -33637,7 +31894,7 @@ async function cmdBackup(runtime, options) {
33637
31894
  const summary = [
33638
31895
  `Created SQLite backup with ${manifest.stores.length} store(s):`,
33639
31896
  ...manifest.stores.map((s) => ` - ${s.storeId}: ${s.sourcePath} -> ${s.backupFile} (schema v${s.schemaVersion}, ${s.bytes} bytes, sha256 ${s.sha256.slice(0, 12)}...)`),
33640
- `Manifest: ${join27(outDir, "manifest.json")}`
31897
+ `Manifest: ${join26(outDir, "manifest.json")}`
33641
31898
  ].join("\n");
33642
31899
  print(runtime.io, runtime.json, payload, summary);
33643
31900
  return 0;
@@ -33652,7 +31909,7 @@ async function cmdBackup(runtime, options) {
33652
31909
  }
33653
31910
  async function cmdRestore(runtime, manifestArg) {
33654
31911
  try {
33655
- const result = restoreBackup(resolve20(runtime.cwd, manifestArg), {
31912
+ const result = restoreBackup(resolve19(runtime.cwd, manifestArg), {
33656
31913
  projectRoot: runtime.cwd
33657
31914
  });
33658
31915
  const payload = {
@@ -33857,7 +32114,7 @@ function parseDriveWaitTimeoutMs(raw) {
33857
32114
  return value;
33858
32115
  }
33859
32116
  function sleepMs(ms) {
33860
- return new Promise((resolve31) => setTimeout(resolve31, ms));
32117
+ return new Promise((resolve30) => setTimeout(resolve30, ms));
33861
32118
  }
33862
32119
  function driveWaitCompleted(drive) {
33863
32120
  return drive?.verified === true && drive.receipt?.settlement?.kind === "terminal" && drive.receipt.settlement.status === "completed";
@@ -34002,7 +32259,7 @@ async function selectInventoryModel(runtime, requested) {
34002
32259
  if (runtime.json || !process.stdin.isTTY || models.length === 0) return void 0;
34003
32260
  runtime.io.stdout(models.slice(0, 100).map((model, index2) => `${index2 + 1}. ${model}`).join("\n") + "\nSelect model number: ");
34004
32261
  const rl = createInterface2({ input: process.stdin, output: process.stderr });
34005
- const answer = await new Promise((resolve31) => rl.once("line", resolve31));
32262
+ const answer = await new Promise((resolve30) => rl.once("line", resolve30));
34006
32263
  rl.close();
34007
32264
  const index = Number.parseInt(answer.trim(), 10) - 1;
34008
32265
  return Number.isInteger(index) && index >= 0 && index < models.length ? models[index] : void 0;
@@ -34028,7 +32285,7 @@ async function cmdModelsScreen(runtime) {
34028
32285
  return 2;
34029
32286
  }
34030
32287
  const rl = createInterface2({ input: process.stdin, output: process.stderr });
34031
- const ask = (q2) => new Promise((resolve31) => rl.question(q2, resolve31));
32288
+ const ask = (q2) => new Promise((resolve30) => rl.question(q2, resolve30));
34032
32289
  try {
34033
32290
  while (true) {
34034
32291
  const policy = loadRoutePolicy(runtime.dirs.workdir);
@@ -34336,12 +32593,12 @@ async function cmdStudioLayout(runtime, workflowPath) {
34336
32593
  const projectRoot = discoverKxmProjectRoot(runtime.cwd) ?? runtime.cwd;
34337
32594
  let filePath = workflowPath;
34338
32595
  if (!filePath) {
34339
- filePath = resolve21(projectRoot, ".kxm", "workflows", "default.yaml");
32596
+ filePath = resolve20(projectRoot, ".kxm", "workflows", "default.yaml");
34340
32597
  }
34341
32598
  let yamlContent;
34342
32599
  let workflowId = "default";
34343
- if (existsSync24(filePath)) {
34344
- yamlContent = readFileSync23(filePath, "utf8");
32600
+ if (existsSync23(filePath)) {
32601
+ yamlContent = readFileSync22(filePath, "utf8");
34345
32602
  } else {
34346
32603
  yamlContent = `schema: kxm.workflow.v1
34347
32604
  description: Feature implementation workflow
@@ -34381,7 +32638,7 @@ steps:
34381
32638
  maxTransitions: 2
34382
32639
  `;
34383
32640
  }
34384
- const parsedYaml = (0, import_yaml13.parse)(yamlContent);
32641
+ const parsedYaml = (0, import_yaml12.parse)(yamlContent);
34385
32642
  const plan = compileKxmWorkflow({ id: workflowId, value: parsedYaml });
34386
32643
  const layout = generateStudioLayout(plan);
34387
32644
  print(runtime.io, runtime.json, { ok: true, command: "studio layout", layout }, JSON.stringify(layout, null, 2));
@@ -34414,16 +32671,16 @@ async function cmdStudioServe(runtime, options) {
34414
32671
  sessionToken,
34415
32672
  planProvider: () => {
34416
32673
  try {
34417
- const wfDir = join28(runtime.cwd, ".kxm", "workflows");
34418
- const defaultPath = join28(wfDir, "default.yaml");
34419
- let targetPath = existsSync24(defaultPath) ? defaultPath : void 0;
34420
- if (!targetPath && existsSync24(wfDir)) {
32674
+ const wfDir = join27(runtime.cwd, ".kxm", "workflows");
32675
+ const defaultPath = join27(wfDir, "default.yaml");
32676
+ let targetPath = existsSync23(defaultPath) ? defaultPath : void 0;
32677
+ if (!targetPath && existsSync23(wfDir)) {
34421
32678
  const yml = readdirSync8(wfDir).find((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
34422
- if (yml) targetPath = join28(wfDir, yml);
32679
+ if (yml) targetPath = join27(wfDir, yml);
34423
32680
  }
34424
- if (targetPath && existsSync24(targetPath)) {
34425
- const raw = readFileSync23(targetPath, "utf8");
34426
- const parsed = (0, import_yaml13.parse)(raw);
32681
+ if (targetPath && existsSync23(targetPath)) {
32682
+ const raw = readFileSync22(targetPath, "utf8");
32683
+ const parsed = (0, import_yaml12.parse)(raw);
34427
32684
  const wfId = basename6(targetPath).replace(/\.(yaml|yml)$/, "");
34428
32685
  return compileKxmWorkflow({ id: wfId, value: parsed });
34429
32686
  }
@@ -34472,14 +32729,14 @@ Press Ctrl+C to stop.
34472
32729
  }
34473
32730
 
34474
32731
  // plugins/kxm/src/cli/context-skills.ts
34475
- import { readFileSync as readFileSync26 } from "node:fs";
34476
- import { join as join32, relative as relative5 } from "node:path";
32732
+ import { readFileSync as readFileSync25 } from "node:fs";
32733
+ import { join as join31, relative as relative5 } from "node:path";
34477
32734
 
34478
32735
  // plugins/kxm/src/memory.ts
34479
- var import_yaml14 = __toESM(require_dist(), 1);
32736
+ var import_yaml13 = __toESM(require_dist(), 1);
34480
32737
  import { randomUUID as randomUUID12 } from "node:crypto";
34481
- import { existsSync as existsSync25, mkdirSync as mkdirSync20, readdirSync as readdirSync9, readFileSync as readFileSync24, writeFileSync as writeFileSync19 } from "node:fs";
34482
- import { extname as extname2, join as join29, resolve as resolve22 } from "node:path";
32738
+ import { existsSync as existsSync24, mkdirSync as mkdirSync19, readdirSync as readdirSync9, readFileSync as readFileSync23, writeFileSync as writeFileSync18 } from "node:fs";
32739
+ import { extname as extname2, join as join28, resolve as resolve21 } from "node:path";
34483
32740
  var MEMORY_SCHEMA = "kxm.memory.v1";
34484
32741
  var MEMORY_BRIEF_SCHEMA = "kxm.memory-brief.v1";
34485
32742
  var MEMORY_MARKER_START = "<!-- kxm:memory:start -->";
@@ -34512,7 +32769,7 @@ function parseFrontmatter(content) {
34512
32769
  }
34513
32770
  function parseMemoryRecord(raw, filename = "memory.md") {
34514
32771
  const { frontmatter, body } = parseFrontmatter(raw);
34515
- const data = (0, import_yaml14.parse)(frontmatter);
32772
+ const data = (0, import_yaml13.parse)(frontmatter);
34516
32773
  if (!data || typeof data !== "object" || Array.isArray(data)) {
34517
32774
  throw new Error(`invalid YAML frontmatter in ${filename}`);
34518
32775
  }
@@ -34592,7 +32849,7 @@ function formatMemoryRecord(record) {
34592
32849
  lifecycle: record.lifecycle,
34593
32850
  evidenceRefs: record.evidenceRefs
34594
32851
  };
34595
- const yamlText = (0, import_yaml14.stringify)(frontmatter).trim();
32852
+ const yamlText = (0, import_yaml13.stringify)(frontmatter).trim();
34596
32853
  const bodyText = record.body?.trim() ?? "";
34597
32854
  return bodyText ? `---
34598
32855
  ${yamlText}
@@ -34605,21 +32862,21 @@ ${yamlText}
34605
32862
  `;
34606
32863
  }
34607
32864
  function memoryDirectories(repoRoot) {
34608
- const root = resolve22(repoRoot);
34609
- const memoryDir = join29(root, ".kxm", "memory");
34610
- const candidatesDir = join29(memoryDir, "candidates");
32865
+ const root = resolve21(repoRoot);
32866
+ const memoryDir = join28(root, ".kxm", "memory");
32867
+ const candidatesDir = join28(memoryDir, "candidates");
34611
32868
  return { memoryDir, candidatesDir };
34612
32869
  }
34613
32870
  function loadAuthoredMemory(repoRoot) {
34614
32871
  const { memoryDir } = memoryDirectories(repoRoot);
34615
- if (!existsSync25(memoryDir)) return [];
32872
+ if (!existsSync24(memoryDir)) return [];
34616
32873
  const records = [];
34617
32874
  const entries = readdirSync9(memoryDir, { withFileTypes: true });
34618
32875
  for (const entry of entries) {
34619
32876
  if (entry.isFile() && extname2(entry.name) === ".md") {
34620
- const fullPath = join29(memoryDir, entry.name);
32877
+ const fullPath = join28(memoryDir, entry.name);
34621
32878
  try {
34622
- const text = readFileSync24(fullPath, "utf8");
32879
+ const text = readFileSync23(fullPath, "utf8");
34623
32880
  const record = parseMemoryRecord(text, entry.name);
34624
32881
  if (record.lifecycle === "active") {
34625
32882
  records.push(record);
@@ -34633,7 +32890,7 @@ function loadAuthoredMemory(repoRoot) {
34633
32890
  }
34634
32891
  function createMemoryNote(repoRoot, summary, options = {}) {
34635
32892
  const { candidatesDir } = memoryDirectories(repoRoot);
34636
- mkdirSync20(candidatesDir, { recursive: true });
32893
+ mkdirSync19(candidatesDir, { recursive: true });
34637
32894
  const scope = options.scope ?? "project";
34638
32895
  if (!VALID_SCOPES.has(scope)) {
34639
32896
  throw new Error(`invalid memory note scope: ${scope}; must be one of agent, project, run, operator`);
@@ -34658,8 +32915,8 @@ function createMemoryNote(repoRoot, summary, options = {}) {
34658
32915
  evidenceRefs: [],
34659
32916
  ...options.body ? { body: options.body.trim() } : {}
34660
32917
  };
34661
- const targetPath = join29(candidatesDir, `${id}.md`);
34662
- writeFileSync19(targetPath, formatMemoryRecord(record), "utf8");
32918
+ const targetPath = join28(candidatesDir, `${id}.md`);
32919
+ writeFileSync18(targetPath, formatMemoryRecord(record), "utf8");
34663
32920
  return { record, path: targetPath };
34664
32921
  }
34665
32922
  function generateMemoryBrief(repoRoot) {
@@ -34695,8 +32952,8 @@ function formatHarnessMemoryBlock(records) {
34695
32952
  }
34696
32953
  function updateHarnessDocument(filePath, block, defaultHeader) {
34697
32954
  let original = "";
34698
- if (existsSync25(filePath)) {
34699
- original = readFileSync24(filePath, "utf8");
32955
+ if (existsSync24(filePath)) {
32956
+ original = readFileSync23(filePath, "utf8");
34700
32957
  }
34701
32958
  let updated = "";
34702
32959
  if (original.includes(MEMORY_MARKER_START) && original.includes(MEMORY_MARKER_END)) {
@@ -34724,25 +32981,25 @@ ${block}
34724
32981
  `;
34725
32982
  }
34726
32983
  if (updated !== original) {
34727
- writeFileSync19(filePath, updated, "utf8");
32984
+ writeFileSync18(filePath, updated, "utf8");
34728
32985
  return true;
34729
32986
  }
34730
32987
  return false;
34731
32988
  }
34732
32989
  function syncHarnessMemory(repoRoot) {
34733
- const root = resolve22(repoRoot);
32990
+ const root = resolve21(repoRoot);
34734
32991
  const records = loadAuthoredMemory(root);
34735
32992
  const block = formatHarnessMemoryBlock(records);
34736
32993
  const updated = [];
34737
32994
  const created = [];
34738
- const agentsPath = join29(root, "AGENTS.md");
32995
+ const agentsPath = join28(root, "AGENTS.md");
34739
32996
  const agentsHeader = "# AGENTS\n\nFollow project instructions.\n";
34740
- const agentsExisted = existsSync25(agentsPath);
32997
+ const agentsExisted = existsSync24(agentsPath);
34741
32998
  if (updateHarnessDocument(agentsPath, block, agentsHeader)) {
34742
32999
  if (agentsExisted) updated.push("AGENTS.md");
34743
33000
  else created.push("AGENTS.md");
34744
33001
  }
34745
- const claudePath = join29(root, "CLAUDE.md");
33002
+ const claudePath = join28(root, "CLAUDE.md");
34746
33003
  const claudeHeader = `# KXM (Claude)
34747
33004
 
34748
33005
  Follow [\`AGENTS.md\`](AGENTS.md). Official phase tracking:
@@ -34754,12 +33011,12 @@ rotation, not a sole writer. If \`grok\` is logged out, never bill Grok through
34754
33011
  use a relief route Tracking **admits**, or stop and name the limits hit. Your reviews are
34755
33012
  artifacts, not hub \`peer-reply\` evidence.
34756
33013
  `;
34757
- const claudeExisted = existsSync25(claudePath);
33014
+ const claudeExisted = existsSync24(claudePath);
34758
33015
  if (updateHarnessDocument(claudePath, block, claudeHeader)) {
34759
33016
  if (claudeExisted) updated.push("CLAUDE.md");
34760
33017
  else created.push("CLAUDE.md");
34761
33018
  }
34762
- const geminiPath = join29(root, "GEMINI.md");
33019
+ const geminiPath = join28(root, "GEMINI.md");
34763
33020
  const geminiHeader = `# KXM (Gemini / Antigravity)
34764
33021
 
34765
33022
  Follow [\`AGENTS.md\`](AGENTS.md). Official phase tracking:
@@ -34770,7 +33027,7 @@ Google goes through the \`antigravity\` **Pi provider** (Tracking \u2192 Decided
34770
33027
  Current admissions come from Tracking and \`kxm harness list\`. Starting rotation
34771
33028
  remains Grok.
34772
33029
  `;
34773
- const geminiExisted = existsSync25(geminiPath);
33030
+ const geminiExisted = existsSync24(geminiPath);
34774
33031
  if (updateHarnessDocument(geminiPath, block, geminiHeader)) {
34775
33032
  if (geminiExisted) updated.push("GEMINI.md");
34776
33033
  else created.push("GEMINI.md");
@@ -34779,10 +33036,10 @@ remains Grok.
34779
33036
  }
34780
33037
 
34781
33038
  // plugins/kxm/src/skills.ts
34782
- var import_yaml15 = __toESM(require_dist(), 1);
34783
- import { createHash as createHash14 } from "node:crypto";
34784
- import { existsSync as existsSync26, mkdirSync as mkdirSync21, readdirSync as readdirSync10, readFileSync as readFileSync25, renameSync as renameSync8, rmSync as rmSync10, statSync as statSync3, writeFileSync as writeFileSync20 } from "node:fs";
34785
- import { join as join30 } from "node:path";
33039
+ var import_yaml14 = __toESM(require_dist(), 1);
33040
+ import { createHash as createHash13 } from "node:crypto";
33041
+ import { existsSync as existsSync25, mkdirSync as mkdirSync20, readdirSync as readdirSync10, readFileSync as readFileSync24, renameSync as renameSync7, rmSync as rmSync9, statSync as statSync3, writeFileSync as writeFileSync19 } from "node:fs";
33042
+ import { join as join29 } from "node:path";
34786
33043
  var SKILL_CANDIDATE_SCHEMA = "kxm.skill-candidate.v1";
34787
33044
  var SKILL_EVALUATION_SCHEMA = "kxm.skill-evaluation.v1";
34788
33045
  var SKILL_DECISION_SCHEMA = "kxm.skill-decision.v1";
@@ -34805,7 +33062,7 @@ var SkillLifecycleError = class extends Error {
34805
33062
  }
34806
33063
  };
34807
33064
  function skillContentSha256(content) {
34808
- return createHash14("sha256").update(content, "utf8").digest("hex");
33065
+ return createHash13("sha256").update(content, "utf8").digest("hex");
34809
33066
  }
34810
33067
  function skillIdFor(name, contentSha256) {
34811
33068
  const slug = name.toLowerCase().replaceAll(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
@@ -34823,7 +33080,7 @@ function parseSkillFrontmatter(content) {
34823
33080
  return { frontmatter: null, body: content };
34824
33081
  }
34825
33082
  try {
34826
- const parsed = (0, import_yaml15.parse)(rawFm);
33083
+ const parsed = (0, import_yaml14.parse)(rawFm);
34827
33084
  if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
34828
33085
  return { frontmatter: parsed, body: rawBody };
34829
33086
  }
@@ -34876,48 +33133,48 @@ var SkillLifecycle = class {
34876
33133
  this.allowOptimizationEvals = options.allowOptimizationEvals === true;
34877
33134
  }
34878
33135
  dir(state) {
34879
- return join30(this.root, state === "candidate" ? "candidates" : `${state}s`.replace("rejecteds", "rejected").replace("promoteds", "promoted"));
33136
+ return join29(this.root, state === "candidate" ? "candidates" : `${state}s`.replace("rejecteds", "rejected").replace("promoteds", "promoted"));
34880
33137
  }
34881
33138
  historyFile(id) {
34882
- return join30(this.root, "history", `${id}.jsonl`);
33139
+ return join29(this.root, "history", `${id}.jsonl`);
34883
33140
  }
34884
33141
  paths(state, id) {
34885
- const dir = join30(this.dir(state), id);
34886
- return { dir, metadata: join30(dir, "metadata.json"), skill: join30(dir, "SKILL.md") };
33142
+ const dir = join29(this.dir(state), id);
33143
+ return { dir, metadata: join29(dir, "metadata.json"), skill: join29(dir, "SKILL.md") };
34887
33144
  }
34888
33145
  appendHistory(id, record) {
34889
- mkdirSync21(join30(this.root, "history"), { recursive: true });
33146
+ mkdirSync20(join29(this.root, "history"), { recursive: true });
34890
33147
  const line = `${JSON.stringify(record)}
34891
33148
  `;
34892
- if (existsSync26(this.historyFile(id))) {
34893
- const existing = readFileSync25(this.historyFile(id), "utf8");
33149
+ if (existsSync25(this.historyFile(id))) {
33150
+ const existing = readFileSync24(this.historyFile(id), "utf8");
34894
33151
  const lines = existing.split("\n").filter((entry) => entry.trim());
34895
- writeFileSync20(this.historyFile(id), [...lines.slice(-499), line.trim()].join("\n") + "\n");
33152
+ writeFileSync19(this.historyFile(id), [...lines.slice(-499), line.trim()].join("\n") + "\n");
34896
33153
  } else {
34897
- writeFileSync20(this.historyFile(id), line);
33154
+ writeFileSync19(this.historyFile(id), line);
34898
33155
  }
34899
33156
  }
34900
33157
  history(id) {
34901
33158
  const file = this.historyFile(id);
34902
- if (!existsSync26(file)) return [];
34903
- return readFileSync25(file, "utf8").split("\n").filter((line) => line.trim()).map((line) => JSON.parse(line));
33159
+ if (!existsSync25(file)) return [];
33160
+ return readFileSync24(file, "utf8").split("\n").filter((line) => line.trim()).map((line) => JSON.parse(line));
34904
33161
  }
34905
33162
  readMetadata(state, id) {
34906
33163
  const { metadata } = this.paths(state, id);
34907
- if (!existsSync26(metadata)) {
33164
+ if (!existsSync25(metadata)) {
34908
33165
  throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${state}`);
34909
33166
  }
34910
- return JSON.parse(readFileSync25(metadata, "utf8"));
33167
+ return JSON.parse(readFileSync24(metadata, "utf8"));
34911
33168
  }
34912
33169
  move(from, to, id) {
34913
- const fromDir = join30(this.dir(from), id);
34914
- const toDir = join30(this.dir(to), id);
34915
- if (!existsSync26(fromDir)) {
33170
+ const fromDir = join29(this.dir(from), id);
33171
+ const toDir = join29(this.dir(to), id);
33172
+ if (!existsSync25(fromDir)) {
34916
33173
  throw new SkillLifecycleError("skill_not_found", `skill ${id} not found in ${from}`);
34917
33174
  }
34918
- mkdirSync21(this.dir(to), { recursive: true });
34919
- if (existsSync26(toDir)) rmSync10(toDir, { recursive: true, force: true });
34920
- renameSync8(fromDir, toDir);
33175
+ mkdirSync20(this.dir(to), { recursive: true });
33176
+ if (existsSync25(toDir)) rmSync9(toDir, { recursive: true, force: true });
33177
+ renameSync7(fromDir, toDir);
34921
33178
  }
34922
33179
  /** Submit a new skill candidate. Content is redacted of secret material at
34923
33180
  * creation; the ID is derived from name + content hash. */
@@ -34959,7 +33216,7 @@ var SkillLifecycle = class {
34959
33216
  const contentSha256 = skillContentSha256(content);
34960
33217
  const id = skillIdFor(name, contentSha256);
34961
33218
  const { dir, metadata, skill } = this.paths("candidate", id);
34962
- if (existsSync26(metadata)) {
33219
+ if (existsSync25(metadata)) {
34963
33220
  throw new SkillLifecycleError(
34964
33221
  "skill_candidate_exists",
34965
33222
  `identical candidate ${id} already exists; changed behavior requires changed content`
@@ -34978,9 +33235,9 @@ var SkillLifecycle = class {
34978
33235
  createdAt: this.now(),
34979
33236
  ...input.supersedes ? { supersedes: input.supersedes } : {}
34980
33237
  };
34981
- mkdirSync21(dir, { recursive: true });
34982
- writeFileSync20(skill, content);
34983
- writeFileSync20(metadata, `${JSON.stringify(record, null, 2)}
33238
+ mkdirSync20(dir, { recursive: true });
33239
+ writeFileSync19(skill, content);
33240
+ writeFileSync19(metadata, `${JSON.stringify(record, null, 2)}
34984
33241
  `);
34985
33242
  this.appendHistory(id, { schema: "kxm.skill-history-event.v1", event: "candidate_created", by: createdBy, supersedes: input.supersedes, at: record.createdAt });
34986
33243
  return record;
@@ -35069,18 +33326,18 @@ var SkillLifecycle = class {
35069
33326
  };
35070
33327
  const candidatePaths = this.paths("candidate", candidateId);
35071
33328
  const promotedPaths = this.paths("promoted", candidateId);
35072
- mkdirSync21(promotedPaths.dir, { recursive: true });
35073
- const skillContent = readFileSync25(candidatePaths.skill, "utf8");
35074
- const metadataContent = readFileSync25(candidatePaths.metadata, "utf8");
35075
- writeFileSync20(promotedPaths.skill, skillContent);
35076
- writeFileSync20(promotedPaths.metadata, metadataContent);
35077
- const patchesDir = join30(this.root, "patches");
35078
- mkdirSync21(patchesDir, { recursive: true });
35079
- const patchPath = join30(patchesDir, `${candidateId}.patch`);
33329
+ mkdirSync20(promotedPaths.dir, { recursive: true });
33330
+ const skillContent = readFileSync24(candidatePaths.skill, "utf8");
33331
+ const metadataContent = readFileSync24(candidatePaths.metadata, "utf8");
33332
+ writeFileSync19(promotedPaths.skill, skillContent);
33333
+ writeFileSync19(promotedPaths.metadata, metadataContent);
33334
+ const patchesDir = join29(this.root, "patches");
33335
+ mkdirSync20(patchesDir, { recursive: true });
33336
+ const patchPath = join29(patchesDir, `${candidateId}.patch`);
35080
33337
  const relSkillPath = `.kxm/skills/promoted/${candidateId}/SKILL.md`;
35081
33338
  const relMetaPath = `.kxm/skills/promoted/${candidateId}/metadata.json`;
35082
33339
  const patch = `${createUnifiedPatch(relSkillPath, skillContent)}${createUnifiedPatch(relMetaPath, metadataContent)}`;
35083
- writeFileSync20(patchPath, patch, "utf8");
33340
+ writeFileSync19(patchPath, patch, "utf8");
35084
33341
  this.appendHistory(candidateId, record);
35085
33342
  return { ...metadata, patch, patchPath };
35086
33343
  }
@@ -35104,7 +33361,7 @@ var SkillLifecycle = class {
35104
33361
  verify(state, id) {
35105
33362
  const metadata = this.readMetadata(state, id);
35106
33363
  const { skill } = this.paths(state, id);
35107
- const content = readFileSync25(skill, "utf8");
33364
+ const content = readFileSync24(skill, "utf8");
35108
33365
  if (skillContentSha256(content) !== metadata.contentSha256) {
35109
33366
  throw new SkillLifecycleError(
35110
33367
  "skill_integrity_violation",
@@ -35122,7 +33379,7 @@ var SkillLifecycle = class {
35122
33379
  }
35123
33380
  list(state) {
35124
33381
  const dir = this.dir(state);
35125
- if (!existsSync26(dir)) return [];
33382
+ if (!existsSync25(dir)) return [];
35126
33383
  const ids = readdirSorted(dir);
35127
33384
  return ids.map((id) => {
35128
33385
  try {
@@ -35135,7 +33392,7 @@ var SkillLifecycle = class {
35135
33392
  read(state, id) {
35136
33393
  const metadata = this.readMetadata(state, id);
35137
33394
  const { skill } = this.paths(state, id);
35138
- return { metadata, content: readFileSync25(skill, "utf8") };
33395
+ return { metadata, content: readFileSync24(skill, "utf8") };
35139
33396
  }
35140
33397
  };
35141
33398
  function boundedList(value, field) {
@@ -35150,18 +33407,18 @@ function boundedList(value, field) {
35150
33407
  return [...new Set(refs)];
35151
33408
  }
35152
33409
  function readdirSorted(dir) {
35153
- return readdirSync10(dir).filter((entry) => statSync3(join30(dir, entry)).isDirectory()).sort();
33410
+ return readdirSync10(dir).filter((entry) => statSync3(join29(dir, entry)).isDirectory()).sort();
35154
33411
  }
35155
33412
 
35156
33413
  // plugins/kxm/src/wiki.ts
35157
- import { mkdirSync as mkdirSync22, writeFileSync as writeFileSync21 } from "node:fs";
35158
- import { join as join31 } from "node:path";
33414
+ import { mkdirSync as mkdirSync21, writeFileSync as writeFileSync20 } from "node:fs";
33415
+ import { join as join30 } from "node:path";
35159
33416
  function writeCompiledWiki(root, wiki) {
35160
33417
  const written = [];
35161
33418
  for (const [relativePath, content] of [...wiki.pages].sort(([left], [right]) => left.localeCompare(right))) {
35162
- const absolute = join31(root, relativePath);
35163
- mkdirSync22(absolute.slice(0, absolute.lastIndexOf("/")), { recursive: true });
35164
- writeFileSync21(absolute, content);
33419
+ const absolute = join30(root, relativePath);
33420
+ mkdirSync21(absolute.slice(0, absolute.lastIndexOf("/")), { recursive: true });
33421
+ writeFileSync20(absolute, content);
35165
33422
  written.push(relativePath);
35166
33423
  }
35167
33424
  return written;
@@ -35327,7 +33584,7 @@ function skillStateFromFlag(value) {
35327
33584
  throw new Error(`invalid skill state ${value}`);
35328
33585
  }
35329
33586
  function skillsRoot(runtime) {
35330
- return join32(runtime.dirs.workdir, ".kxm", "skills");
33587
+ return join31(runtime.dirs.workdir, ".kxm", "skills");
35331
33588
  }
35332
33589
  function csv(value) {
35333
33590
  if (!value) return void 0;
@@ -35344,7 +33601,7 @@ async function cmdSkillsCreate(runtime, options) {
35344
33601
  const metadata = lifecycle.create({
35345
33602
  name: options.name,
35346
33603
  description: options.description ?? "",
35347
- content: readFileSync26(options.file, "utf8"),
33604
+ content: readFileSync25(options.file, "utf8"),
35348
33605
  createdBy: options.createdBy,
35349
33606
  sources: {
35350
33607
  runIds: csv(options.run) ?? [],
@@ -35502,8 +33759,8 @@ async function cmdMemorySync(runtime) {
35502
33759
 
35503
33760
  // plugins/kxm/src/cli/hub.ts
35504
33761
  import { randomUUID as randomUUID14 } from "node:crypto";
35505
- import { existsSync as existsSync32, mkdirSync as mkdirSync26, readFileSync as readFileSync32, readdirSync as readdirSync12, rmSync as rmSync11, writeFileSync as writeFileSync24 } from "node:fs";
35506
- import { join as join42, resolve as resolve25 } from "node:path";
33762
+ import { existsSync as existsSync31, mkdirSync as mkdirSync25, readFileSync as readFileSync31, readdirSync as readdirSync12, rmSync as rmSync10, writeFileSync as writeFileSync23 } from "node:fs";
33763
+ import { join as join41, resolve as resolve24 } from "node:path";
35507
33764
 
35508
33765
  // node_modules/marked/lib/marked.esm.js
35509
33766
  function M() {
@@ -39665,10 +37922,10 @@ var TuiBase = class _TuiBase extends Container {
39665
37922
  * @returns Promise containing the parsed RGB color, or undefined if it times out or fails to parse.
39666
37923
  */
39667
37924
  queryTerminalBackgroundColor({ timeoutMs }) {
39668
- return new Promise((resolve31) => {
37925
+ return new Promise((resolve30) => {
39669
37926
  const query = {
39670
37927
  settled: false,
39671
- resolve: resolve31,
37928
+ resolve: resolve30,
39672
37929
  timer: void 0
39673
37930
  };
39674
37931
  query.timer = setTimeout(() => {
@@ -39691,7 +37948,7 @@ var TuiBase = class _TuiBase extends Container {
39691
37948
  * `CSI ? 997 ; 1 n` for dark or `CSI ? 997 ; 2 n` for light.
39692
37949
  */
39693
37950
  queryTerminalColorScheme({ timeoutMs }) {
39694
- return new Promise((resolve31) => {
37951
+ return new Promise((resolve30) => {
39695
37952
  let settled = false;
39696
37953
  let timer;
39697
37954
  let unsubscribe = () => {
@@ -39705,7 +37962,7 @@ var TuiBase = class _TuiBase extends Container {
39705
37962
  timer = void 0;
39706
37963
  }
39707
37964
  unsubscribe();
39708
- resolve31(scheme);
37965
+ resolve30(scheme);
39709
37966
  };
39710
37967
  unsubscribe = this.onTerminalColorSchemeChange(settle);
39711
37968
  timer = setTimeout(() => settle(void 0), timeoutMs);
@@ -41436,7 +39693,7 @@ import * as path2 from "node:path";
41436
39693
 
41437
39694
  // node_modules/@earendil-works/pi-tui/dist/native-module-path.js
41438
39695
  import { createRequire as createRequire2 } from "node:module";
41439
- import { dirname as dirname17, join as join33 } from "node:path";
39696
+ import { dirname as dirname17, join as join32 } from "node:path";
41440
39697
  import { fileURLToPath as fileURLToPath2 } from "node:url";
41441
39698
  var moduleRequire = createRequire2(import.meta.url);
41442
39699
  var TUI_PACKAGE_NAME = "@earendil-works/pi-tui";
@@ -41445,10 +39702,10 @@ function getNativeModuleCandidates(nativePath, options = {}) {
41445
39702
  const candidates = [];
41446
39703
  try {
41447
39704
  const packageEntry = (options.resolvePackage ?? moduleRequire.resolve)(TUI_PACKAGE_NAME);
41448
- candidates.push(join33(dirname17(packageEntry), "..", nativePath));
39705
+ candidates.push(join32(dirname17(packageEntry), "..", nativePath));
41449
39706
  } catch {
41450
39707
  }
41451
- candidates.push(join33(moduleDir, "..", nativePath), join33(moduleDir, nativePath), join33(dirname17(options.execPath ?? process.execPath), nativePath));
39708
+ candidates.push(join32(moduleDir, "..", nativePath), join32(moduleDir, nativePath), join32(dirname17(options.execPath ?? process.execPath), nativePath));
41452
39709
  return Array.from(new Set(candidates));
41453
39710
  }
41454
39711
 
@@ -41789,7 +40046,7 @@ var ProcessTerminal = class {
41789
40046
  break;
41790
40047
  if (now - lastDataTime >= idleMs)
41791
40048
  break;
41792
- await new Promise((resolve31) => setTimeout(resolve31, Math.min(idleMs, timeLeft)));
40049
+ await new Promise((resolve30) => setTimeout(resolve30, Math.min(idleMs, timeLeft)));
41793
40050
  }
41794
40051
  } finally {
41795
40052
  process.stdin.removeListener("data", onData);
@@ -43904,8 +42161,8 @@ var TuiAltScreen = class extends TuiBase {
43904
42161
  var MAX_RENDER_WRITE_CHARS = 1024 * 1024;
43905
42162
 
43906
42163
  // plugins/kxm/src/tui.ts
43907
- import { mkdirSync as mkdirSync23 } from "node:fs";
43908
- import { join as join37, resolve as resolve24, dirname as dirname18 } from "node:path";
42164
+ import { mkdirSync as mkdirSync22 } from "node:fs";
42165
+ import { join as join36, resolve as resolve23, dirname as dirname18 } from "node:path";
43909
42166
  import { spawnSync as spawnSync5 } from "node:child_process";
43910
42167
 
43911
42168
  // packages/core/tui/src/types/surface.ts
@@ -44040,9 +42297,9 @@ function slugifyBranchPart(text, maxLength = 40) {
44040
42297
  }
44041
42298
 
44042
42299
  // plugins/kxm/src/local-snapshot.ts
44043
- import { existsSync as existsSync27, readdirSync as readdirSync11, readFileSync as readFileSync27 } from "node:fs";
42300
+ import { existsSync as existsSync26, readdirSync as readdirSync11, readFileSync as readFileSync26 } from "node:fs";
44044
42301
  import { homedir as homedir6 } from "node:os";
44045
- import { isAbsolute as isAbsolute8, join as join36, resolve as resolve23 } from "node:path";
42302
+ import { isAbsolute as isAbsolute8, join as join35, resolve as resolve22 } from "node:path";
44046
42303
  function processExists2(pid) {
44047
42304
  try {
44048
42305
  process.kill(pid, 0);
@@ -44162,36 +42419,36 @@ function readOpenMessageMetadata(database) {
44162
42419
  return messages;
44163
42420
  }
44164
42421
  function resolveKxmSnapshotPaths(cwd, env = process.env) {
44165
- const stateDir = env.KXM_STATE_DIR?.trim() || join36(cwd, ".kxm", "state");
42422
+ const stateDir = env.KXM_STATE_DIR?.trim() || join35(cwd, ".kxm", "state");
44166
42423
  const configured = env.KXM_DATA_PATH?.trim();
44167
- const dataPath = configured ? resolve23(cwd, configured) : join36(stateDir, "kxm.db");
42424
+ const dataPath = configured ? resolve22(cwd, configured) : join35(stateDir, "kxm.db");
44168
42425
  return { dataPath, stateDir };
44169
42426
  }
44170
42427
  function resolveKxmStateRoot(stateDir, options) {
44171
- if (options?.kxmStateRoot && existsSync27(options.kxmStateRoot)) {
44172
- return resolve23(options.kxmStateRoot);
42428
+ if (options?.kxmStateRoot && existsSync26(options.kxmStateRoot)) {
42429
+ return resolve22(options.kxmStateRoot);
44173
42430
  }
44174
- if (existsSync27(join36(stateDir, "runtime", "registry.db")) || existsSync27(join36(stateDir, "runtime", "projects"))) {
42431
+ if (existsSync26(join35(stateDir, "runtime", "registry.db")) || existsSync26(join35(stateDir, "runtime", "projects"))) {
44175
42432
  return stateDir;
44176
42433
  }
44177
42434
  const env = options?.env ?? process.env;
44178
42435
  const explicit = env.KXM_STATE_HOME?.trim() || env.KXM_USER_STATE_DIR?.trim() || env.KXM_STATE_ROOT?.trim();
44179
- if (explicit && isAbsolute8(explicit) && existsSync27(resolve23(explicit))) {
44180
- return resolve23(explicit);
42436
+ if (explicit && isAbsolute8(explicit) && existsSync26(resolve22(explicit))) {
42437
+ return resolve22(explicit);
44181
42438
  }
44182
42439
  let base;
44183
42440
  if (process.platform === "win32") {
44184
42441
  const localAppData = env.LOCALAPPDATA?.trim();
44185
- base = localAppData && isAbsolute8(localAppData) ? localAppData : join36(homedir6(), "AppData", "Local");
44186
- base = resolve23(base, "KXM");
42442
+ base = localAppData && isAbsolute8(localAppData) ? localAppData : join35(homedir6(), "AppData", "Local");
42443
+ base = resolve22(base, "KXM");
44187
42444
  } else if (process.platform === "darwin") {
44188
- base = resolve23(homedir6(), "Library", "Application Support", "KXM");
42445
+ base = resolve22(homedir6(), "Library", "Application Support", "KXM");
44189
42446
  } else {
44190
42447
  const xdgState = env.XDG_STATE_HOME?.trim();
44191
- base = xdgState && isAbsolute8(xdgState) ? xdgState : join36(homedir6(), ".local", "state");
44192
- base = resolve23(base, "kxm");
42448
+ base = xdgState && isAbsolute8(xdgState) ? xdgState : join35(homedir6(), ".local", "state");
42449
+ base = resolve22(base, "kxm");
44193
42450
  }
44194
- if (existsSync27(base)) return base;
42451
+ if (existsSync26(base)) return base;
44195
42452
  return void 0;
44196
42453
  }
44197
42454
  function loadLocalMeshSnapshot(dataPath, stateDir, options) {
@@ -44202,7 +42459,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
44202
42459
  let legacyRuns = [];
44203
42460
  let legacyRunTotal = 0;
44204
42461
  let plans = [];
44205
- if (existsSync27(dataPath)) {
42462
+ if (existsSync26(dataPath)) {
44206
42463
  hasLegacy = true;
44207
42464
  const database = new DatabaseSync(dataPath, { readOnly: true });
44208
42465
  try {
@@ -44222,11 +42479,11 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
44222
42479
  let kxmRunTotal = 0;
44223
42480
  const kxmStateRoot = resolveKxmStateRoot(stateDir, options);
44224
42481
  if (kxmStateRoot) {
44225
- const runtimeDir = join36(kxmStateRoot, "runtime");
44226
- const registryDbPath = join36(runtimeDir, "registry.db");
44227
- const projectsDir = join36(runtimeDir, "projects");
42482
+ const runtimeDir = join35(kxmStateRoot, "runtime");
42483
+ const registryDbPath = join35(runtimeDir, "registry.db");
42484
+ const projectsDir = join35(runtimeDir, "projects");
44228
42485
  const projectKeys = /* @__PURE__ */ new Set();
44229
- if (existsSync27(registryDbPath)) {
42486
+ if (existsSync26(registryDbPath)) {
44230
42487
  hasKxm = true;
44231
42488
  try {
44232
42489
  const regDb = new DatabaseSync(registryDbPath, { readOnly: true });
@@ -44242,7 +42499,7 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
44242
42499
  } catch {
44243
42500
  }
44244
42501
  }
44245
- if (existsSync27(projectsDir)) {
42502
+ if (existsSync26(projectsDir)) {
44246
42503
  try {
44247
42504
  for (const entry of readdirSync11(projectsDir, { withFileTypes: true })) {
44248
42505
  if (entry.isDirectory()) {
@@ -44253,8 +42510,8 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
44253
42510
  }
44254
42511
  }
44255
42512
  for (const key of projectKeys) {
44256
- const eventDbPath = join36(projectsDir, key, "run-events.db");
44257
- if (existsSync27(eventDbPath)) {
42513
+ const eventDbPath = join35(projectsDir, key, "run-events.db");
42514
+ if (existsSync26(eventDbPath)) {
44258
42515
  hasKxm = true;
44259
42516
  try {
44260
42517
  const eventDb = new DatabaseSync(eventDbPath, { readOnly: true });
@@ -44284,10 +42541,10 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
44284
42541
  }
44285
42542
  }
44286
42543
  const pids = [];
44287
- if (existsSync27(stateDir)) {
42544
+ if (existsSync26(stateDir)) {
44288
42545
  for (const file of readdirSync11(stateDir).filter((name) => name.endsWith(".pid"))) {
44289
42546
  try {
44290
- const record = JSON.parse(readFileSync27(join36(stateDir, file), "utf8"));
42547
+ const record = JSON.parse(readFileSync26(join35(stateDir, file), "utf8"));
44291
42548
  pids.push({
44292
42549
  file,
44293
42550
  ...record.role ? { role: record.role } : {},
@@ -44326,8 +42583,8 @@ function loadLocalMeshSnapshot(dataPath, stateDir, options) {
44326
42583
  } else {
44327
42584
  source = "legacy";
44328
42585
  }
44329
- const telemetryFile = join36(stateDir, "telemetry.jsonl");
44330
- const spend = existsSync27(telemetryFile) ? readRoutingRecords(telemetryFile) : [];
42586
+ const telemetryFile = join35(stateDir, "telemetry.jsonl");
42587
+ const spend = existsSync26(telemetryFile) ? readRoutingRecords(telemetryFile) : [];
44331
42588
  return {
44332
42589
  source,
44333
42590
  agents: agents.sort((a, b2) => Number(b2.online) - Number(a.online) || a.name.localeCompare(b2.name)),
@@ -44461,11 +42718,11 @@ function spawnDegradeWorktree(repoRoot, runId, options) {
44461
42718
  const cleanRunId = runId.replace(/^run_/, "");
44462
42719
  const desc = options?.description ? slugifyBranchPart(options.description, 30) : "degrade";
44463
42720
  const branchName = `kxm/run-${cleanRunId}-${desc}`;
44464
- const relativeWtPath = join37(".kxm", "worktrees", `run-${cleanRunId}-${desc}`);
44465
- const absoluteWtPath = resolve24(repoRoot, relativeWtPath);
42721
+ const relativeWtPath = join36(".kxm", "worktrees", `run-${cleanRunId}-${desc}`);
42722
+ const absoluteWtPath = resolve23(repoRoot, relativeWtPath);
44466
42723
  const jumpCommand = `cd "${absoluteWtPath}"`;
44467
42724
  try {
44468
- mkdirSync23(dirname18(absoluteWtPath), { recursive: true });
42725
+ mkdirSync22(dirname18(absoluteWtPath), { recursive: true });
44469
42726
  const branchCheck = runner("git", ["rev-parse", "--verify", `refs/heads/${branchName}`]);
44470
42727
  let wtRes;
44471
42728
  if (branchCheck.status === 0) {
@@ -44855,11 +43112,11 @@ async function readJson(response) {
44855
43112
  }
44856
43113
  async function waitForReconnect(signal, milliseconds = 1e3) {
44857
43114
  if (signal.aborted) return;
44858
- await new Promise((resolve31) => {
43115
+ await new Promise((resolve30) => {
44859
43116
  const done = () => {
44860
43117
  clearTimeout(timer);
44861
43118
  signal.removeEventListener("abort", done);
44862
- resolve31();
43119
+ resolve30();
44863
43120
  };
44864
43121
  const timer = setTimeout(done, milliseconds);
44865
43122
  timer.unref();
@@ -45222,8 +43479,8 @@ async function runMeshTui(input) {
45222
43479
  // plugins/kxm/src/session-work.ts
45223
43480
  import { spawnSync as spawnSync6 } from "node:child_process";
45224
43481
  import { randomUUID as randomUUID13 } from "node:crypto";
45225
- import { existsSync as existsSync28, mkdirSync as mkdirSync24, readFileSync as readFileSync28, renameSync as renameSync9, writeFileSync as writeFileSync22 } from "node:fs";
45226
- import { join as join38 } from "node:path";
43482
+ import { existsSync as existsSync27, mkdirSync as mkdirSync23, readFileSync as readFileSync27, renameSync as renameSync8, writeFileSync as writeFileSync21 } from "node:fs";
43483
+ import { join as join37 } from "node:path";
45227
43484
  var MAX_SESSION_BRIEF_TASKS = 5;
45228
43485
  var MAX_SESSION_BRIEF_PLANS = 5;
45229
43486
  var SESSION_BRIEF_SCHEMA = "kxm.session-brief.v1";
@@ -45380,10 +43637,10 @@ function buildSessionBrief(snapshot, current, hub, ship, updateLatest, cost, ses
45380
43637
  };
45381
43638
  }
45382
43639
  function readCachedSessionBrief(stateDir) {
45383
- const file = join38(stateDir, "session-brief.json");
43640
+ const file = join37(stateDir, "session-brief.json");
45384
43641
  try {
45385
- if (!existsSync28(file)) return void 0;
45386
- const raw = readFileSync28(file, "utf8");
43642
+ if (!existsSync27(file)) return void 0;
43643
+ const raw = readFileSync27(file, "utf8");
45387
43644
  const parsed = JSON.parse(raw);
45388
43645
  if (parsed && parsed.schema === "kxm.session-brief.v1" && typeof parsed.generatedAt === "string") {
45389
43646
  const ageMs = Date.now() - Date.parse(parsed.generatedAt);
@@ -45398,18 +43655,18 @@ function readCachedSessionBrief(stateDir) {
45398
43655
  }
45399
43656
  function writeCachedSessionBrief(stateDir, brief) {
45400
43657
  try {
45401
- mkdirSync24(stateDir, { recursive: true, mode: 448 });
45402
- const file = join38(stateDir, "session-brief.json");
43658
+ mkdirSync23(stateDir, { recursive: true, mode: 448 });
43659
+ const file = join37(stateDir, "session-brief.json");
45403
43660
  const tmp = `${file}.tmp.${randomUUID13().slice(0, 8)}`;
45404
- writeFileSync22(tmp, JSON.stringify(brief, null, 2), { encoding: "utf8", mode: 384 });
45405
- renameSync9(tmp, file);
43661
+ writeFileSync21(tmp, JSON.stringify(brief, null, 2), { encoding: "utf8", mode: 384 });
43662
+ renameSync8(tmp, file);
45406
43663
  } catch {
45407
43664
  }
45408
43665
  }
45409
43666
  function estimateSessionCost(stateDir) {
45410
43667
  try {
45411
- const telemetryFile = join38(stateDir, "telemetry.jsonl");
45412
- if (!existsSync28(telemetryFile)) return void 0;
43668
+ const telemetryFile = join37(stateDir, "telemetry.jsonl");
43669
+ if (!existsSync27(telemetryFile)) return void 0;
45413
43670
  const records = readRoutingRecords(telemetryFile);
45414
43671
  if (records.length === 0) return void 0;
45415
43672
  let totalCost = 0;
@@ -45577,11 +43834,11 @@ function formatSessionBriefText(brief) {
45577
43834
  }
45578
43835
 
45579
43836
  // plugins/kxm/src/kxm-install-kind.ts
45580
- import { existsSync as existsSync29, readFileSync as readFileSync29 } from "node:fs";
45581
- import { dirname as dirname19, join as join39 } from "node:path";
43837
+ import { existsSync as existsSync28, readFileSync as readFileSync28 } from "node:fs";
43838
+ import { dirname as dirname19, join as join38 } from "node:path";
45582
43839
  function packageIsKxm(root) {
45583
43840
  try {
45584
- const pkg = JSON.parse(readFileSync29(join39(root, "package.json"), "utf8"));
43841
+ const pkg = JSON.parse(readFileSync28(join38(root, "package.json"), "utf8"));
45585
43842
  return pkg.name === "@kontextmind/kxm";
45586
43843
  } catch {
45587
43844
  return false;
@@ -45630,12 +43887,12 @@ function report(kind, root) {
45630
43887
  return { kind, root, instruction: installKindInstruction(kind, root) };
45631
43888
  }
45632
43889
  function classifyInstallRoot(probe) {
45633
- const piGitRoot = join39(probe.homeDir, ".pi", "agent", "git");
43890
+ const piGitRoot = join38(probe.homeDir, ".pi", "agent", "git");
45634
43891
  if (isUnder(probe.repoRoot, piGitRoot, probe.platform)) return report("pi-git", probe.repoRoot);
45635
- const claudeHome = probe.env.CLAUDE_CONFIG_DIR?.trim() || join39(probe.homeDir, ".claude");
45636
- const pluginsRoot = join39(claudeHome, "plugins");
43892
+ const claudeHome = probe.env.CLAUDE_CONFIG_DIR?.trim() || join38(probe.homeDir, ".claude");
43893
+ const pluginsRoot = join38(claudeHome, "plugins");
45637
43894
  if (isUnder(probe.moduleDir, pluginsRoot, probe.platform)) return report("claude-marketplace", probe.moduleDir);
45638
- const hasGit = existsSync29(join39(probe.repoRoot, ".git"));
43895
+ const hasGit = existsSync28(join38(probe.repoRoot, ".git"));
45639
43896
  if (hasGit && packageIsKxm(probe.repoRoot)) return report("source", probe.repoRoot);
45640
43897
  if (isNpmPackageLayout(probe.repoRoot, probe.platform) && packageIsKxm(probe.repoRoot) && !hasGit) {
45641
43898
  return report("npm-package", probe.repoRoot);
@@ -45655,15 +43912,15 @@ function resolveInstallKind(probe, npmGlobalRoot) {
45655
43912
  }
45656
43913
 
45657
43914
  // plugins/kxm/src/kxm-update-config.ts
45658
- var import_yaml16 = __toESM(require_dist(), 1);
45659
- import { existsSync as existsSync30, readFileSync as readFileSync30 } from "node:fs";
45660
- import { join as join40 } from "node:path";
43915
+ var import_yaml15 = __toESM(require_dist(), 1);
43916
+ import { existsSync as existsSync29, readFileSync as readFileSync29 } from "node:fs";
43917
+ import { join as join39 } from "node:path";
45661
43918
  function loadKxmUpdateConfig(env = process.env) {
45662
- const path4 = join40(kxmUserStateRoot({ env }), "update.yaml");
45663
- if (!existsSync30(path4)) return { schema: KXM_UPDATE_SCHEMA, auto: false, source: "github" };
43919
+ const path4 = join39(kxmUserStateRoot({ env }), "update.yaml");
43920
+ if (!existsSync29(path4)) return { schema: KXM_UPDATE_SCHEMA, auto: false, source: "github" };
45664
43921
  let parsed;
45665
43922
  try {
45666
- parsed = (0, import_yaml16.parse)(readFileSync30(path4, "utf8"));
43923
+ parsed = (0, import_yaml15.parse)(readFileSync29(path4, "utf8"));
45667
43924
  } catch {
45668
43925
  throw new KxmUpdateConfigError("update.yaml is not valid YAML");
45669
43926
  }
@@ -45689,8 +43946,8 @@ function loadKxmUpdateConfig(env = process.env) {
45689
43946
  }
45690
43947
 
45691
43948
  // plugins/kxm/src/session.ts
45692
- import { existsSync as existsSync31, mkdirSync as mkdirSync25, readFileSync as readFileSync31, writeFileSync as writeFileSync23 } from "node:fs";
45693
- import { join as join41 } from "node:path";
43949
+ import { existsSync as existsSync30, mkdirSync as mkdirSync24, readFileSync as readFileSync30, writeFileSync as writeFileSync22 } from "node:fs";
43950
+ import { join as join40 } from "node:path";
45694
43951
  var SESSION_SCHEMA = "kxm.session.v1";
45695
43952
  var SessionConfigError = class extends Error {
45696
43953
  code = "session_config_invalid";
@@ -45700,12 +43957,12 @@ var SessionConfigError = class extends Error {
45700
43957
  }
45701
43958
  };
45702
43959
  function workflowAssetDirs(assetsDir, workflowId) {
45703
- const root = join41(assetsDir, "workflows", workflowId);
45704
- return [root, join41(root, "inputs"), join41(root, "outputs"), join41(root, "generated")];
43960
+ const root = join40(assetsDir, "workflows", workflowId);
43961
+ return [root, join40(root, "inputs"), join40(root, "outputs"), join40(root, "generated")];
45705
43962
  }
45706
43963
  function sessionAssetDirs(assetsDir, sessionId) {
45707
- const root = join41(assetsDir, "sessions", sessionId);
45708
- return [root, join41(root, "inputs"), join41(root, "outputs")];
43964
+ const root = join40(assetsDir, "sessions", sessionId);
43965
+ return [root, join40(root, "inputs"), join40(root, "outputs")];
45709
43966
  }
45710
43967
  function rosterNames(configDir) {
45711
43968
  return [...loadRosterMap(configDir).values()].map((row) => String(row.name));
@@ -45755,7 +44012,7 @@ function workerFromRosterRow(name, row, project) {
45755
44012
  function loadRosterMap(configDir) {
45756
44013
  const byName = /* @__PURE__ */ new Map();
45757
44014
  for (const [file, key] of [["agents.json", "agents"], ["gates.json", "gates"]]) {
45758
- const path4 = join41(configDir, file);
44015
+ const path4 = join40(configDir, file);
45759
44016
  for (const row of loadRoster(path4, key)) {
45760
44017
  const name = String(row.name).trim();
45761
44018
  const lowered = name.toLowerCase();
@@ -45769,10 +44026,10 @@ function loadRosterMap(configDir) {
45769
44026
  return byName;
45770
44027
  }
45771
44028
  function loadRoster(path4, key) {
45772
- if (!existsSync31(path4)) return [];
44029
+ if (!existsSync30(path4)) return [];
45773
44030
  let parsed;
45774
44031
  try {
45775
- parsed = JSON.parse(readFileSync31(path4, "utf8"));
44032
+ parsed = JSON.parse(readFileSync30(path4, "utf8"));
45776
44033
  } catch (error) {
45777
44034
  throw new SessionConfigError(
45778
44035
  `${key} roster at ${path4} is not valid JSON: ${error instanceof Error ? error.message : String(error)}`
@@ -45797,7 +44054,7 @@ function loadRoster(path4, key) {
45797
44054
  });
45798
44055
  }
45799
44056
  function createSession(input) {
45800
- const assetDir = input.mode === "workflow" && input.workflowId ? join41("assets", "workflows", input.workflowId) : join41("assets", "sessions", input.id);
44057
+ const assetDir = input.mode === "workflow" && input.workflowId ? join40("assets", "workflows", input.workflowId) : join40("assets", "sessions", input.id);
45801
44058
  return {
45802
44059
  schema: SESSION_SCHEMA,
45803
44060
  id: input.id,
@@ -45810,11 +44067,11 @@ function createSession(input) {
45810
44067
  };
45811
44068
  }
45812
44069
  function writeSession(assetsDir, session, dryRun = false) {
45813
- const dir = join41(assetsDir, "sessions", session.id);
45814
- const path4 = join41(dir, "session.json");
44070
+ const dir = join40(assetsDir, "sessions", session.id);
44071
+ const path4 = join40(dir, "session.json");
45815
44072
  if (!dryRun) {
45816
- mkdirSync25(dir, { recursive: true });
45817
- writeFileSync23(path4, `${JSON.stringify(session, null, 2)}
44073
+ mkdirSync24(dir, { recursive: true });
44074
+ writeFileSync22(path4, `${JSON.stringify(session, null, 2)}
45818
44075
  `, { encoding: "utf8" });
45819
44076
  }
45820
44077
  return path4;
@@ -45842,22 +44099,22 @@ function installProbeFrom(runtime) {
45842
44099
  const partial = runtime.io.installProbe ?? {};
45843
44100
  return {
45844
44101
  moduleDir: partial.moduleDir ?? dirname20(fileURLToPath3(import.meta.url)),
45845
- repoRoot: partial.repoRoot ?? resolve25("."),
44102
+ repoRoot: partial.repoRoot ?? resolve24("."),
45846
44103
  homeDir: partial.homeDir ?? homedir7(),
45847
44104
  platform: partial.platform ?? process.platform,
45848
44105
  env: partial.env ?? runtime.env
45849
44106
  };
45850
44107
  }
45851
44108
  function warnIgnoredProjectUpdateYaml(runtime) {
45852
- const projectFile = join42(runtime.dirs.workspace, "update.yaml");
45853
- if (!existsSync32(projectFile)) return;
45854
- const userFile = join42(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
44109
+ const projectFile = join41(runtime.dirs.workspace, "update.yaml");
44110
+ if (!existsSync31(projectFile)) return;
44111
+ const userFile = join41(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
45855
44112
  runtime.io.stderr(`kxm: ignoring .kxm/update.yaml in ${runtime.dirs.workdir}; update settings are read only from ${userFile}
45856
44113
  `);
45857
44114
  }
45858
44115
  async function refreshKxmUpdateNotice(runtime, config) {
45859
44116
  const resolved = config ?? loadKxmUpdateConfig(runtime.env);
45860
- const current = readInstalledKxmVersion(resolve25("."));
44117
+ const current = readInstalledKxmVersion(resolve24("."));
45861
44118
  const fetched = await fetchLatestKxmVersion(resolved.source, runtime.env, runtime.fetchImpl);
45862
44119
  const notice = noticeFromVersions(current, fetched.latest, resolved, fetched.error, fetched.asset);
45863
44120
  writeUpdateCache(runtime.dirs.state, notice);
@@ -45890,7 +44147,7 @@ async function cmdDash(runtime, options = {}) {
45890
44147
  return 2;
45891
44148
  }
45892
44149
  const screen = requested;
45893
- const dataPath = resolve25(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join42(runtime.dirs.state, "kxm.db"));
44150
+ const dataPath = resolve24(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join41(runtime.dirs.state, "kxm.db"));
45894
44151
  if (runtime.dryRun) {
45895
44152
  print(runtime.io, runtime.json, {
45896
44153
  ok: true,
@@ -45936,7 +44193,7 @@ async function cmdHub(runtime) {
45936
44193
  config = loadKxmUpdateConfig(runtime.env);
45937
44194
  } catch (error) {
45938
44195
  if (error instanceof KxmUpdateConfigError) {
45939
- const yamlPath = join42(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
44196
+ const yamlPath = join41(kxmUserStateRoot({ env: runtime.env }), "update.yaml");
45940
44197
  runtime.io.stderr(`kxm: ${error.message}; update check skipped; fix or remove ${yamlPath}
45941
44198
  `);
45942
44199
  } else {
@@ -46123,7 +44380,7 @@ async function cmdWorker(runtime, options) {
46123
44380
  return await (runtime.io.spawnWorker ?? ((launchEnv) => spawnScript("kxm-worker.mjs", launchEnv)))(extraEnv);
46124
44381
  }
46125
44382
  async function cmdStop(runtime, waitMsFlag) {
46126
- const pids = existsSync32(runtime.dirs.state) ? readdirSync12(runtime.dirs.state).filter((name) => name.endsWith(".pid")) : [];
44383
+ const pids = existsSync31(runtime.dirs.state) ? readdirSync12(runtime.dirs.state).filter((name) => name.endsWith(".pid")) : [];
46127
44384
  if (runtime.dryRun) {
46128
44385
  print(runtime.io, runtime.json, { ok: true, command: "stop", dryRun: true, pidFiles: pids }, "would signal pid files");
46129
44386
  return 0;
@@ -46138,7 +44395,7 @@ async function cmdStop(runtime, waitMsFlag) {
46138
44395
  const orphans = [];
46139
44396
  for (const file of pids) {
46140
44397
  try {
46141
- const record = JSON.parse(readFileSync32(join42(runtime.dirs.state, file), "utf8"));
44398
+ const record = JSON.parse(readFileSync31(join41(runtime.dirs.state, file), "utf8"));
46142
44399
  const expectedControl = file === "hub.pid" ? "hub.stop" : file.startsWith("worker-") ? `${file.slice(0, -4)}.stop` : void 0;
46143
44400
  const expectedRole = file === "hub.pid" ? "hub" : file.startsWith("worker-") ? "worker" : void 0;
46144
44401
  if (record.version !== 1 || !Number.isInteger(record.pid) || record.pid <= 0 || !record.startedAt || !expectedControl || record.controlFile !== expectedControl || record.role !== expectedRole) {
@@ -46158,7 +44415,7 @@ async function cmdStop(runtime, waitMsFlag) {
46158
44415
  ignored.push(file);
46159
44416
  continue;
46160
44417
  }
46161
- writeFileSync24(join42(runtime.dirs.state, record.controlFile), `${JSON.stringify({ startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {}, requestedAt: (/* @__PURE__ */ new Date()).toISOString() })}
44418
+ writeFileSync23(join41(runtime.dirs.state, record.controlFile), `${JSON.stringify({ startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {}, requestedAt: (/* @__PURE__ */ new Date()).toISOString() })}
46162
44419
  `, { encoding: "utf8", mode: 384 });
46163
44420
  requested.push(file);
46164
44421
  records.set(file, { pid: record.pid, startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {} });
@@ -46176,7 +44433,7 @@ async function cmdStop(runtime, waitMsFlag) {
46176
44433
  while (Date.now() <= deadline && stopped.size < requested.length) {
46177
44434
  for (const [file, record] of records) {
46178
44435
  try {
46179
- const current = JSON.parse(readFileSync32(join42(runtime.dirs.state, file), "utf8"));
44436
+ const current = JSON.parse(readFileSync31(join41(runtime.dirs.state, file), "utf8"));
46180
44437
  if (current.pid !== record.pid || current.startedAt !== record.startedAt || current.generation !== record.generation || !processExists(record.pid)) stopped.add(file);
46181
44438
  } catch {
46182
44439
  stopped.add(file);
@@ -46189,7 +44446,7 @@ async function cmdStop(runtime, waitMsFlag) {
46189
44446
  for (const file of orphans) {
46190
44447
  while (Date.now() <= deadlineOrphans) {
46191
44448
  try {
46192
- const current = JSON.parse(readFileSync32(join42(runtime.dirs.state, file), "utf8"));
44449
+ const current = JSON.parse(readFileSync31(join41(runtime.dirs.state, file), "utf8"));
46193
44450
  if (!Number.isInteger(current.serverPid) || !processExists(current.serverPid)) break;
46194
44451
  } catch {
46195
44452
  break;
@@ -46197,14 +44454,14 @@ async function cmdStop(runtime, waitMsFlag) {
46197
44454
  await (runtime.io.sleep ?? ((ms) => new Promise((resolveSleep) => setTimeout(resolveSleep, ms))))(100);
46198
44455
  }
46199
44456
  try {
46200
- const current = JSON.parse(readFileSync32(join42(runtime.dirs.state, file), "utf8"));
44457
+ const current = JSON.parse(readFileSync31(join41(runtime.dirs.state, file), "utf8"));
46201
44458
  if (Number.isInteger(current.serverPid) && processExists(current.serverPid)) {
46202
44459
  try {
46203
44460
  process.kill(current.serverPid, "SIGKILL");
46204
44461
  } catch {
46205
44462
  }
46206
44463
  }
46207
- rmSync11(join42(runtime.dirs.state, file), { force: true });
44464
+ rmSync10(join41(runtime.dirs.state, file), { force: true });
46208
44465
  } catch {
46209
44466
  }
46210
44467
  stopped.add(file);
@@ -46215,11 +44472,11 @@ async function cmdStop(runtime, waitMsFlag) {
46215
44472
  }
46216
44473
  async function cmdSessionStatus(runtime) {
46217
44474
  const stateDir = runtime.dirs.state;
46218
- const names3 = existsSync32(stateDir) ? readdirSync12(stateDir) : [];
44475
+ const names3 = existsSync31(stateDir) ? readdirSync12(stateDir) : [];
46219
44476
  const claims = [];
46220
44477
  for (const file of names3.filter((name) => name.endsWith(".pid"))) {
46221
44478
  try {
46222
- const record = JSON.parse(readFileSync32(join42(stateDir, file), "utf8"));
44479
+ const record = JSON.parse(readFileSync31(join41(stateDir, file), "utf8"));
46223
44480
  claims.push({
46224
44481
  file,
46225
44482
  role: record.role,
@@ -46234,7 +44491,7 @@ async function cmdSessionStatus(runtime) {
46234
44491
  const recoveries = [];
46235
44492
  for (const file of names3.filter((name) => name.startsWith("worker-recovery-") && name.endsWith(".json"))) {
46236
44493
  try {
46237
- const envelope = JSON.parse(readFileSync32(join42(stateDir, file), "utf8"));
44494
+ const envelope = JSON.parse(readFileSync31(join41(stateDir, file), "utf8"));
46238
44495
  recoveries.push({
46239
44496
  file,
46240
44497
  reason: envelope.reason,
@@ -46339,7 +44596,7 @@ async function cmdSessionBrief(runtime, options = {}) {
46339
44596
  print(runtime.io, runtime.json, { ok: true, command: "session brief", sessionToken }, sessionToken);
46340
44597
  return 0;
46341
44598
  }
46342
- const dataPath = resolve25(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join42(runtime.dirs.state, "kxm.db"));
44599
+ const dataPath = resolve24(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join41(runtime.dirs.state, "kxm.db"));
46343
44600
  const env = {
46344
44601
  ...runtime.env,
46345
44602
  KXM_STATE_DIR: runtime.dirs.state,
@@ -46416,7 +44673,7 @@ async function cmdSessionStart(runtime, options) {
46416
44673
  ...workflowId ? workflowAssetDirs(runtime.dirs.assets, workflowId) : []
46417
44674
  ];
46418
44675
  if (!runtime.dryRun) {
46419
- for (const directory of created) mkdirSync26(directory, { recursive: true });
44676
+ for (const directory of created) mkdirSync25(directory, { recursive: true });
46420
44677
  writeSession(runtime.dirs.assets, session);
46421
44678
  }
46422
44679
  print(runtime.io, runtime.json, {
@@ -46431,16 +44688,16 @@ async function cmdSessionStart(runtime, options) {
46431
44688
 
46432
44689
  // plugins/kxm/src/cli/system.ts
46433
44690
  import { spawnSync as spawnSync8 } from "node:child_process";
46434
- import { createHash as createHash16 } from "node:crypto";
46435
- import { existsSync as existsSync38, mkdtempSync as mkdtempSync2, readFileSync as readFileSync37, rmSync as rmSync13 } from "node:fs";
44691
+ import { createHash as createHash15 } from "node:crypto";
44692
+ import { existsSync as existsSync37, mkdtempSync as mkdtempSync2, readFileSync as readFileSync36, rmSync as rmSync12 } from "node:fs";
46436
44693
  import { tmpdir as tmpdir2 } from "node:os";
46437
- import { join as join48, resolve as resolve29 } from "node:path";
44694
+ import { join as join47, resolve as resolve28 } from "node:path";
46438
44695
  import { createInterface as createInterface3 } from "node:readline";
46439
44696
 
46440
44697
  // plugins/kxm/src/improve.ts
46441
- import { createHash as createHash15 } from "node:crypto";
46442
- import { existsSync as existsSync33, mkdirSync as mkdirSync27, writeFileSync as writeFileSync25 } from "node:fs";
46443
- import { join as join43, relative as relative6, resolve as resolve26 } from "node:path";
44698
+ import { createHash as createHash14 } from "node:crypto";
44699
+ import { existsSync as existsSync32, mkdirSync as mkdirSync26, writeFileSync as writeFileSync24 } from "node:fs";
44700
+ import { join as join42, relative as relative6, resolve as resolve25 } from "node:path";
46444
44701
  var CANDIDATE_SCHEMA = "kxm.candidate.v1";
46445
44702
  var IMPROVEMENT_REPORT_SCHEMA = "kxm.improvement-report.v2";
46446
44703
  function classifyCandidateKind(stepId, agentRole) {
@@ -46586,7 +44843,7 @@ function groupRoutingRecords(records, options = {}) {
46586
44843
  const isCandidate = recurrence >= minRecurrence && verifyPassRate >= minPassRate;
46587
44844
  const candidateKind = isCandidate ? classifyCandidateKind(group.stepId, group.agentRole) : void 0;
46588
44845
  const safeSlug = group.stepId.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "").slice(0, 16) || "step";
46589
- const keyHash = createHash15("sha256").update(`${group.workflowHash}:${group.stepId}:${group.agentRole}:${group.promptHash}`).digest("hex").slice(0, 10);
44846
+ const keyHash = createHash14("sha256").update(`${group.workflowHash}:${group.stepId}:${group.agentRole}:${group.promptHash}`).digest("hex").slice(0, 10);
46590
44847
  const candidateId = isCandidate && candidateKind ? `cand_${candidateKind.replace(/-/g, "_")}_${safeSlug}_${keyHash}` : void 0;
46591
44848
  rows.push({
46592
44849
  workflowHash: group.workflowHash,
@@ -46611,8 +44868,8 @@ function groupRoutingRecords(records, options = {}) {
46611
44868
  });
46612
44869
  }
46613
44870
  function buildImprovementReport(records, options = {}) {
46614
- const projectRoot = options.projectRoot ? resolve26(options.projectRoot) : process.cwd();
46615
- const candidatesDir = options.candidatesDir ? resolve26(options.candidatesDir) : join43(projectRoot, ".kxm", "candidates");
44871
+ const projectRoot = options.projectRoot ? resolve25(options.projectRoot) : process.cwd();
44872
+ const candidatesDir = options.candidatesDir ? resolve25(options.candidatesDir) : join42(projectRoot, ".kxm", "candidates");
46616
44873
  const groups = groupRoutingRecords(records, options);
46617
44874
  const candidates = [];
46618
44875
  for (const group of groups) {
@@ -46624,7 +44881,7 @@ function buildImprovementReport(records, options = {}) {
46624
44881
  group.workflowHash
46625
44882
  );
46626
44883
  const diffFileName = `${group.candidateId}.diff`;
46627
- const diffFilePath = join43(candidatesDir, diffFileName);
44884
+ const diffFilePath = join42(candidatesDir, diffFileName);
46628
44885
  const relDiffPath = relative6(projectRoot, diffFilePath).replace(/\\/g, "/");
46629
44886
  const candidate = {
46630
44887
  schema: CANDIDATE_SCHEMA,
@@ -46646,12 +44903,12 @@ function buildImprovementReport(records, options = {}) {
46646
44903
  createdAt: nowIso()
46647
44904
  };
46648
44905
  if (!options.dryRun) {
46649
- if (!existsSync33(candidatesDir)) {
46650
- mkdirSync27(candidatesDir, { recursive: true });
44906
+ if (!existsSync32(candidatesDir)) {
44907
+ mkdirSync26(candidatesDir, { recursive: true });
46651
44908
  }
46652
- writeFileSync25(diffFilePath, diff, "utf8");
46653
- const jsonFilePath = join43(candidatesDir, `${group.candidateId}.json`);
46654
- writeFileSync25(jsonFilePath, JSON.stringify(candidate, null, 2) + "\n", "utf8");
44909
+ writeFileSync24(diffFilePath, diff, "utf8");
44910
+ const jsonFilePath = join42(candidatesDir, `${group.candidateId}.json`);
44911
+ writeFileSync24(jsonFilePath, JSON.stringify(candidate, null, 2) + "\n", "utf8");
46655
44912
  }
46656
44913
  candidates.push(candidate);
46657
44914
  }
@@ -46666,10 +44923,10 @@ function buildImprovementReport(records, options = {}) {
46666
44923
  }
46667
44924
  function writeImprovementReport(improvementsDir, report2, dryRun = false) {
46668
44925
  const stamp = report2.createdAt.replace(/[:.]/g, "-");
46669
- const path4 = join43(improvementsDir, `${stamp}.json`);
44926
+ const path4 = join42(improvementsDir, `${stamp}.json`);
46670
44927
  if (!dryRun) {
46671
- mkdirSync27(improvementsDir, { recursive: true });
46672
- writeFileSync25(path4, `${JSON.stringify(report2, null, 2)}
44928
+ mkdirSync26(improvementsDir, { recursive: true });
44929
+ writeFileSync24(path4, `${JSON.stringify(report2, null, 2)}
46673
44930
  `, { encoding: "utf8" });
46674
44931
  }
46675
44932
  return path4;
@@ -46708,9 +44965,9 @@ function formatImprovementReport(report2) {
46708
44965
  }
46709
44966
 
46710
44967
  // plugins/kxm/src/modes.ts
46711
- var import_yaml17 = __toESM(require_dist(), 1);
46712
- import { existsSync as existsSync34, readFileSync as readFileSync34 } from "node:fs";
46713
- import { join as join44 } from "node:path";
44968
+ var import_yaml16 = __toESM(require_dist(), 1);
44969
+ import { existsSync as existsSync33, readFileSync as readFileSync33 } from "node:fs";
44970
+ import { join as join43 } from "node:path";
46714
44971
  var DEFAULT_MODES_CONFIG = Object.freeze({
46715
44972
  schema: "kxm.modes.v1",
46716
44973
  majorModes: {
@@ -46773,13 +45030,13 @@ function loadModesConfig(projectRoot) {
46773
45030
  if (!projectRoot) {
46774
45031
  return DEFAULT_MODES_CONFIG;
46775
45032
  }
46776
- const modesPath = join44(projectRoot, ".kxm", "modes.yaml");
46777
- if (!existsSync34(modesPath)) {
45033
+ const modesPath = join43(projectRoot, ".kxm", "modes.yaml");
45034
+ if (!existsSync33(modesPath)) {
46778
45035
  return DEFAULT_MODES_CONFIG;
46779
45036
  }
46780
45037
  try {
46781
- const raw = readFileSync34(modesPath, "utf8");
46782
- const parsed = (0, import_yaml17.parse)(raw);
45038
+ const raw = readFileSync33(modesPath, "utf8");
45039
+ const parsed = (0, import_yaml16.parse)(raw);
46783
45040
  if (parsed && parsed.schema === "kxm.modes.v1" && parsed.majorModes) {
46784
45041
  return {
46785
45042
  schema: "kxm.modes.v1",
@@ -46864,11 +45121,11 @@ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog
46864
45121
  estimatedTokens: estimateTokens(baseSystemPromptChars)
46865
45122
  });
46866
45123
  for (const relPath of resolved.contextFiles) {
46867
- const fullPath = join44(projectRoot, relPath);
45124
+ const fullPath = join43(projectRoot, relPath);
46868
45125
  let chars = 0;
46869
- if (existsSync34(fullPath)) {
45126
+ if (existsSync33(fullPath)) {
46870
45127
  try {
46871
- chars = readFileSync34(fullPath, "utf8").length;
45128
+ chars = readFileSync33(fullPath, "utf8").length;
46872
45129
  } catch {
46873
45130
  chars = 0;
46874
45131
  }
@@ -47000,9 +45257,9 @@ ${divider}
47000
45257
 
47001
45258
  // plugins/kxm/src/ssh-remote.ts
47002
45259
  import { spawnSync as spawnSync7 } from "node:child_process";
47003
- import { existsSync as existsSync35, mkdirSync as mkdirSync28, readFileSync as readFileSync35, readdirSync as readdirSync13, rmSync as rmSync12, statSync as statSync5 } from "node:fs";
45260
+ import { existsSync as existsSync34, mkdirSync as mkdirSync27, readFileSync as readFileSync34, readdirSync as readdirSync13, rmSync as rmSync11, statSync as statSync5 } from "node:fs";
47004
45261
  import { homedir as homedir8 } from "node:os";
47005
- import { join as join45, resolve as resolve27 } from "node:path";
45262
+ import { join as join44, resolve as resolve26 } from "node:path";
47006
45263
  var MAX_SSH_OUTPUT_BYTES = 50 * 1024;
47007
45264
  var MAX_SSH_OUTPUT_LINES = 2e3;
47008
45265
  var DEFAULT_SOCKET_DIR = ".kxm/run/ssh-sockets";
@@ -47028,12 +45285,12 @@ function truncateSshOutput(raw) {
47028
45285
  return { text, truncated };
47029
45286
  }
47030
45287
  function parseSshConfig(configPath) {
47031
- const targetPath = configPath ?? join45(homedir8(), ".ssh", "config");
47032
- if (!existsSync35(targetPath)) {
45288
+ const targetPath = configPath ?? join44(homedir8(), ".ssh", "config");
45289
+ if (!existsSync34(targetPath)) {
47033
45290
  return [];
47034
45291
  }
47035
45292
  try {
47036
- const content = readFileSync35(targetPath, "utf-8");
45293
+ const content = readFileSync34(targetPath, "utf-8");
47037
45294
  const lines = content.split("\n");
47038
45295
  const hosts = [];
47039
45296
  let currentHosts = [];
@@ -47110,15 +45367,15 @@ function resolveSshHostG(host, execFn = spawnSync7) {
47110
45367
  }
47111
45368
  }
47112
45369
  function ensureSocketDir(socketDir = DEFAULT_SOCKET_DIR) {
47113
- const resolved = resolve27(socketDir);
47114
- if (!existsSync35(resolved)) {
47115
- mkdirSync28(resolved, { recursive: true, mode: 448 });
45370
+ const resolved = resolve26(socketDir);
45371
+ if (!existsSync34(resolved)) {
45372
+ mkdirSync27(resolved, { recursive: true, mode: 448 });
47116
45373
  }
47117
45374
  return resolved;
47118
45375
  }
47119
45376
  function buildSshArgs(options) {
47120
45377
  const socketDir = ensureSocketDir(options.socketDir ?? DEFAULT_SOCKET_DIR);
47121
- const controlPath = join45(socketDir, "%C");
45378
+ const controlPath = join44(socketDir, "%C");
47122
45379
  const persist = options.controlPersist ?? DEFAULT_CONTROL_PERSIST;
47123
45380
  const args = [
47124
45381
  "-o",
@@ -47143,7 +45400,7 @@ function buildSshArgs(options) {
47143
45400
  }
47144
45401
  function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync7) {
47145
45402
  const resolvedDir = ensureSocketDir(socketDir);
47146
- const controlPath = join45(resolvedDir, "%C");
45403
+ const controlPath = join44(resolvedDir, "%C");
47147
45404
  try {
47148
45405
  const result = execFn("ssh", ["-O", "check", "-o", `ControlPath=${controlPath}`, host], {
47149
45406
  encoding: "utf-8"
@@ -47155,7 +45412,7 @@ function checkControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawn
47155
45412
  }
47156
45413
  function closeControlSocket(host, socketDir = DEFAULT_SOCKET_DIR, execFn = spawnSync7) {
47157
45414
  const resolvedDir = ensureSocketDir(socketDir);
47158
- const controlPath = join45(resolvedDir, "%C");
45415
+ const controlPath = join44(resolvedDir, "%C");
47159
45416
  try {
47160
45417
  const result = execFn("ssh", ["-O", "stop", "-o", `ControlPath=${controlPath}`, host], {
47161
45418
  encoding: "utf-8"
@@ -47367,7 +45624,6 @@ function executeSshRun(params) {
47367
45624
  // plugins/kxm/src/autocomplete.ts
47368
45625
  var TOP_LEVEL_COMMANDS = [
47369
45626
  "init",
47370
- "migrate",
47371
45627
  "backup",
47372
45628
  "restore",
47373
45629
  "run",
@@ -47400,7 +45656,6 @@ var TOP_LEVEL_COMMANDS = [
47400
45656
  ];
47401
45657
  var SUBCOMMANDS = {
47402
45658
  runs: ["status", "cancel", "list"],
47403
- migrate: ["plan", "apply", "verify"],
47404
45659
  harness: ["list"],
47405
45660
  runtime: ["start", "status", "stop"],
47406
45661
  trust: ["diff", "check"],
@@ -47492,7 +45747,6 @@ _kxm() {
47492
45747
  local -a commands
47493
45748
  commands=(
47494
45749
  'init:Create or validate a KXM project'
47495
- 'migrate:Plan, apply, and verify legacy configuration migration'
47496
45750
  'backup:Create a verified SQLite backup manifest'
47497
45751
  'restore:Restore SQLite stores from a backup manifest'
47498
45752
  'run:Create a KXM workflow run'
@@ -47601,9 +45855,9 @@ complete -c kxm -n "__fish_seen_subcommand_from goal" -a "create list get"
47601
45855
  }
47602
45856
 
47603
45857
  // plugins/kxm/src/completion-install.ts
47604
- import { existsSync as existsSync36, mkdirSync as mkdirSync29, readFileSync as readFileSync36, writeFileSync as writeFileSync26 } from "node:fs";
45858
+ import { existsSync as existsSync35, mkdirSync as mkdirSync28, readFileSync as readFileSync35, writeFileSync as writeFileSync25 } from "node:fs";
47605
45859
  import { homedir as homedir9 } from "node:os";
47606
- import { basename as basename9, delimiter, dirname as dirname22, isAbsolute as isAbsolute9, join as join46, resolve as resolve28 } from "node:path";
45860
+ import { basename as basename9, delimiter, dirname as dirname22, isAbsolute as isAbsolute9, join as join45, resolve as resolve27 } from "node:path";
47607
45861
  var COMPLETION_MARKER = "# kxm completion";
47608
45862
  var PATH_MARKER = "# kxm path";
47609
45863
  function detectShell(env = process.env, platform = process.platform) {
@@ -47619,31 +45873,31 @@ function effectiveHome(options) {
47619
45873
  const env = options.env ?? process.env;
47620
45874
  if (options.homeDir) return options.homeDir;
47621
45875
  const envHome = env.HOME?.trim() ?? env.USERPROFILE?.trim();
47622
- return envHome && envHome.length > 0 ? resolve28(envHome) : homedir9();
45876
+ return envHome && envHome.length > 0 ? resolve27(envHome) : homedir9();
47623
45877
  }
47624
45878
  function completionScriptPath(shell, options = {}) {
47625
45879
  const env = options.env ?? process.env;
47626
45880
  const explicit = options.configDir ?? env.KXM_USER_CONFIG_DIR?.trim();
47627
- const base = explicit && explicit.length > 0 ? resolve28(explicit) : resolve28(effectiveHome(options), ".config", "kxm");
47628
- return join46(base, "completions", `kxm.${shell}`);
45881
+ const base = explicit && explicit.length > 0 ? resolve27(explicit) : resolve27(effectiveHome(options), ".config", "kxm");
45882
+ return join45(base, "completions", `kxm.${shell}`);
47629
45883
  }
47630
45884
  function bashRcCandidate(options) {
47631
45885
  const home = effectiveHome(options);
47632
- const candidates = [join46(home, ".bashrc"), join46(home, ".bash_profile")];
47633
- const existing = candidates.find((candidate) => existsSync36(candidate));
45886
+ const candidates = [join45(home, ".bashrc"), join45(home, ".bash_profile")];
45887
+ const existing = candidates.find((candidate) => existsSync35(candidate));
47634
45888
  return existing ?? candidates[0];
47635
45889
  }
47636
45890
  function zshRcCandidate(options) {
47637
45891
  const env = options.env ?? process.env;
47638
45892
  const home = effectiveHome(options);
47639
- if (env.ZDOTDIR?.trim()) return join46(resolve28(env.ZDOTDIR.trim()), ".zshrc");
47640
- return join46(home, ".zshrc");
45893
+ if (env.ZDOTDIR?.trim()) return join45(resolve27(env.ZDOTDIR.trim()), ".zshrc");
45894
+ return join45(home, ".zshrc");
47641
45895
  }
47642
45896
  function fishCompletionTarget(shell, options) {
47643
45897
  const env = options.env ?? process.env;
47644
45898
  const home = effectiveHome(options);
47645
- if (env.XDG_CONFIG_HOME?.trim()) return join46(resolve28(env.XDG_CONFIG_HOME.trim()), "fish", "completions", "kxm.fish");
47646
- return join46(home, ".config", "fish", "completions", "kxm.fish");
45899
+ if (env.XDG_CONFIG_HOME?.trim()) return join45(resolve27(env.XDG_CONFIG_HOME.trim()), "fish", "completions", "kxm.fish");
45900
+ return join45(home, ".config", "fish", "completions", "kxm.fish");
47647
45901
  }
47648
45902
  function completionRcTarget(shell, options = {}) {
47649
45903
  if (shell === "fish") {
@@ -47675,17 +45929,17 @@ function installShellCompletion(shellInput, options = {}) {
47675
45929
  const scriptPath = completionScriptPath(shell, options);
47676
45930
  const { rcFile } = completionRcTarget(shell, options);
47677
45931
  const script = generateShellCompletion(shell);
47678
- const existingScript = existsSync36(scriptPath);
47679
- const existingScriptMatches = existingScript && readFileSync36(scriptPath, "utf8") === script;
45932
+ const existingScript = existsSync35(scriptPath);
45933
+ const existingScriptMatches = existingScript && readFileSync35(scriptPath, "utf8") === script;
47680
45934
  let rcModified = false;
47681
45935
  let alreadyInstalled = false;
47682
45936
  if (shell === "fish") {
47683
45937
  const fishTarget = fishCompletionTarget(shell, options);
47684
- const fishInstalled = existsSync36(fishTarget) && readFileSync36(fishTarget, "utf8") === script;
45938
+ const fishInstalled = existsSync35(fishTarget) && readFileSync35(fishTarget, "utf8") === script;
47685
45939
  alreadyInstalled = fishInstalled;
47686
45940
  if (!options.dryRun && (!fishInstalled || options.overwrite)) {
47687
- mkdirSync29(resolve28(fishTarget, ".."), { recursive: true });
47688
- writeFileSync26(fishTarget, script, { encoding: "utf8" });
45941
+ mkdirSync28(resolve27(fishTarget, ".."), { recursive: true });
45942
+ writeFileSync25(fishTarget, script, { encoding: "utf8" });
47689
45943
  }
47690
45944
  return {
47691
45945
  ok: true,
@@ -47698,13 +45952,13 @@ function installShellCompletion(shellInput, options = {}) {
47698
45952
  }
47699
45953
  if (rcFile) {
47700
45954
  const line = sourceLine(shell, scriptPath);
47701
- const rcContent = existsSync36(rcFile) ? readFileSync36(rcFile, "utf8") : "";
45955
+ const rcContent = existsSync35(rcFile) ? readFileSync35(rcFile, "utf8") : "";
47702
45956
  alreadyInstalled = rcContent.includes(line) || rcContent.includes(`${COMPLETION_MARKER}`);
47703
45957
  }
47704
45958
  if (!options.dryRun) {
47705
45959
  if (!existingScriptMatches || options.overwrite) {
47706
- mkdirSync29(resolve28(scriptPath, ".."), { recursive: true });
47707
- writeFileSync26(scriptPath, script, { encoding: "utf8" });
45960
+ mkdirSync28(resolve27(scriptPath, ".."), { recursive: true });
45961
+ writeFileSync25(scriptPath, script, { encoding: "utf8" });
47708
45962
  }
47709
45963
  if (rcFile && !alreadyInstalled) {
47710
45964
  const line = sourceLine(shell, scriptPath);
@@ -47713,8 +45967,8 @@ function installShellCompletion(shellInput, options = {}) {
47713
45967
  ${marker}
47714
45968
  ${line}
47715
45969
  `;
47716
- mkdirSync29(resolve28(rcFile, ".."), { recursive: true });
47717
- writeFileSync26(rcFile, (existsSync36(rcFile) ? readFileSync36(rcFile, "utf8") : "") + stanza, { encoding: "utf8" });
45970
+ mkdirSync28(resolve27(rcFile, ".."), { recursive: true });
45971
+ writeFileSync25(rcFile, (existsSync35(rcFile) ? readFileSync35(rcFile, "utf8") : "") + stanza, { encoding: "utf8" });
47718
45972
  rcModified = true;
47719
45973
  }
47720
45974
  }
@@ -47731,13 +45985,13 @@ function kxmBinDir(env = process.env) {
47731
45985
  const argv1 = env.KXM_ENTRY ?? process.argv[1];
47732
45986
  if (argv1 && isAbsolute9(argv1)) {
47733
45987
  const dir = dirname22(argv1);
47734
- if (existsSync36(join46(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
45988
+ if (existsSync35(join45(dir, process.platform === "win32" ? "kxm.cmd" : "kxm"))) return dir;
47735
45989
  }
47736
45990
  const pathEnv = env.PATH ?? "";
47737
45991
  for (const part of pathEnv.split(delimiter)) {
47738
45992
  if (!part) continue;
47739
- const candidate = resolve28(part, process.platform === "win32" ? "kxm.cmd" : "kxm");
47740
- if (existsSync36(candidate)) return resolve28(part);
45993
+ const candidate = resolve27(part, process.platform === "win32" ? "kxm.cmd" : "kxm");
45994
+ if (existsSync35(candidate)) return resolve27(part);
47741
45995
  }
47742
45996
  return void 0;
47743
45997
  }
@@ -47755,11 +46009,11 @@ function installPathEntry(shellInput, options = {}) {
47755
46009
  }
47756
46010
  const { rcFile } = completionRcTarget(shell, options);
47757
46011
  const line = pathLine(binDir);
47758
- const rcContent = existsSync36(rcFile ?? "") ? readFileSync36(rcFile, "utf8") : "";
46012
+ const rcContent = existsSync35(rcFile ?? "") ? readFileSync35(rcFile, "utf8") : "";
47759
46013
  const alreadyInstalled = rcContent.includes(binDir) || (options.env?.PATH ?? process.env.PATH ?? "").split(delimiter).includes(binDir);
47760
46014
  if (!options.dryRun && rcFile && !alreadyInstalled) {
47761
- mkdirSync29(dirname22(rcFile), { recursive: true });
47762
- writeFileSync26(rcFile, rcContent + `
46015
+ mkdirSync28(dirname22(rcFile), { recursive: true });
46016
+ writeFileSync25(rcFile, rcContent + `
47763
46017
  ${PATH_MARKER}
47764
46018
  ${line}
47765
46019
  `, { encoding: "utf8" });
@@ -47768,9 +46022,9 @@ ${line}
47768
46022
  }
47769
46023
 
47770
46024
  // plugins/kxm/src/init-guide-setup.ts
47771
- var import_yaml18 = __toESM(require_dist(), 1);
47772
- import { existsSync as existsSync37, mkdirSync as mkdirSync30, writeFileSync as writeFileSync27 } from "node:fs";
47773
- import { dirname as dirname23, join as join47 } from "node:path";
46025
+ var import_yaml17 = __toESM(require_dist(), 1);
46026
+ import { existsSync as existsSync36, mkdirSync as mkdirSync29, writeFileSync as writeFileSync26 } from "node:fs";
46027
+ import { dirname as dirname23, join as join46 } from "node:path";
47774
46028
  var NATIVE_VENDOR_HARNESS = Object.freeze({
47775
46029
  anthropic: "claude",
47776
46030
  openai: "codex",
@@ -48169,14 +46423,14 @@ function renderGuideSetupFiles(projectRoot, plan) {
48169
46423
  const stage = roleStages.get(role);
48170
46424
  if (!stage) continue;
48171
46425
  files.push({
48172
- path: join47(projectRoot, ".kxm", "agents", `${role}.yaml`),
48173
- content: (0, import_yaml18.stringify)(agentDocument(role, stage, binding))
46426
+ path: join46(projectRoot, ".kxm", "agents", `${role}.yaml`),
46427
+ content: (0, import_yaml17.stringify)(agentDocument(role, stage, binding))
48174
46428
  });
48175
46429
  }
48176
46430
  for (const workflow of plan.workflows) {
48177
46431
  files.push({
48178
- path: join47(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
48179
- content: (0, import_yaml18.stringify)(workflowDocument(workflow))
46432
+ path: join46(projectRoot, ".kxm", "workflows", `${workflow.slug}.yaml`),
46433
+ content: (0, import_yaml17.stringify)(workflowDocument(workflow))
48180
46434
  });
48181
46435
  }
48182
46436
  return files;
@@ -48185,12 +46439,12 @@ function writeGuideSetupFiles(files) {
48185
46439
  const written = [];
48186
46440
  const existed = [];
48187
46441
  for (const file of files) {
48188
- if (existsSync37(file.path)) {
46442
+ if (existsSync36(file.path)) {
48189
46443
  existed.push(file.path);
48190
46444
  continue;
48191
46445
  }
48192
- mkdirSync30(dirname23(file.path), { recursive: true });
48193
- writeFileSync27(file.path, file.content, "utf8");
46446
+ mkdirSync29(dirname23(file.path), { recursive: true });
46447
+ writeFileSync26(file.path, file.content, "utf8");
48194
46448
  written.push(file.path);
48195
46449
  }
48196
46450
  return { written, existed };
@@ -48253,7 +46507,7 @@ function applyKxmPackageUpdate(runtime, notice) {
48253
46507
  detail: `release v${notice.latest} has no sha256 digest for ${name}; refusing to install`
48254
46508
  };
48255
46509
  }
48256
- const releaseDir = mkdtempSync2(join48(tmpdir2(), "kxm-pkg-update-"));
46510
+ const releaseDir = mkdtempSync2(join47(tmpdir2(), "kxm-pkg-update-"));
48257
46511
  try {
48258
46512
  const planned = planKxmPackageUpdate(notice.source, notice.latest, releaseDir, notice.asset);
48259
46513
  if (runtime.dryRun) {
@@ -48262,7 +46516,7 @@ function applyKxmPackageUpdate(runtime, notice) {
48262
46516
  for (const step of planned) {
48263
46517
  if (step.kind === "verify") {
48264
46518
  if (!verifyReleaseAssetDigest(step.path, step.sha256)) {
48265
- const actual = existsSync38(step.path) ? createHash16("sha256").update(readFileSync37(step.path)).digest("hex") : "missing";
46519
+ const actual = existsSync37(step.path) ? createHash15("sha256").update(readFileSync36(step.path)).digest("hex") : "missing";
48266
46520
  return {
48267
46521
  ok: false,
48268
46522
  error: "release_digest_mismatch",
@@ -48279,7 +46533,7 @@ function applyKxmPackageUpdate(runtime, notice) {
48279
46533
  }
48280
46534
  return { ok: true, detail: `installed ${notice.latest} from ${notice.source}` };
48281
46535
  } finally {
48282
- rmSync13(releaseDir, { recursive: true, force: true });
46536
+ rmSync12(releaseDir, { recursive: true, force: true });
48283
46537
  }
48284
46538
  }
48285
46539
  async function cmdExplain(runtime, options) {
@@ -48527,13 +46781,13 @@ async function cmdValidate(runtime, fileFlag) {
48527
46781
  return 2;
48528
46782
  }
48529
46783
  const selectedFile = explicitFile || configuredFile;
48530
- const file = selectedFile ? resolve29(runtime.cwd, selectedFile) : void 0;
48531
- if (file && !existsSync38(file)) {
46784
+ const file = selectedFile ? resolve28(runtime.cwd, selectedFile) : void 0;
46785
+ if (file && !existsSync37(file)) {
48532
46786
  printWorker(runtime, worker, { ok: false, command: "validate", error: "file_not_found", file }, `workflow file not found: ${file}`);
48533
46787
  return 1;
48534
46788
  }
48535
46789
  try {
48536
- const raw = file ? readFileSync37(file, "utf8") : inline;
46790
+ const raw = file ? readFileSync36(file, "utf8") : inline;
48537
46791
  const warnings = [];
48538
46792
  const definitions = parseWorkflowDefinitions(raw, runtime.env, (message) => warnings.push(message));
48539
46793
  const secretEnvs = definitions.map((definition) => ({
@@ -48558,7 +46812,7 @@ async function cmdValidate(runtime, fileFlag) {
48558
46812
  }
48559
46813
  async function cmdArtifactsExist(runtime, pathFlag) {
48560
46814
  const worker = gateOf(runtime, "artifacts-exist");
48561
- const checked = verifyArtifactExists(runtime.dirs.assets, resolve29(runtime.cwd, pathFlag));
46815
+ const checked = verifyArtifactExists(runtime.dirs.assets, resolve28(runtime.cwd, pathFlag));
48562
46816
  if (!checked.ok) {
48563
46817
  printWorker(
48564
46818
  runtime,
@@ -48577,15 +46831,15 @@ async function cmdArtifactsExist(runtime, pathFlag) {
48577
46831
  return 0;
48578
46832
  }
48579
46833
  async function cmdImprove(runtime, options = {}) {
48580
- const file = options.file ? resolve29(runtime.cwd, options.file) : telemetryPath(runtime.dirs.logs);
48581
- const routingRecords = existsSync38(file) ? readRoutingRecords(file).map((entry) => entry.routing) : [];
48582
- const candidatesDir = options.outDir ? resolve29(runtime.cwd, options.outDir) : join48(runtime.cwd, ".kxm", "candidates");
46834
+ const file = options.file ? resolve28(runtime.cwd, options.file) : telemetryPath(runtime.dirs.logs);
46835
+ const routingRecords = existsSync37(file) ? readRoutingRecords(file).map((entry) => entry.routing) : [];
46836
+ const candidatesDir = options.outDir ? resolve28(runtime.cwd, options.outDir) : join47(runtime.cwd, ".kxm", "candidates");
48583
46837
  const report2 = buildImprovementReport(routingRecords, {
48584
46838
  candidatesDir,
48585
46839
  projectRoot: runtime.cwd,
48586
46840
  dryRun: runtime.dryRun
48587
46841
  });
48588
- const reportDir = join48(runtime.dirs.assets, "improvements");
46842
+ const reportDir = join47(runtime.dirs.assets, "improvements");
48589
46843
  const reportPath = writeImprovementReport(reportDir, report2, runtime.dryRun);
48590
46844
  const text = formatImprovementReport(report2);
48591
46845
  print(runtime.io, runtime.json, {
@@ -48717,9 +46971,9 @@ async function maybeOfferCompletionInstall(runtime) {
48717
46971
  if (!pathNeeded) {
48718
46972
  const scriptPath = completionScriptPath(shell, { env: runtime.env, configDir: runtime.env.KXM_USER_CONFIG_DIR });
48719
46973
  const { rcFile } = completionRcTarget(shell, { env: runtime.env });
48720
- if (rcFile && existsSync38(rcFile)) {
46974
+ if (rcFile && existsSync37(rcFile)) {
48721
46975
  try {
48722
- if (readFileSync37(rcFile, "utf8").includes(scriptPath)) return;
46976
+ if (readFileSync36(rcFile, "utf8").includes(scriptPath)) return;
48723
46977
  } catch {
48724
46978
  }
48725
46979
  }
@@ -48851,7 +47105,7 @@ async function cmdRoutingReport(runtime, options) {
48851
47105
  let catalog;
48852
47106
  if (includeEquivalentListCost) {
48853
47107
  try {
48854
- const pricesPath = options.prices ? resolve29(runtime.cwd, options.prices) : join48(runtime.dirs.workspace, "prices.yaml");
47108
+ const pricesPath = options.prices ? resolve28(runtime.cwd, options.prices) : join47(runtime.dirs.workspace, "prices.yaml");
48855
47109
  catalog = loadPriceCatalog(pricesPath);
48856
47110
  } catch {
48857
47111
  }
@@ -49083,23 +47337,12 @@ function createProgram(ctx, result) {
49083
47337
  writeErr: (text) => ctx.io.stderr(text)
49084
47338
  }).helpCommand("help", "Show help");
49085
47339
  addGlobalOptions(program2);
49086
- program2.command("init").description("Create, validate, or plan migration of a KXM project").option("--json", "Print machine-readable JSON").option("--dry-run", "Plan without making changes").option("--name <name>", "Project display name for a new project").option("--project-id <id>", "Stable project ID for controlled provisioning").option("--repository <id=absolute-path>", "Bind a member repository outside Git configuration", (value, previous) => [...previous, value], []).action(async function initAction(options) {
47340
+ program2.command("init").description("Create, validate, repair, or join a KXM project").option("--json", "Print machine-readable JSON").option("--dry-run", "Plan without making changes").option("--name <name>", "Project display name for a new project").option("--project-id <id>", "Stable project ID for controlled provisioning").option("--repository <id=absolute-path>", "Bind a member repository outside Git configuration", (value, previous) => [...previous, value], []).action(async function initAction(options) {
49087
47341
  result.code = await cmdKxmInit(runtimeFrom(ctx, this), options, {
49088
47342
  maybeOfferCompletionInstall,
49089
47343
  maybeOfferGuideSetup
49090
47344
  });
49091
47345
  });
49092
- const migrate = addGlobalOptions(program2.command("migrate").description("Plan, apply, and verify legacy JSON configuration migration"));
49093
- migrate.helpCommand("help", "Show migrate help");
49094
- addGlobalOptions(migrate.command("plan").description("Compute the deterministic legacy-to-KXM migration plan without writes")).action(async function migratePlanAction() {
49095
- result.code = await cmdKxmMigratePlan(runtimeFrom(ctx, this));
49096
- });
49097
- addGlobalOptions(migrate.command("apply").description("Install a reviewed migration with a hash-linked receipt")).option("--decisions <file>", "Reviewed kxm.migration-decision.v1 YAML file").option("--project-id <id>", "Stable project ID for controlled provisioning").option("--name <name>", "Project display name").action(async function migrateApplyAction(options) {
49098
- result.code = await cmdKxmMigrateApply(runtimeFrom(ctx, this), options);
49099
- });
49100
- addGlobalOptions(migrate.command("verify").description("Verify a migration receipt against current sources and target bundle")).action(async function migrateVerifyAction() {
49101
- result.code = await cmdKxmMigrateVerify(runtimeFrom(ctx, this));
49102
- });
49103
47346
  addGlobalOptions(program2.command("backup").description("Create a verified SQLite backup of all stores with a hashed manifest")).option("--out <dir>", "Directory to write backup and manifest").action(async function backupAction(options) {
49104
47347
  result.code = await cmdBackup(runtimeFrom(ctx, this), options);
49105
47348
  });
@@ -49556,7 +47799,7 @@ async function runCli(argv, env = process.env, io = { stdout: (text) => process.
49556
47799
  throw error;
49557
47800
  }
49558
47801
  }
49559
- if (process.argv[1] && resolve30(process.argv[1]) === fileURLToPath4(import.meta.url)) {
47802
+ if (process.argv[1] && resolve29(process.argv[1]) === fileURLToPath4(import.meta.url)) {
49560
47803
  const code = await runCli(process.argv.slice(2));
49561
47804
  process.exitCode = code;
49562
47805
  }