CLI reference

The extension is invoked as gh wm <subcommand> when installed via gh extension install. Building from source produces a binary named gh-wm (same commands as the root Cobra app in cmd/root.go ).

Global

  • Root use string: gh-wm (binary name).
  • Short description: GitHub Workflow Manager — run gh-aw-style task markdown in CI.

version

Purpose: Print the installed gh-wm version (and optional Git commit line for release builds).

Usage:

  • gh wm version — prints gh-wm <version> on one line; if the binary was built with a commit SHA (release workflow), prints commit: <short-sha> on a second line.
  • gh wm --version / gh wm -v — same version string via the root Cobra flag.

Local and CI builds without linker flags report dev. Release assets built from a git tag embed the tag (without the leading v) and a short commit hash. See cmd/version.go and .github/workflows/release.yml .


init

Purpose: Create .wm/ layout, starter tasks, and generate .github/workflows/wm-agent.yml.

Usage: gh wm init

Steps (see cmd/init.go ):

  1. Create .wm/tasks/.
  2. Write embedded config.yml and starter tasks (internal/templates ).
  3. Validate task markdown bodies for gh-aw–compatible ${{ }} expressions (see Task format — expressions ) using compat.gh_aw_expressions in .wm/config.yml (default error).
  4. Collect workflow on: triggers from .wm/tasks (union of issues / issue_comment / pull_request types, slash_commandissue_comment, schedule crons, and always workflow_dispatch) via gen.CollectTriggersFromTasksDir , then generate wm-agent.yml via gen.WriteWMAgent (same pipeline as gh wm compile), including workflow.runs_on from .wm/config.yml (default ubuntu-latest if unset), workflow.install_claude_code (default true: install Claude Code in CI before gh wm run), and optional workflow.gh_wm_extension_version (when set, CI runs gh extension install owner/repo --pin <ref> per gh help extension install). Reusable workflows ensure gh via the composite install-gh-cli action, then install gh-wm with gh extension install (see agent-resolve.yml / agent-run.yml ).
  5. Ensure .wm/.gitignore contains runs/ (per-run artifact dirs from gh wm run) — creates or appends that file when needed.

workflow.pre_steps (optional): A list of GitHub Actions job steps (name, uses, run, with, env, if) run after checkout and before installing gh-wm and running the task. Use this for toolchains (e.g. jdx/mise-action ), dependency installs, or installing the agent CLI. When pre_steps is non-empty, the generated wm-agent.yml uses an inline run job (steps embedded in the file) instead of calling the reusable agent-run.yml workflow, because reusable workflows cannot accept arbitrary step YAML as inputs.

Environment:

VariableDefaultMeaning
GH_WM_REPOan-lee/gh-wmowner/repo for reusable workflow uses: in generated wm-agent.yml.

compile

Purpose: Validate task bodies for ${{ }} (same rules as gh wm validate; see compat.gh_aw_expressions in Task format ), then regenerate .github/workflows/wm-agent.yml from current tasks (task-driven on: union, same as init step 4), workflow.runs_on, workflow.gh_wm_extension_version, and workflow.pre_steps in .wm/config.yml (when present), and GH_WM_REPO.

Usage: gh wm compile

If .wm/config.yml is missing, runner labels default to ubuntu-latest when generating wm-agent.yml. workflow.pre_steps follows the same rules as under init above. compile also ensures .wm/.gitignore lists runs/ (same as the last step under init), so older repos pick up the ignore rule without re-running init.

Breaking change: Earlier versions folded this into gh wm upgrade. upgrade no longer rewrites wm-agent.yml — use compile after changing tasks, schedules, or workflow-related config.


upgrade

Purpose: Run gh extension upgrade an-lee/gh-wm so the installed gh-wm extension matches the latest release (or your pinned source). If the upgrade fails (for example the CLI was not installed as a gh extension), the command prints gh’s output and exits with a non-zero status.

Usage: gh wm upgrade


validate

Purpose: Scan .wm/tasks/*.md bodies for gh-aw–compatible ${{ }} expressions without upgrading the extension or rewriting wm-agent.yml. Use in CI (e.g. after editing tasks).

Usage: gh wm validate--repo-root defaults to . (same as other commands).

Behavior matches compat.gh_aw_expressions in .wm/config.yml (error / warn / off). See Task format — expressions .


update

Purpose: Re-download task files using each task’s source: frontmatter (same idea as gh aw update for workflows with a source). source: may be an https URL or an owner/repo/path shorthand (path under the repo on main, e.g. owner/repo/workflows/task.md).

Usage:

  • gh wm update — update every .wm/tasks/*.md that has a non-empty source: field.
  • gh wm update <task-name> … — update only the named tasks (filename without .md, or with .md).

Tasks created with gh wm add (URL, owner/repo/task shorthand, or path) get a source: when appropriate so gh wm update can re-fetch. gh wm add runs gh wm compile automatically after a successful write. After gh wm update, run gh wm compile to refresh wm-agent.yml if task on: triggers, schedules, or other generator inputs changed.

See cmd/update.go .


assign

Purpose: Add a label to an issue (local gh auth).

Usage: gh wm assign <issue-number>

Flags:

FlagDefaultDescription
--labelagentLabel name to add

Implementation: ghclient.AddIssueLabel — by default invokes gh api; set GH_WM_REST=1 to use go-gh REST (internal/gh ).


add

Purpose: Copy or download a gh-aw–compatible Markdown file into .wm/tasks/ (validates YAML frontmatter).

Usage: gh wm add <owner/repo/task | url | path>

  • owner/repo/task-name — Fetches from the default branch (main), trying workflows/<task>.md first (gh aw layout), then .wm/tasks/<task>.md. Records source: as owner/repo/workflows/… or owner/repo/.wm/tasks/… (gh aw-style shorthand), not a raw URL.
  • https://… or http://… — Downloads the file; source: is the same URL (unless already set in the file).
  • Local path — Copies the file; no source: is injected unless the file already has one.

Writes <cwd>/.wm/tasks/<basename>.md, then runs gh wm compile so wm-agent.yml matches the new task. See cmd/add.go and cmd/github.go .


resolve

Purpose: Print matching task names for a GitHub event (JSON array by default).

Usage: gh wm resolve

Flags:

FlagDefaultDescription
--repo-root.Repository root containing .wm/
--event-name$GITHUB_EVENT_NAMEGitHub event name
--payload$GITHUB_EVENT_PATHPath to event JSON file; if --payload and GITHUB_EVENT_PATH are both unset, payload defaults to {}
--jsontrueIf true, print JSON array; if false, one name per line
--force-task(unset)Pin a single task by name; skips event/on: matching (same idea as local run picking a task). Used for manual runs and CI when the resolve job should return exactly one task.

Schedule narrowing: When --event-name is schedule, set WM_SCHEDULE_CRON to the workflow cron string that fired (same value as github.event.schedule on Actions). agent-resolve.yml sets this automatically for reusable resolve. Omit WM_SCHEDULE_CRON locally to list every task that declares on.schedule.


run

Purpose: Execute one task: activation (validate event/engine, optional working label, branch prep for PR mode), agent, validation (exit + output size), safe-outputs: steps, then conclusion (done/failed labels, checkpoint, branch rollback on failure). With --agent-only, stops after a successful validation phase (skips safe-outputs and conclusion) so a follow-up gh wm process-outputs job can run with a write-capable GITHUB_TOKEN while the agent job used read-only permissions (see architecture.md ).

Usage: gh wm run --task <name> (local agent), or gh wm run --task <name> --remote to dispatch the wm-agent workflow on GitHub.

Git working tree: For local runs (default), gh wm requires a clean repository at --repo-root: git status --porcelain must be empty (no modified, staged, or untracked files). CI checkouts from actions/checkout usually satisfy this. Use --allow-dirty to skip the check (e.g. local scripts or tests). --remote does not require a clean tree (it does not run the agent locally).

Flags:

FlagDefaultDescription
--repo-root.Repository root
--task(required)Task name (filename without .md)
--event-name$GITHUB_EVENT_NAMEEvent name (local run only)
--payload$GITHUB_EVENT_PATHPath to event JSON; if --payload and GITHUB_EVENT_PATH are both unset, payload defaults to {} (local run only)
--allow-dirtyfalseSkip the git clean working tree check (local run only)
--agent-onlyfalseStop after agent validation; do not run safe-outputs or conclusion. Writes result.json / run.json for the partial run. Pair with gh wm process-outputs in a second CI job (see architecture.md ).
--remotefalseRun gh workflow run to trigger workflow_dispatch on the repo’s wm-agent.yml with -f task_name=<task>. Requires the gh CLI and auth. Repository defaults to gh repo view; override with --repo OWNER/NAME. Optional --workflow (default wm-agent.yml), --ref (git ref for the workflow run), and --issue (passed as -f issue_number= for the dispatch inputs). After changing gh-wm or task inputs, run gh wm compile in the target repo so the generated workflow declares the task_name input; older wm-agent.yml files may reject unknown -f fields. --remote does not send a custom GitHub event payload (the run on Actions sees a normal workflow_dispatch event, optionally with issue_number).

Timeout: Uses timeout-minutes from task frontmatter (default 45, max 480). The deadline is applied inside engine.RunTask (so direct library use gets the same behavior as the CLI). See cmd/run.go for the stderr banner.

Output: Before and during the run, stderr prints a banner (task name, repo path, current git branch, engine, timeout in minutes) and wm run: progress lines for each phase—e.g. activation (run directory path, optional checkpoint hint, working label, feature branch when safe-outputs includes create-pull-request), agent: immediately before the subprocess, validation: after the agent exits, and safe-outputs: when the task declares safe-outputs: with at least one key. The live agent stream (combined subprocess stdout/stderr) follows the agent: line on stderr. Each run also writes a per-run artifact directory under .wm/runs/<id>/ (or WM_RUN_DIR/<id>/ when set): prompt.md, combined agent log (default agent-stdout.log, or conversation.json / conversation.jsonl when structured Claude print-mode output is configured — see claude_output_format / WM_CLAUDE_OUTPUT_FORMAT in task-format.md ), meta.json, result.json, and run.json (merged meta + outcome; see architecture.md ). The summary block on stderr includes a line artifacts=<path> when that directory was created. After the run, a short summary line is printed to stderr (task name, repo path, duration, exit code, success, phase=activation, agent, validation, safe-outputs, or last phase reached). If the run fails, stderr also prints failure phase: (for safe-outputs, the message still says safe-outputs (post-agent); otherwise the failing phase name).

GitHub Actions job summary: When GITHUB_STEP_SUMMARY is set (GitHub provides this automatically on Actions runners) and the task uses the built-in claude engine with WM_AGENT_CMD unset, gh wm run appends a markdown section to that file after the run if conversation.jsonl or conversation.json exists under the per-run directory (structured Claude print-mode output). The section includes pipeline success, wall-clock duration, stream event counts, cost and token usage from the final result event when present, and model IDs from modelUsage (or model from config as a fallback label). Plain-text agent logs (agent-stdout.log only) do not produce this block.

Branch + PR (safe-outputs: create-pull-request): If the task lists create-pull-request under safe-outputs and the repo is on the default branch (or detached HEAD), internal/gitbranch creates and checks out wm/<task-slug>-<UTC-timestamp> before the agent runs so commits are not on main. If you are already on a non-default branch, no branch is created. On agent failure after a branch was created, the runner checks out the previous branch when possible (skipped when the previous state was detached HEAD). After a successful agent run, if output.jsonl includes a create_pull_request item (and policy allows it), the safe-output step runs git push then gh pr create --base <default> when there are commits ahead of the remote default branch; it skips if the current branch is still the default, if gh pr list already shows a PR for the current head, or if there is nothing to push.

Agent invocation (internal/engine/agent.go ):

VariableMeaning
WM_AGENT_CMDIf set: split on whitespace for argv; prompt is appended as the last argument unless the string contains {prompt}, in which case that placeholder is replaced by the prompt (single argument). Overrides engine:.
WM_ENGINE_CODEX_CMDWhen engine: codex and WM_AGENT_CMD unset: same {prompt} rule as above (otherwise prompt is appended). Default codex invocation mirrors claude (stdin prompt, --dangerously-skip-permissions, --model / --max-turns from config when set).
(default)claude -p --dangerously-skip-permissions with the prompt on stdin; --model and --max-turns come from .wm/config.yml when set. Optional --output-format json or stream-json (with --verbose) when claude_output_format / WM_CLAUDE_OUTPUT_FORMAT is set (built-in claude only). When safe-outputs: is non-empty, also --append-system-prompt with enforcement text (output.SafeOutputsSystemPromptAppend ) so rules live in Claude Code’s system prompt, not only the user message.
The default claude invocation uses --dangerously-skip-permissions so non-interactive runs can use tools (file edits, gh, git). Subprocess env is the parent environment (GITHUB_TOKEN in Actions, gh auth locally) plus GITHUB_REPOSITORY, WM_TASK, WM_REPO_ROOT, WM_SAFE_OUTPUT_FILE (output.jsonl for gh wm emit), WM_ISSUE_NUMBER / WM_PR_NUMBER when known, and WM_TASK_TOOLS when tools: is set in the task frontmatter (JSON for structured values).

Post-agent: When safe-outputs: has at least one key, outputs are recorded via gh wm emit (see Emit below) into WM_SAFE_OUTPUT_FILE (output.jsonl). If output.jsonl is empty, the phase warns and succeeds (implicit noop). WM_CHECKPOINT=1 enables loading/posting checkpoint comments (internal/engine/runner.go ).

Secrets (CI): ANTHROPIC_API_KEY is expected by reusable workflow for Claude Code; ensure the agent you invoke uses it as required.


process-outputs

Purpose: Resume the pipeline after gh wm run --agent-only: run safe-outputs: (output.RunSuccessOutputs ) and conclusion (concludeRun ) for an existing per-run directory. Used in the second job of agent-run.yml so GitHub writes use a token with issue/PR/content write permissions.

Usage: gh wm process-outputs with the same --repo-root, --event-name, and --payload as the prior gh wm run (defaults to GITHUB_EVENT_NAME / GITHUB_EVENT_PATH when unset). Supply either --run-dir or --task (CI generated workflows use --task so the extension resolves the run directory; no Python or jq required).

The run directory must contain result.json from --agent-only with agent_result.success true (and optional top-level success must not be false). Otherwise the command errors and does not apply GitHub mutations.

Flags:

FlagDefaultDescription
--repo-root.Repository root (must match the agent run)
--run-dirPath to .wm/runs/<id>/ from the agent-only run (required if --task is not set)
--taskTask name: use the newest .wm/runs/<id>/ whose meta.json matches this task (required if --run-dir is not set)
--event-name$GITHUB_EVENT_NAMEEvent name
--payload$GITHUB_EVENT_PATHEvent JSON path

See architecture.md — GitHub Actions token sandbox .

Labels: When applying create_pull_request, add_labels, or create_issue outputs that include label names, process-outputs creates missing repository labels (default neutral color) before gh pr create or issue APIs, so the repo does not need those labels defined ahead of time.


emit

Purpose: Append one validated safe-output line (NDJSON) for the current task run. Intended to be invoked from the agent’s shell tool during gh wm run (environment is set by the runner).

Usage: gh wm emit <subcommand> with flags, e.g. gh wm emit noop --message "nothing to post", gh wm emit add-comment --body "…".

Environment (required): WM_REPO_ROOT, WM_TASK, WM_SAFE_OUTPUT_FILE. Typically also GITHUB_REPOSITORY and WM_ISSUE_NUMBER / WM_PR_NUMBER when commenting or labeling on the triggering thread.

Subcommands: noop, add-comment, add-labels, remove-labels, create-issue, create-pull-request, update-issue, update-pull-request, close-issue, close-pull-request, add-reviewer, create-pull-request-review-comment, submit-pull-request-review, reply-to-pull-request-review-comment, resolve-pull-request-review-thread, push-to-pull-request-branch, missing-tool, missing-data. Use gh wm emit --help and gh wm emit <cmd> --help for flags.

update-issue and update-pull-request accept optional --operation for the body: replace (default), append, prepend, replace-island (same semantics as task-format.md operation on JSON items).

Inline review comments use the head commit SHA and a path/line in the diff; resolving a thread requires a GraphQL review thread id (flag --thread-id / JSON thread_id), not the numeric REST comment id alone.

submit-pull-request-review posts an overall PR review with --event (APPROVE, REQUEST_CHANGES, or COMMENT), optional --body, and optional --commit-id.

See task-format.md — safe-outputs: .


status

Purpose: List issues with agent-related labels.

Usage: gh wm status

Flags:

FlagDefaultDescription
--allfalseUse gh search issues across visible repositories instead of gh issue list for the current repo

See cmd/status.go .


logs

Purpose: List wm-agent workflow runs; prefers runs whose title contains #<issue-number>.

Usage: gh wm logs <issue-number>

If none match, prints recent runs with a note. See cmd/logs.go .


VariableUsed by
GITHUB_EVENT_NAME, GITHUB_EVENT_PATHresolve, run: event name / payload file when flags omitted; if GITHUB_EVENT_PATH unset, payload is {}
GITHUB_REPOSITORYAgent + gh outputs; required for labels/comments
WM_SCHEDULE_CRONresolve schedule narrowing (resolver.go ); set in CI by agent-resolve.yml from github.event.schedule.
WM_AGENT_CMDOverride agent command (agent.go )
WM_ENGINE_CODEX_CMDCodex CLI prefix when engine: codex
WM_TASK_TOOLSSet automatically from tools: (read by agent)
WM_SAFE_OUTPUT_FILESet by run when a per-run dir exists: NDJSON log for gh wm emit (output.jsonl).
WM_REPO_ROOTRepository root (same as --repo-root); used by gh wm emit to load the task.
WM_ISSUE_NUMBER, WM_PR_NUMBEROptional; set by run when the event provides issue/PR numbers (for emit validation / default --target).
WM_CHECKPOINTSet to 1 to enable checkpoint load/post
WM_RUN_DIRIf set, per-run artifacts are written under <WM_RUN_DIR>/<run-id>/ instead of <repo>/.wm/runs/<run-id>/ (useful for CI artifact upload paths).
WM_CLAUDE_OUTPUT_FORMATOverrides claude_output_format in .wm/config.yml : text (default), json, or stream-json for built-in claude (run-dir filename, --output-format, and --verbose when stream-json).
WM_LOG_FORMATSet to json for JSON log/slog output on stderr (structured pipeline logs).
GH_WM_RESTSet to 1 so ghclient / label-comment APIs use go-gh REST instead of gh api subprocesses (internal/gh ).
GITHUB_STEP_SUMMARYGitHub Actions: path to the job summary file; when set, run may append Claude usage stats from conversation.json / conversation.jsonl (internal/engine/conversation_summary.go ).
GH_WM_REPOinit, compile for reusable workflow owner/repo