@kontextmind/kxm
Version:
KXM local-first multi-agent orchestration and operator dashboard
478 lines (455 loc) • 21.5 kB
text/typescript
import { readFileSync } from "node:fs";
import { join, relative, resolve } from "node:path";
import { redactSecrets } from "../redact.ts";
import {
createMemoryNote,
formatMemoryBriefText,
generateMemoryBrief,
syncHarnessMemory,
type MemoryScope,
} from "../memory.ts";
import { SkillLifecycle, type SkillEvaluationKind, type SkillState } from "../skills.ts";
import { writeCompiledWiki } from "../wiki.ts";
import { CloudTokenError } from "../hub-binding.ts";
import { resolveControlPlaneAuthToken } from "../hub-env.ts";
import { print, printPlan, type Runtime } from "./types.ts";
/** Authenticated hub POST for context operations. The CLI operates as the
* control plane: the administrative token scopes one project per request. */
export async function hubContextPost(input: {
serverUrl: string;
path: string;
body: Record<string, unknown>;
authToken?: string | undefined;
/** When set, a cloud binding's token source replaces the explicit env token. */
env?: NodeJS.ProcessEnv | undefined;
command?: string | undefined;
io?: Runtime["io"] | undefined;
json?: boolean | undefined;
fetchImpl: typeof fetch;
}): Promise<{ ok: boolean; status: number; body: unknown; exitCode?: number }> {
let authToken = input.authToken;
if (input.env) {
try {
authToken = resolveControlPlaneAuthToken(input.env);
} catch (error) {
if (error instanceof CloudTokenError) {
const command = input.command ?? "context";
if (input.io) {
print(input.io, Boolean(input.json), { ok: false, command, error: error.code, detail: error.message }, error.message);
}
return { ok: false, status: 0, body: { error: error.code, detail: error.message }, exitCode: 2 };
}
throw error;
}
}
const response = await input.fetchImpl(`${input.serverUrl.replace(/\/$/, "")}${input.path}`, {
method: "POST",
headers: {
"content-type": "application/json",
...(authToken ? { authorization: `Bearer ${authToken}` } : {}),
},
body: JSON.stringify(input.body),
});
const text = redactSecrets((await response.text()).slice(0, 64_000));
let body: unknown = text;
try {
body = JSON.parse(text);
} catch {
// Keep text for diagnostics without treating it as a secret.
}
return { ok: response.ok, status: response.status, body };
}
export function parseContextKinds(value: string | undefined): string[] | undefined {
if (!value) return undefined;
return value.split(",").map((kind) => kind.trim()).filter((kind) => kind.length > 0);
}
export async function cmdContextGet(runtime: Runtime, project: string, options: { role: string; task: string; run?: string | undefined; stage?: string | undefined; budget?: string | undefined; kinds?: string | undefined }): Promise<number> {
const budget = options.budget === undefined ? undefined : Number(options.budget);
if (options.budget !== undefined && (!Number.isInteger(budget) || (budget as number) < 512 || (budget as number) > 200_000)) {
runtime.io.stderr("context get --budget must be an integer between 512 and 200000\n");
return 2;
}
const body: Record<string, unknown> = {
project,
role: options.role,
task: options.task,
};
if (options.run) body.workflowRunId = options.run;
if (options.stage) body.stageId = options.stage;
if (budget !== undefined) body.budgetTokens = budget;
const kinds = parseContextKinds(options.kinds);
if (kinds) body.includeKinds = kinds;
const response = await hubContextPost({
serverUrl: runtime.serverUrl,
path: "/v1/context/get",
body,
env: runtime.env,
command: "context",
io: runtime.io,
json: runtime.json,
fetchImpl: runtime.fetchImpl,
});
if (response.exitCode) return response.exitCode;
print(runtime.io, runtime.json, { ok: response.ok, command: "context get", status: response.status, ...(response.body as object) }, `context get ${response.ok ? "assembled" : `failed (${response.status})`}`);
return response.ok ? 0 : 1;
}
export async function cmdContextRecall(runtime: Runtime, project: string, options: { query?: string | undefined; kinds?: string | undefined; limit?: string | undefined }): Promise<number> {
const body: Record<string, unknown> = { project };
if (options.query) body.query = options.query;
const kinds = parseContextKinds(options.kinds);
if (kinds) body.kinds = kinds;
if (options.limit) body.limit = Number(options.limit);
const response = await hubContextPost({
serverUrl: runtime.serverUrl,
path: "/v1/context/recall",
body,
env: runtime.env,
command: "context",
io: runtime.io,
json: runtime.json,
fetchImpl: runtime.fetchImpl,
});
if (response.exitCode) return response.exitCode;
print(runtime.io, runtime.json, { ok: response.ok, command: "context recall", status: response.status, ...(response.body as object) }, `context recall ${response.ok ? "complete" : `failed (${response.status})`}`);
return response.ok ? 0 : 1;
}
export async function cmdContextState(runtime: Runtime, project: string, key: string, options: { asOf?: string | undefined }): Promise<number> {
const body: Record<string, unknown> = { project, key };
if (options.asOf) body.asOf = options.asOf;
const response = await hubContextPost({
serverUrl: runtime.serverUrl,
path: "/v1/context/state",
body,
env: runtime.env,
command: "context",
io: runtime.io,
json: runtime.json,
fetchImpl: runtime.fetchImpl,
});
if (response.exitCode) return response.exitCode;
print(runtime.io, runtime.json, { ok: response.ok, command: "context state", status: response.status, ...(response.body as object) }, `context state ${response.ok ? "resolved" : `failed (${response.status})`}`);
return response.ok ? 0 : 1;
}
export async function cmdContextEpisode(runtime: Runtime, project: string, options: { run?: string | undefined }): Promise<number> {
const body: Record<string, unknown> = { project };
if (options.run) body.workflowRunId = options.run;
const response = await hubContextPost({
serverUrl: runtime.serverUrl,
path: "/v1/context/episode",
body,
env: runtime.env,
command: "context",
io: runtime.io,
json: runtime.json,
fetchImpl: runtime.fetchImpl,
});
if (response.exitCode) return response.exitCode;
print(runtime.io, runtime.json, { ok: response.ok, command: "context episode", status: response.status, ...(response.body as object) }, `context episode ${response.ok ? "complete" : `failed (${response.status})`}`);
return response.ok ? 0 : 1;
}
export async function cmdContextPromote(runtime: Runtime, project: string, proposalId: string, options: { evidence: string }): Promise<number> {
const evidence = options.evidence.split(",").map((ref) => ref.trim()).filter((ref) => ref.length > 0);
if (evidence.length === 0) {
runtime.io.stderr("context promote --evidence must contain at least one durable evidence reference\n");
return 2;
}
if (runtime.dryRun) {
printPlan(
runtime,
{ command: "context promote", project, proposalId, evidence },
[{ action: "request", target: `POST ${runtime.serverUrl.replace(/\/$/, "")}/v1/context/state/promote` }],
`promote proposal ${proposalId} in ${project}`,
);
return 0;
}
const response = await hubContextPost({
serverUrl: runtime.serverUrl,
path: "/v1/context/state/promote",
body: { project, proposalId, evidence },
env: runtime.env,
command: "context",
io: runtime.io,
json: runtime.json,
fetchImpl: runtime.fetchImpl,
});
if (response.exitCode) return response.exitCode;
print(runtime.io, runtime.json, { ok: response.ok, command: "context promote", status: response.status, ...(response.body as object) }, `context promote ${response.ok ? "recorded" : `failed (${response.status})`}`);
return response.ok ? 0 : 1;
}
export async function cmdContextExplain(runtime: Runtime, project: string, itemId: string): Promise<number> {
const response = await hubContextPost({
serverUrl: runtime.serverUrl,
path: "/v1/context/explain",
body: { project, id: itemId },
env: runtime.env,
command: "context",
io: runtime.io,
json: runtime.json,
fetchImpl: runtime.fetchImpl,
});
if (response.exitCode) return response.exitCode;
print(runtime.io, runtime.json, { ok: response.ok, command: "context explain", status: response.status, ...(response.body as object) }, `context explain ${response.ok ? "complete" : `failed (${response.status})`}`);
return response.ok ? 0 : 1;
}
export async function cmdContextWikiCompile(runtime: Runtime, project: string, options: { out?: string | undefined }): Promise<number> {
const response = await hubContextPost({
serverUrl: runtime.serverUrl,
path: "/v1/context/wiki/compile",
body: { project },
env: runtime.env,
command: "context",
io: runtime.io,
json: runtime.json,
fetchImpl: runtime.fetchImpl,
});
if (response.exitCode) return response.exitCode;
if (!response.ok) {
print(runtime.io, runtime.json, { ok: false, command: "context wiki-compile", status: response.status, body: response.body }, `wiki compile failed (${response.status})`);
return 1;
}
const compiled = response.body as { audit: { pages: string[]; contradictions: number }; pages: { path: string; content: string }[] };
if (options.out && runtime.dryRun) {
const outDir = options.out;
printPlan(
runtime,
{ command: "context wiki-compile", project, pages: compiled.audit.pages, openContradictions: compiled.audit.contradictions, outDir },
compiled.pages.map((page) => ({ action: "write", target: resolve(outDir, page.path) })),
`write ${compiled.pages.length} compiled wiki page(s) to ${outDir}`,
);
return 0;
}
let written: string[] = [];
if (options.out) {
const pages = new Map(compiled.pages.map((page) => [page.path, page.content]));
written = writeCompiledWiki(options.out, { pages, index: pages.get(".kxm/knowledge/wiki/index.md") ?? "", audit: { project, pages: compiled.audit.pages, stateItems: 0, contextItems: 0, contradictions: compiled.audit.contradictions, compiledAt: "" } });
}
print(runtime.io, runtime.json, { ok: true, command: "context wiki-compile", project, pages: compiled.audit.pages, openContradictions: compiled.audit.contradictions, ...(written.length > 0 ? { written: written.length, outDir: options.out } : { dryRun: true }) }, `compiled ${compiled.audit.pages.length} wiki page(s)${written.length > 0 ? ` to ${options.out}` : " (dry-run)"}`);
return 0;
}
export async function cmdContextWikiLint(runtime: Runtime, project: string): Promise<number> {
const response = await hubContextPost({
serverUrl: runtime.serverUrl,
path: "/v1/context/wiki/compile",
body: { project },
env: runtime.env,
command: "context",
io: runtime.io,
json: runtime.json,
fetchImpl: runtime.fetchImpl,
});
if (response.exitCode) return response.exitCode;
if (!response.ok) {
print(runtime.io, runtime.json, { ok: false, command: "context wiki-lint", status: response.status, body: response.body }, `wiki lint failed (${response.status})`);
return 1;
}
const compiled = response.body as {
audit: { stateItems: number; contextItems: number; contradictions: number; compiledAt: string };
lint: { severity: string; rule: string; path: string; message: string }[];
};
const issues = compiled.lint;
print(runtime.io, runtime.json, { ok: issues.length === 0, command: "context wiki-lint", project, issues, audit: compiled.audit }, issues.length === 0 ? "wiki lint clean" : `wiki lint found ${issues.length} issue(s)`);
return issues.every((issue) => issue.severity !== "error") ? 0 : 1;
}
export function skillStateFromFlag(value: string): SkillState {
if (value === "candidate" || value === "promoted" || value === "quarantined" || value === "rejected") return value;
throw new Error(`invalid skill state ${value}`);
}
export function skillsRoot(runtime: Runtime): string {
return join(runtime.dirs.workdir, ".kxm", "skills");
}
export function csv(value: string | undefined): string[] | undefined {
if (!value) return undefined;
const items = value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0);
return items.length > 0 ? items : undefined;
}
export async function cmdSkillsCreate(runtime: Runtime, options: { file: string; name: string; description?: string | undefined; createdBy: string; run?: string | undefined; journal?: string | undefined; receipt?: string | undefined; harness: string; models: string; supersedes?: string | undefined }): Promise<number> {
if (runtime.dryRun) {
print(runtime.io, runtime.json, { ok: true, command: "skills create", dryRun: true, name: options.name }, "would create skill candidate");
return 0;
}
const lifecycle = new SkillLifecycle(skillsRoot(runtime));
try {
const metadata = lifecycle.create({
name: options.name,
description: options.description ?? "",
content: readFileSync(options.file, "utf8"),
createdBy: options.createdBy,
sources: {
runIds: csv(options.run) ?? [],
journalEntryIds: csv(options.journal) ?? [],
evidenceReceipts: csv(options.receipt) ?? [],
},
compatibility: { harness: options.harness, models: csv(options.models) ?? [] },
...(options.supersedes ? { supersedes: options.supersedes } : {}),
});
print(runtime.io, runtime.json, { ok: true, command: "skills create", metadata }, `created skill candidate ${metadata.id}`);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`skills create failed: ${message}\n`);
return 1;
}
}
export async function cmdSkillsEvaluate(runtime: Runtime, skillId: string, options: { kind: string; evaluator: string; fail?: boolean | undefined; score?: string | undefined; details?: string | undefined }): Promise<number> {
const lifecycle = new SkillLifecycle(skillsRoot(runtime), { dryRun: runtime.dryRun });
try {
const outcome = lifecycle.evaluate(skillId, {
kind: options.kind as SkillEvaluationKind,
evaluatorVersion: options.evaluator,
passed: options.fail !== true,
...(options.score !== undefined ? { score: Number(options.score) } : {}),
...(options.details ? { details: options.details } : {}),
});
if (runtime.dryRun) {
printPlan(runtime, { command: "skills evaluate", skillId, quarantined: outcome.quarantined, evaluation: outcome.evaluation }, lifecycle.planned, `record ${options.kind} evaluation${outcome.quarantined ? " (candidate quarantined)" : ""}`);
return 0;
}
print(runtime.io, runtime.json, { ok: true, command: "skills evaluate", skillId, quarantined: outcome.quarantined, evaluation: outcome.evaluation }, `recorded ${options.kind} evaluation${outcome.quarantined ? " (candidate quarantined)" : ""}`);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`skills evaluate failed: ${message}\n`);
return 1;
}
}
export async function cmdSkillsPromote(runtime: Runtime, skillId: string, options: { decidedBy: string; evidence: string; reason?: string | undefined }): Promise<number> {
const lifecycle = new SkillLifecycle(skillsRoot(runtime), { dryRun: runtime.dryRun });
try {
const promoted = lifecycle.promote(skillId, {
decidedBy: options.decidedBy,
reason: options.reason ?? "passed protected evaluation",
evidenceRefs: csv(options.evidence) ?? [],
});
if (runtime.dryRun) {
printPlan(runtime, { command: "skills promote", skillId, metadata: promoted, patchPath: promoted.patchPath }, lifecycle.planned, `promote skill ${skillId}`);
return 0;
}
print(runtime.io, runtime.json, { ok: true, command: "skills promote", skillId, metadata: promoted, patchPath: promoted.patchPath }, `promoted skill ${skillId} (patch: ${promoted.patchPath})`);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`skills promote failed: ${message}\n`);
return 1;
}
}
export async function cmdSkillsReject(runtime: Runtime, skillId: string, options: { decidedBy: string; reason?: string | undefined }): Promise<number> {
const lifecycle = new SkillLifecycle(skillsRoot(runtime), { dryRun: runtime.dryRun });
try {
const metadata = lifecycle.reject(skillId, { decidedBy: options.decidedBy, reason: options.reason ?? "rejected" });
if (runtime.dryRun) {
printPlan(runtime, { command: "skills reject", skillId, metadata }, lifecycle.planned, `reject skill ${skillId} (history retained)`);
return 0;
}
print(runtime.io, runtime.json, { ok: true, command: "skills reject", skillId, metadata }, `rejected skill ${skillId} (history retained)`);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`skills reject failed: ${message}\n`);
return 1;
}
}
export async function cmdSkillsList(runtime: Runtime, options: { state: string }): Promise<number> {
try {
const lifecycle = new SkillLifecycle(skillsRoot(runtime));
const state = skillStateFromFlag(options.state);
const items = lifecycle.list(state).map((metadata) => ({
id: metadata.id,
name: metadata.name,
version: metadata.version,
createdBy: metadata.createdBy,
createdAt: metadata.createdAt,
models: metadata.compatibility.models,
}));
print(runtime.io, runtime.json, { ok: true, command: "skills list", state: options.state, skills: items }, `${items.length} ${state} skill(s)`);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`skills list failed: ${message}\n`);
return 1;
}
}
export async function cmdSkillsVerify(runtime: Runtime, skillId: string, options: { state: string }): Promise<number> {
try {
const lifecycle = new SkillLifecycle(skillsRoot(runtime));
const state = skillStateFromFlag(options.state);
const metadata = lifecycle.verify(state, skillId);
print(runtime.io, runtime.json, { ok: true, command: "skills verify", skillId, state: options.state, contentSha256: metadata.contentSha256 }, `skill ${skillId} integrity verified`);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`skills verify failed: ${message}\n`);
return 1;
}
}
export async function cmdMemoryBrief(runtime: Runtime): Promise<number> {
try {
const brief = generateMemoryBrief(runtime.cwd);
const text = formatMemoryBriefText(brief);
print(runtime.io, runtime.json, { ok: true, command: "memory brief", brief }, text);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`memory brief failed: ${message}\n`);
return 1;
}
}
export async function cmdMemoryNote(
runtime: Runtime,
fact: string,
options: { scope?: string | undefined; kind?: string | undefined; body?: string | undefined },
): Promise<number> {
try {
const { record, path } = createMemoryNote(runtime.cwd, fact, {
scope: (options.scope ?? "project") as MemoryScope,
...(options.kind !== undefined ? { kind: options.kind } : {}),
...(options.body !== undefined ? { body: options.body } : {}),
dryRun: runtime.dryRun,
});
const relPath = relative(runtime.cwd, path);
if (runtime.dryRun) {
printPlan(runtime, { command: "memory note", candidate: record, path: relPath }, [{ action: "write", target: path }], `record memory candidate in ${relPath} (the id is assigned when it is recorded)`);
return 0;
}
print(
runtime.io,
runtime.json,
{ ok: true, command: "memory note", candidate: record, path: relPath },
`Recorded memory candidate ${record.id} in ${relPath} (promoted via PR)`,
);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`memory note failed: ${message}\n`);
return 1;
}
}
export async function cmdMemorySync(runtime: Runtime): Promise<number> {
try {
const result = syncHarnessMemory(runtime.cwd, { dryRun: runtime.dryRun });
if (runtime.dryRun) {
printPlan(
runtime,
{ command: "memory sync", ...result },
result.updated.map((file) => ({ action: "write", target: join(runtime.cwd, file) })),
"regenerate the project memory block in the instruction files this project already has",
);
return 0;
}
const lines = [
...(result.updated.length > 0 ? [`updated: ${result.updated.join(", ")}`] : []),
...(result.unchanged.length > 0 ? [`unchanged: ${result.unchanged.join(", ")}`] : []),
...(result.missing.length > 0 ? [`not present, not created: ${result.missing.join(", ")}`] : []),
];
print(
runtime.io,
runtime.json,
{ ok: true, command: "memory sync", ...result },
`Synced project memory\n${lines.join("\n")}`,
);
return 0;
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
runtime.io.stderr(`memory sync failed: ${message}\n`);
return 1;
}
}