Replace 30+ repetitive setter method bodies with four generic private
helpers: setGlobalSetting, setScopedSetting, setNestedGlobalSetting,
and setProjectSetting. Each setter method retains its original public
signature and behavior — only the internal implementation is consolidated.
Methods with custom logic (setEditorPaddingX, setAutocompleteMaxVisible,
setSearchExcludeDirs, setFallbackChain, removeFallbackChain,
setDefaultModelAndProvider) are left unchanged or minimally adapted.
Net reduction: 79 lines (164 deleted, 85 added).
- Delete theme-schema.json (335 lines): redundant with the TypeBox
schema already defined in theme.ts, only referenced via $schema URLs
in the JSON files for editor autocomplete.
- Delete dark.json (85 lines) and light.json (84 lines): move built-in
theme definitions into themes.ts as TypeScript objects, eliminating
runtime filesystem reads and the getThemesDir() dependency.
- Export ThemeJson type from theme.ts so themes.ts can reference it.
- Net reduction: ~319 lines removed.
Move slash command dispatch logic and 12 individual command handlers
(/export, /share, /copy, /name, /session, /changelog, /hotkeys,
/compact, /thinking, /edit-mode, /arminsayshi, plus showThinkingSelector)
into a new slash-command-handlers.ts module.
InteractiveMode now delegates to dispatchSlashCommand() via a
SlashCommandContext interface, keeping the integration surface minimal.
Handlers that are also invoked from keybindings/events remain on
InteractiveMode and are accessed through the context.
Reduces interactive-mode.ts from 4,783 to 4,272 lines (-511).
Extract two self-contained subsystems from agent-session.ts (3,367 -> 2,737 lines):
- RetryHandler: auto-retry with exponential backoff, credential rotation,
and cross-provider fallback logic
- CompactionOrchestrator: manual/auto compaction, overflow recovery, and
extension integration for custom compaction providers
Also add shared getErrorMessage() utility to replace repeated
`err instanceof Error ? err.message : String(err)` patterns.
The extracted modules receive AgentSession state via dependency injection
interfaces, avoiding state duplication. AgentSession remains the coordinator
that delegates to these modules.
Extract the duplicated commandContextActions implementations from
rpc-mode.ts and print-mode.ts into a shared
createDefaultCommandContextActions() factory in
modes/shared/command-context-actions.ts.
Both headless modes (RPC and print) had identical implementations of
waitForIdle, newSession, fork, navigateTree, switchSession, and reload
that simply delegate to AgentSession. The shared factory provides these
defaults; interactive mode continues to layer TUI-specific behavior on
top via its own overrides.
Also fixes a subtle redundancy in print mode where newSession manually
called options.setup after session.newSession(), even though
session.newSession() already handles the setup callback internally.
Extract the duplicated file lock mechanism from auth-storage.ts and
session-manager.ts into a shared lock-utils.ts module.
- acquireLockSyncWithRetry(): throwing variant (used by auth-storage)
- tryAcquireLockSync(): non-throwing variant (used by session-manager)
- acquireLockAsync(): async lock with retries and staleness detection
Removes ~55 lines of duplicated retry-loop logic. The shared module
also provides a foundation for deduplicating identical patterns in
settings-manager.ts and models-json-writer.ts.
- Replace dedupePrompts() and dedupeThemes() with generic dedupeResources<T>()
that accepts getName/getPath/resourceType callbacks
- Replace discoverSystemPromptFile() and discoverAppendSystemPromptFile() with
generic discoverFileInSearchPaths(filename)
- Import ResourceCollision type for use in dedupeResources signature
- Net reduction of 24 lines (868 → 844) with elimination of duplicated logic
Move duplicated patterns from compaction.ts and branch-summarization.ts
into shared utilities in utils.ts:
- getMessageFromEntry(): unified entry-to-message conversion with
optional toolResult skipping for branch summarization
- collectMessages(): replaces three identical for-loops that collect
AgentMessages from entry ranges
- extractTextContent(): replaces five instances of the
.filter(text).map(text).join() pattern
- createSummarizationMessage(): replaces three identical user-message
construction blocks for LLM summarization calls
Net reduction of ~90 lines of duplication.
- toPosixPath: remove private copies in skills.ts and package-manager.ts,
import from canonical utils/path-display.ts
- ZERO_USAGE: export from agent-loop.ts, replace inline zero-usage
objects in agent.ts and proxy.ts
- shortenPath: extract to shared modes/interactive/utils/shorten-path.ts,
import in tool-execution.ts and session-selector.ts
Extract the duplicated loop pattern (create context, iterate extensions,
iterate handlers, try/catch, emitError) into a private invokeHandlers()
helper. Each emit method is now a thin wrapper that delegates the
iteration to invokeHandlers and provides only its result-processing
callback. Net reduction of ~124 lines with identical runtime behavior.
Replace 7 individual ToolResultEvent type guards (isBashToolResult,
isReadToolResult, etc.) with a unified isToolResultEventType() function,
mirroring the existing isToolCallEventType() pattern.
Inline 14 handler type aliases (SendMessageHandler, SetModelHandler, etc.)
directly into the ExtensionActions interface since they were only used there
and added no semantic value.
Update documentation examples to use the new unified guard.
getSessionStats() calculated cost by summing usage from assistant messages
in state.messages. After auto-compaction, pre-compaction messages are
replaced by a compactionSummary with no usage field — dropping the cost.
Fix: Added cumulative accumulators (_cumulativeCost, _cumulativeInputTokens,
_cumulativeOutputTokens, _cumulativeToolCalls) that are incremented on
every assistant message event, independent of the message array.
getSessionStats() now returns max(array-sum, cumulative) to ensure
monotonically non-decreasing values.
Fixes#1423
* fix: sync worktree completion artifacts back to external state before merge (#1412)
When a worktree's .gsd/ was a real directory (not symlinked to external
state), milestone completion artifacts (SUMMARY, VALIDATION, updated
ROADMAP) were written locally but never synced back. The project root's
deriveState() read from external state and found no SUMMARY — reporting
the milestone as incomplete.
Changes:
- auto-worktree.ts: Added syncWorktreeStateBack() that copies milestone
and slice .md files from worktree .gsd/ to the main external state dir
- auto.ts: Call syncWorktreeStateBack() in tryMergeMilestone before the
git merge, ensuring artifacts are visible from the project root
Fixes#1412
* fix: emit agent_end after abort during tool execution (#1414)
When a user aborts a turn while a tool call is running, the abort RPC
succeeds but agent_end was never emitted. RPC consumers tracking turn
lifecycle via events got stuck in a 'streaming' state permanently.
Fix: After abort() + waitForIdle(), emit a synthetic agent_end if the
agent is no longer streaming. This ensures consumers always see the
turn-complete signal regardless of how the turn ended.
Fixes#1414
Adds a new bundled extension that proactively checks and refreshes AWS
credentials for Bedrock model users.
Startup (session_start):
- Runs 'aws sts get-caller-identity' with the profile extracted from
the configured awsAuthRefresh command
- If credentials are expired, runs the refresh command (e.g. aws sso login)
before the user sends their first prompt
- Shows 'AWS Bedrock login confirmed ✓' when credentials are valid
Mid-session (before_provider_request):
- Re-verifies credentials every 15 minutes before Bedrock API calls
- Catches credential expiry during long sessions without needing retry logic
Zero changes to base files — the entire feature is a single extension file.
Only activates when awsAuthRefresh is set in settings.json and the current
model uses bedrock-converse-stream.
On Windows, executables like npx, tsc, and typescript-language-server
are .cmd batch scripts. Node.js's spawn() can't find them without
shell: true because it looks for exact binary names, not .cmd wrappers.
This caused ENOENT crashes during auto-mode when the LSP tried to
spawn npx tsc --noEmit for TypeScript diagnostics.
Added shell: true conditional on process.platform === 'win32' in the
LSP client's spawn call. Unix platforms are unaffected.
Fixes#1222
* refactor: replace MCPorter CLI with native MCP client using @modelcontextprotocol/sdk
MCPorter is a third-party global CLI that fails to install on many systems,
producing error noise on every startup. Replace it with a native extension
that uses the already-bundled @modelcontextprotocol/sdk Client class directly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* docs: update README extension table from MCPorter to MCP Client
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: add .js suffix to MCP SDK subpath imports for NodeNext resolution
The SDK wildcard export (./*) requires .js suffix for TypeScript NodeNext
module resolution. Also add .js-suffixed virtual module keys so jiti
resolves them correctly in compiled Bun binaries.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implements the directory blacklist feature from #660 (incomplete items 3-4).
Users can now configure directories to exclude from the @ file picker
and fuzzy search via settings.json:
{ "searchExcludeDirs": ["node_modules", ".git", "dist", "build"] }
Changes:
- settings-manager.ts: added searchExcludeDirs setting with get/set
- autocomplete.ts (pi-tui): CombinedAutocompleteProvider accepts
excludeDirs option, filters excluded directory names in both
readdir-based and native fuzzy search paths
- interactive-mode.ts: passes searchExcludeDirs to the provider
The native fd fuzzy search already respects .gitignore. This setting
covers directories that aren't gitignored but shouldn't appear in
autocomplete (e.g., large vendor dirs, build outputs in projects
without comprehensive .gitignore).
Fixes#1190
When buildSystemPrompt() receives a customPrompt (as GSD's contract
provides), it returned early without appending promptGuidelines from
extension-registered tools. The tool definitions still reached the
API's tools parameter, but without prompt guidance the model didn't
know when to prefer them — causing subagent tools to be silently
ignored in favor of async_bash/bg_shell.
Added promptGuidelines append after date/time in the customPrompt
path, matching the behavior of the non-custom path.
Fixes#1184
Node.js's cpSync fails on Windows when the path contains non-ASCII
characters (e.g. C:\Users\Görloff) due to the \\?\ extended-length path
prefix not handling Unicode correctly. This affects both the build
script (copy-assets.cjs) and the runtime resource sync (resource-loader.ts).
Added a try/catch fallback: when cpSync throws, fall back to a manual
recursive copy using copyFileSync which handles non-ASCII paths correctly.
Changed files:
- src/resource-loader.ts: syncResourceDir() catches cpSync failure and
falls back to copyDirRecursive()
- packages/pi-coding-agent/scripts/copy-assets.cjs: all cpSync calls
wrapped in safeCpSync() with the same fallback
Fixes#1178
* Initial plan
* fix: add text-based fallbacks for RPC mode where TUI widgets produce empty turns
- rpc-mode.ts: Emit placeholder widget event instead of silently dropping factory-based setWidget calls
- commands.ts: handleStatus() falls back to text-based status summary when custom() returns undefined
- commands.ts: handleVisualize() notifies that TUI is required when custom() returns undefined
- auto-dashboard.ts: updateProgressWidget() emits string-array fallback before factory widget
- queue-reorder-ui.ts: showQueueReorder() notifies with current order when custom() returns undefined
- index.ts: Dashboard shortcut handler falls back to text status in RPC mode
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>
---------
Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: glittercowboy <186001655+glittercowboy@users.noreply.github.com>