Problem
From anywhere in the cockpit there is no way to jump to an arbitrary Jira ticket. ↵ on a ticket row already mounts the read-only ticket view in-tree (ambre app/src/cockpit/extensions/ticket.tsx, since 2026-09-11) and o in that view opens the browser — but a ticket that is not a row today cannot be reached without leaving the app. More generally the cockpit has no command surface: every verb is a single key, and the extension seam can only add more single keys.
Use cases
- I know the key (
ACC-1234), I am in the cockpit, I want it open here without hunting for the row.
- Same, but I want it in Jira in the browser.
- Later, the same box is where any extension hangs a verb the host cannot know about.
Scope
In (slice 1):
Ctrl+K opens a centred one-step modal over the list (60 cols, list dimmed behind). Footer hint ⌃K launch beside / search.
- Typing a ticket key shows the match on top as the anchor and the destinations as sibling rows:
↵ open here (the existing drills view) and j open in Jira.
commands?: (target: ExtensionTarget) => Command[] on InboxExtension, Command = { id, title, group?, run: () => void | Promise<void> }. The host derives "open here" itself from drills; the ambre ticket extension contributes only "open in Jira". gh-ink stays Jira-free.
- The overlay is a
CommandPalette primitive in @kud/ink-ui, lifted from ambre's app/src/palette.tsx (rendering + grouped-when-idle / flat-fuzzy-when-typing filter). palette.tsx keeps its TSV reading and stdout printing and becomes a thin consumer. Contract roughly items: { id, title, group?, hint?, keywords? }[], onSelect(id), onClose, placeholder?.
- Not-found is one muted line (
no ticket matches "ACC-99"); not-configured is one line plus the fix (Jira not configured · jira config).
- iTerm2 mapping ⌘K →
Ctrl+K documented for the machine, not done by the app (⌘K is iTerm2's default Clear Buffer, so the map must override it).
Out (deferred, deliberately):
- Fuzzy search over ticket titles, recents, PR/issue jump, any non-Jira command, a command list with more than the seeded entries.
- "Open in ambre cockpit" — a phantom target. ambre's cockpit is this shell with three extensions plugged in; from inside it the target is where you already are, and a bare
gh cockpit has no ticket extension. If a new pane running the cockpit on that ticket is wanted, that is a pane spawn and a separate ask.
- Icons, preview pane, multi-column, search history. It is a jump box.
Decisions
- Trigger
Ctrl+K (Iris, Maya). / is already "narrow the list in place" (inbox.tsx:3987) and the launcher is a different verb; ? stays the legend; : is vim muscle memory for something this is not. key.ctrl is currently short-circuited at inbox.tsx:3930, so the chord is free.
- One step, not two (Iris). Pick-ticket-then-pick-where costs a second ↵ for the 90 % case and hides the "where" until you have committed. Idle shows the last few tickets in the same row shape; no verbs without a subject.
- Primitive in ink-ui, now (Maya). Two consumers exist, which is the bar; leaving the filter in
palette.tsx means the inbox hand-rolls a second one — the same drift useTabs was fixed for on 2026-09-15.
- Prompt-sized scope (Theo). A palette with one entry is a prompt, and that is fine. Maya's geometry, Theo's cut.
- Colour (Iris): key in
colors.accent, matched title default, destination verbs in colors.info with the key letter bright and the rest muted; matched characters underlined, never a third colour.
Decisions during the build
- No hotkeys on the rows (Iris). The query box owns the keyboard while the palette is up, so
j types; the rows are flat commands under one cursor — Open KEY here / Open KEY in Jira — verb plain, key in accent, destination word in info, ❯ + bold on the selected row, placeholder ticket key…, no row count unless the list scrolls.
- The primitive is dumb (Maya).
items, query, onQueryChange, onSelect, onClose?, message? — no internal filter, no internal query state. fuzzyFilter ships as a pure helper for the fixed-tree shape.
- The key is matched once, by the host (Maya).
ExtensionTarget gained query and ticketKey; commands(target) is sync and pure, memoised on the query; the inbox composes both messages itself and takes a jiraSetupHint prop for the host's own words.
- The inbox synthesises "open in Jira" too (Maya).
jiraBase is host config and the row menu already opened it; one extracted openInJira serves both. The commands seam landed with no producer, pinned by a stub-extension test so the append path and ordering (host rows first) stay honest.
- Ambre's palette lost its per-group hues. The primitive draws headers in
colors.group; danger rows keep their ! marker in colors.error. Iris's call if the hues are wanted back (a groupColor on the primitive).
Sequencing
Open questions
- Whether ↵ on the palette with an empty query should do anything (probably nothing).
Next steps
Problem
From anywhere in the cockpit there is no way to jump to an arbitrary Jira ticket. ↵ on a ticket row already mounts the read-only ticket view in-tree (ambre
app/src/cockpit/extensions/ticket.tsx, since 2026-09-11) andoin that view opens the browser — but a ticket that is not a row today cannot be reached without leaving the app. More generally the cockpit has no command surface: every verb is a single key, and the extension seam can only add more single keys.Use cases
ACC-1234), I am in the cockpit, I want it open here without hunting for the row.Scope
In (slice 1):
Ctrl+Kopens a centred one-step modal over the list (60 cols, list dimmed behind). Footer hint⌃K launchbeside/ search.↵ open here(the existingdrillsview) andj open in Jira.commands?: (target: ExtensionTarget) => Command[]onInboxExtension,Command = { id, title, group?, run: () => void | Promise<void> }. The host derives "open here" itself fromdrills; the ambre ticket extension contributes only "open in Jira". gh-ink stays Jira-free.CommandPaletteprimitive in@kud/ink-ui, lifted from ambre'sapp/src/palette.tsx(rendering + grouped-when-idle / flat-fuzzy-when-typing filter).palette.tsxkeeps its TSV reading and stdout printing and becomes a thin consumer. Contract roughlyitems: { id, title, group?, hint?, keywords? }[],onSelect(id),onClose,placeholder?.no ticket matches "ACC-99"); not-configured is one line plus the fix (Jira not configured · jira config).Ctrl+Kdocumented for the machine, not done by the app (⌘K is iTerm2's default Clear Buffer, so the map must override it).Out (deferred, deliberately):
gh cockpithas no ticket extension. If a new pane running the cockpit on that ticket is wanted, that is a pane spawn and a separate ask.Decisions
Ctrl+K(Iris, Maya)./is already "narrow the list in place" (inbox.tsx:3987) and the launcher is a different verb;?stays the legend;:is vim muscle memory for something this is not.key.ctrlis currently short-circuited atinbox.tsx:3930, so the chord is free.palette.tsxmeans the inbox hand-rolls a second one — the same driftuseTabswas fixed for on 2026-09-15.colors.accent, matched title default, destination verbs incolors.infowith the key letter bright and the rest muted; matched characters underlined, never a third colour.Decisions during the build
jtypes; the rows are flat commands under one cursor —Open KEY here/Open KEY in Jira— verb plain, key inaccent, destination word ininfo,❯+ bold on the selected row, placeholderticket key…, no row count unless the list scrolls.items,query,onQueryChange,onSelect,onClose?,message?— no internal filter, no internal query state.fuzzyFilterships as a pure helper for the fixed-tree shape.ExtensionTargetgainedqueryandticketKey;commands(target)is sync and pure, memoised on the query; the inbox composes both messages itself and takes ajiraSetupHintprop for the host's own words.jiraBaseis host config and the row menu already opened it; one extractedopenInJiraserves both. Thecommandsseam landed with no producer, pinned by a stub-extension test so the append path and ordering (host rows first) stay honest.colors.group; danger rows keep their!marker incolors.error. Iris's call if the hues are wanted back (agroupColoron the primitive).Sequencing
IssueBoard, so the ambre wiring lands on whichever surface is live when it ships. Do not fold this in.@kud/cli-ui) is non-interactive output and unrelated; stand alone.Open questions
Next steps
@kud/ink-ui0.30.0:CommandPalette+fuzzyFilter, lifted from$AMBRE/app/src/palette.tsx— kud/ink-ui@46bf748@kud/gh-ink0.58.0 /@kud/gh-cockpit0.6.2:commandsseam, Ctrl+K overlay,openInJiraextracted, footer + legend,launcher.test.tsx— kud/gh@df7c837, released by #125ticket.tsx: no change needed — the inbox already held the one URL rule (tin the row menu), so it synthesises both rows itself (Maya, during build);palette.tsxrepointed to the primitive and all three pins bumped — kud/ambre@be8d7166~/.ambredocs:docs/postinstall/iterm2.md— ⌘K → Send Hex Code0x0b, same commit