Skip to content

dojo CLI v1.1 — fully agent-navigable + headless - #4

Merged
DojoGenesis merged 12 commits into
mainfrom
dojo-cli-v1.1-headless-20260718
Jul 18, 2026
Merged

dojo CLI v1.1 — fully agent-navigable + headless#4
DojoGenesis merged 12 commits into
mainfrom
dojo-cli-v1.1-headless-20260718

Conversation

@DojoGenesis

Copy link
Copy Markdown
Owner

Makes the entire CLI command surface runnable non-interactively with a uniform, machine-parseable contract, and maximizes headless coverage — including the interactive/TUI commands.

Fixes DGS-77

Agent-navigable spine

  • dojo --one-shot "/cmd" runs the command through the dispatcher (was: chatted the literal string to a model). Plain one-shot is headless too.
  • --json emits a stable {ok,command,data,error} envelope; streaming commands emit NDJSON then a terminal envelope.
  • --commands machine-readable catalog (from the registry); exit codes 0 ok / 1 runtime / 2 usage.

Capability + hygiene riders

  • /tools <name> schema · /run --plan <file> · /trail edit · /snapshot export <path> · CAS @ver pinning · unknown-subcommand errors.
  • Native prompt/agent hooks (advisory) with Gateway client injection.
  • Plugin-install integrity: source allowlist + optional --sha256 pin.

Maximum headless (this branch's second half)

  • /warroom — real headless debate: Scout + Challenger as labeled NDJSON + a transcript envelope (plain: linear transcript). Interactive TUI unchanged.
  • /bloom — emits belt/XP/streak/sessions/koan instead of refusing.
  • Consent model--yes lets confirm-gated commands (/craft, /protocol install) run headless; safe default (refuse) preserved. /code undo requires --yolo (not --yes) because it reverts the working tree.
  • /protocol edit --content — edit the overlay with no $EDITOR (- reads stdin for multi-line).
  • Streaming commands (/run, /agent, /workflow, /craft) propagate mid-stream gateway errors to ok:false / exit 1.

Verification

make all green · go test ./... -race all packages · golangci-lint clean on changed non-test code · live against gateway 3.2.2 (envelope, NDJSON, /warroom debate, discovery, exit codes, consent refuse/proceed). Interactive REPL output byte-unchanged.

🤖 Generated with Claude Code

TresPies-source and others added 12 commits July 18, 2026 08:28
… JSON envelope + discovery

Wave 1 foundation for the v1.1 agent-drivability cut (DGS-74 stream). The CLI's
35-command surface was unreachable to any non-interactive caller: --one-shot sent
its string straight to the chat stream, so `dojo --one-shot "/health"` chatted the
literal text "/health" instead of running the command.

- internal/output: new Emitter + stable {ok,command,data,error} Envelope + NDJSON
  seam. Nil-safe; Human mode inert, JSON mode accumulates data / streams lines.
- Registry.RunHeadless: JSON dispatch that redirects os.Stdout during the handler
  (stray fmt.Printf becomes a data.text fallback, no JSON corruption) and wraps the
  terminal envelope. printKV/colorStatus consult a package emitter, so ~119 printKV
  sites populate `data` for free with zero per-site change.
- cmd/dojo/main.go: A1 — one-shot "/cmd" now routes through Registry.Dispatch;
  --commands emits the machine-readable catalog; exit codes 0 ok / 1 runtime /
  2 usage (unknown command, config).
- Registry.Commands(): discovery accessor — one source of truth for the catalog.
- docs/v1.1-scout-dossier.md: the informing scout (3-agent recon synthesis).

Verified: go test ./... -race green; REPL output byte-unchanged; live against
gateway 3.2.2 — /health --json returns a structured envelope, unknown → exit 2,
--commands --json lists 36 commands.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nt injection

The `prompt` and `agent` hook types were parsed but no-op'd (warn + return nil).
They now execute against the Gateway: `prompt` buffers a ChatStream, `agent` does
CreateAgent + AgentChatStream. Advisory-only by design — they run and log but
cannot veto the caller; only `command` hooks retain the blocking gate (unchanged).

- inject *client.Client into hooks.Runner (it held only []plugins.Plugin); the
  switch-case was trivial, the wiring was the work. hooks.New(plgs) → New(plgs, gw)
- nil-gw guard falls back to the prior warn-once no-op (tests unaffected)
- small opt-in payload templating ({{.command}}, {{.prompt}}); output via log
  (stderr), never stdout, so it can't corrupt a --json stream
- HookDef.Model honored for prompt; forward-compat no-op for agent (gateway
  ignores AgentChatRequest.Model today) — documented, not blocked
- docs(TODO): flip the stale DAG-polling line (shipped as pollDAGUntilTerminal)

Part of the v1.1 agent-navigable cut (DGS-77). Advisory-only leaves the
veto-semantics decision for a future pass; noted in the follow-up.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ers, integrity

Wave 2 of the v1.1 agent-navigable cut (DGS-77): every command now emits the
{ok,command,data,error} envelope in --json mode (NDJSON for streamers), plus the
capability + hygiene riders. Built by 4 parallel tracks against the Wave-1 seam,
integrated + verified together (make all green, live vs gateway 3.2.2).

Output conversion (~23 command files):
- list/table commands emit structured data via r.out.Data(...); printKV-based
  ones inherit data from the Wave-1 foundation; streaming commands (/agent,
  /workflow, /run, /pilot, /craft, /skill package-all) emit NDJSON via r.out.Emit
- TUI-only /warroom, /bloom return an "unsupported in headless" envelope; /home,
  /pilot fall back to plain; stdin-blocking commands (/plugin install, /protocol
  install, /craft prune·elevate·scaffold, /code undo) refuse cleanly unless --yolo

Capability riders:
- B1 /trail edit <id> <text>; B2 /tools <name> input-schema inspector
- B3 CAS pinning /skill get <name>@<ver>, package-all <dir> [ver]
- B5 /run --plan <file> (hand-authored ExecutionPlan, no chat fallback)
- B6 /snapshot export <id> [path]; D1 unknown-subcommand errors on 5 commands
- C2 plugin-install integrity: source allowlist + optional --sha256 pin, never
  bypassed by --yes (internal/plugins/installer.go)

Integration fixes (main thread):
- headless dispatch seeds a session id (orchestration/agent need session_id)
- /run propagates a mid-stream gateway error to ok=false / exit 1 (was ok=true)
- errcheck: discard hash/Fprint returns (installer.go, runner_test.go)
- docs: /help table + CHANGELOG for the new modes
- tests: internal/output envelope/NDJSON tests, headless dispatch + discovery tests

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ithout --yolo

Foundation for the headless-maximization pass. headlessRefuse is now consent-aware:
a command reaches its y/N prompt in headless mode only after headlessRefuse lets it
past, which now requires explicit consent (--yes, this run only; narrower than the
blunt --yolo which also skips the permissions gate). New r.autoConfirmed() (==
r.headless) is the guard commands use to treat a prompt as pre-answered when
consented — `if !r.autoConfirmed() && !confirm() { … }`.

Safe default preserved: headless WITHOUT --yes/--yolo still refuses. Wiring
autoConfirmed() into each interactive command's gate is a follow-on track.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
A plain --one-shot "/cmd" (without --json) called Dispatch directly and never
marked the registry headless, so /warroom, /bloom and other alt-screen commands
tried to open a TTY (and failed) instead of using their plain-text headless
branch. RunHeadless already sets this for --json; the plain path now does too.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Mirrors the /run fix: a mid-stream SSE error event is captured and the command
returns non-nil, so the headless envelope reports ok=false / exit 1 instead of a
false ok=true, while still emitting the NDJSON error line. (/pilot stays a live
monitor — error events are content, not a command failure.)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…eam fidelity

Wires r.autoConfirmed() into the confirmation gates so a consented headless run
proceeds instead of dying at the permission gate / hanging on a y/N read:
- /code undo, /craft adr, /craft claude-md, /craft scaffold: permissionGate now
  receives autoConfirmed() instead of a hardcoded false
- /craft memory prune, /craft seed elevate: craftConfirm guarded by !autoConfirmed()
- craftStream: drop the out.Len()==0 guard that suppressed a mid-stream error once
  any text had streamed — a partial-then-error run now reports ok=false too

Safe default preserved: without --yes/--yolo these still refuse headlessly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…EDITOR

Adds --content <text> / --content=<text> / --content - (stdin) so the protocol
overlay is editable non-interactively. Explicit opt-in only — never auto-reads
stdin (which would hang a headless caller). No --content falls through to the
unchanged interactive $EDITOR flow; protocolEdit stays byte-identical so its
existing tests are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ript envelope

Replaces the 'interactive-only, unsupported in headless' refusal with a real
headless debate. New tui.RunWarRoomHeadless runs the same two agent streams the
TUI uses (Scout 'measured' + Challenger 'adversarial' via streamAgentToChannel),
serializing onChunk so NDJSON lines never interleave. --json streams
{stream:warroom,agent,content} per chunk then a {topic,scout,challenger}
envelope; plain headless prints a linear transcript. Interactive TUI unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
/bloom's animation has no terminal headless, but it visualizes real spirit
state, so a headless dispatch now emits belt/xp/streak/sessions/next-belt/koan
(structured data in --json, a plain summary otherwise) rather than refusing. The
interactive animated TUI is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ang)

The y/N gate only skipped on the per-command noConfirm, so a headless run with
global --yes passed headlessRefuse and then hung on the stdin read. Gate it on
!autoConfirmed() too, matching the /craft and /code undo pattern.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ng tree)

/code undo reverts all unstaged changes — destructive enough that a broad
per-run --yes should not fire it. New headlessRefuseStrict requires --yolo (or
the REPL); --yes alone is refused. Other confirm-gated commands keep the
narrower --yes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@DojoGenesis
DojoGenesis merged commit 8ca9dee into main Jul 18, 2026
2 checks passed
@DojoGenesis
DojoGenesis deleted the dojo-cli-v1.1-headless-20260718 branch July 18, 2026 15:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants