Compare commits

...

7 Commits

Author SHA1 Message Date
xiaoju 86a422f7e2 fix(cli): nits from review — live --latest in args, dispatchInit uses dispatchGroup
小橘 🍊
2026-05-07 14:54:02 +00:00
xiaoju 648f0c6dec Merge pull request 'refactor: merge role packages into templates + slim prompts' (#78) from refactor/75-merge-roles-phase1 into main 2026-05-07 14:52:25 +00:00
xiaoju 8456a8337b refactor: slim planner & coder prompts with help --skill
Replace inline CLI tutorials (thread ID lookup, cas put/get examples)
with a single 'uncaged-workflow help --skill' reference. Keeps minimal
task-specific instructions (what to store, what to report).

Closes #77
Refs #75, #72

小橘 🍊
2026-05-07 14:47:14 +00:00
xiaoju 9c8b98a551 refactor: merge 7 workflow-role-* packages into templates
- planner/coder/reviewer/tester/committer → workflow-template-develop/src/roles/
- preparer/submitter → workflow-template-solve-issue/src/roles/
- Moved tests, updated imports, removed role packages
- 219 tests pass, build clean

Closes #76
Refs #75, #73

小橘 🍊
2026-05-07 14:45:11 +00:00
xiaoju c3272be760 Merge pull request 'refactor(cli): auto-generate skill doc from command registry' (#74) from refactor/71-auto-gen-skill-doc into main 2026-05-07 14:39:51 +00:00
xiaomo c44b773a86 refactor(cli): auto-generate skill doc from command registry (#71) 2026-05-07 14:35:53 +00:00
xingyue 2776f8e419 Merge pull request 'feat(cli): add WORKFLOW_STORAGE_ROOT env var support' (#68) from feat/63-workflow-storage-root into main 2026-05-07 14:30:03 +00:00
45 changed files with 297 additions and 447 deletions
+236 -126
View File
@@ -21,84 +21,49 @@ import { cmdThreads } from "./cmd-threads.js";
import { parseLiveArgv } from "./live-argv.js";
import { parseRunArgv } from "./run-argv.js";
export function formatCliUsage(): string {
return [
"Usage:",
" uncaged-workflow workflow add <name> <file.esm.js> [--types <path>]",
" uncaged-workflow workflow list",
" uncaged-workflow workflow show <name>",
" uncaged-workflow workflow rm <name>",
" uncaged-workflow workflow history <name>",
" uncaged-workflow workflow rollback <name> [hash]",
"",
" uncaged-workflow thread run <name> [--prompt <text>] [--max-rounds N]",
" uncaged-workflow thread list [name]",
" uncaged-workflow thread show <id>",
" uncaged-workflow thread rm <id>",
" uncaged-workflow thread ps",
" uncaged-workflow thread kill <thread-id>",
" uncaged-workflow thread live <thread-id> [--debug] [--role <name>]",
" uncaged-workflow thread live --latest [--debug] [--role <name>]",
" uncaged-workflow thread pause <thread-id>",
" uncaged-workflow thread resume <thread-id>",
" uncaged-workflow thread fork <thread-id> [--from-role <role>]",
"",
" uncaged-workflow cas get <thread-id> <hash>",
" uncaged-workflow cas put <thread-id> <content>",
" uncaged-workflow cas list <thread-id>",
" uncaged-workflow cas rm <thread-id> <hash>",
" uncaged-workflow cas gc",
"",
" uncaged-workflow init workspace <name>",
" uncaged-workflow init template <name>",
"",
" uncaged-workflow run <name> [...] (shortcut for thread run)",
" uncaged-workflow live <thread-id> [...] (shortcut for thread live)",
"",
"Environment variables:",
" WORKFLOW_STORAGE_ROOT Override storage directory (default: ~/.uncaged/workflow)",
" UNCAGED_WORKFLOW_STORAGE_ROOT Internal override (takes priority over WORKFLOW_STORAGE_ROOT)",
].join("\n");
}
function printDeprecation(oldCmd: string, newCmd: string): void {
printCliWarn(`⚠ "${oldCmd}" is deprecated, use "${newCmd}" instead`);
}
type DispatchFn = (storageRoot: string, argv: string[]) => Promise<number>;
type CommandEntry = {
handler: DispatchFn;
args: string;
description: string;
};
type CommandGroup = {
name: string;
commands: ReadonlyArray<{ name: string; args: string; description: string }>;
};
// ── Individual dispatch functions ──────────────────────────────────────
async function dispatchInit(_storageRoot: string, argv: string[]): Promise<number> {
const sub = argv[0];
const name = argv[1];
if (sub === undefined || name === undefined || argv.length > 2) {
printCliError(`${formatCliUsage()}\n\nerror: init requires workspace|template <name>`);
async function dispatchInitWorkspace(_storageRoot: string, argv: string[]): Promise<number> {
const name = argv[0];
if (name === undefined || argv.length > 1) {
printCliError(`${formatCliUsage()}\n\nerror: init workspace requires <name>`);
return 1;
}
if (sub === "workspace") {
const result = await cmdInitWorkspace(process.cwd(), name);
if (!result.ok) {
printCliError(result.error);
return 1;
}
printCliLine(`initialized workflow workspace at ${result.value.rootPath}`);
return 0;
const result = await cmdInitWorkspace(process.cwd(), name);
if (!result.ok) {
printCliError(result.error);
return 1;
}
printCliLine(`initialized workflow workspace at ${result.value.rootPath}`);
return 0;
}
if (sub === "template") {
const result = await cmdInitTemplate(process.cwd(), name);
if (!result.ok) {
printCliError(result.error);
return 1;
}
printCliLine(`initialized template at ${result.value.templatePath}`);
return 0;
async function dispatchInitTemplate(_storageRoot: string, argv: string[]): Promise<number> {
const name = argv[0];
if (name === undefined || argv.length > 1) {
printCliError(`${formatCliUsage()}\n\nerror: init template requires <name>`);
return 1;
}
printCliError(`${formatCliUsage()}\n\nerror: unknown init subcommand: ${sub}`);
return 1;
const result = await cmdInitTemplate(process.cwd(), name);
if (!result.ok) {
printCliError(result.error);
return 1;
}
printCliLine(`initialized template at ${result.value.templatePath}`);
return 0;
}
async function dispatchAdd(storageRoot: string, argv: string[]): Promise<number> {
@@ -426,49 +391,203 @@ async function dispatchCasRm(storageRoot: string, rest: string[]): Promise<numbe
return 0;
}
const CAS_SUBCOMMAND_TABLE: Record<string, DispatchFn> = {
get: dispatchCasGet,
put: dispatchCasPut,
list: dispatchCasList,
rm: dispatchCasRm,
gc: dispatchGc,
// ── Subcommand tables with metadata ────────────────────────────────────
const WORKFLOW_SUBCOMMAND_TABLE: Record<string, CommandEntry> = {
add: {
handler: dispatchAdd,
args: "<name> <file.esm.js> [--types <path>]",
description: "Register a workflow bundle in the registry",
},
list: { handler: dispatchList, args: "", description: "List all registered workflows" },
show: {
handler: dispatchShow,
args: "<name>",
description: "Show details of a registered workflow",
},
rm: {
handler: dispatchRemove,
args: "<name>",
description: "Remove a workflow from the registry",
},
history: {
handler: dispatchHistory,
args: "<name>",
description: "Show version history of a workflow",
},
rollback: {
handler: dispatchRollback,
args: "<name> [hash]",
description: "Rollback a workflow to a previous version",
},
};
async function dispatchCas(storageRoot: string, argv: string[]): Promise<number> {
const sub = argv[0];
if (sub === undefined) {
printCliError(`${formatCliUsage()}\n\nerror: unknown cas subcommand: (none)`);
return 1;
}
const handler = CAS_SUBCOMMAND_TABLE[sub];
if (handler === undefined) {
printCliError(`${formatCliUsage()}\n\nerror: unknown cas subcommand: ${sub}`);
return 1;
}
return handler(storageRoot, argv.slice(1));
const THREAD_SUBCOMMAND_TABLE: Record<string, CommandEntry> = {
run: {
handler: dispatchRun,
args: "<name> [--prompt <text>] [--max-rounds N]",
description: "Start a new thread executing a workflow",
},
list: {
handler: dispatchThreadList,
args: "[name]",
description: "List threads, optionally filtered by workflow name",
},
show: { handler: dispatchThreadShow, args: "<id>", description: "Show thread details and state" },
rm: { handler: dispatchThreadRm, args: "<id>", description: "Remove a thread" },
fork: {
handler: dispatchFork,
args: "<thread-id> [--from-role <role>]",
description: "Fork a thread, optionally from a specific role",
},
ps: { handler: dispatchPs, args: "", description: "List running threads" },
kill: { handler: dispatchKill, args: "<thread-id>", description: "Kill a running thread" },
live: {
handler: dispatchLive,
args: "<thread-id> | --latest [--debug] [--role <name>]",
description: "Attach to a thread and stream output live",
},
pause: { handler: dispatchPause, args: "<thread-id>", description: "Pause a running thread" },
resume: { handler: dispatchResume, args: "<thread-id>", description: "Resume a paused thread" },
};
const CAS_SUBCOMMAND_TABLE: Record<string, CommandEntry> = {
get: {
handler: dispatchCasGet,
args: "<thread-id> <hash>",
description: "Retrieve content by hash from a thread's CAS",
},
put: {
handler: dispatchCasPut,
args: "<thread-id> <content>",
description: "Store content in a thread's CAS, returns hash",
},
list: {
handler: dispatchCasList,
args: "<thread-id>",
description: "List all CAS entries for a thread",
},
rm: { handler: dispatchCasRm, args: "<thread-id> <hash>", description: "Remove a CAS entry" },
gc: { handler: dispatchGc, args: "", description: "Garbage-collect unreferenced CAS entries" },
};
const INIT_SUBCOMMAND_TABLE: Record<string, CommandEntry> = {
workspace: {
handler: dispatchInitWorkspace,
args: "<name>",
description: "Initialize a new workflow workspace",
},
template: {
handler: dispatchInitTemplate,
args: "<name>",
description: "Initialize a new workflow template",
},
};
// ── Command registry ───────────────────────────────────────────────────
export function getCommandRegistry(): ReadonlyArray<CommandGroup> {
return [
{
name: "workflow",
commands: Object.entries(WORKFLOW_SUBCOMMAND_TABLE).map(([name, e]) => ({
name,
args: e.args,
description: e.description,
})),
},
{
name: "thread",
commands: Object.entries(THREAD_SUBCOMMAND_TABLE).map(([name, e]) => ({
name,
args: e.args,
description: e.description,
})),
},
{
name: "cas",
commands: Object.entries(CAS_SUBCOMMAND_TABLE).map(([name, e]) => ({
name,
args: e.args,
description: e.description,
})),
},
{
name: "init",
commands: Object.entries(INIT_SUBCOMMAND_TABLE).map(([name, e]) => ({
name,
args: e.args,
description: e.description,
})),
},
];
}
// ── Workflow subcommand table (Phase 1) ────────────────────────────────
// ── Auto-generated CLI usage ───────────────────────────────────────────
const WORKFLOW_SUBCOMMAND_TABLE: Record<string, DispatchFn> = {
add: dispatchAdd,
list: dispatchList,
show: dispatchShow,
rm: dispatchRemove,
history: dispatchHistory,
rollback: dispatchRollback,
};
export function formatCliUsage(): string {
const groups = getCommandRegistry();
const lines: string[] = ["Usage:"];
for (const group of groups) {
for (const cmd of group.commands) {
const args = cmd.args ? ` ${cmd.args}` : "";
lines.push(` uncaged-workflow ${group.name} ${cmd.name}${args}`);
}
lines.push("");
}
lines.push(" uncaged-workflow run <name> [...] (shortcut for thread run)");
lines.push(" uncaged-workflow live <thread-id> [...] (shortcut for thread live)");
lines.push("");
lines.push("Environment variables:");
lines.push(
" WORKFLOW_STORAGE_ROOT Override storage directory (default: ~/.uncaged/workflow)",
);
lines.push(
" UNCAGED_WORKFLOW_STORAGE_ROOT Internal override (takes priority over WORKFLOW_STORAGE_ROOT)",
);
return lines.join("\n");
}
async function dispatchWorkflow(storageRoot: string, argv: string[]): Promise<number> {
function printDeprecation(oldCmd: string, newCmd: string): void {
printCliWarn(`⚠ "${oldCmd}" is deprecated, use "${newCmd}" instead`);
}
// ── Group dispatchers ──────────────────────────────────────────────────
function dispatchGroup(
tableName: string,
table: Record<string, CommandEntry>,
storageRoot: string,
argv: string[],
): Promise<number> | null {
const sub = argv[0];
if (sub === undefined) {
printCliError(`${formatCliUsage()}\n\nerror: unknown workflow subcommand: (none)`);
return 1;
printCliError(`${formatCliUsage()}\n\nerror: unknown ${tableName} subcommand: (none)`);
return Promise.resolve(1);
}
const handler = WORKFLOW_SUBCOMMAND_TABLE[sub];
if (handler !== undefined) {
return handler(storageRoot, argv.slice(1));
const entry = table[sub];
if (entry === undefined) {
return null;
}
return entry.handler(storageRoot, argv.slice(1));
}
async function dispatchInit(storageRoot: string, argv: string[]): Promise<number> {
const result = dispatchGroup("init", INIT_SUBCOMMAND_TABLE, storageRoot, argv);
if (result !== null) {
return result;
}
const sub = argv[0];
printCliError(`${formatCliUsage()}\n\nerror: unknown init subcommand: ${sub}`);
return 1;
}
async function dispatchWorkflow(storageRoot: string, argv: string[]): Promise<number> {
const result = dispatchGroup("workflow", WORKFLOW_SUBCOMMAND_TABLE, storageRoot, argv);
if (result !== null) {
return result;
}
const sub = argv[0];
if (sub === "remove") {
printDeprecation("workflow remove", "workflow rm");
return dispatchRemove(storageRoot, argv.slice(1));
@@ -477,33 +596,24 @@ async function dispatchWorkflow(storageRoot: string, argv: string[]): Promise<nu
return 1;
}
// ── Thread subcommand table (Phase 2) ──────────────────────────────────
const THREAD_SUBCOMMAND_TABLE: Record<string, DispatchFn> = {
run: dispatchRun,
list: dispatchThreadList,
show: dispatchThreadShow,
rm: dispatchThreadRm,
fork: dispatchFork,
ps: dispatchPs,
kill: dispatchKill,
live: dispatchLive,
pause: dispatchPause,
resume: dispatchResume,
};
async function dispatchThread(storageRoot: string, argv: string[]): Promise<number> {
const result = dispatchGroup("thread", THREAD_SUBCOMMAND_TABLE, storageRoot, argv);
if (result !== null) {
return result;
}
const sub = argv[0];
if (sub === undefined) {
printCliError(`${formatCliUsage()}\n\nerror: unknown thread subcommand: (none)`);
return 1;
printCliError(`${formatCliUsage()}\n\nerror: unknown thread subcommand: ${sub}`);
return 1;
}
async function dispatchCas(storageRoot: string, argv: string[]): Promise<number> {
const result = dispatchGroup("cas", CAS_SUBCOMMAND_TABLE, storageRoot, argv);
if (result !== null) {
return result;
}
const handler = THREAD_SUBCOMMAND_TABLE[sub];
if (handler === undefined) {
printCliError(`${formatCliUsage()}\n\nerror: unknown thread subcommand: ${sub}`);
return 1;
}
return handler(storageRoot, argv.slice(1));
const sub = argv[0];
printCliError(`${formatCliUsage()}\n\nerror: unknown cas subcommand: ${sub}`);
return 1;
}
// ── Help ────────────────────────────────────────────────────────────────
+16 -42
View File
@@ -1,4 +1,19 @@
import { getCommandRegistry } from "./cli-dispatch.js";
export function formatSkillDoc(): string {
const groups = getCommandRegistry();
const commandSections: string[] = [];
for (const group of groups) {
const rows = group.commands.map((cmd) => {
const args = cmd.args ? `\`${cmd.args}\`` : "(none)";
return `| \`${group.name} ${cmd.name}\` | ${args} | ${cmd.description} |`;
});
commandSections.push(
`### ${group.name}\n\n| Command | Args | Description |\n|---------|------|-------------|\n${rows.join("\n")}`,
);
}
return `# uncaged-workflow CLI Reference
## Core Concepts
@@ -13,48 +28,7 @@ export function formatSkillDoc(): string {
## Commands
### workflow
| Command | Args | Description |
|---------|------|-------------|
| \`workflow add\` | \`<name> <file.esm.js> [--types <path>]\` | Register a workflow bundle in the registry |
| \`workflow list\` | (none) | List all registered workflows |
| \`workflow show\` | \`<name>\` | Show details of a registered workflow |
| \`workflow rm\` | \`<name>\` | Remove a workflow from the registry |
| \`workflow history\` | \`<name>\` | Show version history of a workflow |
| \`workflow rollback\` | \`<name> [hash]\` | Rollback a workflow to a previous version |
### thread
| Command | Args | Description |
|---------|------|-------------|
| \`thread run\` | \`<name> [--prompt <text>] [--max-rounds N]\` | Start a new thread executing a workflow |
| \`thread list\` | \`[name]\` | List threads, optionally filtered by workflow name |
| \`thread show\` | \`<id>\` | Show thread details and state |
| \`thread rm\` | \`<id>\` | Remove a thread |
| \`thread fork\` | \`<thread-id> [--from-role <role>]\` | Fork a thread, optionally from a specific role |
| \`thread ps\` | (none) | List running threads |
| \`thread kill\` | \`<thread-id>\` | Kill a running thread |
| \`thread live\` | \`<thread-id> [--debug] [--role <name>]\` or \`--latest [--debug] [--role <name>]\` | Attach to a thread and stream output live |
| \`thread pause\` | \`<thread-id>\` | Pause a running thread |
| \`thread resume\` | \`<thread-id>\` | Resume a paused thread |
### cas
| Command | Args | Description |
|---------|------|-------------|
| \`cas get\` | \`<thread-id> <hash>\` | Retrieve content by hash from a thread's CAS |
| \`cas put\` | \`<thread-id> <content>\` | Store content in a thread's CAS, returns hash |
| \`cas list\` | \`<thread-id>\` | List all CAS entries for a thread |
| \`cas rm\` | \`<thread-id> <hash>\` | Remove a CAS entry |
| \`cas gc\` | (none) | Garbage-collect unreferenced CAS entries |
### init
| Command | Args | Description |
|---------|------|-------------|
| \`init workspace\` | \`<name>\` | Initialize a new workflow workspace |
| \`init template\` | \`<name>\` | Initialize a new workflow template |
${commandSections.join("\n\n")}
### Top-level shortcuts
-15
View File
@@ -1,15 +0,0 @@
{
"name": "@uncaged/workflow-role-coder",
"version": "0.1.0",
"type": "module",
"main": "src/index.ts",
"types": "src/index.ts",
"scripts": {
"build": "echo 'TODO'",
"test": "echo no tests"
},
"dependencies": {
"@uncaged/workflow": "workspace:*",
"zod": "^4.0.0"
}
}
@@ -1 +0,0 @@
export { type CoderMeta, coderMetaSchema, coderRole } from "./coder.js";
@@ -1,10 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"composite": true
},
"include": ["src/**/*.ts"],
"references": [{ "path": "../workflow" }]
}
@@ -1,15 +0,0 @@
{
"name": "@uncaged/workflow-role-committer",
"version": "0.1.0",
"type": "module",
"main": "src/index.ts",
"types": "src/index.ts",
"scripts": {
"build": "echo 'TODO'",
"test": "bun test"
},
"dependencies": {
"@uncaged/workflow": "workspace:*",
"zod": "^4.0.0"
}
}
@@ -1 +0,0 @@
export { type CommitterMeta, committerMetaSchema, committerRole } from "./committer.js";
@@ -1,10 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"composite": true
},
"include": ["src/**/*.ts"],
"references": [{ "path": "../workflow" }]
}
@@ -1,15 +0,0 @@
{
"name": "@uncaged/workflow-role-planner",
"version": "0.1.0",
"type": "module",
"main": "src/index.ts",
"types": "src/index.ts",
"scripts": {
"build": "echo 'TODO'",
"test": "echo no tests"
},
"dependencies": {
"@uncaged/workflow": "workspace:*",
"zod": "^4.0.0"
}
}
@@ -1,6 +0,0 @@
export {
type PlannerMeta,
phaseSchema,
plannerMetaSchema,
plannerRole,
} from "./planner.js";
@@ -1,10 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"composite": true
},
"include": ["src/**/*.ts"],
"references": [{ "path": "../workflow" }]
}
@@ -1,15 +0,0 @@
{
"name": "@uncaged/workflow-role-preparer",
"version": "0.1.0",
"type": "module",
"main": "src/index.ts",
"types": "src/index.ts",
"scripts": {
"build": "echo 'TODO'",
"test": "echo no tests"
},
"dependencies": {
"@uncaged/workflow": "workspace:*",
"zod": "^4.0.0"
}
}
@@ -1,8 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist"
},
"include": ["src"]
}
@@ -1,15 +0,0 @@
{
"name": "@uncaged/workflow-role-reviewer",
"version": "0.1.0",
"type": "module",
"main": "src/index.ts",
"types": "src/index.ts",
"scripts": {
"build": "echo 'TODO'",
"test": "bun test"
},
"dependencies": {
"@uncaged/workflow": "workspace:*",
"zod": "^4.0.0"
}
}
@@ -1 +0,0 @@
export { type ReviewerMeta, reviewerMetaSchema, reviewerRole } from "./reviewer.js";
@@ -1,10 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"composite": true
},
"include": ["src/**/*.ts"],
"references": [{ "path": "../workflow" }]
}
@@ -1,15 +0,0 @@
{
"name": "@uncaged/workflow-role-submitter",
"version": "0.1.0",
"type": "module",
"main": "src/index.ts",
"types": "src/index.ts",
"scripts": {
"build": "echo 'TODO'",
"test": "bun test"
},
"dependencies": {
"@uncaged/workflow": "workspace:*",
"zod": "^4.0.0"
}
}
@@ -1 +0,0 @@
export { type SubmitterMeta, submitterMetaSchema, submitterRole } from "./submitter.js";
@@ -1,10 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"composite": true
},
"include": ["src/**/*.ts"],
"references": [{ "path": "../workflow" }]
}
@@ -1,15 +0,0 @@
{
"name": "@uncaged/workflow-role-tester",
"version": "0.1.0",
"type": "module",
"main": "src/index.ts",
"types": "src/index.ts",
"scripts": {
"build": "echo 'TODO'",
"test": "bun test"
},
"dependencies": {
"@uncaged/workflow": "workspace:*",
"zod": "^4.0.0"
}
}
@@ -1 +0,0 @@
export { type TesterMeta, testerMetaSchema, testerRole } from "./tester.js";
@@ -1,10 +0,0 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"rootDir": "src",
"outDir": "dist",
"composite": true
},
"include": ["src/**/*.ts"],
"references": [{ "path": "../workflow" }]
}
@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test";
import { committerMetaSchema, committerRole } from "../src/committer.js";
import { committerMetaSchema, committerRole } from "../src/roles/committer.js";
describe("committerRole", () => {
test("committed sample validates against schema", () => {
@@ -6,12 +6,9 @@ import {
START,
validateWorkflowDescriptor,
} from "@uncaged/workflow";
import type { CommitterMeta } from "@uncaged/workflow-role-committer";
import type { PlannerMeta } from "@uncaged/workflow-role-planner";
import { buildDevelopDescriptor } from "../src/descriptor.js";
import { developModerator } from "../src/index.js";
import type { CommitterMeta, PlannerMeta } from "../src/roles/index.js";
import type { DevelopMeta } from "../src/roles.js";
const DEFAULT_PHASES: PlannerMeta["phases"] = [
@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test";
import { reviewerMetaSchema, reviewerRole } from "../src/reviewer.js";
import { reviewerMetaSchema, reviewerRole } from "../src/roles/reviewer.js";
describe("reviewerRole", () => {
test("approved sample validates against schema", () => {
@@ -10,10 +10,6 @@
},
"dependencies": {
"@uncaged/workflow": "workspace:*",
"@uncaged/workflow-role-coder": "workspace:*",
"@uncaged/workflow-role-committer": "workspace:*",
"@uncaged/workflow-role-planner": "workspace:*",
"@uncaged/workflow-role-reviewer": "workspace:*",
"@uncaged/workflow-role-tester": "workspace:*"
"zod": "^4.0.0"
}
}
@@ -10,34 +10,26 @@ import {
import { developModerator } from "./moderator.js";
import { DEVELOP_WORKFLOW_DESCRIPTION, type DevelopMeta, developRoles } from "./roles.js";
export { buildDevelopDescriptor } from "./descriptor.js";
export { developModerator } from "./moderator.js";
export {
type CoderMeta,
type CommitterMeta,
coderMetaSchema,
coderRole,
} from "@uncaged/workflow-role-coder";
export {
type CommitterMeta,
committerMetaSchema,
committerRole,
} from "@uncaged/workflow-role-committer";
export {
type PlannerMeta,
phaseSchema,
plannerMetaSchema,
plannerRole,
} from "@uncaged/workflow-role-planner";
export {
type ReviewerMeta,
reviewerMetaSchema,
reviewerRole,
} from "@uncaged/workflow-role-reviewer";
export {
type TesterMeta,
testerMetaSchema,
testerRole,
} from "@uncaged/workflow-role-tester";
export { buildDevelopDescriptor } from "./descriptor.js";
export { developModerator } from "./moderator.js";
} from "./roles/index.js";
export {
DEVELOP_WORKFLOW_DESCRIPTION,
type DevelopMeta,
@@ -1,9 +1,9 @@
import type { RoleDefinition } from "@uncaged/workflow";
import { type CoderMeta, coderRole } from "@uncaged/workflow-role-coder";
import { type CommitterMeta, committerRole } from "@uncaged/workflow-role-committer";
import { type PlannerMeta, plannerRole } from "@uncaged/workflow-role-planner";
import { type ReviewerMeta, reviewerRole } from "@uncaged/workflow-role-reviewer";
import { type TesterMeta, testerRole } from "@uncaged/workflow-role-tester";
import { type CoderMeta, coderRole } from "./roles/coder.js";
import { type CommitterMeta, committerRole } from "./roles/committer.js";
import { type PlannerMeta, plannerRole } from "./roles/planner.js";
import { type ReviewerMeta, reviewerRole } from "./roles/reviewer.js";
import { type TesterMeta, testerRole } from "./roles/tester.js";
export const DEVELOP_WORKFLOW_DESCRIPTION =
"Plan phases, implement incrementally, review, verify with tests/build/lint, and commit (planner → coder [repeat per phase] → reviewer → tester → committer).";
@@ -11,21 +11,13 @@ export type CoderMeta = z.infer<typeof coderMetaSchema>;
const CODER_SYSTEM = `You are a **coder**. Read the thread for the plan and work on the NEXT incomplete phase only.
## Finding the current thread ID
The thread ID is a 26-character Crockford Base32 string (e.g. \`06F03H5V6JTMDST6P3TVH42RWM\`). It appears in the first message of this conversation. If you are unsure, run:
uncaged-workflow threads
and use the ID of the active thread.
Run \`uncaged-workflow help --skill\` for full CLI reference (thread ID lookup, CAS commands, etc.).
## Reading phase details
Each planner phase is identified by a content-hash and a title. To read a phase's full details (name, description, acceptance criteria), run:
Each planner phase has a content-hash and title. Read full details with \`uncaged-workflow cas get <THREAD_ID> <HASH>\`.
uncaged-workflow cas get <THREAD_ID> <HASH>
Replace \`<THREAD_ID>\` with the actual thread ID and \`<HASH>\` with the phase hash from the plan.
The thread ID (26-char Crockford Base32) appears in the first message. If unsure, run \`uncaged-workflow thread list\`.
## Completing a phase
@@ -0,0 +1,10 @@
export { type CoderMeta, coderMetaSchema, coderRole } from "./coder.js";
export { type CommitterMeta, committerMetaSchema, committerRole } from "./committer.js";
export {
type PlannerMeta,
phaseSchema,
plannerMetaSchema,
plannerRole,
} from "./planner.js";
export { type ReviewerMeta, reviewerMetaSchema, reviewerRole } from "./reviewer.js";
export { type TesterMeta, testerMetaSchema, testerRole } from "./tester.js";
@@ -14,27 +14,15 @@ export type PlannerMeta = z.infer<typeof plannerMetaSchema>;
const PLANNER_SYSTEM = `You are a **planner** for a software task. Break the work into **sequential phases** the coder will execute one at a time.
## Finding the current thread ID
The thread ID is a 26-character Crockford Base32 string (e.g. \`06F03H5V6JTMDST6P3TVH42RWM\`). It appears in the first message of this conversation. If you are unsure, run:
uncaged-workflow threads
and use the ID of the active thread.
Run \`uncaged-workflow help --skill\` for full CLI reference (thread ID lookup, CAS commands, etc.).
## Storing phase details MANDATORY
For each phase you MUST store its full detail text in CAS using this exact CLI command:
For each phase, store its full detail text in CAS via \`uncaged-workflow cas put <THREAD_ID> '<content>'\`. The command prints a content-hash — use that as the phase identifier.
uncaged-workflow cas put <THREAD_ID> '# <name>
The thread ID (26-char Crockford Base32) appears in the first message. If unsure, run \`uncaged-workflow thread list\`.
Description: <description>
Acceptance: <acceptance>'
Replace \`<THREAD_ID>\` with the actual thread ID you found above. The command prints a content-hash to stdout — use that hash as the phase identifier.
**Do NOT store phase details in any other way** (no temp files, no invented paths). The CLI command is the only supported storage mechanism.
**Do NOT store phase details in any other way** the CLI is the only supported storage mechanism.
## Phase granularity
@@ -6,12 +6,5 @@
"composite": true
},
"include": ["src/**/*.ts"],
"references": [
{ "path": "../workflow" },
{ "path": "../workflow-role-coder" },
{ "path": "../workflow-role-committer" },
{ "path": "../workflow-role-planner" },
{ "path": "../workflow-role-reviewer" },
{ "path": "../workflow-role-tester" }
]
"references": [{ "path": "../workflow" }]
}
@@ -11,13 +11,10 @@ import {
START,
validateWorkflowDescriptor,
} from "@uncaged/workflow";
import type { PreparerMeta } from "@uncaged/workflow-role-preparer";
import type { SubmitterMeta } from "@uncaged/workflow-role-submitter";
import { buildSolveIssueDescriptor } from "../src/descriptor.js";
import type { DeveloperMeta } from "../src/developer.js";
import { createSolveIssueRun, solveIssueModerator } from "../src/index.js";
import type { PreparerMeta, SubmitterMeta } from "../src/roles/index.js";
import type { SolveIssueMeta } from "../src/roles.js";
function jsonResponse(payload: Record<string, unknown>): Response {
@@ -1,6 +1,6 @@
import { describe, expect, test } from "bun:test";
import { submitterMetaSchema, submitterRole } from "../src/submitter.js";
import { submitterMetaSchema, submitterRole } from "../src/roles/submitter.js";
describe("submitterRole", () => {
test("submitted sample validates against schema", () => {
@@ -10,8 +10,6 @@
},
"dependencies": {
"@uncaged/workflow": "workspace:*",
"@uncaged/workflow-role-preparer": "workspace:*",
"@uncaged/workflow-role-submitter": "workspace:*",
"zod": "^4.0.0"
}
}
@@ -11,16 +11,6 @@ import {
import { solveIssueModerator } from "./moderator.js";
import { SOLVE_ISSUE_WORKFLOW_DESCRIPTION, type SolveIssueMeta, solveIssueRoles } from "./roles.js";
export {
type PreparerMeta,
preparerMetaSchema,
preparerRole,
} from "@uncaged/workflow-role-preparer";
export {
type SubmitterMeta,
submitterMetaSchema,
submitterRole,
} from "@uncaged/workflow-role-submitter";
export { buildSolveIssueDescriptor } from "./descriptor.js";
export {
type DeveloperMeta,
@@ -28,6 +18,14 @@ export {
developerRole,
} from "./developer.js";
export { solveIssueModerator } from "./moderator.js";
export {
type PreparerMeta,
preparerMetaSchema,
preparerRole,
type SubmitterMeta,
submitterMetaSchema,
submitterRole,
} from "./roles/index.js";
export {
SOLVE_ISSUE_WORKFLOW_DESCRIPTION,
type SolveIssueMeta,
@@ -1,8 +1,7 @@
import type { RoleDefinition } from "@uncaged/workflow";
import { type PreparerMeta, preparerRole } from "@uncaged/workflow-role-preparer";
import { type SubmitterMeta, submitterRole } from "@uncaged/workflow-role-submitter";
import { type DeveloperMeta, developerRole } from "./developer.js";
import { type PreparerMeta, preparerRole } from "./roles/preparer.js";
import { type SubmitterMeta, submitterRole } from "./roles/submitter.js";
export const SOLVE_ISSUE_WORKFLOW_DESCRIPTION =
"Resolve an issue end-to-end by preparing the repo, delegating implementation to the develop workflow, and opening a pull request (preparer → developer → submitter).";
@@ -3,3 +3,4 @@ export {
preparerMetaSchema,
preparerRole,
} from "./preparer.js";
export { type SubmitterMeta, submitterMetaSchema, submitterRole } from "./submitter.js";
@@ -6,9 +6,5 @@
"composite": true
},
"include": ["src/**/*.ts"],
"references": [
{ "path": "../workflow" },
{ "path": "../workflow-role-preparer" },
{ "path": "../workflow-role-submitter" }
]
"references": [{ "path": "../workflow" }]
}
-7
View File
@@ -19,13 +19,6 @@
"references": [
{ "path": "packages/workflow" },
{ "path": "packages/workflow-agent-llm" },
{ "path": "packages/workflow-role-committer" },
{ "path": "packages/workflow-role-coder" },
{ "path": "packages/workflow-role-planner" },
{ "path": "packages/workflow-role-preparer" },
{ "path": "packages/workflow-role-reviewer" },
{ "path": "packages/workflow-role-submitter" },
{ "path": "packages/workflow-role-tester" },
{ "path": "packages/workflow-agent-cursor" },
{ "path": "packages/workflow-agent-hermes" },
{ "path": "packages/workflow-util-agent" },