docs/tui-architecture.md

CLI & TUI Architecture

Source Specs: cli-tui-beautify-plan.md, cli-tui-production-readiness.md, docs/10-tui.md, docs/architecture.md, docs/TUI_TERMINAL_EQUIVALENCE.md

The Picasso CLI (apps/cli) and interactive TUI (packages/tui) run on the shared engine (pinned to 0.2.2-rc.1, kept current by the background engine updater) over a line-delimited JSON-RPC stdio transport managed by packages/core/src/dsh/runtime-session.ts and packages/picasso-sdk-server.

---

1. End-to-End Execution Path

  1. CLI Entry (`apps/cli/src/index.ts`): Startup runs in a fixed order (H-C14): set the runtime asset root (<pkg>/runtime in the npm package, else the repo resolver; PICASSO_RUNTIME_ASSETS_DIR overrides), load the signed-in session from the shared Keychain item (primeLoginSession), read GET /v1/client-config and enforce the minimum CLI version (skipped for update, auth, help, --help, --version), run the first-run engine install when the packaged CLI has no pinned engine (ensureFirstRunRuntime), then parse the subcommand (picasso, picasso run/-p, attach, auth, mcp, memory, settings, cache, skills, update, …). Every error goes through one top-level handler that prints picasso: <message> and exits with the CliError code (1 failure, 2 usage, 3 auth/no provider); a stack prints only with PICASSO_DEBUG=1. --permission-mode safe|auto|yolo|paranoid applies to run/-p as well as the TUI; without it the mode comes from the shared settings file (default auto).
  2. Runtime Session (`packages/core/src/dsh/runtime-session.ts`):
  1. SDK Server Replacement (`packages/picasso-sdk-server/index.js`):
  1. Event Projection (`packages/core/src/dsh/project.ts` & `contract.ts`):
  1. TUI Surface (`packages/tui`):

---

2. Key Invariants

---

3. Account, sync and attach surfaces

SurfaceCodeBehaviour
picasso auth login [--accept-terms] / logout / status [--json]auth-provider-commands.ts, login-store.tsDevice-code sign-in. The token lives in the Keychain item com.picassohq.account-session (account = API origin) shared with Picasso for Mac, written through security -i on stdin. Terms are accepted at a [y/N] prompt or with --accept-terms; a non-interactive login without it exits 2. Signed out, expired, revoked or suspended exit 3. See docs/11-auth-and-accounts.md.
401 / 403 mappingapi-client.ts401 deletes the local token and fails Signed out — run \picasso auth login\`; 403 account_suspended keeps it and fails Your Picasso account is suspended. Contact support@picassohq.com.` (both exit 3). The TUI shows the same strings.
picasso attach --macrelay-attach.ts (RelayAttachSession)Reads the discovery file ~/.picasso/run/mac-helper.json (0600, in a 0700 dir, owner-checked; {version, pid, url, token, startedAt}) written by the running Mac helper, and talks to its loopback relay with the scoped local-attach token in the authorization header. No flags: list the Mac threads. --session <id> --prompt <text>: send a prompt and stream the reply until that turn ends. --session <id> --cancel: stop the current turn. `--interaction <id> --decision allow\allow-session\deny: answer a pending approval. --json for machine output. A bare picasso attach with no PICASSO_REMOTE_HOST also uses the Mac. When the app is not running it prints Picasso for Mac isn't running. This is a non-interactive surface: the interactive TUI attach to a Mac thread is not built yet (H-C22); picasso attach <host>` still opens the TUI against a remote Host.
picasso host …, picasso attach --pair <CODE@host:port#fp> / --host <name>host-commands.ts, host-pairing-client.ts, host-attach.tsRun this machine as a headless host the Mac app and other CLIs pair with directly (no cloud), and pair with one. Security model and commands: docs/mac-relay.md § Host mode. /doctor in the TUI lists paired hosts (online, version, folders).
`picasso settings set sync.cloudSessions on\off, sync.cloudMemory on\off`settings-commands.tsWrites ~/.picasso/settings/settings.json (shared with the Mac, locked writes). Both default off. With sync.cloudSessions on and a signed-in account, each finished run uploads the session (CloudSessionSyncClient.upload); a failure is recorded as a cloud-session-sync-failed event and never fails the turn. sync.cloudMemory is read by the Mac helper; the CLI syncs memory only through picasso memory sync.
`picasso memory key status\export\import\link`memory-key-commands.tsThe account memory key that encrypts synced memory, in the Keychain item com.picassohq.memory-key. export prints the recovery string (a secret) with a warning; import reads it from stdin or a hidden prompt (never argv); link --request on a new device shows six words and waits, link --approve on a device with the key seals it to the requester after the words match.
Cloud session list—picasso list lists local sessions. picasso session list --cloud lists the sessions synced to your account (GET /v1/sessions, needs settings sync.cloudSessions on); picasso session cloud show <id> prints one read-only and picasso session cloud continue <id> starts a local session from it (U3-2).

4. PR lifecycle watch

apps/cli/src/pr-watch.ts runs packages/pr-watch inside the TUI for the sessions live in this process: it follows each session's turns (session.status / turn/start / turn/end notifications), scans the finished turn's engine log for gh pr create, and /pr confirm registers the PR it opened. Wakes go through the session's own queue (session/queue: followup while "Wake threads on PR updates" is on, steer into the next turn while it is off). The header and sidebar show the watched PR's status (backend.loadPrStatus, polled every 15 s). /pr status, /pr watch <n> and /pr unwatch <n> work in the TUI; picasso pr watch <n> --session <id>|--last [--once] and picasso pr status work from a shell. The watch list is the shared <picassoHome>/pr-watch.json, so a watch outlives the process that started it (the next TUI, picasso pr watch, or the Mac relay resumes it). picasso settings set wakeOnPrUpdates on|off toggles waking. Tests stub gh on PATH.

5. TUI surfaces added since 2026-10-28

SurfaceCodeBehaviour
Queue, steer and interruptpackages/tui/src/queue.tsx, composer.tsxWith text, Enter queues while a turn runs (or steers, per the shared enterWhileRunning setting, `picasso settings set enterWhileRunning queue\steer`). In an empty composer Enter sends the selected, else the oldest, queued message into the running turn now; the row reads "queued · Enter sends now". ⌃Q interrupts. A steered message renders as "› steered …" in the user's style; a shell command that exited non-zero shows ✕.
Decisions and optionspackages/tui/src/decisions.tsTwo or more asks in one turn become one "Needs you · N decisions" block answered by number; approvals take 1/2/3 (a/s/d still work). The deferred present_options tool shows 2–4 options as a numbered list (←/→ or n/p, o opens one).
Resume pickerpackages/tui/src/screens/sessions.tsx, project-identity.tsGroups Needs you / Working / Recent / Settled; e archives, u undoes. Each project shows the same initials tile as the Mac app (AuraOne → AO, my-web-app → MW) in a distinct palette colour; nothing is read from project folders.
Start pagepackages/tui/src/start-page.ts, activity-heatmap.ts/activity [github] [year] (threads heatmap, or GitHub/GitLab contributions from the connected repo host), /customize (show, hide and reorder sections, shared with the Mac app in settings.startPage), Recent with a "This month" footer line.
Background workpackages/tui/src/background-jobs.ts, next-recent.tsx/tasks [kill <jobId>] lists the engine's background jobs; `/librarian [pause\resume\look-all\budget <tokens>] and /updates` show the librarian and background engine updates as Next / Recent lists.
Resident runner and schedulesapps/cli/src/service-commands.ts, runner-schedules.ts`picasso service install\uninstall\status installs a LaunchAgent runner (~/Library/LaunchAgents/com.picassohq.cli.runner.plist) so threads and schedules keep going after the terminal closes; it installs nothing when the Mac app's runner is up. /schedules (alias /schedule) needs a runner and says how to turn one on; /settings` shows "Keep threads running when Picasso quits: on/off".
Context timeline, My Work, Xapps/cli/src/tui-backend.ts/context timeline (per-turn context additions, compaction, cache hit/miss, subagent lanes; also picasso context timeline [thread] [--last] [--json], which needs no engine or model call); /work lists the files sessions made (`open\copy\similar <n>); /x` reads public X through the third-party x.md service.
Themes and motionpackages/tui/src/color-themes.ts, ui-settings.ts/theme picks one of 13 presets shared with the Mac app (or a custom theme); --no-animation / PICASSO_REDUCED_MOTION=1 or Settings › Appearance › motion stop animation, and --accessible / PICASSO_A11Y=1 gives plain output.
Tour and footprintpackages/tui/src/tour.ts/tour is a short paged tour of the main keys; /about shows what Picasso puts on this computer and /about prompt prints the install-by-agent prompt.