Skip to content

Merge dev → main (automated)#2008

Merged
SorraTheOrc merged 252 commits into
mainfrom
release/dev-to-main-20260707201132
Jul 7, 2026
Merged

Merge dev → main (automated)#2008
SorraTheOrc merged 252 commits into
mainfrom
release/dev-to-main-20260707201132

Conversation

@SorraTheOrc

Copy link
Copy Markdown
Member

Automated release created by ship skill.

…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)
Sorra the Orc 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.
@SorraTheOrc
SorraTheOrc merged commit 19a03b0 into main Jul 7, 2026
@SorraTheOrc
SorraTheOrc deleted the release/dev-to-main-20260707201132 branch July 7, 2026 20:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant