Merge dev → main (automated)#2008
Merged
Merged
Conversation
…complete stages The work item acceptance criteria had the colour tokens reversed. After investigating the Pi TUI theme (dark.json), the correct mapping is: - intake_complete → mdLink (#81a2be, blue-like) - matches blessed TUI blue-fg - plan_complete → accent (#8abeb7, cyan-like) - matches blessed TUI cyan-fg Updated tests to verify the corrected colour mapping.
The selection widget was receiving plain text strings without colours because the theme was not available when setWidget was called. Changed buildSelectionWidget to return a factory function that captures the theme and applies colours when the TUI calls it with (tui, theme). This ensures the title line is coloured correctly using the Pi TUI theme system regardless of when setWidget is called.
Added console.log to debug whether the factory function is being called and if the theme is available. Also added placement: 'belowEditor' option to match the pattern used by other widgets.
- Remove unused stageColourToken import - Remove duplicate truncateLine function (use truncateToWidth which handles ANSI) - Use PiTheme type from worklog-helpers instead of inline type - Merge formatBrowseOption and formatBrowseOptionColoured into single function - Update tests to work with factory function pattern
… commands - Delete packages/tui/extensions/worklog-widgets.ts entirely (commands, keyboard shortcuts Ctrl+1..9, Ctrl+Up/Down, and persistent widgets worklog.list/worklog.details are all removed) - Update src/commands/piman.ts to stop referencing worklog-widgets.ts - Update docs/ux/design-checklist.md to remove references to removed commands and shortcuts, updating Section 2 (Keyboard Navigation) and Section 6 (Widget System) - Keep packages/tui/extensions/worklog-helpers.ts and index.ts unchanged - Keep packages/tui/tests/worklog-widgets.test.ts unchanged (imports directly from worklog-helpers.ts) - Full test suite passes (174 files, 1874 tests)
Define emoji/fallback icons for work item priority (critical, high, medium, low) and status (open, in-progress, completed, blocked, deleted, input_needed). The specification covers: - Icon selection (Unicode emoji) with text fallbacks for copy/paste - Accessible labels for screen readers (, ) - Env-var and flag mechanisms to disable icons (, ) - TTY auto-detection vs explicit override - Implementation guide for TUI list, detail pane, CLI output, and tests
…rows Create src/icons.ts with emoji/fallback icons for priority and status: - Priority: critical (🔴), high (🟠), medium (🔵), low (⚪) - Status: open (🟢), in-progress (🔄), completed (✅), blocked (⛔), deleted (🗑️), input_needed (❓) - Accessible labels via priorityLabel()/statusLabel() functions - Text fallbacks via priorityFallback()/statusFallback() functions - iconsEnabled() with WL_NO_ICONS env var support Add icons to TUI list rendering (renderListAndDetail in controller.ts): - Priority and status icons appear before the title in list lines - Blessed color tags matching theme priority/status colors - Graceful fallback when noIcons option is active 58 unit tests added for icons module (tests/unit/icons.test.ts)
Updated src/tui/components/metadata-pane.ts: - Added priority and status icons with blessed color tags to metadata pane - Icons appear before status and priority values - Text fallback used when WL_NO_ICONS=1 Updated src/commands/helpers.ts: - Added status/priority icon formatting for CLI output (humanFormatWorkItem) - CLI output shows both icon and text fallback (e.g., 'Status: 🟢 Open [OPEN]') - TUI output remains icon-free in detail-pane format per metadata pane location - Icons for summary, concise, normal, and full formats Updated test snapshots to reflect new icon-containing output.
Updated src/cli-types.ts: - Added noIcons?: boolean to ListOptions and ShowOptions interfaces Updated src/commands/list.ts: - Added --no-icons option to disable icon rendering for scripting/copy-paste - Sets WL_NO_ICONS=1 env var when flag is provided Updated src/commands/show.ts: - Added --no-icons option to disable icon rendering for scripting/copy-paste - Sets WL_NO_ICONS=1 env var when flag is provided Icons in CLI output now show both emoji and text fallback: - Example: 'Status: 🟢 Open [OPEN]' instead of just 'Status: Open' - Text fallback ensures copy/paste and script parsing works well
Updated docs/icons-design.md: - Added implementation status and links to commits - Added implementation summary section with file list - Added CLI usage examples and output examples Updated CLI.md: - Added --no-icons option documentation to list command - Added --no-icons option documentation to show command The --no-icons flag disables emoji icons for scripting/copy-paste. Icons show both emoji and text fallback in CLI output (e.g., 'Status: 🟢 Open [OPEN]').
Updated priority icons to be more visually meaningful: - critical: 🚨 (rotating light) instead of 🔴 - high: ⭐ (star) instead of 🟠 - medium: 📋 (clipboard) instead of 🔵 - low: 🐢 (turtle) instead of ⚪ Updated tests and snapshots to reflect the new icons. Updated design documentation with visual meaning descriptions.
Updated icons-design.md to reflect the new priority icons: - critical: 🚨 (rotating light) - high: ⭐ (star) - medium: 📋 (clipboard) - low: 🐢 (turtle)
The piman extension's extractJsonObject function used a naive brace counter
that didn't account for escaped braces inside JSON string values (e.g.,
regex patterns like /\{[^}]*\}/g in work item descriptions).
This caused 'wl piman' to fail with:
Error: Failed to browse work items: Expected ',' or ']' after array element
The fix:
1. First tries to parse the full output as valid JSON (handles escapes correctly)
2. Falls back to manual extraction with proper string boundary tracking
This is a pre-existing bug in the extension, not caused by icon changes.
…wn view When wl piman calls wl show --format markdown, it now receives icon emojis which cause render errors because truncateToWidth doesn't account for multi-byte terminal width (emojis take 2 columns each). Add --no-icons flag to the wl show call to prevent icons in the detail view. Also update tests to expect the --no-icons flag.
When noIcons is true, statusIcon/priorityIcon return the fallback text (e.g. '[OPEN]'). The formatStatusWithIcon/formatPriorityWithIcon functions were then appending label+fallback again, resulting in duplicate text like '[OPEN] Open [OPEN]'. Fix: Check if icon equals fallback, and if so, just show label+fallback instead of icon+label+fallback.
Commander.js converts --no-icons to property, not . Updated type definitions and logic to use instead of . Also fix --no-icons mode in helpers.ts to avoid duplicate fallback text when icons are disabled (icon already returns fallback text like '[OPEN]').
Update truncate() and truncateToWidth() to correctly count emoji characters as 2 terminal columns (they are double-width in most terminals), preventing render errors when icons are displayed in TUI output. Also add --no-icons flag to wl piman extension's markdown detail view to prevent emoji overflow in the scrollable widget.
Include additional emoji ranges that take 2 terminal columns: - 1F300–1F9FF (Misc Symbols and Pictographs) - 2600–27BF (Misc symbols including ⭐ star)
added 28 commits
July 1, 2026 18:55
Extends recovery.ts with terminal error handling: - TERMINAL_CATEGORIES constant identifying authError, quotaExhausted, terminated as non-retryable categories - getTerminalErrorTitle for user-friendly category names - executeCheckpointAndTerminate: saves checkpoint, displays informative error, does NOT attempt retry - checkpoint-terminate.test.ts: 13 tests covering all 3 terminal categories, checkpoint failure, exception handling, and no-retry verification
- retry-command.ts: /retry command with status/reset/manual-trigger subcommands, shared retry state, diagnostics formatting. - retry-command.test.ts: 30 tests covering status display, reset, manual trigger with various error categories, edge cases. - integration.test.ts: 21 tests covering end-to-end classification → dispatch mapping, agent_end flows, and false positive prevention.
- register-recovery.ts: Central recovery module registration with agent_end error dispatch, turn_end state management, session_start reset, built-in retry suppression (monkey-patch _prepareRetry), and /retry command registration. - index.ts: Import and call registerRecoveryModule during extension init.
- Remove pi-retry from ~/.pi/agent/settings.json packages array - Add recovery module documentation to extensions README.md - Update code references from pi-retry to built-in recovery module
The agent_end handler was not recording errors in retryStates for terminal categories (rateLimit, authError, quotaExhausted, terminated), so /retry status showed zero attempts even after errors occurred. Now all categories record the error in retry state for diagnostics.
The agent_end handler for SERVER_ERROR/TIMEOUT now actually fires triggerInvisibleContinue() — a retry loop that: - Captures the Agent instance via monkey-patched subscribe - Waits for agent idle, removes error from state - Sleeps with exponential backoff (polls abort/session-switch every 100ms) - Calls agent.prompt([]) for invisible retry (same session ID & context) - Loops until success, user abort (ESC), or session switch (/new) - On success, resets retry state to zero Also: - Capture notify function from handler ctx for retry-loop notifications - turn_end handler skips state reset when retry loop is in progress
…ls for terminal categories - /retry status now shows 'Will retry: true/false' for each category based on default recovery config (can be overridden in settings). - When Will retry is false, the 'Current attempt' and 'Is retrying' lines are omitted since they're irrelevant for non-retried categories. - This makes it immediately clear which categories will be retried vs which will checkpoint-and-terminate.
- Added priority field to GroupableItem interface in helpers.ts - Modified assignItemGroups() in grouping.ts to extract critical items into a single 'Critical' group at the top, before Other/Plan Complete/In Review/ Intake Complete/Idea groups - Critical items are excluded from their stage-based groups - All critical items merge into one group regardless of file-path conflicts - No Critical group heading is shown when no critical items exist - Updated next.ts to pass item priority into GroupableItem objects - Added 7 comprehensive test cases covering critical group behavior - Updated CLI.md to document the new Critical group display
…ation Implements worklog shortcuts for use while the editor is active: - New editor-shortcuts.ts module with CurrentItemTracker and ShortcutModeManager classes - CurrentItemTracker tracks the most recently interacted-with work item ID (from browse selection or detected in typed commands) - ShortcutModeManager implements a state machine (inactive/active/ chord_pending) that intercepts terminal input via Pi's onTerminalInput() API - Ctrl+Shift+W toggles shortcut mode when a current work item is known - Single-key shortcuts (i=implement, a=audit, p=plan, n=intake, etc.) dispatch with <id> replaced by the current work item ID - Chord shortcuts (u→p, x→c, etc.) work with two-key sequences - Escape or leader key toggle exits shortcut mode - Footer shows '🔧 WL' indicator when shortcut mode is active - Reserved navigation keys (g, G, space) pass through - Updated browse.ts with onItemSelected callback for item tracking - Updated index.ts to wire up editor shortcut mode and current item tracking from user input events - Tests: 34 test cases covering all state transitions and edge cases
…nts to detail view
Remove the editor-shortcuts.ts module and its test file (leader key
mechanism, CurrentItemTracker, ShortcutModeManager) which was based
on the incorrect assumption that the Pi editor is active when viewing
a work item's detail view. The detail view is a custom overlay where
shortcuts already work via the ShortcutRegistry.
Changes:
- Delete editor-shortcuts.ts and editor-shortcuts.test.ts
- Remove all editor shortcut mode wiring from index.ts (imports,
CurrentItemTracker instance, ShortcutModeManager instance,
registerEditorShortcutMode call, pi.on('input') handler)
- Remove onItemSelected from BrowseFlowOptions and its call in
runBrowseFlow (browse.ts)
- Add shortcut hint line to the detail view overlay (browse.ts),
matching the same formatting pattern as the selection list hints:
shows available 'detail' and 'both' shortcuts filtered by stage,
respects showHelpText setting, shows chord follower hints when
a chord leader is pending
…for children navigation Implements behavior change in the TUI browse list: - Pressing Enter on a parent work item now opens its detail view (previously navigated into children, making parent details inaccessible) - Pressing Ctrl+Enter on a parent item navigates into its children (replaces the old Enter-on-parent behavior) - Items without children continue to work as before (Enter → detail view) - The '..' back-navigation entry behavior is unchanged - Help text now shows 'Ctrl+Enter:children' hint when a parent item is selected Key files: - lib/shortcuts.ts: Added isCtrlEnterKey() helper using Pi's matchesKey() with Kitty protocol and raw ANSI fallbacks - lib/browse.ts: Changed Enter handler to distinguish Enter (detail view) from Ctrl+Enter (children navigation); added help text hint - Tests updated for all new Enter/Ctrl+Enter behavior Acceptance criteria: 1. ✓ Enter on parent opens detail view 2. ✓ Ctrl+Enter on parent navigates into children 3. ✓ Items without children continue with Enter → detail view 4. ✓ '..' back-navigation unchanged 5. ✓ Help text shows Ctrl+Enter hint for parent items 6. ✓ All existing tests updated and passing 7. ✓ Documentation/code comments updated
…hildren This addresses the issue where Ctrl+Enter navigation to children doesn't work in terminals that don't support Kitty protocol or modifyOtherKeys (where Ctrl+Enter sends the same byte as plain Enter). Changes: - Add isShiftEnterKey() helper function to shortcuts.ts - Update browse.ts to accept Shift+Enter alongside Ctrl+Enter for children navigation - Update help text to show 'Ctrl+Enter/Shift+Enter:children' - Add isShiftEnterKey unit tests - Add Shift+Enter navigation tests in browse-hierarchical-navigation.test.ts Fixes: WL-0MR4TNAP1006ATTL Related: WL-0MQZWK5AF004UR14
Tab works in tmux because tmux passes through the Tab character unchanged, unlike modifier key combinations (Ctrl+Enter, Shift+Enter) which tmux often strips modifier information from. Changes: - Add isTabKey() helper to shortcuts.ts - Update browse.ts to accept Tab, Ctrl+Enter, and Shift+Enter for children navigation - Update help text to show 'Tab/Ctrl+Enter/Shift+Enter:children' - Add Tab navigation and isTabKey tests Fixes: WL-0MR4TNAP1006ATTL
…ction with robust regex patterns The original isCtrlEnterKey() and isShiftEnterKey() fallbacks only matched exact escape sequences, missing many valid CSI-u variants (alternate keys, event types, empty shifted key). Replace exact string matching with regex that handles all CSI-u protocol variants for codepoint 13 (enter key): Basic: \x1b[13;5u Alt keys: \x1b[13:13;5u, \x1b[13:13:13;5u, \x1b[13::13;5u Event: \x1b[13;5:1u, \x1b[13;5:2u, \x1b[13;5:3u Same approach for Shift+Enter (modifier 2) and Ctrl+Enter (modifier 5). Also fix a duplicate test name in browse-hierarchical-navigation.test.ts.
Modify moveSelection in browse.ts to wrap selection index at boundaries instead of clamping: Up at first item goes to last, Down at last item goes to first. Modeled after the existing wrapping pattern in actionPalette.ts. Changes: - Replace boundary guard (nextIndex < 0 || nextIndex >= items.length) with wrap logic: nextIndex < 0 -> items.length-1, nextIndex >= items.length -> 0 - Add early return for empty list (items.length === 0) to prevent crash - Keep existing nextIndex === selectedIndex guard for single-item lists - Add 7 tests covering wrap-up, wrap-down, normal movement unaffected, empty list safety, child-level wrap, and single-item list wrap - All 34 browse-hierarchical-navigation tests pass
…trl+Enter/Shift+Enter Root cause: _matchesKey from pi-tui is undefined at runtime, so the fallback detection logic handles all key input. In most terminals without Kitty keyboard protocol, Ctrl+Enter and Shift+Enter both send the same byte as plain Enter (\r, 0x0D), making them indistinguishable. The fallback isEnterKey(\r) returns true while isCtrlEnterKey(\r) and isShiftEnterKey(\r) return false, so the code always falls through to opening the detail view instead of navigating to children. Changes: - browse.ts: Remove Ctrl+Enter/Shift+Enter from children navigation logic; only Tab navigates to children. Update help text to show 'Tab:children'. - browse-hierarchical-navigation.test.ts: Replace Ctrl+Enter (\\u001b[13;5u) and Shift+Enter (\\u001b[13;2u) escape sequences with \\t (Tab) in all navigation tests. Update test names/comments. - browse-auto-refresh.test.ts: Same escape sequence replacement. - Remove debug logging.
Tab in the detail view now navigates to the selected item's children, closing the detail view and opening the child items in the browse list. Implementation: - detailTabNavigationParentId flag tracks Tab press in detail view - Detail view's handleInput: Tab on parent item sets flag and closes - Detail view's render: shows 'Tab:children' hint on parent items - Browse loop: after detail view closes, checks flag and fetches children, setting up selectionState for seamless navigation (navStack is preserved for back-navigation via Escape)
…ENGTH handler The CONTEXT_LENGTH case in the agent_end handler was only showing a notification but never actually calling triggerInvisibleContinue() to resume the agent after a max-tokens stop. The existing triggerInvisibleContinue() flow handles: 1. Wait for agent to become idle 2. Remove the error assistant message from agent state 3. Call agent.prompt([]) to resume the agent loop invisibly 4. If the continuation also hits max tokens, the next agent_end triggers another continuation (loop continues since 'length' is not 'error') File: packages/tui/extensions/Worklog/lib/recovery/register-recovery.ts
…ble timeout error Add regex pattern /stream\s+ended\s+without\s+finish/i to DEFAULT_TIMEOUT_PATTERNS so that 'Stream ended without finish_reason' (and similar variants) from dropped SSE streams is classified as ErrorCategory.TIMEOUT instead of UNKNOWN, triggering automatic retry with exponential backoff. - Add pattern with comment explaining its purpose - Add 5 test assertions: isTimeout matches exact and variant forms, classifyError returns TIMEOUT for the exact error message - All 191 recovery module tests pass
- Add TestGetClosingSentence test class covering: - Ready-to-close returns ready sentence - Not-ready-to-close returns not-ready sentence - Closing sentence excluded from report body - Parses correctly with FailureNotice-wrapped reports - Defaults to not-ready when no verdict line found - Project-level report yields not-ready sentence
Root cause: Two infinite loop patterns in runBrowseFlow's while(true) loop
when using mocked custom() and chooseWorkItem mocks:
1. Inline custom() mocks returning null caused runBrowseFlow to loop back to
the selection list endlessly (mocked chooseWorkItem always returns item).
2. makeCustomMock returning the component caused the same pattern -
runBrowseFlow expects the custom() result to be a ShortcutResult to exit,
but the component object did not match, so the loop continued.
Fix:
- All detail-view custom() mocks now return { type: 'shortcut', ... } instead
of null, causing runBrowseFlow to exit the loop on the first iteration.
- makeCustomMock also returns a shortcut result, with the component captured
in componentRef for test access.
- Updated test assertions to account for help text (shortcut hints) added
to scrollable widget render output.
- Added additional node:fs mock functions (existsSync, statSync, lstatSync,
readdirSync, accessSync, watch, promises, constants) that are accessed
during module loading and test execution.
… config Child 2 (WL-0MR89UMBD0087CKR): Mock icons.js import in test - Added vi.mock for dist/icons.js before the imports in the test file - Mock provides stub implementations for all 8 exported functions: priorityIcon, statusIcon, stageIcon, auditIcon, epicIcon, iconsEnabled, riskIcon, effortIcon - Prevents uncontrolled module resolution during test import Child 3 (WL-0MR89V33L002JBCI): Add vitest memory guard config - Added pool: 'threads' with maxWorkers: 4 to prevent unbounded memory growth during concurrent test execution - Added worktree test files to exclude patterns to prevent vitest from picking up duplicate copies from .worklog/worktrees/
…ling description text - Split the single bullet regex into a two-step approach: first try backtick-wrapped path (allow text after closing backtick), then fall back to extracting the first word as a plain path candidate. - Previous regex required the line to end after the closing backtick (due to $ anchor), so lines like were silently skipped. - Updated docs/FILE_PATH_CONVENTION.md with new rule 4 documenting that trailing description text after backtick paths is allowed. - Added 3 test cases: backtick-with-trailing-text, ## Key Files heading, and combined heading + trailing text.
Root cause: pool: 'threads' uses worker_threads which do not support process.chdir(). Many tests in the suite use process.chdir() for temp directory setup/teardown, causing 51 test files (445 tests) to fail. Fix: Change pool: 'threads' to pool: 'forks' (child_process), which supports process.chdir() while preserving the maxWorkers: 4 memory guardrail and the worktree exclusion pattern. Resolves AC #4 blocking issue for WL-0MR7NPSFK001ZTDD.
… README Adds documentation for: - Vitest pool configuration (pool: 'forks', maxWorkers: 4) - Comprehensive node:fs mock pattern for browse extension tests - icons.js mock pattern for uncontrolled module resolution - Infinite loop guard pattern for runBrowseFlow mocks Resolves AC #5 for WL-0MR7NPSFK001ZTDD.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automated release created by ship skill.