|
|
|
@@ -239,3 +239,46 @@ INSERT INTO tickets (id, type, parent_id, title, description, status, priority,
|
|
|
|
|
|
|
|
|
|
Refinement (2026-06-03): Shift+Tab is REJECTED as the trigger — Tab/Shift+Tab are real a11y focus-traversal intents (focus.next/focus.previous) since T-204, so hijacking Shift+Tab would break keyboard navigation. Instead: (a) Ctrl/Cmd+M cycles, intercepted at the focused Claude composer (so it targets that pane''s session, no global ''find focused session'' needed); (b) the status-line mode label becomes a FOCUSABLE button — cycles on click and on Enter/Space when focused (a11y-native, Tab reaches it); (c) a ''Claude: Cycle permission mode'' palette command. Safe trio default->acceptEdits->plan->default; bypassPermissions only via explicit confirmed path.', 'done', 'medium', NULL, NULL, 'D-78', '2026-06-03 09:37:54', '2026-06-03 11:50:23', NULL, '38722e333acfbd8f5b8491624c587c69', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
|
|
|
|
INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-231', 'task', 'T-208', 'CLI verb to open a doc in a UI reader (drive-half of D-6 parity)', 'Dogfood finding (2026-06-03): Epic C gave the CLI the OBSERVE half of D-6 parity (clide status / pane list read live UI state). The DRIVE half is missing: there is no CLI/IPC verb that opens a doc in a UI reader. Opening a ticket/decision/markdown doc happens only inside the Flutter app, by publishing to the kernel MessageBus (e.g. messages.publish(''builtin.tickets'',''selection'',{''id'':id}); see tickets/src/extension.dart:29 which subscribes publisher ''builtin.tickets'' channel ''selection'', and ticket_detail_view.dart). lib/src/daemon/ has NO verb that reaches the MessageBus, so an agent cannot say ''look at this with me''. Proposed: add a bridge verb (e.g. ui.open / view.open <publisher> <id-or-path>) that publishes a ''selection'' message to the bus, so ''clide ui open tickets T-48'' opens T-48 in the ticket reader (and decisions/markdown analogously). Wiring: thread the kernel MessageBus into the dispatcher the same way panels/readerNav were threaded for T-219/T-221 (daemonClientFactory param or post-boot capture in main.dart). Honor D-6 (every UI action has a CLI verb). Complements Epic C (T-218): observe -> drive.', 'backlog', 'medium', NULL, NULL, 'D-6', '2026-06-03 13:17:32', '2026-06-03 13:17:32', NULL, '9a36072c19b514a56efe7ee7b5970457', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
|
|
|
|
INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-230', 'story', 'T-132', 'Cluster meta messages into a collapsible activity card', 'The Claude pane renders every transcript item as its own row, so a heavy agent turn becomes a wall of tool-call/result rows (Bash <cmd> / Bash · result {…} / ''completed with no output'') that buries the messages that matter (user + Claude prose). Wireframe: docs/design/wireframes/claude/meta-activity-card.png.
|
|
|
|
|
|
|
|
|
|
Fold runs of consecutive ''meta'' items into one live, collapsible activity card:
|
|
|
|
|
- The card shows the MOST RECENT meta line as a live ticker and updates in place as new ones stream in — a running ''what''s happening now''. A step count (''14 steps'') sits on the right.
|
|
|
|
|
- Collapsed by default; click the chevron to expand the full list of folded steps, click to re-collapse.
|
|
|
|
|
- ''Sticky'' items never fold — they render first-class. A sticky item SEALS the current card and a new cluster begins after it. A cluster = one unbroken run of foldable items between two sticky items.
|
|
|
|
|
|
|
|
|
|
Taxonomy over the sealed ConversationItem set (transcript_reader.dart): UserMessage, AssistantTextMessage, AssistantThinkingMessage, AssistantToolUse, ToolResultMessage.
|
|
|
|
|
- Always sticky: UserMessage, AssistantTextMessage. Permission/AskUserQuestion prompts already live in the interaction zone (D-78) and are inherently sticky / cluster-breaking — no change, but note they break the run too.
|
|
|
|
|
- Foldable: AssistantToolUse + its paired ToolResultMessage (pairing already exists via toolUseById, T-168).
|
|
|
|
|
|
|
|
|
|
Refinement decisions (2026-06-03, user):
|
|
|
|
|
1. WHAT FOLDS is a 3-level setting (conservative → aggressive); DEFAULT = level 1. Build the foldable predicate so all three are switchable:
|
|
|
|
|
- L1 (default): fold tool calls + results only; diffs (Edit/Write results) AND AssistantThinkingMessage stay first-class/sticky.
|
|
|
|
|
- L2: also fold thinking; diffs stay sticky.
|
|
|
|
|
- L3: fold everything except UserMessage + AssistantTextMessage (incl. diffs + thinking).
|
|
|
|
|
2. ERRORS: a failed/error ToolResultMessage SURFACES — treated as sticky, stays visible and breaks the cluster (see wireframe state B). Start here; the user noted some non-fatal errors are themselves clutter, so expect to tune which errors surface vs. fold.
|
|
|
|
|
3. DEFAULT/RUN STATE: always collapsed, showing the last line + step count, with the ticker updating live. NO setting. (Rejected auto-expanding the in-flight cluster: it scrolls the last useful sticky message out of view during long runs and only collapses when done — worst timing. Collapsed-with-live-ticker keeps the last sticky message anchored while preserving the motion/dynamism.)
|
|
|
|
|
|
|
|
|
|
Implementation home: a ''collapse adjacent foldable items'' grouping pass over ConversationController.items, consumed by ConversationView; the card is a new conversation_card variant. Keep it accessible (keyboard expand/collapse + semantics: announce step count and collapsed/expanded state).
|
|
|
|
|
|
|
|
|
|
Acceptance:
|
|
|
|
|
1. Consecutive foldable items collapse into one card; the collapsed card shows the latest line + a live-updating step count.
|
|
|
|
|
2. A sticky message (user / Claude prose / surfaced error / interaction-zone prompt) seals the card and starts a new cluster after it.
|
|
|
|
|
3. Expanding shows every folded step (tool call + result pairs) in order; collapsing returns to the one-line view.
|
|
|
|
|
4. The foldable level is a setting (default L1: diffs + thinking stay first-class); switching levels re-groups the transcript.
|
|
|
|
|
5. Failed tool results surface as their own (sticky) row and break the cluster.
|
|
|
|
|
6. Keyboard + screen-reader accessible (expand/collapse, step count announced).', 'ready', 'medium', NULL, NULL, NULL, '2026-06-03 11:15:39', '2026-06-03 13:23:43', NULL, '21cff726143d1c2b3102ee1ef5b6bfdb', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
|
|
|
|
INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-82', 'task', NULL, 'add Catppuccin theme to bundled themes', 'User request: ship a Catppuccin theme alongside the four bundled themes (clide, midnight, paper, terminal — D-44).
|
|
|
|
|
|
|
|
|
|
**Acceptance:**
|
|
|
|
|
- New theme YAML at `lib/kernel/src/theme/themes/catppuccin.yaml` (or split for the four Catppuccin flavours: latte / frappe / macchiato / mocha — pick one or all four; recommend Mocha as the dark default and Latte for the light variant).
|
|
|
|
|
- Tokens populated for every key in `SurfaceTokens` (lib/kernel/src/theme/tokens.dart) — chrome, panels, tabs, list items, status colours, syntax tokens (D-45).
|
|
|
|
|
- WCAG-AA contrast gate (D-22) passes against the theme.
|
|
|
|
|
- Listed in `assets/licenses.yaml` with the upstream license (MIT) and attribution.
|
|
|
|
|
- Theme picker (`builtin.theme-picker`) shows it in the live-switch menu.
|
|
|
|
|
|
|
|
|
|
**Source palettes:** https://github.com/catppuccin/catppuccin#-palette — official hex values, don`t paraphrase.
|
|
|
|
|
|
|
|
|
|
**Why now:** test user request; also a good exercise for any future user-contributed theme since Catppuccin is the most common one to ask for.', 'ready', 'low', NULL, NULL, NULL, '2026-05-06 08:30:15', '2026-06-03 13:24:18', NULL, 'b8cbb4530197f04a1d5277a9cfee5dbc', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
|
|
|
|
INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-232', 'task', 'T-208', 'CLI argv args dont reach typed command handlers (positional/flags vs named)', 'Major dogfood finding (2026-06-03), verified live: parameterized subsystem commands are NOT reachable from the clide CLI. The C client sends raw argv; parseArgv (lib/src/cli/argv_to_request.dart) turns ''clide editor open X'' into args={positional:[X]} (also flags:{}, passthrough:[]). But the typed handlers read NAMED top-level keys: editor.open reads args[''path''] (editor_commands.dart:64), files.read reads args[''path''], pane.close/editor.activate read args[''id''], etc. Nothing maps positional/flags -> those names, so every arg-taking verb returns ''X is required'' from the CLI. Confirmed: ''clide editor open pubspec.yaml'' and ''--path=pubspec.yaml'' both -> ''path is required''; ''clide files read pubspec.yaml'' -> ''files.read requires a path''. grep shows ONLY the new ui_command.dart reads args[''positional'']. Net: an external agent can OBSERVE (no-arg reads: status, git status, files root, pane list, editor active) but cannot DRIVE anything parameterized -- undercutting CLI-first (D-1) and the D-6 parity premise behind the whole T-208 initiative. Needs a decision on the mapping contract: most ergonomic is a per-command POSITIONAL/flag schema declared where the handler registers (extends the co-registered schema of D-74) so ''clide editor open <path>'' binds positional[0]->path; alternative is lifting --flags into top-level named args. Then either remap in argv_dispatch/parseArgv before dispatch, or have handlers read a normalized accessor. High priority: this is the gating bug for ''Give Claude hands''.', 'backlog', 'high', NULL, NULL, 'D-6', '2026-06-03 13:29:49', '2026-06-03 13:29:49', NULL, '9b89ce5c134ecd097db7e5bd19f3ff75', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
|
|
|
|
INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-233', 'task', 'T-208', 'Open a file in the diff panel for review via ui.open', 'Follow-up from T-231 (2026-06-03). T-231 added ''clide ui open <reader> <id|path>'' for the ReaderNav-backed readers (tickets/decisions/markdown), which open by subscribing to a MessageBus ''selection''. The DIFF panel is not covered: DiffController (lib/builtin/diff/src/diff_controller.dart) loads the git working-tree diff via the git.diff IPC verb and refreshes on git.changed events; it has no ''selection''/''focus'' bus channel, so ui.open has no entry point to target a file. Note: because the diff panel shows the working tree, an agent''s uncommitted edits already APPEAR there on the next git.changed refresh -- what''s missing is (a) bringing the diff tab to the front and (b) scrolling/filtering to a specific file. Proposed: give the diff view a bus subscription (publisher ''builtin.diff'', channel ''selection''/''focus'', {path}) that reveals its tab and focuses/filters that file''s diff (DiffController.load already accepts a paths filter), then add ''diff'' as a 4th ui.open target. Related gap: pane.focus is advisory-only (pane_commands.dart) so ''bring tab to front'' isn''t truly wired -- the reveal likely needs the same mechanism the readers use to reveal their tab on selection. Depends in spirit on T-232 (CLI args) only if a non-bus path is chosen; the bus path works regardless.', 'backlog', 'medium', NULL, NULL, 'D-6', '2026-06-03 13:29:59', '2026-06-03 13:29:59', NULL, 'bf44a02d9e8daba9f5d6a6f486c0c881', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
|
|
|
|
INSERT INTO tickets (id, type, parent_id, title, description, status, priority, assigned_to, team, decision_ref, created_at, updated_at, deleted_at, hash, canonical_version) VALUES ('T-231', 'task', 'T-208', 'CLI verb to open a doc in a UI reader (drive-half of D-6 parity)', 'Dogfood finding (2026-06-03): Epic C gave the CLI the OBSERVE half of D-6 parity (clide status / pane list read live UI state). The DRIVE half is missing: there is no CLI/IPC verb that opens a doc in a UI reader. Opening a ticket/decision/markdown doc happens only inside the Flutter app, by publishing to the kernel MessageBus (e.g. messages.publish(''builtin.tickets'',''selection'',{''id'':id}); see tickets/src/extension.dart:29 which subscribes publisher ''builtin.tickets'' channel ''selection'', and ticket_detail_view.dart). lib/src/daemon/ has NO verb that reaches the MessageBus, so an agent cannot say ''look at this with me''. Proposed: add a bridge verb (e.g. ui.open / view.open <publisher> <id-or-path>) that publishes a ''selection'' message to the bus, so ''clide ui open tickets T-48'' opens T-48 in the ticket reader (and decisions/markdown analogously). Wiring: thread the kernel MessageBus into the dispatcher the same way panels/readerNav were threaded for T-219/T-221 (daemonClientFactory param or post-boot capture in main.dart). Honor D-6 (every UI action has a CLI verb). Complements Epic C (T-218): observe -> drive.', 'done', 'medium', NULL, NULL, 'D-6', '2026-06-03 13:17:32', '2026-06-03 13:30:05', NULL, '78cb72d5be8881a31a0cad37ec138d21', 1) ON CONFLICT(id) DO UPDATE SET type=excluded.type, parent_id=excluded.parent_id, title=excluded.title, description=excluded.description, status=excluded.status, priority=excluded.priority, assigned_to=excluded.assigned_to, team=excluded.team, decision_ref=excluded.decision_ref, updated_at=excluded.updated_at, deleted_at=excluded.deleted_at, hash=excluded.hash, canonical_version=excluded.canonical_version WHERE excluded.updated_at > tickets.updated_at OR (excluded.updated_at = tickets.updated_at AND excluded.hash > tickets.hash);
|
|
|
|
|