* feat(plugins): add keybindings and modal window capabilities
Add two independent plugin UI capabilities.
Keybindings: plugins declare keybindings in the manifest under
ui.keybindings ({id, default, description}), validated backend-side
(combo grammar + unique ids, requires a UI bundle) and exposed to the
frontend. Plugin JS binds a handler via registry.registerKeybinding(id,
handler); a global dispatcher fires it. Combos are user-overridable in
Settings > Keyboard Shortcuts (namespaced plugin:{pluginId}:{id}) with
conflict detection against core and other plugins.
Modals: host.openModal({title?, content, size?, dismissible?}) returns a
{ close() } handle and renders in a single host-owned Dialog isolated by
PluginErrorBoundary, cleaned up on plugin unload.
Fixture plugin, PLUGIN-API.md / spec, and a Playwright e2e demonstrate a
keybinding handler opening a modal.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(plugins): address review feedback on keybinding dispatch and validation
Core shortcuts now take precedence over plugin keybindings on a colliding
combo (defaultPrevented cooperation between the two capture-phase
dispatchers), a throwing plugin handler no longer aborts dispatch to other
plugins, and the keybinding combo grammar is tightened: the backend validator
and frontend parser both drop the undocumented control/super modifier
aliases and reject shift+digit/symbol combos that can never actually fire
(KeyboardEvent.key reports the shifted character, not the token).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(plugins): stop conflicting core shortcut from double-firing with plugin keybindings
The earlier defaultPrevented guard only covered the central useAppShortcuts
dispatcher. Per-component core shortcuts registered via useKeyboardShortcut
run in the bubble phase, after the capture-phase plugin keybinding dispatcher,
so a plugin-handled-and-prevented combo still triggered the core action too.
Guard useKeyboardShortcut (and use-editor-keybinds, another capture-phase core
listener) on event.defaultPrevented so exactly one action fires per combo.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(plugins): make core shortcuts always win over plugin keybindings
Panel-search, task-switcher, and other core listeners never checked
event.defaultPrevented, so a plugin bound to the same combo (e.g. Cmd/Ctrl+F)
could still fire alongside them. Instead of patching every core listener,
usePluginShortcuts now computes the set of effective core shortcut combos
(registry, resolved through user overrides, plus FIND_IN_PANEL/SAVE) and
skips invoking any plugin handler whose combo is in that set, so the
matching core listener always handles the event exactly once.
Also adds a hook-level regression test for useEditorKeybinds' existing
defaultPrevented guard, and fixes a leaked capture-phase window listener in
use-keyboard-shortcut.test.ts that could bleed into later tests.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(web): keep Cmd+K closing the command panel after shortcut-precedence guard
cmdk's built-in vim bindings intercept Ctrl/Cmd+K and +P as list-navigation
shortcuts and call preventDefault() before the event reaches our window-level
useKeyboardShortcut listeners. Once that hook started bailing on
event.defaultPrevented (added to fix core/plugin shortcut double-firing), the
palette's own Cmd+K close/reopen toggle got silently suppressed whenever focus
was inside the open panel. Disable cmdk's vim bindings on the command palette
so its Ctrl/Cmd+K and +P always reach the app-level toggle.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* docs(plugins): document keybindings and modal window plugin APIs
Fill in the public author-facing docs that the internal plugin-keybindings
and modal-window contract (PLUGIN-API.md, spec.md) already covers: the
registry.registerKeybinding / host.openModal JS surface, the ui.keybindings
manifest field with its combo grammar, and a capability mention on the
Plugins overview page.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Kandev Agent <agent@kandev.dev>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(web): split file search from command panel and add inline task search
Separate the command panel (Cmd+K) from file search (Cmd+Shift+K) to
prevent file results from overpowering commands. Add inline task search
directly in the command panel — tasks appear in a "Tasks" group when
typing ≥2 characters, with workflow step badges and archived state
indicators. Archived tasks are sorted last with reduced opacity.
- Add "search-files" mode to CommandPanelMode
- Add FILE_SEARCH shortcut (Cmd/Ctrl+Shift+K)
- Move file search to dedicated mode, remove from commands mode
- Add useInlineTaskSearchEffect for commands mode task search
- Remove "Search Tasks" sub-mode entry point (now inline)
- Show file search shortcut hint in command panel footer
* feat(web): add configurable keyboard shortcuts in general settings
Add localStorage-based shortcut override system for the command panel
and file search keybindings. Users can record new key combos via the
settings UI and reset to defaults.
- Add shortcut-overrides module (get/set/reset via localStorage)
- Add KeyboardShortcutsCard with shortcut recorder UI
- Add "Keyboard Shortcuts" section to general settings page
* test(web): add e2e tests for command panel and file search split
Cover the key use cases: Cmd+K opens commands without files,
Cmd+Shift+K opens file search, inline task search with workflow step
badges, escape/backspace navigation, and panel toggle behavior.
* fix(test): remove unused seedData variable in command panel e2e test
* feat(settings): persist keyboard shortcuts to user settings API
Replace localStorage-based shortcut overrides with server-persisted
storage via the user settings API. Adds keyboard_shortcuts field to
the backend model, DTO, store, service, and controller. Frontend
reads overrides from Zustand store (hydrated from SSR/WS) and saves
via PATCH /api/v1/user/settings.
* fix(ui): prioritize tasks over commands, add step colors, fix shortcut display
- Move task results above command results in cmd+K for faster task switching
- Replace magnifier icon with task icon (IconSubtask) for task results
- Apply workflow step color to task badges
- Fix formatShortcut early return bug that dropped Shift modifier
when ctrlOrCmd was set (e.g. Cmd+Shift+K showed as Cmd+K)
- Fix same bug in shortcutToCodeMirrorKeybinding
* fix(ui): use clipboard icon for task results in command panel
* style: format command panel footer
* fix(ui): use hammer icon and map step colors to hex for task badges
* feat(ui): show repo directory and agent name on task results
Display secondary details (repository directory, agent name, last
updated) on the right side of task results in the command panel.
* fix: avoid inline selector closure to prevent getServerSnapshot loop
Select the raw repositories map from the store instead of using an
inline selector that closes over workspaceId and returns new array
references, which triggers React's getServerSnapshot infinite loop.
* feat(task): expose agent name and working directory in task DTO
Add primary_agent_name (from AgentProfileSnapshot) and
primary_working_directory (from RepositorySnapshot) to TaskDTO.
Extend the primary session info query to fetch agent_profile_snapshot
and repository_snapshot columns, and extract the new fields in the
handler layer.
* feat(ui): show agent name and working directory on command panel tasks
Replace executor name with agent name from the primary session and
display the working directory (with repository path fallback) in the
task result details.
* fix(ui): add Commands heading to separate tasks from commands in search
* fix(task): use profile display name instead of agent slug
* feat(workflow): add show_in_command_panel per-step configuration
Add a configurable boolean to each workflow step that controls whether
tasks in that step appear in the command panel (Cmd+K). Default
templates mark backlog/done steps as hidden, active steps as visible.
- Add show_in_command_panel to WorkflowStep, StepDefinition, StepPortable
- Add column with idempotent migration (DEFAULT 1 for backwards compat)
- Include field in DTO serialization and export/import round-trip
- Update all default templates (Plan & Build, Architecture, Kanban, PR Review)
- Add tests for export preservation and import round-trip
* feat(ui): wire show_in_command_panel through frontend and settings
- Add field to HTTP types (WorkflowStep, WorkflowStepDTO, StepDefinition)
- Add to kanban state types and KanbanUpdatePayload
- Map in SSR snapshot mapper and WS kanban handler
- Add "Show in command panel" checkbox to workflow step settings
- Extract StepCheckboxRow component to stay within lint limits
* feat(ui): filter command panel tasks by show_in_command_panel flag
Replace position-based heuristic (resolveExcludedStepIds) with the
per-step show_in_command_panel configuration. Tasks in steps where
the flag is false are hidden from the default Cmd+K view but still
accessible via search.
- Replace resolveExcludedStepIds with resolveVisibleStepIds
- Consolidate useInlineTaskSearchEffect params into options object
* fix(ui): differentiate task sections in command panel
Rename the inline task results heading to "Active Tasks" when showing
the default view, distinguishing it from the "Tasks" command group
that contains "Create New Task".
* fix(ui): keep auto-archive checkbox inline with other step options
* fix(ui): include show_in_command_panel in step update action
The server action was missing show_in_command_panel from its Pick type
and body builder, so the checkbox change was silently dropped.
* fix(ui): pass is_start_step and show_in_command_panel from template steps
Both normalizeWorkflowTemplate mappers (workflow-api.ts and
workspaces.ts) were only picking name/position/color/prompt/events
from backend template steps, dropping is_start_step and
show_in_command_panel. This caused all template steps to appear
with show_in_command_panel checked when creating a new workflow.
* fix(ui): fix command panel empty tasks and step update losing checkbox state
Two fixes:
- Command panel: when kanban steps haven't loaded yet, don't filter
tasks by visibility (resolveVisibleStepIds returns null for empty
steps instead of an empty Set that rejects everything)
- Workflow settings: BackendWorkflowStep type and transformWorkflowStep
were missing show_in_command_panel, so refreshing steps after an
update dropped the field and the checkbox always reverted
* fix(ui): prevent back button from stealing focus in command panel
The back button was focusable and appeared before CommandInput, so
opening file search mode focused the button instead of the input.
Add tabIndex={-1} to keep focus on the search input.
* feat(web): show archive icon for archived tasks in command panel
Replace the text badge (e.g. "COMPLETED") with an IconArchive for
archived tasks in the command panel search results. Active tasks
keep the existing IconHammer icon.
* style: fix Prettier formatting and extract useStepMaps hook
Fix Prettier formatting issues in command-panel.tsx and
workflow-pipeline-editor-panels.tsx. Extract useStepMaps hook
from useInlineTaskSearchEffect to stay within the 100-line
function limit.
* fix: address PR review feedback
- Validate keyboard shortcut payload shape before persisting (service.go)
- Normalize KeyboardShortcuts to {} on read path for legacy rows (sqlite.go)
- Deduplicate getRepoDir into getFileName (command-panel-footer.tsx)
- Show toast on shortcut persistence failure instead of swallowing (keyboard-shortcuts-card.tsx)
- Reset keyboard_shortcuts in E2E test fixture for deterministic tests (test-base.ts)