Sixth slice of T-99. Long-lived event subscription path, the second
half of D-6.
Wire shape:
- Client sends `{cmd:"tail", args:{flags:{events:true, filter:X}}}`.
- Server responds with `{ok:true, data:{streaming:true, filter:X}}`.
- Server pushes `{type:"event", subsystem, kind, ts, data}` lines
until the client closes.
Server (lib/src/ipc/server.dart):
- Takes a DaemonBus, subscribes to DaemonEvent on start.
- Per-subsystem ring buffer (replayDepth=16 per D-6) populated on
every emit.
- `tail --events` connection: send ack, replay matching events from
ring, register the client for future fanout.
- _argv envelope now unwrapped at the server layer so the streaming
check sees the inner `tail` cmd (not just `_argv`).
- Broken subscriber writes drop the subscriber cleanly; the bus
doesn't block on a stalled client.
Client (native/clide-cli/clide.c):
- Sniffs `data.streaming:true` in the ack. If set, loops reading
JSON-line events to stdout (with fflush per line) until EOF.
Tests:
- test/ipc/server_streaming_test.dart — 8 cases covering ack shape,
filter, replay buffer (size + ordering), multi-subscriber fanout,
broken-subscriber cleanup.
- test/cli/clide_cli_e2e_test.dart gets a tail --events test that
spawns the C client, emits two events on the bus, asserts they
print on stdout.
T-99 children remaining: T-130 (MCP), T-131 (wrap-up).
Co-Authored-By: Claude <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 29s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m2s
5 unit tests for the _argv unwrap handler — the e2e test only hit
the happy path; this covers the missing-args, non-list-args, and
parseArgv-error branches the coverage gate needed.
Co-Authored-By: Claude <noreply@anthropic.com>
Third slice of T-99. After this `clide status` actually does
something when typed in a shell.
* native/clide-cli/clide.c — ~250 LOC C. Walks CWD up to .git,
hashes the workspace root with FNV-1a 64-bit (byte-for-byte
identical to the Dart side, pinned via reference vectors in
paths_test.dart), opens the per-workspace socket, and ships argv
across the wire as `{cmd:"_argv", args:{argv:[...]}}`.
* lib/src/cli/argv_dispatch.dart — registers the `_argv` sentinel
command on the dispatcher. The handler runs the T-125 parser on
the embedded argv and either re-dispatches the unwrapped request
through the same dispatcher or returns the pre-built error
response. Keeps the parser in Dart so the C side stays dumb.
* lib/src/ipc/paths.dart — fnv1a64Hex hoisted to a public helper +
fixed to format as unsigned (Dart `int` is signed int64; the high
bit lit a leading minus that broke the cross-language compare).
Reference-vector tests added against the FNV reference.
* `make clide-cli` builds it via the host `cc`; output lands at
native/<platform>/clide and is gitignored. Test
test/cli/clide_cli_e2e_test.dart compiles + exercises the full
round-trip; skips cleanly when no cc is on PATH.
* CONTRIBUTING.md gets a "C clide shell client" section.
T-128 (delete legacy IPC) unblocked.
Co-Authored-By: Claude <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 31s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m2s
Second slice of T-99. Pure Dart function that takes the argv tail of
a `clide ...` invocation and returns either an IpcRequest ready to
dispatch or an ArgvError carrying a pre-built userError response.
The grammar — `SUBSYSTEM VERB [pos...] [--flag value] [--flag=val]
[-- passthrough...]` plus the umbrella commands `status`, `tail`,
`version`, `ping` — sits here so the C client (T-126) is a dumb
pipe: it sends argv as JSON and the server runs the translator
before dispatch.
Wire envelope: cmd is `subsystem.verb` (or just `subsystem` for
umbrella commands). Args is a generic envelope —
`positional: [...]`, `flags: {...}`, `passthrough: [...]` — none
required, all omitted when empty so the dispatch surface stays
minimal. Per-command typed schemas land later as each CLI verb
gets wired end-to-end.
Co-Authored-By: Claude <noreply@anthropic.com>
Complete three overdue cleanups discovered during macOS health check:
D-56 daemon dissolution: delete bin/clide.dart, DaemonServer,
and orphaned tests (test/cli/, subprocess_test, in_process_test).
Update stale "clide --daemon" references in i18n catalogs, error
messages, editor_commands, CI scripts, and decision records.
ptyc retirement: delete ptyc/ source tree, PtySession, scm_rights.
Remove from Toolchain resolution, ToolCheck gate, backend
serialization, testmode harness, Makefile, CI, and sandbox
entitlements. PTY spawning uses NativePty (Dart FFI forkpty) since
the terminal was absorbed in-tree. D-5 amended.
Golden tests: wire the existing but never-applied clideGoldenConfig
via flutter_test_config.dart. Disable CI goldens (Skia anti-aliasing
differs between macOS/Linux even with Ahem). Keep platform-keyed
goldens only — goldens/linux/ and goldens/macos/ each run on their
own OS.
Test suite: 826 pass, 0 fail on macOS (was 829 pass, 11 fail).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`bin/clide` gains the single-word shortcuts CLAUDE.md's tier 2 spells
out: open, active, insert, replace-selection, save. Each maps the
flat positional argv into the canonical editor.* IPC shape. Insert
and replace-selection accept a lone `-` to read text from stdin so
piping works (`pbpaste | clide replace-selection -`).
`clide tail --events` is the subscribe mode. Same socket as the
request side; the client just reads + filters events. --filter
SUBSYSTEM or SUBSYSTEM:ID narrows the stream. Exits cleanly on
SIGINT.
defaultSocketPath() now respects CLIDE_SOCKET_PATH before XDG — the
existing override callers always had this up their sleeve (via
XDG_RUNTIME_DIR manipulation) but making it explicit unblocks
parallel test runs where each test needs its own daemon socket. The
new end-to-end CLI suite does exactly that: 5 tests spin up real
daemon subprocesses and exercise the shortcut surface through the
live IPC stack.
74 core tests pass; round-trip verified by hand (open README.md →
insert → tail --events captures editor.opened / edited /
selection-changed / saved).
Co-Authored-By: Claude <noreply@anthropic.com>