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>
38 KiB
Changelog
All notable changes to clide are documented in this file.
The format follows Keep a Changelog 1.1.0, and this project adheres to Semantic Versioning.
This changelog tracks the Flutter rebuild at the repo root. The Python
Textual implementation's changelog is preserved under
legacy/CHANGELOG.md.
Versions are tracked in pubspec.yaml under version:,
which is the single source of truth. Cutting a release means (a) moving
the entries below from ## [Unreleased] under a new dated version
heading, and (b) bumping pubspec.yaml version: in the same commit.
[Unreleased]
Changed
-
Renamed desktop binary from
clide_apptoclide(Linux + macOS). -
macOS app icon uses the black-circle variant matching the Linux desktop icon.
-
ClideTestApp expanded to three test categories (toolchain, ipc, extensions) with JSON summary, non-zero exit on failure, and
make run-testmode TESTMODE_CATEGORY=<cat>for selective runs.
Added
-
Toolchain — centralized binary resolution replacing five ad-hoc mechanisms. Resolves git, pql, tmux, ptyc, shell once at boot via background isolate. Status bar reads
toolchain.missingdirectly. -
GitClient — typed Dart API wrapping all git operations. Every subprocess call goes through
_run()with toolchain-resolved path and environment. Replaces scatteredProcess.run('git', ...)calls. -
Native directory picker — macOS NSOpenPanel via method channel in AppDelegate, GTK file chooser on Linux. Falls back to text-input dialog on web. Shows "No git repo found" dialog on invalid selection.
-
macOS desktop target — OS-detecting Makefile (
make runworks on macOS/Linux/Windows), 1280x720 default window, squared app icons, sandbox entitlements with SBPL exceptions,_DARWIN_C_SOURCEfor ptyc compilation, native traffic dots skipped (macOS titlebar owns them), expanded PATH for Homebrew and~/.local/binon GUI apps. -
Shared ClideFilterBox widget with search icon, clear button, and debounced input. Applied consistently across all sidebar panes: Files (path filter with flat results), Git (filter staged/unstaged by path), Decisions (filter by ID/title/domain), Tickets (filter by ID/title/status), Problems (filter by source/message), and pql Query (replaces custom input).
-
Interaction model from Wireframe Flows v3: eight new D-records (D-47 through D-54) and five Q-records (Q-26 through Q-30) codifying layout invariants, chrome budget, editor mode, context auto-behavior, collapse spine, focus mode, state persistence, and the canonical keyboard map.
-
Panel collapse spine — collapsed side panels render as a 12px vertical spine with rotated label, hover highlight, and badge dot for pending context (D-51, T-30).
-
Focus mode —
Ctrl+.takes the active panel full-window;Escaperestores the prior layout with collapse states and divider positions intact (D-52, T-31). -
Canonical keyboard shortcuts from the interaction model: collapse toggles (
Ctrl+Shift+1/3), panel focus (Ctrl+1/2/3), sidebar section switching (Alt+1–5), focus mode, andEscapedismiss (D-54, T-33). -
Right panel (context) icon rail — bottom section switcher matching the left sidebar rail pattern (D-47, T-34).
-
Editor-above-Claude mode —
Ctrl+Eopens the editor as a split above Claude in the middle column with a draggable divider;Ctrl+WorEscapecloses it. Prompt bar Y stays fixed (D-49, T-35). -
Layout state persists across sessions — collapse state, sidebar and context panel sizes, active sections, and editor split ratio saved to
.clide/settings.yaml(D-53, T-32). -
Phosphor Icons font (v2.0.8, MIT) — regular, bold, and fill weights. Replaces hand-painted CustomPaint icons in sidebar and context panel icon rails.
-
Decisions panel in sidebar — lists confirmed D-records from
pql decisions listwith ID and title (T-37). -
Tickets panel in sidebar — lists tickets from
pql ticket listwith status dot color-coded by state (T-37). -
Markdown viewer in context panel — shows raw content of the active .md file, auto-updating on buffer switch (T-38).
-
Clickable DQRT record links in markdown —
[D-56],[T-43]etc. rendered as tappable links that navigate to the decision or ticket detail pane via the message bus. -
Ctrl+Plus / Ctrl+Minus / Ctrl+0 text zoom (5% steps, 60%–200% range) via
MediaQuery.textScaler. Resets on app restart. -
Sidebar focus indicators — selecting a ticket or decision highlights the active card in the sidebar list, auto-expands the accordion section if collapsed, and scrolls the card into view.
-
Typography scale constants
clideFontSmall(12) andclideFontBadge(11) for sidebar metadata and status badges.clideLineHeight(1.25) applied app-wide viaDefaultTextStyle. -
files.readIPC command for reading file content by path. -
pql sidebar restructured: Search tab is the default left tab with ranked text search (debounced, scored results with score bar) and a DSL toggle for raw PQL query mode; Markdown tab (filtered to
.mdfiles) on the right. Clicking a result opens it in the context panel markdown viewer with bidirectional focus highlighting. -
Kernel scheduler service with tiered timers (1min, 10min, 15min, 1hr, midnight) running on a background isolate. Emits
SchedulerTickevents on theDaemonBus. Extensions subscribe by tier (T-61). -
Auto-refresh for sidebar panels: decisions refresh on file changes to
decisions/*.mdand on 1-minute scheduler tick; tickets refresh on 1-minute tick and on status change events (T-62). -
Manual refresh button (arrowClockwise icon) in decisions, tickets, and pql markdown panels (T-63).
-
ClideAccordionshared widget — extracted from tickets and decisions views. Supports optionalleadingwidget (color dot). Pin/focus accordion logic: manually toggled sections are pinned; the focused item's section auto-opens; unpinned sections without focus auto-collapse. -
Ticket status buttons wired to
pql ticket status— clicking a status in the detail view transitions the ticket, refreshes the sidebar list, and scrolls the ticket into its new section. -
pql.tickets.statusIPC command accepting a list of IDs for batch status transitions. -
Ticket sidebar sections split into individual statuses: IN PROGRESS, REVIEW, READY, BACKLOG, DONE, CANCELLED (was four coarse groups).
-
Phosphor Icons codepoint reference CSV at
assets/fonts/phosphor/codepoints.csv— full mapping of all 1512 icon glyphs to kebab-case and PascalCase names. -
Graph view in context panel — lists files with inbound/outbound link counts from
pql search --connections(T-39). -
Welcome screen redesigned as full-screen overlay with two-column layout: START actions (open folder, clone, Claude session) with keyboard shortcuts, and RECENT projects list showing path, branch, and relative timestamps. Status line shows version, daemon connection, and active theme.
-
Recent projects history persisted to user settings (up to 10 entries with path, branch, and last-opened timestamp). Last project auto-restored on boot; falls back to cwd, then welcome.
Changed
-
Workspace renders Claude as the always-visible primary surface instead of showing a tab bar (D-47, D-48). The editor is a split overlay, not a tab.
-
Syntax highlighting via tree-sitter (dart:ffi to vendored libtree-sitter.so with embedded wasmtime). 48 grammar WASM files, 48 highlight queries. Colors map to theme syntax tokens.
-
ClideMarkdown renderer — inline grouping for tight list items, proper HTML entity unescaping after AST parse, Josefin Sans Light with 1.25 line height, 16px body text. Inline code sized to
clideFontMonoto match surrounding text weight. -
Default sidebar and context panel widths widened to 400px and 420px respectively (previous maximums). Context panel max raised to 1000px.
-
Ticket detail loading uses
pql ticket show --with-contextfor a single-call fetch of ancestors, decisions, and children. Replaces N+1 parent-chain walk.--with-decisionand--with-childrenflags consolidated into--with-context. -
Ticket descriptions render through ClideMarkdown instead of plain text, with clickable DQRT record links.
-
Decision detail view subscribes to the message bus for navigation (same pattern as tickets), enabling navigation from any source.
-
TreeSitterService is now a shared singleton — eliminates native double-free crashes from multiple WASM engine instances.
-
Code block syntax highlighting fixes overlapping tree-sitter spans that caused duplicated text (e.g.
makemakeinstead ofmake). -
Context panel drag resize fixed — dragging left now correctly grows the right panel instead of shrinking it.
-
POLICY.md — project-wide rules for runtime behavior, dependency vetting, vendored binary management, telemetry, and licensing.
Changed
-
Line length set to 160 across .editorconfig and dart formatter.
-
Core frame vs shipped extension boundary defined (D-46). Builtins are frame infrastructure only; content extensions are bundled but architecturally removable.
Removed
builtin.jirastub — Jira integration belongs as a third-party extension, not a frame builtin.wasm_runandwasm_run_flutterdependencies — replaced by vendored libtree-sitter.so via dart:ffi. Eliminates runtime network download that violated POLICY.md.
Added
-
pql skill installed via
pql init --with-skill=yes(.claude/skills/pql/SKILL.md). Covers vault queries and the planning surface (decisions + tickets). -
Bash(pql)andBash(pql *)permissions in.claude/settings.json. -
pql daemon subsystem (
lib/src/pql/).PqlClientwraps the pql CLI per D-3. IPC verbspql.files | meta | backlinks | outlinks | tags | schema | query | doctor | decisions.sync | decisions.list | decisions.show | decisions.coverage | tickets.list | tickets.show | tickets.board | plan.status. 15 new core tests. -
builtin.pql— sidebar panel with four views: Files (pql-indexed file listing), Query (PQL DSL input + results), Decisions (synced D/Q/R records colour-coded by type), Tickets (kanban board columns). Context panel tab showing backlinks + outlinks for the active file, auto-refreshing oneditor.active-changedevents. -
builtin.problems— sidebar panel aggregating diagnostics frompql.doctorandpql.decisions.sync. Surfaces missing index DB, stale skill installs, and broken decision cross-references with actionable hints. -
Git subsystem in the daemon (
lib/src/git/). Status parser (git status --porcelain), unified-diff parser, and operations (stage, unstage, stage-hunk, discard, commit, stash, log, pull, push). IPC verbsgit.status | diff | stage | stage-all | unstage | stage-hunk | unstage-hunk | discard | commit | stash | stash-pop | log | pull | pushwithgit.changedevents on mutations. 42 new core tests cover parsing, operations, and dispatcher round-trips. -
clide git …CLI shortcuts:git status,git diff [--staged],git stage <paths>,git stage-all,git unstage,git discard,git commit "<msg>",git log [--count N],git stash,git stash-pop,git pull,git push. -
builtin.git— sidebar panel showing staged, unstaged, untracked, and conflicted file groups. Per-file stage/unstage/discard on hover. Inline commit message input with Commit button. Branch + ahead/behind display with Pull/Push actions. Auto-refreshes ongit.changedevents. -
builtin.diff— workspace tab rendering unified diffs with old/new line numbers, addition/removal colouring, and binary/rename metadata. Staged/Unstaged toggle toolbar. Auto-refreshes ongit.changedevents. -
builtin.editor— Tier-2 editor tab wired up. Contributes a singleEditorworkspace tab that renders the daemon's active buffer via a newEditorController. Hydrates on mount (editor.active→editor.read), subscribes toeditor.opened | active-changed | edited | saved | closed, and propagates user edits back througheditor.set-content. Small echo-suppression guard avoids clobbering the caret when the daemon's authoritative edit echo comes back. Text surface is Flutter'sEditableTextprimitive — noTextField/ Material — so the D-7 "no Material root" stance carries into the editor; JetBrainsMono via the sharedclideMonoFamilyconstants, cursor- selection colours bind to the theme.
-
File-tree click in
builtin.filesnow opens the clicked file in the editor viaipc.request('editor.open', {path}). No local command hop — the dispatch goes straight to the daemon and the UI reconciles through theeditor.active-changedevent. -
CLI shortcuts per CLAUDE.md's Tier-2 list:
clide open <path>,clide active,clide insert <text | ->,clide replace-selection <text | ->,clide save,clide tail --events [--filter SUBSYSTEM[:ID]]. A lone-on insert / replace-selection reads text from stdin (pipe-friendly).tailreads the event-broadcast stream and prints JSON lines until SIGINT;--filternarrows by subsystem or subsystem+id. 5 new end-to-end CLI tests spin up real daemon subprocesses via a per-testCLIDE_SOCKET_PATHoverride (new env knob ondefaultSocketPath) so tests run in parallel without colliding. -
Editor subsystem in the daemon (
lib/src/editor/).EditorBufferholds path + content + cursor/selection + dirty flag;EditorRegistryowns the open-buffer set, active-buffer tracking, and file I/O. IPC verbs land alongside (editor.open | active | activate | list | read | insert | replace-selection | set-selection | set-content | save | close) with matching events (editor.opened | active-changed | selection-changed | edited | saved | closed). Omittingidon mutating verbs targets the active buffer so the tier-2 CLI shortcuts (clide insert "…",clide replace-selection "…") read naturally. 16 new core tests cover the lifecycle + dispatcher round-trips. -
builtin.claude— Tier-1 stub upgraded to the real Claude pane per D-41. Contributes a primaryClaudetab in the workspace slot that spawnstmux new-session -A -s clide-claude-<hash> -- claudevia IPCpane.spawn, with<hash>derived from the git root path so reopening the app re-attaches to the running conversation. Primary has no close affordance; closing the tab doesn't kill the session. Commandclaude.new-secondaryis registered for the palette wiring that's coming next. If tmux isn't on PATH, falls back to spawningclaudedirectly and surfaces "no-tmux · fresh every launch" in the header subtitle. Accompanied by D-41 indecisions/architecture.md. -
builtin.files— workspace filesystem panel in the sidebar. Lazy tree rooted at the git root, expand/collapse, click-to-open plumbed to a futureeditor.opencommand. Backed by a new daemon-sidefiles.*IPC subsystem (files.root,files.ls,files.watch) and aFileWatcherthat wrapsDirectory.watch(recursive: true)with ignore-file filtering. Ignore set composes clide's built-in hide list (.git/,.pql/,.clide/,.dart_tool/,build/,node_modules/) with.gitignore/.clideignoreat the root per D-4.IgnoreSet+IgnorePatternsupport line-per-pattern,#comments, anchored / directory-only / negated forms, and**across directories. 11 new unit tests on the matcher; 5 new dispatcher tests; 171 app tests still green. -
builtin.terminal— general-purpose terminal pane, Tier-1 stub upgraded to a working implementation. Contributes aTerminaltab in the workspace slot that spawns$SHELL -lvia IPCpane.spawn, streamspane.outputevents intoxterm.dart, and routes user input throughpane.write. Resize propagates viapane.resizeon viewport change.initState→ spawn;dispose→pane.close. Error-state surface for "daemon not connected" / "shell exited." No Claude-specific behaviour — that lives inbuiltin.claude+ D-41. -
Shared pane widgets under
app/lib/widgets/:ClidePtyViewwrapsxterm.dartwith clide-theme token bindings, JetBrains Mono as the face, and a Semantics live-region wrapper;ClidePaneChromeis the reusable title strip + optional close button. Consumers of the new widgets (builtin.terminal,builtin.claude) drive the xtermTerminalmodel and route bytes through IPCpane.write/pane.outputevents themselves — the widgets are rendering only, no IPC coupling. -
xterm: 4.0.0Dart dependency on the Flutter app — MIT, listed inlicenses.yamlper D-42. Hand-rolling a VT100 / xterm / truecolour parser + renderer would be weeks for no fidelity win. -
Q-23— open question on SSH-remote development (run clide against a workspace on another host). Local-first stays the Tier-1 target; this records the constraint so the daemon / IPC / extension seams don't unknowingly accrete local-only assumptions. -
IPC
panesubsystem in the daemon (per D-6). Commands:pane.spawn | list | focus | close | write | resize | tail. Events:pane.spawned,pane.output(base64-framed),pane.exit,pane.resized,pane.focused,pane.closed.PaneRegistryowns per-panePtySessionlifecycles + id generation (p_N); aDaemonEventSinkseam lets handlers emit events without depending on the IPC server package.DaemonServer.broadcast()fans events out to every connected client (a later pass adds per-client--filterscoping). Panes carry akind:field —terminaltoday,claudeready for step 7. Covered by 14 new Dart core tests exercising the real registry + dispatcher against theptychelper. -
PtySessionin the Dart core (lib/src/pty/) — spawns a child under a PTY via theptycsupporter tool, receives the master fd overSCM_RIGHTS, and exposes a byte stream, write, resize, and kill. A background isolate loops on blockingread(fd)and posts chunks to the main isolate.close()sends SIGTERM to the child so the PTY's EOF wakes the isolate cleanly, then falls through to SIGKILL + fd close + isolate kill as a safety net. Child env is built viamergePtyEnv()which stamps clide's true-colour defaults (TERM=xterm-256color,COLORTERM=truecolor,CLICOLOR_FORCE=1). Test coverage: echo round-trip, cat write/readback, env stamping verification, idempotent close. -
ffi: 2.1.3as a runtime dependency on the Dart core — justified inpubspec.yaml+ documented inlicenses.yamlper D-42. Used bylib/src/pty/ffi/forsocketpair,recvmsgwithSCM_RIGHTS,read/writeon raw fds, andioctl(TIOCSWINSZ). -
ci/test_core.sh+make test-core— runs the Flutter-free core Dart tests (test/) under a 120s hard timeout with process-group cleanup. Wired intopush-checkahead of the app test suite so a hung PTY test can't block the pre-push gate. -
Josefin Sans bundled as
app/assets/fonts/josefin_sans/as the application UI face — variable-font pair (upright + italic, weight range 100-700), OFL-licensed. Declared as theJosefinSansfamily inapp/pubspec.yaml._AppRootinstalls it as the ambientDefaultTextStyleat weightw300(Light) per the project's aesthetic direction; callers can still pass an explicitfontWeightonClideTextto get bolder emphasis. -
JetBrains Mono bundled as
app/assets/fonts/jetbrains_mono/— Regular / Italic / Bold / BoldItalic weights (OFL-licensed, license file checked in alongside). Declared as theJetBrainsMonofamily inapp/pubspec.yaml.app/lib/widgets/src/typography.dartexposesclideUiFamily+clideMonoFamilyplus platform-ordered fallback chains for both faces, for web builds and harnesses that don't load asset fonts. -
app/assets/licenses.yaml— canonical manifest of every bundled third-party artefact (fonts today; Dart packages + native tools as they land). Schema has name, kind, version, homepage, license,license_filepointer, and a one-line purpose. Bundled alongside the per-dep license texts. The About screen (Tier 6) will render this file verbatim. Accompanied byD-42: adding a dep is a two-step commit (artefact +licenses.yamlentry in the same changeset).
Changed
-
CLAUDE.md "Dependencies & supply chain" section gains the "document every bundled dependency" rule, pointing at
app/assets/licenses.yamlandD-42. -
ptyc/— the C PTY-spawn helper, peer ofpqlperD-5. One-shot, libc-only, ~400 LOC. Reads a JSON request on stdin (argv, optionalcwd/env/cols/rows), doesposix_openpt+fork+execvp, and hands the master fd back to the caller over a unix socket viaSCM_RIGHTS. Socket fd defaults to 3; override viaPTYC_SOCK_FDfor language runtimes that shuffle pipe fds through the low numbers (Python'ssubprocesswithstdout=PIPEdoes this). Exec-failure pipe (CLOEXEC) reports child-side errors to the parent without leaking zombies. Root Makefile gainsptyc-testtarget in addition toptyc-build/ptyc-clean. -
Migrated the
docs/ADRs/content intodecisions/as D/R records: ADR 0001 →D-1, ADR 0002 →R-2(superseded byD-5), ADR 0003 →D-3, ADR 0004 →D-4, ADR 0005 →D-5, ADR 0006 →D-6. Titles preserved; ADR 0006's trailing open questions moved toquestions-architecture.mdasQ-1/Q-2/Q-3. The originals are preserved in git history. -
decisions/at the repo root — Q&D record system ported from settled-reach and adapted for clide's domains. Confirmed decisions (D-NNN) live under domain files (architecture.md,extensions.md,accessibility.md,testing.md,tooling.md,process.md); open questions (Q-NNN) live under parallelquestions-<domain>.md; rejected alternatives (R-NNN) live inrejected.md. Record shape, claiming rules, and the eventual pql-side tooling plan are documented indecisions/README.md. Migration of the existingdocs/ADRs/into these files lands in a follow-up commit. -
DECISIONS.mdone-line pointer at the repo root (matches settled-reach's convention). -
tools/scripts/plan— Python stopgap entrypoint fordecisionsandticketsubcommands, writing to.pql/pql.db(gitignored). Supportsdecisions sync | validate | claim | list | show | coverageandticket new | list | show | status | assign | team | block | unblock | label | search | board, plussqlite-query. Verb shape and output format mirror the eventualpqlsubcommands so migration when pql ships feature parity is a call-site find-replace (tools/scripts/plan→pql). Ported from settled-reach with the Scrum layer stripped; ticket IDs areT-NNN(TEXT PKs) and there's nosprintstable. Time-limited perD-40/R-11. -
make decisions-validate— cheap parser dry-run wired intopush-check. Catches malformed records before push. -
Reserved extension slots —
builtin.decisions,builtin.tickets,builtin.claude-control. Id-reserving stubs underapp/lib/builtin/with no contributions yet. Implementations land onceQ-21resolves (decisions + tickets) or when the claude-control tier arrives (.claude/first-class surface — distinct from the existingbuiltin.claudePTY-pane stub). -
CLAUDE.md— new "Decision discipline" guardrail pointing atdecisions/.
Changed
-
CLAUDE.md— inline ADR links rewritten to point at the migrateddecisions/records; bottom "Open questions" section collapsed to a pointer atdecisions/questions-*.md; parent-project note updated to referencedecisions/architecture.mdinstead of the deleteddocs/ADRs/. -
make decisions-validaterewired fromtools/scripts/plantopql decisions validate. -
Decision discipline guardrail in CLAUDE.md now points at
pql decisions claiminstead of the Python stopgap.
Removed
tools/scripts/plan— Python stopgap planning scripts, superseded bypql1.0 nativedecisionsandticketsubcommands. Sunset condition fromD-40met; deletion perR-11.
Removed
-
docs/ADRs/directory — content lifted intodecisions/as D/R records (see Added above). Originals preserved in git history. -
Go sidecar skeleton under
sidecar/—cmd/clide/main.go,go.mod, and theinternal/*packages (cli,daemon,diag,git,ipc,pql,proc,pty,version). Deleted wholesale per ADR 0005: the "sidecar language: Go" premise no longer holds once the core is Dart. All functionality listed for those packages will be reimplemented underlib/as part of Tier 0. -
Go-specific Makefile targets (
lint,vuln,test-race,fmt,tidy,snapshot,tools,installvia Go), thegovulncheck/goimports/golangci-lintversion pins, and the pre-push hook'sGOBINPATH injection. Replaced with Dart/Flutter equivalents (analyze,format,test,test-integration,buildviadart compile exe). -
module:andgo_version:frompubspec.yaml— single-language core means no Go module path to track.
Changed
- ADR 0002 marked superseded by ADR 0005. The "sidecar language: Go" guardrail is retired. CLAUDE.md's guardrails, dependency notes, and command reference are updated to reflect the Dart-core direction.
.gitignoreretargeted: Flutter/Dart output at the repo root (.dart_tool/,build/, platform ephemeral dirs,bin/clide), plus aptyc/section for the C helper's build artefacts. Go-specific rules removed.ci/lint.sh,ci/test.sh,ci/security.sh, and.githooks/pre-pushrewritten for the Dart toolchain — no Go shell-outs, noGOBINPATH dance.
Added
-
Testing docs under
docs/testing/:README.md(what each layer covers, how to run, local vs CI flow),a11y-manual.md(15-minute Orca + VoiceOver checklist run at every tier cut),claude-ui-workflow.md(how Claude Code drives the app through the Playwright harness, including theflt-semantics-placeholderquirk). -
Makefile targets for every test layer and the UI harness:
test,test-a11y,test-integration,test-e2e,test-all,coverage,smoke-bundle,ui-dev,ui-stop,ui-smoke.push-checknow runstest + test-a11y(fast pre-push gate, <90s). -
Per-layer CI shell scripts under
ci/:test.sh(analyze + format + unit + widget + golden, ~5s),test_a11y.sh(a11y contract),test_integration.sh(integration_test one file at a time — desktop can't batch them reliably),test_e2e.sh(daemon subprocess + browser WASM Playwright smoke),smoke_bundle.sh(xvfb-run the Linux release bundle for 5s; catches dynamic-linker / asset-bundle / plugin-init regressions that widget tests can't see),coverage.sh(flutter test --coverage + lcov summary). -
.gitea/workflows/test.yml— four-job pipeline (unit,integration,startup-bundle,e2e) that shells out to theci/*.shscripts. Not activated yet — Gitea Actions has to be enabled in the instance settings first. GitHub-Actions-syntax-compatible, so copying to.github/workflows/is a one-file move when the repo migrates. -
Web WASM harness under
tools/ui/— Playwright driver so Claude Code (and humans) can drive the Flutter build in a real browser via the Semantics tree.build.sh/serve.sh/stop.shmanage a localhttp.serveron:4280with port-based reclaim and kill (so orphaned listeners from earlier runs get swept).driver.tsexposesClideDriverwithbyLabel/click/type/readText/screenshot/dumpSemanticsTree/waitUntilReady(auto-clicks theflt-semantics-placeholderto enable the semantics tree). First Playwright testsmoke.spec.tsasserts welcome + disconnected labels render in the browser. -
Integration tests under
app/integration_test/, run with theintegration_testpackage against the real built app (not an in-memory widget pump). The load-bearing startup gate lives here:app_starts_test.dartbootsClideApp, waits for the root shell to settle, and asserts the three-column layout + welcome tab + statusbar connection indicator all render. Also covers theme-picker modal open/select/dismiss (theme_picker_test.dart) and extension enable/disable lifecycle with contributions mounting/unmounting (extension_lifecycle_test.dart). -
App-level test suite under
app/test/— 168 tests across four layers:- Unit (
kernel/,extension/) — events bus, settings (scope + YAML round-trip), log, i18n fallback chain matrix, theme resolver + loader + controller, panel registry + arrangement, command registry + keybinding parser + palette filter, extension-manager dep-order / cycle detection / enable-disable, manifest loader, extension scanner. - Widget (
widgets/,builtin/) — every primitive's Semantics presence + token consumption + hover/press states; each Tier 0 built-in's contributions, view, and locale-switch re-render. - Golden (
goldens/) — widget primitives only, Alchemist + Ahem font; PNG fixtures checked in under_files/ci/and_files/linux/. - A11y (
a11y/) —semantic_coverage_test.dart(contract-level check that every built-in carries title + version + label-ready contributions),contrast_test.dart(WCAG-AA ratio gate on every bundled theme's canonical token pairs),i18n_coverage_test.dart(asserts every Tier-0-referenced key is present in itsen_UScatalog),keyboard_traversal_test.dart(focusability smoke).
- Unit (
-
Test helpers under
app/test/helpers/—KernelFixture(boots a KernelServices with in-memory defaults + a fake daemon for widget-level tests),FakeDaemonClient(subclasses the real client, no socket, drivable connected-state),golden_harness(Alchemist config with Ahem font for cross-platform pixel stability),widget_harness(wraps a widget in Directionality + ClideKernel + ClideTheme + MediaQuery). -
Flutter desktop app scaffold under
app/with a bareWidgetsApproot (no Material, no Cupertino) and the Tier 0 three-column layout.- Kernel (
app/lib/kernel/) — 18 services consumed by every extension:settings(scope-resolved get/set acrossapp.*/project.*/ext.*),project,extensions,theme,panels(slot registry + arrangement),events,ipc,commands(+ palette + keybinding resolver),clipboard,files,notify,dialog(single-at-a-time modal router),tray,secrets,os,net,focus, andlog. Unified in aClideKernelInheritedWidget. - i18n (
app/lib/kernel/src/i18n/) — text-driven lookup ported from fframe'sL10n: namespaced JSON catalogs,string()/interpolated()calls with caller-supplied placeholders, and a proper locale fallback chain (exact → language → default-country → default-language → placeholder). Improves on fframe's design by adding the chain, which fframe lacks. - A11y from Tier 0 —
Semantics(label:, hint:, button:)on every interactive primitive;SemanticsBinding.ensureSemantics()at boot; atheme/contrast.darthelper exposes token pairs that the a11y suite walks for WCAG-AA compliance. - Extension contract (
app/lib/extension/) — abstractClideExtension, sealedContributionPointhierarchy (TabContribution,StatusItemContribution,ToolbarButtonContribution,CommandContribution,TrayItemContribution,LayoutPresetContribution). Each extension ships one manifest contributing N atoms into kernel slots. Priority-based ordering within a slot, dependency-aware activation, YAML manifest loader + scanner for~/.clide/extensions/. - Three-tier theme pipeline — palette (named colors) → semantic roles → ~60 VS-Code-style surface tokens, each layer with defaults so palette-only themes ship. Ported
summer-nightas the first bundled theme; muted value calibrated for WCAG-AA contrast. - Widget primitives (
app/lib/widgets/) —ClideSurface,ClideText,ClideButton,ClideTabBar,ClideDivider,ClideScrollbar,ClideTooltip,ClideIcon+ eightCustomPainter-rendered icons (folder, gear, x, chevron-left/right, dot, check, plug). All token-consuming, all Semantics-wrapped. - Tier 0 built-in extensions —
builtin.default-layout(classic three-column preset + reset command),builtin.welcome(workspace placeholder),builtin.ipc-status(live-region statusbar indicator),builtin.theme-picker(command + modal, bound toctrl+k). Plus 17 id-reserving stubs (builtin.claude,builtin.terminal,builtin.files,builtin.editor,builtin.git, ...) so later tiers can fill in without rename churn. - Lua runtime boundary —
app/lib/lua/ships as typed stubs (host,adapter,capability_api,render_intent) so third-party Lua extensions can plug in at Tier 6 without retrofitting.
- Kernel (
-
.gitignoreextended to coverapp/sub-package artefacts (app/.dart_tool,app/build, per-platform ephemeral dirs,app/*.iml) and the Playwright harness undertools/ui/(node_modules,out, test-results). -
Dart core package at the repo root:
bin/clide.dart(one binary,--daemonand one-shot subcommand modes),lib/clide.dartbarrel exporting the shared IPC types,lib/src/ipc/(envelope.dart,server.dart,paths.dart,schema_v1.dart), andlib/src/daemon/dispatcher.dart.clide --daemonlistens on a unix socket;clide ping/clide versionround-trip through it with the ADR 0006 exit-code contract (0/1/2/3/4). Includestest/ipc/andtest/daemon/suites covering envelope parsing, the in-process server, and a subprocess smoke that verifies signal-driven shutdown + socket unlink. -
scripts/bazzite-flutter-setup.sh— one-shot installer for the Flutter SDK + desktop build deps on Bazzite / Fedora Silverblue. Drops the SDK under~/opt/flutter, wires PATH in the user's shell rc files, and layers the Linux desktop build deps viarpm-ostree install. -
ADR 0005 — Dart core; sidecar directory dissolved;
ptycas pql-peer. Establishes one Dart AOT binary for both CLI and daemon,lib/as the shared core, and promotes the C PTY helper to a standalone supporter tool on the same footing as pql. -
ADR 0006 — CLI and event surface contract. Defines the subsystem list (
pane,tab,editor,panel,tree,git,pql,canvas,graph,theme,settings,project), the command shape, the versioned JSON event schema, the pql-style exit-code contract, and the command↔event duality rule that operationalises user/Claude parity. -
Architectural decision records carried forward from the short-lived
claudianplugin project (discarded in favour of this Flutter rebuild): ADR 0001 — CLI-first, not MCP. ADR 0002 — Sidecar language: Go. ADR 0003 — pql as supporter tool; wrap, don't duplicate; pql is a Clide subsystem when present. ADR 0004 — Ignore file strategy (ignore_files:in.pql/config.yaml, layered). -
Pre-push quality gate:
.githooks/pre-pushrunsmake push-check(lint + test + test-race + test-integration + vuln + app-analyze + app-test) so bad pushes are caught locally before they hit Gitea. The app-side targets gracefully noop until Flutter is scaffolded. Install withmake hooks(setsgit config core.hooksPath .githooks); the hook prepends$GOBIN/$HOME/go/binto PATH so govulncheck resolves without the user touching their shell profile. -
Go sidecar/CLI skeleton under
sidecar/(modulegit.schweitz.net/jpmschweitzer/clide/sidecar):cmd/clide/main.go,internal/cliwith a stdlib-flag dispatch,internal/diagmirroring pql's exit-code + stderr-JSON contract,internal/versionwith ldflag-stamped build info, and placeholder packages fordaemon,pty,proc,git,ipc,pqlawaiting their tier.clide --versionemits JSON build-info today. -
Root
Makefiledrives both the Go sidecar and the Flutter app under one toolchain. Version is read frompubspec.yamlvia awk and stamped into the sidecar via-ldflags -X. Flutter targets gracefully noop before the app is scaffolded so the Makefile is usable from day one. Pinned Go tooling (govulncheck, goimports, golangci-lint) installs viamake tools. -
ci/entry scripts:test.sh,lint.sh(includes the supply-chain gate — no green lint without a green CVE scan),security.sh,release.sh(stub). -
Project identity files for the Flutter rebuild at the repo root:
pubspec.yaml(single source of truth for version + module path, version 2.0.0-dev), a freshREADME.md, MITLICENSE, and.editorconfig. The Python clide's manifest and README are preserved underlegacy/. -
docs/initial-plan.md— the north-star design document for the Flutter rebuild. Captures what we kept from Python Clide (pane model, git skills, Claude-always-visible), what we took from Obsidian (canvas and graph — no vault, no bases, no plugin inheritance), what Claudian's short experiment contributed (Go sidecar, CLI-first, pql-as-subsystem, ignore-file strategy), and the tier roadmap (Tier 0 app+sidecar handshake → Tier 5 canvas+graph). -
CLAUDE.mdorientation doc for future Claude Code instances: project identity, guardrails as one-liners, tier ordering, parent-project pointers, commands, dependencies & supply chain, open questions. Points at the design doc and ADRs rather than restating their content. -
Claude Code configuration under
.claude/: project-level allow/deny permissions and two skills —skill-create(generic skill authoring guidance) andgit-commit(this repo's commit conventions: no Conventional Commits, Keep a Changelog discipline,pubspec.yaml-and-changelog-bumped-together rule, attribution trailer, safety reminders).