Commit Graph
120 Commits
Author SHA1 Message Date
jpmschweitzerandClaude 41fd586ca0 IPC server: per-request timeout, error logging, stale-socket guard (T-77)
Three hardening fixes:
- 60s per-request timeout (configurable via DaemonServer constructor)
  prevents a misbehaving handler from blocking the connection's
  read pipeline indefinitely. On timeout the client gets a clean
  tool_error response.
- broadcast() and the per-request response writeln are wrapped in
  try/catch with stderr logging. Previously write failures silently
  dropped clients with no diagnostic; events going missing was
  invisible.
- start() probes for a live daemon before unlinking a stale socket.
  If something answers within 200ms, refuse to start. Previously
  two daemons racing to bind would let the second rip the first's
  live socket out.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-05 15:12:14 +02:00
jpmschweitzerandClaude 1787147e82 PTY: fix resource leaks and reader-isolate races (T-76)
NativePty.close() now awaits the reader-isolate spawn, kills the
child first to drive EOF on the master fd, awaits the isolate's
EOF acknowledgement, and only then closes the fd. Previously the
fd-close racing with the polling isolate left a window where the
fd number could be reused and the isolate would briefly target the
wrong file.

Both NativePty and PtySession now surface reader-isolate spawn
errors via the output stream's addError instead of silently
swallowing them.

PtySession.spawn closes the master fd on any post-receive failure,
closes parentSock in finally (was leaking on every spawn), and
kills the ptyc process if recvFd fails.

PtySession._recvFdAsync uses try/finally to close the ReceivePort
and kill the spawn isolate even when Isolate.spawn itself throws.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-05 15:10:43 +02:00
jpmschweitzerandClaude 0d333e8470 PTY: surface errno from forkpty/write/ioctl failures (T-75)
forkpty failures throw PtyException with the captured errno
(previously a generic StateError). The spawned child's chdir/execve
failures write a diagnostic line to its slave PTY before _exit, so
the parent's reader sees "exec failed: <path>" instead of an
indistinguishable EOF.

NativePty.write and PtySession.write loop on short writes and throw
PtyException on hard errors (with errno). NativePty.resize sets
_dead on EBADF so subsequent calls short-circuit cleanly.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-05 15:07:25 +02:00
jpmschweitzerandClaude 816e60d028 reject path traversal in files.read and files.ls (T-78)
Both handlers concatenated the request path onto the workspace root
without validating containment, letting `path: "../../../etc/passwd"`
escape the workspace. resolveUnderRoot normalizes the path and
checks containment under root.absolute.path before any filesystem
access.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-05 15:02:30 +02:00
jpmschweitzerandClaude 4ff004790d suppress bold rendering in terminal to keep cell grid stable
Flutter falls back to synthetic bold when JetBrainsMono-Bold isn't
registered, and synthetic bold drifts glyph advance widths enough
to break the monospace cell grid (cursor block lands between
characters, prompts wrap mid-word). Color is enough to convey
emphasis in TUIs; semantic italic and underline still render.

Also drop the temporary `tmux -L clide kill-server` from the
install target — the rapid-iteration loop is no longer needed.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-05 14:51:04 +02:00
jpmschweitzerandClaude c378503e26 spawn Claude in fullscreen mode and route mouse-wheel to PgUp/PgDown
Spawn `claude` directly as the tmux command with
CLAUDE_CODE_NO_FLICKER=1 so Claude Code runs in its fullscreen TUI
mode (input box pinned at bottom, owns its own scrollback). Mouse
wheel events are converted to PgUp/PgDown key input — universal
scroll signal that Claude, less, vim normal mode all respect, and
sidesteps the mouse-mode-but-no-scroll dead end where TUI apps
capture mouse without binding the wheel.

Drops the 1000-row tmux canvas + SingleChildScrollView experiment
in favor of viewport-sized tmux and Claude's native bottom-pinning.

Makefile install target now kills the clide tmux server so the
new config takes effect immediately. Marked TEMP — to be removed
once we no longer need the rapid-iteration loop.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-05 13:08:47 +02:00
jpmschweitzerandClaude 8a5c626892 update licenses for terminal inlining
licenses.yaml: xterm entry changed from dart-package to
inlined-source with derivative-work description. JetBrains Mono
weights updated (Bold/BoldItalic dropped). Terminal LICENSE
clarifies this is a derivative work based on xterm.dart v4.0.0.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-05 08:52:25 +02:00
jpmschweitzerandClaude 85e275a362 wire Claude pane to clide tmux config and isolated socket
Extract bundled tmux.conf to ~/.config/clide/tmux.conf on first
spawn and pass via -f. Use -L clide for a dedicated tmux server
so clide sessions don't inherit the user's tmux settings.
Terminal maxLines bumped from 5k to 50k.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-05 08:50:50 +02:00
jpmschweitzerandClaude 9c1597a450 inline terminal emulator, drop xterm.dart dependency
Replace the xterm pub.dev package with owned code under
lib/src/terminal/. Based on xterm.dart v4.0.0 by xuty (MIT).
Quiver LRU replaced with hand-rolled LinkedHashMap cache.
Scrollable removed from TerminalView — scroll events are forwarded
via Listener.onPointerSignal instead. zmodem, debugger, and
suggestion modules stripped as unused.

Also: bundle clide.tmux.conf (no status bar, 50k scrollback,
mouse on, zero escape delay, isolated -L clide socket), bump PTY
read buffer to 64KB, add 2px terminal padding, drop bold
JetBrains Mono registration.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-05 08:50:38 +02:00
jpmschweitzerandClaude 7b30748bf5 rewire daemon services on project open
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
The daemon dispatcher (pql, git, files, editor) was built once at
boot against cwd and never updated. Opening a project from the
welcome screen or directory picker had no effect on the daemon
services — pql couldn't find its index, git operated on the wrong
repo.

Now onProjectOpen rebuilds the full dispatcher against the new
workspace root and swaps it into the InProcessClient. The IPC
client's dispatcher field is no longer final.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-03 22:29:03 +02:00
jpmschweitzerandClaude a5221792ba fix sidebar order and ptyc status bar warning
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
Revert priority sort in tabsFor() — registration order in
main.dart is the intended sidebar order, not priority. The
priority fields on extensions were dead code.

Remove ptyc from toolchain missing list since NativePty
replaced it.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-03 22:22:23 +02:00
jpmschweitzerandClaude 38b017e92b dart format session.dart
test / unit + widget + golden + a11y (push) Failing after 34s
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
Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-03 21:52:53 +02:00
jpmschweitzerandClaude b45699ccd1 fix test suite — green on make test
tabsFor() sorts by contribution priority when no user order is
set. Test expectations updated for sidebar defaultSize 400 and
decision ID D-1 (no zero-padding). PTY tests tagged forkpty and
run via dart test (forkpty output unreliable inside flutter test
runner). CI script adds --no-fatal-infos and --exclude-tags.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-03 21:52:39 +02:00
jpmschweitzerandClaude 864a1062a6 remove dead code and fix analyzer warnings
Unused _resolvePtycPath (bin/clide.dart), _TrafficDot (app.dart
and clide_column_hat.dart), stale @override on _spawned field
(claude_pane.dart), duplicate import and unused local in
test_app.dart, unnecessary non-null assertions in project.dart.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-03 21:52:26 +02:00
jpmschweitzerandClaude 943ab966d2 harden PTY FFI layer for macOS
Platform-dispatched constants where Linux and macOS diverge:
TIOCSWINSZ, O_NONBLOCK, MsghdrDarwin struct (4-byte msg_iovlen
and msg_controllen vs Linux's 8-byte size_t fields), and split
recvmsg into Linux/Darwin typed variants so scm_rights uses the
correct struct per platform.

Also: app settings dir uses ~/Library/Application Support on
macOS, removed hardcoded TERMINFO from pane spawn env, removed
debug print from native_pty resize.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-05-03 21:52:13 +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 56d13c71ef use tmux resize-window for explicit window size control
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
tmux determines window size from its client, not the PTY winsize.
TIOCSWINSZ + SIGWINCH on the master fd has no effect on tmux's
internal dimensions. Use tmux resize-window -t <session> -x -y
to explicitly set the window size on resize.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 13:17:56 +02:00
Jeroen SchweitzerandClaude Opus 4.6 956279eca4 tell tmux to refresh-client on resize
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
tmux doesn't re-read the PTY winsize on SIGWINCH. After the ioctl
resize, call tmux refresh-client -C cols,rows to update tmux's
internal window dimensions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 13:11:26 +02:00
Jeroen SchweitzerandClaude Opus 4.6 a028eb7ad3 pass -x/-y to tmux new-session for correct initial size
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
tmux running inside a PTY without a traditional terminal client
defaults to a huge window size (2000+ cols, 10000 rows). Pass
explicit -x and -y flags matching the TerminalView dimensions.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 13:03:51 +02:00
Jeroen SchweitzerandClaude Opus 4.6 0b3c6bc1f8 restore tmux session in Claude pane
test / unit + widget + golden + a11y (push) Failing after 34s
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
Reverts the bare-shell diagnostic. Spawns tmux new-session -A -s
<sessionName> for persistence. Claude command will be added back
after tmux + resize are stable.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 12:20:03 +02:00
Jeroen SchweitzerandClaude Opus 4.6 29fe6795b3 rename CLIDE_WORKSPACE to CLIDE_PROJECT
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
Missed in the workspace → project rename. Consistent with the
project.open / project.validate / ProjectOpened naming.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 12:11:15 +02:00
Jeroen SchweitzerandClaude Opus 4.6 2921adc9cb add TERMINFO env for top/ncurses, tighten output flush
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
top fails with "Error opening terminal: xterm-256color" because the
PTY child can't find terminfo. Set TERMINFO=/usr/share/terminfo
explicitly in the pane environment.

Output flush changed from 16ms Timer to Duration.zero (next event
loop turn) for tighter batching of escape sequences.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 12:08:01 +02:00
Jeroen SchweitzerandClaude Opus 4.6 3e6c0279ef batch terminal output at 60fps, increase read buffer to 64KB
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
Split escape sequences caused rendering artifacts (visible % prompt
mark, garbled lines between commands). Two fixes from the legacy
Python implementation:

1. Read buffer increased from 4096 to 65536 bytes. Larger reads mean
   fewer chunk boundaries that can split multi-byte escape sequences.

2. Output writes throttled to ~60fps via a 16ms Timer. Multiple PTY
   reads within one frame window are batched into a single
   terminal.write() call, so the xterm parser sees complete sequences
   instead of fragments that render as garbage between frames.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 12:01:22 +02:00
Jeroen SchweitzerandClaude Opus 4.6 0485a36091 stagger initial scheduler ticks to avoid rebuild storm
test / unit + widget + golden + a11y (push) Failing after 33s
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
Firing all tier ticks simultaneously on project open causes multiple
IPC calls (pql, git, files) to run in parallel, their responses
triggering rapid setState rebuilds that beachball the merged thread.
Stagger 500ms apart so each panel refreshes sequentially.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 11:41:42 +02:00
Jeroen SchweitzerandClaude Opus 4.6 2f7435f271 debounce terminal resize, add SIGWINCH, remove padding
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
Rapid resize during window drag sends many SIGWINCH signals causing
the shell to redraw repeatedly, corrupting xterm's render. Debounce
to 150ms so only the final size is sent.

NativePty.resize now explicitly sends SIGWINCH (signal 28) after
TIOCSWINSZ, matching the legacy Python implementation.

Removed 8px padding from ClidePtyView to eliminate potential
dimension mismatch between widget size and PTY cols/rows.

Default window size increased to 1600x900.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 11:40:12 +02:00
Jeroen SchweitzerandClaude Opus 4.6 6f12c884cd defer PTY spawn until first TerminalView resize
test / unit + widget + golden + a11y (push) Failing after 1m57s
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
The PTY was spawning at 80x24 defaults before TerminalView had laid
out, then the real resize caused visual artifacts (ghost lines, broken
reflow). Now spawn triggers on the first onResize callback when real
dimensions are known — matching the pty-spike's proven pattern.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-05-01 09:24:10 +02:00
Jeroen SchweitzerandClaude Opus 4.6 6d180a876a resolve bare command names via PATH before execve
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
execve doesn't search PATH — bare names like 'tmux' or 'claude' fail
silently. Resolve via the environment's PATH in Dart before passing
to the child. This matches ptyc's old execvp behavior.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-30 22:42:24 +02:00
Jeroen SchweitzerandClaude Opus 4.6 5aa0eb46e4 replace ptyc with forkpty() via Dart FFI
test / unit + widget + golden + a11y (push) Failing after 3m13s
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
NativePty calls forkpty() directly — no helper binary, no socketpair,
no SCM_RIGHTS. The master fd stays in-process. Reader isolate uses
poll() for clean shutdown.

Based on the pty-spike proof-of-concept. Platform-aware: macOS uses
libSystem (DynamicLibrary.process), Linux needs libutil.so.1.
TIOCSWINSZ platform-detected.

PaneRegistry updated to use NativePty. registerPaneCommands no longer
needs a Toolchain parameter. All ptyc references removed from the
daemon layer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-30 20:30:15 +02:00
Jeroen SchweitzerandClaude Opus 4.6 cf2ae48201 use pql decisions read for full body text
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
pql 1.4.4+ restores the 'decisions read' subcommand which returns
metadata + body + refs in one call. Reverts the temporary 'show
--with-refs' workaround.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 18:54:40 +02:00
Jeroen SchweitzerandClaude Opus 4.6 1f028b336d clean up decision debug prints, add --with-refs to show
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
Remove debug print statements. Pass --with-refs to pql decisions show
so cross-references render in the detail view.

The decision body text is not available from pql — it indexes metadata
only. The body section of the D-record lives in the markdown file.
Rendering it requires either a pql --with-context flag or direct file
read + section extraction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 09:55:30 +02:00
Jeroen SchweitzerandClaude Opus 4.6 63e15d99cf fix decision detail: pql decisions show, not read
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
The pql CLI command is 'decisions show', not 'decisions read'.
The client was sending the wrong subcommand.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 08:59:33 +02:00
Jeroen SchweitzerandClaude Opus 4.6 acc7407fa5 show context panel on decision/ticket selection
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
The context panel was hidden or collapsed when a decision or ticket
was selected. activateTab alone doesn't make the panel visible.
Now both extensions explicitly setVisible + setCollapsed before
activating the detail tab.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-27 08:23:54 +02:00
Jeroen SchweitzerandClaude Opus 4.6 baf90270d1 share DaemonBus between backend and kernel, fix terminal spawn timing
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
The IsolateClient and KernelServices now share the same DaemonBus.
Previously, backend events (pane.output, git.changed) went to a
separate bus that widgets couldn't see.

ClaudePane._spawnWhenReady waits for ProjectOpened before sending
pane.spawn, preventing "No project active" errors.

Recents loaded before runApp so the welcome screen shows them.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-26 17:17:17 +02:00
Jeroen SchweitzerandClaude Opus 4.6 243a92798c fix macOS PTY: cmsghdr layout, signing, sandbox removal
macOS cmsghdr uses socklen_t (4 bytes) for cmsg_len, not size_t
(8 bytes like Linux). The Dart FFI struct was reading at wrong offsets
causing SCM_RIGHTS fd transfer to fail. Added CmsghdrDarwin with
Uint32 cmsg_len alongside the existing Linux layout.

SOL_SOCKET is 0xffff on macOS (was hardcoded to 1 for Linux).
Now platform-detected.

Switched from ad-hoc to Apple Development signing with team ID.
Removed app sandbox from entitlements — proper signing makes it
unnecessary. All SBPL hacks eliminated.

PtySession.spawn() runs recvFd in a child isolate to avoid blocking
the backend isolate's event loop.

Terminal testmode test now passes: pane.spawn via backend isolate
successfully forks and execs the shell.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-26 17:04:22 +02:00
Jeroen SchweitzerandClaude Opus 4.6 45e8132a41 async recvFd + backend terminal test (sandbox blocks ptyc exec)
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
PtySession.spawn() now runs recvFd in a child isolate via
Isolate.spawn so the blocking FFI call doesn't stall the backend
isolate's event loop.

Terminal testmode test spawns a real backend isolate, opens a project,
and sends pane.spawn via IPC — the exact same path the full app uses.
Currently fails: ptyc successfully starts but its fork+execvp is
blocked by the macOS sandbox ("Operation not permitted"). The SBPL
allows /bin/zsh exec from the app process, but ptyc's child process
may not inherit the exec permission, or the PTYC_SOCK_FD is not
inherited by the ptyc child (Dart Process.start fd inheritance on
macOS).

IsolateClient.events getter exposed for test access.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-26 15:55:29 +02:00
Jeroen SchweitzerandClaude Opus 4.6 8f6ab1ac95 add terminal test category, skip on macOS merged thread
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
PtySession.spawn() uses synchronous FFI calls (socketpair, recvFd via
SCM_RIGHTS) that block the merged UI/platform thread on macOS. The
test correctly identifies this — PTY must move to the backend isolate.

Three tests added (echo, shell, write-to-child) that will run on
Linux where threads are separate. macOS skips with a diagnostic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-26 15:26:38 +02:00
Jeroen SchweitzerandClaude Opus 4.6 e9e346cc2d add ptyc spawn test to toolchain category
Sends a valid JSON request to ptyc via stdin, verifies it parses the
request (exit 2 = syscall failed, not exit 1 = bad request). Full PTY
setup needs socketpair/SCM_RIGHTS which only PtySession provides.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-26 15:07:25 +02:00
Jeroen SchweitzerandClaude Opus 4.6 f8e8ee3992 widen project switcher dropdown, fix labels and shortcuts
test / unit + widget + golden + a11y (push) Failing after 30s
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
Dropdown width 380 → 480 to prevent RenderFlex overflow on recent
project rows. "Close Workspace" → "Close Project". Keyboard shortcuts
show ⌘ on macOS instead of Ctrl.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-26 14:16:59 +02:00
Jeroen SchweitzerandClaude Opus 4.6 5515d88407 lazy backend boot and rename workspace to project
Backend isolate now boots in two phases: resolve toolchain on spawn
(binary availability only), initialize services on project.open. The
dispatcher stays empty until a project activates — IPC requests before
that return "No project active".

Scheduler ticker only runs while a project is open. Fires an immediate
first cycle on ProjectOpened so sidebar panels refresh without waiting
for the next interval. Stops on ProjectClosed.

Renamed workspace → project throughout backend messages
(project.validate, project.open, project.ready), callbacks
(onProjectOpen, onValidateProject), and methods
(openProject, validateProject, resolveProject).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-26 14:16:46 +02:00
Jeroen SchweitzerandClaude Opus 4.6 a89910dc36 move daemon services to backend isolate
The merged UI/platform thread on macOS (Flutter 3.41) freezes on any
synchronous work — file I/O, Process.run, even isolate spawning during
early frames. No timing workaround (Timer, addPostFrameCallback,
Future.delayed) was reliable.

Fix: spawn a backend isolate that owns the DaemonDispatcher, GitClient,
PqlClient, FilesService, EditorRegistry, and Toolchain resolution. The
main isolate stays free for rendering. Communication uses SendPort with
the existing IPC message protocol (IpcRequest/IpcResponse/IpcEvent) —
zero new serialization.

New files:
  backend.dart       — spawns isolate, manages SendPort/ReceivePort
  backend_entry.dart — isolate entry point, boots all services
  isolate_client.dart — replaces InProcessClient for production

Toolchain now exposes resolveToolchainPaths() as a top-level function
with self-contained PATH expansion (no module-level state that would
prevent isolate message passing).

ClideTestApp gains boot-sequence tests: compute(), Isolate.run(),
sequential Process.run, and the full resolve+exec chain.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-26 13:53:33 +02:00
jpmschweitzerandClaude 9e340197d7 expand ClideTestApp with IPC, extension, and theme tests
Six improvements to the testmode harness:

1. Shell passthrough tests use the resolved shell instead of
   hardcoded /bin/zsh.
2. Exit code reflects test results (non-zero on any failure).
3. JSON summary line for machine-readable parsing.
4. IPC round-trip smoke (ping, version, unknown-cmd, encode/decode).
5. Extension lifecycle smoke (register + activate files, diff,
   git, terminal; theme loading for all four bundled themes).
6. Test categories via CLIDE_TESTMODE dart-define (toolchain, ipc,
   extensions, all). Makefile exposes TESTMODE_CATEGORY variable.

Also switches main.dart from bool.fromEnvironment to
String.fromEnvironment so category values other than "true" work.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-26 13:45:43 +02:00
Jeroen SchweitzerandClaude Opus 4.6 d3fc9bc48a add native picker and error dialog to project switcher
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
The project switcher dropdown's "Open Local Project" action now uses
the native directory picker (same as the welcome screen). Shows "No
git repo found" dialog when the selected directory is not a git
repository.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 13:18:39 +02:00
Jeroen SchweitzerandClaude Opus 4.6 770048fbd2 add ClideTestApp for platform integration testing
Standalone test harness activated via CLIDE_TESTMODE=true dart-define.
Runs a table of existence checks and exec tests against all external
binaries (git, pql, tmux, ptyc, zsh), including zsh-passthrough and
env variants. Results printed to stdout and shown in a minimal UI.
Auto-exits after 15 seconds.

make run-testmode launches it with a 60-second kill timer and pipes
output to /tmp/clide-testmode.log.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 13:18:39 +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
Jeroen SchweitzerandClaude Opus 4.6 8edcc78bfe add macOS desktop target with sandbox entitlements
OS-detecting Makefile (make run works on macOS/Linux/Windows), 1280x720
default window in MainMenu.xib, squared app icons, sandbox with scoped
SBPL exceptions for subprocess execution, _DARWIN_C_SOURCE for ptyc
compilation, expanded PATH merging Homebrew and ~/.local/bin for GUI
apps, native traffic dots skipped on macOS (titlebar owns them).

DebugProfile.entitlements is gitignored (machine-specific SBPL paths);
a template with __HOMEDIR__/__PROJECTS__/__SHELL__ placeholders is
committed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-25 13:18:39 +02:00
jpmschweitzerandClaude 054808c634 drop withChildren flag — folded into --with-context by pql
test / unit + widget + golden + a11y (push) Failing after 36s
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
Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-24 10:59:34 +02:00
jpmschweitzerandClaude b8c8826b9e use pql --with-context for ticket detail loading
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
Replaces the N+1 parent-chain walk (one IPC call per ancestor, one
per decision ref) with a single pql ticket show --with-context call.
pql now returns ancestors and decisions arrays inline.

Updates PqlClient.ticketShow to accept withContext/withChildren flags,
removes the obsolete withDecision flag.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-24 10:54:17 +02:00
jpmschweitzerandClaude ad1261fdc2 auto-refresh sidebar panels, pin/focus accordion logic
Decisions: file-change subscription for decisions/*.md plus 1-minute
scheduler tick. Tickets: 1-minute scheduler tick plus changed-event
subscription from status button clicks. PQL markdown tab: file-change
subscription for *.md files. All panels get a manual refresh button.

Accordion sections use pin/focus logic: manually toggled sections are
pinned and stay open; the focused item's section auto-opens; unpinned
sections without focus auto-collapse. Both decisions and tickets views
use ClideAccordion with this pattern.

Ticket sidebar split into six status sections (IN PROGRESS, REVIEW,
READY, BACKLOG, DONE, CANCELLED) matching pql's actual statuses.
Status changes scroll the ticket into its new section.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-24 10:50:25 +02:00
jpmschweitzerandClaude 8a0ca92a1f wire ticket status buttons to pql ticket status
Status buttons in the detail view now call pql.tickets.status IPC,
publish a changed event to refresh the sidebar list, and reload the
detail. Disabled for the current status. IPC handler accepts ids as
a list for batch transitions. PqlClient.ticketSetStatus takes
List<String> and joins with comma per pql CLI contract.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-24 10:50:05 +02:00
jpmschweitzerandClaude cf9a510e6c extract ClideAccordion widget, add arrowClockwise icon and codepoint CSV
ClideAccordion — shared accordion section with optional leading widget,
replaces private _AccordionSection in tickets and decisions views.

Phosphor arrowClockwise icon (0xe036) for refresh buttons. Full
codepoint reference CSV (1512 glyphs) at assets/fonts/phosphor/ for
icon lookup without external tools.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-04-24 10:49:52 +02:00