diff --git a/packages/cli-workflow/src/cli.ts b/packages/cli-workflow/src/cli.ts index 1a6a5c6..378c093 100755 --- a/packages/cli-workflow/src/cli.ts +++ b/packages/cli-workflow/src/cli.ts @@ -14,6 +14,7 @@ import { cmdCasWalk, } from "./commands/cas.js"; import { cmdSetup, cmdSetupInteractive } from "./commands/setup.js"; +import { cmdSkillCli } from "./commands/skill.js"; import { cmdThreadFork, cmdThreadKill, @@ -220,6 +221,15 @@ thread }); }); +const skill = program.command("skill").description("Built-in skill references for agents"); + +skill + .command("cli") + .description("Print a markdown reference of all uwf commands") + .action(() => { + console.log(cmdSkillCli()); + }); + program .command("setup") .description("Configure provider, model, and agent") diff --git a/packages/cli-workflow/src/commands/skill.ts b/packages/cli-workflow/src/commands/skill.ts new file mode 100644 index 0000000..e86294a --- /dev/null +++ b/packages/cli-workflow/src/commands/skill.ts @@ -0,0 +1,70 @@ +export function cmdSkillCli(): string { + return `# uwf CLI Reference + +## Setup + +\`\`\` +uwf setup # interactive setup wizard +uwf setup --provider --base-url \\ + --api-key --model # non-interactive setup + [--agent ] # optional: default agent alias +\`\`\` + +## Workflow Commands + +\`\`\` +uwf workflow put # register a workflow from YAML file +uwf workflow show # show workflow by name or CAS hash +uwf workflow list # list all registered workflows +\`\`\` + +## Thread Commands + +\`\`\` +uwf thread start -p # create a thread (no execution) +uwf thread step # execute one moderator→agent→extract cycle + [--agent ] # override agent command +uwf thread show # show thread head pointer +uwf thread list # list active threads + [--all] # include archived threads +uwf thread kill # terminate and archive a thread +uwf thread steps # list all steps in a thread +uwf thread read # render thread context as markdown + [--quota ] # max output characters (default 32000) + [--before ] # load steps before this hash (exclusive) + [--start] # include start step in output +uwf thread fork # fork a thread from a specific step +uwf thread step-details # dump full detail node of a step as YAML +\`\`\` + +## CAS Commands + +\`\`\` +uwf cas get # read a CAS node (type + payload) + [--timestamp] # include timestamp in output +uwf cas put # store a node, print its hash + # : JSON file path or inline JSON string +uwf cas has # check if a hash exists +uwf cas refs # list direct CAS references from a node +uwf cas walk # recursive traversal from a node +uwf cas reindex # rebuild type index from all CAS nodes +uwf cas schema list # list all registered schemas +uwf cas schema get # show a schema by its type hash +\`\`\` + +## Global Options + +\`\`\` +uwf --format # output format: json (default) or yaml +uwf -V, --version # print version +\`\`\` + +## Key Concepts + +- **Workflow**: YAML definition with roles, conditions, and a routing graph; stored as a CAS node identified by its XXH64 hash. +- **Thread**: A single workflow execution (ULID). State is an immutable CAS chain; active threads are indexed in \`threads.yaml\`. +- **Step**: One moderator→agent→extract cycle. Run \`uwf thread step\` repeatedly until \`$END\`. +- **CAS**: Content-Addressed Storage — all nodes are immutable and identified by hash. +- **Role**: Named actor with a system prompt and JSON Schema output; the moderator routes between roles. +`; +} diff --git a/packages/workflow-agent-kit/__tests__/build-role-prompt.test.ts b/packages/workflow-agent-kit/__tests__/build-role-prompt.test.ts index 4965cc4..1a0fd0c 100644 --- a/packages/workflow-agent-kit/__tests__/build-role-prompt.test.ts +++ b/packages/workflow-agent-kit/__tests__/build-role-prompt.test.ts @@ -18,13 +18,16 @@ describe("buildRolePrompt", () => { expect(result).toContain("## Capabilities"); expect(result).toContain("- cursor-agent"); expect(result).toContain("- file-edit"); + expect(result).toContain("## Prepare"); + expect(result).toContain("uwf skill cli"); + expect(result).toContain("cursor-agent, file-edit"); expect(result).toContain("## Procedure"); expect(result).toContain("Implement the feature."); expect(result).toContain("## Output"); expect(result).toContain("Summarize changes."); }); - test("empty fields are omitted", () => { + test("empty fields are omitted but Prepare is always present", () => { const role: RoleDefinition = { description: "A reviewer", goal: "You are a code reviewer.", @@ -35,12 +38,14 @@ describe("buildRolePrompt", () => { }; const result = buildRolePrompt(role); expect(result).toContain("## Goal"); + expect(result).toContain("## Prepare"); + expect(result).toContain("uwf skill cli"); expect(result).toContain("## Procedure"); expect(result).not.toContain("## Capabilities"); expect(result).not.toContain("## Output"); }); - test("all empty returns empty string", () => { + test("all empty still includes Prepare section", () => { const role: RoleDefinition = { description: "Minimal", goal: "", @@ -50,7 +55,12 @@ describe("buildRolePrompt", () => { meta: "placeholder00000" as string, }; const result = buildRolePrompt(role); - expect(result).toBe(""); + expect(result).toContain("## Prepare"); + expect(result).toContain("uwf skill cli"); + expect(result).not.toContain("## Goal"); + expect(result).not.toContain("## Capabilities"); + expect(result).not.toContain("## Procedure"); + expect(result).not.toContain("## Output"); }); test("capabilities rendered as bullet list", () => { diff --git a/packages/workflow-agent-kit/src/build-role-prompt.ts b/packages/workflow-agent-kit/src/build-role-prompt.ts index 199d4a3..5cc7de7 100644 --- a/packages/workflow-agent-kit/src/build-role-prompt.ts +++ b/packages/workflow-agent-kit/src/build-role-prompt.ts @@ -3,8 +3,12 @@ import type { RoleDefinition } from "@uncaged/workflow-protocol"; /** * Build the role prompt from a RoleDefinition. * - * Assembles structured sections: Goal, Capabilities, Procedure, Output. + * Assembles structured sections: Goal, Capabilities, Prepare, Procedure, Output. * Empty strings and empty arrays are omitted from the output. + * + * The Prepare section always instructs the agent to run `uwf skill cli` to load + * workflow knowledge, plus renders the capabilities array as keyword hints for + * implicit skill loading. */ export function buildRolePrompt(role: RoleDefinition): string { const sections: string[] = []; @@ -18,6 +22,22 @@ export function buildRolePrompt(role: RoleDefinition): string { sections.push(`## Capabilities\n\n${list}`); } + const prepareLines: string[] = [ + "Run the following command to load workflow CLI knowledge before starting work:", + "", + "```", + "uwf skill cli", + "```", + ]; + if (role.capabilities.length > 0) { + const keywords = role.capabilities.join(", "); + prepareLines.push( + "", + `You have the following capabilities: ${keywords}. Load relevant skills matching these keywords before starting work.`, + ); + } + sections.push(`## Prepare\n\n${prepareLines.join("\n")}`); + if (role.procedure !== "") { sections.push(`## Procedure\n\n${role.procedure}`); }