Gitea remote kept as secondary (`gitea`). Updated repository URLs in pubspec.yaml, licenses.yaml, and initial-plan.md. README rewritten to reflect the current single-process Flutter architecture. Co-Authored-By: Claude <noreply@anthropic.com>
18 KiB
Clide — Initial Plan (Flutter rebuild)
Working name: clide (unchanged from the Python era). Repo root:
/var/mnt/data/projects/clide. Flutter desktop app + Go sidecar/CLI.
Context
Clide v1.2.0 is a Python Textual TUI — terminal-only, polished, with
a pane model, panel set, git skills, plugin system, and a ttyd-backed
web variant. That implementation is archived under legacy/ in this
repo; git log -- legacy/ preserves its history.
In parallel, a two-day experiment called claudian explored a "thin Obsidian plugin + Go sidecar" approach to the same problem: run Claude Code inside a markdown-aware IDE. That project was tossed when the implementation hit two walls:
- Obsidian is Electron. Terminal rendering in Electron has a documented failure mode on macOS (repaint thrash, WebGL context loss, display-drag ghosting) that the user has lived through. xterm.js mitigations soften the pain; they don't remove it.
- Most of "Obsidian's value" turned out, on examination, to be two features: canvas and graph. The rest — the vault concept, inline YAML "bases," the wikilink implementation, the plugin ecosystem — was either parallel ceremony to what the git repo already provides, or better solved elsewhere (pql).
The lesson: we were fighting the host instead of using it. Clide
becomes a Flutter desktop app, with the Python Clide IDE skeleton
rebuilt natively and a sharpened subset of Obsidian's ideas folded
in. Claudian's architectural patterns (Go sidecar, CLI-first,
pql-as-subsystem, ignore-file strategy, supply-chain gate,
changelog discipline) port over as ADRs 0001–0004 and the
.claude/skills/git-commit rules; the plugin code itself does not.
Intended outcome: Clide is the IDE you open on a code repo.
Claude runs inside it (the terminal is first-class, always visible).
Markdown files are first-class content. Canvas and graph surfaces
support concept development. pql is the query engine. The Go
sidecar handles PTYs, subprocesses, file watching, git, and pql
invocations; the Flutter app renders. Claude drives the UI through
a clide CLI with the same contract as pql.
Guardrails / non-negotiables
- Flutter desktop is the UI host. No Electron, ever. Web target
may work as a happy accident; don't compromise desktop fidelity for
it.
xterm.dartis the terminal renderer. - No heavy lifting in the UI layer. Flutter app stays focused on rendering and interaction. PTYs, subprocesses, file watching, git, pql invocations all live in the Go sidecar.
- CLI-first, not MCP. Claude talks Bash (
clide ...), same contract style as pql. See ADR 0001. - Sidecar language: Go. Static binary, same idioms as pql. See ADR 0002.
- User/Claude parity. Every
clidesubcommand has a UI affordance in the Flutter app, and every UI action has a corresponding subcommand. Incomplete otherwise. - pql is a supporter tool and a clide-managed subsystem. The
only place clide contains pql logic is
sidecar/internal/pql/— pure shell-outs. See ADR 0003. - Repo-is-the-workspace. The git repo root is the workspace.
No parallel "vault" concept. Ignore behaviour drives through
.pql/config.yaml— see ADR 0004. - Dependencies: version-locked and CVE-checked. Go deps exact-
pinned and govulncheck-gated; Dart deps prefer-zero-then-exact-
pinned. See the memory files under
~/.claude/projects/-var-mnt-data-projects-clide/memory/.
What carries forward from each parent project
From Python clide (legacy/) |
Carried forward as |
|---|---|
| Pane/visibility model (Sidebar / Workspace / Context) | Same model, rendered in Flutter widget trees |
| Workspace tabs (Editor / Diff / Terminal — appear when needed) | Same pattern; xterm.dart hosts the Terminal tab |
| Claude-always-visible in the workspace center | Unchanged; the centrepiece |
| Git panel (staged/unstaged, click actions) | Port widget-for-widget; sidecar shells out to git |
Git skills (/commit, /stash, /pull, /push) |
Rewire to call clide git ... under the new CLI |
| Context panel (Jira / TODOs / Problems) | Port; data providers become sidecar subsystems |
| TODO.md parsing for the TODOs panel | Preserve the format; sidecar parses and serves |
| Theme system (22 themes, TOML) | Port the palette over time; Flutter theming is first-class so no bespoke system needed |
| Plugin system (pluggy hookspecs) | Translates into a Dart extension API we design fresh; not imported |
| From Obsidian (as an idea, not a dep) | Carried forward as |
|---|---|
| Canvas | Flutter CustomPaint + InteractiveViewer; .canvas JSON-schema-compatible where easy |
| Graph view | Flutter-native; data from pql, not a re-parser |
Wikilink syntax ([[page]]) |
Parser + renderer, nothing more |
| Frontmatter | Standard YAML; pql already parses it |
| From Obsidian, rejected |
|---|
| "Vault" concept — the git repo is the workspace |
| Inline bases (YAML query tables) — pql at the repo level |
| Obsidian's wikilink implementation — syntax only |
| Plugin ecosystem — clide's extension API is its own |
| From claudian (2-day experiment) | Carried forward as |
|---|---|
| Go sidecar + CLI-first architecture | Unchanged; ADRs 0001–0002 |
| pql-as-supporter-tool + pql-as-clide-subsystem rules | Unchanged; ADR 0003 |
Ignore-file strategy (ignore_files: in .pql/config.yaml) |
Unchanged; ADR 0004 |
Supply-chain gate (make security, make vuln, lockfile discipline) |
Port for Go sidecar; Dart equivalent TBD |
Changelog discipline (Keep a Changelog, project.yaml version sync) |
Port verbatim |
| Commit conventions (imperative, no types, HEREDOC, attribution trailer, logical splits) | Port verbatim to .claude/skills/git-commit |
Python clide's future: feature-frozen at v1.2.0. Stays under
legacy/ for reference. No back-porting.
Architecture
┌────────────────── clide (Flutter Desktop) ───────────────────┐
│ │
│ ┌─────────────┬────────────────────────────┬─────────────┐ │
│ │ Sidebar │ Workspace │ Context │ │
│ │ │ ┌──── Editor / Diff ─────┐ │ │ │
│ │ Files │ │ (appears when needed) │ │ Jira │ │
│ │ Git │ └────────────────────────┘ │ TODOs │ │
│ │ Tree │ │ Problems │ │
│ │ │ ┌──── Claude (xterm.dart) │ │ │ │
│ │ │ │ (always visible) │ │ │ │
│ │ │ └────────────────────────┘ │ │ │
│ │ │ ┌──── Terminal (optional)│ │ │ │
│ │ │ └────────────────────────┘ │ │ │
│ ├─────────────┤ ├─────────────┤ │
│ │ ⎇ main │ Canvas + Graph live as │ │ │
│ │ staged: 2 │ full-screen views │ │ │
│ └─────────────┴────────────────────────────┴─────────────┘ │
│ │ │
│ │ Dart IPC client │
│ ▼ │
│ ┌────────────────────────────────────────────────────────┐ │
│ │ clide sidecar daemon (Go, same binary as CLI) │ │
│ │ - real PTYs (for xterm.dart panes) │ │
│ │ - subprocess management │ │
│ │ - filesystem watchers │ │
│ │ - git shelling-out │ │
│ │ - pql wrapper │ │
│ │ - IPC server (unix socket, token auth, JSON-lines) │ │
│ └─────────────┬──────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌────────────────────────┐ ┌─────────────────────────┐ │
│ │ Claude Code (inside │ │ pql (Go CLI, external) │ │
│ │ a workspace PTY pane) │ │ called by sidecar and │ │
│ │ │ │ by Claude directly │ │
│ │ $ clide open wiki/... │ └─────────────────────────┘ │
│ └────────────────────────┘ │
└──────────────────────────────────────────────────────────────┘
Three surfaces, one coherent tool:
clideFlutter app (UI): renders panels via widget trees, hostsxterm.dartpanes for terminals, connects to the sidecar over IPC.clideCLI (one-shot): Claude's entry point. Parses args, opens the sidecar socket, sends a request, prints JSON on stdout with the pql-style exit-code contract, exits.clide --daemon(long-running sidecar): Same binary, different mode. Owns PTYs, subprocesses, file watchers, git, pql shell-outs. IPC server for app + CLI. Started by the app on load; survives app restarts so Claude sessions persist.
Repo layout (target)
projects/clide/
├── README.md # pitch + link to this plan
├── LICENSE
├── CHANGELOG.md
├── CLAUDE.md # lean orientation for Claude Code
├── project.yaml # single source of truth: version, module path, schema version
├── Makefile # drives both app (flutter) and sidecar (go)
├── .gitignore / .editorconfig
├── .clideignore # optional; see ADR 0004
├── .claude/ # project-level Claude Code config + skills (committed)
├── .githooks/pre-push # push-gate runs lint/test/vuln on both app and sidecar
├── ci/ # lint.sh, test.sh, security.sh, release.sh
├── docs/
│ ├── initial-plan.md # this document
│ └── ADRs/0001..0005 # decisions
├── app/ # Flutter desktop (generated by `flutter create`)
│ ├── pubspec.yaml
│ ├── lib/ # main.dart + panels/, widgets/, ipc/, theme/
│ ├── linux/ macos/ windows/ # platform scaffolding
│ └── test/
├── sidecar/ # Go sidecar + CLI, single binary, two modes
│ ├── go.mod
│ ├── cmd/clide/main.go
│ └── internal/
│ ├── cli/ # one-shot subcommand dispatch
│ ├── daemon/ # --daemon mode
│ ├── ipc/ # unix-socket server (for app + CLI)
│ ├── pty/ # real PTYs for xterm.dart panes
│ ├── proc/ # subprocess management
│ ├── git/ # git shell-outs
│ ├── pql/ # thin wrappers around the pql binary
│ ├── diag/ # exit-code contract + stderr-JSON
│ └── version/ # ldflag-stamped build info
├── legacy/ # archived Python clide v1.2.0
└── tests/ # integration tests that span app + sidecar
Tier roadmap (prioritized)
Tier 0 — Foundation: Flutter app + sidecar daemon connected, empty IDE
- Flutter app scaffold (
app/) with a three-column layout, placeholders. - Go sidecar daemon (
sidecar/) — socket, token auth, JSON-lines protocol, single-instance lock, graceful shutdown. clideCLI one-shot mode — version + apingsubcommand.- Dart IPC client — auto-reconnect, JSON-lines envelope.
Acceptance: clide --daemon runs; Flutter app connects; the app
shows a connected-status indicator and the panels as placeholders.
Tier 1 — Claude runs inside clide
xterm.dartTerminal pane in the Workspace center, connected to a sidecar-owned PTY.- Session persistence: sidecar keeps PTYs alive across app restarts.
- "Open Claude" button that spawns a PTY running
claude(ortmux attach -t claudefor team mode when that ships later).
Acceptance: open clide → Claude is running in the workspace → quit and reopen clide → Claude session is still there.
Tier 2 — Pane model and active-file awareness
- File tree panel (Sidebar/Files), drag-to-reveal, click-to-open.
- Editor tab — markdown and code via
flutter_highlightor our own light renderer; saves through the sidecar's file watcher. clide open <path>,clide active,clide insert,clide replace-selection,clide tail --events …— the CLI surface Claude uses to drive the UI. Equal UI affordance for each.
Acceptance: Claude says "opening notes/today.md" → runs
clide open notes/today.md → the file appears in the Editor tab.
Tier 3 — Git
- Git panel (Sidebar/Git) — staged/unstaged groups, hunk-level stage/unstage, conflict UI. Sidecar shells out to git; Flutter renders.
- Diff tab in the Workspace (side-by-side + unified toggle).
clide git …subcommands back the panel actions. Python clide's/commit,/stash,/pull,/pushskills rewire to callclide git ….
Acceptance: the common day-to-day git flow works entirely in clide, with Claude driving the same operations.
Tier 4 — pql integration (Query panel + everywhere else)
- Query panel renders
pql <args>output as table / list. - Results click-through opens the referenced file in the Editor tab.
- File tree, canvas node sources, graph view, backlinks indicator, problems panel — all pull from pql where applicable.
Acceptance: pql tags output renders in a Query panel; clicking
a tag narrows the file tree; backlinks show in the Context panel.
Tier 5 — Canvas and graph
- Canvas surface:
CustomPaint+InteractiveViewer, pan/zoom, node creation, connection drawing. Format aims for.canvasJSON-schema compatibility with Obsidian. - Graph view: force-directed layout, link data from pql.
clide canvas node …,clide canvas connect …,clide graph open— CLI parity.
Acceptance: open a canvas file from the Sidebar; drag a new
node; Claude can add a node by clide canvas node ….
Tier 6 — Extension API (later)
- Dart extension API (design fresh, not ported from pluggy).
- Settings page, theme picker, keybinding editor.
- Distributable builds (macOS/Linux) via Flutter's native tooling.
Critical integration points
sidecar/cmd/clide/main.go— Cobra root:clide <subcommand>(one-shot) vsclide --daemon(long-running).sidecar/internal/ipc/server.go— single socket; token-auth; dispatches to subsystems (pty, proc, git, pql, canvas, panes).sidecar/internal/pql/— only place Clide contains pql logic; pure wrappers, no re-implementation.app/lib/ipc/client.dart— Dart IPC client; reconnect-on- reload; JSON-lines envelope.- Claude settings —
.claude/settings.jsonallow ruleBash(clide *); no MCP config. - pql — first-class supporter tool, called by Claude directly AND wrapped by Clide where ergonomics justify it.
Decisions locked
- Name: clide (unchanged). Binary:
clide. Flutter app bundle id:clide. - First milestone: Tier 0 + Tier 1.
- Packaging: single app + single sidecar/CLI binary. Platform builds via Flutter's native tooling.
- Distribution: personal-only now, MIT-licensed (carried from legacy/).
- CLI-first, not MCP (ADR 0001).
- No Electron, no Node in the UI layer — Flutter and Dart only.
- pql is a supporter tool; wrap, don't duplicate; pql is a Clide subsystem when present (ADR 0003).
- User-and-Claude parity — every CLI has a UI affordance and vice versa.
- Dataview / bases out of scope — pql is the query engine (ADR 0003).
- Workspace = git repo root — no vault concept.
Open questions (investigate in-repo, not now)
.canvasschema compatibility — how strictly do we match Obsidian's format? Decide during Tier 5 spike.- Extension API shape — widgets? subcommands? both? Decide during Tier 6 planning.
- IPC wire-format stability — when to lock the schema,
versioning strategy. Mirror in
project.yamlschema_version:when it lands. - Code editor quality bar — does the Editor tab need full LSP integration via the sidecar, or a lighter tree-sitter-based highlight-only view first? Decide during Tier 2.
Remaining assumptions (flag if wrong)
- Flutter Desktop on Linux + macOS is the primary target; Windows is a stretch goal. Mobile is not a goal.
- GitHub remote (
github.com/postmeridiem/clide) is the canonical host. pqlstays untouched for Tiers 0–2; upstream pql work happens in pql's repo during Tiers 3–5.- tmux is optional plumbing the sidecar can compose for team mode; not a hard dependency for Tier 1's session persistence (the sidecar holds the PTY fd directly).