Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a5c09adae6 | |||
| f81e2a8aac | |||
| 2b38e583be | |||
| 4ff1394224 |
@@ -0,0 +1,76 @@
|
||||
# @uncaged/cli-workflow
|
||||
|
||||
Command-line interface for the Uncaged workflow engine (`uncaged-workflow`).
|
||||
|
||||
The CLI reads and writes the workflow registry, starts and inspects threads, manages CAS blobs, and prints agent-oriented reference docs via `skill`. It uses the same storage layout as `@uncaged/workflow` (default `~/.uncaged/workflow`).
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @uncaged/cli-workflow
|
||||
```
|
||||
|
||||
In this monorepo: `"@uncaged/cli-workflow": "workspace:*"`. Depends on `"@uncaged/workflow": "workspace:*"`.
|
||||
|
||||
## Usage
|
||||
|
||||
```bash
|
||||
uncaged-workflow workflow list
|
||||
uncaged-workflow run <name> --prompt "Your task"
|
||||
uncaged-workflow thread show <id>
|
||||
uncaged-workflow skill
|
||||
```
|
||||
|
||||
Invoking the CLI with no command (or from this repo: `bun packages/cli-workflow/src/cli.ts`) prints:
|
||||
|
||||
```
|
||||
uncaged-workflow — workflow engine CLI
|
||||
|
||||
Workflow registry:
|
||||
workflow add <name> <file.esm.js> [--types <path>] Register a workflow bundle in the registry
|
||||
workflow list 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 execution:
|
||||
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 List running threads
|
||||
thread kill <thread-id> Kill a running thread
|
||||
thread live <thread-id> | --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
|
||||
|
||||
Content-addressable storage:
|
||||
cas get <hash> Retrieve content by hash from CAS
|
||||
cas put <content> Store content in CAS, prints hash
|
||||
cas list List all hashes in CAS
|
||||
cas rm <hash> Remove a CAS entry by hash
|
||||
cas gc Garbage-collect unreferenced CAS entries
|
||||
|
||||
Development:
|
||||
init workspace <name> Initialize a new workflow workspace
|
||||
init template <name> Initialize a new workflow template
|
||||
|
||||
Shortcuts:
|
||||
run <name> [...] → thread run
|
||||
live <id> [...] → thread live
|
||||
|
||||
Reference:
|
||||
skill [topic] Agent-consumable docs (cli, develop, author)
|
||||
|
||||
Use <command> --help for subcommand details.
|
||||
|
||||
Environment variables:
|
||||
WORKFLOW_STORAGE_ROOT Override storage directory (default: ~/.uncaged/workflow)
|
||||
UNCAGED_WORKFLOW_STORAGE_ROOT Internal override (takes priority over WORKFLOW_STORAGE_ROOT)
|
||||
```
|
||||
|
||||
## API overview
|
||||
|
||||
This package is bin-only; programmatic use is via `@uncaged/workflow`. Entry: `src/cli.ts` → `runCli` in `src/cli-dispatch.js`.
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ParsedAddArgv } from "../src/commands/workflow/add-argv.js";
|
||||
import type { ParsedAddArgv } from "../src/commands/workflow/index.js";
|
||||
|
||||
export function addCliArgs(name: string, filePath: string): ParsedAddArgv {
|
||||
return { name, filePath, typesPath: null };
|
||||
|
||||
@@ -4,16 +4,16 @@ import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { getGlobalCasDir, getRegisteredWorkflow, readWorkflowRegistry } from "@uncaged/workflow";
|
||||
import { cmdCasGet } from "../src/commands/cas/get.js";
|
||||
import { cmdCasList } from "../src/commands/cas/list.js";
|
||||
import { cmdCasPut } from "../src/commands/cas/put.js";
|
||||
import { cmdCasRm } from "../src/commands/cas/rm.js";
|
||||
import { cmdAdd } from "../src/commands/workflow/add.js";
|
||||
import { cmdHistory } from "../src/commands/workflow/history.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 { cmdCasGet, cmdCasList, cmdCasPut, cmdCasRm } from "../src/commands/cas/index.js";
|
||||
import {
|
||||
cmdAdd,
|
||||
cmdHistory,
|
||||
cmdList,
|
||||
cmdRemove,
|
||||
cmdRollback,
|
||||
cmdShow,
|
||||
formatListLines,
|
||||
} from "../src/commands/workflow/index.js";
|
||||
import { addCliArgs } from "./bundle-fixture.js";
|
||||
|
||||
const fixtureDescriptor = `export const descriptor = { description: "fixture", roles: {} };
|
||||
|
||||
@@ -3,9 +3,8 @@ import { mkdir, mkdtemp, readFile, rm, writeFile } from "node:fs/promises";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { createCasStore, getContentMerklePayload, getGlobalCasDir } from "@uncaged/workflow";
|
||||
import { cmdFork } from "../src/commands/thread/fork.js";
|
||||
import { cmdRun } from "../src/commands/thread/run.js";
|
||||
import { cmdAdd } from "../src/commands/workflow/add.js";
|
||||
import { cmdFork, cmdRun } from "../src/commands/thread/index.js";
|
||||
import { cmdAdd } from "../src/commands/workflow/index.js";
|
||||
import { pathExists } from "../src/fs-utils.js";
|
||||
import { addCliArgs } from "./bundle-fixture.js";
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
getGlobalCasDir,
|
||||
putContentMerkleNode,
|
||||
} from "@uncaged/workflow";
|
||||
import { cmdThreadRemove } from "../src/commands/thread/rm.js";
|
||||
import { cmdThreadRemove } from "../src/commands/thread/index.js";
|
||||
import { pathExists } from "../src/fs-utils.js";
|
||||
|
||||
const cliEntryPath = fileURLToPath(new URL("../src/cli.ts", import.meta.url));
|
||||
|
||||
@@ -4,8 +4,7 @@ import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { runCli } from "../src/cli-dispatch.js";
|
||||
import { cmdInitTemplate } from "../src/commands/init/template.js";
|
||||
import { cmdInitWorkspace } from "../src/commands/init/workspace.js";
|
||||
import { cmdInitTemplate, cmdInitWorkspace } from "../src/commands/init/index.js";
|
||||
import { pathExists } from "../src/fs-utils.js";
|
||||
|
||||
describe("init template", () => {
|
||||
|
||||
@@ -4,7 +4,7 @@ import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { formatCliUsage, runCli } from "../src/cli-dispatch.js";
|
||||
import { cmdInitWorkspace } from "../src/commands/init/workspace.js";
|
||||
import { cmdInitWorkspace } from "../src/commands/init/index.js";
|
||||
import { pathExists } from "../src/fs-utils.js";
|
||||
|
||||
describe("init workspace", () => {
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
LIVE_CONTENT_MAX_LINES,
|
||||
type LiveRoleRow,
|
||||
renderLiveRoleStepLines,
|
||||
} from "../src/commands/thread/live.js";
|
||||
} from "../src/commands/thread/index.js";
|
||||
import { parseLiveArgv } from "../src/live-argv.js";
|
||||
|
||||
const cliEntryPath = fileURLToPath(new URL("../src/cli.ts", import.meta.url));
|
||||
|
||||
@@ -5,14 +5,18 @@ import { tmpdir } from "node:os";
|
||||
import { dirname, join } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { getGlobalCasDir } from "@uncaged/workflow";
|
||||
import { cmdCasPut } from "../src/commands/cas/put.js";
|
||||
import { cmdKill, cmdPause, cmdResume } from "../src/commands/thread/control.js";
|
||||
import { cmdThreads } from "../src/commands/thread/list.js";
|
||||
import { cmdPs } from "../src/commands/thread/ps.js";
|
||||
import { cmdThreadRemove } from "../src/commands/thread/rm.js";
|
||||
import { cmdRun } from "../src/commands/thread/run.js";
|
||||
import { cmdThreadShow } from "../src/commands/thread/show.js";
|
||||
import { cmdAdd } from "../src/commands/workflow/add.js";
|
||||
import { cmdCasPut } from "../src/commands/cas/index.js";
|
||||
import {
|
||||
cmdKill,
|
||||
cmdPause,
|
||||
cmdPs,
|
||||
cmdResume,
|
||||
cmdRun,
|
||||
cmdThreadRemove,
|
||||
cmdThreadShow,
|
||||
cmdThreads,
|
||||
} from "../src/commands/thread/index.js";
|
||||
import { cmdAdd } from "../src/commands/workflow/index.js";
|
||||
import { pathExists, readTextFileIfExists } from "../src/fs-utils.js";
|
||||
import { addCliArgs } from "./bundle-fixture.js";
|
||||
|
||||
|
||||
@@ -2,8 +2,8 @@ import type { CommandEntry, DispatchFn } from "./cli-command-types.js";
|
||||
import { printCliError, printCliLine, printCliWarn } from "./cli-output.js";
|
||||
import { getCommandRegistry } from "./cli-registry.js";
|
||||
import { formatCliUsage as formatCliUsageWithGroups } from "./cli-usage.js";
|
||||
import { createCasDispatcher, dispatchGc } from "./commands/cas/dispatch.js";
|
||||
import { createInitDispatcher } from "./commands/init/dispatch.js";
|
||||
import { createCasDispatcher, dispatchGc } from "./commands/cas/index.js";
|
||||
import { createInitDispatcher } from "./commands/init/index.js";
|
||||
import {
|
||||
createThreadDispatcher,
|
||||
dispatchFork,
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
dispatchResume,
|
||||
dispatchRun,
|
||||
dispatchThreadList,
|
||||
} from "./commands/thread/dispatch.js";
|
||||
} from "./commands/thread/index.js";
|
||||
import {
|
||||
createWorkflowDispatcher,
|
||||
dispatchAdd,
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
dispatchRemove,
|
||||
dispatchRollback,
|
||||
dispatchShow,
|
||||
} from "./commands/workflow/dispatch.js";
|
||||
} from "./commands/workflow/index.js";
|
||||
import { formatSkillIndex, formatSkillTopic, getSkillTopics } from "./skill.js";
|
||||
|
||||
export type { CommandEntry, CommandGroup, DispatchFn } from "./cli-command-types.js";
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import type { CommandGroup } from "./cli-command-types.js";
|
||||
import { setCommandGroupsForUsage } from "./cli-usage-context.js";
|
||||
import { CAS_SUBCOMMAND_TABLE } from "./commands/cas/dispatch.js";
|
||||
import { INIT_SUBCOMMAND_TABLE } from "./commands/init/dispatch.js";
|
||||
import { THREAD_SUBCOMMAND_TABLE } from "./commands/thread/dispatch.js";
|
||||
import { WORKFLOW_SUBCOMMAND_TABLE } from "./commands/workflow/dispatch.js";
|
||||
import { CAS_SUBCOMMAND_TABLE } from "./commands/cas/index.js";
|
||||
import { INIT_SUBCOMMAND_TABLE } from "./commands/init/index.js";
|
||||
import { THREAD_SUBCOMMAND_TABLE } from "./commands/thread/index.js";
|
||||
import { WORKFLOW_SUBCOMMAND_TABLE } from "./commands/workflow/index.js";
|
||||
|
||||
export function getCommandRegistry(): ReadonlyArray<CommandGroup> {
|
||||
return [
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CommandEntry, DispatchGroupFn } from "../../cli-command-types.js";
|
||||
import type { CommandEntry } from "../../cli-command-types.js";
|
||||
import { printCliError, printCliLine } from "../../cli-output.js";
|
||||
import { formatCliUsage, USAGE_SKILL_TOPIC_ROWS } from "../../cli-usage.js";
|
||||
import { getCommandGroupsForUsage } from "../../cli-usage-context.js";
|
||||
@@ -7,6 +7,7 @@ import { cmdCasGet } from "./get.js";
|
||||
import { cmdCasList } from "./list.js";
|
||||
import { cmdCasPut } from "./put.js";
|
||||
import { cmdCasRm } from "./rm.js";
|
||||
import type { CasDispatchDeps } from "./types.js";
|
||||
|
||||
function usageText(): string {
|
||||
return formatCliUsage(getCommandGroupsForUsage(), USAGE_SKILL_TOPIC_ROWS);
|
||||
@@ -110,7 +111,7 @@ export const CAS_SUBCOMMAND_TABLE: Record<string, CommandEntry> = {
|
||||
gc: { handler: dispatchGc, args: "", description: "Garbage-collect unreferenced CAS entries" },
|
||||
};
|
||||
|
||||
export function createCasDispatcher(deps: { dispatchGroup: DispatchGroupFn }) {
|
||||
export function createCasDispatcher(deps: CasDispatchDeps) {
|
||||
const { dispatchGroup } = deps;
|
||||
return async function dispatchCas(storageRoot: string, argv: string[]): Promise<number> {
|
||||
const result = dispatchGroup("cas", CAS_SUBCOMMAND_TABLE, storageRoot, argv);
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
export {
|
||||
CAS_SUBCOMMAND_TABLE,
|
||||
createCasDispatcher,
|
||||
dispatchCasGet,
|
||||
dispatchCasList,
|
||||
dispatchCasPut,
|
||||
dispatchCasRm,
|
||||
dispatchGc,
|
||||
} from "./dispatch.js";
|
||||
export { cmdGc } from "./gc.js";
|
||||
export { cmdCasGet } from "./get.js";
|
||||
export { cmdCasList } from "./list.js";
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import type { DispatchGroupFn } from "../../cli-command-types.js";
|
||||
|
||||
export type CasDispatchDeps = {
|
||||
dispatchGroup: DispatchGroupFn;
|
||||
};
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { CommandEntry, DispatchGroupFn } from "../../cli-command-types.js";
|
||||
import type { CommandEntry } from "../../cli-command-types.js";
|
||||
import { printCliError, printCliLine } from "../../cli-output.js";
|
||||
import { formatCliUsage, USAGE_SKILL_TOPIC_ROWS } from "../../cli-usage.js";
|
||||
import { getCommandGroupsForUsage } from "../../cli-usage-context.js";
|
||||
import { cmdInitTemplate } from "./template.js";
|
||||
import type { InitDispatchDeps } from "./types.js";
|
||||
import { cmdInitWorkspace } from "./workspace.js";
|
||||
|
||||
function usageText(): string {
|
||||
@@ -52,7 +53,7 @@ export const INIT_SUBCOMMAND_TABLE: Record<string, CommandEntry> = {
|
||||
},
|
||||
};
|
||||
|
||||
export function createInitDispatcher(deps: { dispatchGroup: DispatchGroupFn }) {
|
||||
export function createInitDispatcher(deps: InitDispatchDeps) {
|
||||
const { dispatchGroup } = deps;
|
||||
return async function dispatchInit(storageRoot: string, argv: string[]): Promise<number> {
|
||||
const result = dispatchGroup("init", INIT_SUBCOMMAND_TABLE, storageRoot, argv);
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
export type { CmdInitTemplateSuccess } from "./template.js";
|
||||
export {
|
||||
createInitDispatcher,
|
||||
dispatchInitTemplate,
|
||||
dispatchInitWorkspace,
|
||||
INIT_SUBCOMMAND_TABLE,
|
||||
} from "./dispatch.js";
|
||||
export { cmdInitTemplate } from "./template.js";
|
||||
export type { CmdInitWorkspaceSuccess } from "./workspace.js";
|
||||
export type { CmdInitTemplateSuccess, CmdInitWorkspaceSuccess } from "./types.js";
|
||||
export { cmdInitWorkspace } from "./workspace.js";
|
||||
|
||||
@@ -12,10 +12,7 @@ import {
|
||||
templateRolesTs,
|
||||
templateTsconfigJson,
|
||||
} from "./templates.js";
|
||||
|
||||
export type CmdInitTemplateSuccess = {
|
||||
templatePath: string;
|
||||
};
|
||||
import type { CmdInitTemplateSuccess } from "./types.js";
|
||||
|
||||
function validateWorkspaceSegment(name: string): Result<void, string> {
|
||||
if (name.length === 0) {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
import type { DispatchGroupFn } from "../../cli-command-types.js";
|
||||
|
||||
export type CmdInitTemplateSuccess = {
|
||||
templatePath: string;
|
||||
};
|
||||
|
||||
export type CmdInitWorkspaceSuccess = {
|
||||
rootPath: string;
|
||||
};
|
||||
|
||||
export type InitDispatchDeps = {
|
||||
dispatchGroup: DispatchGroupFn;
|
||||
};
|
||||
@@ -4,10 +4,7 @@ import { join } from "node:path";
|
||||
import { err, ok, type Result } from "@uncaged/workflow";
|
||||
|
||||
import { pathExists } from "../../fs-utils.js";
|
||||
|
||||
export type CmdInitWorkspaceSuccess = {
|
||||
rootPath: string;
|
||||
};
|
||||
import type { CmdInitWorkspaceSuccess } from "./types.js";
|
||||
|
||||
function validateWorkspaceSegment(name: string): Result<void, string> {
|
||||
if (name.length === 0) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CommandEntry, DispatchGroupFn } from "../../cli-command-types.js";
|
||||
import type { CommandEntry } from "../../cli-command-types.js";
|
||||
import { printCliError, printCliLine } from "../../cli-output.js";
|
||||
import { formatCliUsage, USAGE_SKILL_TOPIC_ROWS } from "../../cli-usage.js";
|
||||
import { getCommandGroupsForUsage } from "../../cli-usage-context.js";
|
||||
@@ -13,6 +13,7 @@ import { cmdPs } from "./ps.js";
|
||||
import { cmdThreadRemove } from "./rm.js";
|
||||
import { cmdRun } from "./run.js";
|
||||
import { cmdThreadShow } from "./show.js";
|
||||
import type { ThreadDispatchDeps } from "./types.js";
|
||||
|
||||
function usageText(): string {
|
||||
return formatCliUsage(getCommandGroupsForUsage(), USAGE_SKILL_TOPIC_ROWS);
|
||||
@@ -191,7 +192,7 @@ export const THREAD_SUBCOMMAND_TABLE: Record<string, CommandEntry> = {
|
||||
resume: { handler: dispatchResume, args: "<thread-id>", description: "Resume a paused thread" },
|
||||
};
|
||||
|
||||
export function createThreadDispatcher(deps: { dispatchGroup: DispatchGroupFn }) {
|
||||
export function createThreadDispatcher(deps: ThreadDispatchDeps) {
|
||||
const { dispatchGroup } = deps;
|
||||
return async function dispatchThread(storageRoot: string, argv: string[]): Promise<number> {
|
||||
const result = dispatchGroup("thread", THREAD_SUBCOMMAND_TABLE, storageRoot, argv);
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { err, ok, type Result } from "@uncaged/workflow";
|
||||
|
||||
export function parseForkArgv(
|
||||
argv: string[],
|
||||
): Result<{ threadId: string; fromRole: string | null }, string> {
|
||||
import type { ParsedForkArgv } from "./types.js";
|
||||
|
||||
export function parseForkArgv(argv: string[]): Result<ParsedForkArgv, string> {
|
||||
if (argv.length === 0) {
|
||||
return err("fork requires <thread-id>");
|
||||
}
|
||||
|
||||
@@ -1,8 +1,21 @@
|
||||
export { cmdKill, cmdPause, cmdResume } from "./control.js";
|
||||
export {
|
||||
createThreadDispatcher,
|
||||
dispatchFork,
|
||||
dispatchKill,
|
||||
dispatchLive,
|
||||
dispatchPause,
|
||||
dispatchPs,
|
||||
dispatchResume,
|
||||
dispatchRun,
|
||||
dispatchThreadList,
|
||||
dispatchThreadRm,
|
||||
dispatchThreadShow,
|
||||
THREAD_SUBCOMMAND_TABLE,
|
||||
} from "./dispatch.js";
|
||||
export { cmdFork } from "./fork.js";
|
||||
export { parseForkArgv } from "./fork-argv.js";
|
||||
export { cmdThreads } from "./list.js";
|
||||
export type { LiveRoleRow } from "./live.js";
|
||||
export {
|
||||
cmdLive,
|
||||
formatLiveDebugLine,
|
||||
@@ -14,3 +27,4 @@ export { cmdPs } from "./ps.js";
|
||||
export { cmdThreadRemove } from "./rm.js";
|
||||
export { cmdRun } from "./run.js";
|
||||
export { cmdThreadShow } from "./show.js";
|
||||
export type { LiveRoleRow } from "./types.js";
|
||||
|
||||
@@ -17,16 +17,10 @@ import { printCliError, printCliLine } from "../../cli-output.js";
|
||||
import { pathExists } from "../../fs-utils.js";
|
||||
import type { ParsedLiveArgv } from "../../live-argv.js";
|
||||
import { findLatestThreadDataPath, resolveThreadDataPath } from "../../thread-scan.js";
|
||||
import type { LiveRoleRow } from "./types.js";
|
||||
|
||||
export const LIVE_CONTENT_MAX_LINES = 10;
|
||||
|
||||
export type LiveRoleRow = {
|
||||
role: string;
|
||||
content: string;
|
||||
meta: Record<string, unknown>;
|
||||
timestamp: number;
|
||||
};
|
||||
|
||||
export function formatLiveTimeLabel(timestampMs: number): string {
|
||||
const d = new Date(timestampMs);
|
||||
const hh = String(d.getHours()).padStart(2, "0");
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import type { DispatchGroupFn } from "../../cli-command-types.js";
|
||||
|
||||
export type LiveRoleRow = {
|
||||
role: string;
|
||||
content: string;
|
||||
meta: Record<string, unknown>;
|
||||
timestamp: number;
|
||||
};
|
||||
|
||||
export type ParsedForkArgv = {
|
||||
threadId: string;
|
||||
fromRole: string | null;
|
||||
};
|
||||
|
||||
export type ThreadDispatchDeps = {
|
||||
dispatchGroup: DispatchGroupFn;
|
||||
};
|
||||
@@ -1,11 +1,6 @@
|
||||
import { err, ok, type Result } from "@uncaged/workflow";
|
||||
|
||||
export type ParsedAddArgv = {
|
||||
name: string;
|
||||
filePath: string;
|
||||
/** Override path to `.d.ts` when adding a bundle. */
|
||||
typesPath: string | null;
|
||||
};
|
||||
import type { ParsedAddArgv } from "./types.js";
|
||||
|
||||
type ParsedLongFlag = { advance: 2; kind: "types"; value: string };
|
||||
|
||||
|
||||
@@ -17,12 +17,7 @@ import {
|
||||
import { storeWorkflowBundleArtifacts } from "../../bundle-store.js";
|
||||
import { validateCliWorkflowName } from "../../workflow-name.js";
|
||||
|
||||
import type { ParsedAddArgv } from "./add-argv.js";
|
||||
|
||||
export type CmdAddSuccess = {
|
||||
hash: string;
|
||||
warnings: ReadonlyArray<string>;
|
||||
};
|
||||
import type { CmdAddSuccess, ParsedAddArgv } from "./types.js";
|
||||
|
||||
function isEsmBundle(path: string): boolean {
|
||||
return path.endsWith(".esm.js");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CommandEntry, DispatchGroupFn } from "../../cli-command-types.js";
|
||||
import type { CommandEntry } from "../../cli-command-types.js";
|
||||
import { printCliError, printCliLine, printCliWarn } from "../../cli-output.js";
|
||||
import { formatCliUsage, USAGE_SKILL_TOPIC_ROWS } from "../../cli-usage.js";
|
||||
import { getCommandGroupsForUsage } from "../../cli-usage-context.js";
|
||||
@@ -9,6 +9,7 @@ import { cmdList, formatListLines } from "./list.js";
|
||||
import { cmdRemove } from "./rm.js";
|
||||
import { cmdRollback } from "./rollback.js";
|
||||
import { cmdShow, formatShowYaml } from "./show.js";
|
||||
import type { WorkflowDispatchDeps } from "./types.js";
|
||||
|
||||
function usageText(): string {
|
||||
return formatCliUsage(getCommandGroupsForUsage(), USAGE_SKILL_TOPIC_ROWS);
|
||||
@@ -140,11 +141,6 @@ export const WORKFLOW_SUBCOMMAND_TABLE: Record<string, CommandEntry> = {
|
||||
},
|
||||
};
|
||||
|
||||
type WorkflowDispatchDeps = {
|
||||
dispatchGroup: DispatchGroupFn;
|
||||
printDeprecation: (oldCmd: string, newCmd: string) => void;
|
||||
};
|
||||
|
||||
export function createWorkflowDispatcher(deps: WorkflowDispatchDeps) {
|
||||
const { dispatchGroup, printDeprecation } = deps;
|
||||
return async function dispatchWorkflow(storageRoot: string, argv: string[]): Promise<number> {
|
||||
|
||||
@@ -1,9 +1,18 @@
|
||||
export type { CmdAddSuccess } from "./add.js";
|
||||
export { cmdAdd, formatAddSuccess } from "./add.js";
|
||||
export type { ParsedAddArgv } from "./add-argv.js";
|
||||
export { parseAddArgv } from "./add-argv.js";
|
||||
export {
|
||||
createWorkflowDispatcher,
|
||||
dispatchAdd,
|
||||
dispatchHistory,
|
||||
dispatchList,
|
||||
dispatchRemove,
|
||||
dispatchRollback,
|
||||
dispatchShow,
|
||||
WORKFLOW_SUBCOMMAND_TABLE,
|
||||
} from "./dispatch.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";
|
||||
export type { CmdAddSuccess, ParsedAddArgv } from "./types.js";
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import type { DispatchGroupFn } from "../../cli-command-types.js";
|
||||
|
||||
export type ParsedAddArgv = {
|
||||
name: string;
|
||||
filePath: string;
|
||||
/** Override path to `.d.ts` when adding a bundle. */
|
||||
typesPath: string | null;
|
||||
};
|
||||
|
||||
export type CmdAddSuccess = {
|
||||
hash: string;
|
||||
warnings: ReadonlyArray<string>;
|
||||
};
|
||||
|
||||
export type WorkflowDispatchDeps = {
|
||||
dispatchGroup: DispatchGroupFn;
|
||||
printDeprecation: (oldCmd: string, newCmd: string) => void;
|
||||
};
|
||||
@@ -0,0 +1,36 @@
|
||||
# @uncaged/workflow-agent-cursor
|
||||
|
||||
`AgentFn` adapter that runs the `cursor-agent` CLI against a workspace path derived from the thread.
|
||||
|
||||
The agent builds a full prompt (system + task + step history via `@uncaged/workflow-util-agent`), extracts the absolute workspace path with your `extract` + Zod schema, then spawns `cursor-agent` with `--workspace`, model, and non-interactive flags.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @uncaged/workflow-agent-cursor @uncaged/workflow @uncaged/workflow-util-agent zod
|
||||
```
|
||||
|
||||
In this monorepo: `"@uncaged/workflow-agent-cursor": "workspace:*"` plus `workspace:*` for `@uncaged/workflow` and `@uncaged/workflow-util-agent`.
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { createCursorAgent } from "@uncaged/workflow-agent-cursor";
|
||||
|
||||
const agent = createCursorAgent({
|
||||
model: null, // null → "auto"
|
||||
timeout: 0, // ms; 0 = no limit (spawnCli timeout disabled)
|
||||
extract: myExtractFn,
|
||||
});
|
||||
```
|
||||
|
||||
## API overview
|
||||
|
||||
| Export | Description |
|
||||
|--------|-------------|
|
||||
| `createCursorAgent(config)` | Returns `AgentFn` that runs `cursor-agent` with `buildAgentPrompt(ctx)` |
|
||||
| `CursorAgentConfig` | `model`, `timeout`, `extract` (must supply workspace path) |
|
||||
| `validateCursorAgentConfig` | Config validation result |
|
||||
| `buildAgentPrompt` | Re-exported from `@uncaged/workflow-util-agent` |
|
||||
|
||||
Requires `cursor-agent` on `PATH` at runtime.
|
||||
@@ -0,0 +1,35 @@
|
||||
# @uncaged/workflow-agent-hermes
|
||||
|
||||
`AgentFn` adapter that runs the `hermes` CLI in non-interactive `chat` mode (Nerve-style flags: `-q`, `--yolo`, `--quiet`, bounded `--max-turns`).
|
||||
|
||||
The agent composes the same thread-aware prompt as other CLI-backed agents via `buildAgentPrompt` from `@uncaged/workflow-util-agent`, then spawns `hermes` and returns stdout on success.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @uncaged/workflow-agent-hermes @uncaged/workflow @uncaged/workflow-util-agent
|
||||
```
|
||||
|
||||
In this monorepo: use `workspace:*` for all three `@uncaged/*` packages.
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { createHermesAgent } from "@uncaged/workflow-agent-hermes";
|
||||
|
||||
const agent = createHermesAgent({
|
||||
model: "your-model", // or null to omit --model
|
||||
timeout: 600_000, // ms, or null for no timeout
|
||||
});
|
||||
```
|
||||
|
||||
## API overview
|
||||
|
||||
| Export | Description |
|
||||
|--------|-------------|
|
||||
| `createHermesAgent(config)` | Returns `AgentFn` wrapping `hermes chat -q ...` |
|
||||
| `HermesAgentConfig` | `model`, `timeout` |
|
||||
| `validateHermesAgentConfig` | Config validation result |
|
||||
| `buildAgentPrompt` | Re-exported from `@uncaged/workflow-util-agent` |
|
||||
|
||||
Requires `hermes` on `PATH` at runtime.
|
||||
@@ -0,0 +1,34 @@
|
||||
# @uncaged/workflow-agent-llm
|
||||
|
||||
`AgentFn` adapter that calls an OpenAI-compatible `POST /chat/completions` endpoint using `@uncaged/workflow`’s `LlmProvider` (base URL, API key, model).
|
||||
|
||||
Single-turn: system text is the current role’s `systemPrompt`, user text is the thread’s initial prompt (`ctx.start.content`). Errors from HTTP, JSON, or empty choices are thrown as `Error` with a JSON payload string.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @uncaged/workflow-agent-llm @uncaged/workflow
|
||||
```
|
||||
|
||||
In this monorepo: `"@uncaged/workflow-agent-llm": "workspace:*"`, `"@uncaged/workflow": "workspace:*"`.
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { createLlmAdapter } from "@uncaged/workflow-agent-llm";
|
||||
|
||||
const agent = createLlmAdapter({
|
||||
baseUrl: "https://api.openai.com/v1",
|
||||
apiKey: process.env.OPENAI_API_KEY!,
|
||||
model: "gpt-4.1-mini",
|
||||
});
|
||||
```
|
||||
|
||||
## API overview
|
||||
|
||||
| Export | Description |
|
||||
|--------|-------------|
|
||||
| `createLlmAdapter(provider)` | `LlmProvider` → `AgentFn` |
|
||||
| `chatCompletionText({ provider, messages })` | Low-level `Result<string, LlmChatError>` helper |
|
||||
| `LlmMessage` | `{ role: "system" \| "user" \| "assistant"; content: string }` |
|
||||
| `LlmChatError` | Discriminated error kinds for failed completions |
|
||||
@@ -0,0 +1,53 @@
|
||||
# @uncaged/workflow-template-develop
|
||||
|
||||
Reference **develop** workflow template: plan phases, implement in a loop, review, test, then commit.
|
||||
|
||||
Export a `WorkflowDefinition` and `createDevelopRun` so a host can bind agents/LLM and run the same graph the bundled `.esm.js` would use. Use `buildDevelopDescriptor()` when assembling `descriptor` metadata for a bundle.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @uncaged/workflow-template-develop @uncaged/workflow zod
|
||||
```
|
||||
|
||||
In this monorepo: `workspace:*` for `@uncaged/workflow-template-develop` and `@uncaged/workflow`.
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { createDevelopRun, developWorkflowDefinition } from "@uncaged/workflow-template-develop";
|
||||
|
||||
const run = createDevelopRun(binding, extract, llmProvider);
|
||||
// run(...) executes the develop moderator graph with your AgentBinding
|
||||
```
|
||||
|
||||
## Roles
|
||||
|
||||
| Role | Purpose |
|
||||
|------|---------|
|
||||
| **planner** | Break work into ordered phases (hashes) |
|
||||
| **coder** | Implement current phase; repeats until phases complete or limits hit |
|
||||
| **reviewer** | Code review gate (`approved` vs send back to coder) |
|
||||
| **tester** | Verify via tests/build/lint (`passed` vs send back to coder) |
|
||||
| **committer** | Final commit step |
|
||||
|
||||
Also exported: role factories/meta schemas (`plannerRole`, `coderRole`, …), `DevelopMeta`, `developRoles`.
|
||||
|
||||
## Moderator flow
|
||||
|
||||
1. **Start** → `planner`
|
||||
2. After **planner** → `coder`
|
||||
3. After **coder** → if all planned phases are done (or last phase completed) → `reviewer`; else `coder` again, until `maxRounds` then `END`
|
||||
4. After **reviewer** → if approved → `tester`; else `coder` (or `END` if out of rounds)
|
||||
5. After **tester** → if passed → `committer`; else `coder` (or `END` if out of rounds)
|
||||
6. After **committer** → `END`
|
||||
|
||||
## API overview
|
||||
|
||||
| Export | Description |
|
||||
|--------|-------------|
|
||||
| `createDevelopRun` | `createWorkflow(developWorkflowDefinition, …)` factory |
|
||||
| `developWorkflowDefinition` | `description`, `roles`, `developModerator` |
|
||||
| `developModerator` | `Moderator<DevelopMeta>` |
|
||||
| `buildDevelopDescriptor` | `buildDescriptor({ … })` for bundle metadata |
|
||||
| `DEVELOP_WORKFLOW_DESCRIPTION` | Human-readable one-liner |
|
||||
@@ -0,0 +1,48 @@
|
||||
# @uncaged/workflow-template-solve-issue
|
||||
|
||||
Reference **solve-issue** workflow template: prepare a repo, delegate implementation to the **develop** workflow, then submit (e.g. open a PR).
|
||||
|
||||
`createSolveIssueRun` wires the `developer` role to `workflowAsAgent("develop")` by default; `binding.overrides.developer` wins if you pass one (for tests or custom hosts).
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @uncaged/workflow-template-solve-issue @uncaged/workflow zod
|
||||
```
|
||||
|
||||
In this monorepo: `workspace:*` for this package and `@uncaged/workflow`.
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { createSolveIssueRun, solveIssueWorkflowDefinition } from "@uncaged/workflow-template-solve-issue";
|
||||
|
||||
const run = createSolveIssueRun(binding, extract, llmProvider);
|
||||
```
|
||||
|
||||
## Roles
|
||||
|
||||
| Role | Purpose |
|
||||
|------|---------|
|
||||
| **preparer** | Set up context / repo state for the issue |
|
||||
| **developer** | Implementation; default runs the registered `develop` workflow as a sub-agent |
|
||||
| **submitter** | Finalize and submit the outcome (e.g. PR) |
|
||||
|
||||
Also exported: `preparerRole`, `developerRole`, `submitterRole` and their Zod meta schemas, `SolveIssueMeta`, `solveIssueRoles`.
|
||||
|
||||
## Moderator flow
|
||||
|
||||
1. **Start** → `preparer`
|
||||
2. After **preparer** → `developer`
|
||||
3. After **developer** → `submitter`
|
||||
4. After **submitter** → `END`
|
||||
|
||||
## API overview
|
||||
|
||||
| Export | Description |
|
||||
|--------|-------------|
|
||||
| `createSolveIssueRun` | Merges `developer` override with `workflowAsAgent("develop")`, then `createWorkflow` |
|
||||
| `solveIssueWorkflowDefinition` | `description`, `roles`, `solveIssueModerator` |
|
||||
| `solveIssueModerator` | Linear `Moderator<SolveIssueMeta>` |
|
||||
| `buildSolveIssueDescriptor` | Descriptor helper for bundles |
|
||||
| `SOLVE_ISSUE_WORKFLOW_DESCRIPTION` | Human-readable one-liner |
|
||||
@@ -0,0 +1,34 @@
|
||||
# @uncaged/workflow-util-agent
|
||||
|
||||
Shared helpers for CLI-backed workflow agents: assemble prompts from thread context and spawn subprocesses with timeouts.
|
||||
|
||||
Used by `@uncaged/workflow-agent-cursor` and `@uncaged/workflow-agent-hermes`. Depends on `@uncaged/workflow` for CAS reads (`getContentMerklePayload`) and `Result` typing.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @uncaged/workflow-util-agent @uncaged/workflow
|
||||
```
|
||||
|
||||
In this monorepo: `workspace:*` for both packages.
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { buildAgentPrompt, spawnCli } from "@uncaged/workflow-util-agent";
|
||||
|
||||
const prompt = await buildAgentPrompt(agentContext);
|
||||
const result = await spawnCli("my-cli", ["--json"], { cwd: "/tmp", timeoutMs: 60_000 });
|
||||
if (!result.ok) { /* handle SpawnCliError */ }
|
||||
const stdout = result.value;
|
||||
```
|
||||
|
||||
## API overview
|
||||
|
||||
| Export | Description |
|
||||
|--------|-------------|
|
||||
| `buildAgentPrompt(ctx)` | System prompt + task + prior step summaries + latest body from CAS; appends `uncaged-workflow thread <id>` tool hint |
|
||||
| `spawnCli(cmd, args, { cwd, timeoutMs })` | `Promise<Result<string, SpawnCliError>>`; captures stdout, non-zero exit and spawn failures as `err` |
|
||||
| `SpawnCliConfig` | `cwd: string \| null`, `timeoutMs: number \| null` |
|
||||
| `SpawnCliError` | `non_zero_exit` \| `timeout` \| `spawn_failed` |
|
||||
| `SpawnCliResult` | Alias for `Result<string, SpawnCliError>` |
|
||||
@@ -0,0 +1,36 @@
|
||||
# @uncaged/workflow
|
||||
|
||||
Core workflow engine: registry, CAS, thread execution, bundle validation, and role/workflow types.
|
||||
|
||||
This package implements the three-phase engine loop that runs single-file ESM workflow bundles (each exports `run` and `descriptor`). It persists threads under `~/.uncaged/workflow/` by default and hashes bundles with XXH64 (Crockford Base32). See the repo root [README](../../README.md) for workflow, bundle, thread, role, and registry concepts.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bun add @uncaged/workflow zod
|
||||
```
|
||||
|
||||
In this monorepo, depend with `"@uncaged/workflow": "workspace:*"`. `zod` is a peer dependency (used by bundle/shape validation at the integration boundary).
|
||||
|
||||
## Usage
|
||||
|
||||
```typescript
|
||||
import { createWorkflow, readWorkflowRegistry, executeThread } from "@uncaged/workflow";
|
||||
// Wire a WorkflowDefinition + AgentBinding + extract + optional LlmProvider into createWorkflow,
|
||||
// then run the returned WorkflowFn inside your host (or use executeThread for disk-backed runs).
|
||||
```
|
||||
|
||||
## API overview
|
||||
|
||||
| Area | Exports (representative) |
|
||||
|------|--------------------------|
|
||||
| **Types** | `WorkflowDefinition`, `WorkflowFn`, `AgentFn`, `AgentBinding`, `Moderator`, `RoleDefinition`, `ThreadContext`, `LlmProvider`, `Result` shape via `ok` / `err`, `START` / `END` |
|
||||
| **Bundle** | `buildDescriptor`, `extractBundleExports`, `validateWorkflowBundle`, `validateWorkflowDescriptor`, `WorkflowDescriptor`, `WorkflowRoleDescriptor` |
|
||||
| **Registry** | `readWorkflowRegistry`, `writeWorkflowRegistry`, `registerWorkflowVersion`, `workflowRegistryPath`, YAML helpers |
|
||||
| **CAS** | `createCasStore`, `createThreadCas`, Merkle helpers (`putStepMerkleNode`, `getContentMerklePayload`, …), `hashWorkflowBundleBytes` |
|
||||
| **Engine** | `createWorkflow`, `executeThread`, `parseThreadDataJsonl`, fork helpers, `garbageCollectCas` |
|
||||
| **Extract / LLM tools** | `llmExtract`, `reactExtract`, `createExtract`, `getExtractProvider` |
|
||||
| **Agent bridge** | `workflowAsAgent` — expose a registered workflow as an agent-backed role |
|
||||
| **Utilities** | `createLogger`, ULID / Crockford Base32 codecs, `getDefaultWorkflowStorageRoot`, paths |
|
||||
|
||||
Full surface is re-exported from `src/index.ts`.
|
||||
Reference in New Issue
Block a user