u-foo 2.3.30 → 2.3.32
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.
- package/package.json +5 -1
- package/scripts/chat-app-smoke.js +30 -0
- package/scripts/ink-demo.js +23 -0
- package/scripts/ink-smoke.js +30 -0
- package/scripts/ucode-app-smoke.js +36 -0
- package/src/chat/commandExecutor.js +6 -2
- package/src/chat/daemonMessageRouter.js +9 -1
- package/src/chat/daemonTransport.js +2 -1
- package/src/chat/dashboardKeyController.js +0 -40
- package/src/chat/dashboardView.js +0 -20
- package/src/chat/index.js +9 -1
- package/src/chat/inputSubmitHandler.js +34 -0
- package/src/chat/projectCloseController.js +1 -1
- package/src/chat/shellCommand.js +42 -0
- package/src/chat/transport.js +16 -3
- package/src/cli.js +4 -3
- package/src/code/agent.js +4 -0
- package/src/code/nativeRunner.js +74 -0
- package/src/code/taskDecomposer.js +5 -4
- package/src/code/tui.js +73 -561
- package/src/daemon/index.js +169 -27
- package/src/daemon/ipcServer.js +23 -1
- package/src/daemon/promptRequest.js +6 -1
- package/src/daemon/run.js +11 -4
- package/src/projects/runtimes.js +1 -1
- package/src/ufoo/agentRegistryDiagnostics.js +43 -0
- package/src/ui/MIGRATION.md +382 -0
- package/src/ui/components/ChatApp.js +2950 -0
- package/src/ui/components/DashboardBar.js +417 -0
- package/src/ui/components/InkDemo.js +96 -0
- package/src/ui/components/MultilineInput.js +387 -0
- package/src/ui/components/UcodeApp.js +813 -0
- package/src/ui/components/agentMirror.js +725 -0
- package/src/ui/components/chatReducer.js +337 -0
- package/src/ui/format/index.js +997 -0
- package/src/ui/index.js +9 -0
- package/src/ui/runInk.js +57 -0
- package/src/utils/nodeExecutable.js +26 -0
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
# Ink TUI Migration Plan
|
|
2
|
+
|
|
3
|
+
Status: Ink is the default TUI for chat and ucode. The legacy blessed
|
|
4
|
+
renderer remains available with `UFOO_TUI=blessed` as a temporary fallback.
|
|
5
|
+
|
|
6
|
+
## Why
|
|
7
|
+
|
|
8
|
+
The legacy chat, ucode and internal-agent TUIs all use blessed. Blessed is
|
|
9
|
+
an unmaintained imperative widget tree with no modern equivalent of React's
|
|
10
|
+
component model, and it's awkward to extend (manual layout math, manual
|
|
11
|
+
redraws, no useful test harness).
|
|
12
|
+
|
|
13
|
+
ink (the React-for-terminals library, what Claude Code, Codex CLI fronts
|
|
14
|
+
and the Gemini CLI all use) gives us declarative components, flexbox
|
|
15
|
+
layout, hooks, and proper isolation of pure logic from rendering.
|
|
16
|
+
|
|
17
|
+
## Approach
|
|
18
|
+
|
|
19
|
+
- Ink is the default renderer; `UFOO_TUI=blessed` keeps the legacy path
|
|
20
|
+
available while fallback removal is evaluated separately.
|
|
21
|
+
- Pure helpers live in `src/ui/format/` and are shared by both, so behaviour
|
|
22
|
+
parity is enforced by test rather than copy/paste.
|
|
23
|
+
- Components live in `src/ui/components/`, written in plain JS via
|
|
24
|
+
`React.createElement` (no JSX, no build step) so jest stays vanilla.
|
|
25
|
+
- ink is loaded through `src/ui/runInk.js`, a thin CJS→ESM bridge so the
|
|
26
|
+
rest of the codebase stays CommonJS.
|
|
27
|
+
|
|
28
|
+
## Progress
|
|
29
|
+
|
|
30
|
+
- **P0** ✅ ink + react deps, runtime bridge, `<InkDemo>` smoke harness,
|
|
31
|
+
pure helpers extracted to `src/ui/format/`.
|
|
32
|
+
- **P1** ✅ ucode TUI ported to ink.
|
|
33
|
+
- **P2** ✅ folded into P3.6 (internal agent view).
|
|
34
|
+
- **P3** ✅ chat TUI ported to ink. Daemon
|
|
35
|
+
connection, dashboard (5 views), tool-merge, status spinner, history,
|
|
36
|
+
agent selection, raw-PTY mirror and internal bus agent view are wired.
|
|
37
|
+
- **P4** ✅ parity close-out complete: full `commandExecutor` dispatch,
|
|
38
|
+
`daemonMessageRouter` callback coverage, persisted history, BUS streams,
|
|
39
|
+
transient agent state, loop summary, project rail switching, cron/settings
|
|
40
|
+
dashboard actions, completion popup, and default Ink entrypoints.
|
|
41
|
+
|
|
42
|
+
## P1 ucode TUI — what's wired
|
|
43
|
+
|
|
44
|
+
| Feature | Status |
|
|
45
|
+
|---|---|
|
|
46
|
+
| Banner + version + session id header | ✅ |
|
|
47
|
+
| Scrolling `<Static>` log (1000 line cap) | ✅ |
|
|
48
|
+
| Multiline input (cursor math, Ctrl+A/E/B/F/D/H/K/U/W, Meta+B/F/D, `\\\n` continuation, Alt+Enter newline, CJK wrap) | ✅ |
|
|
49
|
+
| Up/Down history walk + agent-selection mode | ✅ |
|
|
50
|
+
| Ctrl+C exit, Ctrl+O expand last tool group | ✅ |
|
|
51
|
+
| Tool merge/freeze/expand state machine | ✅ |
|
|
52
|
+
| Spinner + phase status line (request/thinking/text/tool labels) | ✅ |
|
|
53
|
+
| Esc abort with `AbortController` and "Cancelling..." status | ✅ |
|
|
54
|
+
| Agents footer with single-line truncation + `+N more` hint | ✅ |
|
|
55
|
+
| `runSingleCommand` empty/exit/probe/help/error/tool/nl/ubus/resume/nl_bg kinds | ✅ |
|
|
56
|
+
| Background tasks ("BG x/y/z" suffix) | ✅ |
|
|
57
|
+
| ubus / resume / nl_bg branches | ✅ |
|
|
58
|
+
| autoBus polling | ✅ |
|
|
59
|
+
|
|
60
|
+
## Real-TTY checklist
|
|
61
|
+
|
|
62
|
+
```sh
|
|
63
|
+
./bin/ucode.js
|
|
64
|
+
# fallback: UFOO_TUI=blessed ./bin/ucode.js
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Editor
|
|
68
|
+
|
|
69
|
+
- [ ] Type, see characters appear with the cursor on the next cell.
|
|
70
|
+
- [ ] Backspace deletes one cell back; cursor stays correct on CJK.
|
|
71
|
+
- [ ] Left/Right arrows move the cursor; resetting preferred col.
|
|
72
|
+
- [ ] Up/Down on a single line walks input history.
|
|
73
|
+
- [ ] Up/Down on a multiline value moves between visual rows.
|
|
74
|
+
- [ ] `\` followed by Enter inserts a newline; Enter alone submits.
|
|
75
|
+
- [ ] Alt+Enter inserts a newline.
|
|
76
|
+
- [ ] Ctrl+A / Ctrl+E jump to row start / end.
|
|
77
|
+
- [ ] Ctrl+W deletes the previous word (also Meta+Backspace).
|
|
78
|
+
- [ ] Long pasted text doesn't lock up the renderer.
|
|
79
|
+
- [ ] Resize the terminal — input frame and footer span the new width.
|
|
80
|
+
|
|
81
|
+
### Status line
|
|
82
|
+
|
|
83
|
+
- [ ] Shows `UCODE · Ready` while idle.
|
|
84
|
+
- [ ] Shows a spinning indicator + phase ("Waiting for model...",
|
|
85
|
+
"Thinking...", "Generating response...", "Calling X...") during
|
|
86
|
+
`runNaturalLanguageTask`.
|
|
87
|
+
- [ ] Appends `(<elapsed> s, esc cancel)` when a task is in flight.
|
|
88
|
+
- [ ] Esc on a running task flips to "Cancelling..." then back to Ready.
|
|
89
|
+
|
|
90
|
+
### Tool calls
|
|
91
|
+
|
|
92
|
+
- [ ] Single tool call renders one line (`· tool · detail`).
|
|
93
|
+
- [ ] Two+ consecutive tool calls collapse to one row + `(Ctrl+O expand)`.
|
|
94
|
+
- [ ] Ctrl+O expands the most recent group with `│`/`└` branch markers
|
|
95
|
+
and only fires once per group.
|
|
96
|
+
- [ ] When text arrives between tool calls, the previous group freezes
|
|
97
|
+
into the log and a fresh group starts on the next tool call.
|
|
98
|
+
|
|
99
|
+
### Agents footer
|
|
100
|
+
|
|
101
|
+
- [ ] Shows `Agents: none │ No target agents` when nothing's online.
|
|
102
|
+
- [ ] Shows `Agents: @x @y ... │ ↓ select target · ←/→ switch` otherwise.
|
|
103
|
+
- [ ] At narrow widths, the row stays single-line, drops trailing chips
|
|
104
|
+
and emits ` +N more`.
|
|
105
|
+
- [ ] Down enters selection mode (first chip inverse). Left/Right cycle.
|
|
106
|
+
Up exits. Prompt prefix changes to `›@<name> ` when locked.
|
|
107
|
+
|
|
108
|
+
### Smoke
|
|
109
|
+
|
|
110
|
+
- [ ] `node scripts/ucode-app-smoke.js` exits 0.
|
|
111
|
+
- [ ] `npx jest --silent` shows the pre-existing 5 OAuth failures only;
|
|
112
|
+
every ink suite passes.
|
|
113
|
+
|
|
114
|
+
## Decision log
|
|
115
|
+
|
|
116
|
+
- **Don't fork ink.** Claude-code-fixed inlines a customised ink under
|
|
117
|
+
`src/ink/`; we don't need React 19 / ConcurrentRoot / IDE bridging,
|
|
118
|
+
so depending on the public `ink@5` keeps maintenance cost low.
|
|
119
|
+
- **Don't add JSX.** `React.createElement` keeps jest CJS happy and
|
|
120
|
+
avoids a build step. We can revisit if any single component grows
|
|
121
|
+
past ~600 lines and readability suffers.
|
|
122
|
+
- **Don't enable `--experimental-vm-modules` for jest.** The risk of
|
|
123
|
+
surprise ESM behaviour across the existing 1800-test suite is too
|
|
124
|
+
high. Render coverage stays in `scripts/*-smoke.js`; component logic
|
|
125
|
+
is exercised by pure-function tests.
|
|
126
|
+
- **Codex isn't a useful reference.** Its TUI is a Rust ratatui app
|
|
127
|
+
(`codex-rs/tui`), not React-based. The architectural principle worth
|
|
128
|
+
borrowing is its hard split between TUI and core protocol.
|
|
129
|
+
|
|
130
|
+
## P2 dropped, folded into P3
|
|
131
|
+
|
|
132
|
+
The internal-agent view in chat is not an independent program — it's a
|
|
133
|
+
view mode owned by `src/chat/agentViewController.js`. Today it works by
|
|
134
|
+
detaching `screen.children`, writing raw `\x1b[2J` to stdout, and
|
|
135
|
+
flipping `screen.grabKeys`. There is no clean seam to mount an isolated
|
|
136
|
+
ink subtree inside a still-blessed chat host, so attempting P2 in
|
|
137
|
+
isolation would force us to build a stdout-arbitration layer we'd throw
|
|
138
|
+
away once chat itself is on ink. P3.6 ports the agent view as a chat
|
|
139
|
+
sub-mode instead.
|
|
140
|
+
|
|
141
|
+
## P3 audit (chat TUI surface)
|
|
142
|
+
|
|
143
|
+
Source: `src/chat/index.js` (2215 lines) + ~30 controllers in
|
|
144
|
+
`src/chat/`. Highlights:
|
|
145
|
+
|
|
146
|
+
### Lifecycle
|
|
147
|
+
- Public entrypoint `runChat(projectRoot, { globalMode })` from
|
|
148
|
+
`src/chat/index.js`. Wires `daemonCoordinator.connect()` then loops
|
|
149
|
+
forever; exit goes through `process.exit(0)` from a screen `destroy`
|
|
150
|
+
hook.
|
|
151
|
+
- Runners injected via closures: `daemonCoordinator.send`,
|
|
152
|
+
`executeCommand`, `inputSubmitHandler.handleSubmit`,
|
|
153
|
+
`daemonMessageRouter.handleMessage`.
|
|
154
|
+
|
|
155
|
+
### View state machine
|
|
156
|
+
- `dashboardView` ∈ `projects | agents | mode | provider | cron`
|
|
157
|
+
- `focusMode` ∈ `input | dashboard` — toggled by Tab and arrow keys
|
|
158
|
+
- `globalMode` (boolean) + `globalScope` ∈ `controller | project` —
|
|
159
|
+
`globalMode=true` enables a multi-project rail; Esc/Enter walk the
|
|
160
|
+
scope ladder.
|
|
161
|
+
- `enterDashboardMode()` / `exitDashboardMode()` are the two transition
|
|
162
|
+
points; `setGlobalScope()` runs an async, debounced project switch.
|
|
163
|
+
|
|
164
|
+
### Input
|
|
165
|
+
- Submit accepts `@mention`, `@target`, `/command`, plain text and
|
|
166
|
+
numeric disambiguation (when `pending.disambiguate` is set).
|
|
167
|
+
- Editor keys cover Ctrl+A/E/B/F/D/H/K/U/W, Meta+B/F/D, arrows
|
|
168
|
+
(cursor + history when empty), Esc (3-layer: clear @target →
|
|
169
|
+
exit project scope → cancel input), bracketed paste, Tab (toggle
|
|
170
|
+
dashboard), PgUp/PgDn (scroll log).
|
|
171
|
+
- Completion fires on `/` and `@` with sources from command registry,
|
|
172
|
+
group templates, solo profiles and agent mentions; Up arrow jumps to
|
|
173
|
+
the latest suggestion.
|
|
174
|
+
- History is per-project, persisted to `input-history.jsonl` with
|
|
175
|
+
draft restoration on project switch.
|
|
176
|
+
|
|
177
|
+
### Daemon stack
|
|
178
|
+
- `daemonTransport` owns the socket path and retry policy.
|
|
179
|
+
- `daemonConnection` owns the queue + lifecycle (`connect`, `send`,
|
|
180
|
+
`requestStatus`, `close`, `markExit`, `switchConnection`,
|
|
181
|
+
`getState`).
|
|
182
|
+
- `daemonCoordinator` orchestrates project switches with a serialised
|
|
183
|
+
Promise chain.
|
|
184
|
+
- `daemonMessageRouter.handleMessage(msg)` is a stateless dispatcher
|
|
185
|
+
that turns daemon responses into log appends, dashboard updates, PTY
|
|
186
|
+
writes and transient agent state changes.
|
|
187
|
+
- `daemonReconnect.restartDaemonFlow()` provides a per-project lock for
|
|
188
|
+
daemon restarts.
|
|
189
|
+
|
|
190
|
+
### Side controllers
|
|
191
|
+
- `cronScheduler` — `/cron start|stop|list` + the cron dashboard view.
|
|
192
|
+
- `settingsController` — launch mode / agent provider,
|
|
193
|
+
with daemon restart on mode/provider change. `autoResume` stays config/command-driven.
|
|
194
|
+
- `chatLogController` — log buffer + history file replay
|
|
195
|
+
(`loadHistory`, `appendHistory`, `markStreamStart`,
|
|
196
|
+
`setHistoryTarget`, `resetViewState`).
|
|
197
|
+
- `statusLineController` — debounced status line with background-task
|
|
198
|
+
suffix (e.g. `(ufoo-agent processing)`); `queueStatusLine`,
|
|
199
|
+
`resolveStatusLine`, `enqueueBusStatus`, `resolveBusStatus`.
|
|
200
|
+
- `streamTracker` — per-publisher stream state + markdown rendering
|
|
201
|
+
(`beginStream`, `appendStreamDelta`, `finalizeStream`,
|
|
202
|
+
`markPendingDelivery`, `consumePendingDelivery`).
|
|
203
|
+
- `transientAgentState` — TTL-bounded `working / waiting_input /
|
|
204
|
+
blocked` markers per agent.
|
|
205
|
+
- `projectCloseController` — `requestCloseProject(index)` runs daemon
|
|
206
|
+
stop + project switch.
|
|
207
|
+
- `agentDirectory` — agent label resolution + window clamping (pure).
|
|
208
|
+
- `internalAgentLogHistory` — bus log replay for internal agents.
|
|
209
|
+
|
|
210
|
+
### Internal-agent sub-view (`agentViewController`, 1072 lines)
|
|
211
|
+
- Public API: `getCurrentView`, `getViewingAgent`,
|
|
212
|
+
`isAgentViewUsesBus`, `getAgentInputSuppressUntil`,
|
|
213
|
+
`get/setAgentOutputSuppressed`, `renderAgentDashboard`,
|
|
214
|
+
`setAgentBarVisible`, `enterAgentView(agentId, options)`,
|
|
215
|
+
`exitAgentView`, `sendRawToAgent`, `sendResizeToAgent`,
|
|
216
|
+
`requestAgentSnapshot`, `writeToAgentTerm`, `placeAgentCursor`,
|
|
217
|
+
`handleBusAgentKey`, `handleResizeInAgentView`, `refreshAgentView`.
|
|
218
|
+
- Two render modes inside it: PTY mirror (raw ANSI passthrough +
|
|
219
|
+
cursor placement, `agentSockets.connectOutput/Input`,
|
|
220
|
+
`requestSnapshot`) and an embedded bus subview (own input value,
|
|
221
|
+
cursor, log, animated status indicator).
|
|
222
|
+
- `agentBar.computeAgentBar` renders the agent strip across both
|
|
223
|
+
modes.
|
|
224
|
+
- `agentSockets.createAgentSockets` owns the PTY/bus socket
|
|
225
|
+
lifecycle.
|
|
226
|
+
- Exit/restore reattaches `screen.children`, restores scroll region
|
|
227
|
+
and unfreezes `screen.render`.
|
|
228
|
+
|
|
229
|
+
### Layout (`createChatLayout`)
|
|
230
|
+
- 9 widgets: screen, logBox, statusLine, completionPanel, dashboard,
|
|
231
|
+
inputBottomLine, promptBox, input, inputTopLine.
|
|
232
|
+
- Geometry rules: dashboard 1-2 lines, input 5-9 lines (autosizes by
|
|
233
|
+
content), log fills the rest. Many `height: "100%-N"` strings →
|
|
234
|
+
ink flex layout in the port.
|
|
235
|
+
|
|
236
|
+
### Commands
|
|
237
|
+
`/bus`, `/ctx`, `/daemon`, `/doctor`, `/cron`, `/group`, `/init`,
|
|
238
|
+
`/open`, `/launch`, `/project`, `/role`, `/solo`, `/settings`,
|
|
239
|
+
`/help`, plus nested subcommands (`/bus activate|list|rename|send|status`,
|
|
240
|
+
`/cron start|list|stop`, etc.). `commandExecutor.executeCommand(text)`
|
|
241
|
+
is the single dispatch point. `parseCommand(text)`, `parseAtTarget(text)`
|
|
242
|
+
and `shouldEchoCommandInChat(text)` are pure.
|
|
243
|
+
|
|
244
|
+
### Cross-cutting
|
|
245
|
+
- `text.js`: `escapeBlessed`, `stripBlessedTags`, `stripAnsi`,
|
|
246
|
+
`truncateAnsi`, `decodeEscapedNewlines`. Pervasive — used by every
|
|
247
|
+
log message, status line and dashboard line. The blessed-tag
|
|
248
|
+
helpers (`escapeBlessed`/`stripBlessedTags`) become no-ops in ink;
|
|
249
|
+
the ANSI helpers stay relevant.
|
|
250
|
+
- `rawKeyMap.keyToRaw(ch, key)`: converts ink-style key events to
|
|
251
|
+
PTY bytes for the agent view. Stays as-is.
|
|
252
|
+
- `transport.js`: `startDaemon`, `stopDaemon`, `connectWithRetry`.
|
|
253
|
+
Framework-agnostic, no migration needed.
|
|
254
|
+
|
|
255
|
+
### Migration concern shortlist (1 per section)
|
|
256
|
+
1. **Entry**: 50+ `screen.render()` calls turn into React state
|
|
257
|
+
updates; build a `useReducer` so dispatchers are async-safe.
|
|
258
|
+
2. **State machine**: `setGlobalScope` is async + debounced — model
|
|
259
|
+
it as an effect, not a setter.
|
|
260
|
+
3. **Input**: cursor math already lives in `src/ui/format` (P0); we
|
|
261
|
+
reuse it.
|
|
262
|
+
4. **Daemon**: keep `daemonConnection` / `daemonCoordinator` as-is;
|
|
263
|
+
wrap the message router in a `useEffect` subscription that pumps
|
|
264
|
+
into `dispatch`.
|
|
265
|
+
5. **Side controllers**: keep the controllers as plain modules;
|
|
266
|
+
`useEffect` subscribes/unsubscribes instead of attaching to
|
|
267
|
+
blessed events.
|
|
268
|
+
6. **Agent view**: ink can't render arbitrary ANSI inside a `<Box>`,
|
|
269
|
+
but it can yield stdout to a "raw mode" component that writes
|
|
270
|
+
straight through during PTY mirror; we'll model it with
|
|
271
|
+
`<Static>`-style raw write or by suspending ink's render and
|
|
272
|
+
passing stdout through, then re-mount on exit.
|
|
273
|
+
7. **Layout**: replace `height: "100%-N"` with flexbox + `flexGrow`.
|
|
274
|
+
8. **Commands**: long-running commands run on a serialised promise
|
|
275
|
+
chain like ucode's `runChainRef`.
|
|
276
|
+
9. **Cross-cutting**: drop blessed-tag helpers from ink call sites;
|
|
277
|
+
ANSI/text helpers stay.
|
|
278
|
+
|
|
279
|
+
### P3 phase plan
|
|
280
|
+
|
|
281
|
+
| Step | Goal | Status |
|
|
282
|
+
|---|---|---|
|
|
283
|
+
| P3.1 | This audit | ✅ |
|
|
284
|
+
| P3.2 | `UFOO_TUI=ink` switch in `runChat()` | ✅ |
|
|
285
|
+
| P3.3 | ChatApp shell (banner + log + input + status) | ✅ |
|
|
286
|
+
| P3.4 | Five dashboard views as React components | ✅ |
|
|
287
|
+
| P3.5 | Daemon connection + PROMPT/BUS_SEND wiring | ✅ |
|
|
288
|
+
| P3.6 | Raw-PTY internal agent view as a ChatApp mode | ✅ |
|
|
289
|
+
| P3.7 | Real-TTY checklist | ✅ |
|
|
290
|
+
|
|
291
|
+
## P3 chat TUI — what's wired
|
|
292
|
+
|
|
293
|
+
| Feature | Status |
|
|
294
|
+
|---|---|
|
|
295
|
+
| Banner header (project + global mode + scope) | ✅ |
|
|
296
|
+
| Scrolling `<Static>` log (1000 line cap) | ✅ |
|
|
297
|
+
| Multiline input (P1 MultilineInput component) | ✅ |
|
|
298
|
+
| 5 dashboard views (projects/agents/mode/provider/cron) | ✅ |
|
|
299
|
+
| Tab toggles input/dashboard focus | ✅ |
|
|
300
|
+
| Up/Down history walk + agent selection mode | ✅ |
|
|
301
|
+
| Left/Right cycle agents while selected | ✅ |
|
|
302
|
+
| Spinner + phase status line | ✅ |
|
|
303
|
+
| Tool-merge state machine + Ctrl+O expand | ✅ |
|
|
304
|
+
| Daemon connect / send / status poll | ✅ |
|
|
305
|
+
| `PROMPT` for free text, `BUS_SEND` for `@target` | ✅ |
|
|
306
|
+
| `BUS_SEND_OK` / `RESPONSE` / `ERROR` / `STATUS` / `BUS` envelopes | ✅ |
|
|
307
|
+
| Raw PTY agent mirror (Enter on selected agent, Esc to leave) | ✅ |
|
|
308
|
+
| `daemonMessageRouter` (markdown streams, transient state, bus subview) | ✅ |
|
|
309
|
+
| `commandExecutor` full slash-command dispatch (`/cron`, `/group`, `/role`, `/settings` …) | ✅ |
|
|
310
|
+
| Slash + `@` autocomplete | ✅ |
|
|
311
|
+
| Input history persisted file load/save | ✅ |
|
|
312
|
+
| Cron dashboard actions | ✅ |
|
|
313
|
+
| Settings dashboard actions (launch mode, provider) | ✅ |
|
|
314
|
+
|
|
315
|
+
## Real-TTY checklist for chat
|
|
316
|
+
|
|
317
|
+
```sh
|
|
318
|
+
./bin/ufoo.js chat # project mode
|
|
319
|
+
./bin/ufoo.js chat --global # global controller mode
|
|
320
|
+
# fallback: UFOO_TUI=blessed ./bin/ufoo.js chat
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Layout
|
|
324
|
+
- [ ] Banner shows the active project + global/project tag.
|
|
325
|
+
- [ ] `Agents:` footer, status line above input, log fills the rest.
|
|
326
|
+
- [ ] Resize the terminal — input frame and footer stay single-line.
|
|
327
|
+
|
|
328
|
+
### Input + history
|
|
329
|
+
- [ ] Type, Enter sends a `PROMPT`. Backspace, arrows, Ctrl+A/E etc.
|
|
330
|
+
behave the same as the ucode editor.
|
|
331
|
+
- [ ] Up/Down on an empty draft walks the in-memory history.
|
|
332
|
+
- [ ] `\` + Enter inserts a newline.
|
|
333
|
+
- [ ] Esc clears any active agent selection.
|
|
334
|
+
|
|
335
|
+
### Daemon
|
|
336
|
+
- [ ] On launch the daemon spawns automatically (look for the socket
|
|
337
|
+
under `~/.ufoo` or your project's `.ufoo`).
|
|
338
|
+
- [ ] Send a free-text message — daemon answers, status flips to
|
|
339
|
+
`Working on task...` and back to Ready.
|
|
340
|
+
- [ ] Type `@<agent> hi` (or select with arrow keys) — message is
|
|
341
|
+
sent via `BUS_SEND`, ack arrives as `✓ Message delivered`.
|
|
342
|
+
|
|
343
|
+
### Agents footer
|
|
344
|
+
- [ ] Tab into the dashboard, ↓ enters agent selection (first item
|
|
345
|
+
inverse), ←/→ cycles, ↑ exits.
|
|
346
|
+
- [ ] Enter on a selected agent attaches to its PTY (cleared screen +
|
|
347
|
+
scroll region + bottom hint bar). Esc returns to chat without
|
|
348
|
+
losing the previous draft or log.
|
|
349
|
+
|
|
350
|
+
### Tool-merge
|
|
351
|
+
- [ ] Daemon-driven tool calls collapse and `(Ctrl+O expand)` works
|
|
352
|
+
the same as ucode.
|
|
353
|
+
|
|
354
|
+
### Smoke
|
|
355
|
+
- [ ] `node scripts/chat-app-smoke.js` exits 0.
|
|
356
|
+
- [ ] `node scripts/ucode-app-smoke.js` exits 0.
|
|
357
|
+
- [ ] `npx jest --silent` shows the pre-existing 5 OAuth failures
|
|
358
|
+
only; every ink suite passes.
|
|
359
|
+
|
|
360
|
+
## P4 close-out
|
|
361
|
+
|
|
362
|
+
- **STATUS handler fix** — chat now reads `msg.data.active` /
|
|
363
|
+
`msg.data.active_meta` / `msg.data.cron.tasks` so the agents and cron
|
|
364
|
+
counts in the footer actually update.
|
|
365
|
+
- **Slash command dispatch** — `createCommandExecutor` is wired in Ink
|
|
366
|
+
with daemon stop/start/restart, cron IPC, project switching and agent
|
|
367
|
+
activation callbacks.
|
|
368
|
+
- **Input history persistence** — `<projectRoot>/.ufoo/chat/input-history.jsonl`
|
|
369
|
+
is loaded on mount and appended on every submit; format matches the
|
|
370
|
+
blessed inputHistoryController.
|
|
371
|
+
- **Daemon message routing** — Ink routes daemon envelopes through
|
|
372
|
+
`daemonMessageRouter`, including BUS phase status, transient states,
|
|
373
|
+
pending delivery markers, streams, close/launch refreshes and loop
|
|
374
|
+
summary dashboard display.
|
|
375
|
+
- **Inline completion popup** — `/<prefix>` matches commands from
|
|
376
|
+
`COMMAND_REGISTRY`; `@<prefix>` matches the live agents list. Tab
|
|
377
|
+
accepts the top suggestion. Pure helper `buildCompletions` lives in
|
|
378
|
+
`src/ui/format` with full jest coverage.
|
|
379
|
+
- **Exit hygiene** — Ctrl+C now flushes `\x1b[2J\x1b[H` so the shell
|
|
380
|
+
prompt comes back to a clean screen instead of sitting under the
|
|
381
|
+
final ink frame; `runUcodeInkTui` returns `{ code: 0 }` so
|
|
382
|
+
`agent.js`'s `process.exit(res.code)` no longer crashes.
|