Skip to content

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

@SorraTheOrc

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

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions