diff --git a/CHANGELOG.md b/CHANGELOG.md index 258d09df..6c83bd57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,6 +53,25 @@ heading, and (b) bumping `project.yaml` `version:` in the same commit. - `make decisions-validate` — cheap parser dry-run wired into `push-check`. Catches malformed records before push. +- Reserved extension slots — `builtin.decisions`, `builtin.tickets`, + `builtin.claude-control`. Id-reserving stubs under + `app/lib/builtin/` with no contributions yet. Implementations land + once [`Q-021`](decisions/questions-architecture.md#q-021-pql-absorbs-planning-vs-keeps-separate) + resolves (decisions + tickets) or when the claude-control tier + arrives (`.claude/` first-class surface — distinct from the + existing `builtin.claude` PTY-pane stub). + +- `CLAUDE.md` — new "Decision discipline" guardrail pointing at + `decisions/`. + +### Changed + +- `CLAUDE.md` — inline ADR links rewritten to point at the migrated + `decisions/` records; bottom "Open questions" section collapsed to + a pointer at `decisions/questions-*.md`; parent-project note + updated to reference `decisions/architecture.md` instead of the + deleted `docs/ADRs/`. + ### Removed - `docs/ADRs/` directory — content lifted into `decisions/` as D/R diff --git a/CLAUDE.md b/CLAUDE.md index 2e832173..e8956a9a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -13,7 +13,7 @@ A Flutter desktop IDE for Claude Code. One language (Dart) across the stack, plu App ↔ daemon ↔ CLI speak a single JSON-lines unix socket protocol. The daemon outlives app restarts so Claude sessions survive reopens. Native rendering — markdown, canvas, graph — is Dart/Flutter (`CustomPaint` + widgets), not third-party packages. -Design doc: [`docs/initial-plan.md`](docs/initial-plan.md). Decisions: [`docs/ADRs/`](docs/ADRs/). Python Textual predecessor under [`legacy/`](legacy/). +Design doc: [`docs/initial-plan.md`](docs/initial-plan.md). Decisions: [`decisions/`](decisions/) (`D-NNN` confirmed, `Q-NNN` open, `R-NNN` rejected — see [`decisions/README.md`](decisions/README.md)). Python Textual predecessor under [`legacy/`](legacy/). ## Guardrails @@ -21,13 +21,14 @@ These are load-bearing. Violating any means the design is wrong, not the rule. - **Flutter desktop is the host. No Electron, ever.** Web target may work as a happy accident — don't compromise desktop fidelity for it. If we ship a web build at all, prefer Flutter's **WebAssembly (CanvasKit/Skwasm) compile** over the JS/HTML renderer: it matches the desktop rendering pipeline, keeps our custom `CustomPaint` components pixel-identical, and avoids the DOM-renderer quirks around input handling and terminal-style content. `xterm.dart` is the terminal renderer (Tier 1); markdown, canvas, graph are custom `CustomPaint`/widget components (Tiers 2+, 5). - **No heavy lifting in the UI layer.** The app renders and handles input; process/PTY/IO lifecycles live in the daemon. The daemon is Dart too — the split is *process boundary*, not language boundary. -- **CLI-first, not MCP.** Claude talks via Bash (`clide ...`), matching pql's contract. See [ADR 0001](docs/ADRs/0001-cli-first-not-mcp.md). -- **Dart is the core; native supporter tools fill specific gaps.** One Dart AOT binary for CLI + daemon. `ptyc` (C) for PTY spawning. `pql` (Go) for queries. No second "core language." See [ADR 0005](docs/ADRs/0005-dart-core-ptyc-peer.md) (supersedes [ADR 0002](docs/ADRs/0002-sidecar-language-go.md)). +- **CLI-first, not MCP.** Claude talks via Bash (`clide ...`), matching pql's contract. See [`D-001`](decisions/architecture.md#d-001-cli-first-not-mcp). +- **Dart is the core; native supporter tools fill specific gaps.** One Dart AOT binary for CLI + daemon. `ptyc` (C) for PTY spawning. `pql` (Go) for queries. No second "core language." See [`D-005`](decisions/architecture.md#d-005-dart-core-sidecar-dissolved-ptyc-as-pql-peer) (supersedes [`R-002`](decisions/rejected.md#r-002-go-sidecar)). - **Own the rendering stack.** PTY (via `ptyc`), markdown renderer, graph, canvas — all clide-owned, not pulled from opinionated packages. Third-party rendering is where we'd hit ceilings first; we'd rather pay the cost up front. -- **User/Claude parity.** Every CLI subcommand has a UI affordance in the app, and every UI action has a CLI. Events are symmetric: every UI state change is a subscribable event. See [ADR 0006](docs/ADRs/0006-cli-and-event-surface.md). -- **pql: wrap, don't duplicate, and treat it as a clide subsystem when present.** Pql logic only lives in `lib/src/pql/` (pure shell-outs). Clide owns pql's `ignore_files:` config key; it never touches pql's `.pql/` index/cache data. See [ADR 0003](docs/ADRs/0003-pql-as-supporter-tool.md). +- **User/Claude parity.** Every CLI subcommand has a UI affordance in the app, and every UI action has a CLI. Events are symmetric: every UI state change is a subscribable event. See [`D-006`](decisions/architecture.md#d-006-cli-and-event-surface-contract). +- **pql: wrap, don't duplicate, and treat it as a clide subsystem when present.** Pql logic only lives in `lib/src/pql/` (pure shell-outs). Clide owns pql's `ignore_files:` config key; it never touches pql's `.pql/` index/cache data. See [`D-003`](decisions/architecture.md#d-003-pql-as-supporter-tool-clide-wraps-never-duplicates). - **Repo-is-the-workspace.** The git repo root is the workspace — no parallel "vault" concept. Clide dogfoods against its own repo. -- **Ignore discipline.** Single knob: `ignore_files:` in `.pql/config.yaml`, ordered layering. Default `[.gitignore]`; clide writes `[.gitignore, .clideignore]` when `.clideignore` exists. See [ADR 0004](docs/ADRs/0004-ignore-file-strategy.md). +- **Ignore discipline.** Single knob: `ignore_files:` in `.pql/config.yaml`, ordered layering. Default `[.gitignore]`; clide writes `[.gitignore, .clideignore]` when `.clideignore` exists. See [`D-004`](decisions/architecture.md#d-004-ignore-file-strategy). +- **Decision discipline.** All architectural choices live in `decisions/.md` as `D-NNN` records. Open questions live in `decisions/questions-.md` as `Q-NNN`. Rejected alternatives live in `decisions/rejected.md` as `R-NNN`. Before an architectural change, read the relevant domain file; before disagreeing with a guardrail, propose an amendment to the underlying `D-NNN` rather than a one-off. Claim new IDs via `tools/scripts/plan decisions claim D "title"` (until pql ships planning subcommands — see [`Q-021`](decisions/questions-architecture.md#q-021-pql-absorbs-planning-vs-keeps-separate)). See [`decisions/README.md`](decisions/README.md). ## Tier ordering (don't skip ahead) @@ -44,7 +45,7 @@ See `docs/initial-plan.md` for the full tier definitions and acceptance criteria ## Parent projects - **`legacy/`** — Python Textual clide v1.2.0. Feature-frozen. Reference for the pane model, panel set, git skills (`/commit`, `/stash`, `/pull`, `/push` — rewire to `clide git …`), TODO.md parsing format. -- **`projects/claudian`** (April 2026, discarded) — 2-day experiment with an Obsidian-plugin approach. Its architectural patterns (CLI-first, pql-as-subsystem, ignore-file strategy, supply-chain gate, changelog discipline, commit conventions) are the ADRs and skills you see here. It first proposed a Go sidecar; ADR 0005 reversed that in favour of a Dart core. +- **`projects/claudian`** (April 2026, discarded) — 2-day experiment with an Obsidian-plugin approach. Its architectural patterns (CLI-first, pql-as-subsystem, ignore-file strategy, supply-chain gate, changelog discipline, commit conventions) are the records you see under `decisions/` and the skills under `.claude/skills/`. It first proposed a Go sidecar; [`D-005`](decisions/architecture.md#d-005-dart-core-sidecar-dissolved-ptyc-as-pql-peer) reversed that in favour of a Dart core. - **[`projects/pql`](https://github.com/postmeridiem/pql)** — active supporter tool. Clide depends on it; never duplicates it. ## Dependencies & supply chain @@ -81,7 +82,4 @@ One-time setup on a fresh clone: `make hooks && flutter pub get` once Flutter is ## Open questions -- `.canvas` schema compatibility with Obsidian — decide during Tier 5 spike. -- Extension API shape (widgets, subcommands, both) — decide during Tier 6. -- IPC wire-format stability + `schema_version:` in `project.yaml` — decide when the first real subcommand lands. -- Editor tab: full LSP integration via sidecar vs tree-sitter-only highlight — decide during Tier 2. +Open questions live under [`decisions/questions-*.md`](decisions/questions.md). diff --git a/app/lib/builtin/claude_control/claude_control.dart b/app/lib/builtin/claude_control/claude_control.dart new file mode 100644 index 00000000..b968b883 --- /dev/null +++ b/app/lib/builtin/claude_control/claude_control.dart @@ -0,0 +1 @@ +export 'src/extension.dart'; diff --git a/app/lib/builtin/claude_control/src/extension.dart b/app/lib/builtin/claude_control/src/extension.dart new file mode 100644 index 00000000..b0d82f97 --- /dev/null +++ b/app/lib/builtin/claude_control/src/extension.dart @@ -0,0 +1,23 @@ +import 'package:clide_app/extension/extension.dart'; + +/// Tier-reserved stub. Will surface a sidebar tab with sub-tabs +/// Settings / Skills / Agents / Hooks / MCP — `.claude/` as a +/// first-class IDE surface. Orthogonal to pql; purely clide-internal. +/// Commands: `claude.settings.open`, `claude.skills.new`, +/// `claude.skills.edit`, `claude.agents.new`, `claude.agents.edit`, +/// `claude.hooks.log`, `claude.mcp.status`. +/// +/// Distinct from `builtin.claude`, which is reserved for Tier 1's +/// "run Claude Code in a PTY pane." +class ClaudeControlExtension extends ClideExtension { + @override + String get id => 'builtin.claude-control'; + @override + String get title => 'Claude control'; + @override + String get version => '0.0.0-stub'; + @override + List get dependsOn => const []; + @override + List get contributions => const []; +} diff --git a/app/lib/builtin/decisions/decisions.dart b/app/lib/builtin/decisions/decisions.dart new file mode 100644 index 00000000..b968b883 --- /dev/null +++ b/app/lib/builtin/decisions/decisions.dart @@ -0,0 +1 @@ +export 'src/extension.dart'; diff --git a/app/lib/builtin/decisions/src/extension.dart b/app/lib/builtin/decisions/src/extension.dart new file mode 100644 index 00000000..1202c9f1 --- /dev/null +++ b/app/lib/builtin/decisions/src/extension.dart @@ -0,0 +1,19 @@ +import 'package:clide_app/extension/extension.dart'; + +/// Tier-reserved stub. Will surface a sidebar tab (filter by domain / +/// status, backlinks from current file) + commands (`decisions.open`, +/// `decisions.claim`, `decisions.amend`). Data source: `pql decisions +/// …` once `Q-021` resolves; `tools/scripts/plan decisions …` until +/// then. +class DecisionsExtension extends ClideExtension { + @override + String get id => 'builtin.decisions'; + @override + String get title => 'Decisions'; + @override + String get version => '0.0.0-stub'; + @override + List get dependsOn => const []; + @override + List get contributions => const []; +} diff --git a/app/lib/builtin/tickets/src/extension.dart b/app/lib/builtin/tickets/src/extension.dart new file mode 100644 index 00000000..777cdbc4 --- /dev/null +++ b/app/lib/builtin/tickets/src/extension.dart @@ -0,0 +1,19 @@ +import 'package:clide_app/extension/extension.dart'; + +/// Tier-reserved stub. Will surface a sidebar tab (filtered list) + a +/// workspace tab (kanban board) + commands (`tickets.open`, +/// `tickets.new`, `tickets.move`, `tickets.block`). Data source: `pql +/// ticket …` once `Q-021` resolves; `tools/scripts/plan ticket …` until +/// then. Ticket persistence strategy open at `Q-022`. +class TicketsExtension extends ClideExtension { + @override + String get id => 'builtin.tickets'; + @override + String get title => 'Tickets'; + @override + String get version => '0.0.0-stub'; + @override + List get dependsOn => const []; + @override + List get contributions => const []; +} diff --git a/app/lib/builtin/tickets/tickets.dart b/app/lib/builtin/tickets/tickets.dart new file mode 100644 index 00000000..b968b883 --- /dev/null +++ b/app/lib/builtin/tickets/tickets.dart @@ -0,0 +1 @@ +export 'src/extension.dart'; diff --git a/app/lib/main.dart b/app/lib/main.dart index 685ff17d..f05638ec 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -1,6 +1,8 @@ import 'package:clide_app/app.dart'; import 'package:clide_app/builtin/canvas/canvas.dart'; import 'package:clide_app/builtin/claude/claude.dart'; +import 'package:clide_app/builtin/claude_control/claude_control.dart'; +import 'package:clide_app/builtin/decisions/decisions.dart'; import 'package:clide_app/builtin/default_layout/default_layout.dart'; import 'package:clide_app/builtin/diff/diff.dart'; import 'package:clide_app/builtin/editor/editor.dart'; @@ -18,6 +20,7 @@ import 'package:clide_app/builtin/problems/problems.dart'; import 'package:clide_app/builtin/settings_ui/settings_ui.dart'; import 'package:clide_app/builtin/terminal/terminal.dart'; import 'package:clide_app/builtin/theme_picker/theme_picker.dart'; +import 'package:clide_app/builtin/tickets/tickets.dart'; import 'package:clide_app/builtin/todos/todos.dart'; import 'package:clide_app/builtin/welcome/welcome.dart'; import 'dart:io' show Directory, Platform; @@ -75,7 +78,10 @@ Future main() async { ..register(GraphExtension()) ..register(SettingsUiExtension()) ..register(ExtensionsUiExtension()) - ..register(KeybindingsUiExtension()); + ..register(KeybindingsUiExtension()) + ..register(DecisionsExtension()) + ..register(TicketsExtension()) + ..register(ClaudeControlExtension()); await services.extensions.activateAll();