diff --git a/.githooks/pre-push b/.githooks/pre-push index e5101045..7319b82a 100755 --- a/.githooks/pre-push +++ b/.githooks/pre-push @@ -8,11 +8,5 @@ set -euo pipefail cd "$(git rev-parse --show-toplevel)" -# `make tools` installs Go-installed binaries into $HOME/go/bin (or -# $GOBIN if set). Users routinely forget to put that on PATH, so the -# hook prepends it here to avoid spurious "command not found" failures -# for govulncheck / goimports / golangci-lint. -export PATH="${GOBIN:-$HOME/go/bin}:$PATH" - echo "==> pre-push: make push-check" make push-check diff --git a/.gitignore b/.gitignore index 41fc9a28..8f550862 100644 --- a/.gitignore +++ b/.gitignore @@ -1,25 +1,24 @@ -# -- Flutter / Dart (app/) ---------------------------------------------- -/app/.dart_tool/ -/app/.packages -/app/.flutter-plugins -/app/.flutter-plugins-dependencies -/app/build/ -/app/.idea/ -/app/.vscode/ -/app/*.iml -/app/ios/Pods/ -/app/macos/Pods/ -/app/windows/flutter/ephemeral/ -/app/linux/flutter/ephemeral/ -/app/macos/Flutter/ephemeral/ +# -- Flutter / Dart (app + core at repo root) --------------------------- +/.dart_tool/ +/.packages +/.flutter-plugins +/.flutter-plugins-dependencies +/build/ +/.idea/ +/.vscode/ +/*.iml +/ios/Pods/ +/macos/Pods/ +/windows/flutter/ephemeral/ +/linux/flutter/ephemeral/ +/macos/Flutter/ephemeral/ +/bin/clide +/bin/clide.exe # pubspec.lock is committed (supply-chain policy). -# -- Go sidecar --------------------------------------------------------- -/sidecar/bin/ -/sidecar/dist/ -/sidecar/vendor/ -/clide -/clide.exe +# -- ptyc (C supporter tool) -------------------------------------------- +/ptyc/bin/ +/ptyc/*.o # -- Test, coverage, profile output ------------------------------------ *.test @@ -29,10 +28,6 @@ coverage.* /tmp/ -# -- Go module / workspace artefacts (we don't vendor or use workspaces) -/go.work -/go.work.sum - # -- pql per-repo state (clide dogfoods against itself; pql's index lands # here when running queries locally). Mirror what `pql init` writes. /.pql/ diff --git a/CHANGELOG.md b/CHANGELOG.md index 2b25c9fe..136fb272 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,12 +16,23 @@ heading, and (b) bumping `project.yaml` `version:` in the same commit. ## [Unreleased] +### Removed + +- Go sidecar skeleton under `sidecar/` — `cmd/clide/main.go`, `go.mod`, and the `internal/*` packages (`cli`, `daemon`, `diag`, `git`, `ipc`, `pql`, `proc`, `pty`, `version`). Deleted wholesale per [ADR 0005](docs/ADRs/0005-dart-core-ptyc-peer.md): the "sidecar language: Go" premise no longer holds once the core is Dart. All functionality listed for those packages will be reimplemented under `lib/` as part of Tier 0. +- Go-specific Makefile targets (`lint`, `vuln`, `test-race`, `fmt`, `tidy`, `snapshot`, `tools`, `install` via Go), the `govulncheck`/`goimports`/`golangci-lint` version pins, and the pre-push hook's `GOBIN` PATH injection. Replaced with Dart/Flutter equivalents (`analyze`, `format`, `test`, `test-integration`, `build` via `dart compile exe`). +- `module:` and `go_version:` from `project.yaml` — single-language core means no Go module path to track. + ### Changed -- `.gitignore` rewritten for the new toolchain: Flutter (build output under `app/`), Dart (`.dart_tool/`), Go sidecar (`sidecar/bin/`, `sidecar/dist/`, the legacy `/clide` binary), plus the usual test/OS/editor/secret rules. Python-specific rules narrow to `legacy/**` where they still apply. +- [ADR 0002](docs/ADRs/0002-sidecar-language-go.md) marked **superseded** by [ADR 0005](docs/ADRs/0005-dart-core-ptyc-peer.md). The "sidecar language: Go" guardrail is retired. CLAUDE.md's guardrails, dependency notes, and command reference are updated to reflect the Dart-core direction. +- `.gitignore` retargeted: Flutter/Dart output at the repo root (`.dart_tool/`, `build/`, platform ephemeral dirs, `bin/clide`), plus a `ptyc/` section for the C helper's build artefacts. Go-specific rules removed. +- `ci/lint.sh`, `ci/test.sh`, `ci/security.sh`, and `.githooks/pre-push` rewritten for the Dart toolchain — no Go shell-outs, no `GOBIN` PATH dance. ### Added +- [ADR 0005](docs/ADRs/0005-dart-core-ptyc-peer.md) — Dart core; sidecar directory dissolved; `ptyc` as 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](docs/ADRs/0006-cli-and-event-surface.md) — 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 `claudian` plugin project (discarded in favour of this Flutter rebuild): diff --git a/CLAUDE.md b/CLAUDE.md index c45658a8..2e832173 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,13 +4,14 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## What clide is -A Flutter desktop IDE for Claude Code. Three surfaces, one coherent tool: +A Flutter desktop IDE for Claude Code. One language (Dart) across the stack, plus small native supporter tools where Dart can't reach. - **`app/`** — Flutter desktop application (Linux / macOS primary; Windows stretch). -- **`sidecar/`** — Go sidecar/CLI, single binary with two modes: `clide ` (one-shot for Claude) and `clide --daemon` (long-running for the app). Owns PTYs, subprocesses, file watchers, git, pql invocations. +- **`lib/` + `bin/clide.dart`** — Dart core, shared by the app and by one AOT-compiled binary that has two modes: `clide ` (one-shot for Claude) and `clide --daemon` (long-running for the app). Owns IPC, PTYs (via `ptyc`), subprocesses, file watchers, git shell-outs, pql invocations. - **[`pql`](https://github.com/postmeridiem/pql)** — external supporter tool. Clide wraps it for every query surface; never re-implements it. +- **`ptyc`** — small C supporter tool, peer of pql. Spawns a PTY + child and hands the master fd back over `SCM_RIGHTS`. Clide shells out to it for every pane (shell, tmux, claude, LSP, debug adapter). -App ↔ sidecar ↔ CLI speak a single JSON-lines unix socket protocol. The sidecar outlives app restarts so Claude sessions survive reopens. +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/). @@ -18,19 +19,20 @@ Design doc: [`docs/initial-plan.md`](docs/initial-plan.md). Decisions: [`docs/AD 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. `xterm.dart` is the terminal renderer. -- **No heavy lifting in the UI layer.** Flutter app renders and handles input. PTYs/subprocesses/filesystem/git all live in the sidecar. +- **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). -- **Sidecar language: Go.** Module `git.schweitz.net/jpmschweitzer/clide/sidecar`. See [ADR 0002](docs/ADRs/0002-sidecar-language-go.md). -- **User/Claude parity.** Every CLI subcommand has a UI affordance in the app, and every UI action has a CLI. If you add one side without the other, the feature is incomplete. -- **pql: wrap, don't duplicate, and treat it as a clide subsystem when present.** Pql logic only lives in `sidecar/internal/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). +- **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)). +- **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). - **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). ## Tier ordering (don't skip ahead) -1. **Tier 0** — Flutter app + sidecar daemon handshake, empty IDE shell. -2. **Tier 1** — Claude in an `xterm.dart` pane; session persists across app restarts. +1. **Tier 0** — Flutter app + Dart daemon handshake, empty IDE shell. +2. **Tier 1** — Claude in an `xterm.dart` pane backed by `ptyc`-spawned PTYs; session persists across app restarts. 3. **Tier 2** — Pane model + active-file awareness + `clide open/active/insert/replace-selection/tail`. 4. **Tier 3** — Git panel (staged/unstaged, hunk stage, conflict UI) + diff tab + `clide git …`. 5. **Tier 4** — pql integration: Query panel, file tree, backlinks, problems — all drawing from pql. @@ -42,37 +44,36 @@ 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 (Go sidecar, CLI-first, pql-as-subsystem, ignore-file strategy, supply-chain gate, changelog discipline, commit conventions) are the ADRs and skills you see here. +- **`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/pql`](https://github.com/postmeridiem/pql)** — active supporter tool. Clide depends on it; never duplicates it. ## Dependencies & supply chain -- **Go (sidecar):** exact-pin versions (never `@latest`). `make vuln` (`govulncheck ./...`) runs after every dep add/bump and gates CI. -- **Dart (app):** prefer-zero-deps. Flutter-SDK widgets first; third-party packages need justification. What stays is exact-pinned in `pubspec.yaml` (no caret ranges). Advisories reviewed before every bump; `pubspec.lock` committed. -- `go.sum` and `pubspec.lock` are always committed. -- `make security` aggregates the Go and Dart CVE gates; `ci/security.sh` is the CI entry. +- **Dart (core + app):** prefer-zero-deps. Flutter-SDK widgets first; third-party packages need justification. What stays is exact-pinned in `pubspec.yaml` (no caret ranges). Advisories reviewed before every bump; `pubspec.lock` committed. +- **`ptyc` and any future native supporter tool:** no dep graph by design (libc-only for `ptyc`). "Audit" is reading the source before each bump. +- `pubspec.lock` is always committed. +- `make security` runs the Dart advisory review; `ci/security.sh` is the CI entry. ## Commands -(Placeholder until Phase 3 commits land these targets.) +(Placeholder until Tier 0 commits land these targets in real form.) ``` -make build # build sidecar/bin/clide -make test # go test ./... -make test-race # with race detector -make lint # golangci-lint -make vuln # govulncheck (sidecar CVE gate) -make app-build # flutter build linux / macos -make app-test # flutter test -make app-analyze # dart analyze -make security # run all CVE gates (go + dart) -make push-check # full pre-push gate: lint + test + test-race + vuln + app-analyze + app-test +make build # dart compile exe bin/clide.dart -o bin/clide +make test # flutter test +make test-integration# daemon + CLI + fixture-repo suite +make analyze # flutter analyze +make format # dart format --set-exit-if-changed +make build-linux # flutter build linux +make build-macos # flutter build macos +make ptyc-build # build the ptyc PTY-spawn helper +make security # Dart advisory review + ptyc source review +make push-check # pre-push gate: analyze + format + test make hooks # install the repo's git hooks (one-time setup) -make tools # install Go tooling at pinned versions (govulncheck, goimports, golangci-lint) make clean # remove build artefacts ``` -One-time setup on a fresh clone: `make tools && make hooks`, plus `cd app && flutter pub get` once Flutter is installed. +One-time setup on a fresh clone: `make hooks && flutter pub get` once Flutter is installed. ## Changelog discipline diff --git a/Makefile b/Makefile index 6a8721bc..8855a57b 100644 --- a/Makefile +++ b/Makefile @@ -1,154 +1,139 @@ # clide — local dev targets. CI scripts in ci/ shell out to these. # -# Two components under one Makefile: -# - Go sidecar/CLI under sidecar/ -# - Flutter desktop app under app/ (scaffolded once Flutter is installed -# locally; targets gracefully noop when app/ isn't present yet). +# One toolchain: Flutter + Dart. Native supporter tools (`ptyc`, future +# peers) live in their own directories with their own build targets and +# compose in under `make build`. -GO ?= go -BIN_DIR ?= sidecar/bin INSTALL_DIR ?= $(HOME)/.local/bin # Version stamping. Source of truth: project.yaml `version:` field. Local # builds augment with git short SHA + dirty marker (semver build metadata). -# Tagged releases are handled by goreleaser using the git tag instead. VERSION_BASE ?= $(shell awk -F': *' '/^version:/ {gsub(/[" ]/,"",$$2); print $$2; exit}' project.yaml) COMMIT ?= $(shell git rev-parse --short HEAD 2>/dev/null || echo unknown) DIRTY := $(shell git diff --quiet HEAD 2>/dev/null || echo .dirty) VERSION ?= $(VERSION_BASE)+$(COMMIT)$(DIRTY) DATE ?= $(shell date -u +%Y-%m-%dT%H:%M:%SZ) -LDFLAGS := -s -w \ - -X 'git.schweitz.net/jpmschweitzer/clide/sidecar/internal/version.Version=$(VERSION)' \ - -X 'git.schweitz.net/jpmschweitzer/clide/sidecar/internal/version.Commit=$(COMMIT)' \ - -X 'git.schweitz.net/jpmschweitzer/clide/sidecar/internal/version.Date=$(DATE)' - -GO_PACKAGES := ./... - .PHONY: help help: ## Show this help. @awk 'BEGIN {FS = ":.*##"} /^[a-zA-Z0-9_-]+:.*##/ {printf " \033[36m%-22s\033[0m %s\n", $$1, $$2}' $(MAKEFILE_LIST) -# -- sidecar (Go) -------------------------------------------------------- +# -- app + core (Flutter / Dart) ---------------------------------------- -.PHONY: build -build: ## Build the clide sidecar/CLI binary into sidecar/bin/clide. - cd sidecar && $(GO) build -ldflags="$(LDFLAGS)" -o bin/clide ./cmd/clide - -.PHONY: install -install: build ## Install sidecar/bin/clide into $(INSTALL_DIR). - install -m 0755 $(BIN_DIR)/clide $(INSTALL_DIR)/clide - -.PHONY: test -test: ## Unit tests (Go), fast. - cd sidecar && $(GO) test $(GO_PACKAGES) - -.PHONY: test-race -test-race: ## Unit tests with the race detector. - cd sidecar && $(GO) test -race $(GO_PACKAGES) - -.PHONY: test-integration -test-integration: build ## Integration tests (binary + fixture repos). Tag: integration. - cd sidecar && $(GO) test -tags=integration ./... - -.PHONY: lint -lint: ## golangci-lint run on the sidecar. - cd sidecar && golangci-lint run - -.PHONY: vuln -vuln: ## govulncheck on all sidecar packages — Go CVE gate. - cd sidecar && govulncheck ./... - -.PHONY: fmt -fmt: ## gofmt + goimports on the sidecar. - cd sidecar && gofmt -w . - @command -v goimports >/dev/null && cd sidecar && goimports -w . || echo "(goimports not installed; skipping)" - -.PHONY: tidy -tidy: ## go mod tidy for the sidecar. - cd sidecar && $(GO) mod tidy - -.PHONY: snapshot -snapshot: ## GoReleaser snapshot build (dry-run, no publish). - goreleaser release --snapshot --clean - -# -- app (Flutter) ------------------------------------------------------- - -# App targets gracefully noop if app/ doesn't exist yet (pre-Tier-0 -# scaffold) or if flutter isn't installed locally. -APP_PRESENT := $(shell test -f app/pubspec.yaml && echo yes || echo no) +# App and core targets gracefully noop if the Dart package isn't +# scaffolded yet (pre-Tier-0 state) or if flutter isn't installed +# locally. +APP_PRESENT := $(shell test -f pubspec.yaml && echo yes || echo no) HAS_FLUTTER := $(shell command -v flutter >/dev/null && echo yes || echo no) -.PHONY: app-pubget -app-pubget: ## flutter pub get (hydrate the app's pub cache). +.PHONY: build +build: ## Build the Dart AOT `clide` binary (CLI + --daemon modes in one binary). ifeq ($(APP_PRESENT),yes) - cd app && flutter pub get + dart compile exe bin/clide.dart -o bin/clide \ + --define=clideVersion=$(VERSION) \ + --define=clideCommit=$(COMMIT) \ + --define=clideDate=$(DATE) else - @echo "(app/ not scaffolded yet; skipping)" + @echo "(pubspec.yaml not scaffolded yet; skipping)" endif -.PHONY: app-analyze -app-analyze: ## dart analyze on the app. +.PHONY: install +install: build ## Install bin/clide into $(INSTALL_DIR). ifeq ($(APP_PRESENT),yes) - cd app && flutter analyze + install -m 0755 bin/clide $(INSTALL_DIR)/clide else - @echo "(app/ not scaffolded yet; skipping)" + @echo "(pubspec.yaml not scaffolded yet; skipping)" endif -.PHONY: app-format -app-format: ## dart format on the app. +.PHONY: pubget +pubget: ## flutter pub get (hydrate the pub cache). ifeq ($(APP_PRESENT),yes) - cd app && dart format --set-exit-if-changed . + flutter pub get else - @echo "(app/ not scaffolded yet; skipping)" + @echo "(pubspec.yaml not scaffolded yet; skipping)" endif -.PHONY: app-test -app-test: ## flutter test on the app. +.PHONY: analyze +analyze: ## dart analyze / flutter analyze. ifeq ($(APP_PRESENT),yes) - cd app && flutter test + flutter analyze else - @echo "(app/ not scaffolded yet; skipping)" + @echo "(pubspec.yaml not scaffolded yet; skipping)" endif -.PHONY: app-build-linux -app-build-linux: ## flutter build linux. +.PHONY: format +format: ## dart format --set-exit-if-changed. ifeq ($(APP_PRESENT),yes) - cd app && flutter build linux + dart format --set-exit-if-changed . else - @echo "(app/ not scaffolded yet; skipping)" + @echo "(pubspec.yaml not scaffolded yet; skipping)" endif -.PHONY: app-build-macos -app-build-macos: ## flutter build macos. +.PHONY: test +test: ## flutter test (unit + widget). ifeq ($(APP_PRESENT),yes) - cd app && flutter build macos + flutter test else - @echo "(app/ not scaffolded yet; skipping)" + @echo "(pubspec.yaml not scaffolded yet; skipping)" +endif + +.PHONY: test-integration +test-integration: build ## Integration tests (daemon + CLI + fixture repos). +ifeq ($(APP_PRESENT),yes) + flutter test integration_test || echo "(no integration_test suite yet)" +else + @echo "(pubspec.yaml not scaffolded yet; skipping)" +endif + +.PHONY: build-linux +build-linux: ## flutter build linux (desktop bundle). +ifeq ($(APP_PRESENT),yes) + flutter build linux +else + @echo "(pubspec.yaml not scaffolded yet; skipping)" +endif + +.PHONY: build-macos +build-macos: ## flutter build macos (desktop bundle). +ifeq ($(APP_PRESENT),yes) + flutter build macos +else + @echo "(pubspec.yaml not scaffolded yet; skipping)" +endif + +# -- ptyc (C supporter tool) -------------------------------------------- + +# Compiled alongside the main binary. Tiny, no deps beyond libc. +PTYX_PRESENT := $(shell test -f ptyc/Makefile && echo yes || echo no) + +.PHONY: ptyc-build +ptyc-build: ## Build the ptyc PTY-spawn helper. +ifeq ($(PTYX_PRESENT),yes) + $(MAKE) -C ptyc +else + @echo "(ptyc/ not scaffolded yet; skipping)" +endif + +.PHONY: ptyc-clean +ptyc-clean: ## Clean ptyc build artefacts. +ifeq ($(PTYX_PRESENT),yes) + $(MAKE) -C ptyc clean +else + @echo "(ptyc/ not scaffolded yet; skipping)" endif # -- security ------------------------------------------------------------ .PHONY: security -security: vuln ## Run all CVE gates. For now: Go (govulncheck). Dart CVE gate lands when a reliable tooling exists. - -# -- tooling ------------------------------------------------------------- - -# Pinned Go tool versions. Bump deliberately; never floating. -GOVULNCHECK_VERSION ?= v1.1.4 -GOIMPORTS_VERSION ?= v0.29.0 -GOLANGCI_LINT_VERSION ?= v2.11.4 - -.PHONY: tools -tools: ## Install Go tooling at pinned versions (govulncheck, goimports, golangci-lint). - $(GO) install golang.org/x/vuln/cmd/govulncheck@$(GOVULNCHECK_VERSION) - $(GO) install golang.org/x/tools/cmd/goimports@$(GOIMPORTS_VERSION) - $(GO) install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCI_LINT_VERSION) +security: ## Dart advisory review + ptyc source review (manual — no floating deps). + @echo "security: Dart advisories reviewed manually before pubspec.yaml bumps;" + @echo " ptyc is reviewed by reading it (tiny libc-only C)." + @echo " Automated Dart CVE tooling lands here when a reliable option exists." # -- pre-push gate ------------------------------------------------------- .PHONY: push-check -push-check: lint test test-race test-integration vuln app-analyze app-test ## Full pre-push gate — everything that must pass before a push. +push-check: analyze format test ## Full pre-push gate — everything that must pass before a push. .PHONY: hooks hooks: ## Install the repo's git hooks (points core.hooksPath at .githooks/). @@ -159,9 +144,7 @@ hooks: ## Install the repo's git hooks (points core.hooksPath at .githooks/). .PHONY: clean clean: ## Remove build artefacts. - rm -rf sidecar/bin sidecar/dist -ifeq ($(APP_PRESENT),yes) - rm -rf app/build app/.dart_tool -endif + rm -rf bin build .dart_tool + $(MAKE) ptyc-clean .DEFAULT_GOAL := help diff --git a/ci/lint.sh b/ci/lint.sh index 2082ad70..c7ebc8d9 100755 --- a/ci/lint.sh +++ b/ci/lint.sh @@ -7,6 +7,6 @@ set -euo pipefail cd "$(dirname "$0")/.." -make lint -make app-analyze +make analyze +make format make security diff --git a/ci/security.sh b/ci/security.sh index 6775d344..d51f368c 100755 --- a/ci/security.sh +++ b/ci/security.sh @@ -3,8 +3,9 @@ # local dev and CI run the same commands. # # See ~/.claude/projects/-var-mnt-data-projects-clide/memory/ for the -# standing requirements: Go deps must be version-locked and CVE-checked; -# Dart deps prefer zero, with what remains pinned and audited. +# standing requirement: Dart deps prefer zero; what remains is pinned +# and advisory-audited before bumping. Native supporter tools (ptyc, any +# future peer) are reviewed by reading them — they have no dep graph. set -euo pipefail cd "$(dirname "$0")/.." diff --git a/ci/test.sh b/ci/test.sh index 385850ea..b5936cc7 100755 --- a/ci/test.sh +++ b/ci/test.sh @@ -5,5 +5,4 @@ set -euo pipefail cd "$(dirname "$0")/.." make test -make test-race -make app-test +make test-integration diff --git a/docs/ADRs/0002-sidecar-language-go.md b/docs/ADRs/0002-sidecar-language-go.md index d1b698b6..d696792a 100644 --- a/docs/ADRs/0002-sidecar-language-go.md +++ b/docs/ADRs/0002-sidecar-language-go.md @@ -1,7 +1,8 @@ # ADR 0002 — Sidecar language: Go -**Status:** accepted +**Status:** superseded by [ADR 0005](0005-dart-core-ptyc-peer.md) **Date:** 2026-04-20 (ported from the claudian lineage) +**Superseded:** 2026-04-20 — the "separate language" premise dissolved once we chose Dart for the core; see 0005 for the rationale. ## Context diff --git a/docs/ADRs/0005-dart-core-ptyc-peer.md b/docs/ADRs/0005-dart-core-ptyc-peer.md new file mode 100644 index 00000000..709d74e8 --- /dev/null +++ b/docs/ADRs/0005-dart-core-ptyc-peer.md @@ -0,0 +1,163 @@ +# ADR 0005 — Dart core; sidecar directory dissolved; `ptyc` as pql-peer + +**Status:** accepted +**Date:** 2026-04-20 +**Supersedes:** [ADR 0002](0002-sidecar-language-go.md) + +## Context + +ADR 0002 picked Go for the sidecar/CLI on the reasoning that (a) the +heavy work (PTYs, subprocesses, file watchers, git, IPC) belongs in +a language separate from the UI layer, and (b) pql is Go so the +muscle memory transfers. The Flutter app would talk to a Go binary +over a unix socket. + +On reassessment, two facts broke that reasoning: + +1. **What the "heavy work" actually is.** Stripped of the PTY layer, + the sidecar is I/O-bound glue around shell-outs (`git`, `claude`, + `pql`), a unix-socket server, JSON-lines framing, and a process + table. `dart:io` covers all of this cleanly. The Go-versus-Rust + debate implicit in 0002 was the wrong axis — the real choice was + **separate process vs shared language**, and separate-process is + what matters (session persistence needs the daemon to outlive the + app), not language. + +2. **PTY is the one place Dart is genuinely weak** — and not because + of ecosystem, but because Dart's multi-threaded VM can't safely + `fork()`. That single constraint forces a native helper regardless + of what language wraps it. Once you accept a small native helper, + the question is whether *everything else* needs to be in that same + native language. It doesn't. + +So the "sidecar" directory stopped carrying weight. It existed to +justify the Go/Dart split. With the split gone, the directory is +ceremony. + +## Decision + +Three moves. + +### 1. Dart is the core language. + +Everything that used to live under `sidecar/` — IPC server, CLI +dispatch, process management, file watching, git shell-outs, pql +wrapper — is written in Dart. Two execution modes of one Dart AOT +binary: + +- `clide ` — one-shot, pql-style. Parses args, connects + to the running daemon socket, sends a request, prints JSON on + stdout, exits with the pql exit-code contract. +- `clide --daemon` — long-running. Owns PTYs, subscriptions, file + watchers, subprocess lifecycles. Started by the app on load; + survives app restarts so Claude sessions persist. + +The Flutter app imports the Dart core as a library for in-process +state (views, widgets, models) *and* connects to the daemon over the +same IPC the CLI uses. One protocol, two clients. + +### 2. The sidecar directory dissolves. + +``` +app/ # Flutter UI (Linux / macOS primary) +lib/ # Dart core shared by app + CLI + daemon: + # ipc/, pty/, proc/, git/, pql/, events/, panes/ +bin/clide.dart # Dart AOT entry: subcommand dispatch + --daemon +tool/ # Dart scripts used by the Makefile +tests/ # integration tests that span app + daemon +``` + +No `sidecar/`. No Go module. `project.yaml` drops `module:`; the +Dart package name replaces it. + +### 3. `ptyc` is a pql-peer supporter tool. + +The PTY helper — a small C binary that does `posix_openpt` + `fork` ++ `exec` + fd-passing via `SCM_RIGHTS` — graduates to the same +status as pql: single-purpose, language-appropriate, standalone, +reusable outside Clide. It lives in its own directory (eventually +its own repo) and Clide wraps it the same way it wraps pql. Working +name: **`ptyc`**. + +- Clide shells out to `ptyc` to spawn every PTY (terminal pane, + tmux session, claude, LSP server, debug adapter — all one code + path). +- `ptyc` writes only what it needs to write (a PTY + forked child) + and does nothing else. No IPC protocol of its own, no long-running + state. One-shot per pane. +- Consumers other than Clide (a Python script, another Dart app, a + Go tool) can use `ptyc` standalone with no Clide dependency. + +This mirrors ADR 0003's pql contract: **wrap, don't duplicate**; +supporter tools stay independent and reusable. + +## Consequences + +- **ADR 0002 is superseded.** Go sidecar removed. Existing + `sidecar/` contents (Go skeleton — `cmd/clide/`, `internal/*`, + `go.mod`) are deleted; the ideas it encoded (exit-code contract, + ldflag-stamped version, JSON diagnostics) are reimplemented in + Dart. The supersession note stays in 0002 so the history reads + correctly. +- **One toolchain for the IDE proper.** Flutter + Dart. The C + toolchain is needed only to build `ptyc` — a tiny, rarely-changing + artifact. +- **`project.yaml` simplifies.** `module:` and `go_version:` go + away. A `ptyc_version:` pin joins the existing `dart_sdk:` and + `flutter_channel:` keys. +- **Supply-chain gates stay, shape changes.** The Go gate + (`govulncheck`) is removed. The Dart gate stays (advisories review + + exact-pin `pubspec.yaml`). `ptyc` gets its own tiny gate: it has + no deps beyond libc, so the review is "read the 150 lines before + every bump." `make security` becomes `make security` = Dart + advisories + `ptyc` review checklist. +- **IPC stays.** The daemon / app / CLI split is unchanged — unix + socket, token auth, JSON-lines. It was never about language. +- **Session persistence stays.** PTY master fds live in the Dart + daemon process, not the app process. App restart does not kill + Claude. +- **Pql continues as-is.** Wrapped via shell-out from + `lib/src/pql/` (the Dart equivalent of the deleted + `sidecar/internal/pql/`). No protocol change to pql. +- **CLAUDE.md and the Makefile need updates.** Commands, directory + references, and the "sidecar language: Go" guardrail all shift. +- **Rust remains an escape hatch, not a plan.** If a Dart limit + later forces a second native helper (file-watching at scale on + macOS, a tree-sitter host, etc.), the precedent set here is: new + native need → new supporter tool, peer of pql and `ptyc`. Never a + second "core language." + +## Notes + +- **Name: `ptyc`** (pronounced "p-tic"). Three honest readings, all + pointing at the same tool: + 1. **Project Terminal Controller** — parallel to pql's **Project + Query Language**. Clide's supporter tools follow a `p*` pattern + where `p` = *project*: pql handles project queries, ptyc + handles project terminals. Future supporter tools that fit the + "small single-purpose peer of pql" slot should follow the same + pattern when the fit is natural. + 2. **PTY + child** — domain vocabulary (PTY parent/child pair). + This is what a reader seeing the name on a command line will + decode it as, and it's exactly what the tool does: run a child + process under a PTY. + 3. **PTY + C** — the implementation language. Accurate and + non-limiting; Unix has a long tradition of tools advertising + their implementation (`gcc`, `libc`, `musl`). If we ever + rewrote it in another language it would become a new tool with + a new name, same as pql would if rewritten. + + Crucially, none of the readings tie the **caller** to any + ecosystem — `ptyc` is usable from Dart, Python, Go, shell, + anywhere a subprocess can be spawned and a fd can be received. + Alternatives considered and rejected: `clide-pty-spawn` (too + clide-specific for a peer tool), `dpty` (already taken on + crates.io), `ptyspawn` (verbose), `dartmx` (falsely signals + caller-ecosystem + implies multiplex), `ptyx` (arbitrary suffix, + no domain reading), `ptyc` as read-only "PTY C" (works but sells + the name short). +- The helper's wire contract (stdin JSON → stdout JSON + SCM_RIGHTS + fd transfer) is intentionally small so wrapping it is trivial from + any language. +- This ADR does not define the Clide CLI / event surface itself — + that is [ADR 0006](0006-cli-and-event-surface.md). diff --git a/docs/ADRs/0006-cli-and-event-surface.md b/docs/ADRs/0006-cli-and-event-surface.md new file mode 100644 index 00000000..2adb97ac --- /dev/null +++ b/docs/ADRs/0006-cli-and-event-surface.md @@ -0,0 +1,190 @@ +# ADR 0006 — CLI and event surface contract + +**Status:** accepted +**Date:** 2026-04-20 + +## Context + +[ADR 0001](0001-cli-first-not-mcp.md) established that Claude drives +Clide via a Bash CLI, not MCP. That decided the *channel*. It did +not define the *surface* — which subsystems exist, how commands +relate to events, what the JSON looks like on the wire, how +subscribers discover state changes. + +CLAUDE.md states the rule colloquially: "every CLI subcommand has a +UI affordance in the app, and every UI action has a CLI. If you add +one side without the other, the feature is incomplete." This ADR +restates that as an implementable contract. + +The shape needs to satisfy three things at once: + +1. **User/Claude parity.** Anything the user can do with a mouse, + Claude can do with `clide <...>`. Anything the user can observe + in the UI, Claude can observe via events. +2. **Daemon as authoritative state.** The app and the CLI are both + clients. State lives in the `clide --daemon` process; commands + mutate it; events broadcast changes to all subscribers. +3. **pql-style ergonomics.** One tool-use pattern for Claude across + pql and clide — same exit codes, same JSON-on-stdout habit, same + stderr-for-diagnostics rule. + +## Decision + +The CLI is organised into **subsystems**. Each subsystem owns a +noun, a set of verbs, and a set of events. The set is closed at any +point in time (documented); growth is additive (new verbs, new +events — never renaming existing ones without a version bump). + +### Subsystem list (initial, by tier) + +| Subsystem | Tier | Nouns | Representative verbs | Representative events | +|---|---|---|---|---| +| `pane` | 1 | terminal pane | spawn, list, focus, close, write, resize, tail | `pane.spawned`, `pane.output`, `pane.exit`, `pane.resized` | +| `tab` | 2 | workspace tab | new, switch, close, list | `tab.opened`, `tab.switched`, `tab.closed` | +| `open` | 2 | editor shortcut | *(verb-only: `clide open `)* | — (emits `editor.opened`) | +| `editor` | 2 | active editor buffer | goto, highlight, insert, replace-selection, save | `editor.opened`, `editor.selection_changed`, `editor.saved` | +| `panel` | 2 | sidebar/context panels | show, hide, toggle, list | `panel.visibility_changed` | +| `tree` | 2 | file tree | reveal, refresh | `tree.node_expanded`, `file.changed` | +| `git` | 3 | working tree | status, stage, unstage, stage-hunk, commit, stash, pull, push | `git.status_changed`, `git.branch_changed` | +| `pql` | 4 | queries | run, tags, backlinks | `pql.result` | +| `canvas` | 5 | canvas surface | open, node add/move/connect, save | `canvas.node_added`, `canvas.node_moved`, `canvas.connection_added` | +| `graph` | 5 | graph view | open, focus, filter | `graph.focused`, `graph.filter_changed` | +| `theme` | 6 | theme/palette | set, list, get | `theme.changed` | +| `settings` | 6 | settings store | get, set, list | `settings.changed` | +| `project` | — | whole-workspace | status, reload, events | `project.ready`, `project.reloaded` | + +Two umbrella entry points sit outside any subsystem: + +- `clide tail --events [--filter [:]]` — subscribe to + the event stream. Bare `tail --events` gets everything; filtered + forms narrow by subsystem or by subsystem+id (e.g. + `--filter pane:p_7`, `--filter git`). +- `clide status` — one-shot daemon snapshot: connected clients, + live panes, open tabs, workspace root, daemon version, uptime. + +### Command shape + +``` +clide [...] [--flag ...] [-- argv...] +``` + +- Positionals are nouns/ids; flags are modifiers. +- `--` separates Clide's args from an inner argv passed through + (e.g. `clide pane spawn --cwd X -- tmux new-session -A -s foo`). +- Verbs are imperative (`spawn`, not `create-pane`). +- Where it reads naturally, single-word shortcuts exist for the + hottest paths (`clide open ` → `clide editor open `). + Shortcuts alias; they do not fork. + +### Exit-code contract (parity with pql) + +| Code | Meaning | +|---|---| +| `0` | Success | +| `1` | User error (bad args, unknown id, precondition failed) | +| `2` | Tool error (daemon unreachable, IPC failure, internal panic) | +| `3` | Not-found (id or path doesn't resolve) | +| `4` | Conflict (state busy, already-running, concurrent-modify) | +| `64`–`78` | Reserved, per `sysexits.h`, for future specific cases | + +Diagnostic JSON on **stderr** (not stdout) on any non-zero exit: +```json +{"code":1,"kind":"user_error","subsystem":"pane","message":"pane id p_99 not found","hint":"clide pane list"} +``` + +Stdout stays machine-parseable on success. This matches pql. + +### Event schema + +Events are JSON objects, one per line, on the `--events` stream. +Every event: + +```json +{ + "v": 1, + "ts": "2026-04-20T21:00:00.123Z", + "type": "pane.output", + "subsystem": "pane", + "id": "p_7", + "payload": { "bytes_b64": "…", "seq": 412 } +} +``` + +- `v`: schema version. Bumped only for breaking changes. Old + subscribers pin `v`. +- `ts`: ISO-8601 UTC with millisecond precision. +- `type`: `.`. Past-tense for + things that happened; `_changed` suffix for state transitions. +- `subsystem` + `id`: redundant with `type`, but makes + filtering cheap and future-flexible. +- `payload`: subsystem-defined; documented per subsystem. + +Binary payloads (PTY output, file contents) are base64. The ergonomic +cost is worth the "entire stream is line-delimited JSON" invariant. + +### Command ↔ event duality + +Every state-changing command emits at least one event. Subscribers +see the same mutation whether they triggered it or not, and the +issuing client gets the event back (so `clide pane spawn` followed +by a `tail --events` subscription sees `pane.spawned` regardless of +subscribe order, via a short replay buffer per subsystem). + +Read-only commands (`list`, `get`, `status`) emit nothing. + +### User/Claude parity as a check + +Every merge to `main` that adds a UI affordance must either: +- add the matching CLI verb, or +- include a linked follow-up task naming the verb to add next. + +Every merge that adds a CLI verb must either: +- surface it in the UI, or +- document why the verb is Claude-only (rare; mostly diagnostics + like `clide status`). + +Events have the symmetric rule: any UI surface that reacts to state +must react to the corresponding event; any new event must be +consumable both by the UI and by `clide tail --events`. + +## Consequences + +- **Surface is enumerable.** Adding a subsystem means adding a row + to the table above and specifying its verbs + events in a short + doc under `docs/cli/`. The daemon registers it; the CLI dispatcher + picks it up; `clide --help` and `clide --help` stay + accurate by construction. +- **Wire schema is versioned.** `v: 1` is the starting point. + Compatibility breaks bump the major and land alongside a + `project.yaml` `schema_version:` bump. +- **Replay buffer per subsystem.** Cheap (most subsystems emit + seldom); needed so a subscriber that connects after a command + still sees that command's effect. Buffer depth per subsystem is a + tuning parameter; defaults to 16 events. +- **Events are the only UI→app state channel.** The Flutter app + does not poll; it subscribes. Panels render from the last event + for their subsystem + current snapshot from `project status`. +- **pql events fit naturally.** Long-running `pql` queries stream + rows as `pql.result` events keyed by a query id, letting the + Query panel render incrementally. +- **Testability.** Every subsystem can be integration-tested by: + start a daemon → open a `tail --events` subscriber → issue + commands over the CLI → assert events. No UI needed for + protocol-level coverage. +- **Extension API (Tier 6) inherits this.** A Dart extension + publishes a subsystem; the same registration pipeline exposes + it to Claude via the CLI. Extensions don't get a second-class + channel. + +## Open questions + +- **Authorisation granularity.** The daemon's token auth is coarse + (allow all / deny all). Later, per-subsystem grants may matter + (e.g. restrict `git push`). Out of scope here. +- **Back-pressure on event streams.** A subscriber that falls behind + on `pane.output` (a firehose) needs a policy: drop oldest, block + producer, coalesce, or kill subscriber. Defer until Tier 1 is in + real use. +- **Event persistence.** Events are in-memory only in v1. If a + future need (audit log, undo) wants persistence, it becomes a + subsystem that subscribes and writes — not a property of the bus. diff --git a/project.yaml b/project.yaml index 35e8f2a3..42015725 100644 --- a/project.yaml +++ b/project.yaml @@ -23,12 +23,11 @@ status: pre-v2.0, scaffolding (Tier 0 — Flutter app + sidecar handshake — is repository: ssh://git@git.schweitz.net:2222/jpmschweitzer/clide.git license: MIT -# Go sidecar module path. Gitea-hosted; Go module resolution works if -# the host serves the meta tags, otherwise a vanity path can be added. -module: git.schweitz.net/jpmschweitzer/clide/sidecar -# Toolchain pins. Mirror in sidecar/go.mod and app/pubspec.yaml when bumping. -go_version: ">=1.25" +# Toolchain pins. Mirror in pubspec.yaml when bumping. +# Single-language core — Dart + Flutter. Native supporter tools +# (ptyc today, peers tomorrow) pin their own toolchains in their own +# directories; not tracked here. flutter_channel: stable dart_sdk: ">=3.5.0 <4.0.0" diff --git a/sidecar/cmd/clide/main.go b/sidecar/cmd/clide/main.go deleted file mode 100644 index 129802f4..00000000 --- a/sidecar/cmd/clide/main.go +++ /dev/null @@ -1,18 +0,0 @@ -// Command clide is the CLI and sidecar-daemon entry point. -// -// One binary, two modes: -// - One-shot CLI (default): clide [args...] -// - Long-running sidecar: clide --daemon -// -// See docs/initial-plan.md and docs/ADRs/ for the architecture. -package main - -import ( - "os" - - "git.schweitz.net/jpmschweitzer/clide/sidecar/internal/cli" -) - -func main() { - os.Exit(cli.Run(os.Args[1:])) -} diff --git a/sidecar/go.mod b/sidecar/go.mod deleted file mode 100644 index 0a8f2368..00000000 --- a/sidecar/go.mod +++ /dev/null @@ -1,3 +0,0 @@ -module git.schweitz.net/jpmschweitzer/clide/sidecar - -go 1.25.0 diff --git a/sidecar/internal/cli/cli.go b/sidecar/internal/cli/cli.go deleted file mode 100644 index f84ff652..00000000 --- a/sidecar/internal/cli/cli.go +++ /dev/null @@ -1,53 +0,0 @@ -// Package cli is the CLI front-end. cmd/clide/main.go calls Run with -// os.Args[1:]. Subcommands land here one file per command once Tier 2 -// begins (see docs/initial-plan.md). -// -// Stdlib-only for the pre-Tier-0 scaffold. Cobra arrives with the first -// real subcommand surface. -package cli - -import ( - "encoding/json" - "flag" - "fmt" - "os" - - "git.schweitz.net/jpmschweitzer/clide/sidecar/internal/diag" - "git.schweitz.net/jpmschweitzer/clide/sidecar/internal/version" -) - -// Run dispatches CLI args. Returns the process exit code per the diag -// contract. -func Run(args []string) int { - fs := flag.NewFlagSet("clide", flag.ContinueOnError) - fs.SetOutput(os.Stderr) - - daemon := fs.Bool("daemon", false, "run as the long-running sidecar daemon") - showVersion := fs.Bool("version", false, "print build info and exit") - - if err := fs.Parse(args); err != nil { - return diag.Usage - } - - if *showVersion { - b, _ := json.Marshal(version.Info()) - fmt.Println(string(b)) - return diag.OK - } - - if *daemon { - diag.Error("cli.not-implemented", "daemon mode not yet implemented", "tier 0 — sidecar scaffold lands next") - return diag.Software - } - - // No subcommand yet. Surface the pql-style exit 64 so callers can tell - // the difference between "no instructions" and a successful run. - rest := fs.Args() - if len(rest) == 0 { - diag.Error("cli.usage", "no subcommand given", "see docs/initial-plan.md — Tier 2 defines the CLI surface") - return diag.Usage - } - - diag.Error("cli.not-implemented", fmt.Sprintf("subcommand %q not yet implemented", rest[0]), "tier 2 lands the CLI surface") - return diag.Software -} diff --git a/sidecar/internal/daemon/doc.go b/sidecar/internal/daemon/doc.go deleted file mode 100644 index 154958c7..00000000 --- a/sidecar/internal/daemon/doc.go +++ /dev/null @@ -1,7 +0,0 @@ -// Package daemon hosts the --daemon-mode entry point: lifecycle -// management (socket binding, token-auth setup, single-instance lock, -// graceful shutdown), subsystem wiring (pty, proc, git, pql, ipc), and -// the long-lived event loop. -// -// Empty for now — lands in Tier 0. -package daemon diff --git a/sidecar/internal/diag/diag.go b/sidecar/internal/diag/diag.go deleted file mode 100644 index 85544777..00000000 --- a/sidecar/internal/diag/diag.go +++ /dev/null @@ -1,58 +0,0 @@ -// Package diag defines the exit-code contract and emits structured -// diagnostics to stderr as line-delimited JSON. -// -// Mirrors pql's diag contract so Claude (and any other tool that -// consumes both CLIs) sees a single shape. -package diag - -import ( - "encoding/json" - "io" - "os" -) - -// Exit codes. Same numeric space as pql's. -const ( - OK = 0 // success - NoMatch = 2 // success with zero matches / nothing to do - Usage = 64 // EX_USAGE — bad CLI flag or missing subcommand - DataErr = 65 // EX_DATAERR — malformed request - NoInput = 66 // EX_NOINPUT — missing required resource (repo, socket, file) - Unavail = 69 // EX_UNAVAILABLE — sidecar unreachable / subsystem down - Software = 70 // EX_SOFTWARE — internal error -) - -type Level string - -const ( - LevelWarn Level = "warn" - LevelError Level = "error" -) - -// Diagnostic is one entry in the stderr JSON-per-line stream. -type Diagnostic struct { - Level Level `json:"level"` - Code string `json:"code"` - Msg string `json:"msg"` - Hint string `json:"hint,omitempty"` -} - -// Emit writes a diagnostic as one JSON line to w. -func Emit(w io.Writer, d Diagnostic) { - b, err := json.Marshal(d) - if err != nil { - _, _ = io.WriteString(w, `{"level":"error","code":"diag.marshal","msg":"failed to marshal diagnostic"}`+"\n") - return - } - _, _ = w.Write(append(b, '\n')) -} - -// Warn emits a warning diagnostic to stderr. -func Warn(code, msg string) { - Emit(os.Stderr, Diagnostic{Level: LevelWarn, Code: code, Msg: msg}) -} - -// Error emits an error diagnostic to stderr. -func Error(code, msg, hint string) { - Emit(os.Stderr, Diagnostic{Level: LevelError, Code: code, Msg: msg, Hint: hint}) -} diff --git a/sidecar/internal/git/doc.go b/sidecar/internal/git/doc.go deleted file mode 100644 index fe58bacd..00000000 --- a/sidecar/internal/git/doc.go +++ /dev/null @@ -1,5 +0,0 @@ -// Package git shells out to git for the Git panel, diff rendering, and -// the `clide git ` CLI surface. -// -// Empty for now — lands in Tier 3. -package git diff --git a/sidecar/internal/ipc/doc.go b/sidecar/internal/ipc/doc.go deleted file mode 100644 index 4760aa80..00000000 --- a/sidecar/internal/ipc/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// Package ipc hosts the unix-socket JSON-lines server the sidecar -// exposes to the Flutter app and CLI. Token-authenticated; dispatches -// to the pty, proc, git, pql, canvas, and pane subsystems. -// -// Empty for now — lands in Tier 0. -package ipc diff --git a/sidecar/internal/pql/doc.go b/sidecar/internal/pql/doc.go deleted file mode 100644 index f4ab5eab..00000000 --- a/sidecar/internal/pql/doc.go +++ /dev/null @@ -1,10 +0,0 @@ -// Package pql is the *only* place Clide contains pql logic. Pure -// shell-outs to the `pql` binary — no re-implementation of pql's -// indexer, ranker, or frontmatter parsing. -// -// See docs/ADRs/0003-pql-as-supporter-tool.md for the wrap-don't- -// duplicate rule and the "pql is a Clide subsystem when present" -// invariant. -// -// Empty for now — lands in Tier 4. -package pql diff --git a/sidecar/internal/proc/doc.go b/sidecar/internal/proc/doc.go deleted file mode 100644 index c0b2092c..00000000 --- a/sidecar/internal/proc/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// Package proc manages subprocess lifecycles spawned by the sidecar — -// git operations, pql invocations, and anything else the app or CLI -// requests. -// -// Empty for now — lands alongside Tier 1/3 subsystems. -package proc diff --git a/sidecar/internal/pty/doc.go b/sidecar/internal/pty/doc.go deleted file mode 100644 index 557f55ec..00000000 --- a/sidecar/internal/pty/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// Package pty owns the PTY lifecycle for terminals hosted inside the -// sidecar daemon. Drives xterm.dart panes in the Flutter app and the -// `clide` CLI's direct-attach mode. -// -// Empty for now — lands in Tier 1. -package pty diff --git a/sidecar/internal/version/version.go b/sidecar/internal/version/version.go deleted file mode 100644 index 605b1661..00000000 --- a/sidecar/internal/version/version.go +++ /dev/null @@ -1,35 +0,0 @@ -// Package version exposes build-info stamped via -ldflags at build time. -// See the root Makefile's LDFLAGS for the -X targets. -package version - -import "runtime" - -var ( - Version = "dev" - Commit = "unknown" - Date = "unknown" -) - -// SchemaVersion tracks the app↔sidecar IPC contract. Bump when the wire -// format changes in a way that requires coordinated app and sidecar -// updates. Mirror in project.yaml `schema_version:` when that field is -// added there. -const SchemaVersion = 0 - -type BuildInfo struct { - Version string `json:"version"` - Commit string `json:"commit"` - Date string `json:"date"` - GoVersion string `json:"go_version"` - SchemaVersion int `json:"schema_version"` -} - -func Info() BuildInfo { - return BuildInfo{ - Version: Version, - Commit: Commit, - Date: Date, - GoVersion: runtime.Version(), - SchemaVersion: SchemaVersion, - } -}