Skip to content
Development
Skill

/workflow-authoring

Reference for writing a Workflow tool script (script API and gotchas, pipeline-vs-barrier rules, quality patterns, worked examples). Load before authoring a script for a workflow the user already opted into; it does not itself authorize running one.

From plugin
letta-code
3.4k24 skills8 hooks
Install
$ npx -y skills add letta-ai/letta-code --skill workflow-authoring --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/workflow-authoring

Context preview

The summary Claude sees to decide when to auto-load this skill.

Reference for writing a Workflow tool script (script API and gotchas, pipeline-vs-barrier rules, quality patterns, worked examples). Load before authoring a script for a workflow the user already opted into; it does not itself authorize running one.

SKILL.md

workflow-authoring.SKILL.md
name: workflow-authoring
description: Reference for writing a Workflow tool script (script API and gotchas, pipeline-vs-barrier rules, quality patterns, worked examples). Load before authoring a script for a workflow the user already opted into; it does not itself authorize running one.

Workflow authoring reference

A workflow structures work across many subagents — to be comprehensive (decompose and cover in parallel), to be confident (independent perspectives and adversarial checks before committing), or to take on scale one context can't hold (migrations, audits, broad sweeps). The script is where you encode that structure: what fans out, what verifies, what synthesizes.

When you do call the Workflow tool, the right move is often **hybrid**: scout inline first (list the files, scope the diff, find the modules) to discover the work-list, then call Workflow to pipeline over it, passing the list via `args`. You don't need to know the shape before the *task* — only before the *orchestration step*.

Common single-phase workflows you can chain across turns:

  • **Understand** — parallel readers over relevant subsystems → structured map
  • **Design** — judge panel of N independent approaches → scored synthesis
  • **Review** — dimensions → find → adversarially verify
  • **Research** — multi-modal sweep → deep-read → synthesize
  • **Migrate** — discover sites → transform each → verify

For larger work, run several in sequence — read each result before deciding the next phase. You stay in the loop; each workflow is one well-scoped fan-out.

Every script must begin with `export const meta = {...}`:

export const meta = { name: 'find-flaky-tests', // kebab-case, required description: 'Find flaky tests and propose fixes', // required phases: [ // one entry per phase() call { title: 'Scan', detail: 'grep test logs for retries' }, { title: 'Fix', detail: 'one agent per flaky test' }, ], } // script body starts here — use agent()/parallel()/pipeline()/phase()/log()

The `meta` object must be a PURE LITERAL — no variables, function calls, spreads, or template interpolation.

What a subagent is

Every `agent()` call is one agent-free ephemeral conversation linked to the invoking parent agent (`is_subagent: true`, named after the call's `label`). It starts with nothing but its prompt: no memory, no conversation context, no skills. Put ALL context a stage needs in the prompt — file paths, the rule it should apply, what shape to return.

Subagents are told their final text IS the return value (not a human-facing message), so they return raw data. Tools default to read-only (`Read`, `Grep`, `Glob`); widen with `allowedTools` for stages that must write. For stages whose input is entirely in the prompt (synthesis, judging, scoring) pass `allowedTools: []` — a model that can still read files tends to wander, and a subagent that re-issues an identical tool call three times is stopped and resolves to `null`.

Their model defaults to the invoking conversation's model. `opts.model` (or the tool's `model` input) accepts any handle or alias listed by `letta model list`; an unknown value resolves that call to `null`. Use a cheaper model for mechanical stages only when you know a valid handle.

Workflow subagents require the API backend.

Script body hooks

  • `agent(prompt, opts?)` → Promise. Spawn one subagent. Resolves to its final

text, or with `json: true` to the parsed JSON value (say in the prompt what shape to return — nothing validates it; a reply that is not JSON resolves to `null`). Resolves to `null` on any failure — filter with `.filter(Boolean)`. Options: `label` (display name), `phase` (progress group — use this inside pipeline()/parallel() stages to avoid races on the global phase() state), `json`, `model`, `effort` (`'low'` for mechanical stages, higher for the hardest verify/judge stages), `allowedTools`, `systemPrompt` (extra system prompt for this subagent), `timeoutMs` (default 10 minutes), `maxToolCalls` (positive safe integer; default 1000 unique tool calls for this subagent).

  • `pipeline(items, stage1, stage2, ...)` → run each item through all stages

independently, NO barrier between stages. Item A can be in stage 3 while item B is still in stage 1. This is the DEFAULT for multi-stage work. Wall-clock = slowest single-item chain, not sum-of-slowest-per-stage. Every stage callback receives `(prevResult, originalItem, index)` — use originalItem/index in later stages to label work without threading context through stage 1's return value. A stage that throws drops that item to `null` and skips its remaining stages.

  • `parallel(thunks)` → run zero-arg functions concurrently. This is a

BARRIER: it awaits all thunks before returning. A thunk that throws resolves to `null` in the result array — the call itself never rejects, so `.filter(Boolean)` before using the results. Use ONLY when you genuinely need all results together.

  • `phase(title)` — start a new phase; subsequent agent() calls are grouped

under this title in progress output.

  • `log(message)` — emit a progress message to the user.
  • `args` — the value passed as the tool's `args` input, verbatim. Pass

arrays/objects as actual JSON values, NOT as a JSON-encoded string.

Scripts are plain JavaScript, NOT TypeScript — type annotations, interfaces, and generics fail to parse. The script body runs in an async context — use `await` directly and `return` the final result. Standard JS built-ins (JSON, Math, Array, etc.) are available; the hooks are the only globals provided. The script runs inside the CLI process with the CLI's own privileges (the `vm` context is a scope, not a security boundary), and the user approves it by reading it. Keep the script to orchestration: decide what runs and combine results. All reading, searching, and writing belongs in subagents, where the tool allowl

Read more
Ships withletta-code

Letta Code is a stateful agent harness for creating agents that are more like people than tools. Letta Code agents have memory, identity, and a sense of experience over time.

Get the whole plugin
Stats
3,410
Stars
414
Forks
Active
Maintenance
TypeScript
Language
Apache-2.0
License
20m ago
Last commit
11mo ago
Created

Repo: letta-ai/letta-code

Other skills on letta-code.