Files
oh-my-pi/packages/coding-agent/src/cli/classify-install-target.ts
T
Miroslav Drbal 4e199f93f7 feat: add marketplace plugin system
Add Claude Code-compatible marketplace plugin infrastructure:

- Registry: types, ID helpers, atomic read/write for marketplaces.json
  and installed_plugins.json (Claude Code format with version: 2)
- Fetcher: classifySource (6 ordered rules), parseMarketplaceCatalog,
  fetchMarketplace (local sources; git/http stubs for Phase 2)
- Resolver + Cache: resolvePluginSource with pathIsWithin containment,
  cachePlugin, removeCachedPlugin, cleanOrphanedCache
- MarketplaceManager: orchestrates add/remove/update marketplaces,
  install/uninstall/enable plugins, clearPluginRootsCache on mutation
- CLI: omp plugin marketplace add|remove|update|list,
  omp plugin discover, classifyInstallTarget for name@marketplace
- Discovery: listClaudePluginRoots reads OMP registry alongside
  Claude's, OMP authoritative for duplicate plugin IDs
- Args: --plugin-dir repeatable flag (parsing only, runtime wiring TBD)
- Slash command: /reload-plugins clears fs + roots cache
- Test fixtures and 130 tests across 8 test files
2026-03-30 13:44:53 +02:00

30 lines
1.1 KiB
TypeScript

/**
* Classify an install spec as a marketplace plugin reference or a plain npm package.
*
* Rules (applied in order):
* 1. Starts with `@` (scoped npm) -> always npm.
* 2. Contains `@` after the first character -> split on the LAST `@`.
* If the right-hand side is a known marketplace name, it's a marketplace ref.
* Otherwise it's an npm spec (e.g. `pkg@1.2.3`).
* 3. No `@` -> npm.
*/
export function classifyInstallTarget(
spec: string,
knownMarketplaces: Set<string>,
): { type: "marketplace"; name: string; marketplace: string } | { type: "npm"; spec: string } {
// Rule 1: scoped npm package — @ at position 0 is never a marketplace separator.
if (spec.startsWith("@")) return { type: "npm", spec };
// Rule 2: @ somewhere after the first character.
const atIdx = spec.lastIndexOf("@");
if (atIdx > 0) {
const rhs = spec.slice(atIdx + 1);
if (knownMarketplaces.has(rhs)) {
return { type: "marketplace", name: spec.slice(0, atIdx), marketplace: rhs };
}
// Not a known marketplace — treat as npm version specifier.
return { type: "npm", spec };
}
// Rule 3: no @ at all.
return { type: "npm", spec };
}