Problem statement
Add a discoverable single-key TUI shortcut (g) that lets a user delegate the focused work item to GitHub Copilot. The shortcut opens a confirmation modal (with an optional "Force" toggle to override the do-not-delegate tag), runs the existing delegate flow, updates local state, shows feedback, and opens the created GitHub issue in the browser.
Users
- End users / developers who use the TUI to manage work items.
- As a keyboard-first developer, I want to press
g on a focused item and confirm delegation so I can hand off implementation work without leaving the terminal.
- As a producer, I want a Force option available in the confirmation modal to override a
do-not-delegate tag when appropriate.
Success criteria
- Pressing
g when an item is focused opens a confirmation modal in both list and detail views.
- Confirming the modal triggers the delegate flow: item is pushed to GitHub, the resulting issue is assigned to
@copilot, local work item status and assignee are updated, and labels/stage are re-synced to GitHub.
- If the work item has
do-not-delegate and Force is not selected, delegation is blocked and the modal explains why; selecting Force proceeds and maps to --force.
- After successful delegation the TUI shows a toast with the GitHub issue URL and opens the default browser to the issue.
- Automated tests (unit for key handling + modal, mocked delegate flow; integration verifying preservation of other items) are added and pass in CI.
Constraints
- Reuse the existing
wl github delegate flow and internal helpers where possible (do not duplicate push/assign logic).
- TUI changes must be non-destructive: do not alter db import semantics; rely on non-destructive
db.upsertItems() already introduced for delegate flows.
- The
g shortcut must not conflict with existing TUI bindings (D is reserved for do-not-delegate). Chosen binding: lowercase g.
- Modal must allow a Force toggle that maps to the CLI
--force guard-rail; default behaviour respects do-not-delegate tags.
- Non-interactive flows (scripts/agents) are unchanged — this is a TUI enhancement only. The implementation should call existing delegate APIs so behaviour matches CLI.
Existing state
wl github delegate <id> exists in src/commands/github.ts and implements push + assign + local state update flows (see delegate handler and guard-rails).
- The TUI already implements a
D key to toggle do-not-delegate (see src/tui/controller.ts and src/tui/constants.ts).
- There are existing guard-rail and delegate unit/integration tests (e.g.
tests/cli/delegate-guard-rails.test.ts, tests/integration/github-upsert-preservation.test.ts).
- Several related fixes and helpers are implemented (assign helper, upsertItems) to make delegation safe and idempotent; integration tests for preservation exist.
Desired change
- Add
g to src/tui/constants.ts and wire handling in src/tui/controller.ts so g is active in both list and detail views when an item is focused.
- On
g press open a confirmation modal containing: item title, brief summary, Confirm/Cancel buttons, and a Force (override do-not-delegate) checkbox that maps to the CLI --force flag.
- If confirmed, call the same internal delegate flow used by
wl github delegate (programmatic invocation using shared helpers) rather than shelling out to spawn a CLI process; handle JSON/human modes appropriately for feedback.
- On success, update the focused item's display (status/assignee/badges), show a toast with the GitHub issue URL, and open the URL in the default browser.
- Add unit tests for key handling, modal behaviour, Force toggle mapping, and a mocked delegate flow; add or extend integration tests to assert that non-delegated items are preserved.
Feature Plan
Execution order
Features 1 and 2 can start in parallel. Feature 3 depends on Feature 1. Feature 4 depends on Features 1 and 3. Feature 5 depends on all prior features.
Key decisions
- Refactor delegate flow into shared helper (not duplicate logic in TUI).
- Browser-open is opt-in only (env var WL_OPEN_BROWSER).
- Loading indicator shown in modal during delegate execution.
- Use blessed built-in dialog primitives for modal.
g active in both list and detail views.
- Error feedback: short toast + error dialog with full detail.
Feature 1: Extract delegate orchestration helper (WL-0MMJO1ZHO16ED15O)
Summary: Refactor the delegate flow (guard rails, push, assign, state update) from src/commands/github.ts into a reusable async function that returns a structured result, so both CLI and TUI can invoke it programmatically.
Acceptance Criteria:
- A new function
delegateWorkItem(db, config, itemId, options: { force?: boolean }) exists and returns { success, issueUrl, issueNumber, error?, pushed, assigned }.
- The function never calls
process.exit() or writes to console.log; all results are returned as structured data.
- Guard rails (do-not-delegate check, children warning) return structured errors (e.g.,
{ success: false, error: 'do-not-delegate' }) instead of exiting.
- If called with a non-existent item ID, it returns
{ success: false, error: 'not-found' } without throwing.
- The existing CLI
wl github delegate command calls this helper and produces identical stdout, stderr, and exit codes.
- Existing delegate unit tests and guard-rail tests pass without modification (or with minimal import-path adjustments).
Minimal Implementation:
- Extract lines ~450-617 of
src/commands/github.ts into a new async function in a shared module (e.g., src/delegate-helper.ts or co-located in src/commands/github.ts).
- Replace
process.exit(1) with early returns of error result objects.
- Replace
console.log / output.json calls with return values.
- CLI action handler wraps the helper, converting results to process.exit / console output.
- Update existing tests if import paths change.
Dependencies: None (foundational layer).
Deliverables: New/updated src/delegate-helper.ts or refactored src/commands/github.ts; updated CLI action handler; updated tests in tests/cli/delegate-guard-rails.test.ts.
Key Files: src/commands/github.ts:440-617, src/github.ts, src/github-sync.ts, tests/cli/delegate-guard-rails.test.ts.
Feature 2: TUI single-key g binding (WL-0MMJF6COK05XSLFX)
Summary: Register the TUI single-key g keybinding to trigger the delegate confirmation modal when a work item is focused, active in both list and detail views.
Acceptance Criteria:
KEY_DELEGATE constant added to src/tui/constants.ts as ['g'].
g appears in the help menu under Actions with description 'Delegate to Copilot'.
- Pressing
g when a work item is focused opens the delegate confirmation modal.
- Pressing
g with no item focused is a no-op with a short toast: 'No item selected'.
g is suppressed during move mode, search mode, and when modals are open.
g does not conflict with any existing keybinding.
- Unit tests cover focused, no-focused, and suppressed scenarios.
Minimal Implementation:
- Add
KEY_DELEGATE = ['g'] to src/tui/constants.ts.
- Add
{ keys: 'g', description: 'Delegate to Copilot' } to the Actions section of DEFAULT_SHORTCUTS.
- Wire
screen.key(KEY_DELEGATE, ...) in src/tui/controller.ts, gated on same conditions as existing action keys (not in move mode, search, or modal).
- Handler validates focused item, then calls the delegate modal (Feature 3). Can be implemented first with a stub callback.
Dependencies: Soft dependency on Feature 3 (Confirmation modal).
Deliverables: Updated src/tui/constants.ts, updated src/tui/controller.ts, unit tests for key handling, updated TUI help menu.
Key Files: src/tui/constants.ts, src/tui/controller.ts.
Feature 3: Delegate confirmation modal with Force (WL-0MMJO2OAH1Q20TJ3)
Summary: Build a delegate confirmation modal using blessed dialog primitives that shows the item title, a Force checkbox, Confirm/Cancel buttons, and a loading indicator during execution.
Acceptance Criteria:
- Modal displays: item title (truncated if long), 'Delegate to GitHub Copilot?' prompt, Force checkbox (default unchecked), Confirm and Cancel buttons.
- If item has
do-not-delegate tag and Force is unchecked, Confirm is visually disabled or produces an inline error message explaining why delegation is blocked.
- Force checkbox maps to the
force parameter of the delegate helper.
- After Confirm, modal shows a loading indicator ('Pushing to GitHub...' / 'Assigning @copilot...').
- If the user presses Esc during the loading state, the modal closes but the delegate flow continues to completion (no partial state corruption).
- Cancel closes the modal with no side effects.
- Modal is keyboard-navigable (Tab between elements, Enter to confirm, Esc to cancel).
- Unit tests verify: modal opens with correct content, Force toggle behavior, Cancel closes cleanly, do-not-delegate blocking.
Prototype / Experiment: Spike: verify blessed supports dynamic content updates (replace confirm text with loading spinner) inside a modal/form widget. Success: modal text can be updated after creation without destroying/recreating the widget.
Minimal Implementation:
- Create a modal function (e.g.,
showDelegateModal(screen, item, callback)) in src/tui/controller.ts or a new src/tui/delegate-modal.ts.
- Use blessed message/question or form + checkbox + button widgets.
- On Confirm: show loading state, call delegate helper (Feature 1), show result.
- On Cancel: destroy modal, return focus to list.
Dependencies: Feature 1 (WL-0MMJO1ZHO16ED15O).
Deliverables: Modal implementation in src/tui/controller.ts or src/tui/delegate-modal.ts; unit tests for modal behavior.
Key Files: src/tui/controller.ts, src/commands/github.ts.
Feature 4: Post-delegation feedback and error handling (WL-0MMJO338Z167IJ6T)
Summary: After the delegate flow completes (success or failure), update the TUI state, show a toast, display errors in a dialog, and optionally open the browser.
Acceptance Criteria:
- On success: focused item display updates (status badge to 'in-progress', assignee to '@github-copilot'), a toast shows 'Delegated: '.
- On failure: a short toast shows 'Delegation failed' and an error dialog opens with the full error message.
- On delegate failure, the focused item's status and assignee in the TUI list remain unchanged from their pre-delegation values.
- Browser-open is opt-in: only attempted if config
WL_OPEN_BROWSER=true (or equivalent) is set.
- If
WL_OPEN_BROWSER is unset or falsy, no browser process is spawned.
- If browser-open fails (env var set but no browser available), a warning toast is shown but it does not block the success flow.
- Non-delegated items in the list are unchanged (no side effects from upsertItems).
- Unit tests verify: toast content on success, toast + error dialog on failure, item state refresh, browser-open gating.
Minimal Implementation:
- In the delegate callback (after modal confirm), inspect result from the delegate helper.
- Call
showToast(...) with the issue URL or error summary.
- On error, open a blessed message box with full error text.
- Call
renderListAndDetail() to refresh the focused item's display.
- For browser-open: check
process.env.WL_OPEN_BROWSER, call Node child_process.exec with platform-appropriate command (xdg-open on Linux, open on macOS, powershell.exe Start on WSL). Reuse existing platform-detection patterns if available.
Dependencies: Feature 1 (WL-0MMJO1ZHO16ED15O), Feature 3 (WL-0MMJO2OAH1Q20TJ3).
Deliverables: Updated src/tui/controller.ts (feedback handling in delegate callback); browser-open utility (new or reuse existing); unit tests for feedback and error handling.
Key Files: src/tui/controller.ts, src/commands/github.ts.
Feature 5: Delegate TUI integration tests and docs (WL-0MMJO3LBG0NGIBQV)
Summary: Add integration tests for the full TUI delegate flow (mocked GitHub) and update TUI.md / CLI.md documentation.
Acceptance Criteria:
- Integration test: TUI
g key -> modal -> confirm -> delegate helper called with correct args -> item state updated.
- Integration test:
g key with do-not-delegate tag -> Force unchecked -> blocked; Force checked -> proceeds.
- Integration test: delegate failure -> error dialog shown, item state unchanged.
- Integration test: non-delegated items preserved after delegation (reuse assertions from
github-upsert-preservation.test.ts).
- TUI.md updated:
g shortcut documented in 'Work Item Actions' section with description 'Delegate to Copilot'.
- CLI.md updated: mention TUI shortcut as alternative to
wl github delegate in the delegate section.
- All existing tests continue to pass.
Minimal Implementation:
- Add test file
tests/tui/delegate-shortcut.test.ts (or extend existing TUI test structure).
- Mock
assignGithubIssueAsync and upsertIssuesFromWorkItems (same patterns as delegate-guard-rails.test.ts).
- Update TUI.md Work Item Actions section.
- Update CLI.md delegate section.
Dependencies: Feature 1 (WL-0MMJO1ZHO16ED15O), Feature 2 (WL-0MMJF6COK05XSLFX), Feature 3 (WL-0MMJO2OAH1Q20TJ3), Feature 4 (WL-0MMJO338Z167IJ6T).
Deliverables: New tests/tui/delegate-shortcut.test.ts; updated TUI.md; updated CLI.md.
Key Files: tests/cli/delegate-guard-rails.test.ts, tests/integration/github-upsert-preservation.test.ts, TUI.md, CLI.md.
Related work
src/commands/github.ts — Contains the wl github delegate implementation (push, assign, local state update). Use as the behavioral reference for the TUI flow.
src/tui/controller.ts — Current TUI controller; contains existing do-not-delegate toggle and example keybinding wiring.
src/tui/constants.ts — TUI keybindings list (add g entry here).
src/commands/update.ts — CLI flag --do-not-delegate support; relevant for guard-rail parity.
tests/cli/delegate-guard-rails.test.ts — Unit tests for delegate guard-rails; useful for test patterns and mocks.
tests/integration/github-upsert-preservation.test.ts — Integration test verifying delegate/upsert preserves unrelated items; reuse assertions.
- CLI.md — Documentation for update flags and do-not-delegate; update to mention TUI shortcut.
Potentially related work items
- WL-0MM8LXODU1DA2PON — Implement push + assign + local state update flow (core delegate orchestration). Use as implementation reference.
- WL-0MM8LWWCD014HTGU — Add assignGithubIssue helper (GH issue assignment helper used by delegate flow).
- WL-0MM8LX8RB0OVLJWB — Register delegate subcommand with guard rails (CLI registration and guard-rail behavior).
- WL-0MM8LY8LU1PDY487 — End-to-end unit test suite for delegate (use patterns and mocked GH calls).
- WL-0MM8V55PV1Q32K7D — Fix destructive db.import() in GitHub flows / add db.upsertItems (ensures delegate flow is non-destructive).
- WL-0MM8NN4S71WUBRFT — Fix @copilot assignee handle in delegate command (historical bug fixed; verify behaviour uses
@copilot).
- WL-0MLHNPSGP0N397NX — Provide a single-key toggle for
do-not-delegate (already implemented as D, TUI parity exists).
Notes / Implementation hints
- Prefer calling internal delegate helpers (upsert + assign + state update) so UI can render progress and errors without spawning a separate process.
- Use the existing toast helper patterns in
src/tui/controller.ts (e.g., showToast) for immediate feedback.
- For opening URLs use the existing project utility or Node's
open/child_process patterns while keeping it optional (configurable) for headless environments.
- Add tests mirroring
tests/cli/delegate-guard-rails.test.ts structure for TUI flows; mock GH assignment to avoid external calls.
Risks & assumptions
- Risk: GitHub authentication/
gh availability may fail; UI must surface errors and not update local state on failure.
- Risk: Accidental delegation if modal confirmation is bypassed; mitigation: require explicit confirm and show clear do-not-delegate status.
- Assumption: db.upsertItems() exists and preserves unrelated items (see WL-0MM8V55PV1Q32K7D).
Final summary headline:
Add TUI shortcut g to delegate focused work item to GitHub Copilot with confirmation and Force override; update local state, show toast and open created issue.
Problem statement
Add a discoverable single-key TUI shortcut (
g) that lets a user delegate the focused work item to GitHub Copilot. The shortcut opens a confirmation modal (with an optional "Force" toggle to override thedo-not-delegatetag), runs the existing delegate flow, updates local state, shows feedback, and opens the created GitHub issue in the browser.Users
gon a focused item and confirm delegation so I can hand off implementation work without leaving the terminal.do-not-delegatetag when appropriate.Success criteria
gwhen an item is focused opens a confirmation modal in both list and detail views.@copilot, local work itemstatusandassigneeare updated, and labels/stage are re-synced to GitHub.do-not-delegateand Force is not selected, delegation is blocked and the modal explains why; selecting Force proceeds and maps to--force.Constraints
wl github delegateflow and internal helpers where possible (do not duplicate push/assign logic).db.upsertItems()already introduced for delegate flows.gshortcut must not conflict with existing TUI bindings (Dis reserved for do-not-delegate). Chosen binding: lowercaseg.--forceguard-rail; default behaviour respectsdo-not-delegatetags.Existing state
wl github delegate <id>exists insrc/commands/github.tsand implements push + assign + local state update flows (see delegate handler and guard-rails).Dkey to toggledo-not-delegate(seesrc/tui/controller.tsandsrc/tui/constants.ts).tests/cli/delegate-guard-rails.test.ts,tests/integration/github-upsert-preservation.test.ts).Desired change
gtosrc/tui/constants.tsand wire handling insrc/tui/controller.tssogis active in both list and detail views when an item is focused.gpress open a confirmation modal containing: item title, brief summary, Confirm/Cancel buttons, and aForce (override do-not-delegate)checkbox that maps to the CLI--forceflag.wl github delegate(programmatic invocation using shared helpers) rather than shelling out to spawn a CLI process; handle JSON/human modes appropriately for feedback.Feature Plan
Execution order
Features 1 and 2 can start in parallel. Feature 3 depends on Feature 1. Feature 4 depends on Features 1 and 3. Feature 5 depends on all prior features.
Key decisions
gactive in both list and detail views.Feature 1: Extract delegate orchestration helper (WL-0MMJO1ZHO16ED15O)
Summary: Refactor the delegate flow (guard rails, push, assign, state update) from
src/commands/github.tsinto a reusable async function that returns a structured result, so both CLI and TUI can invoke it programmatically.Acceptance Criteria:
delegateWorkItem(db, config, itemId, options: { force?: boolean })exists and returns{ success, issueUrl, issueNumber, error?, pushed, assigned }.process.exit()or writes toconsole.log; all results are returned as structured data.{ success: false, error: 'do-not-delegate' }) instead of exiting.{ success: false, error: 'not-found' }without throwing.wl github delegatecommand calls this helper and produces identical stdout, stderr, and exit codes.Minimal Implementation:
src/commands/github.tsinto a new async function in a shared module (e.g.,src/delegate-helper.tsor co-located insrc/commands/github.ts).process.exit(1)with early returns of error result objects.console.log/output.jsoncalls with return values.Dependencies: None (foundational layer).
Deliverables: New/updated
src/delegate-helper.tsor refactoredsrc/commands/github.ts; updated CLI action handler; updated tests intests/cli/delegate-guard-rails.test.ts.Key Files:
src/commands/github.ts:440-617,src/github.ts,src/github-sync.ts,tests/cli/delegate-guard-rails.test.ts.Feature 2: TUI single-key g binding (WL-0MMJF6COK05XSLFX)
Summary: Register the TUI single-key
gkeybinding to trigger the delegate confirmation modal when a work item is focused, active in both list and detail views.Acceptance Criteria:
KEY_DELEGATEconstant added tosrc/tui/constants.tsas['g'].gappears in the help menu under Actions with description 'Delegate to Copilot'.gwhen a work item is focused opens the delegate confirmation modal.gwith no item focused is a no-op with a short toast: 'No item selected'.gis suppressed during move mode, search mode, and when modals are open.gdoes not conflict with any existing keybinding.Minimal Implementation:
KEY_DELEGATE = ['g']tosrc/tui/constants.ts.{ keys: 'g', description: 'Delegate to Copilot' }to the Actions section ofDEFAULT_SHORTCUTS.screen.key(KEY_DELEGATE, ...)insrc/tui/controller.ts, gated on same conditions as existing action keys (not in move mode, search, or modal).Dependencies: Soft dependency on Feature 3 (Confirmation modal).
Deliverables: Updated
src/tui/constants.ts, updatedsrc/tui/controller.ts, unit tests for key handling, updated TUI help menu.Key Files:
src/tui/constants.ts,src/tui/controller.ts.Feature 3: Delegate confirmation modal with Force (WL-0MMJO2OAH1Q20TJ3)
Summary: Build a delegate confirmation modal using blessed dialog primitives that shows the item title, a Force checkbox, Confirm/Cancel buttons, and a loading indicator during execution.
Acceptance Criteria:
do-not-delegatetag and Force is unchecked, Confirm is visually disabled or produces an inline error message explaining why delegation is blocked.forceparameter of the delegate helper.Prototype / Experiment: Spike: verify blessed supports dynamic content updates (replace confirm text with loading spinner) inside a modal/form widget. Success: modal text can be updated after creation without destroying/recreating the widget.
Minimal Implementation:
showDelegateModal(screen, item, callback)) insrc/tui/controller.tsor a newsrc/tui/delegate-modal.ts.Dependencies: Feature 1 (WL-0MMJO1ZHO16ED15O).
Deliverables: Modal implementation in
src/tui/controller.tsorsrc/tui/delegate-modal.ts; unit tests for modal behavior.Key Files:
src/tui/controller.ts,src/commands/github.ts.Feature 4: Post-delegation feedback and error handling (WL-0MMJO338Z167IJ6T)
Summary: After the delegate flow completes (success or failure), update the TUI state, show a toast, display errors in a dialog, and optionally open the browser.
Acceptance Criteria:
WL_OPEN_BROWSER=true(or equivalent) is set.WL_OPEN_BROWSERis unset or falsy, no browser process is spawned.Minimal Implementation:
showToast(...)with the issue URL or error summary.renderListAndDetail()to refresh the focused item's display.process.env.WL_OPEN_BROWSER, call Nodechild_process.execwith platform-appropriate command (xdg-openon Linux,openon macOS,powershell.exe Starton WSL). Reuse existing platform-detection patterns if available.Dependencies: Feature 1 (WL-0MMJO1ZHO16ED15O), Feature 3 (WL-0MMJO2OAH1Q20TJ3).
Deliverables: Updated
src/tui/controller.ts(feedback handling in delegate callback); browser-open utility (new or reuse existing); unit tests for feedback and error handling.Key Files:
src/tui/controller.ts,src/commands/github.ts.Feature 5: Delegate TUI integration tests and docs (WL-0MMJO3LBG0NGIBQV)
Summary: Add integration tests for the full TUI delegate flow (mocked GitHub) and update TUI.md / CLI.md documentation.
Acceptance Criteria:
gkey -> modal -> confirm -> delegate helper called with correct args -> item state updated.gkey with do-not-delegate tag -> Force unchecked -> blocked; Force checked -> proceeds.github-upsert-preservation.test.ts).gshortcut documented in 'Work Item Actions' section with description 'Delegate to Copilot'.wl github delegatein the delegate section.Minimal Implementation:
tests/tui/delegate-shortcut.test.ts(or extend existing TUI test structure).assignGithubIssueAsyncandupsertIssuesFromWorkItems(same patterns asdelegate-guard-rails.test.ts).Dependencies: Feature 1 (WL-0MMJO1ZHO16ED15O), Feature 2 (WL-0MMJF6COK05XSLFX), Feature 3 (WL-0MMJO2OAH1Q20TJ3), Feature 4 (WL-0MMJO338Z167IJ6T).
Deliverables: New
tests/tui/delegate-shortcut.test.ts; updated TUI.md; updated CLI.md.Key Files:
tests/cli/delegate-guard-rails.test.ts,tests/integration/github-upsert-preservation.test.ts,TUI.md,CLI.md.Related work
src/commands/github.ts— Contains thewl github delegateimplementation (push, assign, local state update). Use as the behavioral reference for the TUI flow.src/tui/controller.ts— Current TUI controller; contains existing do-not-delegate toggle and example keybinding wiring.src/tui/constants.ts— TUI keybindings list (addgentry here).src/commands/update.ts— CLI flag--do-not-delegatesupport; relevant for guard-rail parity.tests/cli/delegate-guard-rails.test.ts— Unit tests for delegate guard-rails; useful for test patterns and mocks.tests/integration/github-upsert-preservation.test.ts— Integration test verifying delegate/upsert preserves unrelated items; reuse assertions.Potentially related work items
@copilot).do-not-delegate(already implemented asD, TUI parity exists).Notes / Implementation hints
src/tui/controller.ts(e.g., showToast) for immediate feedback.open/child_processpatterns while keeping it optional (configurable) for headless environments.tests/cli/delegate-guard-rails.test.tsstructure for TUI flows; mock GH assignment to avoid external calls.Risks & assumptions
ghavailability may fail; UI must surface errors and not update local state on failure.Final summary headline:
Add TUI shortcut
gto delegate focused work item to GitHub Copilot with confirmation and Force override; update local state, show toast and open created issue.