Skip to content

Add a TUI shortcut to delegate a work item to Github Copilot #902

Description

@SorraTheOrc

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.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions