Handbook, Team, and Playbooks from the CLI

Four top-level commands cover the work you would otherwise do in the app's Handbook and Team pages. handbook and team are groups of subcommands; chat and run are single commands:

  • wallfacer handbook reads, edits, organizes, and publishes pages and playbooks.
  • wallfacer team lists the account's agents and human members.
  • wallfacer chat starts a conversation with one agent.
  • wallfacer run runs one published playbook.

Every command works inside the account configured as account_id in ~/.wallfacer/wallfacer.yml or WALLFACER_ACCOUNT_ID (WF_ACCOUNT_ID is accepted too, and WALLFACER_ACCOUNT_ID wins when both are set). wallfacer auth login stores the token; use wallfacer auth status to find the account ID. With an account configured, no command takes it as an argument: the CLI prepends the configured ID to the generated API commands, which take an account-id argument until one is set. A reference that belongs to another account is refused before any request goes out.

Responses are ordinary CLI JSON, so -o yaml, -q/--query, and --raw work the same way they do for the generated API commands.

The handbook holds two kinds of entry. list and search return both with each record's type and state; tree returns the API's own nodes, which carry type but no computed state.

wallfacer handbook tree                         # the whole nested hierarchy
wallfacer handbook list --type playbook         # flat, with each entry's path and state
wallfacer handbook search "pull request"        # title, description, and page body

list and search narrow with --type page or --type playbook. list also takes --include-deleted and --include-archived, which are the only way to list an entry that has been removed from the tree. By ID, read and resolve still reach one.

search sweeps the underlying listings rather than hitting a search endpoint, so it is bounded by --limit (default 20) and --max-pages (default 20). The top-level truncated field reports the first of those alone: it is true when the sweep filled --limit. A sweep that runs out its --max-pages budget with fewer matches reports truncated: false and shows the shortfall as complete: false under pagination. Either way, follow_up.widen names the command that reads further.

Responses carry the identifiers for the next command

Across the handbook command group, responses use up to five envelope keys, and between them they hold everything you need to keep going without knowing anything about the backend. Individual commands add their own top-level fields: search adds query, limit, and truncated, a page or hierarchy write adds note, and reorder adds parent and children. The playbook authoring commands keep their notes inside data instead, as data.note and data.disabled_note.

  • data is the record or the content you asked for, except on the commands that have more than one thing to report, which wrap it. handbook tree puts the nodes at data.tree, handbook reorder returns the whole tree, and handbook draft returns data.present and data.draft. handbook delete reports id, title, deleted, reparented, reparented_to, revision_history, and restorable_with_id. handbook diff returns the two definitions it compared. And every playbook authoring command that writes (create-playbook, update-playbook, archive-playbook, restore-playbook, save-draft, discard-draft, publish) reports the outcome of the write there rather than the record, while diff-draft, which is a read, reports its comparison there instead, so a query like -q 'data.id' comes back empty on most of them. Read data once before scripting against it.
  • reference identifies what was resolved, on the commands that resolve one thing: type, id, account_id, title, description, path, parent_page_id, state, and resolved_from (which of id, name, path, url, or created matched). What else it carries depends on the type and on where it was built. A page adds has_body in either shape, and delete, revisions, revision, create, update, and restore resolve pages only, so a playbook reference never comes back from them. A playbook adds active_version and has_draft on a reference resolved off the handbook tree (versions, version, draft, save-draft, diff-draft, diff, and archive-playbook), and on one built from the record the command read or wrote (read, move, create-playbook, update-playbook, restore-playbook, discard-draft, and publish) it adds version_count and linked_page_ids on top of those; an ID that is not in the tree, such as an archived playbook or a deleted page, resolves against the record too and carries the record set. description, path, and parent_page_id carry omitempty, and so does every type- or source-specific field, so a field the case does not set is absent from the JSON rather than present and empty: -q 'reference.version_count' comes back with nothing on a tree-resolved reference, and path is absent from the reference create-playbook returns, which is built from the record and has no tree to compute a path from. type, id, account_id, title, state, and resolved_from carry no such tag and are always present, empty string included. tree, list, search, and reorder carry no reference at all, and neither does resolve, whose answer is the reference itself, under data. The entries list and search return under data have the same shape as a record-built reference, with resolved_from reading list or search; search hits add matched_in.
  • follow_up names the literal commands that fetch the rest: the record itself as read, a page revisions list as revisions, a playbook versions list as versions and a single version as version plus draft when one exists, each linked page, the parent, and the children. Page create, page update, page restore, and handbook move emit a smaller set that adds tree: read, tree, a page revisions or a playbook versions, and the parent. Other writes use command-specific follow_up objects.
  • pagination passes the API's own meta and links through untouched on list, revisions, and versions. The sweeping commands, search and team list, add pages_read and complete to each source's half.
  • versions appears on handbook diff and records which two versions were compared.

So a read tells you what else exists and how to ask for it:

wallfacer handbook read "R&D/Engineering/Build" -q 'follow_up'

Listings take --page, sweeps take --max-pages

handbook list reads pages and playbooks from two separate listings, so with --type omitted its pagination object has a pages and a playbooks half, each with that listing's meta and links. With --type set, only that listing's half is present. Walk those pages with --page and --per-page, which handbook list, handbook revisions, and handbook versions register:

wallfacer handbook list --page 2
wallfacer handbook list --per-page 50 --page 3

handbook list caps --per-page at 200, applied to each of the two listings it reads.

handbook revisions and handbook versions each return a single pagination.meta and pagination.links, and take the same two flags. Both endpoints serve 25 per page, and the caps differ: --per-page tops out at 100 on revisions and at 200 on versions. Use handbook version <playbook-id> <version> to read one version in full.

handbook search and team list split their pagination the same way, under pages / playbooks and agents / humans, but they sweep the listings for you rather than taking a page number. Neither registers --page or --per-page (an unknown flag is rejected outright), both read each listing at a fixed 100 records per request, and --max-pages is what bounds them. They report pages_read and complete per source alongside that source's last meta and links. complete: false means the command stopped before exhausting that source while another API page was still available, either because handbook search reached --limit or because the sweep hit its --max-pages ceiling.

Either split can come back with one half missing, for its own reason. handbook search reads the pipelines listing only when --type is omitted or set to playbook and the page hits have not already filled --limit, so the playbooks key is absent only under --type page, or when the page hits filled --limit before the playbook sweep was reached. truncated is computed after both sweeps, so a search whose playbook half fills --limit reports truncated: true with a playbooks key present. team list reads both listings when --type is omitted, and only the named half otherwise: --type agent carries no humans key, --type human no agents key. The two halves of team list are also shaped differently, because the API paginates /agents by cursor rather than by page number, discarding a page=N if one is sent, so pagination.agents carries cursor meta and links while pagination.humans carries page numbers.

Names and paths resolve against the tree; restores need an ID

Reads, writes, and run all take a reference, in any of these forms:

FormExample
Stable ID019ec8c5-5424-70e0-b5b1-39998a11f0f5
Unique name"Writing Great PRs"
Full handbook path"R&D/Engineering/Build"
Page URLhttps://app.wallfacer.ai/accounts/<account-id>/handbook/pages/<page-id>
Playbook URLhttps://app.wallfacer.ai/accounts/<account-id>/handbook/<playbook-id>
Playbook version URLhttps://app.wallfacer.ai/accounts/<account-id>/handbook/<playbook-id>/versions/<version>
Handbook linkwallfacer://handbook/pages/<page-id>

The host is not inspected. The account-ID segment still must match the configured account, the entry ID selects the page or playbook, and a playbook version URL also carries the version used by handbook version. The app's browse routes resolve as well: /handbook/<account-id>/pages/<page-id>, /handbook/<account-id>/<playbook-id>, and the /versions/<version> route under either shape.

Names and paths are matched case-insensitively against the live tree, so they reach anything still in it, including a disabled playbook, which comes back with state: "disabled". What a name or a path cannot reach is a deleted page or an archived playbook, which are the two things the tree leaves out. A name that matches more than one entry is never guessed: the command fails and lists every candidate with its type, ID, and path.

wallfacer handbook resolve "Writing Great PRs"   # name, path, or URL to a stable ID and type

Use resolve when you want the ID a script will hold onto. handbook restore and handbook restore-playbook are the two commands that require one, or a detail URL carrying one, because their targets are exactly the entries no name or path reaches.

Wrong-type references fail before anything is written or run. Asking for a playbook and naming a page reports exactly that.

Read the full body, the revisions, and the published definition

handbook read returns a page's markdown body, and for a playbook it returns the record with its active version expanded to the full published definition. The summary the pipeline endpoint returns on its own does not contain the instructions, so read fills it in.

wallfacer handbook read <page-id> -q 'data.body' --raw
wallfacer handbook read <playbook-id>              # record plus the full active definition
wallfacer handbook revisions <page-id>             # newest first
wallfacer handbook revision <page-id> <revision-id>
wallfacer handbook versions <playbook-id>
wallfacer handbook version <playbook-id> 3         # omit the number for the active version
wallfacer handbook draft <playbook-id>             # the unpublished draft, on its own

A draft is never substituted for the active definition. handbook read reports the draft only as a summary, with the command that reads it in full.

Linked pages are the knowledge a playbook delivers to its runs. They arrive as reference.linked_page_ids, and follow_up.linked_pages holds one handbook read command per page. Inlining them into a run's context is budgeted: bodies are inlined in precedence order while they fit, and the pages the performing agent's role page mentions take that budget first. The role page's own body is injected into every session separately and is not charged to the budget. A linked page past the budget is not inlined: it falls back to the run's table of contents, which is byte-budgeted in turn, and rows past that budget collapse into a count of the pages left out and a pointer at handbook_search. The handbook MCP tools read any of them in full.

Editing pages and moving them around the tree

wallfacer handbook create "Writing Great PRs" --body-file pr.md --under "R&D/Engineering"
wallfacer handbook update "Engineering/Build" --body-file build.md
wallfacer handbook move "Engineering/Build" --under "R&D" --position 0
wallfacer handbook reorder --under "R&D/Engineering" "Build" <playbook-id> "Review"
wallfacer handbook delete <page-id>
wallfacer handbook restore <page-id>

--under files an entry under a page and --top-level files it at the root. Passing both is refused. Passing neither on update leaves the entry where it is, which is why an edit never moves a page by accident.

--position on move and on update is an insert point, not a slot you overwrite: the sibling holding that position and everything after it shift down, the parent renormalizes to dense 0..n-1 positions, and a value past the last sibling appends. Pages and playbooks share one ordering under a parent, so a position counts both. The position in the result is the one the entry landed on rather than the integer sent, so read it back from there. Leave the flag off on a move and the entry goes to the end of its new parent; a move whose destination is the parent it already has leaves its position alone. A negative value is refused before the request. create reads the flag the same way: the integer is an insert point there too, so read the landed position back off the result rather than assuming the one you sent. Leave it off on a create and the entry goes to the end of its parent.

--body and --body-file name the same field, so pass one. You can also pipe a JSON object on stdin and layer flags over it; a flag always wins. A body flag is the exception: passing one means stdin is not read at all, so pipe the whole JSON object when you want title, parent_page_id, or position to come from it too. On update, --clear-body empties the body, which is different from omitting --body; create does not register that flag.

reorder takes every child of one parent, in the order you want them, pages and playbooks together. It checks the list against the current tree first and tells you which child is missing, which entry is not a child of that parent, or which one you listed twice, so a partial list cannot silently drop anything.

Deleting a page keeps its revision history and its children. The response reports what was reparented, where to, and the ID that handbook restore needs. restore refuses a page that is not deleted, before the request is sent, so it never reports a restore that did not happen.

A saved draft is not published

Publication is always something you ask for.

wallfacer handbook create-playbook --name "Fix a bug" --definition-file playbook.yaml
wallfacer handbook save-draft <playbook-id> --definition-file draft.yaml   # not published
wallfacer handbook diff-draft <playbook-id>                                # draft vs active
wallfacer handbook publish <playbook-id> --notes "Added the smoke-test step"
wallfacer handbook discard-draft <playbook-id>                             # active version untouched

create-playbook is the exception worth knowing: it validates the definition, stores it as version 1, and activates it in the same call. Pass --draft to also keep that definition as a draft you can edit.

After that, save-draft writes to a single draft slot per playbook and saving again overwrites it. Drafts are stored verbatim and are not validated until you publish, so diff-draft is a local field-by-field comparison against the active definition rather than a server check.

publish reads the stored draft back and submits it as a new version. With no draft saved it fails and names the command that saves one, and a rejected definition fails without creating a version. --activate defaults to true; --activate=false records the version without changing which one new tasks pin to, and reports data.activated: false with a data.note saying so.

Publishing never enables a disabled playbook. When it is disabled the response carries data.disabled_note, pointing at handbook update-playbook --enable. Restoring an archived playbook also brings it back disabled. restore-playbook refuses a playbook that is not archived, before the request is sent: the server's PATCH is idempotent and answers 200 on a playbook it never archived, so the state is checked client-side rather than reporting a restore that wrote nothing.

handbook diff <playbook-id> 2 3 reads two published versions and returns both definitions side by side, as left_definition and right_definition, rather than a list of changes; handbook diff-draft is the one that computes changes. Each side is a version number or a version UUID.

Definitions read in JSON or YAML, from --definition-file or stdin, and the command unwraps a printed CLI response for you. That means handbook version and handbook draft output pipes straight back in:

wallfacer handbook version <playbook-id> 3 | wallfacer handbook save-draft <other-playbook-id>

Page edits reach the next run immediately

A playbook's own instructions are versioned, and a run pins the version that was active when the task started. Handbook pages are not versioned that way. A run reads the page when it needs it, so editing a linked page's body, or changing which pages are linked with update-playbook --link-page, changes what later runs receive without any publish step.

Tasks already running keep the definition they started with. Only the pages are read live.

Find the agent before you start the work

wallfacer team list --type agent          # the actor picker
wallfacer team list --include-disabled    # plus offboarded agents
wallfacer team get "Grace Hopper"
wallfacer team get jin -q 'data.role_page_id' --raw

Agents and human members come back together, each with type, id, name, github_username, state, and chatable. email carries omitempty, so a record with no address has no email key at all; github_username carries no such tag and is always present, null when no GitHub account is linked. Humans add role; agents add handle, title, role_page_id, environment_id, vendor, model, and runtime_status. chatable is true only for an active agent.

Team references are their own set: a member ID, an agent handle with or without a leading @, an email address, or a unique display name, tried in that order. An ambiguous one lists the candidates instead of picking.

The directory does not aggregate a member's tasks. Each record's follow_up points at the task filter that does: wallfacer tasks list --created-by <agent-id> for an agent, --owner-user-id <user-id> for a person. role_page_id points at the handbook page describing what that agent is for.

Chat and run are separate commands with different inputs

wallfacer chat starts a conversation. It sends a prompt and the agent you picked, and it never sends a playbook.

wallfacer chat jin "Look at the failing build on develop"
cat brief.md | wallfacer chat @auggie     # prompt from stdin

--title, --environment-id, --harness, --model, --effort, and --idle-timeout are optional; the agent's own environment is the default. Naming a human member, a paused agent, or a disabled one is refused before a task is created, with the command that lists eligible agents.

wallfacer run starts a playbook run. It sends the playbook and an optional kickoff message, and it never sends a prompt.

wallfacer run <playbook-id> --message "Start with the checkout regression"
wallfacer run https://app.wallfacer.ai/accounts/<account-id>/handbook/<playbook-id>

The run does not name a version. The server runs the playbook's active published version and resolves each step's performer from that version's configured actors. --agent sets the task identity and its default environment, and does not override the step actors. A playbook with only a draft is refused, and so is an archived one. The CLI refuses both before the request is sent and names the state it found, and the API refuses them too: POST /tasks validates the playbook against the account, against archived_at, and against the active version.

A disabled playbook runs. Disabling clears a playbook's triggers so no event spawns a task from it, and a manual run is the deliberate way to fire one whose triggers are switched off; the API and the app treat it the same way. The result carries the playbook reference with state: "disabled", plus a disabled_note saying the triggers are cleared and pointing at handbook update-playbook --enable, so the caller can see what they fired.

Both commands return the created task under data and a follow_up with the task reference:

task      wallfacer tasks get <task-id>
sessions  wallfacer sessions list <task-id>
messages  wallfacer messages list <task-id> <session-id>
reply     echo '{"content":"..."}' | wallfacer messages create <task-id> <session-id>

A run's follow_up adds playbook and version, so the definition the task is executing is one command away. The two modes are also apparent at the top level: a chat carries mode: "chat" and agent, a run carries mode: "playbook", the resolved playbook reference, step_actors, and agent when --agent was given. The task record itself distinguishes them too: a chat task has a prompt and a null pipeline_id, a run has pipeline_id, pipeline_version_id, pipeline_version, and an origin.playbook_name.

End to end: from a search to a running task

Find the playbook, read what it actually says, pick an agent, then start either mode.

# 1. Find it, and take the stable ID.
wallfacer handbook search "bug" --type playbook
wallfacer handbook resolve "Fix a Triaged Bug" -q 'data.id' --raw

# 2. Read the published definition and the knowledge it delivers to its runs.
wallfacer handbook read <playbook-id>
wallfacer handbook read <playbook-id> -q 'reference.linked_page_ids'
wallfacer handbook read <linked-page-id> -q 'data.body' --raw

# 3. Pick who does the work.
wallfacer team list --type agent -q 'data[].handle'

# 4a. Talk to that agent.
wallfacer chat jin "Reproduce the checkout regression on develop"

# 4b. Or run the playbook, which uses its own active version and step actors.
wallfacer run <playbook-id> --message "Start with the checkout regression"

# 5. Follow either task.
wallfacer tasks get <task-id>
wallfacer sessions list <task-id>
  • Playbooks for what a playbook is, how steps are performed, and what a run looks like in the app.
  • Agents for the team directory, agent identities, and credentials.
  • Tasks and Sessions for the task and session resources these commands return.