Skip to content

For agents

Every command takes --json. With --json, or when stdout or stdin is not a terminal, the CLI prints no colour, no spinner and no prompt, so an agent never blocks on stdin. Human warnings go to stderr; in JSON mode they travel inside the data instead and stderr stays quiet.

stdout carries exactly one JSON object per command (except watch --json --follow, below):

{"v":1,"ok":true,"data":{"intent":{"id":"i269d1c348e","status":"open"}}}
{"v":1,"ok":false,"error":{"code":"not_logged_in","message":"not logged in; run 'wayglass login --worker <url>' or set WAYGLASS_URL and WAYGLASS_KEY"}}

v changes only for breaking changes. data is present only when ok is true, error only when it is false. Branch on error.code, not on the message.

Exit Meaning
0 OK. After propose: the change is approved, or waiting for a reviewer agent
1 Error
2 Usage error (bad flag, missing argument, placeholder reason)
3 Blocked by policy (or the decision was rejected)
4 A person must act

propose exits with 3 or 4 after printing a successful envelope (ok: true); the exit code carries the routing, the envelope carries the details. approve and reject exit 0 whenever the verdict was recorded.

Codes the CLI sets itself:

Code Meaning
usage Bad arguments or flags (exit 2)
not_logged_in No Worker URL or key
not_a_repo Not inside a git repository
not_initialized No .wayglass/config.yaml; run wayglass init or set WAYGLASS_PROJECT
no_intent No intent given and none current (exit 2)
not_pushed HEAD is not what the intent last pushed; run wayglass push
no_token The Worker did not mint a fork token and WAYGLASS_FORK_TOKEN is unset
no_why why found no note and no recorded decision
policy_invalid The local .wayglass/policy.yaml does not parse
stack_unsupported declare --on reached a Worker that does not stack intents
land_conflict The approved change conflicts with the trunk; nothing was pushed
repropose_required Merged onto the trunk, the change touches files the decision did not cover; nothing was pushed
trunk_moved The trunk moved during the land; nothing landed, run land again
commit_missing The approved commit is not in this checkout
wrong_worker A session URL points at a Worker you are not logged in to
no_session session show found no forked session in this checkout
canceled Interrupted

Errors from the Worker keep the Worker’s code, for example unauthorized, forbidden, not_found, conflict and bad_request.

Variable Effect
WAYGLASS_URL, WAYGLASS_KEY Worker URL and key; override the stored login
WAYGLASS_AGENT Your name in the audit log (default user@host). Give each agent its own
WAYGLASS_PROJECT Project name; overrides .wayglass/config.yaml, or stands in for it
WAYGLASS_INTENT Intent for push, propose, land when no argument is given
WAYGLASS_FORK_TOKEN Fallback fork token, used only if the Worker cannot mint one
WAYGLASS_CONFIG_DIR Where credentials.yaml lives (default ~/.config/wayglass)
Terminal window
export WAYGLASS_AGENT=claude-code-1
# 1. Say what you will do and why. Read data.overlaps before you start.
wayglass intent declare --json \
--goal "Add rate limiting to login" \
--reason "Brute-force attempts in the audit log" \
--path "web/login.ts" --path "worker/src/limits/**"
# 2. Work, commit with the trailer, push. Read data.overlaps[].replan.
git commit -am "feat(login): rate-limit by IP
Brute-force attempts in the audit log.
Wayglass-Intent: i269d1c348e"
wayglass push --json
# 3. Ask policy.
wayglass propose --json
case $? in
0) ;; # approved, or pending_agent: check data.status
3) exit 3 ;; # blocked: change the code, do not retry as is
4) exit 4 ;; # a person must act: stop and report data.next
esac
# 4. Land once data.status is "approved".
wayglass land --json

Rules worth following:

  • Declare every path you expect to touch. Files outside the declared paths come back in propose as undeclared_paths, and other agents’ overlap checks could not see them coming.
  • On exit 4, stop. Report data.decision.id, data.decision.reason and data.next to whoever started you. Do not try to approve your own change; the Worker refuses it.
  • When propose returns status: "pending_agent", the change waits for a reviewer agent. It is not approved yet, and land refuses until it is.
  • After land, data.verified is true when the Worker confirmed the trunk holds exactly the approved change.

agent_review decisions wait for an agent other than the author. A reviewer agent finds them with:

Terminal window
WAYGLASS_AGENT=claude-reviewer wayglass review --for agent --json
{"v":1,"ok":true,"data":{
"project": "acme-web",
"sample": false,
"queue": [
{
"decision": {
"id": "d4b1e2c3a09", "intent_id": "i269d1c348e", "commit": "9f3c2a1...",
"tags": {"areas": ["web"], "data_classes": [], "risk": "low", "jurisdiction": "us", "...": "..."},
"matched_rule": "rules[5]", "action": "agent_review", "status": "pending_agent",
"reason": "Low risk; second agent checks intent vs diff.",
"approvals_needed": 1, "approvals": [],
"files": [{"path": "web/login.ts", "added": 30, "deleted": 4}],
"created_at": "2026-10-13T18:20:11Z"
},
"intent": {
"id": "i269d1c348e", "agent": "claude-code-1",
"goal": "Add rate limiting to login", "reason": "Brute-force attempts in the audit log",
"declared_paths": ["web/login.ts"], "actual_paths": ["web/login.ts"],
"fork": "acme-web--i269d1c348e", "status": "proposed", "jurisdiction": "us",
"created_at": "2026-10-13T18:02:40Z"
}
}
]
}}

The reviewer reads the diff (the intent’s fork, or git show <commit> from a clone), checks it against the intent’s goal and reason, and decides with a reason that says what it checked:

Terminal window
wayglass approve i269d1c348e --json --reason "Diff matches the goal; limiter keyed by IP, tests cover the lockout window"
wayglass reject i269d1c348e --json --reason "Also changes session expiry, which the intent does not mention"

review --json without --for agent lists the person’s queue: human_approve, multi_approve and quarantine decisions still waiting. If data.warnings is present, the Worker could not show everything, and an empty queue does not mean nothing waits.

wayglass watch --json prints one snapshot of the project: counters, intents, overlaps, decisions and recent audit events. Add --follow to keep polling and print one envelope per line (NDJSON) whenever something changes, until interrupted. Poll errors become error envelopes on their own line, and polling continues with backoff. --interval sets the poll interval (default 2s).

The fork token from intent declare --json (data.token.value) is the only token an agent ever sees, and only if it asks for JSON. It is a write token for that intent’s fork and nothing else, valid for 15 minutes. You do not need to keep it: push gets a fresh one from the Worker each time. If you do use it with plain git, pass it as a header through the environment, never in the remote URL or on disk:

Terminal window
GIT_CONFIG_COUNT=1 \
GIT_CONFIG_KEY_0=http.extraHeader \
GIT_CONFIG_VALUE_0="Authorization: Bearer $TOKEN" \
git fetch "$FORK_REMOTE"