test / unit + widget + golden + a11y (push) Failing after 28s
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 23s
Per D-74: commands register an argument schema beside their handler
instead of hand-validating args inline. DaemonDispatcher accumulates a
cmd->schema registry and, before invoking a handler, normalises the
argv-translator shape ({positional, flags}) into named args, coerces
types, and checks per-arg constraints (charset/pattern, leading-dash
rejection, numeric range, list caps). Violations return userError so no
handler sees malformed input. Schema adoption is opt-in per command —
unschema'd commands dispatch unchanged.
panel.resize adopts a schema (dropping the _ResizeArgs hand-lift from
T-119); git.checkout and git.push gain schemas that reject leading-dash
refs at the dispatcher and, via positional ordering, fix the C-client
CLI path — `clide git checkout <branch>` now reaches the handler, where
the positional token previously never mapped to `branch`. The T-104
validateGitRef + count/path caps stay in place as defense-in-depth
because the git client is reachable directly from the UI, not only
through the dispatcher.
Co-Authored-By: Claude <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 27s
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 27s
Seventh slice of T-99. clide now advertises itself to Claude Code's
/ide command and serves a working MCP endpoint over HTTP+SSE per
D-73 (the Q-33 transport decision, locked in this commit).
What lands:
* D-73 — MCP transport for /ide is SSE over HTTP. Resolves Q-33;
references D-68 + D-72.
* lib/src/ipc/mcp_server.dart — McpServer class. localhost HTTP
listener on a random port; GET /sse opens a long-lived SSE stream
with an initial endpoint event carrying the session id; POST
/messages?sessionId=... accepts JSON-RPC requests and replies via
the matching SSE stream. JSON-RPC handlers for initialize,
tools/list, tools/call.
* Discovery file at $HOME/.claude/ide/<pid>.lock with the workspace
+ url so `/ide` can find us. Removed on stop.
* The two /ide minimum tools (mcp__ide__getDiagnostics,
mcp__ide__executeCode) ship as stubs — real implementations need
the analyzer integration / a clide eval surface, both follow-ups.
* main.dart starts the MCP server alongside the unix IPC server on
daemonClientFactory and project switch. Failure non-fatal — the
UI runs without MCP.
* 12 server tests cover lifecycle (start/stop, lock file), unknown
paths, full JSON-RPC round-trip for all four methods, error
responses, and edge cases (unknown session, malformed JSON,
notification without id).
The "Claude Code's /ide discovers and connects" smoke is deferred to
T-131 wrap-up since it needs a real Claude Code session against the
running app — out of scope for unit/widget tests.
Co-Authored-By: Claude <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Failing after 26s
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 27s
Two extra cases at the server layer that the dispatcher-level tests
don't reach: _argv carrying tail --events should route into the
streaming check, and _argv with non-list args should surface a
userError via the server's write path (not the dispatcher's). Lifts
coverage back over 95.
Co-Authored-By: Claude <noreply@anthropic.com>
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>
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 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 1m1s
Previous follow-up only proved the existing-dir branch on this host
because the dir was already there. Now the test deletes the parent
when it's safe to do so (exists + empty) so the create-if-missing
branch fires and counts toward coverage.
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 1m0s
socketPath-before-start + prepareParentDir-creates-missing — adds
two assertions that net a few more covered lines and put the
coverage gate back over 95%.
Co-Authored-By: Claude <noreply@anthropic.com>
Net new uncovered lines in lib/src/ipc/server.dart from the T-124
commit (handler-throw → toolError, non-request → userError, in-flight
client teardown on stop, multi-request on one connection) are covered
here so the coverage gate holds at 95%.
Co-Authored-By: Claude <noreply@anthropic.com>
First slice of T-99 (the D-56-path-a IPC server). What this lands:
* lib/src/ipc/paths.dart rewritten — `workspaceSocketPath(root)`
returns the per-workspace path per D-70 (FNV-1a 64-bit hash, hex,
no crypto dep — D-70 amended in this commit to record the hash
choice). Old `defaultSocketPath()` removed; the lone fallback in
facade.dart kept with a clear placeholder pending T-127.
* lib/src/ipc/server.dart — IpcServer class. ServerSocket.listen
accept loop (D-72), 0600 socket + 0700 parent (D-71), stale-node
probe + unlink on start, refuses to clobber a live listener.
* lib/main.dart — IpcServer started after the first dispatcher is
built and swapped on project open (workspace path changes).
Failure logged but non-fatal so the UI still works without IPC.
* 11 server tests + 5 path tests cover socket modes, multi-conn,
stale unlink, live-conflict, idempotent start/stop.
T-99 children downstream of T-124 (T-125 / T-126 / T-127 / T-130)
are now unblocked.
Co-Authored-By: Claude <noreply@anthropic.com>
pane.spawn (via PtyException.errno) and editor.open (via
FileSystemException.osError.errorCode) now route ENOENT to
not_found, EACCES/EPERM to user_error with a permissions hint,
EISDIR/ENOTDIR/EEXIST to distinct user-error/conflict, and
EMFILE/ENFILE to tool_error with a "fd limit hit" hint. The
mapping lives in lib/src/ipc/errno_mapping.dart so other handlers
can adopt the same surface as they pick up errno-bearing failures.
Co-Authored-By: Claude <noreply@anthropic.com>
First real content for the `clide` Dart package at the repo root.
One AOT-compiled binary (ADR 0005) with two modes:
* `clide --daemon` long-running unix-socket server; listens on
`$XDG_RUNTIME_DIR/clide-$USER.sock` with stale-socket
reclaim, accepts JSON-lines request/response traffic, clean
SIGTERM shutdown unlinks the socket file.
* `clide <subcommand>` one-shot; opens the socket, sends a
request, writes the response JSON to stdout, exits with the
dispatcher's error code per ADR 0006 (0/1/2/3/4). Unknown
subcommands forward to the daemon so extensions can register
their own without CLI changes.
Tier 0 handlers: `ping` (returns pong + version + UTC ts) and
`version`. Both are covered by `test/ipc/` + `test/daemon/`; the
subprocess test builds `bin/clide`, starts it, pings it, SIGTERMs
it, and asserts the socket file disappears.
Co-Authored-By: Claude <noreply@anthropic.com>