Commit Graph
16 Commits
Author SHA1 Message Date
jpmschweitzerandClaude Fable 5 051ceea3b2 route DaemonClient through a DaemonTransport seam (T-331)
The UI's backend client connected straight to the workspace unix
socket, hard-coding the local shape. It now talks JSON-lines through a
DaemonTransport (new lib/src/ipc/transport.dart, Flutter-free), with
LocalSocketTransport reproducing today's connect byte-for-byte — zero
behavior change, proven by the untouched client test suite plus new
seam tests driving the client over an in-memory transport.

This is the slot the SSH-remote backend (T-329/Q-23) plugs into:
request correlation, reconnect/backoff, and event forwarding live
above the seam and won't change when the endpoint is remote.
main.dart's swapIpcServer becomes swapBackend per the same plan.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 03:04:58 +02:00
jpmschweitzerandClaude Fable 5 5f9c054420 replay-latest ValueStream for session state streams (T-386, T-274)
Broadcast streams drop the current value for late subscribers — the
shape behind T-274: the init event fires while spawn() is still
awaiting the transcript-tail read, before the pane subscribes, so the
status bar stayed blank. New pure-Dart ValueStream<T> (no rxdart —
prefer-zero-deps) replays the latest value to each new subscriber;
statusStream, busyStream, and pendingPromptStream in the claude
builtin now use it. busyStream subscribers see the current state
first (seeded false), which the busy test now asserts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 01:05:08 +02:00
jpmschweitzerandClaude 74f9a45539 extend build-info bake to name + tagline + repository
test / unit + widget + golden + a11y (push) Failing after 32s
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
User asked for the rest of the pubspec-derived strings to share the
same path the version went down. gen-build-info now also writes
`clideName`, `clideTagline`, `clideRepository` to
lib/src/build_info.g.dart from pubspec.yaml. Added a `tagline:`
field to pubspec for the short user-facing line (the welcome
subtitle, future web meta) — pubspec stays the single source of
truth for every name/tagline/version/repository string the app
shows.

Consumers swept:
* welcome banner ('clide' / 'IDE for Claude Code CLI') and status
  line version label read from the constants.
* app.dart WidgetsApp title + project-switcher label use clideName.
* clide_column_hat uses clideName for the empty-projects fallback.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-18 13:32:15 +02:00
jpmschweitzerandClaude 7c2eae42f1 fix theme picker integration test; one bake for build-time facts (T-116)
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 1m1s
The test was awaiting services.commands.execute('theme.pick') whose
Future doesn't complete until the dialog is dismissed — deadlock.
Fire-and-forget around pumpAndSettle, then tap Cancel, then await
the original future. Also tear the widget tree down before
services.dispose() so listening widgets unsubscribe first.

Pre-existing layout overflow in the welcome _StatusLine surfaced
when running the test at narrower viewports. Switched to a whole-
row FittedBox(scaleDown) — uniform shrink on narrow screens, no-op
at standard widths.

User flagged the hardcoded 'clide 2.0.0-dev' string. Replaced with
one generated lib/src/build_info.g.dart (gitignored, regenerated
by `make gen-build-info` from pubspec.yaml + git short SHA + UTC
clock). The same target re-syncs assets/licenses.yaml self.version
in place — no second source. Every make build/run/test depends on
it implicitly. Welcome status line now reads `clideVersion`. Stale
fontSize literals in welcome_view swept to typography constants;
clideFontMeta=13, clideFontDialogTitle=16, clideFontWelcomeBanner=52
added to fill gaps in the scale.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-18 13:22:55 +02:00
jpmschweitzerandClaude f90ddc345f update decisions/ → governance/ refs after D-21 migration
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
Stale path references from the move in 63195d1:
- CLAUDE.md: 8 D-record links, the layout tree, the open-questions
  pointer, all rewritten to governance/.
- docs/design/multitab-pane.md + docs/claude-design/README.md:
  cross-references updated.
- lib/clide.dart: doc-comment refs.
- lib/builtin/problems: user-facing message string.
- Makefile: decisions-validate target docstring.

Note: lib/builtin/decisions/ (the in-app decisions panel package)
keeps its name — it's a feature name, not a filesystem-path mirror.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-11 18:38:39 +02:00
Jeroen SchweitzerandClaude Opus 4.6 a6eca2561b remove dissolved daemon, retire ptyc, fix golden cross-platform
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>
2026-05-07 18:40:01 +02:00
jpmschweitzerandClaude 9c7ec008dc dart format whole tree
Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-03 21:51:59 +02:00
Jeroen SchweitzerandClaude Opus 4.6 73e80a55a6 add Toolchain, GitClient, native directory picker
Toolchain centralizes binary resolution — replaces five ad-hoc
mechanisms (expandedPath, _resolveGit, _resolve, _resolvePtyc,
_existsOnPath). Resolves via Future.delayed after runApp to avoid
blocking the merged UI/platform thread on macOS.

GitClient wraps all git operations with a typed API. Every subprocess
call goes through _run() using toolchain.git + toolchain.gitEnv.
Replaces free functions in operations.dart.

Native directory picker: NSOpenPanel on macOS (method channel in
AppDelegate), GtkFileChooserDialog on Linux. Falls back to text-input
dialog on web or MissingPluginException. Shows "No git repo found"
dialog when the selected directory is not a git repository.

PqlClient and pane commands updated to use Toolchain. ToolCheck
replaced by Toolchain.missing/allOk. All IPC handlers now catch
GitException to prevent unhandled exceptions on the merged thread.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 13:18:39 +02:00
jpmschweitzerandClaude 93ab6ea3bd add pql subsystem — daemon-side client + IPC handlers
PqlClient wraps pql CLI per D-003 (shell-out, never duplicate).
IPC verbs: pql.files, pql.meta, pql.backlinks, pql.outlinks,
pql.tags, pql.schema, pql.query, pql.doctor, pql.decisions.*,
pql.tickets.*, pql.plan.status. 15 new core tests.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-22 14:02:47 +02:00
jpmschweitzerandClaude f619760e7b add git subsystem — daemon-side status, diff, operations + IPC
Shell-outs to git for status (porcelain v1/v2), unified-diff
parsing, and operations (stage, unstage, hunk-apply, discard,
commit, stash, log, pull, push). IPC verbs git.* registered on
the daemon dispatcher with git.changed event emission on
mutations. 42 new core tests.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-22 11:09:16 +02:00
jpmschweitzerandClaude d32e8fdc17 add editor subsystem — daemon state + editor.* IPC
EditorBuffer + Selection + EditorRegistry hold the daemon-side
active-file model (D-006 subsystem 'editor'). Active buffer
tracking means `clide insert "…"` and `clide replace-selection
"…"` target the UI's focused file without the caller supplying an
id. Mutations mark buffers dirty; editor.save writes back to disk
through the workspace root; events fire on every state change so
subscribers can mirror.

IPC surface matches CLAUDE.md's tier-2 list + the natural extras
(list, read, activate, set-selection, set-content, close). Tests
cover open-idempotence, insert at caret, replace-selection range
swap, dirty→save→clean round-trip, close picks a new active
buffer, out-of-range selection clamping.

69 core tests pass.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-22 10:10:42 +02:00
jpmschweitzerandClaude 37108230f2 decouple Pane from PtySession so the web build compiles
test / unit + widget + golden + a11y (push) Failing after 37s
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
Pane is now a pure data class — id, kind, pid, argv, cwd, title,
isClosed. The daemon-side PaneRegistry holds a parallel map of
PtySession keyed on id; registry methods look up both sides when
writing / resizing / closing.

The `clide.dart` barrel no longer re-exports `src/pty/*`,
`src/panes/registry.dart`, or the `*_commands.dart` modules — all
three transitively import `dart:ffi` which isn't available when
compiling to WebAssembly. The daemon entrypoint (bin/clide.dart) +
core tests import them via deep paths now. Pane / PaneKind /
DaemonEventSink / RecordingEventSink stay in the barrel since
they're pure data the Flutter app references over IPC.

Verified: `dart analyze` clean, 53 core tests green, 174 app tests
green, `make ui-smoke` compiles + serves + Playwright smoke passes,
daemon boots + ping round-trips + SIGTERMs cleanly.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-22 10:01:06 +02:00
jpmschweitzerandClaude 4ff5556896 implement builtin.files — workspace file tree with live watcher
Flutter sidebar tab that lazy-loads the workspace tree via IPC
files.ls and refreshes subtrees on files.changed events. Click-to-
open routes through a future editor.open command; until Tier 2
registers it, the execute call no-ops gracefully.

Daemon side adds a new files subsystem:
  - files.root returns the resolved workspace root (git root if
    present, otherwise cwd)
  - files.ls lists a directory with ignore filtering applied
  - files.watch starts a recursive Directory.watch and fans
    FileSystemEvents out as files.changed IPC events
  - FilesService owns the watcher + ignore set lifecycle

IgnoreSet + IgnorePattern implement the common gitignore subset:
anchored (/foo), directory-only (foo/), negation (!foo), **
crossing dirs, ** at trailing position. Built-in layer hides clide-
owned dirs (.git, .pql, .clide, .dart_tool, build, node_modules);
.gitignore + .clideignore at the root layer on top per D-004. Full
multi-file ignore_files: layering from .pql/config.yaml is future
work.

11 new ignore-matcher tests + 5 files.* dispatcher tests.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-22 09:36:02 +02:00
jpmschweitzerandClaude 3715a2e191 add pane subsystem to the daemon + IPC event broadcast
Implements the Tier-1 pane subsystem from D-006: spawn / list / focus /
close / write / resize / tail commands, plus pane.spawned / output /
exit / resized / focused / closed events. PaneRegistry owns per-pane
PtySession lifecycles and id generation (p_N); a DaemonEventSink seam
keeps pane code decoupled from the IPC server package.

DaemonServer.broadcast() fans events out to every connected client.
Per-client subsystem/id filtering (`tail --filter pane:p_7`) is
deferred — Tier 1 broadcasts everything and the subscriber discards.

Panes carry a `kind:` field (terminal | claude). Step 7 (builtin.claude)
adds the claude-specific pane flow on top of this generic substrate —
the subsystem itself stays neutral.

14 new core tests: registry unit coverage (spawn → pane.spawned event,
output → base64 events, write/resize/close round-trips, idempotent
close, claude kind on the wire) plus dispatcher coverage (argv
validation, unknown-id → not-found, text vs bytes_b64, etc). All 37
core tests pass in ~3s under test-core.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-22 09:05:51 +02:00
jpmschweitzerandClaude edd8a20e0d add Dart PTY wrapper and test-core harness
PtySession wraps the ptyc helper: socketpair + Process.start + recvmsg
with SCM_RIGHTS for master-fd transfer, a background isolate that
loops on blocking read() and posts byte chunks, plus write/resize/
kill/close. close() SIGTERMs the child so the PTY's EOF wakes the
reader naturally; SIGKILL + fd close + isolate kill cover the edge
where the shell ignores SIGTERM — avoids the known Linux quirk where
closing an fd doesn't unblock an in-flight read() on it.

Env defaults stamp TERM=xterm-256color, COLORTERM=truecolor,
CLICOLOR_FORCE=1 so shells + tmux + Claude emit 24-bit sequences
that xterm.dart can render. User env (HOME / USER / SHELL) still
inherits via mergePtyEnv().

ffi: 2.1.3 added as a runtime dep — the FFI bindings for socketpair,
recvmsg, read/write, and ioctl(TIOCSWINSZ) need an allocator we're
not writing by hand. Justified in pubspec + listed in licenses.yaml
per D-042.

make test-core (ci/test_core.sh) runs the Flutter-free core tests
under a 120s hard timeout with setsid + process-group kill, wired
ahead of the fast app tests in push-check so a hung PTY test can't
wedge a pre-push. Current core suite: 24 tests in ~1s.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-22 09:01:01 +02:00
jpmschweitzerandClaude 235cbcc046 add Dart core package — IPC envelopes, daemon, ping round-trip
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>
2026-04-21 15:36:40 +02:00