Commit Graph
1085 Commits
Author SHA1 Message Date
jpmschweitzerandClaude Opus 4.8 e9f394ff0f docs(governance): D-103 — drawing-card primitive layer is SVG (amends D-91)
The unified drawing card's primitive layer is SVG, not a bespoke
{type:"rect"} JSON vocabulary; the clide-owned CustomPaint SVG renderer
(T-320) is the engine the rest builds on. Templates lower to SVG; a thin
Flutter overlay carries label/description captions + lightbox, anchored
via data-* attributes; the graph template stays a live widget. Inverts
the former T-320 → T-318 dependency. Raised by the user: "if we are close
to svg, are we not better off extending svg instead?"

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 22:18:53 +02:00
jpmschweitzer ab9f93b541 Update README.md
changed readme to mention added windows build
2026-06-28 15:37:57 +02:00
jpmschweitzerandClaude Opus 4.8 89b7f99a8d build(ci): make dugite-check — track dugite-native upstream for security (T-88)
D-59 requires tracking dugite-native (bundled git) releases for security
updates, but it lived only in the D-record prose with no mechanism (clean-house
flagged RULE-SUNSET-WITHOUT-TICKET). Add ci/check_dugite_version.sh + a
`make dugite-check` target: compares the Makefile DUGITE_VERSION pin against the
latest desktop/dugite-native release and loudly flags CVE / security mentions in
the notes. Informational (not a push gate) — the bump itself is manual per D-63
and automated by T-25.

The script header is the maintenance home (dugite is fetched, not built, and
native/dugite/ is gitignored, so there's no BUILD.md to record it): cadence
(quarterly / on a git CVE), advisory subscriptions, and the bump procedure.
D-59's cost line + a Makefile comment now point at the mechanism. T-88 stays
open as the recurring calendar; first check (2026-06-28) shows v2.53.0-3 current.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 15:12:50 +02:00
jpmschweitzerandClaude Opus 4.8 066197c5e1 fix(i18n): stop warning on missing tool.name.* keys for proper-name tools (T-493)
Tool display names are open-ended (Bash, Grep, Glob, ScheduleWakeup, MCP tools,
…) — they intentionally have no catalog key and fall back to the raw name, so a
miss isn't a gap to fix. The conversation pane was logging an i18n "missing key"
warning for each, cluttering the output dock.

Adds a `warnIfMissing` flag to I18nService.string (default true, so fixed UI
strings still warn on a real gap) threaded through the ClideSettings facade;
_toolNameLabel passes false. Display is unchanged — the placeholder already
rendered the raw name.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 14:58:12 +02:00
jpmschweitzerandClaude Opus 4.8 e5976ad6b1 test(menubar): cover githubGet against a loopback server (T-47 P1)
The injectable-fetch tests skip the real HTTP path; exercise githubGet's
200-body and non-200-throws branches against a localhost HttpServer so no test
hits the network and the gate stays green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 12:18:28 +02:00
jpmschweitzerandClaude Opus 4.8 7c7a91545e feat(menubar): manual "Check for updates" in the About box (T-47 P1)
Help → About gains a "Check for updates" button that fetches the latest GitHub
release, semver-compares it to clideVersion, and shows the result inline:
up-to-date, available (with a tappable link to the release notes), or a clear
error. clide's first and only outbound HTTP call — a plain GET with no user
data, run ONLY on this explicit tap, never on a launch path or a timer. So it's
D-64-clean with no amendment; a background/periodic poll stays deferred (would
need the narrow opt-in amendment first).

The fetch is injectable so no test touches the network. compareSemver handles
2.3.10 > 2.3.9 and ranks pre-releases below their release. Closes T-492 (P1);
the release-channel CI for downloadable signed packages is T-491, and download/
apply (P2/P3) depend on it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 12:17:29 +02:00
jpmschweitzerandClaude Opus 4.8 095c45a023 feat(markdown): live-sync read-mirror of the open editor buffer (T-36)
D-50 behavior 4: opening a renderable .md in the editor now auto-reveals the
context-panel reader, which mirrors the buffer read-only and re-renders as the
user types — rather than a one-shot disk read.

The extension reveals the tab on editor.opened (renderable only — D-50 behavior
5 leaves non-.md files alone). The viewer owns the mirror: on mount it picks up
the active buffer (editor.read, no id), enters mirror mode on editor.opened /
active-changed for a renderable file, re-reads the in-memory buffer on
editor.edited, and drops the mirror (with its edit affordance back) on a disk
load or a switch to a non-renderable buffer.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 11:08:31 +02:00
jpmschweitzerandClaude Opus 4.8 84f4b20cb8 feat(claude): nudge fresh sessions to load the pql + clide skills (T-490)
A fresh Claude session now gets a one-line preamble (clideSkillsNote) telling it
to reach for the bundled pql + clide skills from its first turn rather than
rediscovering the workflows. Layered onto clideContextNote in the single
--append-system-prompt.

Gated on a NEW session (!spec.resume && !spec.isFork): new tabs and the
post-/clear respawn spawn with resume:false and get it; the account-change
respawn (T-480) and real resumes carry prior context (resume:true), and forks
inherit their source — none are re-nagged. Both skills already ship (pql
user-scope, clide repo-scope), so no precursor was needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 10:53:08 +02:00
jpmschweitzerandClaude Opus 4.8 a46677facf feat(welcome): initialize a non-repo folder as a project (T-489)
Closes the new-project story (T-486). The dead-end "not a git repo" dialog now
offers to initialize the folder: project.init runs git init + a non-clobbering
scaffold, then opens + announces on projectCreatedChannel so the account
roadblock fires — the same path a brand-new project takes. Adds initExistingProject
+ the `clide project init [--dir]` verb (default: the current workspace).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 08:42:02 +02:00
jpmschweitzerandClaude Opus 4.8 25430686cc feat(welcome): New project flow + per-repo account roadblock (T-488)
The UI half of the new-project flow (story T-486). A "New project…" welcome
action opens a dialog (location + name) that dispatches project.new (T-487),
opens the result, and announces it on projectCreatedChannel. The Claude
extension consumes that and shows the account roadblock — the embedded
per-workspace picker + accounts list, so a fresh project gets bound to an
account (or Default) right at birth.

The two halves stay decoupled: the welcome builtin only publishes the event
(no claude import); the claude builtin owns the account dialog. Only freshly-
created projects announce, so existing opens are never prompted.

Closes T-488. The non-repo "initialize as a project" path (T-489) is next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 08:35:30 +02:00
jpmschweitzerandClaude Opus 4.8 adaf8966dc feat(cli): clide project new — create + git-init a new project (T-487)
The backend half of the new-project flow (story T-486). createNewProject
validates the name, makes <parent>/<name>/, runs git init (injected from the
toolchain in main.dart so the handler stays Flutter-free), and writes a minimal
scaffold (.gitignore + a CLAUDE.md stub). The project.new verb wraps it; --dir
defaults to the current workspace's parent so a new project lands beside it.

Create-only by design — opening the new workspace and the account roadblock are
the UI flow's job (T-488). Closes T-487.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 08:23:18 +02:00
jpmschweitzerandClaude Opus 4.8 b9fc720f97 feat(git): GitClient.init for the new-project flow (T-487)
clide treats a git repo as the workspace, so creating a new project starts with
`git init`. Add GitClient.init — `git init -b <main>` in workDir, deterministic
default branch, idempotent on an existing repo. The first primitive of the
new-project flow (T-486); the create-dir + scaffold service and the
`clide project new` verb build on it next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 08:19:10 +02:00
jpmschweitzerandClaude Opus 4.8 6d7b96f115 chore(plan): re-scope T-486 — account picker at new-project init, not welcome
Per the design pivot: the account choice is only a real decision at project
birth, so it belongs as a roadblock in the new-project / init-in-a-new-dir flow
(evolving the dead-end _NotARepoDialog), not a welcome-screen list. Opening an
existing project never prompts.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 08:06:42 +02:00
jpmschweitzerandClaude Opus 4.8 8a9167bdfd test(claude): cover remove-nonexistent + list live-update for the 95% margin
Two more real branches the account feature left uncovered: removing an
unregistered account (the no-such-account error) and the registry list
rebuilding when a CLI-side registration notifies the shared settings store.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 01:19:50 +02:00
jpmschweitzerandClaude Opus 4.8 da82b48f9b test(claude): cover the per-repo account branches to clear the 95% gate
Adds the missing-line coverage the epic's new code left uncovered: Account /
DetectedAccount value equality, the set/unset no-workspace error branches, the
login dialog's escape-to-close, and the settings controls' live-update /
no-workspace / duplicate-add / re-login paths.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 01:13:03 +02:00
jpmschweitzerandClaude Opus 4.8 53b93f092b test(claude): scope the Claude-category select assertion to the defaults fields
The Account section (T-482) adds custom-kind controls to the Claude settings
category, so the T-457 test can no longer assert that *every* field in the
category is a select. Scope the kind/options check to the new-session-default
fields (model/effort/permission) it was actually about.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 00:57:26 +02:00
jpmschweitzerandClaude Opus 4.8 996b10d3ec feat(claude): account login pane — host claude login in a modal terminal (T-485)
Completes the `login` verb + the UI add/re-login affordances. The
accountActionChannel 'login' action opens ClaudeLoginDialog — a modal hosting a
TerminalPane that runs `CLAUDE_CONFIG_DIR=<dir> claude login`, so the CLI drives
the OAuth browser flow and credentials land in that account's config dir (D-64:
one CLI-initiated browser flow, on explicit action).

TerminalPane gains optional argv/env/cwdOverride (default stays the login
shell), and its pane.spawn carries env when set. The login pane reuses the
TerminalPane *widget* — a code import, not a runtime dependency, since it spawns
through the always-present pane.spawn IPC rather than the terminal extension.

Tests cover the parameterized spawn args and the dialog's host wiring (title,
CLAUDE_CONFIG_DIR, close). Closes T-485.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 00:52:41 +02:00
jpmschweitzerandClaude Opus 4.8 222d4ae5e3 feat(claude): per-repo account badge in the Claude pane chrome (T-481)
The always-visible affordance for the multi-account epic: a compact badge in
the pane header showing which account this workspace is bound to (or "default"),
colour-tinted per account so two windows are distinguishable at a glance. Tapping
opens a picker of the registered accounts + Default; selecting binds/unbinds via
the shared bindWorkspaceAccount helper (respawn + lock-sync follow on the bus).
Hidden when no accounts are registered, so it adds no chrome for users not using
the feature.

accountAccent derives the tint by hashing the name into a fixed set of theme
tokens — never an arbitrary colour, so the palette stays theme-owned.

The welcome-view accounts section (the other half of T-481) is split to T-486:
the welcome builtin is intentionally decoupled from feature builtins, so it needs
a welcome-section contribution point rather than importing claude directly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 00:43:40 +02:00
jpmschweitzerandClaude Opus 4.8 5c31190c4f feat(claude): Settings → Claude accounts registry list (T-482 part 2)
Completes the account settings surface with the global Accounts list under the
Claude category: each registered account shows a sign-in dot, name, and config
dir, with re-login and remove affordances plus an inline add-account field.

- Sign-in status: accountIsSignedIn, a read-only probe (a .credentials.json, or
  an oauthAccount marker in .claude.json; under-reports on macOS keychain).
- Add: registers ~/.claude-<name> and publishes a login action (T-485 opens the
  pane).
- Remove: registry-remove, guarded while a workspace is bound — matching the CLI
  `account remove`. --purge dir deletion stays on the CLI flag.

All management routes through the AccountRegistry + accountActionChannel, and
the list rebuilds live off the settings notifier. en/nl strings + widget/probe
tests added. Closes T-482.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-28 00:35:47 +02:00
jpmschweitzerandClaude Opus 4.8 c3efcf3855 feat(claude): Settings → Claude per-workspace account picker (T-482 part 1)
A custom settings control under the Claude category: a dropdown of the
registered accounts plus Default. Picking one binds (or unbinds) this
workspace via the AccountRegistry and publishes set/unset on
accountActionChannel — the same channel the CLI verbs use, so the session
respawns onto the account (T-480) and the IDE lock re-syncs (T-479). It reads
live off the shared settings notifier, so a CLI `account set` updates the
dropdown too. Empty states cover no-workspace and no-accounts, each pointing at
the CLI.

Registry writes set the in-memory binding synchronously then flush, so the
control publishes the bus event before the disk write completes.

Part 2 (the global Accounts registry CRUD list + sign-in probe) is still open
on T-482. en/nl strings added; widget test covers the states + bind/unbind.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 23:46:43 +02:00
jpmschweitzerandClaude Opus 4.8 e86543d476 fix(ipc): write the /ide discovery lock into bound-account config dirs (T-479)
A `claude` started with CLAUDE_CONFIG_DIR=<dir> looks for clide's /ide
discovery lock under <dir>/ide, not ~/.claude/ide — so today a per-repo
account session (T-476) can't reach clide's IDE bridge.

McpServer now reconciles a SET of lock paths instead of one: the default
~/.claude/ide always, plus the bound account's <dir>/ide when this workspace
is bound (boundConfigDir, injected from main.dart via the AccountRegistry).
syncDiscoveryLocks() writes/removes locks to match the active set; it runs on
start, once post-boot when the registry becomes resolvable, and on every
accountActionChannel event, so binding/unbinding adds/removes the account lock
at runtime. stop() sweeps every path written — no orphans. Lock content +
0600 are identical across dirs.

Acceptance 1-4 and 6 are covered by mcp_server_test (multi-dir write, no-binding
regression, dynamic add/remove, full cleanup); #5 (a live claude resolving the
bridge end-to-end) is manual.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 21:25:19 +02:00
jpmschweitzerandClaude Opus 4.8 34b9690005 feat(claude): respawn panes on account set/unset + safe --purge (T-480 part 2)
The extension consumer for the per-repo account verbs, making set/unset
fully functional. The Claude extension subscribes to accountActionChannel:

- set / unset → ClaudeSessionOrchestrator.respawnForWorkspace(cwd): closes the
  workspace's solo sessions (awaiting real process death, T-437) and re-spawns
  each on the same id with --resume, so the conversation continues under the
  newly-bound CLAUDE_CONFIG_DIR (resolved at spawn by agentBootstrap). Team and
  forked sessions are skipped — re-joining the broker / re-forking on an account
  swap is out of scope; they adopt the account on their next natural spawn.
- remove --purge → deletes the config dir behind isPurgeableAccountDir, a strict
  guard that only ever removes a ~/.claude-* directory that is a direct child of
  $HOME. The purge payload now carries the dir (the account is gone from the
  registry by publish time).

login still only publishes its action — spawning the `claude login` terminal
pane needs argv+env terminal-pane support and is split to T-485.

Covered: respawnForWorkspace (respawn solo, skip fork/other-repo) and the purge
guard's accept/reject matrix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 21:18:49 +02:00
jpmschweitzerandClaude Opus 4.8 744b6cff43 feat(cli): clide claude account verbs — add/list/login/set/unset/remove (T-480 part 1)
The CLI half of the multi-account feature (epic T-476; D-6 parity). A new
`claude.account` dispatcher command multiplexes the six sub-verbs over an
injected, Flutter-free AccountStore port (runs under `dart test`):

- add <name> [--dir]   register (default ~/.claude-<name>); idempotent, clear
                       conflict error
- list                 {accounts, boundAccount (this workspace), detected}
- set <name>           bind this workspace (persists)
- unset                clear this workspace's binding
- remove <name> [--purge]  registry-remove; refuses while any workspace is
                       bound
- login <name>         (publishes the login action)

Registry reads/writes go through the user-scope SettingsStore; side-effects
that only the UI layer can do — respawn on set/unset, the `claude login`
terminal pane, and the --purge rm — are published on accountActionChannel for
the Claude extension to consume (that consumer is T-480 part 2). main.dart
adapts the real AccountRegistry to the port and registers the command alongside
image.show / status.

Adds SettingsStore.keysAt (binding enumeration) and AccountRegistry.boundName /
boundAccountNames. No changelog yet — set/unset don't auto-respawn until part 2,
so the feature isn't user-complete. Verb behaviour + payloads + the in-use
guard are unit-tested against a fake store.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 17:30:01 +02:00
jpmschweitzerandClaude Opus 4.8 1fa7dee863 feat(claude): spawn hosted sessions under the bound account's CLAUDE_CONFIG_DIR (T-484)
The load-bearing piece of the multi-account epic (T-476): a bound workspace's
hosted claude now spawns with CLAUDE_CONFIG_DIR set to that account's dir, so
it runs under the bound account end-to-end. Every hosted session (primary /
secondary / fork / teammate) inherits it — the orchestrator already routes all
spawns through agentBootstrap.

- New pure resolver claudeConfigDirForWorkspace(cwd, boundConfigDir, env):
  bound account dir > parent CLAUDE_CONFIG_DIR (respect the launcher) > null
  (Claude defaults to ~/.claude). The registry is injected as a plain lookup
  so agent_bootstrap stays Flutter-free (its tests run under `dart test`).
- agentBootstrap merges CLAUDE_CONFIG_DIR BEFORE base, so an explicit
  SpawnSpec.env override still wins (override > binding > parent > unset); the
  key is omitted entirely when the resolver returns null.
- Orchestrator carries an optional AccountRegistry; the claude extension builds
  it from ctx.settings. Null in tests → no injection (unchanged behaviour).

No way to SET a binding yet (that's the CLI T-480 / settings UI T-482), so no
changelog entry — the mechanism is in place, the surface lands next. Unit tests
cover the resolver's four states and the envDelta precedence.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 17:00:06 +02:00
jpmschweitzerandClaude Opus 4.8 a8d321f344 feat(claude): per-repo account registry + storage foundation (T-483)
Foundation for the multi-account epic (T-476): a Flutter-free AccountRegistry
over the app-scope SettingsStore (per-user, never committed to a repo).

- `app.claude.accounts` — the {name, dir} account list (dir = CLAUDE_CONFIG_DIR).
- `app.claude.account.<hash>` — per-workspace binding to an account name; the
  hash is the SAME FNV-1a 64-bit hex D-70 uses for the socket path, trailing
  separators stripped so /repo and /repo/ map alike.
- accountForWorkspace(cwd) resolves an account or null (a binding to a removed
  account degrades to null = Claude's default, never errors).
- probeExistingAccountDirs(home): read-only bootstrap probe for adoptable
  ~/.claude-* config dirs (welcome-view UX is T-481).

Keys are app.-prefixed because SettingsStore requires a scope prefix; app scope
already provides the per-user persistence T-483 wants (T-356 will consolidate
more state later but isn't a hard blocker). No spawn/UI/CLI — downstream
tickets. Unit tests cover CRUD, the disk round-trip, hash/trailing-slash
mapping, independent bindings, and probe filtering.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 16:42:39 +02:00
jpmschweitzerandClaude Opus 4.8 c6adccc5de build(make): route build through build-<os> instead of duplicating the flutter build
`build` ran `flutter build $(FLUTTER_OS)` inline while build-linux/-macos/
-windows ran the same command separately — two places to keep in sync. Make
`build` depend on `build-$(FLUTTER_OS)` so the per-OS target is the single
source of the flutter-build command; `build` just adds the C-client bundling
on top. `make install` → build → build-<os>, and `make build-linux` still
works standalone (release.sh + CI reference it).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 16:32:44 +02:00
jpmschweitzerandClaude Opus 4.8 679b5ba9d9 feat(cli): clide instances / instance verbs for instance discovery (T-247)
Closes the observability half of T-247: a way to find and identify running
clides. `clide instances` scans the runtime socket dir, probes each live
*.sock, and prints its identity (version/pid/workspace/socketPath) as jsonl;
dead sockets are skipped. `clide instance` reports the one you're connected
to. Combined with CLIDE_SOCK honoring (this same ticket), you can now list
instances and pin the CLI to a chosen one.

Server: a new `instance` dispatcher command (registered in buildDispatcher
with the live workspace/pid/socket) returns the identity map; added to the
argv translator's umbrella set so a bare `instance` token routes to it.
Client: a POSIX dir-scan in clide.c (Windows stub until it ships).

Tests: e2e `instances` lists the live test server with its identity; a
cc-free unit test covers the `instance` command shape.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 15:51:13 +02:00
jpmschweitzerandClaude Opus 4.8 ceab497ba8 fix(ipc): sweep dead orphan sockets from the runtime dir on startup (T-247)
The runtime socket dir accumulated orphaned *.sock nodes from crashed
instances — only the current workspace's own path was ever cleaned. Add a
best-effort startup sweep that probes every *.sock in the dir and unlinks
only the dead ones; live instances (something answers) and unresponsive
nodes (possibly hung) are left alone. Runs before bind, alongside the
existing per-workspace stale-unlink. Never blocks our own startup on a
sweep failure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 08:15:05 +02:00
jpmschweitzerandClaude Opus 4.8 6af980f31c fix(cli): honor CLIDE_SOCK as an explicit target, fail loudly if dead (T-247)
The `clide` shell client only ever computed the socket path from the
workspace's FNV-1a hash — it never read CLIDE_SOCK, so the var the app
exports to spawned agents was a no-op and a bogus CLIDE_SOCK was silently
ignored (it still hit the discovered instance). Now: when CLIDE_SOCK is set
the client connects to it directly, beating discovery, and a dead socket
aborts with EX_UNAVAILABLE rather than falling back to a different instance
(the split-brain footgun). Unset → the deterministic per-workspace path
(D-70) as before. The server keeps deterministic binding; this is a
client-side targeting override only.

Also fixes a pre-existing -Wstringop-truncation warning in
find_workspace_root (strncpy+manual-null → snprintf).

Tests: the e2e suite now clears the inherited CLIDE_SOCK so discovery tests
stay hermetic (the suite may run inside a clide instance), plus two new
tests — a valid CLIDE_SOCK pins from a non-git dir, a dead one fails loudly
and returns no data.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 08:11:55 +02:00
jpmschweitzerandClaude Opus 4.8 3cbe0e6ec6 refactor(claude): drop the redundant Team-tab account budget (T-158)
After live review: the shared account /usage budget read as redundant on
both the Activity and Team tabs. Usage is per-account and can't be split per
member, so one place is enough — it lives only on the Activity tab, next to
the refresh control that fetches it (T-415). Removes the Team-tab account
card, its usage prop wiring, the team.section.usage/team.usage.shared i18n
keys, and the now-obsolete team_tab test. The three-tab card facelift stays.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 07:56:09 +02:00
jpmschweitzerandClaude Opus 4.8 a72906d25d chore(plan): reconcile T-477/T-478 label collision from the merge
The merged T-476 breakdown reused T-477/T-478 (already held by the
window-size fix + Summer Night theme). Relabel the two per-repo-account
children to T-483/T-484 (identities/record_ids unchanged), preserving the
labels already referenced in CHANGELOG and pushed commits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-27 07:45:10 +02:00
jpmschweitzerandClaude Opus 4.8 026156261c Merge origin/main: T-476 per-repo-account breakdown
Union-resolved the .pql/changelog logs (append-only, idempotent INSERT … ON
CONFLICT DO UPDATE — latest-updated wins on replay), keeping both sides:
this session's T-158 changes and origin's T-476 6-child breakdown. pql.db
rebuilt clean (2785 statements).

NOTE — label collision to reconcile (record_ids intact, labels only):
the breakdown reused T-477 and T-478, already held by the window-size bug
and the Summer Night theme. Reconcile with `pql ticket relabel`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 11:36:03 +02:00
jpmschweitzerandClaude Opus 4.8 763d628fc9 feat(claude): shared account budget in Team tab + meta-sidebar card facelift (T-158)
Implements T-158 option A: the forwarded /usage budget (already parsed for
the Activity tab, T-415) now also renders in the Team tab as a single
ACCOUNT card, captioned "shared across the team". Usage is per-account —
every team session shares one ~/.claude login — so it's shown once, not
split per member (which would just repeat identical numbers).

Facelift: the Activity, Team, and Config tabs now render their sections as
elevated cards (panelHeader fill, dividerColor hairline, 6px radius) under
small-caps mono headers, matching the settings overlay's card design
(settings_category_view). Shared helpers — metaSectionHeader / metaCard /
metaCardRow — live in meta_sidebar/models.dart; the SESSION control strip
and the pinned Config SETTINGS block adopt the same card. The shared
ClideAccordion (also used by decisions/tickets) is left untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 11:31:50 +02:00
Jeroen SchweitzerandClaude Opus 4.7 3f627c5b17 chore(plan): break T-476 down into 6 children under an epic
Convert T-476 from story to epic and file the implementation slices:
T-477 storage/registry/migration, T-478 spawn-time CLAUDE_CONFIG_DIR
injection, T-479 multi-dir /ide lock writer, T-480 clide CLI verbs
(D-6 parity), T-481 pane badge + welcome accounts UI, T-482 settings
rows. Blocker graph wired (T-356 -> T-477 -> fan-out -> T-480 -> UI).
Epic body refined with the proven mechanism, surveyed alternatives,
concrete code touchpoints, and acceptance.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-06-26 09:50:57 +02:00
jpmschweitzerandClaude Opus 4.8 c4be179a87 chore(plan): record T-158 decision — option A (shared-account team-tab budget)
Locks scope to the single account-wide budget section in the team tab; declines
the per-member split (not meaningful under one shared account) and the
close-as-done option. Not started.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 09:01:21 +02:00
jpmschweitzerandClaude Opus 4.8 367792490d chore(plan): refine T-158 (team-sidebar usage budget) implementation plan
Grounded against the current tree: T-415 already ships the account budget
(parseUsageText/ClaudeUsage + Activity tab). Key finding — usage is
per-ACCOUNT (one shared ~/.claude login across all sessions), so a literal
per-member split would repeat identical numbers and isn't meaningful even
under T-476 (per-repo, not per-session, login). Refinement recommends a
single shared-account section in the team tab (resolves Q-34), lists the
exact files to touch, alternatives, and acceptance. No code yet.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 08:59:50 +02:00
jpmschweitzerandClaude Opus 4.8 259562f8cc docs(git-commit): document the release tagging step (T-393)
The "Cutting a release" ritual moved the changelog and bumped pubspec but
never mentioned tagging — which is how tags silently lapsed from v2.2.0
through v2.8.0. Add step 6: run `make release` (verify + gate + annotated
tag), then push with --follow-tags, and state the rule that every released
version must have a matching git tag.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 23:27:13 +02:00
jpmschweitzerandClaude Opus 4.8 9a5f5b2ab5 build(hooks): widen pre-push fast path to test/ci/.githooks + guard test (T-393)
The fast-path regex matched only `^(lib/|pubspec\.)`, so a push that ONLY
changed a test, a ci/ gate script, or the pre-push hook itself skipped the
entire ~2min suite — exactly the paths most able to break the suite or the
gate went unchecked. Widen the trigger to include test/, ci/, and
.githooks/, and extract it to a single `trigger_re` variable. Also drop the
header comment's false claim that "release CI runs it forced on a tagged
version" — there is no release CI.

Add test/tooling/pre_push_hook_test.dart: it reads the live trigger_re from
the hook and asserts the load-bearing dirs force the full gate while
docs/assets ride along — so the regex can't be silently narrowed again.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 23:26:52 +02:00
jpmschweitzerandClaude Opus 4.8 c1d91e57d2 build(ci): real release finalizer + make release target (T-393)
ci/release.sh was a stub that printed a goreleaser/sidecar TODO and
exited 64 — both the sidecar and goreleaser are dissolved (D-56), so it
could never run. Replace it with a finalizer for the single-process app:
read the version from pubspec, assert CHANGELOG has a dated section for it
and the tree is clean, run the full gate (make push-check), then create
the annotated vX.Y.Z tag if missing. Never pushes — prints the
--follow-tags push + build commands. Add a `make release` wrapper so it's
driven through the Makefile like the other gates (CLAUDE.md tooling rule).

This closes the loop that left releases untagged: running `make release`
after the release commit can no longer forget the tag.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 23:26:37 +02:00
jpmschweitzerandClaude Opus 4.8 b7db22924d feat(theme): canonical theme/namespace lists + ship Summer Night (T-371, T-478)
The bundled-theme list and the Tier-0 i18n namespace list were each
hand-maintained in 3+ places that had drifted: the testmode harness loaded
8 themes (catppuccin silently unvalidated) while the app and contrast gate
loaded 10, and the i18n gate checked 4 of the shipped namespaces. Export
one canonical const each — kBundledThemePaths and kTier0Namespaces — and
have the app, the testmode harness, and the a11y gates iterate them.

Drift-proof meta-assertions:
- contrast gate fails if any theme YAML on disk is absent from
  kBundledThemePaths (so a theme can't sit unvalidated).
- i18n gate derives its subjects from assets/i18n/en_us/ at test time and
  asserts en/nl key parity for every shipped catalog (26, was 4), plus that
  every kTier0Namespaces entry has a shipped catalog.

Surfaced summer-night.yaml: a legacy-ported palette on disk, unbundled and
never contrast-checked. Per the maintainer's call, ship it: flesh the
palette to full token + syntax coverage (honouring the v1.2 colours, clide
derivation pattern for the new keys) and add a structurally identical
summer-night-hc sibling that clears the strict extended gate. Both pass.

No licenses.yaml change: summer-night is clide's own (ported from legacy
clide v1.2 under legacy/, MIT); the only third-party palette, Catppuccin,
is already acknowledged; no fonts added.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:31:16 +02:00
jpmschweitzerandClaude Opus 4.8 1e49f3e1d9 fix(editor): collapse the workspace split when the last buffer closes (T-459)
EditorRegistry.close() guarded its active-changed emit on `_activeId !=
null`, so closing the LAST buffer (active clears to null) emitted only
editor.closed — never the active-changed(id:null) the editor extension
listens for to call closeEditor(). editorOpen stayed true and the top
split sat orphaned over the Claude pane. Always emit active-changed when
the active buffer is removed, including the cleared-to-null case; the
slot renderer already collapses correctly once editorOpen flips false.

The existing extension test fabricated the null active-changed event, so
it passed despite the registry never emitting it — that gap is why the
bug shipped. Add a registry test that drives the real close() path, plus
a slot_host widget test asserting the split (drag handle) drops out and
the primary pane fills the column.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:49:12 +02:00
jpmschweitzerandClaude Opus 4.8 8c9c881d81 fix(welcome): open Linux window at 1600x900 to clear footer overlap (T-477)
At the 720p default the vertically-centred welcome content slid under the
floating version/theme status line (Positioned bottom:24), overlapping the
tips card. The taller default gives the centred block clear bottom margin
and matches the macOS default size.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 09:14:05 +02:00
jpmschweitzerandClaude Opus 4.8 3aebfa7ec3 release v2.8.1
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v2.8.1
2026-06-22 07:46:10 +02:00
jpmschweitzerandClaude Opus 4.8 ea340ef141 fix(i18n): localize remaining Claude-pane strings (T-462)
Completes i18n coverage of the conversation surface — the strings the
T-464 migration left as "decorative" English:

- running-indicator verbs (Pondering/Conjuring/… → catalog, Dutch added)
- pane chrome title (primary / secondary N) and banner role
- tool-card titles (Edit → Bewerken) via a tool.name.* key set, with
  command/proper-name tools (Bash, Grep, Glob, LS) falling back to the
  raw name
- collapser step/edit/agent counters
- the folded-activity ticker (result/error/thinking/image + tool name)

English degrades to the placeholder where a key is absent, so untranslated
or MCP tool names render unchanged. en_us and nl_nl stay at key parity.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 00:24:34 +02:00
jpmschweitzerandClaude Opus 4.8 c6db2fa7dc release v2.8.0
The i18n epic (T-462): every user-facing label — panels, dialogs, command
palette, menus, settings, framework chrome — now resolves through the i18n
catalog instead of a hardcoded string. Catalogs are bundled per locale under
assets/i18n/<locale>/<namespace>.json. Adds a live UI-language selector
(Settings → Appearance → Language) and a full Dutch (nl-NL) pack; English
stays the default. Architecture in D-102.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
v2.8.0
2026-06-20 19:46:45 +02:00
jpmschweitzerandClaude Opus 4.8 9d7ab35a19 test(i18n): cover the i18n facade, selector, manifest routing + locale (T-462)
Tests for the epic's new code so the 95% floor holds: ClideSettings.i18n
string/interpolated null-safe + localizedCommandTitle; settings-renderer
localization (section/field/help/select/file, project-scope tag, didUpdateWidget);
extension-activation rollback + settings-contribution unregister; the Dutch
gate check; RootShell applying app.locale on boot; and prompt/conversation
tool-body + file-ref coverage. Also harden the menu toggle re-tap (capture the
button point while stable, then tapAt) so it no longer throws getCenter under
the serial coverage load.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 18:26:30 +02:00
jpmschweitzerandClaude Opus 4.8 328902a3d8 feat(i18n): Dutch (nl-NL) translation pack (T-462)
A full nl_NL catalog set under assets/i18n/nl_nl/ — all 26 namespaces, key-for-
key parity with en_us. Proper nouns (clide, Claude, Git, font names) and the
Vim mode names stay as-is; everything else is translated. Selectable via
Settings → Appearance → Language. The a11y i18n gate now also asserts the
Dutch pack covers every Tier-0 key, so a locale switch never falls back to
English for a built-in label.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 17:34:48 +02:00
jpmschweitzerandClaude Opus 4.8 43c650de10 feat(i18n): UI language selector (Settings → Appearance → Language) (T-462)
Make the localization usable: an app.locale select (English / Nederlands) in
the Appearance category, applied live by root_shell — it parses app.locale and
calls i18n.setLocale on boot + on settings change (setLocale is a no-op when
unchanged). nl_NL registered in availableLocales. The ui-design skill now
documents the locale-dir config and the rule to design for string-length
variation (translations run ~20% longer; never hard-size to the English label).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 17:34:08 +02:00
jpmschweitzerandClaude Opus 4.8 bee7f98e45 docs(i18n): record D-102 (i18n routing) + ui-design skill rule
Document the T-462 i18n architecture: ext-id namespaces auto-loaded on
activation, a 'core' catalog for framework chrome, the null-safe
ClideSettings.i18n read facade, contribution titleKey/labelKey fields, and the
assets/i18n/<locale>/<namespace>.json locale-dir layout. Add the "route
user-facing strings through the catalog" rule to the ui-design skill.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 15:57:05 +02:00
jpmschweitzerandClaude Opus 4.8 1bdfd1be1c refactor(i18n): bundle catalogs under assets/i18n/<locale>/ (T-462)
Move the catalogs out of lib/kernel/src/i18n/catalog/ into assets/i18n/, and
switch the layout from a `{namespace}_{locale}.json` filename to a per-locale
DIRECTORY: assets/i18n/en_us/{namespace}.json. Adding a language is now just a
new folder (assets/i18n/nl_nl/, nl_be, en_eu, …) of the same namespace files —
no renames — which is cleaner to maintain and mirrors how an external
extension ships its own catalog. AssetCatalogLoader/FileCatalogLoader resolve
`<root>/<locale>/<namespace>.json`; pubspec bundles the locale folder.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 15:54:14 +02:00