@mu-crew/mu 3.13.3 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/AGENTS.md +73 -35
  2. package/README.md +71 -57
  3. package/dist/chunk-5HECDBK3.js +4482 -0
  4. package/dist/chunk-5HECDBK3.js.map +1 -0
  5. package/dist/chunk-AW2NONYU.js +301 -0
  6. package/dist/chunk-AW2NONYU.js.map +1 -0
  7. package/dist/chunk-GQNHIPJN.js +4060 -0
  8. package/dist/chunk-GQNHIPJN.js.map +1 -0
  9. package/dist/{chunk-3BARPJLY.js → chunk-PZ5GOCWH.js} +16 -2
  10. package/dist/chunk-PZ5GOCWH.js.map +1 -0
  11. package/dist/chunk-S424D5EP.js +13766 -0
  12. package/dist/chunk-S424D5EP.js.map +1 -0
  13. package/dist/{cli-CKOMHBXV.js → cli-GB2CAVVW.js} +6776 -4350
  14. package/dist/cli-GB2CAVVW.js.map +1 -0
  15. package/dist/cli.js +2 -3
  16. package/dist/cli.js.map +1 -1
  17. package/dist/errors-KHOYKD7D.js +65 -0
  18. package/dist/extension/mu-pi.js +1617 -836
  19. package/dist/extension/mu-pi.js.map +1 -1
  20. package/dist/index.d.ts +4015 -2561
  21. package/dist/index.js +614 -297
  22. package/dist/index.js.map +1 -1
  23. package/dist/tui-ALPHYTA2.js +1773 -0
  24. package/dist/tui-ALPHYTA2.js.map +1 -0
  25. package/docs/ARCHITECTURE.md +117 -66
  26. package/docs/VOCABULARY.md +112 -49
  27. package/docs/guide/README.md +3 -3
  28. package/docs/guide/backends.md +8 -4
  29. package/docs/guide/cleanup.md +61 -33
  30. package/docs/guide/delegate.md +141 -66
  31. package/docs/guide/dispatch.md +148 -53
  32. package/docs/guide/getting-started.md +37 -44
  33. package/docs/guide/other-agent-clis.md +102 -0
  34. package/docs/guide/recovery.md +31 -50
  35. package/docs/guide/remote.md +115 -45
  36. package/docs/guide/sql.md +104 -24
  37. package/docs/guide/stop-a-worker.md +30 -37
  38. package/docs/guide/sync.md +11 -3
  39. package/docs/guide/upgrade.md +93 -43
  40. package/docs/reference/env.md +23 -32
  41. package/docs/reference/naming.md +12 -7
  42. package/docs/reference/surface-contract.md +153 -0
  43. package/package.json +33 -11
  44. package/prompts/mu-debug.md +3 -1
  45. package/prompts/mu-refute.md +2 -1
  46. package/prompts/mu-research.md +3 -1
  47. package/prompts/mu-review.md +2 -1
  48. package/prompts/mu-rules-audit.md +5 -4
  49. package/prompts/mu-sweep.md +2 -1
  50. package/prompts/mu-tournament.md +4 -2
  51. package/prompts/mu-until.md +3 -1
  52. package/prompts/ultrathink.md +1 -1
  53. package/skills/mu/SKILL.md +60 -291
  54. package/skills/mu/recipes/adversarial-review.md +12 -103
  55. package/skills/mu/recipes/brief.md +14 -61
  56. package/skills/mu/recipes/course-correct.md +23 -0
  57. package/skills/mu/recipes/findings.md +17 -111
  58. package/skills/mu/recipes/models.md +17 -92
  59. package/skills/mu/recipes/move.md +14 -0
  60. package/skills/mu/recipes/orchestrator-loop.md +20 -119
  61. package/skills/mu/recipes/recovery.md +8 -29
  62. package/skills/mu/recipes/remote-workers.md +16 -318
  63. package/skills/mu/recipes/tasks-or-calls.md +15 -78
  64. package/skills/mu/recipes/test-health.md +19 -77
  65. package/skills/mu/recipes/worker.md +17 -24
  66. package/dist/chunk-45KQLMEL.js +0 -2908
  67. package/dist/chunk-45KQLMEL.js.map +0 -1
  68. package/dist/chunk-G434JKV2.js +0 -641
  69. package/dist/chunk-G434JKV2.js.map +0 -1
  70. package/dist/chunk-KUGUFBWH.js +0 -12527
  71. package/dist/chunk-KUGUFBWH.js.map +0 -1
  72. package/dist/chunk-QITLNZEM.js +0 -87
  73. package/dist/chunk-QITLNZEM.js.map +0 -1
  74. package/dist/chunk-VIEYQMQA.js +0 -393
  75. package/dist/chunk-VIEYQMQA.js.map +0 -1
  76. package/dist/chunk-WI6AWCMK.js +0 -19
  77. package/dist/chunk-WI6AWCMK.js.map +0 -1
  78. package/dist/cli-CKOMHBXV.js.map +0 -1
  79. package/dist/devtools-LS4ZEPWM.js +0 -3772
  80. package/dist/devtools-LS4ZEPWM.js.map +0 -1
  81. package/dist/tui-LFJ5P4Y2.js +0 -40956
  82. package/dist/tui-LFJ5P4Y2.js.map +0 -1
  83. package/dist/tui-sync-worker.js +0 -17
  84. package/dist/tui-sync-worker.js.map +0 -1
  85. package/dist/yank-XDOONQSL.js +0 -13
  86. package/dist/yank-XDOONQSL.js.map +0 -1
  87. package/docs/ROADMAP.md +0 -123
  88. package/docs/VISION.md +0 -298
  89. package/docs/architecture/control-socket.md +0 -142
  90. package/docs/architecture/dag.md +0 -117
  91. package/docs/architecture/mux.md +0 -167
  92. package/docs/architecture/ops-log.md +0 -160
  93. package/docs/architecture/sdk.md +0 -187
  94. package/docs/architecture/sync.md +0 -159
  95. package/docs/architecture/tui.md +0 -165
  96. package/docs/guide/tui.md +0 -98
  97. package/docs/history/CHANGELOG-pre-3.md +0 -4866
  98. package/docs/img/tui-dashboard.png +0 -0
  99. package/docs/specs/2026-09-28-agent-state-from-murmur.md +0 -333
  100. package/docs/specs/2026-09-29-task-substates.md +0 -247
  101. package/docs/specs/2026-10-03-keep-driving-nudge.md +0 -163
  102. package/docs/specs/2026-10-21-fast-and-slow-gates.md +0 -229
  103. package/skills/mu/recipes/backlog-triage.md +0 -45
  104. package/skills/mu/recipes/codemode-driver.md +0 -118
  105. package/skills/mu/recipes/deep-research.md +0 -69
  106. package/skills/mu/recipes/drift-audit.md +0 -75
  107. package/skills/mu/recipes/fan-out.md +0 -42
  108. package/skills/mu/recipes/hypothesis-panel.md +0 -48
  109. package/skills/mu/recipes/long-run.md +0 -32
  110. package/skills/mu/recipes/loop-until-done.md +0 -45
  111. package/skills/mu/recipes/plan.md +0 -67
  112. package/skills/mu/recipes/refute.md +0 -55
  113. package/skills/mu/recipes/review-panel.md +0 -65
  114. package/skills/mu/recipes/rules-audit.md +0 -89
  115. package/skills/mu/recipes/tournament.md +0 -44
  116. package/skills/mu/recipes/ultrathink.md +0 -86
  117. package/skills/mu/recipes/watcher.md +0 -28
  118. package/skills/mu/recipes/waves.md +0 -21
  119. /package/dist/{chunk-3BARPJLY.js.map → errors-KHOYKD7D.js.map} +0 -0
@@ -1,159 +0,0 @@
1
- # Sync
2
-
3
- Machines share a workstream by exchanging ops through a shared folder
4
- (`MU_SYNC_DIR`) kept in step by any folder syncer, such as Syncthing. The ops
5
- themselves are described in [ops-log.md](ops-log.md). Overview:
6
- [ARCHITECTURE.md](../ARCHITECTURE.md). Why segments and not
7
- Litestream, cr-sqlite or a peer list, and why the DB must never sit in
8
- `MU_SYNC_DIR`:
9
- [ROADMAP § Rejected sync substrates](../ROADMAP.md#rejected-sync-substrates).
10
-
11
- ## Segments
12
-
13
- `src/segments.ts` is the transport.
14
-
15
- - `flushSegment` appends this machine's unflushed ops to
16
- `<MU_SYNC_DIR>/<machine_id>.jsonl`.
17
- - `ingestSegment` reads a peer segment from its watermark into
18
- `applyOp`. Ambient ingest skips a peer that is caught up: the manifest
19
- counts the watermark, and the segment's size, exact mtime and
20
- manifest sha256 equal what the last clean read to the end recorded
21
- (in `sync_fingerprints`). Any change, including a rewrite with a
22
- backdated mtime, fails that check, so the full read hashes the file
23
- and reports the mismatch. `mu sync` always reads every line.
24
- - When a peer segment has grown, ambient ingest resumes: a clean read
25
- saved the byte offset and running sha256 state with the fingerprint
26
- (`src/segment-resume.ts`), so only the appended bytes are read, and
27
- they count only if the resumed hash equals the manifest's sha256. A
28
- shrunk file, a stale manifest, a defect, or `mu sync` takes the full
29
- read and whole-file verify. The resumed hash trusts the bytes it
30
- already read, so a local change to that prefix (with the file still
31
- growing) is caught only by `mu sync`.
32
- - Peer discovery is implicit: every non-self `*.jsonl` is a peer. A
33
- peer disappears only when its segment is deleted.
34
- - A Syncthing conflict copy (`<machine>.sync-conflict-….jsonl`) is
35
- ingested too, with its own watermark keyed by the file stem: it can
36
- diverge from the original, so line counts are not comparable.
37
-
38
- **Single writer per file.** A machine appends only to its own segment,
39
- so nothing is contended and any folder syncer is adequate. Segments
40
- are regenerable, so there is no fsync. `src/file-lock.ts` stops two
41
- local `mu` processes from interleaving partial lines. A flush never
42
- appends without that lock: if another process still holds it after
43
- 2 s (15 s for `mu sync`), the flush writes nothing, the ops stay
44
- pending in `ops`, and the next invocation appends them. A stale lock
45
- (holder crashed, older than 30 s) is broken as before.
46
-
47
- **Only `SYNCED_ENTITIES` and only this machine's ops are flushed.**
48
- Pane ids and absolute paths never leave the machine, and peers never
49
- echo each other's history.
50
-
51
- **Payload encoding.** `ops.payload` is TEXT that is usually JSON, but
52
- `mu log "text"` writes prose. The encoder passes valid JSON through
53
- verbatim, so the crc covers those exact bytes, and JSON-encodes
54
- anything else, unwrapping on ingest.
55
-
56
- ## Robustness layers
57
-
58
- The first three checks stop ingest at the first bad record and advance
59
- the watermark only that far. The fourth is whole-file: it reports the
60
- defect without halting, because every line still present is valid, and
61
- only a fresh copy of the file clears it:
62
-
63
- | Layer | Catches |
64
- | --- | --- |
65
- | `JSON.parse` | a torn write (line lacks its trailing `,"crc"…}` framing) or a malformed shape (framing present). Same failure, different remediation: a cut transfer vs a writer bug |
66
- | crc32 per line | bit rot |
67
- | monotonic HLC | reorder, duplicate, truncation |
68
- | `.manifest` sidecar | truncation exactly on a line boundary |
69
-
70
- **Damage halts; refusal skips.** Halting is right for damage, where a
71
- gap looks the same as reordering. It is wrong for a well-formed line
72
- mu declines to project: a refusal leaves no hole, and halting would
73
- freeze the peer's watermark for good, since `mu sync --repair` only
74
- resets the watermark and re-reads into the same line. Ingest
75
- distinguishes three cases:
76
-
77
- | Line | Result |
78
- | --- | --- |
79
- | damaged (including a blank line) | halt |
80
- | a known machine-local entity | reported defect, skipped |
81
- | a historical log-only intent (`workstream.export` prose from mu < 1.1) | skipped silently, as flush and `--from` skip it |
82
- | an unrecognised entity | applied as a no-op, no defect (a reader behind the writer is normal in a mixed fleet) |
83
-
84
- `mu sync` suggests `--repair` only for defects a re-read can clear.
85
-
86
- **Sync never fails a command.** A truncated segment, a garbage segment,
87
- a sync dir that is a file, or a vanished directory warns on stderr and
88
- returns.
89
-
90
- ## The ambient hook
91
-
92
- Sync is ambient, not a daemon: no watcher, no background process, no
93
- poll outliving a command. It happens because you already run `mu`
94
- constantly.
95
-
96
- The seam is `handle()` (`src/cli/handle.ts`), which every verb passes
97
- through and which is already async. It awaits `ambientIngest` and
98
- `ambientFlush`; the flush is async because it takes the file lock,
99
- while most verb bodies are synchronous
100
- better-sqlite3 code. One `await` before `fn(db)` and one after cover
101
- every verb.
102
-
103
- - **Ingest before the body**, so the verb reads the freshest state.
104
- - **Flush after the body**, so this invocation's ops reach the segment
105
- now.
106
- - **Flush on the error path too.** A verb may commit ops before
107
- throwing. Those ops are canonical, so withholding them would make
108
- this machine's history depend on the exit code.
109
-
110
- `src/sync.ts` holds `ambientIngest`, `ambientFlush`, `ambientSyncPass`,
111
- `peerStatuses`, `ingestFromDb` (`mu sync --from <path>`) and
112
- `repairPeer` (`mu sync --repair <peer>`, a unique prefix; ambiguity is
113
- exit 4). A prefix names a machine, so it resets the watermark of the
114
- peer's segment and of every conflict copy of it.
115
-
116
- ### Carve-outs
117
-
118
- | Surface | Behaviour | Why |
119
- | --- | --- | --- |
120
- | `MU_SYNC_DIR` unset | one `if` on one env var; no filesystem touch | the single-machine case pays nothing (measured: baseline; about 3ms with one peer) |
121
- | `mu sql` | opts out (`handle(..., { ambientSync: false })`) | an ingest changing a row count mid-inspection reads as a mu bug |
122
- | `mu sync` | opts out and runs the pass itself | otherwise the report prints "ingested 0" right after ingesting |
123
- | the TUI | slow tick only (10s), one pass per beat across tabs, `quiet: true` | the fast tick can go to 100ms; the TUI owns the screen, so problems show on the Doctor card |
124
-
125
- ## Out-of-order arrival
126
-
127
- An `edge` or `note` put whose task has not arrived yet skips as
128
- `absent`, correctly. Ingest still advances the watermark past it, so
129
- without a second pass it would never land. Peers are discovered by
130
- `localeCompare` over random-UUID filenames, so the arrival order is a
131
- coin flip per fleet.
132
-
133
- `reprojectDeferredOps` (`src/apply.ts`) re-queries the log for note and
134
- edge puts that are resolvable now but unprojected. It skips ops whose
135
- parent task is gone and keys with a newer `del`, so a deleted edge is
136
- never resurrected and an orphan is not retried forever. It runs once
137
- per ingest pass, not per peer, because an edge in one segment may name
138
- a task in another. Ambient ingest runs it only while SQLite's
139
- `user_version` is 1. `applyIncomingOp` sets that marker in the same
140
- transaction whenever an op changes a row or defers (`absent`), whether
141
- it came from a segment, `--from`, or an SDK caller; the repair clears
142
- it. A process that dies between
143
- the two leaves the marker set, so the next invocation runs the repair.
144
- `mu sync` and `--from` always run it.
145
-
146
- There is no retry queue. A queue would be a second source of truth and
147
- would not survive a short-lived `mu` process when the parent arrives
148
- days later.
149
-
150
- ## Mixed-fleet hazards
151
-
152
- `src/fleet-hazards.ts` adds three checks to the default `mu doctor`,
153
- all no-ops when `MU_SYNC_DIR` is unset:
154
-
155
- | Check | Severity |
156
- | --- | --- |
157
- | `MU_DB_PATH` inside `MU_SYNC_DIR` | fail |
158
- | DB on a network mount | warn |
159
- | two workstream names differing only by case | warn |
@@ -1,165 +0,0 @@
1
- # TUI architecture
2
-
3
- The TUI is a read-only, live-updating dashboard of 10 cards on `ink`.
4
- It lives entirely under `src/cli/tui/`, the only place ink and react
5
- are imported (a [ROADMAP](../ROADMAP.md#anti-feature-pledges) pledge).
6
- CLI verbs stay the mutation API: the TUI yanks `mu` commands and the
7
- operator runs them. Overview: [ARCHITECTURE.md](../ARCHITECTURE.md).
8
- Press `?` in the TUI for the keymap.
9
-
10
- ## Entry points
11
-
12
- - Bare `mu` launches the TUI when `process.stdout.isTTY` is true. It
13
- loads every workstream and picks the initial tab with the focus
14
- ladder in `src/cli/tui-launch-focus.ts`: `$MU_SESSION` → tmux session
15
- name → cwd inside a workspace → cwd equal to a workspace's project
16
- root (`src/project-root.ts`; latest activity breaks ties) → tab 0.
17
- With no workstreams it prints `mu --help` plus
18
- `Get started: mu workstream init <name>` and exits 0.
19
- - On non-TTY stdout (pipes, CI, most agent calls) bare `mu` prints help.
20
- `MU_NO_TUI=1` forces that path inside a terminal.
21
- - `mu state` is the static card; `mu state --tui` selects the TUI.
22
- - The import stays dynamic (`await import("./cli/tui/index.js")`). A
23
- static import of ink anywhere else would pull the TUI graph into the
24
- help, version and `--json` paths.
25
-
26
- ## Cluster shape
27
-
28
- | Files | Role |
29
- | --- | --- |
30
- | `index.ts`, `escapes.ts` | `runTui` entry; alt-screen and SGR mouse-mode lifecycle; pure escape bytes |
31
- | `app.tsx` | `<App>` root: popup state machine, global keymap, tabs, footer |
32
- | `state.ts` | `useDashboardSnapshot` poll loop (fast and slow tiers) |
33
- | `keys.ts`, `keymap-spec.ts`, `help.tsx` | pure key dispatch; the keymap source of truth that drives both dispatch and the `?` overlay |
34
- | `mouse.ts`, `use-popup-action-queue.ts` | vendored SGR mouse parser with double-click; one queued popup action per render |
35
- | `layout.ts`, `columns.ts`, `wrap-ansi.ts`, `use-terminal-size.ts` | responsive columns and row budgets; aligned clipping; ANSI-aware wrapping; resize hook |
36
- | `popup-input.ts` | `usePopupInput`: popup keyboard, off while the help overlay covers the popup |
37
- | `titled-box.tsx`, `popup-shell.tsx`, `list-row.tsx`, `padded-rows.tsx`, `status-bar.tsx`, `tab-strip*.ts(x)` | chrome primitives |
38
- | `format-helpers.ts`, `agent-display.ts` | shared formatters and agent-row display |
39
- | `use-popup-filter.tsx`, `use-status-filter.tsx`, `use-notes-drill.ts` | `/` filter; status (`o`/`i`/`c`) and substate (`p`/`w`) toggles; shared notes drill |
40
- | `yank.ts`, `tuicr.ts`, `lazygit.ts`, `tmux-attach.ts` | clipboard; the alt-screen handoffs |
41
- | `cards/*.tsx` | the 10 dashboard cards, plus `_placeholder.tsx` |
42
- | `popups/*.tsx` | one fullscreen popup per card, plus `dag.tsx` (`g`), `all-tasks.tsx` (`t`), the `task-list-popup.tsx` scaffold, `drill.tsx`, `task-detail.tsx`, `cursor-row.tsx`, `scroll.ts`, `viewport.ts`, `show-loader.ts` |
43
-
44
- ## State machine
45
-
46
- `<App>` owns:
47
-
48
- - **Popup state:** `null` (dashboard) or one popup id. One popup at a
49
- time; `Esc` or `q` returns to the dashboard. `?` over a popup hides
50
- it (`display="none"`) and turns its keyboard off rather than
51
- unmounting it, so its cursor, filter and drill survive the overlay.
52
- - **Card visibility:** toggled by `0`-`9`.
53
- - **Tick rate:** fast tick, 1s default, adjusted with `+` / `=` and
54
- `-`. There is no reset key: `0` toggles the Commits card.
55
- - **Active workstream tab:** `Tab` / `Shift-Tab` cycles when there are
56
- two or more. A torn-down workstream keeps its tab position and
57
- renders dimmed with strikethrough until the name is recreated. A
58
- torn-down single workstream shows its normally hidden tab.
59
- - **Footer flash:** a transient status-bar message.
60
-
61
- Popups own their local state (cursor, filter, drill mode, local modes
62
- such as Workspaces' `list` / `commits` / `show`). They never mutate App
63
- state. They receive a read-only props bag: `snapshot`, `db`,
64
- `workstream`, `fastTickNonce`, `slowTickNonce`, `yank`, `onClose`,
65
- `onModeChange`, `onFilterEditingChange`, `onFooter`.
66
-
67
- ## Polling tiers
68
-
69
- | Tier | Interval | Loader | Reads | Cost |
70
- | --- | --- | --- | --- | --- |
71
- | fast | 1s, adjustable | `loadWorkstreamSnapshotFast` | SQL only: tasks, tracks, workspace rows, recent events, workspace orphans | p50 < 1ms |
72
- | slow | 10s (`SLOW_TICK_MS`) | `loadWorkstreamSnapshotSlow` | subprocesses: agent liveness and state, workspace dirty status, recent commits, doctor summary | p50 hundreds of ms |
73
-
74
- `mergeSnapshotFastSlow` merges the last slow result into every fast
75
- render, so cards never flicker through loading. `r` / `F5` runs both
76
- tiers now. A tab switch clears the slow cache and fetches eagerly.
77
- `snapshotKey` returns the same `data` reference across no-op ticks, so
78
- React's diffing short-circuits. The slow tick also runs ambient sync
79
- ([sync.md](sync.md#carve-outs)).
80
-
81
- ## Render geometry
82
-
83
- `layout.ts`:
84
-
85
- - **Columns by width:** stacked below 120 columns, then 2 at 120, 3 at
86
- 180, 4 at 240. Stream cards (Commits, Activity log) trail; Commits
87
- trails last.
88
- - **Row budgets:** each card has `min` / `max` / `chrome`. The allocator
89
- distributes rows up to `max` first so a noisy list cannot crowd its
90
- siblings, then gives any spare rows to the cards still hiding data,
91
- in proportion to what each hides. A column with hidden data always
92
- fills its height; short lists are not padded. Overflow shows as
93
- `+N more · Shift+N` in the card's bottom border.
94
- - **Card placement:** `arrangeColumns` starts from the group packing
95
- and moves or swaps cards between columns while that lowers the total
96
- hidden rows (a local optimum, well under 1ms). It keeps the previous
97
- assignment unless the new one hides at least max(3, 10%) fewer rows,
98
- so cards do not jump as counts change. A resize, toggle or tab
99
- switch reflows freely.
100
- - **Culling:** when even minimum budgets do not fit, cards drop by
101
- priority (Doctor → Recent → Workspaces → …) and the screen shows
102
- `+N cards hidden · resize taller`.
103
-
104
- `wrap-ansi.ts` wraps by visual width (`string-width`) and closes open
105
- SGR state on every exit path, so a fragment without a trailing
106
- `\x1b[0m` cannot bleed into ink chrome. Drill bodies are space-padded
107
- to the exact box width, or ink's `wrap="truncate"` ANSI miscount eats
108
- the right border.
109
-
110
- ## Read-only, with handoffs
111
-
112
- Every popup row exposes one canonical `mu` command through `y`.
113
- `yank.ts` probes pbcopy, wl-copy, xclip, xsel and clip.exe, then falls
114
- back to OSC-52 written to `/dev/tty`, since ink owns stdout.
115
-
116
- The handoffs leave the alt screen, disable mouse mode, run a
117
- foreground subprocess, and restore both on exit:
118
-
119
- - `t` in any `git show` drill runs `tuicr -r <sha>` in the project root
120
- or workspace.
121
- - `l` in the Commits popup runs lazygit.
122
- - `a` in the Agents popup attaches to the agent's mux pane.
123
-
124
- A TUI gesture that mutates state needs a ROADMAP entry first.
125
-
126
- ## Input
127
-
128
- Mouse support uses SGR mouse mode. `mouse.ts` parses
129
- `ESC[<button;x;y;M/m`, detects double-clicks, and exposes `useMouse()`.
130
- A double-click on a card opens its popup. A double-click on a popup row
131
- emits one `clickRow` action (the 0-based body row) through the action
132
- queue. The popup maps that row through its own scroll window and
133
- header strips (`clickedItem` in `popups/scroll.ts`), then focuses and
134
- drills the item. App cannot do the mapping: only the popup knows its
135
- window start and strip rows.
136
-
137
- `keys.ts` holds `dispatchGlobalKey` (dashboard), `dispatchPopupKey`
138
- (popup), and `shouldSwallowGlobalKey` (keys a popup consumes).
139
-
140
- ## Drill recursion
141
-
142
- List popups drill with `Enter`. Each popup pins the drilled row's
143
- identity at `Enter` (task, agent, check, commit sha, log seq, track
144
- goals), so a refresh that reorders or extends the list cannot move an
145
- open drill to another row. `DrillScrollView`
146
- (`popups/drill.tsx`) is the shared scrollable text leaf: Workspaces'
147
- git show, Agents' scrollback, Activity log payloads, and Doctor
148
- remediation. Task popups drill into `TaskDetailDrill`
149
- (`popups/task-detail.tsx`, the notes timeline). Tracks chains track →
150
- task list → `TaskDetailDrill`.
151
-
152
- `useDrillKeymap` owns scroll state. An optional `resetKey` resets
153
- scroll on identity change while a tick refresh keeps it, and
154
- `onScrollChange` serves the DAG popup's focused-root tracking. Body
155
- wrap metadata is shared so the scroll clamp and the painter cannot
156
- disagree. Subprocess-backed drills use `popups/show-loader.ts`, which
157
- keeps the prior body during a refetch.
158
-
159
- ## Test seam
160
-
161
- See `test/README.md`. `test/_ink-render.ts` provides
162
- `createInkInputStream`, `createInkCaptureStream`, `simulateInput` and
163
- `latestRenderedFrame`: mount a popup or `<App>`, drive keys, assert on
164
- the visible frame and spy callbacks. Source greps are for narrow
165
- structural guards only, never behaviour.
package/docs/guide/tui.md DELETED
@@ -1,98 +0,0 @@
1
- # How to use the TUI dashboard
2
-
3
- The dashboard is read-only. To change anything, press `y` on a row to
4
- copy its `mu` command, then run it in your shell.
5
-
6
- ## Open it
7
-
8
- ```bash
9
- mu # every workstream, as tabs
10
- mu state --tui -w a,b # only these workstreams
11
- MU_NO_TUI=1 mu # print help instead
12
- ```
13
-
14
- Bare `mu` opens the dashboard only when stdout is a terminal. Scripts
15
- get help text. For a static card or JSON, use `mu state` and
16
- `mu state --json`. Quit with `q` or `Ctrl-C`.
17
-
18
- The first active tab is the first match of: `$MU_SESSION`, the current
19
- `mu-<ws>` tmux session, a workspace that contains the current
20
- directory, a project root that contains it, then the first tab.
21
-
22
- ## Read the cards
23
-
24
- | Key | Card | Shows |
25
- | --- | ---- | ----- |
26
- | `0` | Commits | recent commits in the project root |
27
- | `1` | Agents | agents with runtime state, CLI, and role |
28
- | `2` | Tracks | parallel tracks of tasks |
29
- | `3` | Ready | tasks with no open blockers |
30
- | `4` | Activity log | recent ops as prose |
31
- | `5` | Workspaces | per-agent workspaces, commits behind, dirty flag |
32
- | `6` | In-progress | `IN_PROGRESS` tasks |
33
- | `7` | Blocked | tasks with an open blocker |
34
- | `8` | Recent | recently closed tasks |
35
- | `9` | Doctor | quick health checks |
36
- | `g` | DAG | the whole task graph (popup only) |
37
- | `t` | All tasks | every task, sortable (popup only) |
38
-
39
- A digit hides or shows its card. `Shift` plus the digit opens the
40
- card as a popup. Only one popup is open at a time.
41
-
42
- Cards reflow into 2, 3, or 4 columns at 120, 180, and 240 columns
43
- wide. On a short terminal, low-priority cards drop out and the footer
44
- says how many are hidden.
45
-
46
- With two or more workstreams, a tab strip appears. `Tab` and
47
- `Shift-Tab` switch tabs. A `*` marks `scratch`. A torn-down
48
- workstream's tab stays, dimmed and struck through.
49
-
50
- ## Drill into rows
51
-
52
- `Enter` opens the focused row. On a task it opens the task's notes.
53
- `Esc` or `q` goes back one level.
54
-
55
- - Tracks: track, then its tasks, then a task's notes.
56
- - Workspaces: workspace, then its commits, then `git show`.
57
- - Commits: commit, then the backend's show view.
58
- - Activity log: the full text of the entry.
59
- - Doctor: the remediation text for the check.
60
-
61
- In a `git show` view, `t` opens `tuicr -r <sha>`. In the Commits popup,
62
- `l` opens lazygit. In the Agents popup, `a` attaches to the agent's
63
- pane. The dashboard comes back when the tool exits.
64
-
65
- ## Filter and sort
66
-
67
- `/` filters the popup's rows by substring as you type. `Enter` keeps
68
- the filter. `Esc` clears it.
69
-
70
- In the DAG and All-tasks popups, `o`, `i`, and `c` toggle `OPEN`,
71
- `IN_PROGRESS`, and `CLOSED`, `p` toggles parked tasks, and `w` toggles
72
- tasks closed as anything but `done`. In All tasks, `b`
73
- cycles the blocked filter and `s` cycles the sort: ROI, recency, age,
74
- id.
75
-
76
- ## Keys
77
-
78
- | Where | Keys | Action |
79
- | ----- | ---- | ------ |
80
- | dashboard | `+` or `=`, `-` | refresh faster (100 ms floor), slower (10 s ceiling) |
81
- | dashboard | `r` or `F5` | refresh now |
82
- | dashboard | `c` | clear the footer |
83
- | any | `?` | help overlay |
84
- | popup | `j` `k` | move |
85
- | popup | `g` `G` | top, bottom |
86
- | popup | `Ctrl-D` `Ctrl-U`, `PgDn` `PgUp` | half page, full page |
87
- | popup | `y` | copy the row's `mu` command |
88
-
89
- The mouse works for navigation only. Double-click a card to open it,
90
- double-click a row to drill in, and scroll inside lists. There is no
91
- mouse binding for back.
92
-
93
- ## Refresh
94
-
95
- Task, track, and log data refreshes every second from SQLite. Agent
96
- state, workspace dirty flags, commits, and doctor checks come from
97
- subprocesses and refresh every 10 seconds. Switching tabs refreshes
98
- the new tab at once.