diff --git a/packages/cli/src/__tests__/workflow-cli-e2e.test.ts b/packages/cli/src/__tests__/workflow-cli-e2e.test.ts index e149cda..8803b81 100644 --- a/packages/cli/src/__tests__/workflow-cli-e2e.test.ts +++ b/packages/cli/src/__tests__/workflow-cli-e2e.test.ts @@ -1,5 +1,5 @@ /** - * Smoke / integration tests for `nerve workflow` citty handlers with a real HOME + * Smoke / integration tests for `nerve workflow` and `nerve thread` citty handlers with a real HOME * layout and logs.db. `loadDaemonModule` is mocked so tests use workspace * `@uncaged/nerve-store` directly (no ~/.uncaged-nerve daemon install required). */ @@ -20,9 +20,9 @@ vi.mock("../workspace-daemon.js", async () => { import { createLogStore } from "@uncaged/nerve-store"; -import { workflowCommand } from "../commands/workflow.js"; +import { threadCommand } from "../commands/thread.js"; -describe("nerve workflow CLI (runCommand + temp HOME)", () => { +describe("nerve thread CLI (runCommand + temp HOME)", () => { let prevHome: string | undefined; let fakeHome: string; let stdoutSpy: ReturnType | null; @@ -70,21 +70,19 @@ describe("nerve workflow CLI (runCommand + temp HOME)", () => { rmSync(fakeHome, { recursive: true, force: true }); }); - it("workflow runs --all completes without throwing", async () => { + it("thread list --all completes without throwing", async () => { + await expect(runCommand(threadCommand, { rawArgs: ["list", "--all"] })).resolves.toBeDefined(); + }); + + it("thread inspect completes without throwing", async () => { await expect( - runCommand(workflowCommand, { rawArgs: ["runs", "--all"] }), + runCommand(threadCommand, { rawArgs: ["inspect", "e2e-run", "--limit", "10"] }), ).resolves.toBeDefined(); }); - it("workflow inspect completes without throwing", async () => { + it("thread show completes without throwing (role rounds path)", async () => { await expect( - runCommand(workflowCommand, { rawArgs: ["inspect", "e2e-run", "--limit", "10"] }), - ).resolves.toBeDefined(); - }); - - it("workflow thread completes without throwing (role rounds path)", async () => { - await expect( - runCommand(workflowCommand, { rawArgs: ["thread", "e2e-run", "--budget", "50000"] }), + runCommand(threadCommand, { rawArgs: ["show", "e2e-run", "--budget", "50000"] }), ).resolves.toBeDefined(); }); }); diff --git a/packages/cli/src/__tests__/workflow.test.ts b/packages/cli/src/__tests__/workflow.test.ts index 239e3fa..438e844 100644 --- a/packages/cli/src/__tests__/workflow.test.ts +++ b/packages/cli/src/__tests__/workflow.test.ts @@ -259,7 +259,7 @@ describe("buildListOutput", () => { // header + 2 run lines expect(lines).toHaveLength(3); expect(paginationHint).not.toBeNull(); - expect(paginationHint).toContain("nerve workflow runs"); + expect(paginationHint).toContain("nerve thread list"); expect(paginationHint).toContain("--offset 2"); expect(paginationHint).toContain("3 more"); }); diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index 75c6f3f..cf5987e 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -4,12 +4,9 @@ import { consumeGlobalDaemonCliFlags } from "./cli-global.js"; import { daemonCommand } from "./commands/daemon.js"; import { devCommand } from "./commands/dev.js"; import { initCommand } from "./commands/init.js"; -import { logsCommand } from "./commands/logs.js"; import { senseCommand } from "./commands/sense.js"; -import { daemonStartCommand } from "./commands/start.js"; -import { statusCommand } from "./commands/status.js"; -import { stopCommand } from "./commands/stop.js"; import { storeCommand } from "./commands/store.js"; +import { threadCommand } from "./commands/thread.js"; import { validateCommand } from "./commands/validate.js"; import { workflowCommand } from "./commands/workflow.js"; @@ -43,13 +40,10 @@ const main = defineCommand({ init: initCommand, daemon: daemonCommand, dev: devCommand, - start: daemonStartCommand, - stop: stopCommand, - status: statusCommand, - logs: logsCommand, validate: validateCommand, sense: senseCommand, store: storeCommand, + thread: threadCommand, workflow: workflowCommand, }, }); diff --git a/packages/cli/src/commands/thread.ts b/packages/cli/src/commands/thread.ts new file mode 100644 index 0000000..81ddd44 --- /dev/null +++ b/packages/cli/src/commands/thread.ts @@ -0,0 +1,277 @@ +import { defineCommand } from "citty"; + +import { isRemoteDaemonCli } from "../cli-global.js"; +import { resolveDaemonTransport } from "../daemon-client.js"; +import { isRunning } from "../workspace.js"; +import { + DEFAULT_PAGE_SIZE, + DEFAULT_THREAD_BUDGET_CHARS, + THREAD_ROUNDS_FETCH_LIMIT, + buildInspectOutput, + buildListOutput, + buildThreadCommandOutput, + getAllWorkflowRuns, + openStore, + parseIntArg, +} from "./workflow.js"; + +// --------------------------------------------------------------------------- +// nerve thread list +// --------------------------------------------------------------------------- + +const threadListCommand = defineCommand({ + meta: { + name: "list", + description: "List active (queued/started) workflow runs from logs", + }, + args: { + all: { + type: "boolean", + description: "Include completed/failed/crashed runs", + default: false, + }, + workflow: { + type: "string", + description: "Filter by workflow name", + default: "", + }, + limit: { + type: "string", + description: `Max runs to show (default: ${DEFAULT_PAGE_SIZE})`, + default: String(DEFAULT_PAGE_SIZE), + }, + offset: { + type: "string", + description: "Skip first N runs (for pagination)", + default: "0", + }, + }, + async run({ args }) { + const store = await openStore(); + + try { + const limit = Math.max(1, parseIntArg(args.limit, DEFAULT_PAGE_SIZE)); + const offset = Math.max(0, parseIntArg(args.offset, 0)); + const filterWorkflow = args.workflow.length > 0 ? args.workflow : null; + + const runs = args.all + ? getAllWorkflowRuns(store, filterWorkflow) + : store.getActiveWorkflowRuns(filterWorkflow ?? undefined); + + const { lines, paginationHint } = buildListOutput( + runs, + offset, + limit, + args.all, + filterWorkflow, + ); + + for (const line of lines) { + process.stdout.write(line); + } + if (paginationHint !== null) { + process.stdout.write(paginationHint); + } + } finally { + store.close(); + } + }, +}); + +// --------------------------------------------------------------------------- +// nerve thread show +// --------------------------------------------------------------------------- + +const threadShowCommand = defineCommand({ + meta: { + name: "show", + description: "Print role rounds for a workflow run (agent-oriented, budget-limited)", + }, + args: { + runId: { + type: "positional", + description: "The run ID to dump role rounds for", + }, + before: { + type: "string", + description: + "Exclusive upper bound on 1-based round index (use with hint from prior output to load older rounds)", + default: "0", + }, + budget: { + type: "string", + description: `Max output characters including header (default: ${String(DEFAULT_THREAD_BUDGET_CHARS)})`, + default: String(DEFAULT_THREAD_BUDGET_CHARS), + }, + }, + async run({ args }) { + const store = await openStore(); + + try { + const before = Math.max(0, parseIntArg(args.before, 0)); + const budgetChars = Math.max(1, parseIntArg(args.budget, DEFAULT_THREAD_BUDGET_CHARS)); + + const run = store.getWorkflowRun(args.runId); + if (run === null) { + process.stderr.write(`โŒ No workflow run found with runId: ${args.runId}\n`); + process.exit(1); + } + + const totalRoleRounds = store.getThreadRoundCount(args.runId); + if (totalRoleRounds === 0) { + process.stdout.write( + `๐Ÿงต Workflow thread: ${run.runId}\n workflow: ${run.workflow}\n status: ${run.status}\n\n๐Ÿ“ญ No role rounds recorded for this run.\n`, + ); + return; + } + + const descRows = store.getThreadRounds(args.runId, { + before, + limit: THREAD_ROUNDS_FETCH_LIMIT, + }); + + const prefixLines = [ + "๐Ÿงต Role rounds (workflow thread)\n", + ` runId: ${run.runId}\n`, + ` workflow: ${run.workflow}\n`, + ` status: ${run.status}\n`, + ` rounds: ${String(totalRoleRounds)} role event(s) total\n\n`, + ]; + + const { lines, paginationHint } = buildThreadCommandOutput( + prefixLines, + descRows, + budgetChars, + args.runId, + ); + + for (const line of lines) { + process.stdout.write(line); + } + if (paginationHint !== null) { + process.stdout.write(paginationHint); + } + + if (descRows.length === 0 && before > 0) { + process.stdout.write(`\n๐Ÿ“ญ No rounds with index < ${String(before)}.\n`); + } + } finally { + store.close(); + } + }, +}); + +// --------------------------------------------------------------------------- +// nerve thread inspect +// --------------------------------------------------------------------------- + +const threadInspectCommand = defineCommand({ + meta: { + name: "inspect", + description: "Show details and thread events for a workflow run", + }, + args: { + runId: { + type: "positional", + description: "The run ID to inspect", + }, + limit: { + type: "string", + description: `Max log entries to show (default: ${DEFAULT_PAGE_SIZE})`, + default: String(DEFAULT_PAGE_SIZE), + }, + offset: { + type: "string", + description: "Skip first N log entries (for pagination)", + default: "0", + }, + }, + async run({ args }) { + const store = await openStore(); + + try { + const limit = Math.max(1, parseIntArg(args.limit, DEFAULT_PAGE_SIZE)); + const offset = Math.max(0, parseIntArg(args.offset, 0)); + + const run = store.getWorkflowRun(args.runId); + if (run === null) { + process.stderr.write(`โŒ No workflow run found with runId: ${args.runId}\n`); + process.exit(1); + } + + const allLogs = store.query({ source: "workflow", refId: args.runId }); + const { header, eventLines, paginationHint } = buildInspectOutput( + run, + allLogs, + offset, + limit, + ); + + for (const line of [...header, ...eventLines]) { + process.stdout.write(line); + } + if (paginationHint !== null) { + process.stdout.write(paginationHint); + } + } finally { + store.close(); + } + }, +}); + +// --------------------------------------------------------------------------- +// nerve thread kill +// --------------------------------------------------------------------------- + +const threadKillCommand = defineCommand({ + meta: { + name: "kill", + description: "Kill a running or queued workflow thread by runId", + }, + args: { + runId: { + type: "positional", + description: "The run ID to kill", + }, + }, + async run({ args }) { + if (!isRemoteDaemonCli() && !isRunning()) { + process.stderr.write("โŒ Nerve daemon is not running. Start it with `nerve daemon start`.\n"); + process.exit(1); + } + + const transport = resolveDaemonTransport(); + let response: { ok: true } | { ok: false; error: string }; + try { + response = await transport.killWorkflow(args.runId); + } catch (e) { + const msg = e instanceof Error ? e.message : String(e); + process.stderr.write(`โŒ Failed to contact daemon: ${msg}\n`); + process.exit(1); + } + + if (!response.ok) { + process.stderr.write(`โŒ Kill failed: ${response.error}\n`); + process.exit(1); + } + + process.stdout.write(`โœ… Kill signal sent for run "${args.runId}".\n`); + }, +}); + +// --------------------------------------------------------------------------- +// nerve thread (parent command) +// --------------------------------------------------------------------------- + +export const threadCommand = defineCommand({ + meta: { + name: "thread", + description: "Inspect and manage workflow threads (runs)", + }, + subCommands: { + list: threadListCommand, + show: threadShowCommand, + inspect: threadInspectCommand, + kill: threadKillCommand, + }, +}); diff --git a/packages/cli/src/commands/workflow.ts b/packages/cli/src/commands/workflow.ts index 3b0f040..166340a 100644 --- a/packages/cli/src/commands/workflow.ts +++ b/packages/cli/src/commands/workflow.ts @@ -1,7 +1,7 @@ -import { existsSync } from "node:fs"; +import { existsSync, readFileSync } from "node:fs"; import { join } from "node:path"; -import { DEFAULT_ENGINE_MAX_ROUNDS, isPlainRecord } from "@uncaged/nerve-core"; +import { DEFAULT_ENGINE_MAX_ROUNDS, isPlainRecord, parseNerveConfig } from "@uncaged/nerve-core"; import { defineCommand } from "citty"; import { stringify } from "yaml"; @@ -14,7 +14,7 @@ import { getNerveRoot, isRunning } from "../workspace.js"; export const DEFAULT_PAGE_SIZE = 20; -/** Default max characters for `nerve workflow thread` output (including run header). */ +/** Default max characters for `nerve thread show` output (including run header). */ export const DEFAULT_THREAD_BUDGET_CHARS = 8000; /** Max role-round rows read from SQLite per invocation (DESC by round). */ @@ -50,7 +50,7 @@ export function formatTs(timestampMs: number | null | undefined): string { } } -async function openStore(): Promise { +export async function openStore(): Promise { const nerveRoot = getNerveRoot(); const dbPath = getDbPath(); if (!existsSync(dbPath)) { @@ -143,7 +143,7 @@ export function buildListOutput( const allFlagStr = allFlag ? " --all" : ""; paginationHint = `\nโฉ ${remaining} more run(s) not shown. Fetch next page:\n` + - ` nerve workflow runs --offset ${offset + limit}${allFlagStr}${wfFlag}\n`; + ` nerve thread list --offset ${offset + limit}${allFlagStr}${wfFlag}\n`; } return { lines, paginationHint }; @@ -196,14 +196,14 @@ export function buildInspectOutput( if (remaining > 0) { paginationHint = `\nโฉ ${remaining} more event(s) not shown. Fetch next page:\n` + - ` nerve workflow inspect ${run.runId} --offset ${offset + limit}\n`; + ` nerve thread inspect ${run.runId} --offset ${offset + limit}\n`; } return { header, eventLines, paginationHint }; } // --------------------------------------------------------------------------- -// nerve workflow thread โ€” agent-oriented role rounds +// nerve thread show โ€” agent-oriented role rounds // --------------------------------------------------------------------------- export type PartitionedMessage = { @@ -270,13 +270,13 @@ function buildTruncatedSingleRound( lines: [...prefixLines, single], paginationHint: hintRound > 1 - ? `\nโฉ Older rounds exist. Fetch with:\n nerve workflow thread ${runId} --before ${String(hintRound)}${budgetFlag}\n` + ? `\nโฉ Older rounds exist. Fetch with:\n nerve thread show ${runId} --before ${String(hintRound)}${budgetFlag}\n` : null, }; } /** - * Build stdout lines for `nerve workflow thread`: newest-first selection from + * Build stdout lines for `nerve thread show`: newest-first selection from * `descRows` until `budgetChars` (including `prefixLines`), then chronological order. */ export function buildThreadCommandOutput( @@ -309,24 +309,69 @@ export function buildThreadCommandOutput( const shownMinRound = picked.length === 0 ? null : Math.min(...picked.map((r) => r.round)); let paginationHint: string | null = null; if (shownMinRound !== null && shownMinRound > 1) { - paginationHint = `\nโฉ Older rounds not shown. Fetch with:\n nerve workflow thread ${runId} --before ${String(shownMinRound)}${budgetFlag}\n`; + paginationHint = `\nโฉ Older rounds not shown. Fetch with:\n nerve thread show ${runId} --before ${String(shownMinRound)}${budgetFlag}\n`; } return { lines: [...prefixLines, ...blocksAsc], paginationHint }; } // --------------------------------------------------------------------------- -// nerve workflow list (daemon โ€” registered workflows + queue depth) +// nerve workflow list (reads workflow definitions from workspace YAML) // --------------------------------------------------------------------------- -const workflowDaemonListCommand = defineCommand({ +const workflowListCommand = defineCommand({ meta: { name: "list", - description: "List workflows from the running daemon (concurrency, active, queued)", + description: "List workflow definitions from nerve.yaml", + }, + async run() { + const configPath = join(getNerveRoot(), "nerve.yaml"); + let raw: string; + try { + raw = readFileSync(configPath, "utf8"); + } catch { + process.stderr.write(`โŒ Could not read ${configPath}\n`); + process.exit(1); + } + + const result = parseNerveConfig(raw); + if (!result.ok) { + process.stderr.write(`โŒ Config validation failed: ${result.error.message}\n`); + process.exit(1); + } + + const config = result.value; + const workflowEntries = Object.entries(config.workflows); + + if (workflowEntries.length === 0) { + process.stdout.write("๐Ÿ“ญ No workflows defined in nerve.yaml.\n"); + return; + } + + const rows = workflowEntries.map(([name, wf]) => ({ + name, + concurrency: wf.concurrency, + overflow: wf.overflow, + ...(wf.overflow === "queue" ? { maxQueue: wf.maxQueue } : {}), + })); + + process.stdout.write(`๐Ÿ“‹ Workflow definitions (${String(rows.length)}):\n\n`); + process.stdout.write(formatRowsAsAlignedTable(rows)); + }, +}); + +// --------------------------------------------------------------------------- +// nerve workflow status (daemon โ€” registered workflows + queue depth) +// --------------------------------------------------------------------------- + +const workflowStatusCommand = defineCommand({ + meta: { + name: "status", + description: "Show live workflow status from the running daemon (concurrency, active, queued)", }, async run() { if (!isRemoteDaemonCli() && !isRunning()) { - process.stderr.write("โŒ Nerve daemon is not running. Start it with `nerve start`.\n"); + process.stderr.write("โŒ Nerve daemon is not running. Start it with `nerve daemon start`.\n"); process.exit(1); } @@ -359,210 +404,6 @@ const workflowDaemonListCommand = defineCommand({ }, }); -// --------------------------------------------------------------------------- -// nerve workflow runs -// --------------------------------------------------------------------------- - -const workflowRunsCommand = defineCommand({ - meta: { - name: "runs", - description: "List active (queued/started) workflow runs from logs", - }, - args: { - all: { - type: "boolean", - description: "Include completed/failed/crashed runs", - default: false, - }, - workflow: { - type: "string", - description: "Filter by workflow name", - default: "", - }, - limit: { - type: "string", - description: `Max runs to show (default: ${DEFAULT_PAGE_SIZE})`, - default: String(DEFAULT_PAGE_SIZE), - }, - offset: { - type: "string", - description: "Skip first N runs (for pagination)", - default: "0", - }, - }, - async run({ args }) { - const store = await openStore(); - - try { - const limit = Math.max(1, parseIntArg(args.limit, DEFAULT_PAGE_SIZE)); - const offset = Math.max(0, parseIntArg(args.offset, 0)); - const filterWorkflow = args.workflow.length > 0 ? args.workflow : null; - - const runs = args.all - ? getAllWorkflowRuns(store, filterWorkflow) - : store.getActiveWorkflowRuns(filterWorkflow ?? undefined); - - const { lines, paginationHint } = buildListOutput( - runs, - offset, - limit, - args.all, - filterWorkflow, - ); - - for (const line of lines) { - process.stdout.write(line); - } - if (paginationHint !== null) { - process.stdout.write(paginationHint); - } - } finally { - store.close(); - } - }, -}); - -// --------------------------------------------------------------------------- -// nerve workflow inspect -// --------------------------------------------------------------------------- - -const workflowInspectCommand = defineCommand({ - meta: { - name: "inspect", - description: "Show details and thread events for a workflow run", - }, - args: { - runId: { - type: "positional", - description: "The run ID to inspect", - }, - limit: { - type: "string", - description: `Max log entries to show (default: ${DEFAULT_PAGE_SIZE})`, - default: String(DEFAULT_PAGE_SIZE), - }, - offset: { - type: "string", - description: "Skip first N log entries (for pagination)", - default: "0", - }, - }, - async run({ args }) { - const store = await openStore(); - - try { - const limit = Math.max(1, parseIntArg(args.limit, DEFAULT_PAGE_SIZE)); - const offset = Math.max(0, parseIntArg(args.offset, 0)); - - const run = store.getWorkflowRun(args.runId); - if (run === null) { - process.stderr.write(`โŒ No workflow run found with runId: ${args.runId}\n`); - process.exit(1); - } - - const allLogs = store.query({ source: "workflow", refId: args.runId }); - const { header, eventLines, paginationHint } = buildInspectOutput( - run, - allLogs, - offset, - limit, - ); - - for (const line of [...header, ...eventLines]) { - process.stdout.write(line); - } - if (paginationHint !== null) { - process.stdout.write(paginationHint); - } - } finally { - store.close(); - } - }, -}); - -// --------------------------------------------------------------------------- -// nerve workflow thread -// --------------------------------------------------------------------------- - -const workflowThreadCommand = defineCommand({ - meta: { - name: "thread", - description: "Print role rounds for a workflow run (agent-oriented, budget-limited)", - }, - args: { - runId: { - type: "positional", - description: "The run ID to dump role rounds for", - }, - before: { - type: "string", - description: - "Exclusive upper bound on 1-based round index (use with hint from prior output to load older rounds)", - default: "0", - }, - budget: { - type: "string", - description: `Max output characters including header (default: ${String(DEFAULT_THREAD_BUDGET_CHARS)})`, - default: String(DEFAULT_THREAD_BUDGET_CHARS), - }, - }, - async run({ args }) { - const store = await openStore(); - - try { - const before = Math.max(0, parseIntArg(args.before, 0)); - const budgetChars = Math.max(1, parseIntArg(args.budget, DEFAULT_THREAD_BUDGET_CHARS)); - - const run = store.getWorkflowRun(args.runId); - if (run === null) { - process.stderr.write(`โŒ No workflow run found with runId: ${args.runId}\n`); - process.exit(1); - } - - const totalRoleRounds = store.getThreadRoundCount(args.runId); - if (totalRoleRounds === 0) { - process.stdout.write( - `๐Ÿงต Workflow thread: ${run.runId}\n workflow: ${run.workflow}\n status: ${run.status}\n\n๐Ÿ“ญ No role rounds recorded for this run.\n`, - ); - return; - } - - const descRows = store.getThreadRounds(args.runId, { - before, - limit: THREAD_ROUNDS_FETCH_LIMIT, - }); - - const prefixLines = [ - "๐Ÿงต Role rounds (workflow thread)\n", - ` runId: ${run.runId}\n`, - ` workflow: ${run.workflow}\n`, - ` status: ${run.status}\n`, - ` rounds: ${String(totalRoleRounds)} role event(s) total\n\n`, - ]; - - const { lines, paginationHint } = buildThreadCommandOutput( - prefixLines, - descRows, - budgetChars, - args.runId, - ); - - for (const line of lines) { - process.stdout.write(line); - } - if (paginationHint !== null) { - process.stdout.write(paginationHint); - } - - if (descRows.length === 0 && before > 0) { - process.stdout.write(`\n๐Ÿ“ญ No rounds with index < ${String(before)}.\n`); - } - } finally { - store.close(); - } - }, -}); - // --------------------------------------------------------------------------- // nerve workflow trigger // --------------------------------------------------------------------------- @@ -604,7 +445,7 @@ const workflowTriggerCommand = defineCommand({ } if (!isRemoteDaemonCli() && !isRunning()) { - process.stderr.write("โŒ Nerve daemon is not running. Start it with `nerve start`.\n"); + process.stderr.write("โŒ Nerve daemon is not running. Start it with `nerve daemon start`.\n"); process.exit(1); } @@ -624,47 +465,7 @@ const workflowTriggerCommand = defineCommand({ } process.stdout.write(`โœ… Triggered workflow "${args.name}" via daemon.\n`); - process.stdout.write("\n๐Ÿ’ก Inspect active runs with: nerve workflow runs\n"); - }, -}); - -// --------------------------------------------------------------------------- -// nerve workflow kill -// --------------------------------------------------------------------------- - -const workflowKillCommand = defineCommand({ - meta: { - name: "kill", - description: "Kill a running or queued workflow thread by runId", - }, - args: { - runId: { - type: "positional", - description: "The run ID to kill", - }, - }, - async run({ args }) { - if (!isRemoteDaemonCli() && !isRunning()) { - process.stderr.write("โŒ Nerve daemon is not running. Start it with `nerve start`.\n"); - process.exit(1); - } - - const transport = resolveDaemonTransport(); - let response: { ok: true } | { ok: false; error: string }; - try { - response = await transport.killWorkflow(args.runId); - } catch (e) { - const msg = e instanceof Error ? e.message : String(e); - process.stderr.write(`โŒ Failed to contact daemon: ${msg}\n`); - process.exit(1); - } - - if (!response.ok) { - process.stderr.write(`โŒ Kill failed: ${response.error}\n`); - process.exit(1); - } - - process.stdout.write(`โœ… Kill signal sent for run "${args.runId}".\n`); + process.stdout.write("\n๐Ÿ’ก Inspect active runs with: nerve thread list\n"); }, }); @@ -675,14 +476,11 @@ const workflowKillCommand = defineCommand({ export const workflowCommand = defineCommand({ meta: { name: "workflow", - description: "Manage and inspect workflow runs", + description: "Manage workflow definitions and trigger workflows", }, subCommands: { - list: workflowDaemonListCommand, - runs: workflowRunsCommand, - inspect: workflowInspectCommand, - thread: workflowThreadCommand, + list: workflowListCommand, + status: workflowStatusCommand, trigger: workflowTriggerCommand, - kill: workflowKillCommand, }, });