refactor(cli): restructure cmd-*.ts into commands/ subdirectories

Reorganize flat cmd-*.ts files into commands/{workflow,thread,cas,init}/
subdirectories that strictly mirror the CLI subcommand hierarchy:

- workflow/: add, list, show, rm, history, rollback
- thread/: run, list, show, rm, fork, ps, kill, live, pause, resume
- cas/: get, put, list, rm, gc
- init/: workspace, template

Each group has an index.ts re-export. Split multi-command files
(cmd-cas.ts, cmd-thread.ts, cmd-init.ts) into per-subcommand files.
Rename cmd-help.ts → skill.ts to match the primary command name.

Update all import paths in cli-dispatch.ts and test files.

Pure structural change — no logic modifications.

Ref: #93, closes #94
This commit is contained in:
2026-05-08 00:36:54 +08:00
parent bfea771a52
commit 2ef004eecf
39 changed files with 384 additions and 321 deletions
@@ -1,4 +1,4 @@
import type { ParsedAddArgv } from "../src/cmd-add.js"; import type { ParsedAddArgv } from "../src/commands/workflow/add.js";
export function addCliArgs(name: string, filePath: string): ParsedAddArgv { export function addCliArgs(name: string, filePath: string): ParsedAddArgv {
return { name, filePath, typesPath: null }; return { name, filePath, typesPath: null };
@@ -4,13 +4,16 @@ import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { getGlobalCasDir, getRegisteredWorkflow, readWorkflowRegistry } from "@uncaged/workflow"; import { getGlobalCasDir, getRegisteredWorkflow, readWorkflowRegistry } from "@uncaged/workflow";
import { cmdAdd } from "../src/cmd-add.js"; import { cmdCasGet } from "../src/commands/cas/get.js";
import { cmdCasGet, cmdCasList, cmdCasPut, cmdCasRm } from "../src/cmd-cas.js"; import { cmdCasList } from "../src/commands/cas/list.js";
import { cmdHistory } from "../src/cmd-history.js"; import { cmdCasPut } from "../src/commands/cas/put.js";
import { cmdList, formatListLines } from "../src/cmd-list.js"; import { cmdCasRm } from "../src/commands/cas/rm.js";
import { cmdRemove } from "../src/cmd-remove.js"; import { cmdAdd } from "../src/commands/workflow/add.js";
import { cmdRollback } from "../src/cmd-rollback.js"; import { cmdHistory } from "../src/commands/workflow/history.js";
import { cmdShow } from "../src/cmd-show.js"; import { cmdList, formatListLines } from "../src/commands/workflow/list.js";
import { cmdRemove } from "../src/commands/workflow/rm.js";
import { cmdRollback } from "../src/commands/workflow/rollback.js";
import { cmdShow } from "../src/commands/workflow/show.js";
import { addCliArgs } from "./bundle-fixture.js"; import { addCliArgs } from "./bundle-fixture.js";
const fixtureDescriptor = `export const descriptor = { description: "fixture", roles: {} }; const fixtureDescriptor = `export const descriptor = { description: "fixture", roles: {} };
@@ -3,9 +3,9 @@ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os"; import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { createCasStore, getContentMerklePayload, getGlobalCasDir } from "@uncaged/workflow"; import { createCasStore, getContentMerklePayload, getGlobalCasDir } from "@uncaged/workflow";
import { cmdAdd } from "../src/cmd-add.js"; import { cmdFork } from "../src/commands/thread/fork.js";
import { cmdFork } from "../src/cmd-fork.js"; import { cmdRun } from "../src/commands/thread/run.js";
import { cmdRun } from "../src/cmd-run.js"; import { cmdAdd } from "../src/commands/workflow/add.js";
import { pathExists } from "../src/fs-utils.js"; import { pathExists } from "../src/fs-utils.js";
import { addCliArgs } from "./bundle-fixture.js"; import { addCliArgs } from "./bundle-fixture.js";
@@ -10,7 +10,7 @@ import {
getGlobalCasDir, getGlobalCasDir,
putContentMerkleNode, putContentMerkleNode,
} from "@uncaged/workflow"; } from "@uncaged/workflow";
import { cmdThreadRemove } from "../src/cmd-thread.js"; import { cmdThreadRemove } from "../src/commands/thread/rm.js";
import { pathExists } from "../src/fs-utils.js"; import { pathExists } from "../src/fs-utils.js";
const cliEntryPath = fileURLToPath(new URL("../src/cli.ts", import.meta.url)); const cliEntryPath = fileURLToPath(new URL("../src/cli.ts", import.meta.url));
+1 -1
View File
@@ -5,7 +5,7 @@ import {
formatSkillIndex, formatSkillIndex,
formatSkillTopic, formatSkillTopic,
getSkillTopics, getSkillTopics,
} from "../src/cmd-help.js"; } from "../src/skill.js";
const STORAGE_ROOT = "/tmp/help-test-storage"; const STORAGE_ROOT = "/tmp/help-test-storage";
@@ -4,7 +4,8 @@ import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { runCli } from "../src/cli-dispatch.js"; import { runCli } from "../src/cli-dispatch.js";
import { cmdInitTemplate, cmdInitWorkspace } from "../src/cmd-init.js"; import { cmdInitTemplate } from "../src/commands/init/template.js";
import { cmdInitWorkspace } from "../src/commands/init/workspace.js";
import { pathExists } from "../src/fs-utils.js"; import { pathExists } from "../src/fs-utils.js";
describe("init template", () => { describe("init template", () => {
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
import { join } from "node:path"; import { join } from "node:path";
import { formatCliUsage, runCli } from "../src/cli-dispatch.js"; import { formatCliUsage, runCli } from "../src/cli-dispatch.js";
import { cmdInitWorkspace } from "../src/cmd-init.js"; import { cmdInitWorkspace } from "../src/commands/init/workspace.js";
import { pathExists } from "../src/fs-utils.js"; import { pathExists } from "../src/fs-utils.js";
describe("init workspace", () => { describe("init workspace", () => {
+1 -1
View File
@@ -13,7 +13,7 @@ import {
LIVE_CONTENT_MAX_LINES, LIVE_CONTENT_MAX_LINES,
type LiveRoleRow, type LiveRoleRow,
renderLiveRoleStepLines, renderLiveRoleStepLines,
} from "../src/cmd-live.js"; } from "../src/commands/thread/live.js";
import { parseLiveArgv } from "../src/live-argv.js"; import { parseLiveArgv } from "../src/live-argv.js";
const cliEntryPath = fileURLToPath(new URL("../src/cli.ts", import.meta.url)); const cliEntryPath = fileURLToPath(new URL("../src/cli.ts", import.meta.url));
@@ -5,15 +5,16 @@ import { tmpdir } from "node:os";
import { dirname, join } from "node:path"; import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { getGlobalCasDir } from "@uncaged/workflow"; import { getGlobalCasDir } from "@uncaged/workflow";
import { cmdAdd } from "../src/cmd-add.js"; import { cmdCasPut } from "../src/commands/cas/put.js";
import { cmdCasPut } from "../src/cmd-cas.js"; import { cmdKill } from "../src/commands/thread/kill.js";
import { cmdKill } from "../src/cmd-kill.js"; import { cmdThreads } from "../src/commands/thread/list.js";
import { cmdPause } from "../src/cmd-pause.js"; import { cmdPause } from "../src/commands/thread/pause.js";
import { cmdPs } from "../src/cmd-ps.js"; import { cmdPs } from "../src/commands/thread/ps.js";
import { cmdResume } from "../src/cmd-resume.js"; import { cmdResume } from "../src/commands/thread/resume.js";
import { cmdRun } from "../src/cmd-run.js"; import { cmdThreadRemove } from "../src/commands/thread/rm.js";
import { cmdThreadRemove, cmdThreadShow } from "../src/cmd-thread.js"; import { cmdRun } from "../src/commands/thread/run.js";
import { cmdThreads } from "../src/cmd-threads.js"; import { cmdThreadShow } from "../src/commands/thread/show.js";
import { cmdAdd } from "../src/commands/workflow/add.js";
import { pathExists, readTextFileIfExists } from "../src/fs-utils.js"; import { pathExists, readTextFileIfExists } from "../src/fs-utils.js";
import { addCliArgs } from "./bundle-fixture.js"; import { addCliArgs } from "./bundle-fixture.js";
+24 -24
View File
@@ -1,30 +1,30 @@
import { printCliError, printCliLine, printCliWarn } from "./cli-output.js"; import { printCliError, printCliLine, printCliWarn } from "./cli-output.js";
import { cmdAdd, formatAddSuccess, parseAddArgv } from "./cmd-add.js"; import { cmdGc } from "./commands/cas/gc.js";
import { cmdCasGet, cmdCasList, cmdCasPut, cmdCasRm } from "./cmd-cas.js"; import { cmdCasGet } from "./commands/cas/get.js";
import { cmdFork, parseForkArgv } from "./cmd-fork.js"; import { cmdCasList } from "./commands/cas/list.js";
import { cmdGc } from "./cmd-gc.js"; import { cmdCasPut } from "./commands/cas/put.js";
import { import { cmdCasRm } from "./commands/cas/rm.js";
formatSkillDoc, import { cmdInitTemplate } from "./commands/init/template.js";
formatSkillIndex, import { cmdInitWorkspace } from "./commands/init/workspace.js";
formatSkillTopic, import { cmdFork, parseForkArgv } from "./commands/thread/fork.js";
getSkillTopics, import { cmdKill } from "./commands/thread/kill.js";
} from "./cmd-help.js"; import { cmdThreads } from "./commands/thread/list.js";
import { cmdHistory } from "./cmd-history.js"; import { cmdLive } from "./commands/thread/live.js";
import { cmdInitTemplate, cmdInitWorkspace } from "./cmd-init.js"; import { cmdPause } from "./commands/thread/pause.js";
import { cmdKill } from "./cmd-kill.js"; import { cmdPs } from "./commands/thread/ps.js";
import { cmdList, formatListLines } from "./cmd-list.js"; import { cmdResume } from "./commands/thread/resume.js";
import { cmdLive } from "./cmd-live.js"; import { cmdThreadRemove } from "./commands/thread/rm.js";
import { cmdPause } from "./cmd-pause.js"; import { cmdRun } from "./commands/thread/run.js";
import { cmdPs } from "./cmd-ps.js"; import { cmdThreadShow } from "./commands/thread/show.js";
import { cmdRemove } from "./cmd-remove.js"; import { cmdAdd, formatAddSuccess, parseAddArgv } from "./commands/workflow/add.js";
import { cmdResume } from "./cmd-resume.js"; import { cmdHistory } from "./commands/workflow/history.js";
import { cmdRollback } from "./cmd-rollback.js"; import { cmdList, formatListLines } from "./commands/workflow/list.js";
import { cmdRun } from "./cmd-run.js"; import { cmdRemove } from "./commands/workflow/rm.js";
import { cmdShow, formatShowYaml } from "./cmd-show.js"; import { cmdRollback } from "./commands/workflow/rollback.js";
import { cmdThreadRemove, cmdThreadShow } from "./cmd-thread.js"; import { cmdShow, formatShowYaml } from "./commands/workflow/show.js";
import { cmdThreads } from "./cmd-threads.js";
import { parseLiveArgv } from "./live-argv.js"; import { parseLiveArgv } from "./live-argv.js";
import { parseRunArgv } from "./run-argv.js"; import { parseRunArgv } from "./run-argv.js";
import { formatSkillIndex, formatSkillTopic, getSkillTopics } from "./skill.js";
type DispatchFn = (storageRoot: string, argv: string[]) => Promise<number>; type DispatchFn = (storageRoot: string, argv: string[]) => Promise<number>;
-43
View File
@@ -1,43 +0,0 @@
import { createCasStore, err, getGlobalCasDir, ok, type Result } from "@uncaged/workflow";
export async function cmdCasGet(
storageRoot: string,
_threadId: string,
hash: string,
): Promise<Result<string, string>> {
const cas = createCasStore(getGlobalCasDir(storageRoot));
const content = await cas.get(hash);
if (content === null) {
return err(`cas entry not found: ${hash}`);
}
return ok(content);
}
export async function cmdCasPut(
storageRoot: string,
_threadId: string,
content: string,
): Promise<Result<string, string>> {
const cas = createCasStore(getGlobalCasDir(storageRoot));
const hash = await cas.put(content);
return ok(hash);
}
export async function cmdCasList(
storageRoot: string,
_threadId: string,
): Promise<Result<string[], string>> {
const cas = createCasStore(getGlobalCasDir(storageRoot));
const hashes = await cas.list();
return ok(hashes);
}
export async function cmdCasRm(
storageRoot: string,
_threadId: string,
hash: string,
): Promise<Result<void, string>> {
const cas = createCasStore(getGlobalCasDir(storageRoot));
await cas.delete(hash);
return ok(undefined);
}
@@ -0,0 +1,14 @@
import { createCasStore, err, getGlobalCasDir, ok, type Result } from "@uncaged/workflow";
export async function cmdCasGet(
storageRoot: string,
_threadId: string,
hash: string,
): Promise<Result<string, string>> {
const cas = createCasStore(getGlobalCasDir(storageRoot));
const content = await cas.get(hash);
if (content === null) {
return err(`cas entry not found: ${hash}`);
}
return ok(content);
}
@@ -0,0 +1,5 @@
export { cmdGc } from "./gc.js";
export { cmdCasGet } from "./get.js";
export { cmdCasList } from "./list.js";
export { cmdCasPut } from "./put.js";
export { cmdCasRm } from "./rm.js";
@@ -0,0 +1,10 @@
import { createCasStore, getGlobalCasDir, ok, type Result } from "@uncaged/workflow";
export async function cmdCasList(
storageRoot: string,
_threadId: string,
): Promise<Result<string[], string>> {
const cas = createCasStore(getGlobalCasDir(storageRoot));
const hashes = await cas.list();
return ok(hashes);
}
@@ -0,0 +1,11 @@
import { createCasStore, getGlobalCasDir, ok, type Result } from "@uncaged/workflow";
export async function cmdCasPut(
storageRoot: string,
_threadId: string,
content: string,
): Promise<Result<string, string>> {
const cas = createCasStore(getGlobalCasDir(storageRoot));
const hash = await cas.put(content);
return ok(hash);
}
@@ -0,0 +1,11 @@
import { createCasStore, getGlobalCasDir, ok, type Result } from "@uncaged/workflow";
export async function cmdCasRm(
storageRoot: string,
_threadId: string,
hash: string,
): Promise<Result<void, string>> {
const cas = createCasStore(getGlobalCasDir(storageRoot));
await cas.delete(hash);
return ok(undefined);
}
@@ -0,0 +1,4 @@
export type { CmdInitTemplateSuccess } from "./template.js";
export { cmdInitTemplate } from "./template.js";
export type { CmdInitWorkspaceSuccess } from "./workspace.js";
export { cmdInitWorkspace } from "./workspace.js";
@@ -0,0 +1,203 @@
import { mkdir, readFile, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path";
import { err, ok, type Result } from "@uncaged/workflow";
import { pathExists } from "../../fs-utils.js";
export type CmdInitTemplateSuccess = {
templatePath: string;
};
function validateWorkspaceSegment(name: string): Result<void, string> {
if (name.length === 0) {
return err("workspace name must not be empty");
}
if (name === "." || name === "..") {
return err("invalid workspace name");
}
if (name.includes("/") || name.includes("\\")) {
return err("workspace name must not contain path separators");
}
return ok(undefined);
}
function hasTemplatesWorkspaceGlob(workspaces: unknown): boolean {
return Array.isArray(workspaces) && workspaces.includes("templates/*");
}
async function readPackageJsonWorkspaces(dir: string): Promise<unknown | null> {
const pkgPath = join(dir, "package.json");
if (!(await pathExists(pkgPath))) {
return null;
}
let raw: string;
try {
raw = await readFile(pkgPath, "utf8");
} catch {
return null;
}
let parsed: unknown;
try {
parsed = JSON.parse(raw) as unknown;
} catch {
return null;
}
if (typeof parsed !== "object" || parsed === null || !("workspaces" in parsed)) {
return null;
}
return (parsed as { workspaces: unknown }).workspaces;
}
/** Resolve uncaged-workflow workspace root (package.json with `templates/*` in `workspaces`). */
async function findWorkflowWorkspaceRoot(startDir: string): Promise<Result<string, string>> {
let dir = resolve(startDir);
for (;;) {
const workspaces = await readPackageJsonWorkspaces(dir);
if (workspaces !== null && hasTemplatesWorkspaceGlob(workspaces)) {
return ok(dir);
}
const parent = dirname(dir);
if (parent === dir) {
return err(
'not inside a workflow workspace (no package.json with workspaces containing "templates/*")',
);
}
dir = parent;
}
}
function templatePackageJson(templateName: string): string {
return `${JSON.stringify(
{
name: `template-${templateName}`,
version: "0.0.0",
private: true,
type: "module",
dependencies: {
"@uncaged/workflow": "^0.1.0",
zod: "^4.0.0",
},
},
null,
2,
)}\n`;
}
function templateTsconfigJson(): string {
return `${JSON.stringify(
{
extends: "../../tsconfig.json",
compilerOptions: {
rootDir: "src",
outDir: "dist",
},
include: ["src/**/*.ts"],
},
null,
2,
)}\n`;
}
function templateRolesTs(): string {
return `import type { RoleDefinition } from "@uncaged/workflow";
import * as z from "zod/v4";
export const HELLO_TEMPLATE_DESCRIPTION =
"Minimal starter template: one greeter role, then END.";
export type HelloTemplateMeta = {
greeter: {
message: string;
};
};
const greeterMetaSchema = z.object({
message: z.string(),
});
export const greeterRole: RoleDefinition<HelloTemplateMeta["greeter"]> = {
description: "Says hello — replace with your first role.",
systemPrompt: "You are a helpful assistant. Reply with one short friendly sentence.",
extractPrompt: "Extract the assistant's greeting as message.",
schema: greeterMetaSchema,
extractRefs: null,
};
`;
}
function templateModeratorTs(): string {
return `import { END, type Moderator, type ModeratorContext } from "@uncaged/workflow";
import type { HelloTemplateMeta } from "./roles.js";
export const helloTemplateModerator: Moderator<HelloTemplateMeta> = (
ctx: ModeratorContext<HelloTemplateMeta>,
) => {
if (ctx.steps.length === 0) {
return "greeter";
}
return END;
};
`;
}
function templateIndexTs(): string {
return `import type { WorkflowDefinition } from "@uncaged/workflow";
import { helloTemplateModerator } from "./moderator.js";
import {
HELLO_TEMPLATE_DESCRIPTION,
type HelloTemplateMeta,
greeterRole,
} from "./roles.js";
export {
HELLO_TEMPLATE_DESCRIPTION,
type HelloTemplateMeta,
greeterRole,
} from "./roles.js";
export { helloTemplateModerator } from "./moderator.js";
export const helloTemplateWorkflowDefinition: WorkflowDefinition<HelloTemplateMeta> = {
description: HELLO_TEMPLATE_DESCRIPTION,
roles: {
greeter: greeterRole,
},
moderator: helloTemplateModerator,
};
`;
}
export async function cmdInitTemplate(
startDir: string,
templateName: string,
): Promise<Result<CmdInitTemplateSuccess, string>> {
const validated = validateWorkspaceSegment(templateName);
if (!validated.ok) {
return validated;
}
const rootResult = await findWorkflowWorkspaceRoot(startDir);
if (!rootResult.ok) {
return rootResult;
}
const workspaceRoot = rootResult.value;
const templateDir = join(workspaceRoot, "templates", templateName);
if (await pathExists(templateDir)) {
return err(`template already exists: ${templateDir}`);
}
await mkdir(join(templateDir, "src"), { recursive: true });
await Promise.all([
writeFile(join(templateDir, "package.json"), templatePackageJson(templateName), "utf8"),
writeFile(join(templateDir, "tsconfig.json"), templateTsconfigJson(), "utf8"),
writeFile(join(templateDir, "src", "roles.ts"), templateRolesTs(), "utf8"),
writeFile(join(templateDir, "src", "moderator.ts"), templateModeratorTs(), "utf8"),
writeFile(join(templateDir, "src", "index.ts"), templateIndexTs(), "utf8"),
]);
return ok({ templatePath: templateDir });
}
@@ -1,18 +1,14 @@
import { mkdir, readFile, writeFile } from "node:fs/promises"; import { mkdir, writeFile } from "node:fs/promises";
import { dirname, join, resolve } from "node:path"; import { join } from "node:path";
import { err, ok, type Result } from "@uncaged/workflow"; import { err, ok, type Result } from "@uncaged/workflow";
import { pathExists } from "./fs-utils.js"; import { pathExists } from "../../fs-utils.js";
export type CmdInitWorkspaceSuccess = { export type CmdInitWorkspaceSuccess = {
rootPath: string; rootPath: string;
}; };
export type CmdInitTemplateSuccess = {
templatePath: string;
};
function validateWorkspaceSegment(name: string): Result<void, string> { function validateWorkspaceSegment(name: string): Result<void, string> {
if (name.length === 0) { if (name.length === 0) {
return err("workspace name must not be empty"); return err("workspace name must not be empty");
@@ -233,183 +229,3 @@ export async function cmdInitWorkspace(
return ok({ rootPath }); return ok({ rootPath });
} }
function hasTemplatesWorkspaceGlob(workspaces: unknown): boolean {
return Array.isArray(workspaces) && workspaces.includes("templates/*");
}
async function readPackageJsonWorkspaces(dir: string): Promise<unknown | null> {
const pkgPath = join(dir, "package.json");
if (!(await pathExists(pkgPath))) {
return null;
}
let raw: string;
try {
raw = await readFile(pkgPath, "utf8");
} catch {
return null;
}
let parsed: unknown;
try {
parsed = JSON.parse(raw) as unknown;
} catch {
return null;
}
if (typeof parsed !== "object" || parsed === null || !("workspaces" in parsed)) {
return null;
}
return (parsed as { workspaces: unknown }).workspaces;
}
/** Resolve uncaged-workflow workspace root (package.json with `templates/*` in `workspaces`). */
async function findWorkflowWorkspaceRoot(startDir: string): Promise<Result<string, string>> {
let dir = resolve(startDir);
for (;;) {
const workspaces = await readPackageJsonWorkspaces(dir);
if (workspaces !== null && hasTemplatesWorkspaceGlob(workspaces)) {
return ok(dir);
}
const parent = dirname(dir);
if (parent === dir) {
return err(
'not inside a workflow workspace (no package.json with workspaces containing "templates/*")',
);
}
dir = parent;
}
}
function templatePackageJson(templateName: string): string {
return `${JSON.stringify(
{
name: `template-${templateName}`,
version: "0.0.0",
private: true,
type: "module",
dependencies: {
"@uncaged/workflow": "^0.1.0",
zod: "^4.0.0",
},
},
null,
2,
)}\n`;
}
function templateTsconfigJson(): string {
return `${JSON.stringify(
{
extends: "../../tsconfig.json",
compilerOptions: {
rootDir: "src",
outDir: "dist",
},
include: ["src/**/*.ts"],
},
null,
2,
)}\n`;
}
function templateRolesTs(): string {
return `import type { RoleDefinition } from "@uncaged/workflow";
import * as z from "zod/v4";
export const HELLO_TEMPLATE_DESCRIPTION =
"Minimal starter template: one greeter role, then END.";
export type HelloTemplateMeta = {
greeter: {
message: string;
};
};
const greeterMetaSchema = z.object({
message: z.string(),
});
export const greeterRole: RoleDefinition<HelloTemplateMeta["greeter"]> = {
description: "Says hello — replace with your first role.",
systemPrompt: "You are a helpful assistant. Reply with one short friendly sentence.",
extractPrompt: "Extract the assistant's greeting as message.",
schema: greeterMetaSchema,
extractRefs: null,
};
`;
}
function templateModeratorTs(): string {
return `import { END, type Moderator, type ModeratorContext } from "@uncaged/workflow";
import type { HelloTemplateMeta } from "./roles.js";
export const helloTemplateModerator: Moderator<HelloTemplateMeta> = (
ctx: ModeratorContext<HelloTemplateMeta>,
) => {
if (ctx.steps.length === 0) {
return "greeter";
}
return END;
};
`;
}
function templateIndexTs(): string {
return `import type { WorkflowDefinition } from "@uncaged/workflow";
import { helloTemplateModerator } from "./moderator.js";
import {
HELLO_TEMPLATE_DESCRIPTION,
type HelloTemplateMeta,
greeterRole,
} from "./roles.js";
export {
HELLO_TEMPLATE_DESCRIPTION,
type HelloTemplateMeta,
greeterRole,
} from "./roles.js";
export { helloTemplateModerator } from "./moderator.js";
export const helloTemplateWorkflowDefinition: WorkflowDefinition<HelloTemplateMeta> = {
description: HELLO_TEMPLATE_DESCRIPTION,
roles: {
greeter: greeterRole,
},
moderator: helloTemplateModerator,
};
`;
}
export async function cmdInitTemplate(
startDir: string,
templateName: string,
): Promise<Result<CmdInitTemplateSuccess, string>> {
const validated = validateWorkspaceSegment(templateName);
if (!validated.ok) {
return validated;
}
const rootResult = await findWorkflowWorkspaceRoot(startDir);
if (!rootResult.ok) {
return rootResult;
}
const workspaceRoot = rootResult.value;
const templateDir = join(workspaceRoot, "templates", templateName);
if (await pathExists(templateDir)) {
return err(`template already exists: ${templateDir}`);
}
await mkdir(join(templateDir, "src"), { recursive: true });
await Promise.all([
writeFile(join(templateDir, "package.json"), templatePackageJson(templateName), "utf8"),
writeFile(join(templateDir, "tsconfig.json"), templateTsconfigJson(), "utf8"),
writeFile(join(templateDir, "src", "roles.ts"), templateRolesTs(), "utf8"),
writeFile(join(templateDir, "src", "moderator.ts"), templateModeratorTs(), "utf8"),
writeFile(join(templateDir, "src", "index.ts"), templateIndexTs(), "utf8"),
]);
return ok({ templatePath: templateDir });
}
@@ -2,9 +2,9 @@ import { join } from "node:path";
import { buildForkPlan, err, generateUlid, ok, type Result } from "@uncaged/workflow"; import { buildForkPlan, err, generateUlid, ok, type Result } from "@uncaged/workflow";
import { pathExists, readTextFileIfExists } from "./fs-utils.js"; import { pathExists, readTextFileIfExists } from "../../fs-utils.js";
import { resolveThreadDataPath } from "./thread-scan.js"; import { resolveThreadDataPath } from "../../thread-scan.js";
import { ensureWorkerForHash, sendWorkerTcpCommand } from "./worker-spawn.js"; import { ensureWorkerForHash, sendWorkerTcpCommand } from "../../worker-spawn.js";
export function parseForkArgv( export function parseForkArgv(
argv: string[], argv: string[],
@@ -0,0 +1,17 @@
export { cmdFork, parseForkArgv } from "./fork.js";
export { cmdKill } from "./kill.js";
export { cmdThreads } from "./list.js";
export type { LiveRoleRow } from "./live.js";
export {
cmdLive,
formatLiveDebugLine,
formatLiveTimeLabel,
LIVE_CONTENT_MAX_LINES,
renderLiveRoleStepLines,
} from "./live.js";
export { cmdPause } from "./pause.js";
export { cmdPs } from "./ps.js";
export { cmdResume } from "./resume.js";
export { cmdThreadRemove } from "./rm.js";
export { cmdRun } from "./run.js";
export { cmdThreadShow } from "./show.js";
@@ -2,12 +2,12 @@ import { join } from "node:path";
import { err, type Result } from "@uncaged/workflow"; import { err, type Result } from "@uncaged/workflow";
import { readTextFileIfExists } from "./fs-utils.js"; import { readTextFileIfExists } from "../../fs-utils.js";
import { import {
resolveRunningHashForThread, resolveRunningHashForThread,
sendWorkerTcpCommand, sendWorkerTcpCommand,
type WorkerCtl, type WorkerCtl,
} from "./worker-spawn.js"; } from "../../worker-spawn.js";
export async function cmdKill( export async function cmdKill(
storageRoot: string, storageRoot: string,
@@ -1,7 +1,7 @@
import { err, ok, type Result } from "@uncaged/workflow"; import { err, ok, type Result } from "@uncaged/workflow";
import { listHistoricalThreads } from "./thread-scan.js"; import { listHistoricalThreads } from "../../thread-scan.js";
import { validateCliWorkflowName } from "./workflow-name.js"; import { validateCliWorkflowName } from "../../workflow-name.js";
export async function cmdThreads( export async function cmdThreads(
storageRoot: string, storageRoot: string,
@@ -12,10 +12,10 @@ import {
type WorkflowCompletion, type WorkflowCompletion,
} from "@uncaged/workflow"; } from "@uncaged/workflow";
import { printCliError, printCliLine } from "./cli-output.js"; import { printCliError, printCliLine } from "../../cli-output.js";
import { pathExists } from "./fs-utils.js"; import { pathExists } from "../../fs-utils.js";
import type { ParsedLiveArgv } from "./live-argv.js"; import type { ParsedLiveArgv } from "../../live-argv.js";
import { findLatestThreadDataPath, resolveThreadDataPath } from "./thread-scan.js"; import { findLatestThreadDataPath, resolveThreadDataPath } from "../../thread-scan.js";
export const LIVE_CONTENT_MAX_LINES = 10; export const LIVE_CONTENT_MAX_LINES = 10;
@@ -2,12 +2,12 @@ import { join } from "node:path";
import { err, type Result } from "@uncaged/workflow"; import { err, type Result } from "@uncaged/workflow";
import { readTextFileIfExists } from "./fs-utils.js"; import { readTextFileIfExists } from "../../fs-utils.js";
import { import {
resolveRunningHashForThread, resolveRunningHashForThread,
sendWorkerTcpCommand, sendWorkerTcpCommand,
type WorkerCtl, type WorkerCtl,
} from "./worker-spawn.js"; } from "../../worker-spawn.js";
export async function cmdPause( export async function cmdPause(
storageRoot: string, storageRoot: string,
@@ -1,4 +1,4 @@
import { listRunningThreads } from "./thread-scan.js"; import { listRunningThreads } from "../../thread-scan.js";
export async function cmdPs(storageRoot: string): Promise<string[]> { export async function cmdPs(storageRoot: string): Promise<string[]> {
const rows = await listRunningThreads(storageRoot); const rows = await listRunningThreads(storageRoot);
@@ -2,12 +2,12 @@ import { join } from "node:path";
import { err, type Result } from "@uncaged/workflow"; import { err, type Result } from "@uncaged/workflow";
import { readTextFileIfExists } from "./fs-utils.js"; import { readTextFileIfExists } from "../../fs-utils.js";
import { import {
resolveRunningHashForThread, resolveRunningHashForThread,
sendWorkerTcpCommand, sendWorkerTcpCommand,
type WorkerCtl, type WorkerCtl,
} from "./worker-spawn.js"; } from "../../worker-spawn.js";
export async function cmdResume( export async function cmdResume(
storageRoot: string, storageRoot: string,
@@ -3,23 +3,7 @@ import { dirname, join } from "node:path";
import { err, garbageCollectCas, ok, type Result } from "@uncaged/workflow"; import { err, garbageCollectCas, ok, type Result } from "@uncaged/workflow";
import { readTextFileIfExists } from "./fs-utils.js"; import { resolveThreadDataPath } from "../../thread-scan.js";
import { resolveThreadDataPath } from "./thread-scan.js";
export async function cmdThreadShow(
storageRoot: string,
threadId: string,
): Promise<Result<string, string>> {
const dataPath = await resolveThreadDataPath(storageRoot, threadId);
if (dataPath === null) {
return err(`thread not found: ${threadId}`);
}
const text = await readTextFileIfExists(dataPath);
if (text === null) {
return err(`thread data missing: ${threadId}`);
}
return ok(text.endsWith("\n") ? text.slice(0, -1) : text);
}
export async function cmdThreadRemove( export async function cmdThreadRemove(
storageRoot: string, storageRoot: string,
@@ -8,8 +8,8 @@ import {
type Result, type Result,
readWorkflowRegistry, readWorkflowRegistry,
} from "@uncaged/workflow"; } from "@uncaged/workflow";
import { ensureWorkerForHash, sendWorkerTcpCommand } from "./worker-spawn.js"; import { ensureWorkerForHash, sendWorkerTcpCommand } from "../../worker-spawn.js";
import { validateCliWorkflowName } from "./workflow-name.js"; import { validateCliWorkflowName } from "../../workflow-name.js";
export async function cmdRun( export async function cmdRun(
storageRoot: string, storageRoot: string,
@@ -0,0 +1,19 @@
import { err, ok, type Result } from "@uncaged/workflow";
import { readTextFileIfExists } from "../../fs-utils.js";
import { resolveThreadDataPath } from "../../thread-scan.js";
export async function cmdThreadShow(
storageRoot: string,
threadId: string,
): Promise<Result<string, string>> {
const dataPath = await resolveThreadDataPath(storageRoot, threadId);
if (dataPath === null) {
return err(`thread not found: ${threadId}`);
}
const text = await readTextFileIfExists(dataPath);
if (text === null) {
return err(`thread data missing: ${threadId}`);
}
return ok(text.endsWith("\n") ? text.slice(0, -1) : text);
}
@@ -14,8 +14,8 @@ import {
writeWorkflowRegistry, writeWorkflowRegistry,
} from "@uncaged/workflow"; } from "@uncaged/workflow";
import { storeWorkflowBundleArtifacts } from "./bundle-store.js"; import { storeWorkflowBundleArtifacts } from "../../bundle-store.js";
import { validateCliWorkflowName } from "./workflow-name.js"; import { validateCliWorkflowName } from "../../workflow-name.js";
export type ParsedAddArgv = { export type ParsedAddArgv = {
name: string; name: string;
@@ -6,7 +6,7 @@ import {
readWorkflowRegistry, readWorkflowRegistry,
} from "@uncaged/workflow"; } from "@uncaged/workflow";
import { validateCliWorkflowName } from "./workflow-name.js"; import { validateCliWorkflowName } from "../../workflow-name.js";
export async function cmdHistory( export async function cmdHistory(
storageRoot: string, storageRoot: string,
@@ -0,0 +1,7 @@
export type { CmdAddSuccess, ParsedAddArgv } from "./add.js";
export { cmdAdd, formatAddSuccess, parseAddArgv } from "./add.js";
export { cmdHistory } from "./history.js";
export { cmdList, formatListLines } from "./list.js";
export { cmdRemove } from "./rm.js";
export { cmdRollback } from "./rollback.js";
export { cmdShow, formatShowYaml } from "./show.js";
@@ -7,7 +7,7 @@ import {
writeWorkflowRegistry, writeWorkflowRegistry,
} from "@uncaged/workflow"; } from "@uncaged/workflow";
import { validateCliWorkflowName } from "./workflow-name.js"; import { validateCliWorkflowName } from "../../workflow-name.js";
export async function cmdRemove(storageRoot: string, name: string): Promise<Result<void, string>> { export async function cmdRemove(storageRoot: string, name: string): Promise<Result<void, string>> {
const nameOk = validateCliWorkflowName(name); const nameOk = validateCliWorkflowName(name);
@@ -10,8 +10,8 @@ import {
writeWorkflowRegistry, writeWorkflowRegistry,
} from "@uncaged/workflow"; } from "@uncaged/workflow";
import { pathExists } from "./fs-utils.js"; import { pathExists } from "../../fs-utils.js";
import { validateCliWorkflowName } from "./workflow-name.js"; import { validateCliWorkflowName } from "../../workflow-name.js";
export async function cmdRollback( export async function cmdRollback(
storageRoot: string, storageRoot: string,
@@ -8,7 +8,7 @@ import {
} from "@uncaged/workflow"; } from "@uncaged/workflow";
import { stringify } from "yaml"; import { stringify } from "yaml";
import { validateCliWorkflowName } from "./workflow-name.js"; import { validateCliWorkflowName } from "../../workflow-name.js";
export async function cmdShow( export async function cmdShow(
storageRoot: string, storageRoot: string,