@kontextmind/kxm 0.7.112 → 0.7.118

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 (34) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.kxm/agents/implementer.yaml +1 -1
  3. package/.kxm/roles/writer.yaml +1 -1
  4. package/docs/contributing/assignment-runner.md +3 -3
  5. package/package.json +1 -1
  6. package/packages/core/tui/dist/index.js +478 -0
  7. package/packages/core/tui/src/adapters/claude.ts +131 -0
  8. package/packages/core/tui/src/adapters/historyExport.ts +35 -0
  9. package/packages/core/tui/src/adapters/omp.ts +174 -0
  10. package/packages/core/tui/src/adapters/pi.ts +34 -0
  11. package/packages/core/tui/src/exports/index.ts +8 -0
  12. package/packages/core/tui/src/tui/history.ts +106 -0
  13. package/packages/core/tui/src/tui/modelSelector.ts +43 -0
  14. package/packages/core/tui/src/tui/optimizer.ts +154 -0
  15. package/packages/core/tui/src/tui/queue.ts +90 -0
  16. package/packages/core/tui/src/tui/roleBudget.ts +104 -0
  17. package/plugins/kxm/.claude-plugin/plugin.json +1 -1
  18. package/plugins/kxm/dist/cli.js +220 -22
  19. package/plugins/kxm/dist/mcp-server.js +1 -1
  20. package/plugins/kxm/dist/runtime-supervisor.js +36 -4
  21. package/plugins/kxm/dist/runtime.js +36 -4
  22. package/plugins/kxm/dist/server.js +22 -0
  23. package/plugins/kxm/package.json +1 -1
  24. package/plugins/kxm/skills/kxm-harness-auth/SKILL.md +6 -0
  25. package/plugins/kxm/src/autocomplete.ts +3 -0
  26. package/plugins/kxm/src/cli/plugins.ts +201 -0
  27. package/plugins/kxm/src/cli.ts +14 -0
  28. package/plugins/kxm/src/harness.ts +36 -4
  29. package/plugins/kxm/src/mcp-server.ts +1 -1
  30. package/scripts/assignment-run.d.mts +7 -0
  31. package/scripts/assignment-run.mjs +128 -1
  32. package/scripts/demo-overlay.mjs +175 -0
  33. package/scripts/harness-run.mjs +2 -2
  34. package/scripts/native-critic.mjs +7 -7
@@ -11,7 +11,7 @@
11
11
  "name": "kxm",
12
12
  "source": "./plugins/kxm",
13
13
  "description": "Durable workflows, peer agents, and kxm tui",
14
- "version": "0.7.112",
14
+ "version": "0.7.118",
15
15
  "category": "development",
16
16
  "tags": ["kxm", "multi-agent", "workflows", "mcp"]
17
17
  }
@@ -3,7 +3,7 @@ purpose: Implement the approved change within the declared repository scope.
3
3
  harness: grok
4
4
  model:
5
5
  provider: xai
6
- model: grok-4.6
6
+ model: grok-4.7
7
7
  tools:
8
8
  preset: workspace-writer
9
9
  defaultRepositoryAccess: none
@@ -5,7 +5,7 @@ id: writer
5
5
  # Effort default: medium for implementation (AGENTS.md). agy ids bake in the
6
6
  # effort tier, so those entries carry no effort field.
7
7
  roster:
8
- - model: xai/grok-4.6
8
+ - model: xai/grok-4.7
9
9
  effort: medium
10
10
  enabled: true
11
11
  - model: openrouter/qwen/qwen3-coder-plus
@@ -33,7 +33,7 @@ permissions:
33
33
 
34
34
  | Role | Admitted route (harness / model) | Vendor | Permission |
35
35
  |---|---|---|---|
36
- | `writer` | `grok` / `grok-4.6`; relief: `pi` / `openrouter/qwen/qwen3-coder-plus` | `xai`; `alibaba` | `edit` |
36
+ | `writer` | `grok` / `grok-4.7`; relief: `pi` / `openrouter/qwen/qwen3-coder-plus` | `xai`; `alibaba` | `edit` |
37
37
  | `planner` | `claude` / `fable` | `anthropic` | `read-only` |
38
38
  | `reviewer-arch` | `claude` / `fable` | `anthropic` | `read-only` |
39
39
  | `reviewer-cli` | `codex` / `gpt-5.6-sol` | `openai` | `read-only` |
@@ -115,7 +115,7 @@ Write a closed `kxm.assignment.v1` manifest. Unknown keys are refused.
115
115
  "assignment_id": "asg-writer-1",
116
116
  "kind": "implement",
117
117
  "harness": "grok",
118
- "model": "grok-4.6",
118
+ "model": "grok-4.7",
119
119
  "effort": "medium",
120
120
  "permission": "edit",
121
121
  "cwd": "/abs/kxm-fix-improve-sources",
@@ -247,7 +247,7 @@ then is that `BLOCK` resolved.
247
247
  ├── output-schema.json # the structured-output schema given to the harness
248
248
  ├── pre-dispatch.json # written before spawn (kxm.assignment-dispatch.v1)
249
249
  ├── completion.json # kxm.assignment-completion.v1, or refusal.json
250
- ├── routing-record.json # read by kxm routing report
250
+ ├── routing-record.json # export with `node scripts/assignment-run.mjs routing-export --task-dir <abs>` and pass the JSONL to `kxm improve report --file` / `kxm routing report --file`
251
251
  ├── telemetry.jsonl # usage, latency and cost
252
252
  ├── runner-errors.jsonl # bounded failure codes
253
253
  ├── witness/ # latest.json and history/<receipt-id>.json
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kontextmind/kxm",
3
- "version": "0.7.112",
3
+ "version": "0.7.118",
4
4
  "description": "KXM local-first multi-agent orchestration and operator dashboard",
5
5
  "type": "module",
6
6
  "author": "KontextMind",
@@ -1129,6 +1129,268 @@ function createKxmTuiPanelComponent(options) {
1129
1129
  return new KxmTuiPanelComponent(options);
1130
1130
  }
1131
1131
 
1132
+ // packages/core/tui/src/tui/optimizer.ts
1133
+ function evaluatePlanOptimizations(tasks) {
1134
+ const proposals = [];
1135
+ const byStage = /* @__PURE__ */ new Map();
1136
+ for (const task of tasks) {
1137
+ const list = byStage.get(task.stageId) ?? [];
1138
+ list.push(task);
1139
+ byStage.set(task.stageId, list);
1140
+ }
1141
+ for (const [stageId, stageTasks] of byStage) {
1142
+ if (stageTasks.length >= 2) {
1143
+ const independent = stageTasks.filter((t) => t.dependencies.length === 0 || t.dependencies.every((dep) => !stageTasks.some((st) => st.id === dep)));
1144
+ if (independent.length >= 2) {
1145
+ proposals.push({
1146
+ id: `opt_fanout_${stageId}`,
1147
+ category: "concurrency",
1148
+ title: `Parallelize ${independent.length} tasks in stage "${stageId}"`,
1149
+ description: `Tasks [${independent.map((t) => t.id).join(", ")}] have zero mutual dependencies and can execute concurrently via kxm_fanout.`,
1150
+ targetTaskIds: independent.map((t) => t.id),
1151
+ projectedTimeSavingsSeconds: Math.round(independent.length * 15),
1152
+ projectedCostSavingsPercent: 0,
1153
+ selected: true
1154
+ });
1155
+ }
1156
+ }
1157
+ }
1158
+ for (const task of tasks) {
1159
+ const isFrontier = /claude-3-5-sonnet|claude-3-opus|gpt-4o|gpt-5/i.test(task.model);
1160
+ if (isFrontier && (task.category === "docs" || /doc|readme|markdown/i.test(task.title))) {
1161
+ proposals.push({
1162
+ id: `opt_cost_${task.id}`,
1163
+ category: "cost",
1164
+ title: `Downscale model for documentation task "${task.title}"`,
1165
+ description: `Task "${task.id}" is documentation-focused. Routing to a high-throughput lightweight model saves significant token budget.`,
1166
+ targetTaskIds: [task.id],
1167
+ projectedTimeSavingsSeconds: 5,
1168
+ projectedCostSavingsPercent: 65,
1169
+ suggestedHarness: "pi",
1170
+ suggestedModel: "qwen/qwen3-coder-plus",
1171
+ selected: true
1172
+ });
1173
+ } else if (isFrontier && (task.category === "lint" || /lint|format|style/i.test(task.title))) {
1174
+ proposals.push({
1175
+ id: `opt_lint_${task.id}`,
1176
+ category: "cost",
1177
+ title: `Downscale model for formatting/lint task "${task.title}"`,
1178
+ description: `Task "${task.id}" performs deterministic formatting/linting. Use a fast local/tier-1 helper.`,
1179
+ targetTaskIds: [task.id],
1180
+ projectedTimeSavingsSeconds: 8,
1181
+ projectedCostSavingsPercent: 80,
1182
+ suggestedHarness: "pi",
1183
+ suggestedModel: "google/gemini-2.5-flash",
1184
+ selected: true
1185
+ });
1186
+ }
1187
+ }
1188
+ const totalTimeSavings = proposals.reduce((acc, p) => acc + p.projectedTimeSavingsSeconds, 0);
1189
+ const totalCostSavings = proposals.length > 0 ? Math.round(proposals.reduce((acc, p) => acc + p.projectedCostSavingsPercent, 0) / proposals.length) : 0;
1190
+ return {
1191
+ proposals,
1192
+ totalProjectedTimeSavingsSeconds: totalTimeSavings,
1193
+ totalProjectedCostSavingsPercent: totalCostSavings
1194
+ };
1195
+ }
1196
+ function applyPlanOptimizations(tasks, proposals) {
1197
+ const selected = proposals.filter((p) => p.selected);
1198
+ if (selected.length === 0) return { tasks: [...tasks], appliedCount: 0 };
1199
+ const modelOverrides = /* @__PURE__ */ new Map();
1200
+ for (const proposal of selected) {
1201
+ if (proposal.category === "cost" && proposal.suggestedModel) {
1202
+ for (const targetId of proposal.targetTaskIds) {
1203
+ modelOverrides.set(targetId, {
1204
+ ...proposal.suggestedHarness ? { harness: proposal.suggestedHarness } : {},
1205
+ model: proposal.suggestedModel
1206
+ });
1207
+ }
1208
+ }
1209
+ }
1210
+ const updated = tasks.map((task) => {
1211
+ const override = modelOverrides.get(task.id);
1212
+ if (!override) return task;
1213
+ return {
1214
+ ...task,
1215
+ ...override.harness ? { harness: override.harness } : {},
1216
+ ...override.model ? { model: override.model } : {}
1217
+ };
1218
+ });
1219
+ return {
1220
+ tasks: updated,
1221
+ appliedCount: selected.length
1222
+ };
1223
+ }
1224
+
1225
+ // packages/core/tui/src/tui/history.ts
1226
+ function filterHistoryEvents(events, options = {}) {
1227
+ const category = options.category ?? "all";
1228
+ const query = options.query?.trim().toLowerCase();
1229
+ return events.filter((ev) => {
1230
+ if (category !== "all" && ev.category !== category) return false;
1231
+ if (!query) return true;
1232
+ const matchTarget = `${ev.id} ${ev.title} ${ev.stageId ?? ""} ${ev.taskId ?? ""} ${ev.role ?? ""} ${ev.model ?? ""} ${ev.journalSummary ?? ""}`.toLowerCase();
1233
+ return matchTarget.includes(query);
1234
+ });
1235
+ }
1236
+ function renderWorkflowHistoryMarkdown(runId, events) {
1237
+ const totalSpend = events.reduce((acc, ev) => acc + (ev.costUsd ?? 0), 0);
1238
+ const passedCount = events.filter((ev) => ev.status === "passed").length;
1239
+ const failedCount = events.filter((ev) => ev.status === "failed").length;
1240
+ let mdContent = `# KXM Workflow Retrospective: ${runId}
1241
+
1242
+ `;
1243
+ mdContent += `**Total Events:** ${events.length} (Passed: ${passedCount}, Failed: ${failedCount})
1244
+ `;
1245
+ mdContent += `**Total Spend:** $${totalSpend.toFixed(4)} USD
1246
+
1247
+ `;
1248
+ mdContent += `## Chronological Event Journal
1249
+
1250
+ `;
1251
+ mdContent += `| Timestamp | Category | Title | Status | Role / Model | Duration | Spend |
1252
+ `;
1253
+ mdContent += `|---|---|---|---|---|---|---|
1254
+ `;
1255
+ for (const ev of events) {
1256
+ const elapsed = ev.durationMs !== void 0 ? `${(ev.durationMs / 1e3).toFixed(1)}s` : "-";
1257
+ const spend = ev.costUsd !== void 0 ? `$${ev.costUsd.toFixed(4)}` : "-";
1258
+ const roleModel = ev.role ? `${ev.role} (${ev.harness ?? "-"}/${ev.model ?? "-"})` : "-";
1259
+ mdContent += `| ${ev.timestamp} | ${ev.category} | ${ev.title} | ${ev.status} | ${roleModel} | ${elapsed} | ${spend} |
1260
+ `;
1261
+ }
1262
+ mdContent += `
1263
+ ## Produced Artifacts & Proofs
1264
+
1265
+ `;
1266
+ for (const ev of events) {
1267
+ if (ev.artifacts && ev.artifacts.length > 0) {
1268
+ mdContent += `### ${ev.title} (${ev.id})
1269
+ `;
1270
+ for (const art of ev.artifacts) {
1271
+ mdContent += `- \`${art}\`
1272
+ `;
1273
+ }
1274
+ }
1275
+ }
1276
+ return mdContent;
1277
+ }
1278
+ function formatWorkflowHistoryJsonl(runId, events) {
1279
+ return events.map((ev) => JSON.stringify({
1280
+ schema: "kxm.workflow-history-event.v1",
1281
+ runId,
1282
+ ...ev
1283
+ })).join("\n") + "\n";
1284
+ }
1285
+
1286
+ // packages/core/tui/src/tui/queue.ts
1287
+ function reorderQueuedTasks(tasks, fromIndex, toIndex) {
1288
+ if (fromIndex < 0 || fromIndex >= tasks.length || toIndex < 0 || toIndex >= tasks.length || fromIndex === toIndex) {
1289
+ return { success: false, tasks, warning: "invalid_indices" };
1290
+ }
1291
+ const target = tasks[fromIndex];
1292
+ if (target.status === "in_flight" || target.status === "completed") {
1293
+ return {
1294
+ success: false,
1295
+ tasks,
1296
+ warning: `cannot move ${target.status} task "${target.id}"`
1297
+ };
1298
+ }
1299
+ const draft = [...tasks];
1300
+ const [removed] = draft.splice(fromIndex, 1);
1301
+ draft.splice(toIndex, 0, removed);
1302
+ const idToIndex = /* @__PURE__ */ new Map();
1303
+ for (let i = 0; i < draft.length; i++) {
1304
+ idToIndex.set(draft[i].id, i);
1305
+ }
1306
+ for (let i = 0; i < draft.length; i++) {
1307
+ const task = draft[i];
1308
+ for (const depId of task.dependencies) {
1309
+ const depIndex = idToIndex.get(depId);
1310
+ if (depIndex !== void 0 && depIndex > i) {
1311
+ return {
1312
+ success: false,
1313
+ tasks,
1314
+ warning: `Prerequisite violation: "${task.id}" depends on "${depId}", which is scheduled later at position ${depIndex + 1}`
1315
+ };
1316
+ }
1317
+ }
1318
+ }
1319
+ return {
1320
+ success: true,
1321
+ tasks: draft
1322
+ };
1323
+ }
1324
+
1325
+ // packages/core/tui/src/tui/roleBudget.ts
1326
+ function evaluateRoleBudget(config, state, projectedCostUsd = 0) {
1327
+ const projectedRunSpend = state.currentRunSpendUsd + projectedCostUsd;
1328
+ if (config.runSpendCapUsd !== void 0 && projectedRunSpend > config.runSpendCapUsd) {
1329
+ if (config.onExhausted === "cascade_to_roster" && config.fallbackModel) {
1330
+ return {
1331
+ status: "exhausted",
1332
+ nextAction: "cascade",
1333
+ fallbackModel: config.fallbackModel,
1334
+ fallbackHarness: config.fallbackHarness ?? "pi",
1335
+ warning: `Role "${config.roleId}" reached run spend cap ($${config.runSpendCapUsd.toFixed(2)}); rolling over to ${config.fallbackModel}`
1336
+ };
1337
+ }
1338
+ if (config.onExhausted === "borrow_from_pool" && (config.emergencyPoolLimitUsd ?? 0) > state.borrowedFromPoolUsd) {
1339
+ return {
1340
+ status: "exhausted",
1341
+ nextAction: "borrow",
1342
+ warning: `Role "${config.roleId}" borrowing from project emergency buffer`
1343
+ };
1344
+ }
1345
+ return {
1346
+ status: "exhausted",
1347
+ nextAction: config.onExhausted === "pause_for_approval" ? "pause" : "fail_closed",
1348
+ warning: `Role "${config.roleId}" exhausted run spend cap ($${config.runSpendCapUsd.toFixed(2)})`
1349
+ };
1350
+ }
1351
+ if (config.monthlySpendCapUsd !== void 0 && state.currentMonthlySpendUsd + projectedCostUsd > config.monthlySpendCapUsd) {
1352
+ if (config.onExhausted === "cascade_to_roster" && config.fallbackModel) {
1353
+ return {
1354
+ status: "exhausted",
1355
+ nextAction: "cascade",
1356
+ fallbackModel: config.fallbackModel,
1357
+ fallbackHarness: config.fallbackHarness ?? "pi",
1358
+ warning: `Role "${config.roleId}" reached monthly budget limit ($${config.monthlySpendCapUsd.toFixed(2)})`
1359
+ };
1360
+ }
1361
+ return {
1362
+ status: "exhausted",
1363
+ nextAction: "pause",
1364
+ warning: `Role "${config.roleId}" reached monthly budget cap ($${config.monthlySpendCapUsd.toFixed(2)})`
1365
+ };
1366
+ }
1367
+ if (config.runSpendCapUsd && projectedRunSpend / config.runSpendCapUsd >= 0.85) {
1368
+ return {
1369
+ status: "near_limit",
1370
+ nextAction: "proceed",
1371
+ warning: `Role "${config.roleId}" is at ${Math.round(projectedRunSpend / config.runSpendCapUsd * 100)}% of run budget`
1372
+ };
1373
+ }
1374
+ return {
1375
+ status: "ok",
1376
+ nextAction: "proceed"
1377
+ };
1378
+ }
1379
+
1380
+ // packages/core/tui/src/tui/modelSelector.ts
1381
+ function filterModelOptions(models, query, providerFilter) {
1382
+ const q = query?.trim().toLowerCase();
1383
+ const prov = providerFilter?.trim().toLowerCase();
1384
+ return models.filter((m) => {
1385
+ if (prov && m.provider.toLowerCase() !== prov && m.harness.toLowerCase() !== prov) {
1386
+ return false;
1387
+ }
1388
+ if (!q) return true;
1389
+ const matchTarget = `${m.id} ${m.selector} ${m.name} ${m.provider} ${m.harness}`.toLowerCase();
1390
+ return matchTarget.includes(q);
1391
+ });
1392
+ }
1393
+
1132
1394
  // packages/core/tui/src/services/registry.ts
1133
1395
  function describe(error) {
1134
1396
  if (error instanceof Error && error.message) return error.message.split(/\r?\n/u)[0].slice(0, KXM_TUI_LIMITS.detailMax);
@@ -1356,7 +1618,126 @@ async function runKxmTuiPanel(input) {
1356
1618
  return stopped && input.abort?.aborted ? 130 : 0;
1357
1619
  }
1358
1620
 
1621
+ // packages/core/tui/src/adapters/omp.ts
1622
+ function resolveKxmTheme(theme) {
1623
+ if ("headerBg" in theme && typeof theme.headerBg === "function") {
1624
+ return theme;
1625
+ }
1626
+ return createKxmTuiThemeFromPi(theme);
1627
+ }
1628
+ function createKxmTuiOmpOverlay(options) {
1629
+ let closed = false;
1630
+ let mode = options.initialMode ?? "expanded";
1631
+ let autoDispatch = Boolean(options.autoDispatch);
1632
+ const theme = resolveKxmTheme(options.theme);
1633
+ const panel = new KxmTuiPanelComponent({
1634
+ registry: options.registry,
1635
+ theme,
1636
+ title: options.title,
1637
+ ...options.goal ? { breadcrumb: `Goal: ${options.goal}` } : {},
1638
+ helpLines: [
1639
+ "[Ctrl+O/F2] Toggle Fold [h] History [a] Toggle Auto [o] Optimize [Shift+\u2191/\u2193] Reorder"
1640
+ ],
1641
+ requestRender: () => options.tui.requestRender(),
1642
+ onQuit: () => {
1643
+ if (closed) return;
1644
+ closed = true;
1645
+ panel.dispose();
1646
+ options.done?.(true);
1647
+ },
1648
+ ...options.width === void 0 ? {} : { getWidth: options.width }
1649
+ });
1650
+ function renderCollapsed(width) {
1651
+ const goalText = options.goal ? `Goal: ${options.goal}` : options.title;
1652
+ const modeBadge = autoDispatch ? theme.accent("[AUTO: ON]") : theme.dim("[STEP MODE]");
1653
+ const hint = theme.dim("[Ctrl+O to Expand]");
1654
+ const content = ` [KXM] ${goalText} \u2500\u2500 ${modeBadge} \u2500\u2500 ${hint}`;
1655
+ const pad = Math.max(0, width - content.length);
1656
+ return [content + " ".repeat(pad)];
1657
+ }
1658
+ function handleInput(data) {
1659
+ if (closed) return;
1660
+ if (data === "" || data === "\x1BOQ" || data === "\x1B[12~") {
1661
+ mode = mode === "collapsed" ? "expanded" : "collapsed";
1662
+ options.tui.requestRender();
1663
+ return;
1664
+ }
1665
+ if (mode === "collapsed") {
1666
+ if (data === "\r" || data === " " || data === "o" || data === "O") {
1667
+ mode = "expanded";
1668
+ options.tui.requestRender();
1669
+ }
1670
+ return;
1671
+ }
1672
+ if (data === "a" || data === "A") {
1673
+ autoDispatch = !autoDispatch;
1674
+ options.onToggleAutoDispatch?.(autoDispatch);
1675
+ options.tui.requestRender();
1676
+ return;
1677
+ }
1678
+ if (data === "h" || data === "H") {
1679
+ mode = mode === "history" ? "expanded" : "history";
1680
+ options.tui.requestRender();
1681
+ return;
1682
+ }
1683
+ if (data === "o" || data === "O") {
1684
+ mode = "optimizer";
1685
+ options.onRunOptimizer?.();
1686
+ options.tui.requestRender();
1687
+ return;
1688
+ }
1689
+ if ((data === "x" || data === "X") && mode === "history") {
1690
+ options.onExportHistory?.("both");
1691
+ options.tui.requestRender();
1692
+ return;
1693
+ }
1694
+ if (data === "\x1B" && mode !== "expanded") {
1695
+ mode = "expanded";
1696
+ options.tui.requestRender();
1697
+ return;
1698
+ }
1699
+ panel.handleInput(data);
1700
+ options.tui.requestRender();
1701
+ }
1702
+ return {
1703
+ getMode: () => mode,
1704
+ setMode: (nextMode) => {
1705
+ mode = nextMode;
1706
+ options.tui.requestRender();
1707
+ },
1708
+ isAutoDispatch: () => autoDispatch,
1709
+ toggleAutoDispatch: () => {
1710
+ autoDispatch = !autoDispatch;
1711
+ options.onToggleAutoDispatch?.(autoDispatch);
1712
+ options.tui.requestRender();
1713
+ },
1714
+ render: (width) => {
1715
+ if (mode === "collapsed") {
1716
+ return renderCollapsed(width);
1717
+ }
1718
+ return panel.render(width);
1719
+ },
1720
+ handleInput,
1721
+ invalidate: () => panel.invalidate(),
1722
+ dispose: () => {
1723
+ if (closed) return;
1724
+ closed = true;
1725
+ panel.dispose();
1726
+ options.done?.(true);
1727
+ }
1728
+ };
1729
+ }
1730
+
1359
1731
  // packages/core/tui/src/adapters/pi.ts
1732
+ var createKxmTuiPiOverlay = createKxmTuiOmpOverlay;
1733
+ function renderPiWorkflowWidget(options) {
1734
+ const filled = Math.min(10, Math.max(0, Math.round(options.progressPercent / 100 * 10)));
1735
+ const bar = `[${"\u2588".repeat(filled)}${"\u2591".repeat(10 - filled)}] ${options.progressPercent}%`;
1736
+ const mode = options.autoDispatch ? "AUTO" : "STEP";
1737
+ const line1 = `[KXM] Goal: ${options.goal} \u2500\u2500 Stage: ${options.stage} (${bar}) [${mode}]`;
1738
+ const line2 = options.activeTask ? ` Active: ${options.activeTask}` : ` Waiting for next dispatch (use /kxm progress)`;
1739
+ return [line1, line2];
1740
+ }
1360
1741
  function createKxmTuiPiPanel(options) {
1361
1742
  let closed = false;
1362
1743
  const panel = new KxmTuiPanelComponent({
@@ -1384,6 +1765,86 @@ function createKxmTuiPiPanel(options) {
1384
1765
  invalidate: () => panel.invalidate()
1385
1766
  };
1386
1767
  }
1768
+
1769
+ // packages/core/tui/src/adapters/claude.ts
1770
+ function formatClaudeTaskHud(options, compact = false) {
1771
+ const percent = options.totalTasks > 0 ? Math.round(options.completedTasks / options.totalTasks * 100) : 0;
1772
+ const filled = Math.round(percent / 100 * 10);
1773
+ const bar = `[${"\u2588".repeat(filled)}${"\u2591".repeat(10 - filled)}] ${percent}%`;
1774
+ const modeTag = options.autoDispatch ? "[AUTO]" : "[STEP]";
1775
+ const attemptTag = options.attempt && options.attempt > 1 ? ` (Attempt ${options.attempt}/${options.maxAttempts ?? 3})` : "";
1776
+ const timeTag = options.elapsedSeconds !== void 0 ? ` \u23F1 ${options.elapsedSeconds}s` : "";
1777
+ if (compact) {
1778
+ return `[KXM ${modeTag}] ${options.goal} \u2500\u2500 Stage ${options.stageIndex}/${options.totalStages} ${bar}${attemptTag}${timeTag}`;
1779
+ }
1780
+ const activeTask = options.activeTaskTitle ? `
1781
+ \u2502 Active Task: ${options.activeTaskTitle}${attemptTag} [${options.activeRole ?? "agent"} (${options.activeModel ?? "default"})]` : "";
1782
+ return [
1783
+ `\u250C\u2500\u2500 KXM TASK HUD \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510`,
1784
+ `\u2502 Goal: ${options.goal}`,
1785
+ `\u2502 Stage ${options.stageIndex}/${options.totalStages}: ${options.stageName} \u2502 Progress: ${bar}${timeTag} \u2502 Mode: ${modeTag}${activeTask}`,
1786
+ `\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518`
1787
+ ].join("\n");
1788
+ }
1789
+ function formatClaudeUpcomingQueue(tasks) {
1790
+ if (tasks.length === 0) return "No upcoming tasks in queue.";
1791
+ const lines = ["### KXM Upcoming Task Queue:"];
1792
+ for (let i = 0; i < tasks.length; i++) {
1793
+ const t = tasks[i];
1794
+ const deps = t.dependencies.length > 0 ? ` (Requires: ${t.dependencies.join(", ")})` : " (Ready)";
1795
+ lines.push(`${i + 1}. [${t.status.toUpperCase()}] **${t.title}** (\`${t.id}\`)`);
1796
+ lines.push(` Role: \`${t.role}\` [${t.harness}/${t.model}]${deps}`);
1797
+ }
1798
+ return lines.join("\n");
1799
+ }
1800
+ function formatClaudeOptimizerReport(result) {
1801
+ if (result.proposals.length === 0) {
1802
+ return "Plan Optimizer: No pending optimization proposals.";
1803
+ }
1804
+ const lines = [
1805
+ `### KXM Plan Optimizer Proposals:`,
1806
+ `Total Projected Savings: -${result.totalProjectedTimeSavingsSeconds}s execution time, -${result.totalProjectedCostSavingsPercent}% token cost`,
1807
+ ""
1808
+ ];
1809
+ for (const p of result.proposals) {
1810
+ const icon = p.category === "concurrency" ? "\u26A1" : "\u{1F4B0}";
1811
+ lines.push(`${icon} **${p.title}** (\`${p.id}\`)`);
1812
+ lines.push(` ${p.description}`);
1813
+ if (p.suggestedModel) {
1814
+ lines.push(` Suggested Routing: \`${p.suggestedHarness ?? "pi"}/${p.suggestedModel}\``);
1815
+ }
1816
+ lines.push("");
1817
+ }
1818
+ return lines.join("\n").trim();
1819
+ }
1820
+ function formatClaudeRoleBudget(config, state) {
1821
+ const runCap = config.runSpendCapUsd !== void 0 ? `$${config.runSpendCapUsd.toFixed(2)}` : "Unmetered";
1822
+ const monthlyCap = config.monthlySpendCapUsd !== void 0 ? `$${config.monthlySpendCapUsd.toFixed(2)}` : "Unmetered";
1823
+ const currentRun = `$${state.currentRunSpendUsd.toFixed(4)}`;
1824
+ const currentMonthly = `$${state.currentMonthlySpendUsd.toFixed(2)}`;
1825
+ return [
1826
+ `Role: ${config.roleId} (${config.type})`,
1827
+ `Run Spend: ${currentRun} / ${runCap}`,
1828
+ `Monthly Spend: ${currentMonthly} / ${monthlyCap}`,
1829
+ `On Exhausted: ${config.onExhausted}${config.fallbackModel ? ` (Fallback: ${config.fallbackModel})` : ""}`
1830
+ ].join(" \u2502 ");
1831
+ }
1832
+ function formatClaudeWorkflowHistory(runId, events) {
1833
+ return renderWorkflowHistoryMarkdown(runId, events);
1834
+ }
1835
+
1836
+ // packages/core/tui/src/adapters/historyExport.ts
1837
+ import { mkdirSync, writeFileSync } from "node:fs";
1838
+ import { join } from "node:path";
1839
+ function exportWorkflowHistory(runId, events, destinationDir) {
1840
+ mkdirSync(destinationDir, { recursive: true });
1841
+ const sanitizedRunId = runId.replaceAll(/[^a-zA-Z0-9_-]/g, "_");
1842
+ const mdPath = join(destinationDir, `retrospective-${sanitizedRunId}.md`);
1843
+ const jsonlPath = join(destinationDir, `history-${sanitizedRunId}.jsonl`);
1844
+ writeFileSync(mdPath, renderWorkflowHistoryMarkdown(runId, events), "utf8");
1845
+ writeFileSync(jsonlPath, formatWorkflowHistoryJsonl(runId, events), "utf8");
1846
+ return { mdPath, jsonlPath };
1847
+ }
1387
1848
  export {
1388
1849
  KXM_TUI_ABORT_ACTION,
1389
1850
  KXM_TUI_CLEAR_ACTION,
@@ -1400,11 +1861,14 @@ export {
1400
1861
  KXM_TUI_STEP_STATES,
1401
1862
  KxmTuiPanelComponent,
1402
1863
  alignRight,
1864
+ applyPlanOptimizations,
1403
1865
  assertKxmTuiSurface,
1404
1866
  checkedKxmTuiSections,
1405
1867
  createDefaultKxmTuiTheme,
1406
1868
  createKxmTuiAnsiTheme,
1869
+ createKxmTuiOmpOverlay,
1407
1870
  createKxmTuiPanelComponent,
1871
+ createKxmTuiPiOverlay,
1408
1872
  createKxmTuiPiPanel,
1409
1873
  createKxmTuiRegistry,
1410
1874
  createKxmTuiThemeFromPi,
@@ -1413,11 +1877,22 @@ export {
1413
1877
  currentSection,
1414
1878
  decodeKxmTuiInput,
1415
1879
  deleteBackward,
1880
+ evaluatePlanOptimizations,
1881
+ evaluateRoleBudget,
1882
+ exportWorkflowHistory,
1416
1883
  fieldIsEditable,
1884
+ filterHistoryEvents,
1885
+ filterModelOptions,
1417
1886
  firstSelectableFieldIndex,
1418
1887
  fitText,
1419
1888
  focusedFieldIsEditable,
1420
1889
  formatAge,
1890
+ formatClaudeOptimizerReport,
1891
+ formatClaudeRoleBudget,
1892
+ formatClaudeTaskHud,
1893
+ formatClaudeUpcomingQueue,
1894
+ formatClaudeWorkflowHistory,
1895
+ formatWorkflowHistoryJsonl,
1421
1896
  groupedKxmTuiChoices,
1422
1897
  initialKxmTuiPanelState,
1423
1898
  insertAt,
@@ -1432,6 +1907,9 @@ export {
1432
1907
  renderKxmTuiPanel,
1433
1908
  renderKxmTuiPanelFrame,
1434
1909
  renderKxmTuiPanelText,
1910
+ renderPiWorkflowWidget,
1911
+ renderWorkflowHistoryMarkdown,
1912
+ reorderQueuedTasks,
1435
1913
  runKxmTuiPanel,
1436
1914
  validateKxmTuiSurface,
1437
1915
  visibleChoices,