@kontextmind/kxm 0.7.4 → 0.7.6

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.
Files changed (40) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/docs/README.md +1 -0
  3. package/docs/adr/ADR-0002-browser-automation-steel-doks.md +103 -0
  4. package/docs/agent-skills.md +14 -0
  5. package/docs/browser-automation.md +116 -0
  6. package/docs/kb/how-credentials-retrieved-safely.md +31 -0
  7. package/docs/kb/how-to-capture-and-annotate-section.md +60 -0
  8. package/docs/kb/how-to-connect-playwright-to-steel.md +54 -0
  9. package/docs/kb/how-to-recover-expired-session-or-orphan.md +54 -0
  10. package/docs/kb/how-to-resume-after-mfa.md +28 -0
  11. package/docs/kb/how-to-take-over-session.md +32 -0
  12. package/docs/kb/why-authentication-disappeared.md +32 -0
  13. package/docs/kb/why-automation-opened-different-browser.md +32 -0
  14. package/docs/kb/why-session-viewer-cannot-control.md +31 -0
  15. package/docs/prompts/browser-annotate-feedback.md +41 -0
  16. package/docs/prompts/browser-diagnose-recover.md +38 -0
  17. package/docs/prompts/browser-explore.md +42 -0
  18. package/docs/prompts/browser-repro-fix.md +48 -0
  19. package/docs/prompts/browser-start.md +41 -0
  20. package/docs/prompts/browser-takeover.md +50 -0
  21. package/package.json +1 -1
  22. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  23. package/plugins/kxm/dist/cli.js +421 -155
  24. package/plugins/kxm/dist/mcp-server.js +1 -1
  25. package/plugins/kxm/dist/runtime.js +699 -16
  26. package/plugins/kxm/package.json +1 -1
  27. package/plugins/kxm/skills/hints.json +30 -0
  28. package/plugins/kxm/skills/kxm-browser-annotate/SKILL.md +90 -0
  29. package/plugins/kxm/skills/kxm-browser-auth/SKILL.md +47 -0
  30. package/plugins/kxm/skills/kxm-browser-diagnostics/SKILL.md +48 -0
  31. package/plugins/kxm/skills/kxm-browser-explore/SKILL.md +48 -0
  32. package/plugins/kxm/skills/kxm-browser-session/SKILL.md +94 -0
  33. package/plugins/kxm/skills/kxm-browser-takeover/SKILL.md +87 -0
  34. package/plugins/kxm/skills/kxm-browser-verify/SKILL.md +71 -0
  35. package/plugins/kxm/src/browser.ts +603 -0
  36. package/plugins/kxm/src/cli.ts +39 -0
  37. package/plugins/kxm/src/mcp-server.ts +1 -1
  38. package/plugins/kxm/src/modes.ts +348 -0
  39. package/plugins/kxm/src/runtime.ts +2 -0
  40. package/schemas/vnext/modes.schema.json +56 -0
@@ -7263,7 +7263,7 @@ var require_public_api = __commonJS({
7263
7263
  }
7264
7264
  return doc;
7265
7265
  }
7266
- function parse11(src, reviver, options) {
7266
+ function parse12(src, reviver, options) {
7267
7267
  let _reviver = void 0;
7268
7268
  if (typeof reviver === "function") {
7269
7269
  _reviver = reviver;
@@ -7304,7 +7304,7 @@ var require_public_api = __commonJS({
7304
7304
  return value.toString(options);
7305
7305
  return new Document.Document(value, _replacer, options).toString(options);
7306
7306
  }
7307
- exports.parse = parse11;
7307
+ exports.parse = parse12;
7308
7308
  exports.parseAllDocuments = parseAllDocuments;
7309
7309
  exports.parseDocument = parseDocument2;
7310
7310
  exports.stringify = stringify11;
@@ -11162,7 +11162,7 @@ var require_fast_uri = __commonJS({
11162
11162
  normalizeString(uri, options);
11163
11163
  } else if (typeof uri === "object") {
11164
11164
  uri = /** @type {T} */
11165
- parse11(serialize(uri, options), options);
11165
+ parse12(serialize(uri, options), options);
11166
11166
  }
11167
11167
  return uri;
11168
11168
  }
@@ -11202,8 +11202,8 @@ var require_fast_uri = __commonJS({
11202
11202
  function resolveComponent(base, relative7, options, skipNormalization) {
11203
11203
  const target = {};
11204
11204
  if (!skipNormalization) {
11205
- base = parse11(serialize(base, options), options);
11206
- relative7 = parse11(serialize(relative7, options), options);
11205
+ base = parse12(serialize(base, options), options);
11206
+ relative7 = parse12(serialize(relative7, options), options);
11207
11207
  }
11208
11208
  options = options || {};
11209
11209
  if (!options.tolerant && relative7.scheme) {
@@ -11495,7 +11495,7 @@ var require_fast_uri = __commonJS({
11495
11495
  }
11496
11496
  return { parsed, malformedAuthorityOrPort, malformedPercentEncoding, malformedSchemeSpecific, malformedHost, malformedScheme };
11497
11497
  }
11498
- function parse11(uri, opts) {
11498
+ function parse12(uri, opts) {
11499
11499
  return parseWithStatus(uri, opts).parsed;
11500
11500
  }
11501
11501
  function normalizeString(uri, opts) {
@@ -11532,7 +11532,7 @@ var require_fast_uri = __commonJS({
11532
11532
  resolveComponent,
11533
11533
  equal,
11534
11534
  serialize,
11535
- parse: parse11
11535
+ parse: parse12
11536
11536
  };
11537
11537
  module.exports = fastUri;
11538
11538
  module.exports.default = fastUri;
@@ -14778,9 +14778,9 @@ var require__ = __commonJS({
14778
14778
  // plugins/kxm/src/cli.ts
14779
14779
  import { spawn as spawn2, spawnSync as spawnSync8 } from "node:child_process";
14780
14780
  import { createHash as createHash16, createHmac as createHmac3, randomUUID as randomUUID12 } from "node:crypto";
14781
- import { existsSync as existsSync30, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync2, readFileSync as readFileSync28, readdirSync as readdirSync11, rmSync as rmSync10, writeFileSync as writeFileSync26 } from "node:fs";
14781
+ import { existsSync as existsSync31, mkdirSync as mkdirSync28, mkdtempSync as mkdtempSync2, readFileSync as readFileSync29, readdirSync as readdirSync11, rmSync as rmSync10, writeFileSync as writeFileSync26 } from "node:fs";
14782
14782
  import { homedir as homedir7, tmpdir as tmpdir2 } from "node:os";
14783
- import { basename as basename7, dirname as dirname21, join as join36, relative as relative6, resolve as resolve22 } from "node:path";
14783
+ import { basename as basename7, dirname as dirname21, join as join37, relative as relative6, resolve as resolve22 } from "node:path";
14784
14784
 
14785
14785
  // plugins/kxm/src/sqlite.ts
14786
14786
  import { createRequire } from "node:module";
@@ -20364,8 +20364,8 @@ function validateWorkflow(workflow, agents, models, repositories, gates, issues)
20364
20364
  const writable = Object.values(objectValue(step.repositories) ?? {}).filter((access) => access === "write").length;
20365
20365
  if (maxWriteRepositories > writable) issues.push(issue2("semantic", "write_repository_bound_invalid", file, `${stepId} maxWriteRepositories exceeds writable repository scope`));
20366
20366
  }
20367
- const join37 = objectValue(step.join);
20368
- const minimumPassed = join37 && typeof join37.minimumPassed === "number" ? join37.minimumPassed : void 0;
20367
+ const join38 = objectValue(step.join);
20368
+ const minimumPassed = join38 && typeof join38.minimumPassed === "number" ? join38.minimumPassed : void 0;
20369
20369
  if (minimumPassed !== void 0 && minimumPassed > maximum) issues.push(issue2("semantic", "join_impossible", file, `${stepId} minimumPassed exceeds assignment maximum`));
20370
20370
  const distinctBy = names(assignment?.distinctBy);
20371
20371
  if (distinctBy.length > 0) {
@@ -39388,7 +39388,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
39388
39388
  const maxAttempts = compileCountField(step.maxAttempts, 1, `${id}.maxAttempts`, id, sink);
39389
39389
  const timeoutMs = compileOptionalDuration(step.timeoutMs, `${id}.timeoutMs`, id, sink);
39390
39390
  const assignments = compileAssignments(step, id, agent, sink);
39391
- const join37 = compileJoin(step, id, sink);
39391
+ const join38 = compileJoin(step, id, sink);
39392
39392
  const requiredEvidence = compileEvidence(step, id, sink);
39393
39393
  const transitions = compileTransitions(step, id, index, stepIndex, sink);
39394
39394
  const outcomes = Object.keys(transitions).sort(compareCodeUnits6);
@@ -39416,7 +39416,7 @@ function compileStep(step, index, stepIndex, requirePlanHash, sink) {
39416
39416
  transitions: orderedTransitions,
39417
39417
  requiresPlanHash: requirePlanHash.includes(id),
39418
39418
  assignments,
39419
- join: join37
39419
+ join: join38
39420
39420
  };
39421
39421
  if (kind === "agent" || kind === "moa") {
39422
39422
  if (!agent) return void 0;
@@ -39466,15 +39466,15 @@ function compileAssignments(step, stepId, primaryAgentId, sink) {
39466
39466
  };
39467
39467
  }
39468
39468
  function compileJoin(step, stepId, sink) {
39469
- const join37 = objectValue2(step.join);
39470
- if (!join37) return { strategy: "all" };
39471
- const declared = stringValue3(join37.strategy);
39469
+ const join38 = objectValue2(step.join);
39470
+ if (!join38) return { strategy: "all" };
39471
+ const declared = stringValue3(join38.strategy);
39472
39472
  const strategy = declared && JOIN_STRATEGIES.has(declared) ? declared : "all";
39473
- const minimumPassed = compileOptionalCount(join37.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
39473
+ const minimumPassed = compileOptionalCount(join38.minimumPassed, `${stepId}.join.minimumPassed`, stepId, sink);
39474
39474
  const compiled = {
39475
39475
  strategy,
39476
39476
  ...minimumPassed !== void 0 ? { minimumPassed } : {},
39477
- ...typeof join37.cancelRemaining === "boolean" ? { cancelRemaining: join37.cancelRemaining } : {}
39477
+ ...typeof join38.cancelRemaining === "boolean" ? { cancelRemaining: join38.cancelRemaining } : {}
39478
39478
  };
39479
39479
  return compiled;
39480
39480
  }
@@ -41907,11 +41907,256 @@ function suggestWorkflowAndRoles(prompt, options = {}) {
41907
41907
  };
41908
41908
  }
41909
41909
 
41910
- // plugins/kxm/src/task-manager.ts
41910
+ // plugins/kxm/src/modes.ts
41911
41911
  var import_yaml13 = __toESM(require_dist(), 1);
41912
+ import { existsSync as existsSync27, readFileSync as readFileSync25 } from "node:fs";
41913
+ import { join as join33 } from "node:path";
41914
+ var DEFAULT_MODES_CONFIG = Object.freeze({
41915
+ schema: "kxm.modes.v1",
41916
+ majorModes: {
41917
+ coder: {
41918
+ description: "First-pass implementation, bug fixing, and test authoring",
41919
+ baseTools: ["read", "edit", "write", "bash"],
41920
+ contextFiles: ["AGENTS.md"],
41921
+ thinkingLevel: "medium",
41922
+ model: "grok/grok-4.6"
41923
+ },
41924
+ planner: {
41925
+ description: "High-level architectural planning, scoping, and decomposition",
41926
+ baseTools: ["read", "grep", "find"],
41927
+ contextFiles: ["plans/implementation-plan.md"],
41928
+ thinkingLevel: "high",
41929
+ model: "claude/fable"
41930
+ },
41931
+ auditor: {
41932
+ description: "Security, compliance, and code quality verification",
41933
+ baseTools: ["read", "grep"],
41934
+ contextFiles: ["SECURITY.md"],
41935
+ thinkingLevel: "high",
41936
+ model: "codex/gpt-5.6-sol"
41937
+ },
41938
+ browser: {
41939
+ description: "Web application exploration, screenshotting, and UI testing",
41940
+ baseTools: ["read", "bash"],
41941
+ contextFiles: ["docs/browser-automation.md"],
41942
+ thinkingLevel: "medium",
41943
+ model: "grok/grok-4.6"
41944
+ }
41945
+ },
41946
+ domains: {
41947
+ git: {
41948
+ description: "Git version control operations",
41949
+ tools: ["git_status", "git_diff", "git_commit"],
41950
+ promptSnippet: "Follow git branch conventions; never commit directly to main."
41951
+ },
41952
+ k8s: {
41953
+ description: "Kubernetes cluster inspection and deployment",
41954
+ tools: ["kubectl_get", "kubectl_describe"],
41955
+ promptSnippet: "Target local dev cluster; verify namespaces before mutating."
41956
+ },
41957
+ database: {
41958
+ description: "Database queries and schema verification",
41959
+ tools: ["sqlite_query", "sqlite_schema"],
41960
+ promptSnippet: "Database is SQLite at .kxm/state/kxm.db; use read-only queries."
41961
+ },
41962
+ browser: {
41963
+ description: "Remote Steel browser sessions and visual testing",
41964
+ tools: ["steel_session", "steel_scrape", "steel_screenshot"],
41965
+ promptSnippet: "Use Steel on DOKS for browser automation; invoke takeover on MFA."
41966
+ }
41967
+ }
41968
+ });
41969
+ function estimateTokens(charCount) {
41970
+ return Math.ceil(charCount / 3.8);
41971
+ }
41972
+ function loadModesConfig(projectRoot) {
41973
+ if (!projectRoot) {
41974
+ return DEFAULT_MODES_CONFIG;
41975
+ }
41976
+ const modesPath = join33(projectRoot, ".kxm", "modes.yaml");
41977
+ if (!existsSync27(modesPath)) {
41978
+ return DEFAULT_MODES_CONFIG;
41979
+ }
41980
+ try {
41981
+ const raw = readFileSync25(modesPath, "utf8");
41982
+ const parsed = (0, import_yaml13.parse)(raw);
41983
+ if (parsed && parsed.schema === "kxm.modes.v1" && parsed.majorModes) {
41984
+ return {
41985
+ schema: "kxm.modes.v1",
41986
+ majorModes: { ...DEFAULT_MODES_CONFIG.majorModes, ...parsed.majorModes },
41987
+ domains: { ...DEFAULT_MODES_CONFIG.domains, ...parsed.domains }
41988
+ };
41989
+ }
41990
+ } catch {
41991
+ }
41992
+ return DEFAULT_MODES_CONFIG;
41993
+ }
41994
+ function resolveActiveMode(config, majorModeName = "coder", domainNames = []) {
41995
+ const major = config.majorModes[majorModeName] || config.majorModes["coder"] || DEFAULT_MODES_CONFIG.majorModes["coder"];
41996
+ const tools = new Set(major.baseTools);
41997
+ const contextFiles = new Set(major.contextFiles || []);
41998
+ const promptSnippets = [];
41999
+ const enabledDomains = [];
42000
+ if (config.domains) {
42001
+ for (const dName of domainNames) {
42002
+ const d2 = config.domains[dName.toLowerCase().trim()];
42003
+ if (d2) {
42004
+ enabledDomains.push(dName.toLowerCase().trim());
42005
+ d2.tools.forEach((t) => tools.add(t));
42006
+ (d2.contextFiles || []).forEach((f) => contextFiles.add(f));
42007
+ if (d2.promptSnippet) {
42008
+ promptSnippets.push(d2.promptSnippet);
42009
+ }
42010
+ }
42011
+ }
42012
+ }
42013
+ return {
42014
+ majorMode: majorModeName,
42015
+ enabledDomains,
42016
+ tools: Array.from(tools),
42017
+ contextFiles: Array.from(contextFiles),
42018
+ promptSnippets,
42019
+ thinkingLevel: major.thinkingLevel,
42020
+ model: major.model || "grok/grok-4.6"
42021
+ };
42022
+ }
42023
+ function calculatePromptFootprint(resolved, projectRoot = process.cwd(), catalog) {
42024
+ const breakdown = [];
42025
+ const baseSystemPromptChars = 3200;
42026
+ breakdown.push({
42027
+ name: `Base System Prompt (${resolved.majorMode})`,
42028
+ chars: baseSystemPromptChars,
42029
+ estimatedTokens: estimateTokens(baseSystemPromptChars)
42030
+ });
42031
+ for (const relPath of resolved.contextFiles) {
42032
+ const fullPath = join33(projectRoot, relPath);
42033
+ let chars = 0;
42034
+ if (existsSync27(fullPath)) {
42035
+ try {
42036
+ chars = readFileSync25(fullPath, "utf8").length;
42037
+ } catch {
42038
+ chars = 0;
42039
+ }
42040
+ }
42041
+ if (chars === 0) {
42042
+ chars = 1500;
42043
+ }
42044
+ breakdown.push({
42045
+ name: `Context File: ${relPath}`,
42046
+ chars,
42047
+ estimatedTokens: estimateTokens(chars)
42048
+ });
42049
+ }
42050
+ if (resolved.promptSnippets.length > 0) {
42051
+ const snippetChars = resolved.promptSnippets.join("\n").length;
42052
+ breakdown.push({
42053
+ name: `Domain Prompts (${resolved.enabledDomains.join(", ")})`,
42054
+ chars: snippetChars,
42055
+ estimatedTokens: estimateTokens(snippetChars)
42056
+ });
42057
+ }
42058
+ const toolSchemaChars = resolved.tools.length * 650;
42059
+ breakdown.push({
42060
+ name: `Tool Schemas (${resolved.tools.length} active tools)`,
42061
+ chars: toolSchemaChars,
42062
+ estimatedTokens: estimateTokens(toolSchemaChars)
42063
+ });
42064
+ const totalChars = breakdown.reduce((sum, item) => sum + item.chars, 0);
42065
+ const totalTokens = breakdown.reduce((sum, item) => sum + item.estimatedTokens, 0);
42066
+ const maxContextWindow = 2e5;
42067
+ const contextWindowRatio = Math.round(totalTokens / maxContextWindow * 1e3) / 10;
42068
+ const cat = catalog || loadPriceCatalog(projectRoot);
42069
+ const rawModel = resolved.model || "grok/grok-4.6";
42070
+ const [prov, mod] = rawModel.includes("/") ? rawModel.split("/", 2) : [void 0, rawModel];
42071
+ const inputCost = cat ? calculateModelCost(cat, {
42072
+ model: mod || rawModel,
42073
+ ...prov !== void 0 ? { provider: prov } : {},
42074
+ tokensIn: totalTokens,
42075
+ tokensOut: 0,
42076
+ cacheReadTokens: 0,
42077
+ cacheWriteTokens: 0
42078
+ }) : void 0;
42079
+ const cacheReadCost = cat ? calculateModelCost(cat, {
42080
+ model: mod || rawModel,
42081
+ ...prov !== void 0 ? { provider: prov } : {},
42082
+ tokensIn: 0,
42083
+ tokensOut: 0,
42084
+ cacheReadTokens: totalTokens,
42085
+ cacheWriteTokens: 0
42086
+ }) : void 0;
42087
+ const outputEstimateCost = cat ? calculateModelCost(cat, {
42088
+ model: mod || rawModel,
42089
+ ...prov !== void 0 ? { provider: prov } : {},
42090
+ tokensIn: 0,
42091
+ tokensOut: 1e3,
42092
+ cacheReadTokens: 0,
42093
+ cacheWriteTokens: 0
42094
+ }) : void 0;
42095
+ return {
42096
+ majorMode: resolved.majorMode,
42097
+ enabledDomains: resolved.enabledDomains,
42098
+ model: resolved.model || "grok/grok-4.6",
42099
+ breakdown,
42100
+ totalChars,
42101
+ totalTokens,
42102
+ contextWindowRatio,
42103
+ projectedCost: {
42104
+ inputCostUsd: inputCost?.costUsd ?? null,
42105
+ cacheReadCostUsd: cacheReadCost?.costUsd ?? null,
42106
+ outputCostEstimateUsd: outputEstimateCost?.costUsd ?? null
42107
+ }
42108
+ };
42109
+ }
42110
+ function formatModesExplainReport(footprint) {
42111
+ const divider = "\u2550".repeat(60);
42112
+ const subDivider = "\u2500".repeat(60);
42113
+ let out = `
42114
+ ${divider}
42115
+ `;
42116
+ out += `KXM PRE-FLIGHT CONTEXT EXPLAIN
42117
+ `;
42118
+ out += `${divider}
42119
+ `;
42120
+ out += `Major Mode: ${footprint.majorMode}
42121
+ `;
42122
+ out += `Enabled Domains: ${footprint.enabledDomains.length > 0 ? footprint.enabledDomains.join(", ") : "(none)"}
42123
+ `;
42124
+ out += `Target Model: ${footprint.model}
42125
+
42126
+ `;
42127
+ out += `CONTEXT BREAKDOWN:
42128
+ `;
42129
+ for (const item of footprint.breakdown) {
42130
+ const padName = item.name.padEnd(38, " ");
42131
+ const tokenStr = `${item.estimatedTokens.toLocaleString()} tokens`.padStart(16, " ");
42132
+ out += ` \u2022 ${padName} ${tokenStr}
42133
+ `;
42134
+ }
42135
+ out += `${subDivider}
42136
+ `;
42137
+ const totalPad = `TOTAL PROMPT FOOTPRINT:`.padEnd(38, " ");
42138
+ const totalStr = `${footprint.totalTokens.toLocaleString()} tokens`.padStart(16, " ");
42139
+ out += ` ${totalPad} ${totalStr} (${footprint.contextWindowRatio}% of 200k window)
42140
+
42141
+ `;
42142
+ out += `PROJECTED COSTS (per turn):
42143
+ `;
42144
+ out += ` \u2022 Initial Turn Input Cost: ${footprint.projectedCost.inputCostUsd !== null ? `$${footprint.projectedCost.inputCostUsd.toFixed(4)}` : "unmetered/unknown"}
42145
+ `;
42146
+ out += ` \u2022 Subsequent Cache-Read Cost: ${footprint.projectedCost.cacheReadCostUsd !== null ? `$${footprint.projectedCost.cacheReadCostUsd.toFixed(4)} (approx 90% savings)` : "unmetered/unknown"}
42147
+ `;
42148
+ out += ` \u2022 Output Estimate (1k tokens): ${footprint.projectedCost.outputCostEstimateUsd !== null ? `$${footprint.projectedCost.outputCostEstimateUsd.toFixed(4)}` : "unmetered/unknown"}
42149
+ `;
42150
+ out += `${divider}
42151
+ `;
42152
+ return out;
42153
+ }
42154
+
42155
+ // plugins/kxm/src/task-manager.ts
42156
+ var import_yaml14 = __toESM(require_dist(), 1);
41912
42157
  import { randomUUID as randomUUID10 } from "node:crypto";
41913
- import { existsSync as existsSync27, mkdirSync as mkdirSync25, readFileSync as readFileSync25, readdirSync as readdirSync8, writeFileSync as writeFileSync23 } from "node:fs";
41914
- import { join as join33, resolve as resolve19 } from "node:path";
42158
+ import { existsSync as existsSync28, mkdirSync as mkdirSync25, readFileSync as readFileSync26, readdirSync as readdirSync8, writeFileSync as writeFileSync23 } from "node:fs";
42159
+ import { join as join34, resolve as resolve19 } from "node:path";
41915
42160
  var GOAL_SCHEMA = "kxm.goal.v1";
41916
42161
  var TASK_SCHEMA = "kxm.task.v1";
41917
42162
  function goalsDirectory(repoRoot3) {
@@ -41936,20 +42181,20 @@ function createGoal(repoRoot3, input) {
41936
42181
  createdAt: now,
41937
42182
  updatedAt: now
41938
42183
  };
41939
- const filePath = join33(dir, `${id}.yaml`);
41940
- writeFileSync23(filePath, (0, import_yaml13.stringify)(record).trim() + "\n", "utf8");
42184
+ const filePath = join34(dir, `${id}.yaml`);
42185
+ writeFileSync23(filePath, (0, import_yaml14.stringify)(record).trim() + "\n", "utf8");
41941
42186
  return record;
41942
42187
  }
41943
42188
  function listGoals(repoRoot3) {
41944
42189
  const dir = goalsDirectory(repoRoot3);
41945
- if (!existsSync27(dir)) return [];
42190
+ if (!existsSync28(dir)) return [];
41946
42191
  const goals = [];
41947
42192
  const entries = readdirSync8(dir, { withFileTypes: true });
41948
42193
  for (const entry of entries) {
41949
42194
  if (entry.isFile() && (entry.name.endsWith(".yaml") || entry.name.endsWith(".yml"))) {
41950
42195
  try {
41951
- const text = readFileSync25(join33(dir, entry.name), "utf8");
41952
- const doc = (0, import_yaml13.parse)(text);
42196
+ const text = readFileSync26(join34(dir, entry.name), "utf8");
42197
+ const doc = (0, import_yaml14.parse)(text);
41953
42198
  if (doc && doc.schema === GOAL_SCHEMA) goals.push(doc);
41954
42199
  } catch {
41955
42200
  }
@@ -41981,20 +42226,20 @@ function createTask(repoRoot3, input) {
41981
42226
  createdAt: now,
41982
42227
  updatedAt: now
41983
42228
  };
41984
- const filePath = join33(dir, `${id}.yaml`);
41985
- writeFileSync23(filePath, (0, import_yaml13.stringify)(record).trim() + "\n", "utf8");
42229
+ const filePath = join34(dir, `${id}.yaml`);
42230
+ writeFileSync23(filePath, (0, import_yaml14.stringify)(record).trim() + "\n", "utf8");
41986
42231
  return record;
41987
42232
  }
41988
42233
  function listTasks(repoRoot3, options = {}) {
41989
42234
  const dir = tasksDirectory(repoRoot3);
41990
- if (!existsSync27(dir)) return [];
42235
+ if (!existsSync28(dir)) return [];
41991
42236
  const tasks = [];
41992
42237
  const entries = readdirSync8(dir, { withFileTypes: true });
41993
42238
  for (const entry of entries) {
41994
42239
  if (entry.isFile() && (entry.name.endsWith(".yaml") || entry.name.endsWith(".yml"))) {
41995
42240
  try {
41996
- const text = readFileSync25(join33(dir, entry.name), "utf8");
41997
- const doc = (0, import_yaml13.parse)(text);
42241
+ const text = readFileSync26(join34(dir, entry.name), "utf8");
42242
+ const doc = (0, import_yaml14.parse)(text);
41998
42243
  if (doc && doc.schema === TASK_SCHEMA) {
41999
42244
  if (options.goalId && doc.goalId !== options.goalId) continue;
42000
42245
  if (options.status && doc.status !== options.status) continue;
@@ -42008,11 +42253,11 @@ function listTasks(repoRoot3, options = {}) {
42008
42253
  }
42009
42254
  function getTask(repoRoot3, taskId) {
42010
42255
  const dir = tasksDirectory(repoRoot3);
42011
- const filePath = join33(dir, `${taskId}.yaml`);
42012
- if (!existsSync27(filePath)) return void 0;
42256
+ const filePath = join34(dir, `${taskId}.yaml`);
42257
+ if (!existsSync28(filePath)) return void 0;
42013
42258
  try {
42014
- const text = readFileSync25(filePath, "utf8");
42015
- const doc = (0, import_yaml13.parse)(text);
42259
+ const text = readFileSync26(filePath, "utf8");
42260
+ const doc = (0, import_yaml14.parse)(text);
42016
42261
  return doc && doc.schema === TASK_SCHEMA ? doc : void 0;
42017
42262
  } catch {
42018
42263
  return void 0;
@@ -42026,8 +42271,8 @@ function updateTaskStatus(repoRoot3, taskId, status, options = {}) {
42026
42271
  task.status = status;
42027
42272
  if (options.workflowRunId) task.workflowRunId = options.workflowRunId;
42028
42273
  task.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
42029
- const filePath = join33(tasksDirectory(repoRoot3), `${taskId}.yaml`);
42030
- writeFileSync23(filePath, (0, import_yaml13.stringify)(task).trim() + "\n", "utf8");
42274
+ const filePath = join34(tasksDirectory(repoRoot3), `${taskId}.yaml`);
42275
+ writeFileSync23(filePath, (0, import_yaml14.stringify)(task).trim() + "\n", "utf8");
42031
42276
  return task;
42032
42277
  }
42033
42278
  function syncTaskWithTracker(repoRoot3, taskId, options = {}) {
@@ -42042,13 +42287,13 @@ function syncTaskWithTracker(repoRoot3, taskId, options = {}) {
42042
42287
  task.trackerSync.syncStatus = "synced";
42043
42288
  task.trackerSync.lastSyncedAt = now;
42044
42289
  task.updatedAt = now;
42045
- const filePath = join33(tasksDirectory(repoRoot3), `${taskId}.yaml`);
42046
- writeFileSync23(filePath, (0, import_yaml13.stringify)(task).trim() + "\n", "utf8");
42290
+ const filePath = join34(tasksDirectory(repoRoot3), `${taskId}.yaml`);
42291
+ writeFileSync23(filePath, (0, import_yaml14.stringify)(task).trim() + "\n", "utf8");
42047
42292
  return task;
42048
42293
  }
42049
42294
 
42050
42295
  // plugins/kxm/src/cli.ts
42051
- var import_yaml16 = __toESM(require_dist(), 1);
42296
+ var import_yaml17 = __toESM(require_dist(), 1);
42052
42297
 
42053
42298
  // plugins/kxm/src/studio-layout.ts
42054
42299
  import { createServer } from "node:http";
@@ -42812,9 +43057,9 @@ function createStudioServer(options = {}) {
42812
43057
  }
42813
43058
 
42814
43059
  // plugins/kxm/src/role.ts
42815
- var import_yaml14 = __toESM(require_dist(), 1);
42816
- import { existsSync as existsSync28, mkdirSync as mkdirSync26, readdirSync as readdirSync9, readFileSync as readFileSync26, rmSync as rmSync8, writeFileSync as writeFileSync24 } from "node:fs";
42817
- import { join as join34 } from "node:path";
43060
+ var import_yaml15 = __toESM(require_dist(), 1);
43061
+ import { existsSync as existsSync29, mkdirSync as mkdirSync26, readdirSync as readdirSync9, readFileSync as readFileSync27, rmSync as rmSync8, writeFileSync as writeFileSync24 } from "node:fs";
43062
+ import { join as join35 } from "node:path";
42818
43063
  var KXM_ROLE_SCHEMA = "kxm.role.v1";
42819
43064
  var DEFAULT_ROLES = {
42820
43065
  writer: {
@@ -42893,22 +43138,22 @@ var DEFAULT_ROLES = {
42893
43138
  };
42894
43139
  function rolesDirectory(scope, repoRoot3 = process.cwd(), userConfigDir) {
42895
43140
  if (scope === "global") {
42896
- return join34(userConfigDirectory(userConfigDir), "roles");
43141
+ return join35(userConfigDirectory(userConfigDir), "roles");
42897
43142
  }
42898
- return join34(repoConfigDirectory(repoRoot3), "roles");
43143
+ return join35(repoConfigDirectory(repoRoot3), "roles");
42899
43144
  }
42900
43145
  function ensureRolesDirectory(scope, repoRoot3 = process.cwd(), userConfigDir) {
42901
43146
  const dir = rolesDirectory(scope, repoRoot3, userConfigDir);
42902
- if (!existsSync28(dir)) {
43147
+ if (!existsSync29(dir)) {
42903
43148
  mkdirSync26(dir, { recursive: true });
42904
43149
  }
42905
43150
  return dir;
42906
43151
  }
42907
43152
  function parseRoleFile(filePath) {
42908
- if (!existsSync28(filePath)) return void 0;
43153
+ if (!existsSync29(filePath)) return void 0;
42909
43154
  try {
42910
- const raw = readFileSync26(filePath, "utf8");
42911
- const parsed = (0, import_yaml14.parse)(raw);
43155
+ const raw = readFileSync27(filePath, "utf8");
43156
+ const parsed = (0, import_yaml15.parse)(raw);
42912
43157
  if (!parsed || typeof parsed !== "object" || parsed.schema !== KXM_ROLE_SCHEMA) {
42913
43158
  return void 0;
42914
43159
  }
@@ -42934,10 +43179,10 @@ function listRoles(options = {}) {
42934
43179
  const globalDir = rolesDirectory("global", repoRoot3, options.userConfigDir);
42935
43180
  const localDir = rolesDirectory("local", repoRoot3, options.userConfigDir);
42936
43181
  const localRoles = /* @__PURE__ */ new Map();
42937
- if (scopeFilter !== "global" && existsSync28(localDir)) {
43182
+ if (scopeFilter !== "global" && existsSync29(localDir)) {
42938
43183
  for (const entry of readdirSync9(localDir)) {
42939
43184
  if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
42940
- const filePath = join34(localDir, entry);
43185
+ const filePath = join35(localDir, entry);
42941
43186
  const parsed = parseRoleFile(filePath);
42942
43187
  if (parsed) {
42943
43188
  localRoles.set(parsed.id, { role: parsed, filePath });
@@ -42946,10 +43191,10 @@ function listRoles(options = {}) {
42946
43191
  }
42947
43192
  }
42948
43193
  const globalRoles = /* @__PURE__ */ new Map();
42949
- if (scopeFilter !== "local" && existsSync28(globalDir)) {
43194
+ if (scopeFilter !== "local" && existsSync29(globalDir)) {
42950
43195
  for (const entry of readdirSync9(globalDir)) {
42951
43196
  if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
42952
- const filePath = join34(globalDir, entry);
43197
+ const filePath = join35(globalDir, entry);
42953
43198
  const parsed = parseRoleFile(filePath);
42954
43199
  if (parsed) {
42955
43200
  globalRoles.set(parsed.id, { role: parsed, filePath });
@@ -42997,13 +43242,13 @@ function getRole(roleId, options = {}) {
42997
43242
  const repoRoot3 = options.repoRoot ?? process.cwd();
42998
43243
  if (scope !== "global") {
42999
43244
  const localDir = rolesDirectory("local", repoRoot3, options.userConfigDir);
43000
- const localFile = join34(localDir, `${roleId}.yaml`);
43245
+ const localFile = join35(localDir, `${roleId}.yaml`);
43001
43246
  const parsed = parseRoleFile(localFile);
43002
43247
  if (parsed) return { role: parsed, scope: "local", filePath: localFile };
43003
43248
  }
43004
43249
  if (scope !== "local") {
43005
43250
  const globalDir = rolesDirectory("global", repoRoot3, options.userConfigDir);
43006
- const globalFile = join34(globalDir, `${roleId}.yaml`);
43251
+ const globalFile = join35(globalDir, `${roleId}.yaml`);
43007
43252
  const parsed = parseRoleFile(globalFile);
43008
43253
  if (parsed) return { role: parsed, scope: "global", filePath: globalFile };
43009
43254
  }
@@ -43013,19 +43258,19 @@ function addRole(role, options = {}) {
43013
43258
  const scope = options.scope ?? "local";
43014
43259
  const repoRoot3 = options.repoRoot ?? process.cwd();
43015
43260
  const dir = ensureRolesDirectory(scope, repoRoot3, options.userConfigDir);
43016
- const filePath = join34(dir, `${role.id}.yaml`);
43017
- if (existsSync28(filePath) && !options.overwrite) {
43261
+ const filePath = join35(dir, `${role.id}.yaml`);
43262
+ if (existsSync29(filePath) && !options.overwrite) {
43018
43263
  throw new Error(`role_already_exists: role '${role.id}' already exists at ${filePath}`);
43019
43264
  }
43020
- writeFileSync24(filePath, (0, import_yaml14.stringify)(role), "utf8");
43265
+ writeFileSync24(filePath, (0, import_yaml15.stringify)(role), "utf8");
43021
43266
  return { id: role.id, filePath, scope };
43022
43267
  }
43023
43268
  function removeRole(roleId, options = {}) {
43024
43269
  const scope = options.scope ?? "local";
43025
43270
  const repoRoot3 = options.repoRoot ?? process.cwd();
43026
43271
  const dir = rolesDirectory(scope, repoRoot3, options.userConfigDir);
43027
- const filePath = join34(dir, `${roleId}.yaml`);
43028
- if (!existsSync28(filePath)) {
43272
+ const filePath = join35(dir, `${roleId}.yaml`);
43273
+ if (!existsSync29(filePath)) {
43029
43274
  throw new Error(`role_not_found: role '${roleId}' not found in ${scope} directory (${filePath})`);
43030
43275
  }
43031
43276
  rmSync8(filePath);
@@ -43046,8 +43291,8 @@ function modifyRole(roleId, updates, options = {}) {
43046
43291
  const scope = options.scope ?? target.scope;
43047
43292
  const repoRoot3 = options.repoRoot ?? process.cwd();
43048
43293
  const dir = ensureRolesDirectory(scope, repoRoot3, options.userConfigDir);
43049
- const filePath = join34(dir, `${roleId}.yaml`);
43050
- writeFileSync24(filePath, (0, import_yaml14.stringify)(updated), "utf8");
43294
+ const filePath = join35(dir, `${roleId}.yaml`);
43295
+ writeFileSync24(filePath, (0, import_yaml15.stringify)(updated), "utf8");
43051
43296
  return { id: roleId, role: updated, filePath, scope };
43052
43297
  }
43053
43298
  var KXM_ROLE_HOSTS_SCHEMA = "kxm.role-hosts.v1";
@@ -43095,19 +43340,19 @@ var DEFAULT_ROLE_SEATS = {
43095
43340
  };
43096
43341
  function findExistingRoleHostsFile(scope, repoRoot3 = process.cwd(), userConfigDir) {
43097
43342
  const dir = scope === "global" ? userConfigDirectory(userConfigDir) : repoConfigDirectory(repoRoot3);
43098
- const yamlPath = join34(dir, "role-hosts.yaml");
43099
- const ymlPath = join34(dir, "role-hosts.yml");
43100
- const jsonPath = join34(dir, "role-hosts.json");
43101
- if (existsSync28(yamlPath)) return { filePath: yamlPath, exists: true, format: "yaml" };
43102
- if (existsSync28(ymlPath)) return { filePath: ymlPath, exists: true, format: "yaml" };
43103
- if (existsSync28(jsonPath)) return { filePath: jsonPath, exists: true, format: "json" };
43343
+ const yamlPath = join35(dir, "role-hosts.yaml");
43344
+ const ymlPath = join35(dir, "role-hosts.yml");
43345
+ const jsonPath = join35(dir, "role-hosts.json");
43346
+ if (existsSync29(yamlPath)) return { filePath: yamlPath, exists: true, format: "yaml" };
43347
+ if (existsSync29(ymlPath)) return { filePath: ymlPath, exists: true, format: "yaml" };
43348
+ if (existsSync29(jsonPath)) return { filePath: jsonPath, exists: true, format: "json" };
43104
43349
  return { filePath: yamlPath, exists: false, format: "yaml" };
43105
43350
  }
43106
43351
  function parseRoleHostsFile(filePath) {
43107
- if (!existsSync28(filePath)) return void 0;
43352
+ if (!existsSync29(filePath)) return void 0;
43108
43353
  try {
43109
- const raw = readFileSync26(filePath, "utf8");
43110
- const parsed = (0, import_yaml14.parse)(raw);
43354
+ const raw = readFileSync27(filePath, "utf8");
43355
+ const parsed = (0, import_yaml15.parse)(raw);
43111
43356
  if (!parsed || typeof parsed !== "object") return void 0;
43112
43357
  if (parsed.schema && parsed.schema !== KXM_ROLE_HOSTS_SCHEMA) return void 0;
43113
43358
  return {
@@ -43178,17 +43423,17 @@ function saveRoleHostsConfig(config, options = {}) {
43178
43423
  const scope = options.scope ?? "local";
43179
43424
  const repoRoot3 = options.repoRoot ?? process.cwd();
43180
43425
  const dir = scope === "global" ? userConfigDirectory(options.userConfigDir) : repoConfigDirectory(repoRoot3);
43181
- if (!existsSync28(dir)) {
43426
+ if (!existsSync29(dir)) {
43182
43427
  mkdirSync26(dir, { recursive: true });
43183
43428
  }
43184
43429
  const format = options.format ?? "yaml";
43185
- const filePath = join34(dir, format === "json" ? "role-hosts.json" : "role-hosts.yaml");
43430
+ const filePath = join35(dir, format === "json" ? "role-hosts.json" : "role-hosts.yaml");
43186
43431
  const payload = {
43187
43432
  schema: KXM_ROLE_HOSTS_SCHEMA,
43188
43433
  seats: config.seats ?? {},
43189
43434
  ...config.hostProviders && Object.keys(config.hostProviders).length > 0 ? { hostProviders: config.hostProviders } : {}
43190
43435
  };
43191
- const content = format === "json" ? JSON.stringify(payload, null, 2) + "\n" : (0, import_yaml14.stringify)(payload);
43436
+ const content = format === "json" ? JSON.stringify(payload, null, 2) + "\n" : (0, import_yaml15.stringify)(payload);
43192
43437
  writeFileSync24(filePath, content, "utf8");
43193
43438
  return { filePath, scope };
43194
43439
  }
@@ -43273,9 +43518,9 @@ function resolveRoleSeat(seatId, options = {}) {
43273
43518
  }
43274
43519
 
43275
43520
  // plugins/kxm/src/workflow-manager.ts
43276
- var import_yaml15 = __toESM(require_dist(), 1);
43277
- import { existsSync as existsSync29, mkdirSync as mkdirSync27, readdirSync as readdirSync10, readFileSync as readFileSync27, rmSync as rmSync9, writeFileSync as writeFileSync25 } from "node:fs";
43278
- import { join as join35 } from "node:path";
43521
+ var import_yaml16 = __toESM(require_dist(), 1);
43522
+ import { existsSync as existsSync30, mkdirSync as mkdirSync27, readdirSync as readdirSync10, readFileSync as readFileSync28, rmSync as rmSync9, writeFileSync as writeFileSync25 } from "node:fs";
43523
+ import { join as join36 } from "node:path";
43279
43524
  var WORKFLOW_TEMPLATES = {
43280
43525
  "implement-and-verify": {
43281
43526
  schema: "kxm.workflow.v1",
@@ -43426,22 +43671,22 @@ var WORKFLOW_TEMPLATES = {
43426
43671
  var DEFAULT_WORKFLOW_TEMPLATE = WORKFLOW_TEMPLATES["implement-and-verify"];
43427
43672
  function workflowsDirectory(scope, repoRoot3 = process.cwd(), userConfigDir) {
43428
43673
  if (scope === "global") {
43429
- return join35(userConfigDirectory(userConfigDir), "workflows");
43674
+ return join36(userConfigDirectory(userConfigDir), "workflows");
43430
43675
  }
43431
- return join35(repoConfigDirectory(repoRoot3), "workflows");
43676
+ return join36(repoConfigDirectory(repoRoot3), "workflows");
43432
43677
  }
43433
43678
  function ensureWorkflowsDirectory(scope, repoRoot3 = process.cwd(), userConfigDir) {
43434
43679
  const dir = workflowsDirectory(scope, repoRoot3, userConfigDir);
43435
- if (!existsSync29(dir)) {
43680
+ if (!existsSync30(dir)) {
43436
43681
  mkdirSync27(dir, { recursive: true });
43437
43682
  }
43438
43683
  return dir;
43439
43684
  }
43440
43685
  function parseWorkflowFile(filePath) {
43441
- if (!existsSync29(filePath)) return void 0;
43686
+ if (!existsSync30(filePath)) return void 0;
43442
43687
  try {
43443
- const raw = readFileSync27(filePath, "utf8");
43444
- const parsed = (0, import_yaml15.parse)(raw);
43688
+ const raw = readFileSync28(filePath, "utf8");
43689
+ const parsed = (0, import_yaml16.parse)(raw);
43445
43690
  if (!parsed || typeof parsed !== "object") return void 0;
43446
43691
  return parsed;
43447
43692
  } catch {
@@ -43454,10 +43699,10 @@ function listWorkflowDefinitions(options = {}) {
43454
43699
  const globalDir = workflowsDirectory("global", repoRoot3, options.userConfigDir);
43455
43700
  const localDir = workflowsDirectory("local", repoRoot3, options.userConfigDir);
43456
43701
  const localDefs = /* @__PURE__ */ new Map();
43457
- if (scopeFilter !== "global" && existsSync29(localDir)) {
43702
+ if (scopeFilter !== "global" && existsSync30(localDir)) {
43458
43703
  for (const entry of readdirSync10(localDir)) {
43459
43704
  if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
43460
- const filePath = join35(localDir, entry);
43705
+ const filePath = join36(localDir, entry);
43461
43706
  const def = parseWorkflowFile(filePath);
43462
43707
  if (def) {
43463
43708
  const id = def.id || entry.replace(/\.ya?ml$/i, "");
@@ -43467,10 +43712,10 @@ function listWorkflowDefinitions(options = {}) {
43467
43712
  }
43468
43713
  }
43469
43714
  const globalDefs = /* @__PURE__ */ new Map();
43470
- if (scopeFilter !== "local" && existsSync29(globalDir)) {
43715
+ if (scopeFilter !== "local" && existsSync30(globalDir)) {
43471
43716
  for (const entry of readdirSync10(globalDir)) {
43472
43717
  if (entry.endsWith(".yaml") || entry.endsWith(".yml")) {
43473
- const filePath = join35(globalDir, entry);
43718
+ const filePath = join36(globalDir, entry);
43474
43719
  const def = parseWorkflowFile(filePath);
43475
43720
  if (def) {
43476
43721
  const id = def.id || entry.replace(/\.ya?ml$/i, "");
@@ -43526,13 +43771,13 @@ function getWorkflowDefinition(workflowId, options = {}) {
43526
43771
  const repoRoot3 = options.repoRoot ?? process.cwd();
43527
43772
  if (scope !== "global") {
43528
43773
  const localDir = workflowsDirectory("local", repoRoot3, options.userConfigDir);
43529
- const localFile = join35(localDir, `${workflowId}.yaml`);
43774
+ const localFile = join36(localDir, `${workflowId}.yaml`);
43530
43775
  const parsed = parseWorkflowFile(localFile);
43531
43776
  if (parsed) return { workflow: parsed, scope: "local", filePath: localFile };
43532
43777
  }
43533
43778
  if (scope !== "local") {
43534
43779
  const globalDir = workflowsDirectory("global", repoRoot3, options.userConfigDir);
43535
- const globalFile = join35(globalDir, `${workflowId}.yaml`);
43780
+ const globalFile = join36(globalDir, `${workflowId}.yaml`);
43536
43781
  const parsed = parseWorkflowFile(globalFile);
43537
43782
  if (parsed) return { workflow: parsed, scope: "global", filePath: globalFile };
43538
43783
  }
@@ -43542,11 +43787,11 @@ function addWorkflowDefinition(workflowId, content = DEFAULT_WORKFLOW_TEMPLATE,
43542
43787
  const scope = options.scope ?? "local";
43543
43788
  const repoRoot3 = options.repoRoot ?? process.cwd();
43544
43789
  const dir = ensureWorkflowsDirectory(scope, repoRoot3, options.userConfigDir);
43545
- const filePath = join35(dir, `${workflowId}.yaml`);
43546
- if (existsSync29(filePath) && !options.overwrite) {
43790
+ const filePath = join36(dir, `${workflowId}.yaml`);
43791
+ if (existsSync30(filePath) && !options.overwrite) {
43547
43792
  throw new Error(`workflow_already_exists: workflow '${workflowId}' already exists at ${filePath}`);
43548
43793
  }
43549
- const payload = typeof content === "string" ? content : (0, import_yaml15.stringify)(content);
43794
+ const payload = typeof content === "string" ? content : (0, import_yaml16.stringify)(content);
43550
43795
  writeFileSync25(filePath, payload, "utf8");
43551
43796
  return { id: workflowId, filePath, scope };
43552
43797
  }
@@ -43554,8 +43799,8 @@ function removeWorkflowDefinition(workflowId, options = {}) {
43554
43799
  const scope = options.scope ?? "local";
43555
43800
  const repoRoot3 = options.repoRoot ?? process.cwd();
43556
43801
  const dir = workflowsDirectory(scope, repoRoot3, options.userConfigDir);
43557
- const filePath = join35(dir, `${workflowId}.yaml`);
43558
- if (!existsSync29(filePath)) {
43802
+ const filePath = join36(dir, `${workflowId}.yaml`);
43803
+ if (!existsSync30(filePath)) {
43559
43804
  throw new Error(`workflow_not_found: workflow '${workflowId}' not found in ${scope} directory (${filePath})`);
43560
43805
  }
43561
43806
  rmSync9(filePath);
@@ -43573,8 +43818,8 @@ function modifyWorkflowDefinition(workflowId, updates, options = {}) {
43573
43818
  const scope = options.scope ?? target.scope;
43574
43819
  const repoRoot3 = options.repoRoot ?? process.cwd();
43575
43820
  const dir = ensureWorkflowsDirectory(scope, repoRoot3, options.userConfigDir);
43576
- const filePath = join35(dir, `${workflowId}.yaml`);
43577
- writeFileSync25(filePath, (0, import_yaml15.stringify)(updated), "utf8");
43821
+ const filePath = join36(dir, `${workflowId}.yaml`);
43822
+ writeFileSync25(filePath, (0, import_yaml16.stringify)(updated), "utf8");
43578
43823
  return { id: workflowId, workflow: updated, filePath, scope };
43579
43824
  }
43580
43825
 
@@ -43596,7 +43841,7 @@ var USAGE_ERROR_CODES = /* @__PURE__ */ new Set([
43596
43841
  ]);
43597
43842
  function spawnScript(scriptName, extraEnv = {}) {
43598
43843
  return new Promise((resolveExit) => {
43599
- const child = spawn2(process.execPath, [join36(repoRoot2, "scripts", scriptName)], {
43844
+ const child = spawn2(process.execPath, [join37(repoRoot2, "scripts", scriptName)], {
43600
43845
  stdio: "inherit",
43601
43846
  env: { ...process.env, ...extraEnv }
43602
43847
  });
@@ -43686,10 +43931,10 @@ function workspaceDirs(cwd, workspaceFlag, env) {
43686
43931
  return {
43687
43932
  workdir,
43688
43933
  workspace,
43689
- config: derive ? join36(workspace, "config") : resolve22(workdir, env.KXM_CONFIG_DIR?.trim() || join36(workspace, "config")),
43690
- logs: derive ? join36(workspace, "logs") : resolve22(workdir, env.KXM_LOGS_DIR?.trim() || join36(workspace, "logs")),
43691
- assets: derive ? join36(workspace, "assets") : resolve22(workdir, env.KXM_ASSETS_DIR?.trim() || join36(workspace, "assets")),
43692
- state: derive ? join36(workspace, "state") : resolve22(workdir, env.KXM_STATE_DIR?.trim() || join36(workspace, "state"))
43934
+ config: derive ? join37(workspace, "config") : resolve22(workdir, env.KXM_CONFIG_DIR?.trim() || join37(workspace, "config")),
43935
+ logs: derive ? join37(workspace, "logs") : resolve22(workdir, env.KXM_LOGS_DIR?.trim() || join37(workspace, "logs")),
43936
+ assets: derive ? join37(workspace, "assets") : resolve22(workdir, env.KXM_ASSETS_DIR?.trim() || join37(workspace, "assets")),
43937
+ state: derive ? join37(workspace, "state") : resolve22(workdir, env.KXM_STATE_DIR?.trim() || join37(workspace, "state"))
43693
43938
  };
43694
43939
  }
43695
43940
  function maskEnvName(name) {
@@ -43749,7 +43994,7 @@ async function hubGet(url, fetchImpl) {
43749
43994
  }
43750
43995
  }
43751
43996
  function localWorkflowSnapshot(dataPath, runId) {
43752
- if (!existsSync30(dataPath)) throw new Error("state_database_not_found");
43997
+ if (!existsSync31(dataPath)) throw new Error("state_database_not_found");
43753
43998
  const database = new DatabaseSync(dataPath, { readOnly: true });
43754
43999
  try {
43755
44000
  const rows = runId ? database.prepare("SELECT record FROM workflow_runs WHERE id = ?").all(runId) : database.prepare("SELECT record FROM workflow_runs ORDER BY rowid DESC LIMIT 200").all();
@@ -43855,7 +44100,7 @@ function activeWorkflowDefinition(runtime, definitionId) {
43855
44100
  let raw;
43856
44101
  if (file) {
43857
44102
  try {
43858
- raw = readFileSync28(resolve22(runtime.cwd, file), "utf8");
44103
+ raw = readFileSync29(resolve22(runtime.cwd, file), "utf8");
43859
44104
  } catch {
43860
44105
  throw new Error("workflow definition file is unavailable");
43861
44106
  }
@@ -44138,7 +44383,7 @@ async function cmdBackup(runtime, options) {
44138
44383
  const summary = [
44139
44384
  `Created SQLite backup with ${manifest.stores.length} store(s):`,
44140
44385
  ...manifest.stores.map((s) => ` - ${s.storeId}: ${s.sourcePath} -> ${s.backupFile} (schema v${s.schemaVersion}, ${s.bytes} bytes, sha256 ${s.sha256.slice(0, 12)}...)`),
44141
- `Manifest: ${join36(outDir, "manifest.json")}`
44386
+ `Manifest: ${join37(outDir, "manifest.json")}`
44142
44387
  ].join("\n");
44143
44388
  print(runtime.io, runtime.json, payload, summary);
44144
44389
  return 0;
@@ -44429,9 +44674,9 @@ function npmGlobalRootFn(runtime) {
44429
44674
  };
44430
44675
  }
44431
44676
  function warnIgnoredProjectUpdateYaml(runtime) {
44432
- const projectFile = join36(runtime.dirs.workspace, "update.yaml");
44433
- if (!existsSync30(projectFile)) return;
44434
- const userFile = join36(vnextUserStateRoot({ env: runtime.env }), "update.yaml");
44677
+ const projectFile = join37(runtime.dirs.workspace, "update.yaml");
44678
+ if (!existsSync31(projectFile)) return;
44679
+ const userFile = join37(vnextUserStateRoot({ env: runtime.env }), "update.yaml");
44435
44680
  runtime.io.stderr(`kxm: ignoring .kxm/update.yaml in ${runtime.dirs.workdir}; update settings are read only from ${userFile}
44436
44681
  `);
44437
44682
  }
@@ -44460,7 +44705,7 @@ function applyKxmPackageUpdate(runtime, notice) {
44460
44705
  detail: `release v${notice.latest} has no sha256 digest for ${name}; refusing to install`
44461
44706
  };
44462
44707
  }
44463
- const releaseDir = mkdtempSync2(join36(tmpdir2(), "kxm-pkg-update-"));
44708
+ const releaseDir = mkdtempSync2(join37(tmpdir2(), "kxm-pkg-update-"));
44464
44709
  try {
44465
44710
  const planned = planKxmPackageUpdate(notice.source, notice.latest, releaseDir, notice.asset);
44466
44711
  if (runtime.dryRun) {
@@ -44469,7 +44714,7 @@ function applyKxmPackageUpdate(runtime, notice) {
44469
44714
  for (const step of planned) {
44470
44715
  if (step.kind === "verify") {
44471
44716
  if (!verifyReleaseAssetDigest(step.path, step.sha256)) {
44472
- const actual = existsSync30(step.path) ? createHash16("sha256").update(readFileSync28(step.path)).digest("hex") : "missing";
44717
+ const actual = existsSync31(step.path) ? createHash16("sha256").update(readFileSync29(step.path)).digest("hex") : "missing";
44473
44718
  return {
44474
44719
  ok: false,
44475
44720
  error: "release_digest_mismatch",
@@ -44555,6 +44800,22 @@ async function cmdModelInventoryRefresh(runtime) {
44555
44800
  print(runtime.io, runtime.json, { ok: !failed, command: "models inventory refresh", output: ".kxm/models/inventory.yaml", ...inventory }, `wrote ${inventory.models.length} models to .kxm/models/inventory.yaml`);
44556
44801
  return failed ? 1 : 0;
44557
44802
  }
44803
+ async function cmdExplain(runtime, options) {
44804
+ const modesConfig = loadModesConfig(runtime.dirs.workdir);
44805
+ const majorMode = options.mode || "coder";
44806
+ const domains = options.domains ? options.domains.split(",").map((s) => s.trim()).filter(Boolean) : [];
44807
+ const resolved = resolveActiveMode(modesConfig, majorMode, domains);
44808
+ if (options.model) {
44809
+ resolved.model = options.model;
44810
+ }
44811
+ const footprint = calculatePromptFootprint(resolved, runtime.dirs.workdir);
44812
+ if (runtime.json) {
44813
+ print(runtime.io, runtime.json, { ok: true, command: "explain", ...footprint }, "");
44814
+ } else {
44815
+ runtime.io.stdout(formatModesExplainReport(footprint) + "\n");
44816
+ }
44817
+ return 0;
44818
+ }
44558
44819
  async function cmdUpdate(runtime, harness, options) {
44559
44820
  if (options.check && (options.kxm || options.self || options.extensions || options.models || harness)) {
44560
44821
  print(runtime.io, runtime.json, { ok: false, command: "update", error: "scope_conflict" }, "--check cannot be combined with other update flags");
@@ -44764,12 +45025,12 @@ async function cmdValidate(runtime, fileFlag) {
44764
45025
  }
44765
45026
  const selectedFile = explicitFile || configuredFile;
44766
45027
  const file = selectedFile ? resolve22(runtime.cwd, selectedFile) : void 0;
44767
- if (file && !existsSync30(file)) {
45028
+ if (file && !existsSync31(file)) {
44768
45029
  printWorker(runtime, worker, { ok: false, command: "validate", error: "file_not_found", file }, `workflow file not found: ${file}`);
44769
45030
  return 1;
44770
45031
  }
44771
45032
  try {
44772
- const raw = file ? readFileSync28(file, "utf8") : inline;
45033
+ const raw = file ? readFileSync29(file, "utf8") : inline;
44773
45034
  const warnings = [];
44774
45035
  const definitions = parseWorkflowDefinitions(raw, runtime.env, (message) => warnings.push(message));
44775
45036
  const secretEnvs = definitions.map((definition) => ({
@@ -44826,7 +45087,7 @@ async function cmdDash(runtime, options = {}) {
44826
45087
  return 2;
44827
45088
  }
44828
45089
  const screen = requested;
44829
- const dataPath = resolve22(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join36(runtime.dirs.state, "kxm.db"));
45090
+ const dataPath = resolve22(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join37(runtime.dirs.state, "kxm.db"));
44830
45091
  if (runtime.dryRun) {
44831
45092
  print(runtime.io, runtime.json, {
44832
45093
  ok: true,
@@ -44872,7 +45133,7 @@ async function cmdHub(runtime) {
44872
45133
  config = loadKxmUpdateConfig(runtime.env);
44873
45134
  } catch (error) {
44874
45135
  if (error instanceof KxmUpdateConfigError) {
44875
- const yamlPath = join36(vnextUserStateRoot({ env: runtime.env }), "update.yaml");
45136
+ const yamlPath = join37(vnextUserStateRoot({ env: runtime.env }), "update.yaml");
44876
45137
  runtime.io.stderr(`kxm: ${error.message}; update check skipped; fix or remove ${yamlPath}
44877
45138
  `);
44878
45139
  } else {
@@ -45008,7 +45269,7 @@ async function cmdWorker(runtime, options) {
45008
45269
  return await (runtime.io.spawnWorker ?? ((launchEnv) => spawnScript("kxm-worker.mjs", launchEnv)))(extraEnv);
45009
45270
  }
45010
45271
  async function cmdStop(runtime, waitMsFlag) {
45011
- const pids = existsSync30(runtime.dirs.state) ? readdirSync11(runtime.dirs.state).filter((name) => name.endsWith(".pid")) : [];
45272
+ const pids = existsSync31(runtime.dirs.state) ? readdirSync11(runtime.dirs.state).filter((name) => name.endsWith(".pid")) : [];
45012
45273
  if (runtime.dryRun) {
45013
45274
  print(runtime.io, runtime.json, { ok: true, command: "stop", dryRun: true, pidFiles: pids }, "would signal pid files");
45014
45275
  return 0;
@@ -45023,7 +45284,7 @@ async function cmdStop(runtime, waitMsFlag) {
45023
45284
  const orphans = [];
45024
45285
  for (const file of pids) {
45025
45286
  try {
45026
- const record = JSON.parse(readFileSync28(join36(runtime.dirs.state, file), "utf8"));
45287
+ const record = JSON.parse(readFileSync29(join37(runtime.dirs.state, file), "utf8"));
45027
45288
  const expectedControl = file === "hub.pid" ? "hub.stop" : file.startsWith("worker-") ? `${file.slice(0, -4)}.stop` : void 0;
45028
45289
  const expectedRole = file === "hub.pid" ? "hub" : file.startsWith("worker-") ? "worker" : void 0;
45029
45290
  if (record.version !== 1 || !Number.isInteger(record.pid) || record.pid <= 0 || !record.startedAt || !expectedControl || record.controlFile !== expectedControl || record.role !== expectedRole) {
@@ -45043,7 +45304,7 @@ async function cmdStop(runtime, waitMsFlag) {
45043
45304
  ignored.push(file);
45044
45305
  continue;
45045
45306
  }
45046
- writeFileSync26(join36(runtime.dirs.state, record.controlFile), `${JSON.stringify({ startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {}, requestedAt: (/* @__PURE__ */ new Date()).toISOString() })}
45307
+ writeFileSync26(join37(runtime.dirs.state, record.controlFile), `${JSON.stringify({ startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {}, requestedAt: (/* @__PURE__ */ new Date()).toISOString() })}
45047
45308
  `, { encoding: "utf8", mode: 384 });
45048
45309
  requested.push(file);
45049
45310
  records.set(file, { pid: record.pid, startedAt: record.startedAt, ...record.generation ? { generation: record.generation } : {} });
@@ -45061,7 +45322,7 @@ async function cmdStop(runtime, waitMsFlag) {
45061
45322
  while (Date.now() <= deadline && stopped.size < requested.length) {
45062
45323
  for (const [file, record] of records) {
45063
45324
  try {
45064
- const current = JSON.parse(readFileSync28(join36(runtime.dirs.state, file), "utf8"));
45325
+ const current = JSON.parse(readFileSync29(join37(runtime.dirs.state, file), "utf8"));
45065
45326
  if (current.pid !== record.pid || current.startedAt !== record.startedAt || current.generation !== record.generation || !processExists2(record.pid)) stopped.add(file);
45066
45327
  } catch {
45067
45328
  stopped.add(file);
@@ -45074,7 +45335,7 @@ async function cmdStop(runtime, waitMsFlag) {
45074
45335
  for (const file of orphans) {
45075
45336
  while (Date.now() <= deadlineOrphans) {
45076
45337
  try {
45077
- const current = JSON.parse(readFileSync28(join36(runtime.dirs.state, file), "utf8"));
45338
+ const current = JSON.parse(readFileSync29(join37(runtime.dirs.state, file), "utf8"));
45078
45339
  if (!Number.isInteger(current.serverPid) || !processExists2(current.serverPid)) break;
45079
45340
  } catch {
45080
45341
  break;
@@ -45082,14 +45343,14 @@ async function cmdStop(runtime, waitMsFlag) {
45082
45343
  await (runtime.io.sleep ?? ((ms) => new Promise((resolveSleep) => setTimeout(resolveSleep, ms))))(100);
45083
45344
  }
45084
45345
  try {
45085
- const current = JSON.parse(readFileSync28(join36(runtime.dirs.state, file), "utf8"));
45346
+ const current = JSON.parse(readFileSync29(join37(runtime.dirs.state, file), "utf8"));
45086
45347
  if (Number.isInteger(current.serverPid) && processExists2(current.serverPid)) {
45087
45348
  try {
45088
45349
  process.kill(current.serverPid, "SIGKILL");
45089
45350
  } catch {
45090
45351
  }
45091
45352
  }
45092
- rmSync10(join36(runtime.dirs.state, file), { force: true });
45353
+ rmSync10(join37(runtime.dirs.state, file), { force: true });
45093
45354
  } catch {
45094
45355
  }
45095
45356
  stopped.add(file);
@@ -45100,11 +45361,11 @@ async function cmdStop(runtime, waitMsFlag) {
45100
45361
  }
45101
45362
  async function cmdSessionStatus(runtime) {
45102
45363
  const stateDir = runtime.dirs.state;
45103
- const names3 = existsSync30(stateDir) ? readdirSync11(stateDir) : [];
45364
+ const names3 = existsSync31(stateDir) ? readdirSync11(stateDir) : [];
45104
45365
  const claims = [];
45105
45366
  for (const file of names3.filter((name) => name.endsWith(".pid"))) {
45106
45367
  try {
45107
- const record = JSON.parse(readFileSync28(join36(stateDir, file), "utf8"));
45368
+ const record = JSON.parse(readFileSync29(join37(stateDir, file), "utf8"));
45108
45369
  claims.push({
45109
45370
  file,
45110
45371
  role: record.role,
@@ -45119,7 +45380,7 @@ async function cmdSessionStatus(runtime) {
45119
45380
  const recoveries = [];
45120
45381
  for (const file of names3.filter((name) => name.startsWith("worker-recovery-") && name.endsWith(".json"))) {
45121
45382
  try {
45122
- const envelope = JSON.parse(readFileSync28(join36(stateDir, file), "utf8"));
45383
+ const envelope = JSON.parse(readFileSync29(join37(stateDir, file), "utf8"));
45123
45384
  recoveries.push({
45124
45385
  file,
45125
45386
  reason: envelope.reason,
@@ -45224,7 +45485,7 @@ async function cmdSessionBrief(runtime, options = {}) {
45224
45485
  print(runtime.io, runtime.json, { ok: true, command: "session brief", sessionToken }, sessionToken);
45225
45486
  return 0;
45226
45487
  }
45227
- const dataPath = resolve22(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join36(runtime.dirs.state, "kxm.db"));
45488
+ const dataPath = resolve22(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join37(runtime.dirs.state, "kxm.db"));
45228
45489
  const env = {
45229
45490
  ...runtime.env,
45230
45491
  KXM_STATE_DIR: runtime.dirs.state,
@@ -45314,14 +45575,14 @@ async function cmdSessionStart(runtime, options) {
45314
45575
  }
45315
45576
  async function cmdImprove(runtime, options = {}) {
45316
45577
  const file = options.file ? resolve22(runtime.cwd, options.file) : telemetryPath(runtime.dirs.logs);
45317
- const routingRecords = existsSync30(file) ? readRoutingRecords(file).map((entry) => entry.routing) : [];
45318
- const candidatesDir = options.outDir ? resolve22(runtime.cwd, options.outDir) : join36(runtime.cwd, ".kxm", "candidates");
45578
+ const routingRecords = existsSync31(file) ? readRoutingRecords(file).map((entry) => entry.routing) : [];
45579
+ const candidatesDir = options.outDir ? resolve22(runtime.cwd, options.outDir) : join37(runtime.cwd, ".kxm", "candidates");
45319
45580
  const report2 = buildImprovementReport(routingRecords, {
45320
45581
  candidatesDir,
45321
45582
  projectRoot: runtime.cwd,
45322
45583
  dryRun: runtime.dryRun
45323
45584
  });
45324
- const reportDir = join36(runtime.dirs.assets, "improvements");
45585
+ const reportDir = join37(runtime.dirs.assets, "improvements");
45325
45586
  const reportPath = writeImprovementReport(reportDir, report2, runtime.dryRun);
45326
45587
  const text = formatImprovementReport(report2);
45327
45588
  print(runtime.io, runtime.json, {
@@ -45480,7 +45741,7 @@ function skillStateFromFlag(value) {
45480
45741
  throw new Error(`invalid skill state ${value}`);
45481
45742
  }
45482
45743
  function skillsRoot(runtime) {
45483
- return join36(runtime.dirs.workdir, ".kxm", "skills");
45744
+ return join37(runtime.dirs.workdir, ".kxm", "skills");
45484
45745
  }
45485
45746
  function csv(value) {
45486
45747
  if (!value) return void 0;
@@ -45497,7 +45758,7 @@ async function cmdSkillsCreate(runtime, options) {
45497
45758
  const metadata = lifecycle.create({
45498
45759
  name: options.name,
45499
45760
  description: options.description ?? "",
45500
- content: readFileSync28(options.file, "utf8"),
45761
+ content: readFileSync29(options.file, "utf8"),
45501
45762
  createdBy: options.createdBy,
45502
45763
  sources: {
45503
45764
  runIds: csv(options.run) ?? [],
@@ -45767,9 +46028,9 @@ async function maybeOfferCompletionInstall(runtime) {
45767
46028
  if (!pathNeeded) {
45768
46029
  const scriptPath = completionScriptPath(shell, { env: runtime.env, configDir: runtime.env.KXM_USER_CONFIG_DIR });
45769
46030
  const { rcFile } = completionRcTarget(shell, { env: runtime.env });
45770
- if (rcFile && existsSync30(rcFile)) {
46031
+ if (rcFile && existsSync31(rcFile)) {
45771
46032
  try {
45772
- if (readFileSync28(rcFile, "utf8").includes(scriptPath)) return;
46033
+ if (readFileSync29(rcFile, "utf8").includes(scriptPath)) return;
45773
46034
  } catch {
45774
46035
  }
45775
46036
  }
@@ -46077,8 +46338,8 @@ async function cmdStudioLayout(runtime, workflowPath) {
46077
46338
  }
46078
46339
  let yamlContent;
46079
46340
  let workflowId = "default";
46080
- if (existsSync30(filePath)) {
46081
- yamlContent = readFileSync28(filePath, "utf8");
46341
+ if (existsSync31(filePath)) {
46342
+ yamlContent = readFileSync29(filePath, "utf8");
46082
46343
  } else {
46083
46344
  yamlContent = `schema: kxm.workflow.v1
46084
46345
  description: Feature implementation workflow
@@ -46118,7 +46379,7 @@ steps:
46118
46379
  maxTransitions: 2
46119
46380
  `;
46120
46381
  }
46121
- const parsedYaml = (0, import_yaml16.parse)(yamlContent);
46382
+ const parsedYaml = (0, import_yaml17.parse)(yamlContent);
46122
46383
  const plan = compileVnextWorkflow({ id: workflowId, value: parsedYaml });
46123
46384
  const layout = generateStudioLayout(plan);
46124
46385
  print(runtime.io, runtime.json, { ok: true, command: "studio layout", layout }, JSON.stringify(layout, null, 2));
@@ -46151,16 +46412,16 @@ async function cmdStudioServe(runtime, options) {
46151
46412
  sessionToken,
46152
46413
  planProvider: () => {
46153
46414
  try {
46154
- const wfDir = join36(runtime.cwd, ".kxm", "workflows");
46155
- const defaultPath = join36(wfDir, "default.yaml");
46156
- let targetPath = existsSync30(defaultPath) ? defaultPath : void 0;
46157
- if (!targetPath && existsSync30(wfDir)) {
46415
+ const wfDir = join37(runtime.cwd, ".kxm", "workflows");
46416
+ const defaultPath = join37(wfDir, "default.yaml");
46417
+ let targetPath = existsSync31(defaultPath) ? defaultPath : void 0;
46418
+ if (!targetPath && existsSync31(wfDir)) {
46158
46419
  const yml = readdirSync11(wfDir).find((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
46159
- if (yml) targetPath = join36(wfDir, yml);
46420
+ if (yml) targetPath = join37(wfDir, yml);
46160
46421
  }
46161
- if (targetPath && existsSync30(targetPath)) {
46162
- const raw = readFileSync28(targetPath, "utf8");
46163
- const parsed = (0, import_yaml16.parse)(raw);
46422
+ if (targetPath && existsSync31(targetPath)) {
46423
+ const raw = readFileSync29(targetPath, "utf8");
46424
+ const parsed = (0, import_yaml17.parse)(raw);
46164
46425
  const wfId = basename7(targetPath).replace(/\.(yaml|yml)$/, "");
46165
46426
  return compileVnextWorkflow({ id: wfId, value: parsed });
46166
46427
  }
@@ -46246,7 +46507,7 @@ async function cmdRoleGet(runtime, roleId, options) {
46246
46507
  runtime.io,
46247
46508
  runtime.json,
46248
46509
  { ok: true, command: "role get", roleId, scope: result.scope, filePath: result.filePath, role: result.role },
46249
- (0, import_yaml16.stringify)(result.role)
46510
+ (0, import_yaml17.stringify)(result.role)
46250
46511
  );
46251
46512
  return 0;
46252
46513
  }
@@ -46358,8 +46619,8 @@ async function cmdRoleAdd(runtime, roleId, options) {
46358
46619
  let roleDef;
46359
46620
  if (options.file) {
46360
46621
  const filePath = resolve22(runtime.cwd, options.file);
46361
- const content = readFileSync28(filePath, "utf8");
46362
- roleDef = (0, import_yaml16.parse)(content);
46622
+ const content = readFileSync29(filePath, "utf8");
46623
+ roleDef = (0, import_yaml17.parse)(content);
46363
46624
  roleDef.id = roleId;
46364
46625
  } else {
46365
46626
  const skills = options.skills ? options.skills.split(",").map((s) => s.trim()).filter(Boolean) : [];
@@ -46657,8 +46918,8 @@ async function cmdRoleResume(runtime, runId, ruling) {
46657
46918
  return 1;
46658
46919
  }
46659
46920
  }
46660
- const dbPath = join36(runtime.cwd, ".kxm", "state", "kxm.db");
46661
- if (existsSync30(dbPath)) {
46921
+ const dbPath = join37(runtime.cwd, ".kxm", "state", "kxm.db");
46922
+ if (existsSync31(dbPath)) {
46662
46923
  try {
46663
46924
  const database = new DatabaseSync(dbPath);
46664
46925
  try {
@@ -46777,7 +47038,7 @@ async function cmdWorkflowAdd(runtime, workflowId, options) {
46777
47038
  }
46778
47039
  if (options.file) {
46779
47040
  const filePath = resolve22(runtime.cwd, options.file);
46780
- content = readFileSync28(filePath, "utf8");
47041
+ content = readFileSync29(filePath, "utf8");
46781
47042
  } else if (!content) {
46782
47043
  content = {
46783
47044
  schema: "kxm.workflow.v1",
@@ -46921,7 +47182,7 @@ async function cmdRoutingReport(runtime, options) {
46921
47182
  let catalog;
46922
47183
  if (includeEquivalentListCost) {
46923
47184
  try {
46924
- const pricesPath = options.prices ? resolve22(runtime.cwd, options.prices) : join36(runtime.dirs.workspace, "prices.yaml");
47185
+ const pricesPath = options.prices ? resolve22(runtime.cwd, options.prices) : join37(runtime.dirs.workspace, "prices.yaml");
46925
47186
  catalog = loadPriceCatalog(pricesPath);
46926
47187
  } catch {
46927
47188
  }
@@ -47023,7 +47284,7 @@ async function cmdWorkflowStart(runtime, definitionIdArg, options) {
47023
47284
  }
47024
47285
  let payload;
47025
47286
  try {
47026
- const raw = payloadFlag.startsWith("@") ? readFileSync28(resolve22(runtime.cwd, payloadFlag.slice(1)), "utf8") : payloadFlag;
47287
+ const raw = payloadFlag.startsWith("@") ? readFileSync29(resolve22(runtime.cwd, payloadFlag.slice(1)), "utf8") : payloadFlag;
47027
47288
  const value = JSON.parse(raw);
47028
47289
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("object required");
47029
47290
  payload = value;
@@ -47090,7 +47351,7 @@ async function cmdWorkflowDegrade(runtime, runId, stageId, options) {
47090
47351
  }
47091
47352
  }
47092
47353
  async function cmdWorkflowInspect(runtime, action, runId) {
47093
- const dataPath = resolve22(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join36(runtime.dirs.state, "kxm.db"));
47354
+ const dataPath = resolve22(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join37(runtime.dirs.state, "kxm.db"));
47094
47355
  if (action === "get" && !runId) {
47095
47356
  runtime.io.stderr(`Usage: ${CLI_NAME} workflow get <runId>
47096
47357
  `);
@@ -47263,10 +47524,10 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
47263
47524
  let snapshot;
47264
47525
  try {
47265
47526
  if (snapshotPath) {
47266
- if (!existsSync30(snapshotPath)) throw new Error("snapshot_missing");
47267
- snapshot = JSON.parse(readFileSync28(snapshotPath, "utf8"));
47527
+ if (!existsSync31(snapshotPath)) throw new Error("snapshot_missing");
47528
+ snapshot = JSON.parse(readFileSync29(snapshotPath, "utf8"));
47268
47529
  } else {
47269
- const dataPath = resolve22(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join36(runtime.dirs.state, "kxm.db"));
47530
+ const dataPath = resolve22(runtime.dirs.workdir, runtime.env.KXM_DATA_PATH?.trim() || join37(runtime.dirs.state, "kxm.db"));
47270
47531
  const local = localWorkflowSnapshot(dataPath, runId);
47271
47532
  if (!local.runs[0]) throw new Error("workflow_not_found");
47272
47533
  snapshot = { run: local.runs[0], journal: local.journal };
@@ -47278,7 +47539,7 @@ async function cmdRetrospectiveExport(runtime, runId, options) {
47278
47539
  return 1;
47279
47540
  }
47280
47541
  const doc = buildRetrospective(snapshot.run, snapshot.journal);
47281
- const outDir = resolve22(runtime.cwd, String(options.outDir || join36(runtime.dirs.assets, "retrospectives")));
47542
+ const outDir = resolve22(runtime.cwd, String(options.outDir || join37(runtime.dirs.assets, "retrospectives")));
47282
47543
  const assetsRoot = resolve22(runtime.dirs.assets);
47283
47544
  const assetsPrefix = `${assetsRoot}${process.platform === "win32" ? "\\" : "/"}`;
47284
47545
  if (outDir !== assetsRoot && !outDir.startsWith(assetsPrefix)) {
@@ -47734,6 +47995,11 @@ function createProgram(ctx, result) {
47734
47995
  addGlobalOptions(routing.command("benchmark").description("Dedicated offline benchmark for side-by-side model comparison (Decision Q12)")).option("--task <fixture>", "Task prompt or fixture path for benchmark comparison").option("--arms <models>", "Comma-separated model routes to benchmark (e.g. grok/grok-4.6,claude/fable)").option("--runs <count>", "Benchmark runs per arm", "1").action(async function routingBenchmarkAction(options) {
47735
47996
  result.code = await cmdRoutingBenchmark(runtimeFrom(ctx, this), options);
47736
47997
  });
47998
+ addGlobalOptions(
47999
+ program2.command("explain").description("Pre-flight context footprint and token cost inspection for workflow modes").option("--mode <name>", "Major mode (coder, planner, auditor, browser)", "coder").option("--domains <list>", "Comma-separated domain modules (git, k8s, database, browser)").option("--model <id>", "Target model identifier (e.g. grok/grok-4.6, claude/fable)")
48000
+ ).action(async function explainAction(options) {
48001
+ result.code = await cmdExplain(runtimeFrom(ctx, this), options);
48002
+ });
47737
48003
  const hub = addGlobalOptions(program2.command("hub").description("Start, inspect, and stop the local KXM hub"));
47738
48004
  hub.helpCommand("help", "Show hub help");
47739
48005
  addGlobalOptions(hub.command("view").description("Check hub /health and /ready")).action(bind(cmdStatus));