feat: expand wizard setup flow

This commit is contained in:
Peter Steinberger
2026-01-01 19:14:14 +01:00
parent 850cbfe369
commit a72fdf7c26
15 changed files with 1201 additions and 30 deletions

93
src/commands/doctor.ts Normal file
View File

@@ -0,0 +1,93 @@
import { confirm, intro, note, outro } from "@clack/prompts";
import { buildWorkspaceSkillStatus } from "../agents/skills-status.js";
import type { ClawdisConfig } from "../config/config.js";
import {
CONFIG_PATH_CLAWDIS,
readConfigFileSnapshot,
writeConfigFile,
} from "../config/config.js";
import { resolveGatewayService } from "../daemon/service.js";
import type { RuntimeEnv } from "../runtime.js";
import { defaultRuntime } from "../runtime.js";
import { resolveUserPath, sleep } from "../utils.js";
import { healthCommand } from "./health.js";
import {
applyWizardMetadata,
DEFAULT_WORKSPACE,
guardCancel,
printWizardHeader,
} from "./onboard-helpers.js";
function resolveMode(cfg: ClawdisConfig): "local" | "remote" {
return cfg.gateway?.mode === "remote" ? "remote" : "local";
}
export async function doctorCommand(runtime: RuntimeEnv = defaultRuntime) {
printWizardHeader(runtime);
intro("Clawdis doctor");
const snapshot = await readConfigFileSnapshot();
let cfg: ClawdisConfig = snapshot.valid ? snapshot.config : {};
if (snapshot.exists && !snapshot.valid) {
note("Config invalid; doctor will run with defaults.", "Config");
}
const workspaceDir = resolveUserPath(
cfg.agent?.workspace ?? DEFAULT_WORKSPACE,
);
const skillsReport = buildWorkspaceSkillStatus(workspaceDir, { config: cfg });
note(
[
`Eligible: ${skillsReport.skills.filter((s) => s.eligible).length}`,
`Missing requirements: ${
skillsReport.skills.filter(
(s) => !s.eligible && !s.disabled && !s.blockedByAllowlist,
).length
}`,
`Blocked by allowlist: ${
skillsReport.skills.filter((s) => s.blockedByAllowlist).length
}`,
].join("\n"),
"Skills status",
);
let healthOk = false;
try {
await healthCommand({ json: false, timeoutMs: 10_000 }, runtime);
healthOk = true;
} catch (err) {
runtime.error(`Health check failed: ${String(err)}`);
}
if (!healthOk) {
const service = resolveGatewayService();
const loaded = await service.isLoaded({ env: process.env });
if (!loaded) {
note("Gateway daemon not installed.", "Gateway");
} else {
const restart = guardCancel(
await confirm({
message: "Restart gateway daemon now?",
initialValue: true,
}),
runtime,
);
if (restart) {
await service.restart({ stdout: process.stdout });
await sleep(1500);
try {
await healthCommand({ json: false, timeoutMs: 10_000 }, runtime);
} catch (err) {
runtime.error(`Health check failed: ${String(err)}`);
}
}
}
}
cfg = applyWizardMetadata(cfg, { command: "doctor", mode: resolveMode(cfg) });
await writeConfigFile(cfg);
runtime.log(`Updated ${CONFIG_PATH_CLAWDIS}`);
outro("Doctor complete.");
}