- Added the `xd://` virtual device protocol (`internal-urls/xd-protocol.ts`, `tools/xdev.ts`): tools declaring `loadMode: "discoverable"` are unmounted from the request tools array and driven via `read xd://` (list/docs+schema) and `write xd://<tool>` (execute), gated by the `tools.xdev` setting (default on) and inlined into the system prompt.
- Merged the `irc`, `job`, and `launch` tools into a single `hub` tool (`tools/hub/`, `async/job-manager.ts`): messaging keeps `send`/`inbox`/`list`, job control maps to `wait`/`cancel`/`jobs`, process supervision keeps `start`/`logs`/`stop`/`restart`/`describe` with `ps`, and the unified `wait` races background jobs against peer messages; SDK `IrcTool`/`JobTool`/`LaunchTool` are replaced by `HubTool`.
- Removed the hidden `resolve` tool in favor of the `xd://resolve`/`xd://reject`/`xd://propose` resolution devices, auto-including `write` whenever a deferrable tool or plan mode is present.
- Removed the BM25 tool-discovery system: the `search_tool_bm25` tool, the `tool-discovery` module, the `tools.discoveryMode`/`mcp.discoveryMode`/`mcp.discoveryDefaultServers`/`tools.essentialOverride` settings, per-tool MCP selection, and the `mcp_tool_selection` message type.
- Unified tool presentation on `ToolLoadMode` (`essential`|`discoverable`), replacing the custom-tool `xdev?: boolean` opt-out; custom, extension, MCP, RPC host, image-generation, and TTS tools now default to `discoverable`, and added a `satisfies` predicate to `SoftToolRequirement`.
- Removed the standalone `ssh` command tool and `ssh/ssh-executor` (the `ssh://` read/write/search protocol stays), and made `--tools` address hidden built-ins.
- Updated collab-web to render `xd://` dispatches and `hub` op families, dropped the `search_tool_bm25`/`ssh`/`report-finding` renderers, refreshed tool docs and prompts, and migrated the affected tests and changelogs.
- Added schema and type updates for task-agent fields and model resolver settings.
- Extended discovery helper logic to carry resolved task-agent metadata through execution setup.
- Updated task/agent registration and execution paths to use the new capability/field data.
- Expanded test coverage for agent-field parsing, model resolution, and executor prewalk behavior.
Discovered plugin .mcp.json stdio servers launched relative command/cwd
values against the session cwd instead of the plugin's config directory,
breaking bundled ChatGPT/Codex plugins (e.g. Computer Use) with ENOENT
when spawning ./... from an unrelated cwd.
The claude-plugins and omp-plugins providers now resolve relative cwd and
path-like command (./ or ../) against the .mcp.json directory via a shared
resolvePluginStdioPaths helper; bare executables such as npx are left
untouched so PATH lookup still works.
Fixes#5330
- Configured the default `task` subagent to use `auto` thinking.
- Enabled `auto` as a valid thinking-level value in agent frontmatter.
- Adjusted thinking-level precedence to ensure that explicit `:level` suffixes in resolved model patterns override agent-defined defaults.
Synthesized project .omp/RULES.md as a distinct sticky rule name so capability dedup no longer shadows it behind the user sticky rule.
Added a public rules capability regression test covering user and project sticky RULES.md files loading together.
Fixes#4739
Claude plugin manifests may declare a `skills` path that resolves directly to a
directory whose `SKILL.md` IS the skill (e.g. `"skills": ["./"]` or a
subdirectory containing only `SKILL.md`). `scanSkillsFromDir` only scanned
`<dir>/<name>/SKILL.md` children, so the single-skill directory layout — the
common shape the Claude plugins reference documents for plugins shipping one
skill — silently dropped every array-form manifest entry that pointed at it.
Add an opt-in `includeSelf` flag to `ScanSkillsFromDirOptions`: when set,
`<dir>/SKILL.md` (if present) is loaded as a skill in addition to the existing
child scan. The Claude plugin skills loader opts in; every other provider
(agents, builtin, claude.ts, codex, github, omp-plugins, opencode) keeps the
strict child-scan semantic they rely on. Frontmatter `name` still wins over
the directory basename fallback.
Regression test: `skills: ["./single"]` where `./single/SKILL.md` is the only
skill file loads the skill under its frontmatter name.
Claude plugin manifests allow command path entries to name either directories
or flat `.md` command files. The array resolver was now preserving those file
paths, but `loadSlashCommands` still sent every resolved entry through
`loadFilesFromDir`, which only globs inside directories. A manifest such as
`{"commands":["./custom/deploy.md"]}` therefore replaced the default scan
and then loaded nothing.
Teach the command loader to stat each resolved entry: `.md` files are read as
single slash commands with the same plugin namespace and source metadata as
directory-loaded files; directories continue through `loadFilesFromDir`.
Missing entries keep the existing silent-empty behavior.
Add a regression test covering a mixed array of a direct command file and a
command directory while proving default `commands/` remains replaced unless
listed explicitly.
Review feedback on #4610: array-form skills was silently replacing the
default `skills/` scan when the manifest declared any explicit entries.
Per the Claude plugins reference "Path behavior rules"
(https://code.claude.com/docs/en/plugins-reference#path-behavior-rules):
- `skills` ADDS to the default `skills/` scan
- `commands` / `slash-commands` REPLACE the default `commands/` scan
`resolvePluginDir` now takes an explicit `includeFallback` flag. `loadSkills`
passes `true` (fallback + declared entries, deduped by resolved absolute
path so a manifest may still list `./skills` alongside extras without
double-load); `loadSlashCommands` passes `false` (replace semantic
preserved). Deduplication keeps the fallback first and declared entries
in manifest order.
Regression tests cover both semantics: skills-array merges with default
`skills/`; commands-array replaces default `commands/` so a stray
`commands/default.md` no longer loads once the manifest declares an
alternative — matching Claude's documented behavior.
The Claude plugin manifest allows `commands`, `slash-commands`, and `skills`
to be either a single string or an array of strings — documented under
https://code.claude.com/docs/en/plugins-reference#path-behavior-rules and
used by real marketplace plugins such as addyosmani/agent-skills whose
plugin.json declares `"commands": ["./.claude/commands", "./commands"]`.
`resolvePluginDir` in `packages/coding-agent/src/discovery/claude-plugins.ts`
typed those manifest fields as `string` only, so array-shaped values were
silently dropped: no items loaded, no warning surfaced. Slash commands
(`spec`, `plan`, `build`, `test`, `review`) never appeared in the picker.
Normalize `string | string[]` at the resolver, load every in-root entry,
and emit one out-of-plugin-root warning per bad entry so misconfigured
paths remain observable. Skills and slash-command loaders now fan out over
the resolved directory list and merge warnings from all sources.
Fixes#4609
- Added eight new Go-specific rules to the discovery package.
- Registered the new Go rules in the default rule source index.
- Covered the new Go AST matching conditions with test cases in `builtin-defaults.test.ts`.
Codex discovery surfaced every `~/.codex/hooks/*.{ts,js}` file as an OMP
hook (silently defaulting untyped names to `pre:<basename>`), and
`discoverExtensionPaths` then handed those paths to `loadExtension` for
dynamic import. A standalone Codex hook script with a top-level
`process.exit(0)` terminated the host CLI cleanly — `try/catch` around
`await import()` cannot intercept a synchronous exit, so OMP died at the
`loadExtensions:start` startup marker with no error surface.
Two-layer fix:
- `packages/coding-agent/src/extensibility/utils.ts`: new
`withExitGuard` helper patches `process.exit` for the duration of a
guarded callback so an exit raises `ExtensionExitError` instead of
terminating the process; nested and concurrent guards restore correctly
via depth counter.
- `extensibility/extensions/loader.ts`, `extensibility/hooks/loader.ts`,
and `extensibility/plugins/manager.ts` wrap their dynamic-import sites
in `withExitGuard` so the existing per-module `try/catch` records the
intercepted exit as a load error and OMP keeps starting.
- `discovery/codex.ts:loadHooks` no longer treats arbitrary files as
OMP hooks: only `pre-<tool>.{ts,js}` and `post-<tool>.{ts,js}` are
registered. Files like `memory-bank-reminder.ts` are silently skipped
rather than imported as extension factories.
Adds regression coverage:
- `test/extension-loader-process-exit.test.ts` — `loadExtensions` /
`loadHooks` return errors and leave `process.exit` restored when a
module exits at import time; sibling modules still load.
- `test/discovery/codex-hooks-discovery.test.ts` — codex provider
registers `pre-*` / `post-*` files and drops everything else.
Fixes#3680
Expanded environment variable placeholders in Claude marketplace plugin MCP url and headers before registration. Added a regression test covering context7-style HTTP server headers.\n\nFixes #3621
- Renamed the `find` and `search` tools to `glob` and `grep` respectively across the codebase to improve command clarity.
- Implemented full-stack support for the renamed tools, including CLI arguments, system prompts, SDK exports, and tool registration.
- Added automated migration logic in `settings` to transform legacy `find` and `search` configuration keys to their new equivalents.
- Updated the `collab-web` renderer registry to ensure backwards compatibility with legacy tool outputs.
Migrate 203 test files (356 call sites) from fs.rm/fs.rmSync to
removeWithRetries/removeSyncWithRetries to reduce EBUSY test failures
on Windows. removeWithRetries is now exported from @oh-my-pi/pi-utils.
The migration uses a regex-based approach that:
- Replaces fs.rm(path, { recursive, force }) → removeWithRetries(path)
- Replaces fs.rmSync(path, { recursive, force }) → removeSyncWithRetries(path)
- Replaces fs.rm(path) → removeWithRetries(path) (no options)
- Skips fs.rm/fs.rmSync inside template literals (bun --eval scripts)
- Adds imports to existing @oh-my-pi/pi-utils import or creates new one
- Removes unused fs imports where fs.rm was the only fs usage (4 files)
Mapped OpenCode MCP array commands to stdio command plus args and accepted environment as the provider-native env key.\n\nAdded regression coverage for array command normalization, environment mapping, env fallback, and empty args omission.\n\nFixes #3180
This change introduces a new `openrouter` API type and extensively refactors OpenAI-family streaming providers, centralizing shared logic and improving robustness.
Key changes include:
- **Unified OpenAI-family Logic:** Consolidated core utilities, compat resolution, request shaping, and stream processing into `openai-shared.ts`, reducing duplication across `openai-completions`, `openai-responses`, and `openai-codex-responses`.
- **OpenRouter API Type:** Introduced a dedicated `openrouter` API type with dual-surface compatibility, allowing it to dispatch requests as either OpenAI Chat Completions or Responses.
- **Enhanced Provider Integration:**
- Improved Perplexity search to leverage shared OpenAI streaming transports, including API-key fallback to OpenRouter and support for Perplexity's Responses API.
- Integrated xAI-specific logic directly into the shared `stream.ts` dispatch, removing the dedicated `xai-responses` provider.
- Refined credential parsing for Google Gemini CLI and handling of Azure deployment names.
- **Robustness & Consistency:** Improved error handling for Codex, standardized output token parameter resolution, and ensured consistent application of reasoning suppression across all Chat Completions dialects.
- **New Documentation:** Added `provider-endpoint-constraints.md` to detail endpoint-specific behaviors and quirks for various providers.
- **Telemetry & Debugging:** Extended telemetry propagation to advisor calls and overflow compaction tasks. Improved debugging for Codex WebSocket failures and stream error messages.
- **Tooling & Security:** Updated browser stealth scripts to prevent detection and added a new `ts-no-inline-cast-access` TTSR rule.
GitHub documents applyTo as a single comma-separated string (e.g.
"**/*.ts,**/*.tsx") and treats both ** and **/* as all-files. The rule
loader kept the whole CSV value as one glob and missed **/* for
always-apply. Split applyTo via parseCSV and include **/* as all-files.
Addresses review feedback on #2734.
Added GitHub Copilot instruction-file discovery to the github rule provider path, mapping applyTo frontmatter into always-apply or glob-scoped rules and avoiding duplicate always-apply prompt content when context imports the same file.\n\nFixes #2731
When a Claude plugin skill's SKILL.md frontmatter uses a display-style
name (e.g. `name: Understand Anything`), the synthesized slash command
inherited that text. `expandSlashCommand` splits the command at the
first whitespace, so `/understand` never resolved and the multi-word
form could not expand either.
Use `path.basename(path.dirname(skill.path))` so the slash command
always matches the documented `skills/<name>/SKILL.md` → `/<name>`
contract while the frontmatter still drives the description/body.
Fixes#2415
Loaded Claude Code marketplace plugin SKILL.md files into the slash-command capability so Claude-native plugin docs like /understand work in autocomplete and invocation.\n\nAdded a regression covering OMP installed plugin registries and bare slash-command expansion.\n\nFixes #2415
Address review against GitHub Copilot CLI docs (add-custom-instructions):
- COPILOT_CUSTOM_INSTRUCTIONS_DIRS: each dir contributes AGENTS.md (context) and .github/instructions/**/*.instructions.md (recursive), per spec. Dropped the non-spec <dir>/copilot-instructions.md and the wrong top-level <dir>/*.instructions.md scan.
- Scan the project .github/instructions/ tree recursively ('within or below').
- Prompts: .github/prompts/*.prompt.md is a VS Code construct, not a Copilot CLI feature; dropped the fictional ~/.copilot/prompts/ user dir and reframed comments/descriptions accordingly.
The github provider only scanned the project .github/ tree, so Copilot CLI's user-global config was ignored. Add user-global instructions (~/.copilot/copilot-instructions.md), COPILOT_HOME relocation, COPILOT_CUSTOM_INSTRUCTIONS_DIRS (copilot-instructions.md + *.instructions.md), and prompt discovery (*.prompt.md in .github/prompts/ and ~/.copilot/prompts/).
Closes#1913, #1915, #1916.
CLAUDE.md, AGENTS.md, GEMINI.md (and the other discovered context-file
flavors) all carry the @-import convention every other agent ships:
`@path/to/file` inside a memory file inlines that file's contents at
launch. The discovery loaders previously read the file content verbatim
and handed it straight to the system prompt builder, so a CLAUDE.md
whose entire body is `@AGENTS.md` shipped a single literal `@AGENTS.md`
line and Claude never saw the project rules.
Added discovery/at-imports.ts implementing the Claude-Code semantics:
relative paths resolve against the importing file's directory, `~/`
expands to home, recursion stops at MAX_AT_IMPORT_DEPTH (5) with cycle
detection, fenced code blocks and inline code spans are opaque so
`npm install @types/node` and `git@github.com` round-trip verbatim, and
missing files keep the literal @-token intact. Wired the expander
into loadProjectContextFiles so every provider that registers under
the context-file capability benefits without per-provider plumbing.
Fixes#2111
- Added a new built-in TTSR rule `ts-no-test-timers.md` that flags `Bun.sleep`, `setTimeout`, and `setInterval` usage in `*.test.ts` files.
- Registered `ts-no-test-timers` in the built-in rules index so it ships with default discovery providers.
- Updated builtin-defaults tests to enforce rule-name uniqueness and verify the new rule only matches in `*.test.ts` scopes.
- Added astCondition to rule frontmatter parsing and rule metadata, with AST-grep normalization.
- Updated TTSR bucketing so astCondition-only rules are treated as interruptible matches.
- Added ts-redundant-clear-guard as a built-in JS/TS tool rule for guarded clear* calls.
- Added AST snapshot matching in agent sessions with per-stream cache throttling and cleanup.
- 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.
The github provider registered context-files (.github/copilot-instructions.md)
and instructions (.github/instructions/*.instructions.md), but no skills
capability — so .github/skills/<name>/SKILL.md, the layout GitHub documents
for Copilot Agent Skills, was silently never discovered. The skill://
URL resolved to 'Available: none' and nothing surfaced in the system prompt.
Register a skill capability on the github provider (priority 30, project-only)
pointing at .github/skills/ and reuse scanSkillsFromDir with
requireDescription: true to match the Agent Skills spec and the sibling
native/omp-plugins providers. Pin the wiring with a discovery test that
loads the skills capability scoped to the github provider against a temp
cwd containing a SKILL.md, and a negative case that drops a skill missing
a description.
Fixes#1906
Native user-level config discovery (MCP, skills, rules, slash commands, prompts, instructions, hooks, tools, settings, extensions, and the top-level SYSTEM.md/RULES.md/AGENTS.md) now resolves the user scope through getAgentDir() in builtin.ts, omp-extension-roots.ts, and the discovery-layer getUserPath() helper. A named profile sees only its own ~/.omp/profiles/<name>/agent config instead of the default profile's ~/.omp/agent leaking into every profile, matching the /mcp config writer and getMCPConfigPath("user").
discoverExtensionModulePaths now detects top-level symlinked directories that the native glob skips (follow_links=false) and synthesizes their index/package.json entry-point matches, so an extension shared across profiles via a symlink loads like a real directory. Symlinked extension files were already handled.
cli: check --tiny-worker on the profile-flag-stripped resolvedArgv, matching the adjacent --smoke-test check and launch routing.
- Warns against leaving `@deprecated` compatibility shims instead of finishing a refactor.
- Registered in the builtin rule index and covered by the defaults test.