Pass args directly to /review to append them as additional instructions
to the generated prompt for all structured review modes (PR-style,
uncommitted, specific commit). When inline args are present, option 4
(custom editor) is suppressed from the menu. The no-UI (Task tool) path
forwards args as a focus hint.
Adds appendInstructions() helper to centralize the prompt append format.
- Added `/force` slash command and `ToolChoiceQueue` system for managing tool-choice directives with lifecycle callbacks and requeue semantics.
- Added `setForcedToolChoice()`, `peekQueueInvoker()`, `buildToolChoice()`, `steer()`, and `getToolChoiceQueue()` methods to AgentSession and ToolSession APIs.
- Refactored tool-choice override mechanism from simple override to queue-based system with generator directives, lifecycle callbacks, and requeue preservation.
- Removed `PendingActionStore` class and replaced with `ToolChoiceQueue`; updated `ResolveTool` and custom tool loader to use queue invokers.
- Fixed tool-choice queue cleanup on agent loop abort and requeue semantics to preserve callbacks across abort cycles.
- Extracted `structuredCloneJSON()` utility to centralized package for consistent deep cloning with JSON fallback.
- Consolidated duplicate cloning logic across openai-responses, openai-responses-shared, and validation modules.
- Added resilience to message cloning in extension runner with try-catch fallback for non-cloneable objects.
- Optimized context emission in extension runner by skipping message cloning when no handlers exist.
- Added optional `contentIndex` field to AssistantMessageEvent variants for type consistency.
- Remove pi-ref.ts deferred barrel import (no longer needed after circular dep fix)
- Update extension/hook/custom-tool/custom-command loaders to use direct imports
- Fix warmPythonEnvironment mock return type in python tool tests (add docs field)
- Added multi-session support to ACP mode enabling session forking, resumption, and closure operations.
- Added session model state reporting and unstable_setSessionModel RPC command for direct model configuration.
- Added turn-level usage tracking and stable message ID tracking for assistant chunks in ACP responses.
- Added Settings.cloneForCwd() method and ExtensionRunner.getFlagValues() for configuration isolation across sessions.
- Fixed ACP session cleanup to properly cancel in-flight prompts and dispose resources on disconnect.
- Refactored AcpAgent to manage multiple concurrent sessions with per-session lifecycle instead of single session.
- Extracted prompt rendering and formatting utilities from coding-agent to centralized pi-utils package with new API surface (prompt.render, prompt.format, prompt.registerHelper).
- Migrated parseFrontmatter utility from coding-agent to pi-utils package; updated 8 files to import from @oh-my-pi/pi-utils.
- Removed 170-line prompt-format.ts module and consolidated 192 lines of Handlebars helper registrations into pi-utils prompt module.
- Updated 60+ files across coding-agent and typescript-edit-benchmark to use new prompt.render() and prompt.format() API from pi-utils.
- Simplified prompt-templates.ts by delegating core functionality to pi-utils while retaining custom helper registrations (jtdToTypeScript, jsonStringify, etc.).
- Replaced all Bun.which() calls with $which() utility from @oh-my-pi/pi-utils across 22 files.
- Removed findBashOnPath() wrapper function from procmgr.ts, consolidating binary path resolution.
- Updated AGENTS.md documentation to reflect new $which() API usage pattern.
- Centralized binary detection logic through shared utility, reducing code duplication.
- Reorganized edit tool from `patch/` to `edit/` directory with dedicated mode subdirectories (chunk, patch, hashline, replace).
- Replaced line-scoped edit operations with substring-based `find` parameter and added `replace_body` operation for preserving signatures.
- Added chunk focus modes (Expanded, Collapsed, Container) and focused rendering to display only touched chunks and adjacent siblings.
- Implemented notebook (ipynb) language support with virtual source conversion and cell-based chunk parsing.
- Enhanced chunk edit error messages with consistent checksum mismatch reporting and improved chunk selector auto-resolution.
- Extracted edit mode implementations into separate modules with improved helper functions and LSP integration for diagnostics.
The four extension/hook/custom-tool/custom-command loaders statically
imported the package barrel `@oh-my-pi/pi-coding-agent` to expose it as
`pi` to user code. This created a self-referential cycle:
tools/index -> task -> sdk -> custom-commands/loader
-> @oh-my-pi/pi-coding-agent (package barrel)
-> modes/components -> tool-execution -> renderers
-> tools/read (TDZ on readToolRenderer)
Triggered at startup by 'omp --help' / 'omp stats --help' depending on
ESM evaluation order, manifesting as:
ReferenceError: Cannot access 'readToolRenderer' before initialization
Introduce `extensibility/pi-ref.ts` that resolves the barrel lazily via
`require` on first call. All four loaders use `getPiRef()` instead of a
static `import * as piCodingAgent`. The barrel is fully initialized by
the time any loader function actually runs, so the lookup is safe.
- Added external editor callback support to extension UI dialogs and hook selector for opening plans in external editor.
- Added `matchesAppExternalEditor()` keybinding matcher to detect external editor shortcuts (ctrl+g) across components.
- Added `trimTrailingNewline` option to `openInEditor()` function to preserve trailing newlines when editing externally.
- Modified plan review flow to re-read plan content from disk before approval to reflect external editor changes.
- Refactored plan preview rendering to cache and reuse UI container instead of recreating children on each render.
- Refactored Promise creation in external editor utility to use `Promise.withResolvers()` for cleaner async handling.
- Extracted git operations from ControlledGit class into centralized utils/git module with 1276 lines of typed command wrappers.
- Replaced dependency injection of ControlledGit instances with direct cwd string parameters across commit agent tools and workflows.
- Migrated all git command execution from inline shell calls and custom helpers to structured git module API (diff, status, branch, worktree, patch, etc.).
- Removed ControlledGit class, operations.ts, and helper functions (findGitHeadPath, mergeStdoutStderr, joinPatch) now provided by git module.
- Exported git utilities from main package entry point for extension and plugin use.
listInstalledPlugins() was marking user-scope entries as shadowedBy:
"project" based on plugin ID presence in the project registry alone.
A disabled project copy (enabled: false) is not active in discovery, so
the user copy is the effective install and must not appear shadowed.
activeProjectIds now only includes project plugins where the first entry
does not have enabled: false.
Plugins can now be installed at user scope (global) or project scope
(per-project, higher capability priority). Scope is encoded in registry
file location, not a metadata field:
user: ~/.omp/plugins/installed_plugins.json
project: <nearest-project>/.omp/plugins/installed_plugins.json
cache: ~/.omp/plugins/cache/plugins/ (shared, path-referenced)
Project root discovery: resolveActiveProjectRegistryPath(cwd) walks up
from cwd looking for the nearest .omp/ directory, falling back to the
nearest .git root. This is the single resolver used by install, uninstall,
list, upgrade, discovery, and doctor.
Discovery: listClaudePluginRoots(home, cwd?) reads both registries when
cwd is provided. Project entries shadow user entries for the same plugin
ID. Cache key is canonical ("${home}:${resolvedProjectPath}") so nested
cwds within the same project share a cache entry.
Manager changes:
- installPlugin({ scope? }): routes registry reads/writes by scope;
checks collectReferencedPaths() across both registries before deleting
any cached plugin dir to prevent cross-scope data loss
- uninstallPlugin(id, scope?), setPluginEnabled(id, enabled, scope?),
upgradePlugin(id, scope?): throw a disambiguation error when the plugin
exists in both scopes and no scope is specified
- upgradePluginAcrossScopes(id): new; upgrades all scopes the plugin is
installed in; returns InstalledPluginEntry[]
- upgradeAllPlugins(): uses upgradePluginAcrossScopes; result includes scope
- listInstalledPlugins(): returns InstalledPluginSummary[] merged from both
registries; user entries marked shadowedBy: "project" when overridden
CLI: omp plugin install|uninstall|upgrade|enable|disable --scope user|project
Slash: /marketplace install [--scope user|project] name@marketplace
MarketplaceManager constructed with projectInstalledRegistryPath in all
CLI handlers and builtin-registry.ts via resolveActiveProjectRegistryPath.
.gitignore: .omp/plugins/ added (local runtime state, not committed).
preloadPluginRoots/clearClaudePluginRootsCache carry cwd through for LSP.
main.ts passes getProjectDir() at startup.
Tests: 226 pass across 13 files. New: project-scope.test.ts (resolver
walk-up, .git fallback, null return, canonical path, shadow precedence);
manager scope tests (registry isolation, disambiguation errors, cross-scope
cache-ref protection, upgradePluginAcrossScopes, shadowedBy marking).
fixes#581
- Added error handling for marketplace plugin selector failures with user-facing error messages.
- Fixed marketplace clone promotion timing by extracting logic into separate function and applying consistently across addMarketplace and refreshMarketplace methods.
- Added cleanup of temporary clone directories on source verification failures to prevent orphaned files.
- Improved plugin selector UI rendering by expanding onCancel callback to trigger requestRender after completion.
- Fixed LSP config path handling by replacing hardcoded filename array with variable reference.
- Enhanced error handling for unknown plugin source variants in marketplace validation.
- addMarketplace reads registry before fetching; if the fetched catalog
name collides with an existing entry, cleans up the clone and throws
before any cache corruption occurs
- installPlugin rejects relative string sources when the marketplace was
added via URL (only marketplace.json is cached, not the plugin tree)
- /reload-plugins now calls refreshSlashCommandState() so newly
installed/removed plugin commands take effect without restart
- parseMarketplaceCatalog rejects string sources that don't start
with './' at parse time instead of deferring to install-time crash
- classifyInstallTarget skips known npm dist-tags (latest, next, beta,
etc.) and semver-like strings before checking marketplace names
- updateMarketplace throws if fetched catalog name differs from the
registered name, preventing stale data on upstream renames
- parseMarketplaceCatalog validates variant-specific required fields
(github.repo, url.url, git-subdir.url+path, npm.package) at parse
time instead of deferring to install-time crashes
- parseMarketplaceCatalog rejects plugin names that fail isValidNameSegment
instead of deferring the error to install time
- cachePlugin copies to staging dir first, then renames into place;
a failed copy no longer destroys the active install
- Fix ReturnType<> usage in cloneAndReadCatalog
- Carry over enabled:false from existing entries during force reinstall
and upgrade; disabled plugins stay disabled without user intent change
- After clearing plugin roots cache, fire-and-forget preloadPluginRoots()
so sync LSP config reads stay valid for the rest of the session
- Store injected --plugin-dir roots in module-level variable; survive
cache clears by re-merging on next listClaudePluginRoots() call
- Defer old plugin deletion in force-reinstall until new cache succeeds;
transient failures no longer leave the plugin uninstalled
- Classify HTTP URLs by pathname instead of full string; query params
no longer cause .json URLs to be misclassified as git
- Honor enabled:false flag in listClaudePluginRoots (skip disabled plugins)
- Normalize local source URIs to absolute paths in addMarketplace
- Resolver returns explicit tempCloneRoot instead of path-based inference
The regex that strips SKILL.md from the path to compute baseDir only
matches forward slashes. On Windows where paths use backslashes, the
replace is a no-op and baseDir equals the full SKILL.md file path.
This breaks sub-path resolution for skills: the subpath gets appended
to the SKILL.md file path instead of the skill directory.
Fix: use character class matching both path separators.
- Added `wait_for_picker_scan()` function to search_db module with cancellation token support for polling picker scan completion.
- Integrated picker-based file search into glob matching logic with `collect_files_from_picker()` helper to reuse shared SearchDb picker results.
- Refactored fff and grep modules to use centralized `wait_for_picker_scan()` wrapper instead of direct FilePicker calls, improving cancellation handling.
- Propagated SearchDb instance through agent session initialization and input controller to enable unified file picker coordination across search operations.
- Added contract system for validating benchmark commands, metrics, scope paths, constraints, and off-limits paths.
- Contract validation enforces matching initialization parameters against autoresearch.md before init_experiment.
- Segment fingerprinting detects configuration drift and warns when metrics are not directly comparable.
- Added pending run detection and recovery to resume incomplete experiments from .autoresearch/runs/.
- Run directories organize artifacts with benchmark logs and optional checks logs for traceability.
- Extended experiment state to track run number, command, scope, off-limits, constraints, and fingerprint.
- Added `defaultInactive` property to ToolDefinition for conditional tool registration and activation control.
- Added dynamic tool activation/deactivation API `setActiveTools()` for managing experiment tools in autoresearch mode.
- Replaced single `command-start.md` workflow with separate `command-initialize.md` and `command-resume.md` prompts for autoresearch initialization and session resumption.
- Added interactive intent dialog for autoresearch optimization goals with automatic session resumption detection based on autoresearch.md presence.
- Refactored autoresearch command handler to distinguish resume vs initialize flows and dynamically activate/deactivate experiment tools based on mode and session state.
- Added autoresearch extension with autonomous experiment loop supporting init, run, and log experiment tools for metric-driven optimization.
- Added widget placement system enabling extensions to position UI components above or below the editor via ExtensionWidgetOptions.
- Added dashboard controller with interactive overlay for viewing experiment results, metrics, and progress with keyboard navigation.
- Removed auto-correction logic for off-by-one range edits in hashline editor to preserve user intent in patch operations.
- Added state reconstruction utilities to parse autoresearch.jsonl logs and rebuild experiment state across sessions.
- Added comprehensive type definitions and helper utilities for metric parsing, ASI validation, and process management.
- Added mcpServerName and mcpToolName optional properties to tool definitions for MCP server discovery and tool name tracking.
- Changed aborted tool call handling to preserve existing tool results instead of replacing with synthetic 'aborted' results at turn boundaries.
- Extracted flushPendingToolCalls() and flushPendingAbortedToolCalls() helpers to consolidate orphaned tool call handling logic.
- Updated tool result handling to use message timestamps instead of Date.now() for synthetic results consistency.
- Exposed `settings` instance in `CustomToolContext` for session-specific configuration access.
- Improved artifact spill configuration to use session settings with schema defaults as fallback.
- Refactored type annotations and removed Required wrappers for better type safety in settings handling.
- Replaced AgentTool type with Tool type in tool registry for improved type consistency.
- Added `toExtensionId()` method to all capability types for granular extension identification and disabling.
- Added `disabledExtensions` and `includeDisabled` options to LoadOptions for filtering disabled capabilities by extension ID.
- Added plugin manifest support for `extensions` entry points with automatic discovery from installed plugins.
- Fixed skill loading to properly respect disabled skill names from custom directories.
- Improved cross-platform path handling by using `path.basename()` instead of string splitting in context file and state manager.
- Refactored capability index loading to validate source metadata and filter disabled extensions before processing.
Three early-return paths in #runAutoCompaction emitted auto_compaction_end
with result=undefined, aborted=false, and no errorMessage when compaction
was legitimately not needed (no model selected, no candidate models
available, or nothing to compact yet). The event-controller had no way to
distinguish these benign skips from a genuine failure, and fell through to
show the warning:
'Auto context-full maintenance failed; continuing without maintenance'
This was visible after a successful compaction: the next threshold check
would find nothing new to compact (prepareCompaction returns null), emit a
soft-skip event, and trigger the false warning.
Add skipped?: boolean to the auto_compaction_end event type and set it on
the three soft-skip paths. Update the event-controller to treat skipped
events as silent no-ops. Propagate the field through the extension and
hook AutoCompactionEndEvent interfaces so extensions can observe the
distinction.
- Extracted OpenAI Responses API stream processing logic into shared module with 424 lines of reusable utilities.
- Consolidated text signature encoding, tool call normalization, and message conversion functions into openai-responses-shared module.
- Refactored openai-responses.ts and azure-openai-responses.ts to delegate stream processing to shared processResponsesStream() helper.
- Removed 473 lines of duplicated stream event handling and utility functions across OpenAI provider implementations.
- Added `onPayload` callback option to intercept and transform provider request payloads before transmission across agent and AI packages.
- Added structured text signature metadata with phase information to OpenAI and Azure OpenAI providers for enhanced response tracking.
- Added `before_provider_request` extension event to coding-agent for chaining payload transformations across multiple handlers.
- Improved error messages in `response.failed` events with detailed error codes, messages, and incomplete reasons from provider responses.
- Introduced Effort enum and ThinkingConfig metadata for per-model reasoning capabilities with min/max effort levels.
- Migrated thinking level API from string-based ThinkingLevel to structured Effort enum across agent and AI packages.
- Added model-thinking module with effort mapping, policy application, and semantic versioning utilities for provider-specific thinking modes.
- Removed supportsXhigh() function and replaced effort clamping with model-aware validation using ThinkingConfig metadata.
- Expanded models.json with thinking configuration objects for 50+ models including Claude, Gemini, and OpenAI variants.
- Added Python analysis scripts for edit tool usage patterns and tool invocation stream processing.
* idiomatic rust fixes
* idiomatic rust fixes
* display an image if we are fetching an image
* MIME type strictness
* codex nagging me
* codex nagging
* handoff instead of compaction as context filled strategy and surfacing
* handoff instead of compaction as context filled strategy and surfacing p2
* handoff instead of compaction as context filled strategy and surfacing p3
* handoff instead of compaction as context filled strategy and surfacing p4
* handoff instead of compaction as context filled strategy and surfacing p5
* handoff instead of compaction as context filled strategy and surfacing, fixes
* failing fetch test from the fetch tool updates
* handoff focus prompt skeleton
* handoff focus prompt skeleton p2
* fetch bugs
* further codex improvements
* further codex improvements
---------
Co-authored-by: Brit <lol@no.com>
- Extracted thinking module with ThinkingEffort, ThinkingLevel, and ThinkingMode types to centralize reasoning configuration across packages.
- Migrated ThinkingLevel type from pi-agent-core to pi-ai package with new validation functions parseThinkingLevel() and getAvailableThinkingLevel().
- Consolidated thinking level constants and descriptions into reusable exports (ALL_THINKING_LEVELS, THINKING_MODE_DESCRIPTIONS) for consistent UI display.
- Removed local thinking-effort-label utility and replaced formatThinkingEffortLabel() with centralized formatThinking() function from pi-ai.
- Refactored thinking mode handling to distinguish ThinkingSelector (user-facing with 'off' option) from ThinkingEffort (provider-level).