Hermes Agent Skill Authoring
NousResearch/hermes-agent
Author in-repo SKILL.md files: frontmatter and structure. An agent skill from NousResearch/hermes-agent.
Reference for writing a Workflow tool script (script API and gotchas, agent() options, pipeline() vs parallel(), verification and convergence patterns, resume, worked example).
$ npx skills add QwenLM/qwen-code --skill workflow-authoring -a claude-codeProject install by default; add -g for ~/.claude/skills/.
$ gh skill install QwenLM/qwen-code workflow-authoring --agent claude-codeProject scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).
$ git clone --depth 1 https://github.com/QwenLM/qwen-code.git skills-src && mkdir -p .claude/skills && cp -r skills-src/packages/core/src/skills/bundled/workflow-authoring .claude/skills/workflow-authoring && rm -rf skills-srcUse ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.
Claude Code skills documentation · loads skills from .claude/skills/
Install the "workflow-authoring" agent skill from https://github.com/QwenLM/qwen-code/tree/main/packages/core/src/skills/bundled/workflow-authoring into .claude/skills/workflow-authoring/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "workflow-authoring", then confirm the skill loads.Claude Code copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$skill-installer install https://github.com/QwenLM/qwen-code/tree/main/packages/core/src/skills/bundled/workflow-authoringType this inside Codex. $skill-installer <name> installs a curated skill from openai/skills. The installer writes to $CODEX_HOME/skills (default ~/.codex/skills). Restart Codex if the skill does not show up.
$ npx skills add QwenLM/qwen-code --skill workflow-authoring -a codexProject install goes to .agents/skills/; add -g for ~/.codex/skills/.
$ gh skill install QwenLM/qwen-code workflow-authoring --agent codexProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/QwenLM/qwen-code.git skills-src && mkdir -p .agents/skills && cp -r skills-src/packages/core/src/skills/bundled/workflow-authoring .agents/skills/workflow-authoring && rm -rf skills-srcUse ~/.agents/skills/ instead of .agents/skills for a personal install.
Codex skills documentation · loads skills from .agents/skills/
Install the "workflow-authoring" agent skill from https://github.com/QwenLM/qwen-code/tree/main/packages/core/src/skills/bundled/workflow-authoring into .agents/skills/workflow-authoring/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "workflow-authoring", then confirm the skill loads.Codex copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add QwenLM/qwen-code --skill workflow-authoring -a cursorProject install goes to .agents/skills/; add -g for ~/.cursor/skills/.
$ gh skill install QwenLM/qwen-code workflow-authoring --agent cursorProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/QwenLM/qwen-code.git skills-src && mkdir -p .cursor/skills && cp -r skills-src/packages/core/src/skills/bundled/workflow-authoring .cursor/skills/workflow-authoring && rm -rf skills-srcUse ~/.cursor/skills/ instead of .cursor/skills for a personal install.
Cursor skills documentation · loads skills from .cursor/skills/, .agents/skills/, .claude/skills/, .codex/skills/
Install the "workflow-authoring" agent skill from https://github.com/QwenLM/qwen-code/tree/main/packages/core/src/skills/bundled/workflow-authoring into .cursor/skills/workflow-authoring/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "workflow-authoring", then confirm the skill loads.Cursor copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gemini skills install https://github.com/QwenLM/qwen-code.git --path packages/core/src/skills/bundled/workflow-authoring--scope user (default) or --scope workspace; --path is the subfolder of the repo that holds the skill; --consent skips the security confirmation prompt.
$ npx skills add QwenLM/qwen-code --skill workflow-authoring -a gemini-cliProject install goes to .agents/skills/; add -g for ~/.gemini/skills/.
$ gh skill install QwenLM/qwen-code workflow-authoring --agent gemini-cliProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/QwenLM/qwen-code.git skills-src && mkdir -p .gemini/skills && cp -r skills-src/packages/core/src/skills/bundled/workflow-authoring .gemini/skills/workflow-authoring && rm -rf skills-srcUse ~/.gemini/skills/ instead of .gemini/skills for a personal install, then run /skills reload.
Gemini CLI skills documentation · loads skills from .gemini/skills/, .agents/skills/
Install the "workflow-authoring" agent skill from https://github.com/QwenLM/qwen-code/tree/main/packages/core/src/skills/bundled/workflow-authoring into .gemini/skills/workflow-authoring/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "workflow-authoring", then confirm the skill loads.Gemini CLI copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ gh skill install QwenLM/qwen-code workflow-authoringInstalls for Copilot at project scope by default; add --scope user for a personal install. Preview a skill first with gh skill preview. Needs GitHub CLI 2.90.0 or later (public preview).
$ npx skills add QwenLM/qwen-code --skill workflow-authoring -a github-copilotProject install goes to .agents/skills/; add -g for ~/.copilot/skills/.
$ git clone --depth 1 https://github.com/QwenLM/qwen-code.git skills-src && mkdir -p .github/skills && cp -r skills-src/packages/core/src/skills/bundled/workflow-authoring .github/skills/workflow-authoring && rm -rf skills-srcUse ~/.copilot/skills/ instead of .github/skills for a personal install. Commit .github/skills so cloud agent and code review can use it.
GitHub Copilot skills documentation · loads skills from .github/skills/, .claude/skills/, .agents/skills/
Install the "workflow-authoring" agent skill from https://github.com/QwenLM/qwen-code/tree/main/packages/core/src/skills/bundled/workflow-authoring into .github/skills/workflow-authoring/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "workflow-authoring", then confirm the skill loads.GitHub Copilot copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
$ npx skills add QwenLM/qwen-code --skill workflow-authoring -a opencodeOpenCode documents no install command of its own. Project install goes to .agents/skills/; add -g for ~/.config/opencode/skills/.
$ gh skill install QwenLM/qwen-code workflow-authoring --agent opencodeProject scope by default (.agents/skills/); add --scope user for a personal install.
$ git clone --depth 1 https://github.com/QwenLM/qwen-code.git skills-src && mkdir -p .opencode/skills && cp -r skills-src/packages/core/src/skills/bundled/workflow-authoring .opencode/skills/workflow-authoring && rm -rf skills-srcUse ~/.config/opencode/skills/ instead of .opencode/skills for a personal install.
OpenCode skills documentation · loads skills from .opencode/skills/, .claude/skills/, .agents/skills/
Install the "workflow-authoring" agent skill from https://github.com/QwenLM/qwen-code/tree/main/packages/core/src/skills/bundled/workflow-authoring into .opencode/skills/workflow-authoring/ in this project. Copy the whole folder (SKILL.md and every file beside it), keep the folder name "workflow-authoring", then confirm the skill loads.OpenCode copies the folder itself, the same result as the manual copy. Check what it changed before you commit it.
workflow-authoringReference for writing a Workflow tool script (script API and gotchas, agent() options, pipeline() vs parallel(), verification and convergence patterns, resume, worked example).
Workflow Authoring is an agent skill from QwenLM/qwen-code. Reference for writing a Workflow tool script (script API and gotchas, agent() options, pipeline() vs parallel(), verification and convergence patterns, resume, worked example). Load before authoring a script for a workflow the user already opted into; it does not itself authorize running one.
Its SKILL.md is about 6.9k tokens, which your agent loads only when the skill is triggered. The skill folder holds 1 other file (for example `SKILL.test.ts`).
The repository describes itself as: An open-source AI coding agent that lives in your terminal. The licence is Apache-2.0.
Read from SKILL.md and the folder at commit 55ee50d. It shows what the files ask for, not the result of running them.
Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.
From allowed-tools in the SKILL.md frontmatter.
Ships script files (TypeScript), which the agent can run.
Shell commands in SKILL.md call:
gitFrom the folder's file list and the shell code blocks in SKILL.md.
No URLs in SKILL.md. Its commands use git, which can reach the network depending on how they are called.
From URLs in SKILL.md, links to its own repository left out.
Names no API keys, tokens, secrets or passwords.
From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.
Workflow Authoring loads about 6.9k tokens when it runs. Until then it costs about 78 tokens; SKILL.md has 3,626 words of instructions outside code blocks.
Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.
The automated check found no risky patterns in SKILL.md.
Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.
The full file from QwenLM/qwen-code at commit 55ee50d, republished under its Apache-2.0 licence (© QwenLM). 3,626 words, ~6,856 tokens.
.claude/skills/workflow-authoring/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.Everything below is about writing the script. Whether a workflow may run at all is decided by the Workflow tool's own opt-in rule — this reference does not authorize a run.
Reach for one to be comprehensive (decompose the work and cover every part in parallel), to be confident (independent perspectives and adversarial checks before an answer is committed to), or to take on scale a single context cannot hold — migrations, audits, broad sweeps. The script is where that structure is encoded: what fans out, what verifies, what synthesizes. Parallelism on its own is not a reason; work that is already one short sequence of edits belongs in the main loop.
The strongest pattern is hybrid: discover the work list in the main loop (list the files, scope the diff, read the failing test), then hand that list to a workflow. You do not need to know the shape of the work before the task — only before the orchestration step. When the work has distinct phases, run several small workflows across turns and read each result before choosing the next, rather than authoring one large script that runs unattended.
Common single-phase shapes: understand (parallel readers over subsystems,
merged into one map), design (independent approaches, judged, then
synthesized), review (dimensions, find, verify each finding), research (broad
sweep, deep read, synthesis), migrate (discover sites, transform each under
isolation: 'worktree', verify).
The source is wrapped as an async IIFE, so top-level await and a top-level
return are both legal — and a trailing expression is not a return value.
End every successful path with an explicit return.
It is plain JavaScript, not TypeScript, and it cannot import anything. A
script with a dynamic import() anywhere in it — even in a branch that never
runs — is refused before it starts, so none of its agents runs first; do file,
network, and package work inside an agent instead.
The script may start with a literal export const meta = {...} declaration
with name, description, and optionally whenToUse and
phases: [{ title, detail? }]. It must be a pure literal — no variables,
calls, or interpolation — and it is stripped before execution, so nothing in
the script body can read it. Fields outside that list are dropped. The
approval dialog prints the name, the description, and each phase title with
its detail as a one-line explanation beside it: give every phase a detail,
because for a run that may dispatch hundreds of agents it is what the user
reads before approving. In a workflow an extension ships, whenToUse also lists
the workflow for the model to start when a request matches it; leave it out and
the workflow runs only when someone asks for it by name.
Injected globals, and nothing else:
phase(title) — open a phase. Everything dispatched afterwards is attributed
to it in the live phase tree, until the next phase opens.log(msg) — one line into the run log the user watches.agent(prompt, opts?) — dispatch one subagent. See agent() options.parallel(thunks) — run thunks through the shared concurrency window,
resolving to a position-aligned array. parallel() itself rejects on
invalid arguments.pipeline(items, ...stages) — run each item through the stages
independently. See Default to pipeline().workflow(nameOrRef, args?, { stepId }?) — run a saved workflow inline. See Saved
workflows and workflow().args — the structured value the caller passed, or undefined.budget — { total, spent(), remaining() }. See Scaling to the token
budget.Pass THUNKS to parallel(), not eager calls: parallel([() => agent(...)]),
not parallel([agent(...)]). The eager form is refused outright: a
non-function element rejects the whole batch, and by then every agent() in it
has already been admitted, counted against the caps, and spent — with its
result discarded.
Each list a single call takes — the thunks of parallel(), and the items and
the stages of pipeline() — holds at most 4096 entries. A longer list rejects
the whole call before any of its thunks or stages runs; it is never truncated.
Like any invalid argument, that rejection can be caught, and inside an outer
parallel()/pipeline() it becomes that slot's null. The limit is per call,
not per run: split a larger input into batches of thunks and await each in
turn. Batching does not lift the agent cap or the token budget.
const thunks = files.map((file) => () => agent(`Summarize ${file}`));
let summaries = [];
for (let i = 0; i < thunks.length; i += 4096) {
summaries = summaries.concat(await parallel(thunks.slice(i, i + 4096)));
}A script must be deterministic so a resume replays the same call sequence.
Math.random() throws, and so does all of Date — Date(), new Date(),
Date.now(), Date.parse() and Date.UTC() alike. Pass timestamps in via
args, or stamp the result after the workflow returns. A script that calls any
of them is refused before it starts, so none of its agents runs first.
Scripts run in a node:vm sandbox with no filesystem, shell, network, or
environment access. All I/O happens through the prompts you give the agents, so
say explicitly what each one should read and whether it may edit files.
agent(prompt, { stepId?, label?, phase?, schema?, model?, effort?, agentType?, isolation?, workingDir?, stallMs?, disallowedTools?, tools? })
stepId (string, ≤128 chars) — optional caller node ID; does not affect caching. Also accepted in workflow() options.label (string) — display name in run views and failures.
Make it unique per dispatch to distinguish failures.phase (string) — opens a named phase at this call, exactly as phase(title)
would: this dispatch and every dispatch issued after it are attributed to that
phase. It is not scoped to the one call, so in a fan-out open phases with
phase() between groups rather than per dispatch.schema (JSON Schema object) — the subagent must deliver its result by
calling structured_output with arguments matching the schema; agent()
resolves to the validated object. A schema that does not compile, or that
requires a property its own object forbids, makes agent() resolve to null
without starting the agent. Each failed submission hands its error back to
the agent, and the third failed submission stops it. With no valid result
agent() resolves to null and the failure states how many submissions failed
and the last error; check for null.agentType (string) — resolves against the declarative-agents registry
(.qwen/agents/<name>.md, project then user then built-in). Unresolved names
make the admitted agent() resolve to null and record "agent({agentType}):
agent type 'X' not found"; check for null.model (string) — per-call model override; routes provider correctly via the
subagent runtime view.effort ('low' | 'medium' | 'high' | 'xhigh' | 'max') — the
reasoning effort for this one agent. It is limited to the tiers /effort
offers for the agent's model or, for a model whose settings declare none, to
the tiers its provider's built-in table accepts: a tier the model does not
offer becomes the next stronger tier it does offer, or its strongest tier
when none is stronger. A model that offers no tiers, or thinking turned off
for the session or the model, leaves the agent with the effort it would have
had without the option. The session's own effort is never changed, and an
explicit tier replaces any thinking budget the agent would otherwise inherit;
a thinking setting fixed in the provider settings (such as extra_body or
samplingParams) still takes precedence over the tier, as it does over
/effort. Omitting effort inherits the session's effort only while the
agent stays on the session's provider: a model override that switches
provider starts from that model's own reasoning settings, so pass effort
there. Aliases such as 'med' and 'x-high' are accepted; any other value
rejects the call. Use 'low' for cheap mechanical stages and the higher tiers
only for the hardest verify or judge stages. A different effort is a
different resume cache key.isolation — 'worktree' provisions a fresh git worktree under
<projectRoot>/.qwen/worktrees/agent-<7hex>; the worktree is auto-removed if
no changes, otherwise the path and branch are returned alongside the result.
'remote' makes the admitted agent() resolve to null and records
"agent({isolation:'remote'}) is not available in this build". A 'worktree'
dispatch is also refused — it resolves to null with the reason recorded —
when the session is already inside a worktree (nested isolation worktrees are
not supported; to run agents in that worktree, pass it as workingDir), when
git is not available or the directory is not a git repository, when the
parent working tree has uncommitted changes (the subagent would see a stale
HEAD), or when the worktree cannot be created. The nested case refuses every
dispatch, so rule it out before a large isolation: 'worktree' fan-out.workingDir (string) — pin the subagent to an EXISTING git worktree of this
repository that the caller owns; nothing is created and nothing is removed.
Use it when the directory the agent must work in already exists and its
uncommitted state is the point (a review worktree, a checkout a previous step
provisioned) — exactly the case isolation cannot serve. Mutually exclusive
with isolation. The path must be a linked worktree of this repository
registered via git worktree add (it may live anywhere on disk) — the main
checkout is not eligible.stallMs (number, ms) — a no-progress stall watchdog, not a wall-clock cap.
The dispatch is aborted and retried (up to 3 attempts total) after this many
milliseconds with no observable subagent progress — including before the
first response arrives; the timer is suspended while a tool is in flight, so
a legitimately slow tool is not a stall. Default 180000 (override via
QWEN_CODE_WORKFLOW_STALL_SECONDS, whole seconds); 0 disables the
watchdog. Wall time per attempt is bounded separately.disallowedTools (string[]) — tools this agent may not call, on top of the
floor below; it can only narrow the agent's tools, never re-enable one. Name a
tool by its tool name (run_shell_command, write_file, edit) or its
display name (Shell, WriteFile, Edit), or deny MCP tools with
mcp__<server> (every tool of that server), mcp__<server>__*, or
mcp__<server>__<tool>. An entry that names no built-in or registered tool
and is not an mcp__ pattern, such as 'Bash', resolves the call to null
with the reason recorded rather than silently denying nothing. Entries must
be non-empty strings without surrounding whitespace, or the call is rejected.
A schema agent whose denies, from this call or from its agentType,
include structured_output resolves to null with the reason recorded,
because it would have no way to return its result. The resume cache key
depends on which tools are denied, not on their order or duplicates, and not
on whether a built-in tool is named by its tool name or its display name.tools (string[]) — the only tools this agent may be given; it narrows and
never brings back a tool the floor below or a deny takes away. Name tools
exactly: a built-in by tool or display name, an MCP tool by the name the model
sees (mcp__<server>__<tool>). Patterns ('*', mcp__<server>,
mcp__<server>__*), exec and an empty list reject the call; in code mode
the agent keeps exec, which can call only the listed tools. An entry that
names no tool, such as 'Bash', resolves the call to null with the reason
recorded, and so does a list that shares no tool with the agentType's own
allowlist or whose every tool is denied. A schema agent is also given
structured_output. A correctly named tool this session does not have, or one
no subagent may use (such as todo_write), is simply not given, as with an
agentType allowlist. Built-in spellings, order and duplicates do not change
the resume key; other spellings do.Workflow subagents can never use AskUserQuestion, SendMessage, Monitor,
EnterPlanMode, ExitPlanMode, or the Agent tool, whatever their agentType or
their tools. A subagent therefore cannot fan out further and cannot ask anyone
anything: the script owns all fan-out, and every ambiguity has to be resolved in
the prompt it is given. Never ask a subagent to spawn its own verifiers —
dispatch them from the script.
A subagent's final text, or the validated object under schema.
agent() resolves to null when that admitted agent fails on its own —
including turn/time caps, model or setup errors, missing structured output, and
exhausted stall retries — and it does so for a bare await agent() exactly as
it does inside parallel()/pipeline(), so check for null wherever you read
a result. Call-shape validation failures — such as an empty prompt, an
unsupported option combination, or an invalid option value — reject a bare
call; inside parallel()/pipeline(), the surrounding ordinary thunk or stage
rejection becomes a position-aligned null. Run-level rejections no later call
could survive — the token budget, the 1000-agent cap, and cancellation — throw
and end a parallel()/pipeline() batch. An admitted agent that fails and
settles to null still counts as dispatched and is named, with its error, in
the run's failures list; a null returned by an ordinary thunk or stage is not
an agent dispatch.
A pipeline() stage that returns null — or throws — drops that item: its
remaining stages are skipped and its slot in the result is null. So a null
check belongs in the stage that dispatched the agent, never in a later stage,
which will not run for that item.
A result must be JSON-serializable to survive the sandbox boundary and the
resume journal. A thunk that resolves to something that is not becomes null
at its index.
max(2, min(16, availableParallelism()-2)) agents in flight per
run — availableParallelism() follows CPU affinity and container CPU limits,
not the host's core count — override via QWEN_CODE_MAX_WORKFLOW_CONCURRENCY
(clamped to 64).agent() calls per run, override via QWEN_CODE_MAX_WORKFLOW_AGENTS
(clamped to 10000). The call past the cap throws.parallel() or pipeline() call, with no
override.QWEN_CODE_MAX_WORKFLOW_SECONDS (applied as given). A fan-out near the agent
cap will not fit inside the default cap.await, with
no override. A synchronous loop that runs longer is aborted.QWEN_CODE_WORKFLOW_AGENT_MAX_TURNS, clamped
to 500) and 10 minutes (QWEN_CODE_WORKFLOW_AGENT_MAX_MINUTES, clamped
to 100). Raise them for legitimately long work rather than letting agents
come back null — but a value above the clamp is silently cut down to it.agent() call; the stall timeout itself
(QWEN_CODE_WORKFLOW_STALL_SECONDS) is applied as given.budget.total
(null = uncapped) before committing to a large fan-out, because once it is
reached every further agent() call is refused.pipeline()pipeline() runs each item through every stage independently — item A can be
in stage 3 while item B is still in stage 1 — so wall-clock is the slowest
single chain. parallel() is a barrier: it waits for every thunk before
anything moves on, so it costs the slowest item of every stage.
A barrier is right only when a stage genuinely needs cross-item context:
deduplicating or merging across the full result set before expensive downstream
work, exiting early when the total count is zero, or a prompt that compares one
finding against all the others. It is not justified by needing to flatten, map,
or filter between stages (do that inside a pipeline stage), by two stages being
conceptually separate, or by the code reading more tidily. Smell test:
parallel() → a pure transform → parallel() is a pipeline someone wrote with
an unnecessary barrier. When in doubt, pipeline().
A subagent's answer is a claim, not a result. For findings that matter, spawn independent verifiers prompted to refute, and drop what a majority refutes. When a claim can be wrong in several different ways, give each verifier a distinct lens (correctness, security, performance, does it actually reproduce) — diversity catches what repetition cannot. For a wide solution space, generate several independent attempts, judge them in parallel, and synthesize from the winner while grafting the best ideas from the rest.
For discovery of unknown size, keep running finders until some number of consecutive rounds turn up nothing new; a fixed round count stops partway into the tail. Deduplicate each round against everything already seen, never against only what survived judging — otherwise rejected findings reappear every round and the loop never terminates. A closing pass that asks what is still missing (a search angle never run, a claim never verified, a file never read) usually produces the next round of real work.
Scale the fleet to what was actually asked: a quick check gets a few agents and
one verification pass; an explicit request to be thorough or exhaustive earns a
larger pool and a multi-vote adversarial round. Whenever a run bounds its own
coverage — top-N, sampling, no retry — log() what was dropped. Silent
truncation reads as full coverage, which is worse than a smaller honest result.
workflow(nameOrRef, args?, { stepId }?) shares this run's caps. Calls have
individual traces grouping their agents. It nests one level only;
a nested workflow() call throws.
It takes one of two forms. workflow('<name>') resolves a name against
<projectRoot>/.qwen/workflows (project scope, also surfaced as /<name>
slash commands) and ~/.qwen/workflows (user scope, lower precedence when both
define the same name); an active extension's workflow is always named
'<extension>:<name>'. workflow({ scriptPath: '<absolute path>' }) loads a
script file directly from either of those directories, an active extension's
workflow file, or the generated-scripts root
($QWEN_CODE_PROJECT_DIR/workflows/generated — the per-project runtime dir,
not the project tree); any other path is refused. A bare string is always a name: a path passed as a string is rejected
as an invalid workflow name. At the top level that rejection ends the run;
inside parallel()/pipeline() it becomes a position-aligned null like any
other thunk rejection — with no agent dispatched and nothing in the failures
list — so null-check a workflow() result too. In a session that runs named
workflows only (tools.workflowNameOnly), workflow({ scriptPath }) throws the
same way; nest by name.
Use the workflow-creator skill to create or edit saved workflows.
Every run hands back its runId, the script's path on disk, and its journal path. An inline script is persisted under the generated-scripts root, so a resume edits that file and passes the path back instead of re-sending the whole source.
resumeFromRunId replays a prior run: each agent() call's journal key hashes
its prompt and opts chained in call order, so calls whose rolling prefix-hash
still matches are served from cache for the longest unchanged prefix, and the
first changed or missing call onward runs live. Post-processing after the last
agent can therefore change freely without losing the cache. Pass the same
args — they seed the chain, so different args re-run everything. A run whose
journal is no longer on disk has nothing to resume: the call is refused before
any agent runs, so start it again without resumeFromRunId. A run id that is
still running, paused, or not yet exited is refused too, since a second start
would run two copies of its agents against one journal. A run whose process
exited mid-run is later listed as failed with an interrupted error, and
resumes like any other.
The journal is one JSON line per event: a launched line when the run starts
(never on a resume), a started line when an agent is dispatched, then a result line when it returns a value or a failed line
when it settles without one. Only result lines feed the resume cache. A
started line with neither after it means the run was interrupted with that
agent in flight — not that the agent is broken. Read the journal before
diagnosing an empty or surprising result: a cached result can itself be empty,
and a null slot in the output means an agent failed, not that the work found
nothing.
Runs appear in the background-tasks view and the /workflows dialog (live
phase tree, token usage, cooperative pause/resume, cancel);
run_in_background: true returns a run handle immediately in the interactive
TUI and delivers completion through the conversation.
Saved /<name> commands typed in the interactive TUI's ink renderer stay in the foreground:
watch the live tool card; /workflows <runId> shows the run after it settles.
Completion displays the result and delivers it to the model through a
notification, without another user prompt.
The OpenTUI renderer does not yet run client-scheduled tools; there, ask the
model to call Workflow({ name: '<name>' }) instead.
Review a change set across several dimensions, verifying each finding as soon as its dimension is done — a pipeline, so a slow dimension never holds up verification of a fast one.
export const meta = {
name: 'Review changes',
description: 'Review the diff across dimensions and verify every finding',
phases: [
{ title: 'Review', detail: 'One reviewer per dimension reads the diff' },
{
title: 'Verify',
detail: 'An independent verifier tries to refute each finding',
},
],
};
const DIMENSIONS = [
{
key: 'correctness',
lens: 'logic errors, wrong edge cases, broken invariants',
},
{
key: 'security',
lens: 'injection, path traversal, secrets, unsafe defaults',
},
{
key: 'performance',
lens: 'accidental O(n^2), unbounded memory, chatty I/O',
},
];
const FINDINGS = {
type: 'object',
properties: {
findings: {
type: 'array',
items: {
type: 'object',
properties: {
file: { type: 'string' },
claim: { type: 'string' },
},
required: ['file', 'claim'],
},
},
},
required: ['findings'],
};
const VERDICT = {
type: 'object',
properties: { isReal: { type: 'boolean' }, why: { type: 'string' } },
required: ['isReal', 'why'],
};
const target = args?.target;
if (!target) {
throw new Error('args.target is required, e.g. { target: "HEAD~1..HEAD" }');
}
phase('Review');
const reviewed = await pipeline(
DIMENSIONS,
// The stage that dispatches an agent is the stage that handles its null:
// returning null here would drop the dimension and skip the verify stage.
async (dimension) => {
const review = await agent(
`Review the changes in ${target} for ${dimension.lens}. ` +
`Read the files; do not edit anything.`,
{ label: `review:${dimension.key}`, schema: FINDINGS },
);
if (review === null) {
log(`review:${dimension.key} came back empty — its findings are missing`);
return [];
}
return review.findings;
},
(findings, dimension) => {
phase('Verify');
return parallel(
findings.map((finding, index) => async () => {
const label = `verify:${dimension.key}:${index + 1}`;
const verdict = await agent(
`Adversarially verify this claim about ${finding.file}: ` +
`"${finding.claim}". Try to REFUTE it. Read the code first.`,
{ label, schema: VERDICT },
);
if (verdict === null) {
log(`${label} came back empty — "${finding.claim}" is unverified`);
return null;
}
return { ...finding, verdict };
}),
);
},
);
// A stage that throws drops its dimension to a null slot. Say which ones
// before flattening, or the drop reads as a dimension that found nothing.
reviewed.forEach((entries, index) => {
if (entries === null) {
log(
`${DIMENSIONS[index].key} was dropped before its findings were verified`,
);
}
});
const verdicts = reviewed.filter((entries) => entries !== null).flat();
const confirmed = verdicts.filter(
(entry) => entry !== null && entry.verdict.isReal,
);
const refuted = verdicts.filter(
(entry) => entry !== null && !entry.verdict.isReal,
);
log(`confirmed ${confirmed.length} finding(s), refuted ${refuted.length}`);
return { confirmed, refuted };Note what the example does with failure: it refuses to run without the input
it needs, handles each null in the stage that dispatched the agent, gives
every verify dispatch its own label, log()s every dimension and agent it
loses, and returns what the verifiers refuted next to what they confirmed — a
verifier can be wrong too, and nothing is silently omitted.
When the user's message sets a turn target with a +500k-style directive
(+1m, "use 300k tokens"), budget.total is that target and spent() then
counts every output token this turn — the main loop and every agent, not just
this run. Otherwise total is an operator's per-run cap, or null. Once
spent() reaches total, further agent() calls throw; agents already
running are not stopped by it. Loop with
while (budget.total && budget.remaining() > 50_000) { ... } — guard on
budget.total, since with no target remaining() is Infinity and the loop
runs to the 1000-agent cap — or size a fan-out once with
const FLEET = budget.total ? Math.floor(budget.total / 100_000) : 5;.
© QwenLM, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file
SKILL.md and 1 other file in packages/core/src/skills/bundled/workflow-authoring of QwenLM/qwen-code.
Open the folder on GitHubat commit 55ee50d
Workflow Authoring next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.
| Skill | Stars | Used in | Tokens | Auto-check | Licence | Repo updated |
|---|---|---|---|---|---|---|
| Workflow Authoring this skillQwenLM/qwen-code | 28k | — | ~6.9k | Automated safety check: Pass | Apache-2.0 | |
| Hermes Agent Skill AuthoringNousResearch/hermes-agent | 252k | — | ~3.6k | Automated safety check: Pass | MIT | |
| Configuring Oauth2 Authorization Flowmukul975/Anthropic-Cybersecurity-Skills | 34k | — | ~1.7k | Automated safety check: Pass | Apache-2.0 | |
| Authoring Skillsvercel/next.js | 143k | — | ~1k | Automated safety check: Pass | MIT | |
| Abp Authorizationabpframework/abp | 14k | — | ~1.3k | Automated safety check: Pass | LGPL-3.0 | |
| Implementing GCP Binary Authorizationmukul975/Anthropic-Cybersecurity-Skills | 34k | — | ~2k | Automated safety check: Pass | Apache-2.0 |
NousResearch/hermes-agent
Author in-repo SKILL.md files: frontmatter and structure. An agent skill from NousResearch/hermes-agent.
mukul975/Anthropic-Cybersecurity-Skills
Configures secure OAuth 2.0 authorization flows, including Authorization Code with PKCE, Client Credentials, and Device Authorization Grant, covering flow selection, PKCE implementation, token…
vercel/next.js
How to create and maintain agent skills in .agents/skills/. An agent skill from vercel/next.js.
abpframework/abp
ABP permission system - PermissionDefinitionProvider, [Authorize] attribute, CheckPolicyAsync, IsGrantedAsync, ICurrentUser, IPermissionManager, multi-tenancy side.
mukul975/Anthropic-Cybersecurity-Skills
Implements GCP Binary Authorization end to end, including creating KMS-backed attestors, Container Analysis notes, deploy-time policies, and signing image attestations, so that only trusted…
netdata/netdata
Author, modify, or review Netdata collectors across Go, IBM, C, Rust and external plugins.
QwenLM/qwen-code
Reproduces a feature from Codex or Claude Code in Qwen Code by running the reference agent under capture, reading the traces, then implementing matching behavior.
QwenLM/qwen-code
Guides end-to-end testing of the Qwen Code CLI in headless mode with real model calls, MCP test servers and inspection of raw API traffic.
QwenLM/qwen-code
Scheduled CI skill that scans a repository for small, certain docs, test and code hygiene issues and fixes them on one branch with a commit per finding.
QwenLM/qwen-code
Builds a rebranded Qwen Code desktop package from the Tauri shell using only a brand id and a logo, with sensible derived defaults.
QwenLM/qwen-code
Walks through capturing and comparing V8 heap snapshots to find memory leaks in the Qwen Code Node.js CLI, using tmux and the chrome-devtools CLI.
QwenLM/qwen-code
Drives Qwen Code in a real tmux session the way a user would and saves a readable step-by-step transcript of each screen for maintainers to review.
Reference for writing a Workflow tool script (script API and gotchas, agent() options, pipeline() vs parallel(), verification and convergence patterns, resume, worked example). Workflow Authoring is an agent skill from QwenLM/qwen-code. Reference for writing a Workflow tool script (script API and gotchas, agent() options, pipeline() vs parallel(), verification and convergence patterns, resume, worked example).
Run `npx skills add QwenLM/qwen-code --skill workflow-authoring -a claude-code`. Or copy the skill folder (packages/core/src/skills/bundled/workflow-authoring in QwenLM/qwen-code) into .claude/skills/workflow-authoring in your project. Claude Code loads it when a task matches its description.
Run `npx skills add QwenLM/qwen-code --skill workflow-authoring -a codex`. Or copy the skill folder (packages/core/src/skills/bundled/workflow-authoring in QwenLM/qwen-code) into .agents/skills/workflow-authoring in your project. Codex loads it when a task matches its description.
Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add QwenLM/qwen-code --skill workflow-authoring -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/workflow-authoring, .gemini/skills/workflow-authoring, .github/skills/workflow-authoring and .opencode/skills/workflow-authoring in your project.
Going by SKILL.md and its folder, Workflow Authoring needs TypeScript for the scripts in its folder and the command-line tools its instructions call (git). Our summary lists: Node.js.
SKILL.md contains no URLs. Its commands use git, which can reach the network depending on how they are called. This is read from the text; nothing was executed.
Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.
Workflow Authoring is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.
About 6.9k tokens (SKILL.md is roughly 27k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.
Skills that share tags, products or a category with Workflow Authoring: Hermes Agent Skill Authoring (NousResearch/hermes-agent, 252k stars), Configuring Oauth2 Authorization Flow (mukul975/Anthropic-Cybersecurity-Skills, 34k stars), Authoring Skills (vercel/next.js, 143k stars) and Abp Authorization (abpframework/abp, 14k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.
QwenLM (a GitHub organization) maintains it in QwenLM/qwen-code, which has 28,370 GitHub stars. The repository holds 41 skills in this directory. The repository was last updated on October 9, 2026.
Source: QwenLM/qwen-code on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.