@kontextmind/kxm
Version:
KXM local-first multi-agent orchestration and operator dashboard
1,918 lines • 75.3 kB
JavaScript
// packages/core/tui/src/types/surface.ts
var KXM_TUI_CONTRACT_SCHEMA = "kxm.tui-contract.v1";
var KXM_TUI_FIELD_KINDS = ["text", "enum", "choice", "info"];
var KXM_TUI_STATUSES = ["ready", "available", "blocked"];
var KXM_TUI_STEP_STATES = ["pending", "satisfied", "running", "done", "failed"];
var KXM_TUI_NOTICE_LEVELS = ["info", "error"];
var KXM_TUI_SET_ACTION = "set";
var KXM_TUI_CLEAR_ACTION = "clear";
var KXM_TUI_ABORT_ACTION = "abort";
var KXM_TUI_LIMITS = Object.freeze({
sourceMax: 128,
idMax: 128,
choiceIdMax: 256,
labelMax: 48,
choiceLabelMax: 96,
detailMax: 240,
shortDetailMax: 96,
stepDetailMax: 160,
statusTextMax: 240,
valueMax: 4096,
keyPathMax: 128,
outputLineMax: 512,
outputMaxLines: 64,
choicesMax: 128,
actionsMax: 4,
stepsMax: 24,
fieldsMax: 64,
sectionsMax: 32,
orderMax: 1e3
});
var ID_PATTERN = /^[a-z0-9][a-z0-9._:-]*$/;
var SOURCE_PATTERN = /^[A-Za-z0-9@][A-Za-z0-9@/._:-]*$/;
var KEY_PATH_PATTERN = /^[A-Za-z0-9_][A-Za-z0-9_.[\]-]*$/;
var ACTION_KEY_PATTERN = /^[A-Za-z0-9]$/;
function issue(path, code, message) {
return { path, code, message };
}
function isPlainObject(value) {
return typeof value === "object" && value !== null && !Array.isArray(value);
}
function boundedText(issues, path, value, max, required) {
if (value === void 0) {
if (required) issues.push(issue(path, "required", `${path} is required`));
return;
}
if (typeof value !== "string" || value.length === 0) {
issues.push(issue(path, "invalid_string", `${path} must be a non-empty string`));
return;
}
if (value.length > max) {
issues.push(issue(path, "too_long", `${path} exceeds ${max} characters`));
return;
}
if (/[\u0000-\u0008\u000a-\u001f\u007f]/u.test(value)) {
issues.push(issue(path, "control_character", `${path} must not contain control characters`));
}
}
function optionalEnum(issues, path, value, allowed) {
if (value === void 0) return;
if (typeof value !== "string" || !allowed.includes(value)) {
issues.push(issue(path, "invalid_choice", `${path} must be one of ${allowed.join(", ")}`));
}
}
function ratioField(issues, path, value) {
if (value === void 0) return;
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1) {
issues.push(issue(path, "invalid_ratio", `${path} must be a finite number between 0 and 1`));
}
}
function validateChoice(issues, path, raw) {
if (!isPlainObject(raw)) {
issues.push(issue(path, "invalid_object", `${path} must be an object`));
return;
}
boundedText(issues, `${path}.id`, raw.id, KXM_TUI_LIMITS.choiceIdMax, true);
if (typeof raw.id === "string" && /[\s]/u.test(raw.id)) {
issues.push(issue(`${path}.id`, "invalid_choice_id", `${path}.id must not contain whitespace`));
}
boundedText(issues, `${path}.label`, raw.label, KXM_TUI_LIMITS.choiceLabelMax, true);
boundedText(issues, `${path}.detail`, raw.detail, KXM_TUI_LIMITS.shortDetailMax, false);
boundedText(issues, `${path}.group`, raw.group, KXM_TUI_LIMITS.shortDetailMax, false);
boundedText(issues, `${path}.statusText`, raw.statusText, KXM_TUI_LIMITS.shortDetailMax, false);
boundedText(issues, `${path}.action`, raw.action, KXM_TUI_LIMITS.idMax, false);
optionalEnum(issues, `${path}.status`, raw.status, KXM_TUI_STATUSES);
}
function validateAction(issues, path, raw) {
if (!isPlainObject(raw)) {
issues.push(issue(path, "invalid_object", `${path} must be an object`));
return;
}
if (typeof raw.key !== "string" || !ACTION_KEY_PATTERN.test(raw.key)) {
issues.push(issue(`${path}.key`, "invalid_action_key", `${path}.key must be a single alphanumeric key`));
}
boundedText(issues, `${path}.label`, raw.label, KXM_TUI_LIMITS.shortDetailMax, true);
boundedText(issues, `${path}.action`, raw.action, KXM_TUI_LIMITS.idMax, true);
}
function validateStep(issues, path, raw) {
if (!isPlainObject(raw)) {
issues.push(issue(path, "invalid_object", `${path} must be an object`));
return;
}
boundedText(issues, `${path}.label`, raw.label, KXM_TUI_LIMITS.shortDetailMax, true);
boundedText(issues, `${path}.detail`, raw.detail, KXM_TUI_LIMITS.stepDetailMax, false);
optionalEnum(issues, `${path}.state`, raw.state, KXM_TUI_STEP_STATES);
if (raw.state === void 0) issues.push(issue(`${path}.state`, "required", `${path}.state is required`));
}
function validateField(issues, path, raw) {
if (!isPlainObject(raw)) {
issues.push(issue(path, "invalid_object", `${path} must be an object`));
return;
}
boundedText(issues, `${path}.id`, raw.id, KXM_TUI_LIMITS.idMax, true);
if (typeof raw.id === "string" && !ID_PATTERN.test(raw.id)) {
issues.push(issue(`${path}.id`, "invalid_id", `${path}.id must match ${ID_PATTERN}`));
}
boundedText(issues, `${path}.label`, raw.label, KXM_TUI_LIMITS.labelMax, true);
if (raw.kind === void 0) {
issues.push(issue(`${path}.kind`, "required", `${path}.kind is required`));
} else {
optionalEnum(issues, `${path}.kind`, raw.kind, KXM_TUI_FIELD_KINDS);
}
boundedText(issues, `${path}.value`, raw.value, KXM_TUI_LIMITS.valueMax, false);
boundedText(issues, `${path}.placeholder`, raw.placeholder, KXM_TUI_LIMITS.shortDetailMax, false);
boundedText(issues, `${path}.detail`, raw.detail, KXM_TUI_LIMITS.detailMax, false);
boundedText(issues, `${path}.statusText`, raw.statusText, KXM_TUI_LIMITS.statusTextMax, false);
if (raw.updatedAt !== void 0) {
boundedText(issues, `${path}.updatedAt`, raw.updatedAt, 64, false);
if (typeof raw.updatedAt === "string" && !Number.isFinite(Date.parse(raw.updatedAt))) {
issues.push(issue(`${path}.updatedAt`, "invalid_timestamp", `${path}.updatedAt must be an ISO timestamp`));
}
}
optionalEnum(issues, `${path}.status`, raw.status, KXM_TUI_STATUSES);
if (raw.keyPath !== void 0) {
boundedText(issues, `${path}.keyPath`, raw.keyPath, KXM_TUI_LIMITS.keyPathMax, false);
if (typeof raw.keyPath === "string" && !KEY_PATH_PATTERN.test(raw.keyPath)) {
issues.push(issue(`${path}.keyPath`, "invalid_key_path", `${path}.keyPath must be a dotted config key`));
}
}
for (const listKey of ["busy", "awaitingInput"]) {
if (raw[listKey] !== void 0 && typeof raw[listKey] !== "boolean") {
issues.push(issue(`${path}.${listKey}`, "invalid_boolean", `${path}.${listKey} must be a boolean`));
}
}
if (raw.progress !== void 0) {
if (!isPlainObject(raw.progress)) {
issues.push(issue(`${path}.progress`, "invalid_object", `${path}.progress must be an object`));
} else {
boundedText(issues, `${path}.progress.label`, raw.progress.label, KXM_TUI_LIMITS.shortDetailMax, true);
ratioField(issues, `${path}.progress.ratio`, raw.progress.ratio);
}
}
for (const [listKey, max, check] of [
["choices", KXM_TUI_LIMITS.choicesMax, validateChoice],
["actions", KXM_TUI_LIMITS.actionsMax, validateAction],
["steps", KXM_TUI_LIMITS.stepsMax, validateStep]
]) {
const list = raw[listKey];
if (list === void 0) continue;
if (!Array.isArray(list) || list.length > max) {
issues.push(issue(`${path}.${listKey}`, "invalid_list", `${path}.${listKey} must be an array of at most ${max}`));
continue;
}
list.forEach((entry, index) => check(issues, `${path}.${listKey}[${index}]`, entry));
}
const output = raw.output;
if (output !== void 0) {
if (!Array.isArray(output) || output.length > KXM_TUI_LIMITS.outputMaxLines) {
issues.push(issue(`${path}.output`, "invalid_list", `${path}.output must be an array of at most ${KXM_TUI_LIMITS.outputMaxLines} lines`));
} else {
output.forEach((line, index) => {
if (typeof line !== "string") issues.push(issue(`${path}.output[${index}]`, "invalid_string", "output lines must be strings"));
else if (line.length > KXM_TUI_LIMITS.outputLineMax) issues.push(issue(`${path}.output[${index}]`, "too_long", "output line is too long"));
});
}
}
}
function validateSection(issues, path, raw) {
if (!isPlainObject(raw)) {
issues.push(issue(path, "invalid_object", `${path} must be an object`));
return;
}
boundedText(issues, `${path}.id`, raw.id, KXM_TUI_LIMITS.idMax, true);
if (typeof raw.id === "string" && !ID_PATTERN.test(raw.id)) {
issues.push(issue(`${path}.id`, "invalid_id", `${path}.id must match ${ID_PATTERN}`));
}
boundedText(issues, `${path}.title`, raw.title, KXM_TUI_LIMITS.labelMax, true);
boundedText(issues, `${path}.detail`, raw.detail, KXM_TUI_LIMITS.shortDetailMax, false);
boundedText(issues, `${path}.notice`, raw.notice, KXM_TUI_LIMITS.detailMax, false);
optionalEnum(issues, `${path}.noticeLevel`, raw.noticeLevel, KXM_TUI_NOTICE_LEVELS);
if (typeof raw.order !== "number" || !Number.isInteger(raw.order) || raw.order < 0 || raw.order > KXM_TUI_LIMITS.orderMax) {
issues.push(issue(`${path}.order`, "invalid_order", `${path}.order must be an integer between 0 and ${KXM_TUI_LIMITS.orderMax}`));
}
if (!Array.isArray(raw.fields) || raw.fields.length > KXM_TUI_LIMITS.fieldsMax) {
issues.push(issue(`${path}.fields`, "invalid_list", `${path}.fields must be an array of at most ${KXM_TUI_LIMITS.fieldsMax}`));
return;
}
const seen = /* @__PURE__ */ new Set();
raw.fields.forEach((field, index) => {
const fieldPath = `${path}.fields[${index}]`;
validateField(issues, fieldPath, field);
if (isPlainObject(field) && typeof field.id === "string") {
if (seen.has(field.id)) issues.push(issue(`${fieldPath}.id`, "duplicate_id", `${field.id} is published twice in ${path}`));
seen.add(field.id);
}
});
}
function validateKxmTuiSurface(sections) {
if (!Array.isArray(sections)) return [issue("sections", "invalid_list", "sections must be an array")];
if (sections.length > KXM_TUI_LIMITS.sectionsMax) {
return [issue("sections", "too_many", `sections must number at most ${KXM_TUI_LIMITS.sectionsMax}`)];
}
const issues = [];
const seenSources = /* @__PURE__ */ new Map();
sections.forEach((section, index) => {
const path = `sections[${index}]`;
validateSection(issues, path, section);
if (!isPlainObject(section)) return;
boundedText(issues, `${path}.source`, section.source, KXM_TUI_LIMITS.sourceMax, true);
if (typeof section.source === "string" && !SOURCE_PATTERN.test(section.source)) {
issues.push(issue(`${path}.source`, "invalid_source", `${path}.source must be an addressable owner id`));
}
if (typeof section.source === "string" && typeof section.id === "string") {
const ids = seenSources.get(section.source) ?? /* @__PURE__ */ new Set();
seenSources.set(section.source, ids);
if (ids.has(section.id)) issues.push(issue(`${path}.id`, "duplicate_section", `${section.source}/${section.id} is published twice`));
ids.add(section.id);
}
});
return issues;
}
function assertKxmTuiSurface(sections) {
const issues = validateKxmTuiSurface(sections);
if (issues.length > 0) {
const first = issues[0];
throw new Error(`kxm tui surface refused (${issues.length} issue(s)): ${first.path} ${first.code}: ${first.message}`);
}
return sections.slice().sort((a, b) => sectionOrder(a) - sectionOrder(b));
}
function sectionOrder(value) {
return isPlainObject(value) && typeof value.order === "number" ? value.order : Number.MAX_SAFE_INTEGER;
}
function isSelectableField(field) {
return field !== void 0 && field.kind !== "info";
}
function fieldIsEditable(field) {
if (!isSelectableField(field)) return false;
const editable = field;
return editable.kind === "text" || editable.kind === "enum" || editable.kind === "choice" && (editable.choices?.length ?? 0) > 0;
}
function groupedKxmTuiChoices(field) {
const choices = field.choices ?? [];
if (choices.length === 0) return [];
if (choices.every((choice) => choice.group === void 0)) return [{ group: "", choices }];
const groups = [];
const index = /* @__PURE__ */ new Map();
for (const choice of choices) {
const group = choice.group ?? "";
let bucket = index.get(group);
if (!bucket) {
bucket = [];
index.set(group, bucket);
groups.push({ group, choices: bucket });
}
bucket.push(choice);
}
return groups;
}
// packages/core/tui/src/tui/keys.ts
import { decodeKittyPrintable, parseKey } from "@earendil-works/pi-tui";
var KXM_TUI_INPUT_MAX_LENGTH = 4096;
var CONTROL_BYTES = /[\u0000-\u0008\u000a-\u001f\u007f\u0080-\u009f]/u;
var NAMED_KEYS = Object.freeze({
up: "up",
down: "down",
left: "left",
right: "right",
home: "home",
end: "end",
pageup: "pageUp",
pagedown: "pageDown",
enter: "enter",
return: "enter",
escape: "escape",
esc: "escape",
tab: "tab",
backspace: "backspace",
delete: "delete"
});
var CONTROL_BYTES_GLOBAL = /[\u0000-\u0008\u000a-\u001f\u007f\u0080-\u009f]/gu;
function normalizeInputText(value) {
return value.replace(CONTROL_BYTES_GLOBAL, " ").replace(/ {2,}/gu, " ").trim().slice(0, KXM_TUI_INPUT_MAX_LENGTH);
}
function isControlInput(data) {
for (let index = 0; index < data.length; index += 1) {
const code = data.charCodeAt(index);
if (code < 32 || code === 127) return true;
}
return false;
}
function decodeKxmTuiInput(data) {
if (typeof data !== "string" || data.length === 0) return { kind: "ignored" };
const named = parseKey(data);
if (named === "ctrl+c") return { kind: "ctrlC" };
if (named) {
const kind = NAMED_KEYS[named.toLowerCase()];
if (kind) return { kind };
if (named === "space") return { kind: "text", text: " " };
if (Array.from(named).length === 1 && !CONTROL_BYTES.test(named)) {
return { kind: "text", text: named };
}
return { kind: "ignored" };
}
const kitty = decodeKittyPrintable(data);
if (kitty) {
const text = normalizeInputText(kitty);
return text ? { kind: "text", text } : { kind: "ignored" };
}
if (data.startsWith("\x1B")) return { kind: "ignored" };
const pasted = normalizeInputText(data);
return pasted ? { kind: "text", text: pasted } : { kind: "ignored" };
}
function deleteBackward(text) {
const units = Array.from(text);
units.pop();
return units.join("");
}
function insertAt(text, caret, insert, maxLength = KXM_TUI_INPUT_MAX_LENGTH) {
const units = Array.from(text);
const at = Math.max(0, Math.min(caret, units.length));
const added = Array.from(insert);
const merged = [...units.slice(0, at), ...added, ...units.slice(at)].join("");
const next = merged.length > maxLength ? merged.slice(0, maxLength) : merged;
return { text: next, caret: Math.min(next.length, at + added.length) };
}
// packages/core/tui/src/tui/layout.ts
import { truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
var KXM_TUI_ELLIPSIS = "\u2026";
function fitText(text, width) {
if (width <= 0) return "";
if (visibleWidth(text) <= width) return text;
if (!text.includes("\x1B")) {
let out = "";
for (const unit of Array.from(text)) {
if (visibleWidth(out) + visibleWidth(unit) > width - KXM_TUI_ELLIPSIS.length) break;
out += unit;
}
return out + KXM_TUI_ELLIPSIS;
}
return truncateToWidth(text, width, KXM_TUI_ELLIPSIS);
}
function alignRight(left, right, width) {
if (width <= 0) return "";
const detail = fitText(right, Math.max(0, width - 2));
const detailWidth = visibleWidth(detail);
const leftWidth = Math.max(1, width - detailWidth - 1);
const fitted = fitText(left, leftWidth);
const filler = Math.max(1, width - visibleWidth(fitted) - detailWidth);
return `${fitted}${" ".repeat(filler)}${detail}`;
}
function padRight(text, width) {
const missing = width - visibleWidth(text);
return missing > 0 ? text + " ".repeat(missing) : text;
}
function wrapPlain(text, width) {
if (width <= 0) return [];
const lines = [];
let current = "";
for (const word of text.split(/\s+/u).filter(Boolean)) {
if (!current.length) current = word;
else if (visibleWidth(current) + 1 + visibleWidth(word) <= width) current = `${current} ${word}`;
else {
lines.push(current);
current = word;
}
}
if (current.length) lines.push(current);
return lines.length ? lines : [];
}
function formatAge(iso, nowMs) {
if (!iso) return "\u2014";
const at = Date.parse(iso);
if (!Number.isFinite(at)) return "\u2014";
const seconds = Math.floor((nowMs - at) / 1e3);
if (seconds < 0) return "\u2014";
if (seconds < 60) return `${seconds}s`;
const minutes = Math.floor(seconds / 60);
if (minutes < 60) return `${minutes}m${String(seconds % 60).padStart(2, "0")}s`;
const hours = Math.floor(minutes / 60);
if (hours < 24) return `${hours}h${String(minutes % 60).padStart(2, "0")}m`;
const days = Math.floor(hours / 24);
return `${days}d${String(hours % 24).padStart(2, "0")}h`;
}
function progressBar(ratio, width = 10) {
const cells = Math.max(1, width);
if (ratio === void 0 || !Number.isFinite(ratio)) return "\u2591".repeat(cells);
const filled = Math.round(Math.max(0, Math.min(1, ratio)) * cells);
return "\u2588".repeat(filled) + "\u2591".repeat(Math.max(0, cells - filled));
}
var KXM_TUI_MARKS = Object.freeze({
pending: "\u25CB",
active: "\u25B6",
waiting: "\u29D7",
passed: "\u2714",
failed: "\u2716",
cancelled: "\u2298",
blocked: "\u2717",
ready: "\u25CF",
available: "\u25D0",
cursor: "\u25B8",
blankCursor: " "
});
// packages/core/tui/src/tui/theme.ts
var SGR = Object.freeze({
text: "37",
accent: "1;36",
muted: "2",
dim: "2",
success: "1;32",
error: "1;31",
warning: "1;33",
border: "90"
});
function createPlainKxmTuiTheme() {
const passthrough = (text) => text;
return {
colored: false,
fg: (_token, text) => text,
accent: passthrough,
success: passthrough,
error: passthrough,
warning: passthrough,
muted: passthrough,
dim: passthrough,
label: passthrough,
value: passthrough,
key: passthrough,
cursor: passthrough,
headerBg: passthrough,
panelBg: passthrough
};
}
function createKxmTuiAnsiTheme(color) {
if (!color) return createPlainKxmTuiTheme();
const fg = (token, text) => `\x1B[${SGR[token]}m${text}\x1B[0m`;
return {
colored: true,
fg,
accent: (text) => fg("accent", text),
success: (text) => fg("success", text),
error: (text) => fg("error", text),
warning: (text) => fg("warning", text),
muted: (text) => fg("muted", text),
dim: (text) => fg("dim", text),
label: (text) => fg("text", text),
value: (text) => `\x1B[1m${text}\x1B[0m`,
key: (text) => fg("muted", text),
cursor: (text) => fg("accent", text),
headerBg: (text) => `\x1B[1;97;44m${text}\x1B[0m`,
panelBg: (text) => `\x1B[48;5;236m${text}\x1B[0m`
};
}
function createDefaultKxmTuiTheme(env = process.env) {
return createKxmTuiAnsiTheme(!(env.NO_COLOR || env.KXM_TUI_NO_COLOR));
}
function createKxmTuiThemeFromPi(theme, colored = true) {
if (!colored) return createPlainKxmTuiTheme();
const fg = (token, text) => theme.fg(token === "dim" ? "muted" : token, text);
return {
colored: true,
fg,
accent: (text) => fg("accent", text),
success: (text) => fg("success", text),
error: (text) => fg("error", text),
warning: (text) => fg("warning", text),
muted: (text) => fg("muted", text),
dim: (text) => fg("dim", text),
label: (text) => fg("text", text),
value: (text) => fg("accent", text),
key: (text) => fg("muted", text),
cursor: (text) => fg("accent", text),
headerBg: (text) => theme.bg("selectedBg", text),
panelBg: (text) => theme.bg("selectedBg", text)
};
}
// packages/core/tui/src/tui/panel.ts
var PANE_KEYS = /* @__PURE__ */ new Set(["j", "k", "h", "l"]);
function initialKxmTuiPanelState(sections) {
return {
pane: "sections",
mode: "browse",
sectionIndex: 0,
fieldIndex: firstSelectableFieldIndex(sections[0]?.fields ?? []),
choiceIndex: 0,
draft: "",
caret: 0,
filter: "",
filtering: false,
help: false,
pending: []
};
}
function pendingKey(invocation) {
return `${invocation.source}\0${invocation.sectionId}\0${invocation.fieldId}`;
}
function firstSelectableFieldIndex(fields) {
const index = fields.findIndex(isSelectableField);
return index < 0 ? 0 : index;
}
function visibleChoices(field, filter) {
const choices = field?.choices ?? [];
const needle = filter.trim().toLowerCase();
if (!needle) return [...choices];
return choices.filter(
(choice) => `${choice.group ?? ""} ${choice.label} ${choice.id} ${choice.detail ?? ""}`.toLowerCase().includes(needle)
);
}
function currentSection(sections, state) {
return sections[state.sectionIndex];
}
function currentField(sections, state) {
return currentSection(sections, state)?.fields[state.fieldIndex];
}
function reconcileKxmTuiPanelState(state, sections) {
const sectionIndex = Math.min(state.sectionIndex, Math.max(0, sections.length - 1));
const fields = sections[sectionIndex]?.fields ?? [];
let fieldIndex = Math.min(state.fieldIndex, Math.max(0, fields.length - 1));
if (!isSelectableField(fields[fieldIndex])) fieldIndex = firstSelectableFieldIndex(fields);
const field = fields[fieldIndex];
const keepsEdit = state.mode === "edit" && (field?.statusText !== void 0 || field?.awaitingInput === true);
return {
...state,
sectionIndex,
fieldIndex,
mode: keepsEdit ? "edit" : "browse",
choiceIndex: 0,
pending: []
};
}
function focusedFieldIsEditable(sections, state) {
return fieldIsEditable(currentField(sections, state));
}
function reduceKxmTuiInput(state, sections, input) {
return reduceDecoded(state, sections, decodeKxmTuiInput(input));
}
function invokeEffect(state, section, field, action, value) {
const invocation = {
source: section.source,
sectionId: section.id,
fieldId: field.id,
action,
...value === void 0 ? {} : { value }
};
const key = pendingKey(invocation);
return {
state: { ...state, pending: state.pending.includes(key) ? state.pending : [...state.pending, key] },
effects: [{ type: "invoke", invocation }]
};
}
function moveSection(state, sections, delta) {
if (sections.length === 0) return state;
const next = Math.max(0, Math.min(state.sectionIndex + delta, sections.length - 1));
if (next === state.sectionIndex) return state;
return {
...state,
sectionIndex: next,
fieldIndex: firstSelectableFieldIndex(sections[next]?.fields ?? []),
mode: "browse",
choiceIndex: 0,
draft: "",
caret: 0,
filter: "",
filtering: false
};
}
function moveField(state, fields, delta) {
const step = delta < 0 ? -1 : 1;
let next = state.fieldIndex + step;
while (next >= 0 && next < fields.length && !isSelectableField(fields[next])) next += step;
if (next < 0 || next >= fields.length) return state;
return {
...state,
fieldIndex: next,
mode: "browse",
choiceIndex: 0,
draft: "",
caret: 0,
filter: "",
filtering: false
};
}
function moveChoice(state, count, delta) {
if (count === 0) return state;
return { ...state, choiceIndex: Math.max(0, Math.min(state.choiceIndex + delta, count - 1)) };
}
function choiceAt(field, state) {
const choices = visibleChoices(field, state.filter);
return choices[Math.max(0, Math.min(state.choiceIndex, choices.length - 1))];
}
function enterField(state, section, field) {
if (!field) return { state, effects: [] };
if (field.kind === "choice") {
if ((field.choices?.length ?? 0) === 0) {
return { state, effects: [{ type: "notice", message: `${field.label} has nothing to offer yet` }] };
}
return { state: { ...state, mode: "choices", choiceIndex: 0 }, effects: [] };
}
if (field.kind === "enum") {
const choices = field.choices ?? [];
if (choices.length === 0) {
return { state, effects: [{ type: "notice", message: `${field.label} has no values published` }] };
}
const current = choices.findIndex((choice) => choice.id === field.value);
const next = choices[(current + 1) % choices.length];
if (next.status === "blocked") {
return { state, effects: [{ type: "notice", message: next.statusText ?? `${next.label} is unavailable` }] };
}
return invokeEffect(state, section, field, next.action ?? KXM_TUI_SET_ACTION, next.id);
}
if (field.kind !== "text") return { state, effects: [] };
return { state: { ...state, mode: "edit", draft: field.value ?? "", caret: (field.value ?? "").length }, effects: [] };
}
function reduceDecoded(state, sections, decoded) {
const section = sections[state.sectionIndex];
const field = currentField(sections, state);
if (decoded.kind === "ctrlC") {
if (field?.busy && section) {
return invokeEffect(state, section, field, KXM_TUI_ABORT_ACTION);
}
return { state, effects: [{ type: "quit" }] };
}
if (!section) return { state, effects: [{ type: "quit" }] };
if (state.mode === "edit") return reduceEdit(state, section, field, decoded);
if (state.mode === "choices") return reduceChoices(state, section, field, decoded);
return reduceBrowse(state, sections, section, field, decoded);
}
function caretSource(text, caret) {
return Array.from(text).slice(0, caret).join("").length;
}
function reduceEdit(state, section, field, decoded) {
if (!field) return { state: { ...state, mode: "browse" }, effects: [] };
switch (decoded.kind) {
case "escape":
return { state: { ...state, mode: "browse", draft: "", caret: 0 }, effects: [] };
case "enter": {
const value = normalizeInputText(state.draft).trim();
if (!value) return { state: { ...state, mode: "browse", draft: "", caret: 0 }, effects: [] };
const step = invokeEffect(state, section, field, KXM_TUI_SET_ACTION, value);
return { state: { ...step.state, mode: "browse", draft: "", caret: 0 }, effects: step.effects };
}
case "backspace": {
if (state.caret === 0) return { state, effects: [] };
const cut = caretSource(state.draft, state.caret);
const before = deleteBackward(state.draft.slice(0, cut));
const draft = `${before}${state.draft.slice(cut)}`;
return { state: { ...state, draft, caret: Array.from(before).length }, effects: [] };
}
case "delete": {
const units = Array.from(state.draft);
const text = [...units.slice(0, state.caret), ...units.slice(state.caret + 1)].join("");
return { state: { ...state, draft: text }, effects: [] };
}
case "left":
return { state: { ...state, caret: Math.max(0, state.caret - 1) }, effects: [] };
case "right":
return { state: { ...state, caret: Math.min(Array.from(state.draft).length, state.caret + 1) }, effects: [] };
case "home":
return { state: { ...state, caret: 0 }, effects: [] };
case "end":
return { state: { ...state, caret: Array.from(state.draft).length }, effects: [] };
case "text": {
const text = decoded.text ?? "";
if (!text) return { state, effects: [] };
const inserted = insertAt(state.draft, state.caret, text);
return { state: { ...state, draft: inserted.text, caret: inserted.caret }, effects: [] };
}
default:
return { state, effects: [] };
}
}
function reduceChoices(state, section, field, decoded) {
if (!field) return { state: { ...state, mode: "browse" }, effects: [] };
const choices = visibleChoices(field, state.filter);
switch (decoded.kind) {
case "escape":
return { state: { ...state, mode: "browse", filter: "", filtering: false, choiceIndex: 0 }, effects: [] };
case "up":
return { state: moveChoice(state, choices.length, -1), effects: [] };
case "down":
return { state: moveChoice(state, choices.length, 1), effects: [] };
case "pageUp":
return { state: moveChoice(state, choices.length, -8), effects: [] };
case "pageDown":
return { state: moveChoice(state, choices.length, 8), effects: [] };
case "enter": {
const choice = choiceAt(field, state);
if (!choice) return { state, effects: [] };
if (choice.status === "blocked") {
return { state, effects: [{ type: "notice", message: choice.statusText ?? `${choice.label} is unavailable` }] };
}
const clearing = (choice.action ?? KXM_TUI_SET_ACTION) === KXM_TUI_CLEAR_ACTION;
const step = invokeEffect(
state,
section,
field,
choice.action ?? KXM_TUI_SET_ACTION,
clearing ? void 0 : choice.id
);
return { state: { ...step.state, mode: "browse", filter: "", filtering: false }, effects: step.effects };
}
case "backspace": {
if (state.filter.length === 0) return { state: { ...state, filtering: false }, effects: [] };
return { state: { ...state, filter: deleteBackward(state.filter), choiceIndex: 0 }, effects: [] };
}
case "text": {
const text = normalizeInputText(decoded.text ?? "");
if (!text) return { state, effects: [] };
return { state: { ...state, filtering: true, filter: `${state.filter}${text}`, choiceIndex: 0 }, effects: [] };
}
default:
return { state, effects: [] };
}
}
function reduceBrowse(state, sections, section, field, decoded) {
if (state.help && (decoded.kind === "escape" || decoded.kind === "enter")) {
return { state: { ...state, help: false }, effects: [] };
}
if (state.help) return { state, effects: [] };
if (decoded.kind === "text") {
const key = decoded.text ?? "";
if (key === "q") return { state, effects: [{ type: "quit" }] };
if (key === "h" || key === "?") return { state: { ...state, help: true }, effects: [] };
const action = field?.actions?.find((candidate) => candidate.key === key);
if (action && field) return invokeEffect(state, section, field, action.action);
if ((key === "x" || key === "d") && field && field.value !== void 0 && field.kind !== "info") {
return invokeEffect(state, section, field, KXM_TUI_CLEAR_ACTION);
}
if (PANE_KEYS.has(key)) {
if (key === "h") return { state: { ...state, pane: "sections" }, effects: [] };
if (key === "l") return { state: { ...state, pane: "fields" }, effects: [] };
const delta = key === "k" ? -1 : 1;
return {
state: state.pane === "fields" ? moveField(state, section.fields, delta) : moveSection(state, sections, delta),
effects: []
};
}
return { state, effects: [] };
}
switch (decoded.kind) {
case "escape":
return state.pane === "fields" ? { state: { ...state, pane: "sections", mode: "browse" }, effects: [] } : { state, effects: [{ type: "quit" }] };
case "tab":
return { state: { ...state, pane: state.pane === "fields" ? "sections" : "fields" }, effects: [] };
case "right":
return { state: { ...state, pane: "fields" }, effects: [] };
case "left":
return { state: { ...state, pane: "sections" }, effects: [] };
case "up":
return { state: state.pane === "fields" ? moveField(state, section.fields, -1) : moveSection(state, sections, -1), effects: [] };
case "down":
return { state: state.pane === "fields" ? moveField(state, section.fields, 1) : moveSection(state, sections, 1), effects: [] };
case "pageUp":
return { state: moveSection(state, sections, -1), effects: [] };
case "pageDown":
return { state: moveSection(state, sections, 1), effects: [] };
case "enter":
return state.pane === "fields" ? enterField(state, section, field) : { state: { ...state, pane: "fields" }, effects: [] };
default:
return { state, effects: [] };
}
}
// packages/core/tui/src/tui/render.ts
import { stripTerminalSequences } from "@earendil-works/pi-tui";
var SECTION_PANE_RATIO = 1 / 3;
var MIN_PANE_WIDTH = 16;
var PROGRESS_WIDTH = 20;
var OUTPUT_TAIL = 6;
var SPLIT_MIN_WIDTH = 76;
function statusMark(status, theme, fallback) {
switch (status) {
case "ready":
return theme.fg("success", KXM_TUI_MARKS.ready);
case "available":
return theme.fg("accent", KXM_TUI_MARKS.available);
case "blocked":
return theme.fg("error", KXM_TUI_MARKS.blocked);
default:
return theme.dim(fallback);
}
}
function cursorMark(active, theme) {
return active ? theme.cursor(KXM_TUI_MARKS.cursor) : KXM_TUI_MARKS.blankCursor;
}
function paneSplit(width) {
const third = Math.max(MIN_PANE_WIDTH, Math.floor(width * SECTION_PANE_RATIO));
const sections = Math.min(third, Math.max(MIN_PANE_WIDTH, Math.floor(width / 2)));
return { sections, fields: Math.max(MIN_PANE_WIDTH, width - sections - 1) };
}
function fieldIsPending(state, section, field) {
return state.pending.includes(pendingKey({ source: section.source, sectionId: section.id, fieldId: field.id }));
}
function sectionRow(section, focused, pane, theme, width) {
const cursor = cursorMark(focused && pane === "sections", theme);
const flag = section.notice ? theme.fg(section.noticeLevel === "error" ? "error" : "warning", "!") : " ";
const count = section.fields.length;
const unset = section.fields.filter((field) => field.kind !== "info" && field.value === void 0).length;
const right = unset > 0 ? `${unset} unset / ${count}` : `${count} field${count === 1 ? "" : "s"}`;
return fitText(alignRight(`${cursor} ${flag} ${section.title}`, right, width), width);
}
function choiceSuffix(choice) {
const parts = [];
if (choice.detail) parts.push(choice.detail);
if (choice.statusText) parts.push(choice.statusText);
return parts.join(" \xB7 ");
}
function choiceRows(field, state, theme, width) {
const shown = visibleChoices(field, state.filter);
const lines = [];
if (state.filtering) {
lines.push(fitText(` ${theme.dim(`filter: ${state.filter || "(type to filter \xB7 esc clears)"}`)}`, width));
}
if (shown.length === 0) {
lines.push(fitText(` ${theme.dim("no match")}`, width));
return lines;
}
let position = -1;
for (const group of groupedKxmTuiChoices(field)) {
const filtered = shown.filter((choice) => (choice.group ?? "") === group.group);
if (filtered.length === 0) continue;
if (group.group) lines.push(fitText(` ${theme.key(group.group.toUpperCase())}`, width));
for (const choice of filtered) {
position += 1;
lines.push(
fitText(
alignRight(
`${cursorMark(position === state.choiceIndex, theme)} ${statusMark(choice.status, theme, KXM_TUI_MARKS.pending)} ${choice.label}`,
choiceSuffix(choice),
width
),
width
)
);
}
}
return lines;
}
var STEP_MARKS = {
pending: "\xB7",
satisfied: KXM_TUI_MARKS.passed,
running: KXM_TUI_MARKS.active,
done: KXM_TUI_MARKS.passed,
failed: KXM_TUI_MARKS.failed
};
function stepColor(state, theme) {
switch (state) {
case "running":
return (text) => theme.fg("accent", text);
case "done":
return (text) => theme.fg("success", text);
case "failed":
return (text) => theme.fg("error", text);
default:
return theme.dim;
}
}
function stepRows(steps, theme, width) {
return steps.map((step) => {
const paint = stepColor(step.state, theme);
return fitText(alignRight(` ${paint(STEP_MARKS[step.state])} ${step.label}`, step.detail ?? "", width), width);
});
}
function progressRow(field, theme, width) {
const progress = field.progress;
if (!progress) return "";
const ratio = progress.ratio;
const right = ratio === void 0 ? "working" : `${Math.round(ratio * 100)}%`;
return fitText(alignRight(` ${progress.label}`, `${progressBar(ratio, PROGRESS_WIDTH)} ${right}`, width), width);
}
function focusedFieldDetail(field, state, theme, width) {
const lines = [];
if (field.keyPath) lines.push(fitText(` ${theme.dim(`key: ${field.keyPath}`)}`, width));
for (const line of wrapPlain(field.detail ?? "", width - 3)) {
if (line) lines.push(fitText(` ${theme.dim(line)}`, width));
}
for (const line of wrapPlain(field.statusText ?? "", width - 3)) {
if (line) lines.push(fitText(` ${theme.fg("error", line)}`, width));
}
const progress = progressRow(field, theme, width);
if (progress) lines.push(progress);
if (field.steps?.length) lines.push(...stepRows(field.steps, theme, width));
if (field.output?.length) {
lines.push(...field.output.slice(-OUTPUT_TAIL).map((line) => fitText(` ${theme.dim(line)}`, width)));
}
if (field.actions?.length) {
lines.push(fitText(` ${theme.key(field.actions.map((action) => `${action.key} ${action.label}`).join(" \xB7 "))}`, width));
}
if (state.mode === "choices") lines.push(...choiceRows(field, state, theme, width));
if (state.mode === "edit" || field.awaitingInput) {
const draft = state.mode === "edit" ? `${state.draft.slice(0, state.caret)}|${state.draft.slice(state.caret)}` : "|";
lines.push(fitText(` ${theme.dim("\u203A")} ${draft}`, width));
}
return lines;
}
function fieldRows(section, state, theme, width, nowMs) {
const lines = [];
section.fields.forEach((field, index) => {
const focused = state.pane === "fields" && state.fieldIndex === index;
if (field.kind === "info") {
lines.push(fitText(` ${theme.dim(field.label)} ${theme.dim(field.value ?? field.placeholder ?? "")}`, width));
return;
}
const pending = fieldIsPending(state, section, field);
const mark = statusMark(field.status, theme, pending ? "\u25CC" : KXM_TUI_MARKS.pending);
const value = pending && field.value === void 0 ? theme.dim("saving\u2026") : field.value;
const shown = value ?? theme.dim(field.placeholder ?? "not set");
const age = field.updatedAt ? theme.dim(` (${formatAge(field.updatedAt, nowMs)})`) : "";
lines.push(fitText(`${cursorMark(focused, theme)} ${mark} ${alignRight(field.label, `${shown}${age}`, width - 4)}`, width));
if (focused) lines.push(...focusedFieldDetail(field, state, theme, width));
});
return lines;
}
function noticeRows(section, theme, width) {
if (!section?.notice) return [];
const token = section.noticeLevel === "error" ? "error" : "warning";
return wrapPlain(section.notice, width - 2).map((line) => fitText(theme.fg(token, line), width));
}
function singlePaneLines(sections, state, theme, width, nowMs) {
const section = sections[state.sectionIndex];
if (state.pane === "fields" && section) {
return [
fitText(theme.headerBg(alignRight(` ${section.title}`, section.source, width)), width),
...noticeRows(section, theme, width),
...fieldRows(section, state, theme, width, nowMs)
];
}
return [
fitText(theme.headerBg(alignRight(" SECTIONS", `${sections.length}`, width)), width),
...sections.map((candidate, index) => sectionRow(candidate, index === state.sectionIndex, state.pane, theme, width))
];
}
function twoPaneLines(sections, state, theme, width, nowMs) {
const split = paneSplit(width);
const left = [fitText(theme.key(" SECTIONS"), split.sections)];
sections.forEach(
(candidate, index) => left.push(sectionRow(candidate, index === state.sectionIndex, state.pane, theme, split.sections))
);
const right = [];
const section = sections[state.sectionIndex];
if (section) {
right.push(fitText(alignRight(` ${section.title}`, section.source, split.fields), split.fields));
if (section.detail) right.push(fitText(` ${theme.dim(section.detail)}`, split.fields));
right.push(...noticeRows(section, theme, split.fields));
right.push(...fieldRows(section, state, theme, split.fields, nowMs));
} else {
right.push(fitText(theme.dim("no surface is published"), split.fields));
}
const rows = [];
for (let index = 0; index < Math.max(left.length, right.length); index += 1) {
rows.push(fitText(`${padRight(left[index] ?? "", split.sections)} ${right[index] ?? ""}`, width));
}
return rows;
}
function renderKxmTuiPanel(sections, state, theme, options, width = 120) {
const nowMs = options.nowMs ?? Date.now();
const title = `${theme.cursor(options.title)}${options.breadcrumb ? ` ${theme.dim(options.breadcrumb)}` : ""}`;
const header = [fitText(title, width)];
if (state.help) {
const help = (options.helpLines ?? ["no help published"]).flatMap((line) => wrapPlain(line, Math.max(1, width - 2)));
return [...header, ...help.map((line) => fitText(line, width)), fitText(theme.dim("esc close help"), width)];
}
const body = width < SPLIT_MIN_WIDTH ? singlePaneLines(sections, state, theme, width, nowMs) : twoPaneLines(sections, state, theme, width, nowMs);
const footer = options.footer ? [fitText(theme.dim(options.footer), width)] : [];
return [...header, ...body, ...footer];
}
function renderKxmTuiPanelText(sections, state, options, width = 120) {
return `${renderKxmTuiPanel(sections, state, createPlainKxmTuiTheme(), options, width).map((line) => stripTerminalSequences(line)).join("\n")}
`;
}
// packages/core/tui/src/tui/panelComponent.ts
import { Box, HStack, ScrollView, Text, TruncatedText, VStack, Key, matchesKey } from "@earendil-works/pi-tui";
var KXM_TUI_PANEL_HELP = Object.freeze([
"\u2191\u2193 move \xB7 \u2190\u2192 or Tab panes \xB7 PgUp/PgDn section \xB7 enter edit or open list",
"type to filter a list \xB7 x clears a value \xB7 esc backs out \xB7 q quit \xB7 h help"
]);
function rowsComponent() {
const stack = new VStack([], { gap: 0 });
const scroll = new ScrollView(stack, { primary: true, overscroll: "contain", scrollbar: "auto" });
const fill = (rows) => {
stack.clear();
for (const row of rows) stack.addChild(new TruncatedText(row, 0, 0));
};
return { stack, scroll, fill };
}
var KxmTuiPanelComponent = class {
root = new VStack([], { gap: 0 });
options;
body = rowsComponent();
sections;
state;
notice;
unsubscribe;
constructor(options) {
this.options = options;
this.sections = options.registry.getSections();
this.state = initialKxmTuiPanelState(this.sections);
this.unsubscribe = options.registry.subscribe(() => this.refresh());
this.rebuild();
}
getState() {
return this.state;
}
getSections() {
return this.sections;
}
/** Point the cursor at a field, e.g. after opening a named section. */
focus(sectionId, fieldId) {
const sectionIndex = this.sections.findIndex((section) => section.id === sectionId);
if (sectionIndex < 0) return;
const fields = this.sections[sectionIndex]?.fields ?? [];
const fieldIndex = fieldId ? Math.max(0, fields.findIndex((field) => field.id === fieldId)) : this.state.fieldIndex;
this.state = { ...this.state, pane: "fields", sectionIndex, fieldIndex };
this.rebuild();
this.options.requestRender();
}
setStatus(message) {
this.notice = message;
this.rebuild();
this.options.requestRender();
}
refresh() {
const next = this.options.registry.getSections();
this.sections = next;
this.state = reconcileKxmTuiPanelState(this.state, next);
this.rebuild();
this.options.requestRender();
}
handleInput(data) {
if (matchesKey(data, Key.pageUp) || matchesKey(data, Key.ctrl("u"))) {
this.body.scroll.scrollBy(-(this.viewport() - 2));
this.options.requestRender();
return;
}
if (matchesKey(data, Key.pageDown) || matchesKey(data, Key.ctrl("d"))) {
this.body.scroll.scrollBy(this.viewport() - 2);
this.options.requestRender();
return;
}
const step = reduceKxmTuiInput(this.state, this.sections, data);
this.state = step.state;
for (const effect of step.effects) this.run(effect);
this.rebuild();
this.options.requestRender();
}
viewport() {
const height = this.body.scroll.viewportHeight;
return height > 3 ? height : 10;
}
run(effect) {
if (effect.type === "quit") {
this.options.onQuit();
return;
}
if (effect.type === "notice") {
this.notice = effect.message;
return;
}
this.notice = void 0;
void this.options.registry.invoke(effect.invocation).then((result) => {
if (!result.ok) this.setStatus(result.message ?? "the owner refused that change");
else if (this.notice === void 0) this.setStatus(void 0);
});
}
footer() {
const status = this.notice ?? this.options.statusText?.();
const base = "\u2191\u2193 \xB7 \u2190\u2192 panes \xB7 enter edit \xB7 x clear \xB7 esc back \xB7 h help \xB7 q quit";
return status ? `${base} ${status}` : base;
}
rebuild() {
const theme = this.options.theme;
const width = this.options.getWidth?.() ?? 120;
const title = new Box(1, 0, theme.headerBg);
title.addChild(new HStack([
{ component: new Text(theme.cursor(this.options.title), 0, 0), basis: 12, shrink: 1, minSize: 6 },
{
component: new Text(
this.options.breadcrumb ? theme.dim(fitText(this.options.breadcrumb, Math.max(4, width - 16))) : "",
0,
0
),
grow: 1,
shrink: 1,
minSize: 8
}
], { gap: 2 }));
this.body.fill(
renderKxmTuiPanel(this.sections, this.state, theme, {
title: this.options.title,
...this.options.breadcrumb ? { breadcrumb: this.options.breadcrumb } : {},
helpLines: this.options.helpLines ?? KXM_TUI_PANEL_HELP,
footer: this.footer()
}, width)
);
const panel = new Box(0, 0, theme.panelBg);
panel.addChild(this.body.scroll);
this.root.clear();
this.root.addChild(title, { basis: "auto", shrink: 0 });
this.root.addChild(panel, { basis: "auto", grow: 1, shrink: 1, minSize: 1 });
}
handleMouse(event) {
return this.root.handleMouse?.(event);
}
invalidate() {
this.root.invalidate();
}
render(width) {
return this.root.render(width);
}
dispose() {
this.unsubscribe();
this.unsubscribe = () => void 0;
}
};
function createKxmTuiPanelComponent(options) {
return new KxmTuiPanelComponent(options);
}
// packages/core/tui/src/tui/optimizer.ts
function evaluatePlanOptimizations(tasks) {
const proposals = [];
const byStage = /* @__PURE__ */ new Map();
for (const task of tasks) {
const list = byStage.get(task.stageId) ?? [];
list.push(task);
byStage.set(task.stageId, list);
}
for (const [stageId, stageTasks] of byStage) {
if (stageTasks.length >= 2) {
const independent = stageTasks.filter((t) => t.dependencies.length === 0 || t.dependencies.every((dep) => !stageTasks.some((st) => st.id === dep)));
if (independent.length >= 2) {
proposals.push({
id: `opt_fanout_${stageId}`,
category: "concurrency",
title: `Parallelize ${independent.length} tasks in stage "${stageId}"`,
description: `Tasks [${independent.map((t) => t.id).join(", ")}] have zero mutual dependencies and can execute concurrently via kxm_fanout.`,
targetTaskIds: independent.map((t) => t.id),
projectedTimeSavingsSeconds: Math.round(independent.length * 15),
projectedCostSavingsPercent: 0,
selected: true
});
}
}
}
for (const task of tasks) {
const isFrontier = /claude-3-5-sonnet|claude-3-opus|gpt-4o|gpt-5/i.test(task.model);
if (isFrontier && (task.category === "docs" || /doc|readme|markdown/i.test(task.title))) {
proposals.push({
id: `opt_cost_${task.id}`,
category: "cost",
title: `Downscale model for documentation task "${task.title}"`,
description: `Task "${task.id}" is documentation-focused. Routing to a high-throughput lightweight model saves significant token budget.`,
targetTaskIds: [task.id],
projectedTimeSavingsSeconds: 5,
projectedCostSavingsPercent: 65,
suggestedHarness: "pi",
suggestedModel: "qwen/qwen3-coder-plus",
selected: true
});
} else if (isFrontier && (task.category === "lint" || /lint|format|style/i.test(task.title))) {
proposals.push({
id: `opt_lint_${task.id}`,
category: "cost",
title: `Downscale model for formatting/lint task "${task.title}"`,
description: `Task "${task.id}" performs deterministic formatting/linting. Use a fast local/tier-1 helper.`,
targetTaskIds: [task.id],
projectedTimeSavingsSeconds: 8,
projectedCostSavingsPercent: 80,
suggestedHarness: "pi",
suggestedModel: "google/gemini-2.5-flash",
selected: true
});
}
}
const totalTimeSavings = proposals.reduce((acc, p) => acc + p.projectedTimeSavingsSeconds, 0);
const totalCostSavings = proposals.length > 0 ? Math.round(proposals.reduce((acc, p) => acc + p.projectedCostSavingsPercent, 0) / proposals.length) : 0;
return {
proposals,
totalProjectedTimeSavingsSeconds: totalTimeSavings,
totalProjectedCostSavingsPercent: totalCostSavings
};
}
function applyPlanOptimizations(tasks, proposals) {
const selected = proposals.filter((p) => p.selected);
if (selected.length === 0) return { tasks: [...tasks], appliedCount: 0 };
const modelOverrides = /* @__PURE__ */ new Map();
for (const proposal of selected) {
if (proposal.category === "cost" && proposal.suggestedModel) {
for (const targetId of proposal.targetTaskIds) {
modelOverrides.set(targetId, {
...proposal.suggestedHarness ? { harness: proposal.suggestedHarness } : {},
model: proposal.suggestedModel
});
}
}
}
const updated = tasks.map((task) => {
const override = modelOverrides.get(task.id);
if (!override) return task;
return {
...task,
...override.harness ? { harness: override.harness } : {},
...override.model ? { model: override.model } : {}
};
});
return {
tasks: updated,
appliedCount: selected.length
};
}
// packages/core/tui/src/tui/history.ts
function filterHistoryEvents(events, options = {}) {
const category = options.category ?? "all";
const query = options.query?.trim().toLowerCase();
return events.filter((ev) => {
if (category !== "all" && ev.category !== category) return false;
if (!query) return true;
const matchTarget = `${ev.id} ${ev.title} ${ev.stageId ?? ""} ${ev.taskId ?? ""} ${ev.role ?? ""} ${ev.model ?? ""} ${ev.journalSummary ?? ""}`.toLowerCase();
return matchTarget.includes(query);
});
}
function renderWorkflowHistoryMarkdown(runId, events) {
const totalSpend = events.reduce((acc, ev) => acc + (ev.costUsd ?? 0), 0);
const passedCount = events.filter((ev) => ev.status === "passed").length;
const failedCount = events.filter((ev) => ev.status === "failed").length;
let mdContent = `# KXM Workflow Retrospective: ${runId}
`;
mdContent += `**Total Events:** ${events.length} (Passed: ${passedCount}, Failed: ${failedCount})
`;
mdContent += `**Total Spend:** $${totalSpend.toFixed(4)} USD
`;
mdContent += `## Chronological Event Journal
`;
mdContent += `| Timestamp | Category | Title | Status | Role / Model | Duration | Spend |
`;
mdContent += `|---|---|---|---|---|---|---|
`;
for (const ev of events) {
const elapsed = ev.durationMs !== void 0 ? `${(ev.durationMs / 1e3).toFixed(1)}s` : "-";
const spend = ev.costUsd !== void 0 ? `$${ev.costUsd.toFixed(4)}` : "-";
const roleModel = ev.role ? `${ev.role} (${ev.harness ?? "-"}/${ev.model ?? "-"})` : "-";
mdContent += `| ${ev.timestamp} | ${ev.category} | ${ev.title} | ${ev.status} | ${roleModel} | ${elapsed} | ${spend} |
`;
}
mdContent += `
## Produced Artifacts & Proofs
`;
for (const ev of events) {
if (ev.artifacts && ev.artifacts.length > 0) {
mdContent += `### ${ev.title} (${ev.id})
`;
for (const art of ev.artifacts) {
mdContent += `- \`${art}\`
`;
}
}
}
return mdContent;
}
function formatWorkflowHistoryJsonl(runId, events) {
return events.map((ev) => JSON.stringify({
schema: "kxm.workflow-history-event.v1",
runId,
...ev
})).join("\n") + "\n";
}
// packages/core/tui/src/tui/queue.ts
function reorderQueuedTasks(tasks, fromIndex, toIndex) {
if (fromIndex < 0 || fromIndex >= tasks.length || toIndex < 0 || toIndex >= tasks.length || fromIndex === toIndex) {
return { success: false, tasks, warning: "invalid_indices" };
}
const target = tasks[fromIndex];
if (target.status === "in_flight" || target.status === "completed") {
return {
success: false,
tasks,
warning: `cannot move ${target.status} task "${target.id}"`
};
}
const draft = [...tasks];
const [removed] = draft.splice(fromIndex, 1);
draft.splice(toIndex, 0, removed);
const idToIndex = /* @__PURE__ */ new Map();
for (let i = 0; i < draft.length; i++) {
idToIndex.set(draft[i].id, i);
}
for (let i = 0; i < draft.length; i++) {
const task = draft[i];
for (const depId of task.dependencies) {
const depIndex = idToIndex.get(depId);
if (depIndex !== void 0 && depIndex > i) {
return {
success: false,
tasks,
warning: `Prerequisite violation: "${task.id}" depends on "${depId}", which is scheduled later at position ${depIndex + 1}`
};
}
}
}
return {
success: true,
tasks: draft
};
}
// packages/core/tui/src/tui/roleBudget.ts
function evaluateRoleBudget(config, state, projectedCostUsd = 0) {
const projectedRunSpend = state.currentRunSpendUsd + projectedCostUsd;
if (config.runSpendCapUsd !== void 0 && projectedRunSpend > config.runSpendCapUsd) {
if (config.onExhausted === "cascade_to_roster" && config.fallbackModel) {
return {
status: "exhausted",
nextAction: "cascade",
fallbackModel: config.fallbackModel,
fallbackHarness: config.fallbackHarness ?? "pi",
warning: `Role "${config.roleId}" reached run spend cap ($${config.runSpendCapUsd.toFixed(2)}); rolling over to ${config.fallbackModel}`
};
}
if (config.onExhausted === "borrow_from_pool" && (config.emergencyPoolLimitUsd ?? 0) > state.borrowedFromPoolUsd) {
return {
status: "exhausted",
nextAction: "borrow",
warning: `Role "${config.roleId}" borrowing from project emergency buffer`
};
}
return {
status: "exhausted",
nextAction: config.onExhausted === "pause_for_approval" ? "pause" : "fail_closed",
warning: `Role "${config.roleId}" exhausted run spend cap ($${config.runSpendCapUsd.toFixed(2)})`
};
}
if (config.monthlySpendCapUsd !== void 0 && state.currentMonthlySpendUsd + projectedCostUsd > config.monthlySpendCapUsd) {
if (config.onExhausted === "cascade_to_roster" && config.fallbackModel) {
return {
status: "exhausted",
nextAction: "cascade",
fallbackModel: config.fallbackModel,
fallbackHarness: config.fallbackHarness ?? "pi",
warning: `Role "${config.roleId}" reached monthly budget limit ($${config.monthlySpendCapUsd.toFixed(2)})`
};
}
return {
status: "exhausted",
nextAction: "pause",
warning: `Role "${config.roleId}" reached monthly budget cap ($${config.monthlySpendCapUsd.toFixed(2)})`
};
}
if (config.runSpendCapUsd && projectedRunSpend / config.runSpendCapUsd >= 0.85) {
return {
status: "near_limit",
nextAction: "proceed",
warning: `Role "${config.roleId}" is at ${Math.round(projectedRunSpend / config.runSpendCapUsd * 100)}% of run budget`
};
}
return {
status: "ok",
nextAction: "proceed"
};
}
// packages/core/tui/src/tui/modelSelector.ts
function filterModelOptions(models, query, providerFilter) {
const q = query?.trim().toLowerCase();
const prov = providerFilter?.trim().toLowerCase();
return models.filter((m) => {
if (prov && m.provider.toLowerCase() !== prov && m.harness.toLowerCase() !== prov) {
return false;
}
if (!q) return true;
const matchTarget = `${m.id} ${m.selector} ${m.name} ${m.provider} ${m.harness}`.toLowerCase();
return matchTarget.includes(q);
});
}
// packages/core/tui/src/services/registry.ts
function describe(error) {
if (error instanceof Error && error.message) return error.message.split(/\r?\n/u)[0].slice(0, KXM_TUI_LIMITS.detailMax);
return String(error).split(/\r?\n/u)[0].slice(0, KXM_TUI_LIMITS.detailMax);
}
function normalizeSource(source) {
if (!/^[A-Za-z0-9@][A-Za-z0-9@/._:-]*$/u.test(source) || source.length > KXM_TUI_LIMITS.sourceMax) {
throw new Error(`invalid kxm tui surface source: ${source.slice(0, 64)}`);
}
return source;
}
function createKxmTuiRegistry(options = {}) {
const contributions = /* @__PURE__ */ new Map();
const notices = /* @__PURE__ */ new Map();
const listeners = /* @__PURE__ */ new Set();
const published = /* @__PURE__ */ new Map();
const issues = /* @__PURE__ */ new Map();
let generation = 0;
let disposed = false;
let epoch = 0;
const report = (error, context) => {
if (options.onInternalError) options.onInternalError(error, context);
};
const emit = (event) => {
for (const listener of Array.from(listeners)) {
try {
listener(event);
} catch (error) {
report(error, "listener");
}
}
};
function readOwner(source, force) {
if (!force) {
const cached = published.get(source);
if (cached) return cached;
}
const contribution = contributions.get(source);
if (!contribution) {
published.set(source, []);
return [];
}
let sections;
try {
const raw = contribution.listSections();
const ownerIssues = validateKxmTuiSurface(raw);
if (ownerIssues.length > 0) {
issues.set(source, ownerIssues);
sections = raw.filter((section) => Boolean(section) && typeof section === "object").map((section) => ({
...section,
notice: section.notice ?? `published surface was refused: ${ownerIssues[0].code} at ${ownerIssues[0].path}`,
noticeLevel: "error"
}));
} else {
issues.delete(source);
sections = raw;
}
} catch (error) {
issues.set(source, [{ path: source, code: "list_sections_failed", message: describe(error) }]);
report(error, `listSections:${source}`);
sections = [{ source, id: "error", title: source, order: 900, notice: describe(error), noticeLevel: "error", fields: [] }];
}
const pending = notices.get(source);
const withNotice = pending !== void 0 && sections.length > 0 ? sections.map((section, index) => index === 0 ? { ...section, notice: pending, noticeLevel: "error" } : section) : sections;
published.set(source, withNotice);
return withNotice;
}
function merged() {
const all = [];
for (const source of contributions.keys()) all.push(...readOwner(source, false));
return all.slice().sort((a, b) => a.order - b.order || a.source.localeCompare(b.source) || a.title.localeCompare(b.title));
}
return {
register(contribution) {
if (disposed) throw new Error("kxm tui registry is disposed");
const source = normalizeSource(contribution.source);
notices.delete(source);
published.delete(source);
issues.delete(source);
contributions.set(source, { ...contribution, source });
emit({ reason: "publish", source });
},
unregister(source) {
contributions.delete(source);
published.delete(source);
issues.delete(source);
notices.delete(source);
emit({ reason: "publish", source });
},
getSections() {
return merged();
},
getIssues() {
return [...issues.values()].flat();
},
async invoke(invocation) {
if (disposed) return { ok: false, message: "kxm tui registry is disposed" };
const contribution = contributions.get(invocation.source);
if (!contribution) {
emit({ reason: "refused", source: invocation.source, message: "unknown owner" });
return { ok: false, message: `${invocation.source} does not own a surface here` };
}
const handler = contribution.handlers[invocation.action];
if (!handler) {
const message = `${invocation.source} does not accept "${invocation.action}"`;
emit({ reason: "refused", source: invocation.source, message });
return { ok: false, message };
}
const currentEpoch = epoch;
try {
await handler({
sectionId: invocation.sectionId,
fieldId: invocation.fieldId,
action: invocation.action,
...invocation.value === void 0 ? {} : { value: invocation.value }
});
} catch (error) {
const failure = contribution.describeError?.(error, invocation.action) ?? describe(error);
notices.set(invocation.source, failure);
emit({ reason: "failed", source: invocation.source, message: failure });
readOwner(invocation.source, true);
generation += 1;
return { ok: false, message: failure };
}
if (currentEpoch !== epoch) {
readOwner(invocation.source, true);
return { ok: false, message: "stale write result was discarded" };
}
notices.delete(invocation.source);
readOwner(invocation.source, true);
generation += 1;
emit({ reason: "publish", source: invocation.source });
return { ok: true };
},
subscribe(listener) {
listeners.add(listener);
return () => listeners.delete(listener);
},
getGeneration() {
return generation;
},
hasSource(source) {
return contributions.has(source);
},
listSources() {
return [...contributions.keys()];
},
dispose() {
if (disposed) return;
disposed = true;
epoch += 1;
listeners.clear();
contributions.clear();
published.clear();
issues.clear();
notices.clear();
}
};
}
function checkedKxmTuiSections(sections) {
return assertKxmTuiSurface(sections);
}
// packages/core/tui/src/adapters/terminal.ts
import { ProcessTerminal, TuiAltScreen, stripTerminalSequences as stripTerminalSequences2 } from "@earendil-works/pi-tui";
function renderKxmTuiPanelFrame(input) {
const theme = input.theme ?? createPlainKxmTuiTheme();
const lines = renderKxmTuiPanel(
input.registry.getSections(),
initialKxmTuiPanelState(input.registry.getSections()),
theme,
{ title: input.title, footer: input.footer ?? "non-interactive frame", nowMs: input.nowMs ?? Date.now() },
input.width ?? 100
);
return `${lines.map((line) => stripTerminalSequences2(line)).join("\n")}
`;
}
async function runKxmTuiPanel(input) {
const theme = input.theme ?? createDefaultKxmTuiTheme();
const tty = input.isTty ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
if (!tty) {
input.stdout(renderKxmTuiPanelFrame({
registry: input.registry,
title: input.title,
theme,
...input.now === void 0 ? {} : { nowMs: input.now() },
...input.breadcrumb ? { footer: input.breadcrumb } : {}
}));
return 0;
}
const terminal = input.terminal ?? new ProcessTerminal();
const tui = new TuiAltScreen(terminal, false, void 0, { mouse: true });
let timer;
let stopped = false;
let finished = () => void 0;
const done = new Promise((resolve) => {
finished = resolve;
});
const stop = () => {
if (stopped) return;
stopped = true;
if (timer) clearInterval(timer);
input.abort?.removeEventListener("abort", stop);
panel.dispose();
tui.stop();
finished();
};
const panel = new KxmTuiPanelComponent({
registry: input.registry,
theme,
title: input.title,
...input.breadcrumb ? { breadcrumb: input.breadcrumb } : {},
...input.helpLines ? { helpLines: input.helpLines } : {},
requestRender: () => tui.requestRender(),
onQuit: stop,
getWidth: () => terminal.columns
});
input.abort?.addEventListener("abort", stop, { once: true });
tui.setLayoutRoot(panel.root);
tui.setFocus(panel);
tui.start();
timer = setInterval(() => panel.refresh(), input.pollMs ?? 2e3);
timer.unref?.();
await done;
return stopped && input.abort?.aborted ? 130 : 0;
}
// packages/core/tui/src/adapters/omp.ts
function resolveKxmTheme(theme) {
if ("headerBg" in theme && typeof theme.headerBg === "function") {
return theme;
}
return createKxmTuiThemeFromPi(theme);
}
function createKxmTuiOmpOverlay(options) {
let closed = false;
let mode = options.initialMode ?? "expanded";
let autoDispatch = Boolean(options.autoDispatch);
const theme = resolveKxmTheme(options.theme);
const panel = new KxmTuiPanelComponent({
registry: options.registry,
theme,
title: options.title,
...options.goal ? { breadcrumb: `Goal: ${options.goal}` } : {},
helpLines: [
"[Ctrl+O/F2] Toggle Fold [h] History [a] Toggle Auto [o] Optimize [Shift+\u2191/\u2193] Reorder"
],
requestRender: () => options.tui.requestRender(),
onQuit: () => {
if (closed) return;
closed = true;
panel.dispose();
options.done?.(true);
},
...options.width === void 0 ? {} : { getWidth: options.width }
});
function renderCollapsed(width) {
const goalText = options.goal ? `Goal: ${options.goal}` : options.title;
const modeBadge = autoDispatch ? theme.accent("[AUTO: ON]") : theme.dim("[STEP MODE]");
const hint = theme.dim("[Ctrl+O to Expand]");
const content = ` [KXM] ${goalText} \u2500\u2500 ${modeBadge} \u2500\u2500 ${hint}`;
const pad = Math.max(0, width - content.length);
return [content + " ".repeat(pad)];
}
function handleInput(data) {
if (closed) return;
if (data === "" || data === "\x1BOQ" || data === "\x1B[12~") {
mode = mode === "collapsed" ? "expanded" : "collapsed";
options.tui.requestRender();
return;
}
if (mode === "collapsed") {
if (data === "\r" || data === " " || data === "o" || data === "O") {
mode = "expanded";
options.tui.requestRender();
}
return;
}
if (data === "a" || data === "A") {
autoDispatch = !autoDispatch;
options.onToggleAutoDispatch?.(autoDispatch);
options.tui.requestRender();
return;
}
if (data === "h" || data === "H") {
mode = mode === "history" ? "expanded" : "history";
options.tui.requestRender();
return;
}
if (data === "o" || data === "O") {
mode = "optimizer";
options.onRunOptimizer?.();
options.tui.requestRender();
return;
}
if ((data === "x" || data === "X") && mode === "history") {
options.onExportHistory?.("both");
options.tui.requestRender();
return;
}
if (data === "\x1B" && mode !== "expanded") {
mode = "expanded";
options.tui.requestRender();
return;
}
panel.handleInput(data);
options.tui.requestRender();
}
return {
getMode: () => mode,
setMode: (nextMode) => {
mode = nextMode;
options.tui.requestRender();
},
isAutoDispatch: () => autoDispatch,
toggleAutoDispatch: () => {
autoDispatch = !autoDispatch;
options.onToggleAutoDispatch?.(autoDispatch);
options.tui.requestRender();
},
render: (width) => {
if (mode === "collapsed") {
return renderCollapsed(width);
}
return panel.render(width);
},
handleInput,
invalidate: () => panel.invalidate(),
dispose: () => {
if (closed) return;
closed = true;
panel.dispose();
options.done?.(true);
}
};
}
// packages/core/tui/src/adapters/pi.ts
var createKxmTuiPiOverlay = createKxmTuiOmpOverlay;
function renderPiWorkflowWidget(options) {
const filled = Math.min(10, Math.max(0, Math.round(options.progressPercent / 100 * 10)));
const bar = `[${"\u2588".repeat(filled)}${"\u2591".repeat(10 - filled)}] ${options.progressPercent}%`;
const mode = options.autoDispatch ? "AUTO" : "STEP";
const line1 = `[KXM] Goal: ${options.goal} \u2500\u2500 Stage: ${options.stage} (${bar}) [${mode}]`;
const line2 = options.activeTask ? ` Active: ${options.activeTask}` : ` Waiting for next dispatch (use /kxm progress)`;
return [line1, line2];
}
function createKxmTuiPiPanel(options) {
let closed = false;
const panel = new KxmTuiPanelComponent({
registry: options.registry,
theme: createKxmTuiThemeFromPi(options.theme),
title: options.title,
...options.breadcrumb === void 0 ? {} : { breadcrumb: options.breadcrumb },
...options.helpLines === void 0 ? {} : { helpLines: options.helpLines },
requestRender: () => options.tui.requestRender(),
onQuit: () => {
if (closed) return;
closed = true;
panel.dispose();
options.done?.(true);
},
...options.width === void 0 ? {} : { getWidth: options.width }
});
return {
render: (width) => panel.render(width),
handleInput: (data) => {
if (closed) return;
panel.handleInput(data);
options.tui.requestRender();
},
invalidate: () => panel.invalidate()
};
}
// packages/core/tui/src/adapters/claude.ts
function formatClaudeTaskHud(options, compact = false) {
const percent = options.totalTasks > 0 ? Math.round(options.completedTasks / options.totalTasks * 100) : 0;
const filled = Math.round(percent / 100 * 10);
const bar = `[${"\u2588".repeat(filled)}${"\u2591".repeat(10 - filled)}] ${percent}%`;
const modeTag = options.autoDispatch ? "[AUTO]" : "[STEP]";
const attemptTag = options.attempt && options.attempt > 1 ? ` (Attempt ${options.attempt}/${options.maxAttempts ?? 3})` : "";
const timeTag = options.elapsedSeconds !== void 0 ? ` \u23F1 ${options.elapsedSeconds}s` : "";
if (compact) {
return `[KXM ${modeTag}] ${options.goal} \u2500\u2500 Stage ${options.stageIndex}/${options.totalStages} ${bar}${attemptTag}${timeTag}`;
}
const activeTask = options.activeTaskTitle ? `
\u2502 Active Task: ${options.activeTaskTitle}${attemptTag} [${options.activeRole ?? "agent"} (${options.activeModel ?? "default"})]` : "";
return [
`\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`,
`\u2502 Goal: ${options.goal}`,
`\u2502 Stage ${options.stageIndex}/${options.totalStages}: ${options.stageName} \u2502 Progress: ${bar}${timeTag} \u2502 Mode: ${modeTag}${activeTask}`,
`\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`
].join("\n");
}
function formatClaudeUpcomingQueue(tasks) {
if (tasks.length === 0) return "No upcoming tasks in queue.";
const lines = ["### KXM Upcoming Task Queue:"];
for (let i = 0; i < tasks.length; i++) {
const t = tasks[i];
const deps = t.dependencies.length > 0 ? ` (Requires: ${t.dependencies.join(", ")})` : " (Ready)";
lines.push(`${i + 1}. [${t.status.toUpperCase()}] **${t.title}** (\`${t.id}\`)`);
lines.push(` Role: \`${t.role}\` [${t.harness}/${t.model}]${deps}`);
}
return lines.join("\n");
}
function formatClaudeOptimizerReport(result) {
if (result.proposals.length === 0) {
return "Plan Optimizer: No pending optimization proposals.";
}
const lines = [
`### KXM Plan Optimizer Proposals:`,
`Total Projected Savings: -${result.totalProjectedTimeSavingsSeconds}s execution time, -${result.totalProjectedCostSavingsPercent}% token cost`,
""
];
for (const p of result.proposals) {
const icon = p.category === "concurrency" ? "\u26A1" : "\u{1F4B0}";
lines.push(`${icon} **${p.title}** (\`${p.id}\`)`);
lines.push(` ${p.description}`);
if (p.suggestedModel) {
lines.push(` Suggested Routing: \`${p.suggestedHarness ?? "pi"}/${p.suggestedModel}\``);
}
lines.push("");
}
return lines.join("\n").trim();
}
function formatClaudeRoleBudget(config, state) {
const runCap = config.runSpendCapUsd !== void 0 ? `$${config.runSpendCapUsd.toFixed(2)}` : "Unmetered";
const monthlyCap = config.monthlySpendCapUsd !== void 0 ? `$${config.monthlySpendCapUsd.toFixed(2)}` : "Unmetered";
const currentRun = `$${state.currentRunSpendUsd.toFixed(4)}`;
const currentMonthly = `$${state.currentMonthlySpendUsd.toFixed(2)}`;
return [
`Role: ${config.roleId} (${config.type})`,
`Run Spend: ${currentRun} / ${runCap}`,
`Monthly Spend: ${currentMonthly} / ${monthlyCap}`,
`On Exhausted: ${config.onExhausted}${config.fallbackModel ? ` (Fallback: ${config.fallbackModel})` : ""}`
].join(" \u2502 ");
}
function formatClaudeWorkflowHistory(runId, events) {
return renderWorkflowHistoryMarkdown(runId, events);
}
// packages/core/tui/src/adapters/historyExport.ts
import { mkdirSync, writeFileSync } from "node:fs";
import { join } from "node:path";
function exportWorkflowHistory(runId, events, destinationDir) {
mkdirSync(destinationDir, { recursive: true });
const sanitizedRunId = runId.replaceAll(/[^a-zA-Z0-9_-]/g, "_");
const mdPath = join(destinationDir, `retrospective-${sanitizedRunId}.md`);
const jsonlPath = join(destinationDir, `history-${sanitizedRunId}.jsonl`);
writeFileSync(mdPath, renderWorkflowHistoryMarkdown(runId, events), "utf8");
writeFileSync(jsonlPath, formatWorkflowHistoryJsonl(runId, events), "utf8");
return { mdPath, jsonlPath };
}
export {
KXM_TUI_ABORT_ACTION,
KXM_TUI_CLEAR_ACTION,
KXM_TUI_CONTRACT_SCHEMA,
KXM_TUI_ELLIPSIS,
KXM_TUI_FIELD_KINDS,
KXM_TUI_INPUT_MAX_LENGTH,
KXM_TUI_LIMITS,
KXM_TUI_MARKS,
KXM_TUI_NOTICE_LEVELS,
KXM_TUI_PANEL_HELP,
KXM_TUI_SET_ACTION,
KXM_TUI_STATUSES,
KXM_TUI_STEP_STATES,
KxmTuiPanelComponent,
alignRight,
applyPlanOptimizations,
assertKxmTuiSurface,
checkedKxmTuiSections,
createDefaultKxmTuiTheme,
createKxmTuiAnsiTheme,
createKxmTuiOmpOverlay,
createKxmTuiPanelComponent,
createKxmTuiPiOverlay,
createKxmTuiPiPanel,
createKxmTuiRegistry,
createKxmTuiThemeFromPi,
createPlainKxmTuiTheme,
currentField,
currentSection,
decodeKxmTuiInput,
deleteBackward,
evaluatePlanOptimizations,
evaluateRoleBudget,
exportWorkflowHistory,
fieldIsEditable,
filterHistoryEvents,
filterModelOptions,
firstSelectableFieldIndex,
fitText,
focusedFieldIsEditable,
formatAge,
formatClaudeOptimizerReport,
formatClaudeRoleBudget,
formatClaudeTaskHud,
formatClaudeUpcomingQueue,
formatClaudeWorkflowHistory,
formatWorkflowHistoryJsonl,
groupedKxmTuiChoices,
initialKxmTuiPanelState,
insertAt,
isControlInput,
isSelectableField,
normalizeInputText,
padRight,
pendingKey,
progressBar,
reconcileKxmTuiPanelState,
reduceKxmTuiInput,
renderKxmTuiPanel,
renderKxmTuiPanelFrame,
renderKxmTuiPanelText,
renderPiWorkflowWidget,
renderWorkflowHistoryMarkdown,
reorderQueuedTasks,
runKxmTuiPanel,
validateKxmTuiSurface,
visibleChoices,
wrapPlain
};