460 lines
14 KiB
JavaScript
460 lines
14 KiB
JavaScript
import { handleRemote } from "../../../remote-questions/mod.js";
|
|
import { getAutoSession } from "../../auto/session.js";
|
|
import { dispatchDirectPhase } from "../../auto-direct-dispatch.js";
|
|
import { handleConfig } from "../../commands-config.js";
|
|
import { handleDebug } from "../../commands-debug.js";
|
|
import { handleEscalate } from "../../commands-escalate.js";
|
|
import {
|
|
handleCapture,
|
|
handleDoctor,
|
|
handleKnowledge,
|
|
handleRunHook,
|
|
handleSkillHealth,
|
|
handleSteer,
|
|
handleTriage,
|
|
handleUpdate,
|
|
} from "../../commands-handlers.js";
|
|
import { handleInspect } from "../../commands-inspect.js";
|
|
import { handleLogs } from "../../commands-logs.js";
|
|
import {
|
|
handleCleanupBranches,
|
|
handleCleanupProjects,
|
|
handleCleanupSnapshots,
|
|
handleCleanupWorktrees,
|
|
handleRecover,
|
|
handleSkip,
|
|
} from "../../commands-maintenance.js";
|
|
import { handlePrBranch } from "../../commands-pr-branch.js";
|
|
import { handleRate } from "../../commands-rate.js";
|
|
import { handleSessionReport } from "../../commands-session-report.js";
|
|
import { handleShip } from "../../commands-ship.js";
|
|
import { handleExport } from "../../export.js";
|
|
import { handleHistory } from "../../history.js";
|
|
import { handleUndo } from "../../undo.js";
|
|
import { projectRoot } from "../context.js";
|
|
export async function handleOpsCommand(trimmed, ctx, pi) {
|
|
if (trimmed === "init") {
|
|
const { detectProjectState } = await import("../../detection.js");
|
|
const { handleReinit, showProjectInit } = await import(
|
|
"../../init-wizard.js"
|
|
);
|
|
const basePath = projectRoot();
|
|
const detection = detectProjectState(basePath);
|
|
if (detection.state === "v2-sf" || detection.state === "v2-sf-empty") {
|
|
await handleReinit(ctx, detection);
|
|
} else {
|
|
await showProjectInit(ctx, pi, basePath, detection);
|
|
}
|
|
return true;
|
|
}
|
|
if (trimmed === "keys" || trimmed.startsWith("keys ")) {
|
|
const { handleKeys } = await import("../../key-manager.js");
|
|
await handleKeys(trimmed.replace(/^keys\s*/, "").trim(), ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "doctor" || trimmed.startsWith("doctor ")) {
|
|
await handleDoctor(trimmed.replace(/^doctor\s*/, "").trim(), ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "repair" || trimmed.startsWith("repair ")) {
|
|
const s = getAutoSession();
|
|
const args = trimmed.replace(/^repair\s*/, "").trim();
|
|
// Switch to repair work mode
|
|
const transition = s.setMode({ workMode: "repair" });
|
|
ctx.ui.notify(
|
|
`Repair mode: ${transition.from.workMode} → ${transition.to.workMode}`,
|
|
"info",
|
|
);
|
|
// If --autonomous flag, also set run control
|
|
if (args.includes("--autonomous")) {
|
|
s.setMode({ runControl: "autonomous" });
|
|
ctx.ui.notify(
|
|
"Repair will run autonomously until clean or blocked.",
|
|
"info",
|
|
);
|
|
}
|
|
// Run doctor diagnostics
|
|
await handleDoctor("fix", ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "uok" || trimmed.startsWith("uok ")) {
|
|
const { handleUok } = await import("../../commands-uok.js");
|
|
await handleUok(trimmed.replace(/^uok\s*/, "").trim(), ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "logs" || trimmed.startsWith("logs ")) {
|
|
await handleLogs(trimmed.replace(/^logs\s*/, "").trim(), ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "debug" || trimmed.startsWith("debug ")) {
|
|
await handleDebug(trimmed.replace(/^debug\s*/, "").trim(), ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "escalate" || trimmed.startsWith("escalate ")) {
|
|
await handleEscalate(trimmed.replace(/^escalate\s*/, "").trim(), ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "forensics" || trimmed.startsWith("forensics ")) {
|
|
const { handleForensics } = await import("../../forensics.js");
|
|
await handleForensics(trimmed.replace(/^forensics\s*/, "").trim(), ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "scan" || trimmed.startsWith("scan ")) {
|
|
const { handleScan } = await import("../../commands-scan.js");
|
|
await handleScan(trimmed.replace(/^scan\s*/, "").trim(), ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "changelog" || trimmed.startsWith("changelog ")) {
|
|
const { handleChangelog } = await import("../../changelog.js");
|
|
await handleChangelog(trimmed.replace(/^changelog\s*/, "").trim(), ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "history" || trimmed.startsWith("history ")) {
|
|
await handleHistory(
|
|
trimmed.replace(/^history\s*/, "").trim(),
|
|
ctx,
|
|
projectRoot(),
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "undo-task" || trimmed.startsWith("undo-task ")) {
|
|
const { handleUndoTask } = await import("../../undo.js");
|
|
await handleUndoTask(
|
|
trimmed.replace(/^undo-task\s*/, "").trim(),
|
|
ctx,
|
|
pi,
|
|
projectRoot(),
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "reset-slice" || trimmed.startsWith("reset-slice ")) {
|
|
const { handleResetSlice } = await import("../../undo.js");
|
|
await handleResetSlice(
|
|
trimmed.replace(/^reset-slice\s*/, "").trim(),
|
|
ctx,
|
|
pi,
|
|
projectRoot(),
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "undo" || trimmed.startsWith("undo ")) {
|
|
await handleUndo(
|
|
trimmed.replace(/^undo\s*/, "").trim(),
|
|
ctx,
|
|
pi,
|
|
projectRoot(),
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "skip") {
|
|
ctx.ui.notify(
|
|
"Usage: /skip <unit-id> Example: /skip M001/S01/T03",
|
|
"warning",
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed.startsWith("skip ")) {
|
|
await handleSkip(
|
|
trimmed.replace(/^skip\s*/, "").trim(),
|
|
ctx,
|
|
projectRoot(),
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "recover") {
|
|
await handleRecover(ctx, projectRoot());
|
|
return true;
|
|
}
|
|
if (trimmed === "rate" || trimmed.startsWith("rate ")) {
|
|
await handleRate(
|
|
trimmed.replace(/^rate\s*/, "").trim(),
|
|
ctx,
|
|
projectRoot(),
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "export" || trimmed.startsWith("export ")) {
|
|
await handleExport(
|
|
trimmed.replace(/^export\s*/, "").trim(),
|
|
ctx,
|
|
projectRoot(),
|
|
);
|
|
return true;
|
|
}
|
|
if (
|
|
trimmed === "cleanup projects" ||
|
|
trimmed.startsWith("cleanup projects ")
|
|
) {
|
|
await handleCleanupProjects(
|
|
trimmed.replace(/^cleanup projects\s*/, "").trim(),
|
|
ctx,
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "cleanup worktrees") {
|
|
await handleCleanupWorktrees(ctx, projectRoot());
|
|
return true;
|
|
}
|
|
if (trimmed === "cleanup") {
|
|
await handleCleanupBranches(ctx, projectRoot());
|
|
await handleCleanupSnapshots(ctx, projectRoot());
|
|
return true;
|
|
}
|
|
if (trimmed === "cleanup branches") {
|
|
await handleCleanupBranches(ctx, projectRoot());
|
|
return true;
|
|
}
|
|
if (trimmed === "cleanup snapshots") {
|
|
await handleCleanupSnapshots(ctx, projectRoot());
|
|
return true;
|
|
}
|
|
if (trimmed.startsWith("capture ") || trimmed === "capture") {
|
|
await handleCapture(trimmed.replace(/^capture\s*/, "").trim(), ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "triage" || trimmed.startsWith("triage ")) {
|
|
await handleTriage(
|
|
trimmed.replace(/^triage\s*/, "").trim(),
|
|
ctx,
|
|
pi,
|
|
process.cwd(),
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "todo" || trimmed.startsWith("todo ")) {
|
|
const { handleTodo } = await import("../../commands-todo.js");
|
|
await handleTodo(trimmed.replace(/^todo\s*/, "").trim(), ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "rate" || trimmed.startsWith("rate ")) {
|
|
const { handleRate } = await import("../../commands-rate.js");
|
|
await handleRate(
|
|
trimmed.replace(/^rate\s*/, "").trim(),
|
|
ctx,
|
|
process.cwd(),
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "config") {
|
|
await handleConfig(ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "hooks") {
|
|
const { formatHookStatus } = await import("../../post-unit-hooks.js");
|
|
ctx.ui.notify(formatHookStatus(), "info");
|
|
return true;
|
|
}
|
|
if (trimmed === "skill-health" || trimmed.startsWith("skill-health ")) {
|
|
await handleSkillHealth(
|
|
trimmed.replace(/^skill-health\s*/, "").trim(),
|
|
ctx,
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed.startsWith("run-hook ")) {
|
|
await handleRunHook(trimmed.replace(/^run-hook\s*/, "").trim(), ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "run-hook") {
|
|
ctx.ui.notify(
|
|
`Usage: /run-hook <hook-name> <unit-type> <unit-id>
|
|
|
|
Unit types:
|
|
execute-task - Task execution (unit-id: M001/S01/T01)
|
|
plan-slice - Slice planning (unit-id: M001/S01)
|
|
research-milestone - Milestone research (unit-id: M001)
|
|
complete-slice - Slice completion (unit-id: M001/S01)
|
|
complete-milestone - Milestone completion (unit-id: M001)
|
|
|
|
Examples:
|
|
/run-hook code-review execute-task M001/S01/T01
|
|
/run-hook lint-check plan-slice M001/S01`,
|
|
"warning",
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed.startsWith("steer ")) {
|
|
await handleSteer(trimmed.replace(/^steer\s+/, "").trim(), ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "steer") {
|
|
ctx.ui.notify(
|
|
"Usage: /steer <description of change>. Example: /steer Use Postgres instead of SQLite",
|
|
"warning",
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed.startsWith("knowledge ")) {
|
|
await handleKnowledge(trimmed.replace(/^knowledge\s+/, "").trim(), ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "knowledge") {
|
|
ctx.ui.notify(
|
|
"Usage: /knowledge <rule|pattern|lesson> <description>. Example: /knowledge rule Use real DB for integration tests",
|
|
"warning",
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "harness" || trimmed.startsWith("harness ")) {
|
|
const { handleHarness } = await import("../../commands-harness.js");
|
|
await handleHarness(trimmed.replace(/^harness\s*/, "").trim(), ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "solver-eval" || trimmed.startsWith("solver-eval ")) {
|
|
const { handleAutonomousSolverEval } = await import(
|
|
"../../autonomous-solver-eval.js"
|
|
);
|
|
await handleAutonomousSolverEval(
|
|
trimmed.replace(/^solver-eval\s*/, "").trim(),
|
|
ctx,
|
|
projectRoot(),
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "migrate" || trimmed.startsWith("migrate ")) {
|
|
const { handleMigrate } = await import("../../migrate/command.js");
|
|
await handleMigrate(trimmed.replace(/^migrate\s*/, "").trim(), ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "remote" || trimmed.startsWith("remote ")) {
|
|
await handleRemote(trimmed.replace(/^remote\s*/, "").trim(), ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "dispatch" || trimmed.startsWith("dispatch ")) {
|
|
const phase = trimmed.replace(/^dispatch\s*/, "").trim();
|
|
if (!phase) {
|
|
ctx.ui.notify(
|
|
"Usage: /dispatch <phase> (research|plan|execute|complete|reassess|uat|replan)",
|
|
"warning",
|
|
);
|
|
return true;
|
|
}
|
|
await dispatchDirectPhase(ctx, pi, phase, projectRoot());
|
|
return true;
|
|
}
|
|
if (trimmed === "notifications" || trimmed.startsWith("notifications ")) {
|
|
const { handleNotificationsCommand } = await import(
|
|
"./notifications-handler.js"
|
|
);
|
|
await handleNotificationsCommand(
|
|
trimmed.replace(/^notifications\s*/, "").trim(),
|
|
ctx,
|
|
pi,
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "inspect") {
|
|
await handleInspect(ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "update") {
|
|
await handleUpdate(ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "fast" || trimmed.startsWith("fast ")) {
|
|
const { handleFast } = await import("../../service-tier.js");
|
|
await handleFast(trimmed.replace(/^fast\s*/, "").trim(), ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "mcp" || trimmed.startsWith("mcp ")) {
|
|
const { handleMcpStatus } = await import("../../commands-mcp-status.js");
|
|
await handleMcpStatus(trimmed.replace(/^mcp\s*/, "").trim(), ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "extensions" || trimmed.startsWith("extensions ")) {
|
|
const { handleExtensions } = await import("../../commands-extensions.js");
|
|
await handleExtensions(trimmed.replace(/^extensions\s*/, "").trim(), ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "rethink") {
|
|
const { handleRethink } = await import("../../rethink.js");
|
|
await handleRethink(trimmed, ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "codebase" || trimmed.startsWith("codebase ")) {
|
|
const { handleCodebase } = await import("../../commands-codebase.js");
|
|
await handleCodebase(trimmed.replace(/^codebase\s*/, "").trim(), ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "ship" || trimmed.startsWith("ship ")) {
|
|
await handleShip(trimmed.replace(/^ship\s*/, "").trim(), ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "session-report" || trimmed.startsWith("session-report ")) {
|
|
await handleSessionReport(
|
|
trimmed.replace(/^session-report\s*/, "").trim(),
|
|
ctx,
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "pr-branch" || trimmed.startsWith("pr-branch ")) {
|
|
await handlePrBranch(trimmed.replace(/^pr-branch\s*/, "").trim(), ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "add-tests" || trimmed.startsWith("add-tests ")) {
|
|
const { handleAddTests } = await import("../../commands-add-tests.js");
|
|
await handleAddTests(trimmed.replace(/^add-tests\s*/, "").trim(), ctx, pi);
|
|
return true;
|
|
}
|
|
if (trimmed === "scaffold sync" || trimmed.startsWith("scaffold sync ")) {
|
|
const { handleScaffoldSync } = await import(
|
|
"../../commands-scaffold-sync.js"
|
|
);
|
|
await handleScaffoldSync(
|
|
trimmed.replace(/^scaffold sync\s*/, "").trim(),
|
|
ctx,
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "scaffold") {
|
|
ctx.ui.notify(
|
|
"Usage: /scaffold sync [--dry-run] [--include-editing] [--only=<glob>]",
|
|
"warning",
|
|
);
|
|
return true;
|
|
}
|
|
if (
|
|
trimmed === "extract-learnings" ||
|
|
trimmed.startsWith("extract-learnings ")
|
|
) {
|
|
const { handleExtractLearnings } = await import(
|
|
"../../commands-extract-learnings.js"
|
|
);
|
|
await handleExtractLearnings(
|
|
trimmed.replace(/^extract-learnings\s*/, "").trim(),
|
|
ctx,
|
|
pi,
|
|
);
|
|
return true;
|
|
}
|
|
if (
|
|
trimmed === "worktree" ||
|
|
trimmed.startsWith("worktree ") ||
|
|
trimmed === "wt" ||
|
|
trimmed.startsWith("wt ")
|
|
) {
|
|
const { handleWorktree } = await import("../../commands-worktree.js");
|
|
await handleWorktree(trimmed.replace(/^(worktree|wt)\s*/, "").trim(), ctx);
|
|
return true;
|
|
}
|
|
if (trimmed === "eval-review" || trimmed.startsWith("eval-review ")) {
|
|
const { handleEvalReview } = await import("../../commands-eval-review.js");
|
|
await handleEvalReview(
|
|
trimmed.replace(/^eval-review\s*/, "").trim(),
|
|
ctx,
|
|
pi,
|
|
);
|
|
return true;
|
|
}
|
|
if (trimmed === "plan" || trimmed.startsWith("plan ")) {
|
|
const { handlePlan } = await import("../../commands-plan.js");
|
|
const handled = await handlePlan(
|
|
trimmed.replace(/^plan\s*/, "").trim(),
|
|
ctx,
|
|
);
|
|
if (handled) return true;
|
|
ctx.ui.notify("Usage: /plan promote|list|diff|specs ...", "info");
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|