- Added per-account usage reporting in the `omp usage` command.
- Added `provider`, `json`, and `redact` options to customize usage output.
- Updated CLI wiring to route usage commands to the new per-account behavior.
- Replaced canonical-row resolution with getCanonicalModelSelections in model lists and selector flow.
- Hydrated model selector state from registry on construction and kept cached selections during refresh.
- Preserved highlighted and cached model selection when offline refresh completed or reordered models.
- Added parity checks between getCanonicalModelSelections and resolveCanonicalModel via registry tests.
- Updated PI native streaming requests to send model identifiers as provider-qualified keys.
- Updated the auth-gateway model registry to store `${provider}/${id}` first and retain legacy bare IDs as fallback keys.
- Derived descriptors, default-model map, env keys, login list, and refresh dispatch from one ProviderDefinition per provider.
- Disabled OpenAI Codex stream obfuscation and interrupted whitespace-only tool-call argument deltas.
- Derived auth-broker callback ports and paste-code login set from the registry.
- Added Homebrew and mise install-source detection in `resolveUpdateMethod` using prefix, bin-dir, and shim checks.
- Added `runUpdateCommand` delegation to `updateViaHomebrew` and `updateViaMise` for manager installs.
- Changed `--force` behavior for package managers to run `brew reinstall` and `mise install --force`.
- Routed background-job completions and late LSP diagnostics through the new non-interrupting aside channel so the model sees them mid-run between requests.
- Removed inline custom rendering from the LSP tool now that diagnostics surface through the shared transcript renderer.
- Split top-level and delimited read selectors into separate rows before grouping.
- Merged duplicate same-file read selectors into one summarized row with ellipsis truncation.
- Computed grouped-read status and totals from aggregated rows for accurate summaries.
- Updated changelog entries and fixtures to document/read expectations.
- Gallery fixtures gained a `renderState` hook to render non-standard component states.
- Added a `read_group` filesystem fixture that uses `ReadToolGroupComponent` to render delimited, full-file, and range read states.
- Added gallery CLI coverage to verify grouped read fixture output and expected ranges.
- Added first-party-first provider priority defaults for model ranking.
- Consolidated model resolution to use getModelMatchPreferences from session settings.
- Prioritized providerPriorityRank ahead of usage rank when picking preferred models.
- Added second-pass fallback to default-model or API-key-valid matching order.
- Updated startup handling so `applyStartupCwd` now re-syncs `parsed.cwd` to the resolved absolute project directory after `setProjectDir` runs.
- Adjusted the CLI cwd tests to verify a relative `--cwd` argument is normalized to an absolute path and does not double-resolve against the new process cwd.
- Changed the `github-copilot` service provider to resolve credentials only from `COPILOT_GITHUB_TOKEN`.
- Updated CLI extra help text to document `COPILOT_GITHUB_TOKEN` as the GitHub Copilot environment variable.
- Reworded environment variable docs to reflect the revised Copilot/GitHub token usage and order.
- Removed the built-in `background` (`bg`) slash command and its `handleBackgroundCommand` path from the interactive flow.
- Deleted background event subscription and shutdown handling by removing `handleBackgroundEvent` from the event, input, and interactive controllers.
- Simplified `InteractiveModeContext` by dropping background-only fields and helpers such as `isBackgrounded`, background UI context creation, and background event callbacks.
- Added `ApiKeyResolver`/`ApiKey` types and exported auth-retry helpers.
- Changed stream and gateway auth retry handling to use resolver steps.
- Added initial-key, force-refresh, and rotate credential retries for auth failures.
- Updated agent and coding-agent integrations to use context-aware API-key resolvers.
- Anchored each redraw at column 0 and terminated rows with CRLF instead of bare LF.
- Capped each line to terminal width so wrapping cannot desync the cursor-up.
- Threaded stdout/stderr columns into the progress sink.
- Migrated Effort and THINKING_EFFORTS imports to @oh-my-pi/pi-ai/effort in CLI args and launch command files.
- Split model-registry dependencies across focused @oh-my-pi/pi-ai submodules instead of the root barrel export.
- Added anonymous Perplexity authentication mode for unauthenticated web searches.
- Switched web-search setup checks to use `isExplicitlyAvailable` and removed key enforcement in doctor.
- Updated Perplexity OAuth flow to reuse auth handling for all non-key searches and anonymous responses.
- Updated CLI and provider option help text to mark the Perplexity key optional with fallback.
- Fixed custom-rendered tools with `mergeCallAndResult` (e.g. `lsp`) emitting a redundant tool-name line above the framed result.
- Collapsed the leading blank line for self-delimiting framed boxes.
- Added gallery fidelity routing `lsp`/`task` through the custom-tool branch via a `customRendered` fixture flag.
- Added gallery harness tests guarding state coverage and the custom-branch fallback label.
- Added lazy-loaded `gallery` command registration and new filters for tool, state, width, expanded, and plain output.
- Implemented gallery state rendering with terminal-width defaults, state filtering, and unknown-tool fallback handling.
- Added shared fixture types and aggregated renderer fixtures for multiple tool families in `galleryFixtures`.
- Added tests for renderer state coverage, route-specific output (streaming/progress/success/error), and fixture fallback.
- Stopped capping the synthesized answer at 12 lines while sources expanded in full.
- Rendered the answer through Markdown so headings, bold, lists, and code display formatted.
- Added regression tests for expanded/collapsed answer rendering.
`omp plugin install .` (and any cwd-relative, absolute, or tilde-prefixed
spec) failed with `Invalid package name: .` because
`classifyInstallTarget` only emitted `marketplace` / `npm`, so local paths
fell through to `validatePackageName`, which rejects every non-npm name.
The classifier now emits a third `local` arm for `.`, `..`, `./…`,
`..\…`, `~`, `~/…`, `~\…`, `/…`, `C:\…`, `C:/…`, and `\\unc` specs.
`handleInstall` dispatches that arm to `PluginManager.link()` — the same
code path as `omp plugin link <path>` — so the two verbs are
interchangeable for local plugin directories. `--dry-run` short-circuits
before any filesystem work, `--scope`/`--force` surface a warning since
they are no-ops here (link is idempotent, and scope only governs
marketplace installs).
Coverage: thirteen-case classifier matrix in `marketplace/cli.test.ts`
plus a new `plugin-install-local.test.ts` with spy-based routing checks
for `./`, `../`, `/`, `~/`, plus a real-filesystem test that stages a
plugin folder, invokes `runPluginCommand`, and verifies the resulting
symlink + lockfile entry. The new test calls `mock.restore()` in
`afterEach` so `piUtils` / `MarketplaceManager.prototype` spies do not
leak into sibling suites.
Fixes#1945
- Skipped count/concurrency normalization when --bench is set.
- Errored when no OAuth accounts resolve for the provider.
- Updated flag docs to run one request per OAuth account.
- Added `getOAuthAccesses` to resolve each stored credential once.
- Sent one live request per account, reporting TTFT and TPS.
- Streamed per-account progress with interactive status lines.
- Added `omp dry-balance` command with model, count, concurrency, and JSON flags.
- Implemented random session-id sampling with bounded concurrency for OAuth access dry-run checks.
- Added success/failure summary generation with account and reason stats and optional JSON output.
- Set CLI exit status to 1 when any dry-balance attempt fails.
- Added the new dry-balance capability to the unreleased changelog notes.
- Adjusted session and dashboard renderers to derive heights from live terminal rows.
- Propagated terminal-row callbacks through picker/controller wiring and session selectors.
- Reworked list visibility and page navigation to honor row-based budgets and footer lines.
- Added DECRQM 2031 startup probing and stopped OSC11 polling once support was confirmed.
bun install -g <pkg>@<v> did not reliably re-resolve transitive
optionalDependencies, so @oh-my-pi/pi-natives and the platform leaf
@oh-my-pi/pi-natives-<tag> stayed at the previous version while
@oh-my-pi/pi-coding-agent moved. The loader’s validateLoadedBindings
then aborted because the .node file exposed the old
__piNativesV<old> sentinel instead of __piNativesV<new>.
buildBunInstallArgs now pins @oh-my-pi/pi-natives and (when the
running tag is one the release pipeline publishes) the platform
leaf to the same version it installs for @oh-my-pi/pi-coding-agent,
so bun replaces all three in lock-step. The leaf is gated by the
same SUPPORTED_PLATFORMS set the loader uses, so unsupported tags
still surface the original 'no matching version' diagnostic instead
of EBADPLATFORM.
Fixes#1824
- Renamed `TodoWriteTool` to `TodoTool` and its source/prompt files.
- Updated tool registration, schema, renderers, and gating to `todo`.
- Adjusted cursor provider native tool names and tests to match.
- Renamed strike-animation constants and `todo-error-reminder` type.
- Added local CONNECT proxy with TLS interception to capture Claude API traffic.
- Drives Claude Code via headless PTY/xterm and extracts the first /v1/messages exchange.
- Added `claude:trace` npm script and CLI with JSON/text output modes.
- Added integration test using a fake Claude script against a local TLS server.
The Anthropic web search path built request headers via buildAnthropicSearchHeaders, which never threaded model headers through buildAnthropicHeaders, so ANTHROPIC_CUSTOM_HEADERS was dropped from every web-search request regardless of mode. The streaming path's resolveAnthropicCustomHeaders also gated on isFoundryEnabled(), so users with a corporate ANTHROPIC_BASE_URL + ANTHROPIC_CUSTOM_HEADERS (e.g. X-Gateway-Key) got 401s on web_search unless they set CLAUDE_CODE_USE_FOUNDRY=true.
Loosen the resolver to also apply when ANTHROPIC_BASE_URL points to a non-Anthropic host, export the baseUrl-keyed variant, and have buildAnthropicSearchHeaders pass the resolved custom headers as modelHeaders so search and streaming paths behave identically. Stock api.anthropic.com (no Foundry) still omits the headers.
Fixes#1693
- Expanded the existing entries in docs/environment-variables.md so the override-semantics ('search-only, isolates from main ANTHROPIC_API_KEY / ANTHROPIC_BASE_URL / FOUNDRY_BASE_URL') are spelled out, and added a usage note for enterprise-gateway split routing.
- Surfaced the search-only env vars (ANTHROPIC_SEARCH_API_KEY / ANTHROPIC_SEARCH_BASE_URL / ANTHROPIC_SEARCH_MODEL) in the Anthropic provider section of docs/tools/web_search.md, where users were already looking.
- Added ANTHROPIC_SEARCH_BASE_URL alongside ANTHROPIC_SEARCH_API_KEY in 'omp --help' so the pair shows up together in the CLI env-var summary.
Fixes#1694
omp resolves the update target by querying https://registry.npmjs.org/ directly, but `bun install -g pkg@<version>` would then consult bun's on-disk manifest snapshot AND honour the user's npm-mirror configuration (corporate proxy, Taobao, …). Either source can lag the upstream registry by minutes-to-hours, in which case bun rejects the version with `No version matching "X" found for specifier "@oh-my-pi/pi-coding-agent" (but package exists)` even though the registry omp just queried is serving it.
The bun install step now runs with both `--no-cache` (skip the manifest snapshot) and `--registry=https://registry.npmjs.org/` (pin the official catalog regardless of bunfig/.npmrc) so the install observes the same registry state the version check used. The registry URL is centralised in an `NPM_REGISTRY` constant shared by `getLatestRelease` and `buildBunInstallArgs`.
Fixes#1686
- Enabled resume picker to preload sessions and toggle folder/all scope with Tab.
- Enabled resume flow to fall back to all-project sessions and switch cwd on resume.
- Added centralized applyCwdChange to refresh caches, commands, and UI after cwd updates.
- Updated session restoration to adopt restored session cwd and sessionDir when present.
- Fixed `session_id` never being created or populated; every history row had `NULL` for session.
- Added schema migration (`ALTER TABLE history ADD COLUMN session_id`) for pre-existing databases.
- Wired interactive mode to call `setSessionResolver(...)` so prompts are stamped with the active session at submission time.
- Re-enabled session ranking in `--resume` and in-session pickers via `matchingSessionIds()`, merging fuzzy and prompt-history signals.
Follow-up to #1503. When an extension registered a flag whose name collides
with a value-taking built-in — e.g. plan-mode's boolean `--plan` vs the
built-in `--plan <plan-model>` selector — the extension-aware reparse still
took the built-in branch. `omp --extension plan-mode --plan "review the diff"`
consumed "review the diff" as the plan-model value, leaving parsed.messages
empty and overwriting result.plan with the prompt text. recoverFlagValue only
patched the extension flag value, not the corrupted parsed object that
applyExtensionFlags returns as initialArgs.
Fix at the source: parseArgs now checks the registered extension-flag set
BEFORE the built-in branches, so a registered flag is parsed with the
extension's semantics (boolean toggle / string value) and surfaces in
unknownFlags without consuming the following token or touching the built-in
field. This makes recoverFlagValue dead, so applyExtensionFlags is simplified
to read resolved values straight from unknownFlags.
Tests: parseArgs-level shadowing guard (boolean --plan keeps the message and
leaves result.plan unset); applyExtensionFlags message/built-in-field
preservation for colliding boolean (--plan) and string (--model) flags;
non-colliding flag-looking-value rule retained. Verified the new guards fail
without the shadowing fix.
Two review fixes for the extension-flag/initial-prompt work:
1. @file ordering — `processFileArguments` runs `process.exit(1)` on a
missing/unreadable file. It had been moved after `createSession`, which
writes the terminal breadcrumb eagerly (SessionManager.create →
#newSessionSync), so `omp @missing.md "x"` left a junk session/breadcrumb
behind before exiting.
Resolve extension-registered CLI flags BEFORE creating the session: load the
session's extensions up front (new `loadSessionExtensions` helper, the single
source of createAgentSession's discovery-branch logic), build an
ExtensionFlagSink straight from the loaded extensions + runtime, re-parse
argv, then process @file args — all before any session exists. The loaded
result is handed back to createAgentSession via `preloadedExtensions` (now
checked before `disableExtensionDiscovery`, so it can't double-load) and the
same EventBus is shared, so no extra work. This keeps the P1#1 fix
(`--flag @value` is the flag's value, not a file) while failing fast with no
session side effects.
2. "Can we avoid the big list of names?" — removed the hand-maintained
`BUILTIN_FLAG_NAMES` set (and its stale "rejected at registration" doc).
`applyExtensionFlags` now always falls back to recovering a flag's value from
argv when parseArgs didn't surface it; the recovery scan mirrors parseArgs's
consumption rules (flag-looking space-form values stay their own flag) and is
a no-op for flags that were absent or already surfaced, so no list of
built-in names is needed.
Adds `ExtensionRunner.aggregateFlags` (static) so getFlags and the CLI's
pre-session sink share one implementation.
Tests: pre-session flag resolution via the exact main.ts sink pattern;
list-free recovery of an arbitrary colliding built-in (`--model`); and the
flag-looking-value rule. Verified typecheck + extension/runner/acp suites.
Extends `omp plugin install` to accept git sources alongside npm specs and
marketplace refs. Bun's installer already understands git URLs; the blocker
was `PluginManager.install`'s strict npm-name validator and the assumption
that the actual package name could be derived from the spec.
- `git-url.ts`: `parseGitUrl` now recognizes npm-style namespaced shorthand
(`github:user/repo`, `gitlab:`, `bitbucket:`, `codeberg:`, `sourcehut:` /
`srht:`), with optional `#ref` and `.git` suffix. Exposes `isGitSpec` as
`parseGitUrl(s) !== null`. Existing protocol-URL and `git:` shorthand paths
are untouched.
- `manager.ts`: `install()` branches on `isGitSpec`. Git specs go through a
separate `validateGitSpec` (shell-metachar rejection only — `/`, `:`, `@`,
`#`, `+` are legal) and the real package name is discovered by snapshotting
`plugins/package.json` deps before `bun install` and diffing afterwards.
Falls back to value-match on force-reinstall where the key already exists.
- Help text in `plugin-cli` documents the new sources and adds a github:
example.
Smoke tested end-to-end on Windows with both forms against the test repo:
PluginManager.install('github:oldschoola/omp-insights')
PluginManager.install('https://github.com/oldschoola/omp-insights')
both resolve `@oldschoola/omp-insights@1.2.3` and write a correct lock entry.
Shell-injection probe (`github:foo/bar; rm -rf /`) is rejected.
The previous collision guard threw in registerFlag, which broke loading the
bundled plan-mode example extension (it registers `--plan`, also a built-in)
even when `--plan` was never passed — making a documented extension unusable.
Registering a built-in-named flag is a supported pattern: `--plan` is both the
built-in plan-model selector and plan-mode's boolean mode toggle, and the value
must reach both. So instead of rejecting, preserve delivery: remove the guard,
and in applyExtensionFlags recover a colliding flag's value from argv
(resolveCollidingFlag) when parseArgs routed it to the built-in branch and it
never reached unknownFlags. Non-colliding flags are unchanged (peer-* etc.).
Verified the real bundled plan-mode.ts loads with --plan registered and
delivered; replaced the reject-test with a loads-without-throwing regression
plus colliding-flag delivery coverage.
Three issues from an adversarial review, all rooted in the startup argv parse
running before extensions load:
1. Flag-looking string values (`--name --print`): the extension-aware reparse
consumed the following token as the value, disagreeing with the startup
parse that treated `--print` as the built-in flag — so the reparse could
silently flip command shape. Extension string flags now consume a following
token only in `--flag=value` form or when it is not flag-looking; pass a
flag-looking value as `--flag=value`. Keeps both parses consistent.
2. `@file` string values (`--target @notes.md`): file args were processed from
the startup parse, which misreads the value as a file and reads it into the
prompt. processFileArguments now runs on the extension-aware parse
(initialArgs.fileArgs); pipedInput stays early for mode detection.
3. Built-in collisions: an extension flag named like a built-in (e.g. `model`)
was consumed by the built-in branch and never delivered to the runner.
registerFlag now rejects names in BUILTIN_FLAG_NAMES with a clear error
(isolated per-extension by loadExtension's try/catch).
Adds tests for all three plus the documented startup-parse misclassification.
Addresses review: a boolean flag in equals form still leaked its value. parseArgs
splices `--headless=true` into `--headless`, `true` so value-consuming flags can
pick the value up via `args[++i]`; a boolean flag sets itself without consuming
it, leaving `true` to fall through as a positional message — and since
applyExtensionFlags feeds this parse into buildInitialMessage, `omp
--headless=true "do the task"` sent `true` as the prompt.
Track the spliced value's index and, if no branch advanced past it (i.e. the
matched flag did not consume a value), drop it after the dispatch. Closes the
whole equals-form class — boolean extension flags and built-in non-consuming
flags (`--no-tools=true`, `--print=1`) alike — at the single parsing site.
Adds tests for boolean extension + built-in flags in equals form.