@kontextmind/kxm
Version:
KXM local-first multi-agent orchestration and operator dashboard
418 lines (402 loc) • 15.1 kB
text/typescript
import { existsSync, readFileSync, readdirSync } from "node:fs";
import { basename, join, resolve } from "node:path";
import { parse as parseYaml } from "yaml";
import { probeHarnessesAsync } from "../harness.ts";
import { suggestWorkflowAndRoles } from "../suggest.ts";
import {
createGoal,
createTask,
listGoals,
listTasks,
getTask,
syncTaskWithTracker,
goalFilePath,
taskFilePath,
type TaskStatus,
type TrackerType,
} from "../task-manager.ts";
import { discoverKxmProjectRoot } from "../project-config.ts";
import { compileKxmWorkflow } from "../engine-compile.ts";
import {
createStudioServer,
DEFAULT_STUDIO_PORT,
generateStudioLayout,
} from "../studio-layout.ts";
import { readSessionTokenFromDisk } from "../commands.ts";
import { print, printPlan, type Runtime } from "./types.ts";
import { cmdKxmRun, resolveKxmRunTarget } from "./project.ts";
export async function cmdSuggest(
runtime: Runtime,
promptParts: string[],
probeHarnesses = probeHarnessesAsync,
): Promise<number> {
try {
const prompt = promptParts.join(" ").trim();
if (!prompt) {
print(runtime.io, runtime.json, { ok: false, command: "suggest", error: "prompt_required" }, "prompt must be non-empty");
return 2;
}
// Native authentication probes may initialize state. A dry run must not invoke them.
const inventory = runtime.dryRun ? undefined : await probeHarnesses({ env: runtime.env });
const suggestion = suggestWorkflowAndRoles(prompt, { availableHarnesses: inventory?.harnesses });
if (suggestion.execution.supported && [".yaml", ".yml"].some((extension) =>
existsSync(join(runtime.cwd, ".kxm", "workflows", `${suggestion.workflowId}${extension}`)))) {
suggestion.roles = [];
suggestion.execution = {
supported: false,
error: "workflow_already_exists",
reason: `Workflow ${suggestion.workflowId} already exists; its agents and permissions may differ from the recommended template.`,
nextSteps: ["Review the existing definition and every agent's harness/model before running it, or install the recommended template under a fresh flat ID. No existing workflow is overwritten or recommended for execution."],
};
}
const execution = suggestion.execution;
const text = [
`Suggested Workflow: ${suggestion.workflowId} (${suggestion.area})`,
`Template: ${suggestion.template}`,
`Confidence: ${(suggestion.confidence * 100).toFixed(0)}%`,
`Reasons: ${suggestion.reasons.join("; ")}`,
`Suggested Skills: ${suggestion.suggestedSkills.join(", ") || "none"}`,
"",
"Install definition only (requires kxm init; does not execute):",
` ${suggestion.suggestedCommand}`,
"",
...(execution.supported ? [
"Suggested agent routing (not applied):",
...suggestion.roles.map((role) => ` ${role.agent}: ${role.harness} (${role.role}; use a configured compatible model)`),
"Prerequisites:",
...execution.prerequisites.map((step) => ` ${step}`),
`Create a run only (${execution.shell}; does not execute steps):`,
` ${execution.createCommand}`,
"Then drive the returned run ID with live calls and inspect its result:",
` ${execution.driveCommand}`,
` ${execution.statusCommand}`,
` ${execution.receiptCommand}`,
] : [
`Execution unavailable: ${execution.reason}`,
...execution.nextSteps.map((step) => ` ${step}`),
...(runtime.dryRun ? ["Harness authentication was not probed during --dry-run."] : []),
]),
].join("\n");
print(runtime.io, runtime.json, {
ok: execution.supported,
command: "suggest",
prompt,
...suggestion,
...(runtime.dryRun ? { dryRun: true } : {}),
...(!execution.supported ? { error: execution.error } : {}),
}, text);
return execution.supported ? 0 : 1;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
print(runtime.io, runtime.json, { ok: false, command: "suggest", error: "suggest_failed", detail: message }, `suggest failed: ${message}`);
return 1;
}
}
export async function cmdGoalCreate(
runtime: Runtime,
title: string,
options: { area?: string | undefined; metric?: string[] | undefined; targetDate?: string | undefined },
): Promise<number> {
try {
const goal = createGoal(runtime.cwd, {
title,
area: options.area,
successMetrics: options.metric,
targetDate: options.targetDate,
}, { dryRun: runtime.dryRun });
if (runtime.dryRun) {
printPlan(runtime, { command: "goal create", goal }, [{ action: "write", target: goalFilePath(runtime.cwd, goal.id) }], `create goal ${goal.title} (the id is assigned when it is created)`);
return 0;
}
print(
runtime.io,
runtime.json,
{ ok: true, command: "goal create", goal },
`Created goal ${goal.id}: ${goal.title}`,
);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`goal create failed: ${message}\n`);
return 1;
}
}
export async function cmdGoalList(runtime: Runtime): Promise<number> {
try {
const goals = listGoals(runtime.cwd);
const text = goals.length === 0
? "No goals recorded in .kxm/goals/"
: goals.map((g) => `[${g.status}] ${g.id}: ${g.title} (${g.area})`).join("\n");
print(runtime.io, runtime.json, { ok: true, command: "goal list", count: goals.length, goals }, text);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`goal list failed: ${message}\n`);
return 1;
}
}
export async function cmdTaskCreate(
runtime: Runtime,
title: string,
options: { goal?: string | undefined; objective?: string | undefined; workflow?: string | undefined; tracker?: string | undefined; issue?: string | undefined },
): Promise<number> {
try {
const task = createTask(runtime.cwd, {
title,
goalId: options.goal,
objective: options.objective ?? title,
assignedWorkflow: options.workflow,
trackerSync: options.tracker && options.issue
? { tracker: options.tracker as TrackerType, issueKey: options.issue }
: undefined,
}, { dryRun: runtime.dryRun });
if (runtime.dryRun) {
printPlan(runtime, { command: "task create", task }, [{ action: "write", target: taskFilePath(runtime.cwd, task.id) }], `create task ${task.title} [${task.status}] (the id is assigned when it is created)`);
return 0;
}
print(
runtime.io,
runtime.json,
{ ok: true, command: "task create", task },
`Created task ${task.id}: ${task.title} [${task.status}]`,
);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`task create failed: ${message}\n`);
return 1;
}
}
export async function cmdTaskList(
runtime: Runtime,
options: { goal?: string | undefined; status?: string | undefined },
): Promise<number> {
try {
const tasks = listTasks(runtime.cwd, {
goalId: options.goal,
status: options.status as TaskStatus | undefined,
});
const text = tasks.length === 0
? "No tasks recorded in .kxm/tasks/"
: tasks.map((t) => `[${t.status}] ${t.id}: ${t.title}${t.assignedWorkflow ? ` -> ${t.assignedWorkflow}` : ""}`).join("\n");
print(runtime.io, runtime.json, { ok: true, command: "task list", count: tasks.length, tasks }, text);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`task list failed: ${message}\n`);
return 1;
}
}
export async function cmdTaskGet(runtime: Runtime, taskId: string): Promise<number> {
try {
const task = getTask(runtime.cwd, taskId);
if (!task) {
runtime.io.stderr(`Task ${taskId} not found\n`);
return 1;
}
const text = [
`Task: ${task.id}`,
`Title: ${task.title}`,
`Status: ${task.status}`,
`Objective: ${task.objective}`,
task.assignedWorkflow ? `Workflow: ${task.assignedWorkflow}` : "",
task.workflowRunId ? `Active Run: ${task.workflowRunId}` : "",
task.trackerSync ? `Tracker: ${task.trackerSync.tracker} (#${task.trackerSync.issueKey}) [${task.trackerSync.syncStatus}]` : "",
].filter(Boolean).join("\n");
print(runtime.io, runtime.json, { ok: true, command: "task get", task }, text);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`task get failed: ${message}\n`);
return 1;
}
}
export async function cmdTaskRun(runtime: Runtime, taskId: string): Promise<number> {
try {
const task = getTask(runtime.cwd, taskId);
if (!task) {
runtime.io.stderr(`Task ${taskId} not found\n`);
return 1;
}
const workflow = task.assignedWorkflow;
if (runtime.dryRun) {
const target = resolveKxmRunTarget(runtime, workflow, true);
if (typeof target === "number") return target;
printPlan(
runtime,
{ command: "task run", taskId, ...target, status: task.status, execution: { status: "not_started", mode: "live" } },
[
{ action: "request", target: "POST kxm-runtime /v1/runs (starts the Runtime supervisor if it is not running)" },
],
`create workflow ${target.workflowId} for task ${taskId}; task status stays ${task.status} until work actually starts`,
);
return 0;
}
return await cmdKxmRun(runtime, workflow, [task.objective], true);
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`task run failed: ${message}\n`);
return 1;
}
}
export async function cmdTaskSync(runtime: Runtime, taskId: string): Promise<number> {
try {
const synced = syncTaskWithTracker(runtime.cwd, taskId, { dryRun: runtime.dryRun });
if (runtime.dryRun) {
printPlan(runtime, { command: "task sync", task: synced }, [{ action: "write", target: taskFilePath(runtime.cwd, taskId) }], `mark task ${taskId} synced with ${synced.trackerSync?.tracker} #${synced.trackerSync?.issueKey}`);
return 0;
}
print(
runtime.io,
runtime.json,
{ ok: true, command: "task sync", task: synced },
`Synced task ${taskId} with ${synced.trackerSync?.tracker} #${synced.trackerSync?.issueKey}`,
);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`task sync failed: ${message}\n`);
return 1;
}
}
export async function cmdStudioLayout(runtime: Runtime, workflowPath?: string | undefined): Promise<number> {
try {
const projectRoot = discoverKxmProjectRoot(runtime.cwd) ?? runtime.cwd;
let filePath = workflowPath;
if (!filePath) {
filePath = resolve(projectRoot, ".kxm", "workflows", "default.yaml");
}
let yamlContent: string;
let workflowId = "default";
if (existsSync(filePath)) {
yamlContent = readFileSync(filePath, "utf8");
} else {
yamlContent = `schema: kxm.workflow.v1
description: Feature implementation workflow
coordinator: coordinator
limits:
maxTransitions: 12
steps:
- id: plan
kind: agent
agent: planner
maxAttempts: 2
on:
passed: implement
failed:
target: $terminal
terminalStatus: failed
- id: implement
kind: agent
agent: writer
maxAttempts: 3
on:
passed: verify
failed:
target: $terminal
terminalStatus: failed
- id: verify
kind: gate
gate: verify-gate
expect: pass
maxAttempts: 2
on:
passed:
target: $terminal
terminalStatus: completed
failed:
target: implement
maxTransitions: 2
`;
}
const parsedYaml = parseYaml(yamlContent) as any;
const plan = compileKxmWorkflow({ id: workflowId, value: parsedYaml });
const layout = generateStudioLayout(plan);
print(runtime.io, runtime.json, { ok: true, command: "studio layout", layout }, JSON.stringify(layout, null, 2));
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`studio layout failed: ${message}\n`);
return 1;
}
}
export async function cmdStudioServe(
runtime: Runtime,
options: { port?: string | undefined; host?: string | undefined; token?: string | undefined },
): Promise<number> {
const port = options.port ? parseInt(options.port, 10) : DEFAULT_STUDIO_PORT;
const host = options.host ?? "127.0.0.1";
const sessionToken = options.token
?? runtime.env.KXM_SESSION_TOKEN
?? readSessionTokenFromDisk({ userConfigDir: runtime.env.KXM_USER_CONFIG_DIR })?.token;
if (runtime.dryRun) {
print(
runtime.io,
runtime.json,
{ ok: true, command: "studio serve", dryRun: true, port, host },
`would start studio server on http://${host}:${port}`,
);
return 0;
}
try {
const serverHandle = createStudioServer({
port,
host,
projectRoot: runtime.cwd,
sessionToken,
planProvider: () => {
try {
const wfDir = join(runtime.cwd, ".kxm", "workflows");
const defaultPath = join(wfDir, "default.yaml");
let targetPath = existsSync(defaultPath) ? defaultPath : undefined;
if (!targetPath && existsSync(wfDir)) {
const yml = readdirSync(wfDir).find((f) => f.endsWith(".yaml") || f.endsWith(".yml"));
if (yml) targetPath = join(wfDir, yml);
}
if (targetPath && existsSync(targetPath)) {
const raw = readFileSync(targetPath, "utf8");
const parsed = parseYaml(raw) as any;
const wfId = basename(targetPath).replace(/\.(yaml|yml)$/, "");
return compileKxmWorkflow({ id: wfId, value: parsed });
}
} catch {
// fallback
}
return undefined;
},
});
const actualPort = await serverHandle.listen();
const info = {
ok: true,
command: "studio serve",
port: actualPort,
host,
url: `http://${host}:${actualPort}`,
};
print(
runtime.io,
runtime.json,
info,
`KXM Web Studio listening on http://${host}:${actualPort} (Decision Q8 & D14)\nPress Ctrl+C to stop.\n`,
);
if (runtime.env.KXM_STUDIO_ONCE) {
await serverHandle.close();
return 0;
}
await new Promise<void>((resolveClose) => {
const shutdown = async () => {
process.off("SIGINT", shutdown);
process.off("SIGTERM", shutdown);
await serverHandle.close();
resolveClose();
};
process.once("SIGINT", shutdown);
process.once("SIGTERM", shutdown);
});
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`studio serve failed: ${message}\n`);
return 1;
}
}