pql init --with-skill=yes installs .claude/skills/pql/SKILL.md
covering vault queries and the planning surface (decisions +
tickets). Lock file tracks version + SHA-256 for drift detection.
Bash(pql) and Bash(pql *) added to .claude/settings.json so
pql commands run without permission prompts.
Co-Authored-By: Claude <noreply@anthropic.com>
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
GitController hydrates from git.status IPC and auto-refreshes on
git.changed events. Panel shows staged/unstaged/untracked/conflict
groups with per-file actions and an inline commit field.
DiffController renders unified diffs with line numbers, addition/
removal colouring, and a staged/unstaged toggle. Both extensions
upgraded from stubs to 0.1.0.
ClideText gains an optional fontFamily parameter so diff lines can
render in JetBrainsMono without breaking golden tests (font still
inherits from ambient DefaultTextStyle by default).
Co-Authored-By: Claude <noreply@anthropic.com>
Maps clide git status|diff|stage|unstage|discard|commit|log|stash|
pull|push to the git.* IPC verbs. Updates --help to list the new
tier-3 subcommands.
Co-Authored-By: Claude <noreply@anthropic.com>
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>
test / unit + widget + golden + a11y (push) Failing after 38s
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
EditorController mirrors the daemon's active buffer into the Flutter
UI. Hydrates on mount (editor.active → editor.read), subscribes to
editor.opened / active-changed / edited / saved / closed, and pushes
local edits back via editor.set-content. A one-shot echo-suppression
flag keeps the daemon's authoritative edit echo from clobbering the
caret the user just moved.
Text surface is EditableText directly — no TextField / Material
import, so D-007's "bare WidgetsApp" guardrail carries into the
editor tab. JetBrainsMono via the shared mono-family constants;
cursor + selection colours come from the theme tokens.
File-tree click now calls ipc.request('editor.open', {path}) instead
of the kernel command registry (which wouldn't have reached the
daemon). The opened file becomes active, the editor tab's controller
sees editor.active-changed, and the text content reconciles.
Linux release bundle still builds; 174 app tests + 74 core tests
stay green.
Co-Authored-By: Claude <noreply@anthropic.com>
`bin/clide` gains the single-word shortcuts CLAUDE.md's tier 2 spells
out: open, active, insert, replace-selection, save. Each maps the
flat positional argv into the canonical editor.* IPC shape. Insert
and replace-selection accept a lone `-` to read text from stdin so
piping works (`pbpaste | clide replace-selection -`).
`clide tail --events` is the subscribe mode. Same socket as the
request side; the client just reads + filters events. --filter
SUBSYSTEM or SUBSYSTEM:ID narrows the stream. Exits cleanly on
SIGINT.
defaultSocketPath() now respects CLIDE_SOCKET_PATH before XDG — the
existing override callers always had this up their sleeve (via
XDG_RUNTIME_DIR manipulation) but making it explicit unblocks
parallel test runs where each test needs its own daemon socket. The
new end-to-end CLI suite does exactly that: 5 tests spin up real
daemon subprocesses and exercise the shortcut surface through the
live IPC stack.
74 core tests pass; round-trip verified by hand (open README.md →
insert → tail --events captures editor.opened / edited /
selection-changed / saved).
Co-Authored-By: Claude <noreply@anthropic.com>
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>
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>
test / unit + widget + golden + a11y (push) Failing after 45s
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
Tier-0 stub flipped to the real Claude pane. Primary-per-repo
semantics: session name is `clide-claude-<hash>` where <hash> is
FNV-1a over the workspace root path, so reopening clide attaches
to the same `claude` process through `tmux new-session -A`.
Primary has no close button — closing the tab disposes the widget
but deliberately does NOT call pane.close, so the tmux session
survives until the user actually exits claude or clide is shut
down. Secondary sessions (spawned via the claude.new-secondary
command registered here, UI wiring lands next) close normally and
the pane.close cascades into tmux kill-session.
Graceful fallback when tmux isn't on PATH: retries the spawn with
argv=['claude'], surfaces "no-tmux · fresh every launch" in the
header subtitle so the user knows persistence is off.
Session-naming unit tests cover determinism + uniqueness per repo.
Full app suite: 174 tests passing.
Co-Authored-By: Claude <noreply@anthropic.com>
Captures the session-persistence + primary-vs-secondary policy
referenced from ptyc/README.md and deferred through the Tier 1
substrate work. Primary per repo (keyed on git root) survives app
restart via `tmux new-session -A`; secondaries are ephemeral and
collapse back to the primary on close; primary has no close
affordance. Daemon owns the session lifecycle, the UI just asks
pane.spawn / pane.close and observes events.
Required by the next commit (builtin.claude) which consumes this
policy.
Co-Authored-By: Claude <noreply@anthropic.com>
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>
Flips the Tier-0 stub into a real extension. Contributes a Terminal
tab in the workspace slot that spawns $SHELL -l via IPC pane.spawn,
feeds pane.output events into xterm.dart's Terminal, and routes user
input back through pane.write. Viewport resize propagates via
pane.resize. Surface for disconnected-daemon / exited-shell states so
there's no silent dead tab.
Knows nothing about Claude deliberately — the Claude-specific pane
(primary-per-repo, tmux-backed, D-041) lives under builtin.claude in
the next steps.
Co-Authored-By: Claude <noreply@anthropic.com>
ClidePtyView is a theme-bound wrapper around xterm.dart's TerminalView
— token-derived TerminalTheme, JetBrainsMono as the face, Semantics
live-region label so screen readers + Playwright both hear it. The
consumer (terminal / Claude extensions) owns the `Terminal` model and
wires IPC pane.write / pane.output → terminal.write() themselves; the
widget deliberately has no IPC dependency so it stays trivially
testable.
ClidePaneChrome is the shared pane header — title + subtitle + leading
icon + trailing widgets + optional close button. The close button is
null-conditional so primary Claude panes (D-041, landing in step 7)
can render without one.
xterm 4.0.0 added as a justified runtime dep + logged in
licenses.yaml per D-042. 3 new widget tests cover header rendering,
close-button presence, and the close-tap round-trip.
Q-023 records the SSH-remote-development question so the daemon + IPC
seams don't accrete local-only assumptions during Tier 1-5.
Co-Authored-By: Claude <noreply@anthropic.com>
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>
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>
test / unit + widget + golden + a11y (push) Successful in 1m30s
test / integration_test (xvfb) (push) Failing after 1m14s
test / bundle smoke (xvfb 5s) (push) Failing after 1m3s
test / daemon subprocess + web WASM smoke (push) Successful in 1m42s
Three sections instead of one flat list. `self:` carries clide's own
MIT license (rendered first in the About screen so the user knows
what they're running before the dependency list). `dependencies:`
covers artefacts that actually ship in the binary — JetBrainsMono,
JosefinSans, yaml. `dev_dependencies:` tracks build-time tooling
(mocktail, alchemist, flutter_lints, lints, test) for audit
completeness without polluting the user-facing About panel.
Also backfilled the two root-package dev deps that were missed the
first pass: `lints: 5.0.0` and `test: 1.25.8` (root pubspec, not
app/). Root LICENSE mirrored into app/assets/LICENSE + declared as
a bundled asset so the About screen can read it at runtime (Flutter
can't reference paths above the package root).
D-042 updated to describe the three-section split and the dev-vs-
runtime distinction.
Co-Authored-By: Claude <noreply@anthropic.com>
Every third-party artefact shipping in the clide binary — fonts,
Dart packages, native tools, bundled assets — now has an entry in
app/assets/licenses.yaml with name, kind, version, homepage, license
identifier, relative path to the bundled license text, and a
one-line purpose. The About screen (Tier 6) will render this file
verbatim. Seeded with the current set: JetBrainsMono, JosefinSans,
yaml, mocktail, alchemist, flutter_lints.
D-042 captures the two-step-commit rule (artefact + licenses.yaml
entry in the same changeset) alongside D-031's prefer-zero-deps
budget: preferring zero deps is the budget; licenses.yaml is the
visible consequence when the budget grows.
CLAUDE.md "Dependencies & supply chain" gains a matching guardrail
line. The per-dep license text files (OFL.txt for both fonts) are
declared as pubspec assets too so the About screen has something to
display, not just a manifest.
Co-Authored-By: Claude <noreply@anthropic.com>
Vendor the JosefinSans variable font (upright + italic, weight range
100-700), OFL licensed. _AppRoot installs it as the ambient
DefaultTextStyle at weight w300 (Light); ClideText inherits rather
than re-specifying the family, so Alchemist's Ahem injection keeps
goldens deterministic per D-024 while production renders the
intended face.
typography.dart gains clideUiFamily + clideUiFamilyFallback
alongside the existing mono constants; .gitignore learns about
Alchemist's failures/ diff output dir so local re-runs don't leak
into `git status`.
Co-Authored-By: Claude <noreply@anthropic.com>
Vendor the 4 core weights (Regular, Italic, Bold, BoldItalic) of
JetBrains Mono v2.304 under app/assets/fonts/jetbrains_mono/, ~1 MB
total. OFL license checked in alongside. Declared as the
JetBrainsMono family in pubspec so Flutter bundles it automatically.
app/lib/widgets/src/typography.dart exposes clideMonoFamily plus a
platform-ordered fallback chain (SF Mono / Menlo / Monaco →
JetBrains Mono system install / Fira Code / Hack / DejaVu / Liberation →
Cascadia / Consolas → monospace) for web builds and test harnesses
that don't load asset fonts. Terminal widgets pick both up via a
single import.
Co-Authored-By: Claude <noreply@anthropic.com>
One-shot helper that opens a PTY, forks a child, execvp's the given
argv with the slave as stdin/stdout/stderr, and hands the master fd
back to the caller over a unix socket via SCM_RIGHTS. Wire contract
is stdin JSON → stdout JSON + fd transfer per D-005; socket fd
defaults to 3 with PTYC_SOCK_FD override for language runtimes whose
subprocess machinery shuffles the low fd numbers.
Minimal JSON parser (no deps) scoped to the exact accepted shape.
Exec-failure pipe (CLOEXEC) reports child-side errors back without
zombies. Window size applied via TIOCSWINSZ before fork; child
becomes session leader + makes the slave its controlling TTY.
Root Makefile PTYX_PRESENT typo fixed → PTYC_PRESENT, and a
ptyc-test target added alongside ptyc-build / ptyc-clean. Eight
smoke tests pass (happy path, env replacement, cwd, window size,
bad argv, type errors, exec failure, unknown keys).
Co-Authored-By: Claude <noreply@anthropic.com>
test / unit + widget + golden + a11y (push) Successful in 4m34s
test / integration_test (xvfb) (push) Failing after 1m4s
test / bundle smoke (xvfb 5s) (push) Failing after 58s
test / daemon subprocess + web WASM smoke (push) Successful in 2m27s
CLAUDE.md gains a "Decision discipline" guardrail pointing at
decisions/<domain>.md, rewrites every inline ADR link to the new
anchor-link style (ADR 0001 → D-001, etc.), collapses the bottom
"Open questions" section to a one-line pointer, and updates the
parent-project note to reference decisions/architecture.md instead
of the deleted docs/ADRs/.
Three extension slots are reserved with id-only stubs, registered
alongside the existing 21 built-ins in main.dart:
- builtin.decisions — future sidebar for D/Q/R records
- builtin.tickets — future sidebar + kanban workspace
- builtin.claude-control — future .claude/ first-class surface
(Settings / Skills / Agents / Hooks / MCP). Distinct from the
existing builtin.claude PTY-pane stub.
`flutter analyze` clean; builtin + a11y test suites still pass.
Co-Authored-By: Claude <noreply@anthropic.com>
One new Makefile target (tools/scripts/plan decisions validate)
wired as a prerequisite of push-check so malformed records fail
locally before they reach a reviewer. Parser dry-run is cheap
(~100ms) and stays within the <90s pre-push budget.
Co-Authored-By: Claude <noreply@anthropic.com>
Ports settled-reach's decisions_sync.py + ticket + decision scripts,
Scrum-stripped. Writes to .pql/pql.db (gitignored). Verb shape
mirrors the eventual `pql` subcommands so migration is a call-site
find-replace once pql ships feature parity (D-040, R-011).
Ticket IDs are T-NNN (TEXT PKs) — reshape from settled-reach's
integer auto-increment so the stopgap's writes are compatible with
pql's future reads without a data migration. No `sprints` table; the
pyramid is kanban / waterfall (D-035).
Verified end-to-end: sync parses 71 records (39 confirmed, 23 open
questions, 9 rejected) with 62 cross-refs and zero broken links;
`ticket new`, `ticket status`, `ticket board`, and `decisions show
--with-refs` all round-trip correctly.
Co-Authored-By: Claude <noreply@anthropic.com>
Adopt settled-reach's Q&D record convention. Confirmed decisions
live under decisions/<domain>.md as D-NNN; open questions under
questions-<domain>.md as Q-NNN; rejected alternatives in rejected.md
as R-NNN. Markdown is source of truth; .pql/pql.db (added later) is a
query index.
Backlog captured from the Tier-0 Flutter planning sessions: bare
WidgetsApp, theme pipeline, kernel admission rule, feature-first
layout, a11y + i18n as Tier-0 contracts, test pyramid, kanban over
Scrum, pql-owns-planning, Python stopgap sunset clause.
ADR migration (D-001, D-003-D-006 confirmed, R-002 rejected) is
staged for the next commit so the diff stays readable.
Co-Authored-By: Claude <noreply@anthropic.com>
Three docs under docs/testing/:
* README.md — layer-by-layer reference (what each covers, the
runner, how to invoke, local vs CI flow). Points at the load-
bearing startup gate and the client-side-only constraint so a
macOS clone runs the same suite as Linux.
* a11y-manual.md — 15-minute manual checklist run at every tier
cut. Orca on Linux, VoiceOver on macOS. Catches prose drift
automation can't judge.
* claude-ui-workflow.md — how Claude Code drives the WASM build
through Playwright, including the `flt-semantics-placeholder`
quirk (semantics are opt-in in Flutter web; the driver
auto-clicks the placeholder to enable them).
Co-Authored-By: Claude <noreply@anthropic.com>
Makefile grows targets for each test layer (test, test-a11y,
test-integration, test-e2e, test-all), the coverage + smoke-bundle
helpers, and the UI harness (ui-dev, ui-stop, ui-smoke). push-check
now runs `test + test-a11y` — fast pre-push gate under 90s.
ci/coverage.sh is renamed to ci/test_coverage.sh so it matches the
`test_*.sh` naming of the other layer scripts and sidesteps the
repo's `coverage.*` gitignore pattern (intended for coverage output
files like coverage.lcov, not scripts).
Co-Authored-By: Claude <noreply@anthropic.com>
ci/*.sh — shell-only, client-side-only so `git clone && make test`
works on any Linux or macOS dev box without network or shared state.
One script per testing layer so both Makefile targets and the CI
workflow can call them without duplicating logic. Rewrite of the
existing ci/test.sh to shell out to dart + flutter layers in one
pass (analyze, format, dart test, flutter test) plus five new
scripts for the other layers.
smoke_bundle.sh is the "tests passed but app doesn't start" gate
the user flagged: builds the Linux release bundle, runs it under
xvfb for 5s, fails on any non-SIGTERM exit — catches dynamic-linker
errors, missing-asset regressions, plugin-init crashes that widget
tests can't see.
.gitea/workflows/test.yml is a four-job pipeline (unit, integration,
startup-bundle, e2e) that shells out to the ci/*.sh scripts. NOT
activated — Gitea Actions has to be enabled in the instance settings
first. GitHub-Actions-compatible, so copying to .github/workflows/
is the whole migration if the repo moves.
Co-Authored-By: Claude <noreply@anthropic.com>
tools/ui/ — scripts + Playwright config that let Claude Code (and
humans) drive the Flutter WASM build in a real browser. The point is
to avoid screenshot round-trips: every interactive widget in clide
ships a Semantics wrapper anyway (a11y requirement), so the automation
layer just queries the `flt-semantics[aria-label]` DOM.
* build.sh — `flutter build web --wasm` from app/
* serve.sh — `python3 -m http.server 4280` in the background.
Before binding, kills any stale listener on the port (orphans
from earlier failed runs no longer accumulate).
* stop.sh — port-based kill; escalates to SIGKILL after 300ms.
The pidfile is now advisory — port ownership is the source of
truth.
* driver.ts — `ClideDriver` class with `byLabel`, `click`, `type`,
`readText`, `screenshot`, `dumpSemanticsTree`, and
`waitUntilReady` that auto-clicks the `flt-semantics-placeholder`
so the semantic tree is populated before queries.
* tests/smoke.spec.ts — first driver test; asserts welcome +
disconnected labels surface in the Semantics DOM.
Co-Authored-By: Claude <noreply@anthropic.com>
Three tests under app/integration_test/, run against the real built
app via the integration_test package. The load-bearing one is
app_starts_test.dart — boots ClideApp, waits for the root shell to
settle, asserts the three-column layout + welcome tab + statusbar
indicator all render. This is the "tests pass and then app fails to
start" gate that widget tests can't see because they never pump the
real bindings.
theme_picker_test.dart invokes theme.pick, asserts the modal opens,
selects a theme, asserts the modal dismisses.
extension_lifecycle_test.dart disables ipc-status via settings,
asserts its statusbar item unmounts, re-enables, asserts it
reappears.
Co-Authored-By: Claude <noreply@anthropic.com>
Four layers under app/test/:
* kernel/, extension/ — unit tests for every kernel service and
the extension contract. i18n fallback chain gets the full
matrix (exact/lang/default/placeholder, namespace isolation,
unknown namespace, interpolation). Theme resolver and
extension-manager topo sort covered.
* widgets/, builtin/ — widget tests for every primitive and each
Tier 0 built-in. Assertions reach into the Semantics node so
a missing label fails structurally, not visually.
* goldens/ — Alchemist + Ahem font for cross-platform pixel
stability; primitives only (button, tab bar, icon set). No
goldens for compositions — they'd churn through every tier.
* a11y/ — contract-level gate: semantic coverage walks the
built-in catalogue, contrast walks every token pair in every
bundled theme against WCAG-AA thresholds (catches real
regressions: the first run caught `tab.inactive_text` at 2.81
in summer-night), i18n coverage asserts every referenced key
exists in its catalog (exists-in-map, not "key == value" — too
ambiguous for keys that happen to equal their translation).
Helpers under helpers/: `KernelFixture.create()` boots a
KernelServices with in-memory stores + a FakeDaemonClient (no
socket, drivable connected-state), `widget_harness` wraps a
widget in the minimum tree that resolves theme + i18n.
Co-Authored-By: Claude <noreply@anthropic.com>
First real content under app/. Lays the whole Tier 0 foundation in one
commit because the pieces depend on each other circularly (kernel →
extension → widgets → built-ins all reference types from the layer
below); splitting would leave intermediate commits that don't compile.
Key shapes:
* bare WidgetsApp root — no Material, no Cupertino, no Scaffold.
ClideTheme InheritedWidget is the only source of tokens.
* ClideKernel InheritedWidget aggregates 18 services (settings,
project, extensions, theme, panels, events, ipc, commands +
palette + keybindings, clipboard, files, notify, dialog, tray,
secrets, os, net, focus, log, i18n). ExtensionContext exposes
them through a stable interface.
* ClideExtension + sealed ContributionPoint hierarchy (Tab,
StatusItem, Toolbar, Command, TrayItem, LayoutPreset). One
manifest ships N contributions into kernel slots.
* Three-tier theme pipeline: palette (named colors) → semantic
roles → ~60 surface tokens. Defaults at each layer so legacy
palette-only themes produce a complete SurfaceTokens.
* A11y baked in from day one — every interactive primitive wraps
in Semantics(label:, hint:, button:); ensureSemantics() at boot;
theme/contrast.dart helper exposes token pairs for the WCAG
gate in the a11y test suite.
* i18n ported from fframe's L10n pattern (text-driven, namespaced
JSON catalogs, caller-supplied placeholders) with a proper
locale fallback chain (exact → language → default → placeholder)
fframe lacks.
* Four Tier-0 built-ins live (default-layout, welcome, ipc-status,
theme-picker) plus 17 id-reserving stubs so later tiers fill in
without rename churn.
.gitignore extended to cover app/ sub-package artefacts and the
Playwright harness scratch dirs.
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>
One-shot installer that drops the Flutter SDK under ~/opt/flutter,
wires PATH in ~/.bashrc and ~/.zshrc, and layers the desktop build
deps (GTK, ninja, clang, etc.) via rpm-ostree install. Reboot
required before `flutter doctor` will report green.
Co-Authored-By: Claude <noreply@anthropic.com>
Go sidecar is retired (ADR 0005). The matching Claude-Code allow list
still permitted `go`, `golangci-lint`, `goreleaser`, `govulncheck`,
`npm` — none of which are used anymore. Narrow the allow list to the
commands that actually run in this project.
Co-Authored-By: Claude <noreply@anthropic.com>
ADR 0005 supersedes ADR 0002. The "sidecar vs app" split was
load-bearing on Go-vs-Dart being a meaningful boundary. Going
all-Dart collapses that — the daemon becomes clide --daemon mode
of the same AOT binary the CLI lives in, both sharing lib/ with
the Flutter app. The one native gap (Dart's multi-threaded VM
can't safely fork+exec) is filled by a small C supporter tool —
ptyc, Project Terminal Controller, peer of pql — rather than
introducing a second core language.
ADR 0006 defines the CLI/event surface on top of that Dart core:
subsystem list (pane/tab/editor/panel/tree/git/pql/canvas/graph/
theme/settings/project), command shape, versioned JSON event
schema, pql-parity exit codes, and command-event duality as the
operational form of user/Claude parity.
Deleted: sidecar/cmd, sidecar/go.mod, every sidecar/internal
package. Rewritten: Makefile (dart compile exe, flutter
analyze/format/test, build-linux/build-macos, ptyc-build),
ci/*.sh, .githooks/pre-push (no more GOBIN PATH dance),
.gitignore (Flutter/Dart at repo root, ptyc section),
project.yaml (drop module: and go_version:), CLAUDE.md
(guardrails, dependencies, commands refreshed).
Co-Authored-By: Claude <noreply@anthropic.com>
.githooks/pre-push runs make push-check, which aggregates the gates
that must pass before a push lands: Go lint, test, test-race, build,
test-integration, vuln, plus Flutter app-analyze and app-test. App-
side targets gracefully noop until Flutter is scaffolded, so the
gate is usable today without waiting on the app bootstrap.
Hooks are versioned under .githooks/ rather than the usual local
.git/hooks so the gate travels with the repo. `make hooks` wires
them up by pointing git core.hooksPath at the tracked directory —
one-time setup, documented in CLAUDE.md alongside `make tools`.
The hook prepends $GOBIN/$HOME/go/bin to PATH before invoking make,
so govulncheck / goimports / golangci-lint installed via `make tools`
resolve even when the user hasn't added that directory to their
login PATH.
The git-commit skill already forbids --no-verify, which is what keeps
this gate meaningful: "the hook is slow" is a reason to fix the slow
test, not to bypass the gate.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
cmd/clide/main.go is the single entry; the same binary will later
switch into sidecar-daemon mode via --daemon. internal/cli holds the
dispatch (stdlib flag for now, Cobra arrives with the first real
subcommand in Tier 2). internal/diag mirrors pql's exit-code contract
so the two tools share one mental model for callers. internal/version
exposes the build-info struct that the Makefile's -ldflags -X targets
stamp; Info() is wired to --version so the binary reports both the
declared project.yaml version and the commit it was built from.
The daemon, pty, proc, git, ipc, and pql sub-packages ship as doc.go
stubs naming the subsystem they will own. They keep the layout legible
before the code lands so tier work has a home on arrival.
Module path targets Gitea (git.schweitz.net/jpmschweitzer/clide/sidecar),
matching where the repo lives. Go module resolution works if the host
serves the meta tags; a vanity import path can be added later if
needed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Root Makefile drives both the Go sidecar under sidecar/ and the Flutter
app under app/ through one interface. Mirrors the pql/claudian pattern:
VERSION read from project.yaml via awk and stamped into the sidecar
binary via -ldflags -X, so the version the codebase claims to be and
the version the binary reports cannot drift.
Flutter targets (app-analyze, app-test, app-build-*) check for
app/pubspec.yaml and flutter on PATH and gracefully noop when either
is missing. That makes the Makefile usable today — before the app is
scaffolded — without ceremony.
ci/ scripts shell out to the Makefile so local dev and CI run the same
commands. lint.sh includes the supply-chain gate (make security) so
there is no version of "green lint, known-vulnerable dep" that CI
accepts.
make tools installs govulncheck, goimports, and golangci-lint at
exact pinned versions — bump deliberately, never floating.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lean orientation doc for future Claude Code instances. States the
three surfaces (Flutter app, Go sidecar/CLI, pql as supporter),
summarizes the guardrails as one-liners with pointers into ADRs
0001-0004, lists the tier ordering so new work doesn't skip ahead,
and names the parent projects (legacy/ for Python clide, the
discarded claudian experiment that contributed the architectural
patterns, pql as the active supporter). Commands section is a
placeholder until the Makefile lands in the next commit.
Orientation, not a decision log — rationale lives in the ADRs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
project.yaml is fresh and declares version 2.0.0-dev — a clean break
from Python clide's v1.2.0 (which remains under legacy/). It carries
module path, toolchain pins (go >=1.25, Flutter stable, Dart >=3.5),
and the Gitea repository URL. Same schema as the Makefile will read
via awk for sidecar version stamping.
README.md and LICENSE land alongside. The README pitches the rebuild
tersely and points at docs/initial-plan.md for the full story; the
Python README is preserved under legacy/README.md.
.gitignore is rewritten for the new toolchain. The previous file was
wall-to-wall Python rules; now the root ignores Flutter/Dart/Go build
output plus the usual OS/editor/secrets/.claude-local entries, and
Python-specific rules scope to legacy/** so they don't fire on any
stray caches at the repo root.
.editorconfig sets tabs for Go, two-space for Dart and everything
else, and the usual EOL + trailing-whitespace rules.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
docs/initial-plan.md is the new north star. It states the rebuild's
premise explicitly: Python Clide's IDE skeleton gets rebuilt native
in Flutter, a sharpened subset of Obsidian's ideas (just canvas and
graph — no vault, no bases, no plugin inheritance) folds in, and
the architectural patterns from the short-lived claudian plugin
port over via ADRs 0001–0004.
Tier 0 is "Flutter app + sidecar daemon connected, empty IDE."
Tier 1 is "Claude runs inside clide, session survives app restart."
Later tiers layer panes, git, pql integration, canvas and graph,
and an extension API — each ordered so the guardrails (CLI-first,
user/Claude parity, pql-as-subsystem) stay enforceable at every
step.
Future Claude Code instances should read this before proposing
changes that touch architecture.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Ports the patterns that crystallized during the short-lived claudian
plugin project (discarded in favour of this Flutter rebuild):
- ADRs 0001-0004 capture decisions that survive the host change —
CLI-first over MCP, Go for the sidecar, pql as a supporter tool
that becomes a clide-managed subsystem when present, and the
ignore-file strategy that wires all file-enumerating surfaces
through one knob in .pql/config.yaml.
- .claude/settings.json and the git-commit and skill-create skills
come over with naming updated for clide. The git-commit skill's
"no Conventional Commits" convention supersedes the Python-era
clide style under legacy/; the Keep-a-Changelog discipline and
the project.yaml-version-and-changelog-bumped-together rule
apply going forward.
- CHANGELOG.md starts fresh at the repo root to track the Flutter
rebuild. The Python changelog is preserved under legacy/.
.gitignore narrows from `.claude/` to just `.claude/settings.local.json`
so project-level config and skills travel with the repo.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Clide is being rebuilt as a Flutter desktop app. The Python Textual
implementation moves wholesale into legacy/ rather than being deleted:
its pane model, panel set, git skills, and panel communication design
are real thought that should remain readable next to the new code
while the rebuild finds its shape. Git's rename tracking preserves
history, so `git log -- legacy/` still works.
The Flutter rebuild lives at the repo root alongside a Go sidecar
(the architecture claudian was heading toward, which folds into
clide as a core component rather than a separate plugin project).
Bootstrap of the new shape lands in subsequent commits.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- CHANGELOG: add v1.2.0 with clide-web, database layer, workspace tabs,
and ttyd/zellij removal
- CLAUDE.md: add clide-web to tech stack, dev commands, and project
structure
- web-deployment.md: document /projects/<name> routing, setup wizard,
auto-respawn, database schema, and make targets
- install-clide-web.sh: update URLs to path-based routing
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use printf instead of echo for consistent ANSI color rendering
across shells and make implementations.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Change JiraView, TodosView, and ProblemsView backgrounds from
\$background to \$surface so the right panel visually matches the
left sidebar panel.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Remove all external binary dependencies that clide-web replaces:
- ttyd-nerd-font git submodule (C binary)
- zellij config (config.kdl, bare.kdl)
- clide-launcher shell script
- update-ttyd.sh build script
Update systemd service and install script for the new FastAPI stack.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Python web server that wraps Clide for browser access. Replaces the
previous ttyd (C binary) + zellij (Rust binary) stack with a single
FastAPI application using tmux for session persistence.
Key features:
- WebSocket ↔ PTY bridge via tmux attach
- Project switching via /projects/<name> URL routing
- Vendored xterm.js for offline LAN operation
- Auto-respawn on Clide exit (tmux pane-died hook)
- Setup wizard for first-run configuration
- No scrollbar (TUI manages its own scrolling)
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>