Skip to content

feat(interaction): /plugin marketplace command - #7

Merged
GeekCmore merged 8 commits into
mainfrom
feat/plugin-market-command
Sep 5, 2026
Merged

GeekCmore merged 8 commits into
mainfrom
feat/plugin-market-command

Conversation

@GeekCmore

Copy link
Copy Markdown
Collaborator

What

The /plugin command family: Mayfly's consumer for the plugin marketplace index published by Ephemeral-AI-Lab/dsh-plugins (dist/index.json, marketplace PR: Ephemeral-AI-Lab/dsh-plugins#8).

  • /plugin — grouped flat catalog (Enter opens the read-only detail panel; i installs, u removes, r refreshes; type-to-filter). Badges carry source tier (official/dsh/community), surfaces (TUI/Web/Server), installed/update/removed state.
  • /plugin list — what the profile carries: installed, update available, removed-from-market (tombstone) rows.
  • /plugin install <id> [--source npm|github], uninstall <id>, info <id>, refresh.
  • Detail panel: Provides, per-frontend surface verdict (derived server ∨ own contribution), engines, capabilities disclosure, verified@version, copyable install command.

Design notes

  • Discovery metadata only — the runtime contract stays package + cordis.patch.yml; no runtime manifest, per the architecture rule.
  • Installs/removals shell out to dsh plugin --profile <name> add|remove <specs> through updaterInternals.spawnOnce (the updater's swap seam): npm specs and github:owner/repo#ref[&path:subdir] both supported; all rows of an entry install together (sibling peers resolve); declared allowBuilds names merge into the profile pnpm-workspace.yaml first; profile-patch rows append to / remove from the profile's cordis.patch.yml.
  • Catalog loads cache-first ($DSH_HOME/storages/mayfly-plugin-market/cache.json, 1h TTL), falls back raw.githubusercontent → jsDelivr, serves stale cache offline; mayfly.marketIndexUrl setting overrides the whole chain.
  • Bundle membership is a startup boundary: success notices say "restart Mayfly and start a new session".
  • The staged /plugin locale block from an earlier draft stays untouched (superseded copy deck noted in locale.ts); this family ships its own strings.
  • Panels follow the D40 boundary: InfoPanels are construction-frozen; a locale switch re-renders newly-opened panels.

Verification

  • pnpm run verify:changed green: changed files at 100% statements / branches / functions / lines (620 branches), including fiber-unload gates staged through deferred seams and locale re-render with and without the service.
  • 79 new tests across plugin-market.spec.ts (catalog loader: parse guard, TTL, fallback chain, stale/offline, force) and plugin-commands.spec.ts (command worlds, installer seams, lifecycle, locale).

Follow-up

Website marketplace section (nav third tab + generated plugin pages) lands as a separate PR on top of this branch.

🤖 Generated with Claude Code

GeekCmore and others added 3 commits September 5, 2026 00:05
- plugin-market/types|catalog|installer: index parse guard with a
  schemaVersion check, cache-first loader with raw → jsDelivr fallback
  and stale serving, installer over the dsh CLI seam (npm and github:
  specs incl. monorepo subdirs, allowBuilds preflight, profile-patch
  row insert/remove, installed-state derivation)
- plugin-commands: flat grouped-by-badge browse panel (Enter detail,
  i/u/r hotkeys), installed view with updates and removed-from-market
  rows, install/uninstall/info/list/refresh argument paths, restart
  reminders, web-only warnings
- settings: mayfly.marketIndexUrl (official chain when empty)
- locale: zh catalog for the family; tips row; command reference rows
- specs: catalog loader and command worlds over the updater seams,
  including fiber-unload gates and locale re-render

Co-Authored-By: Claude Code <noreply@anthropic.com>
- shape arms: installed/update/versionless details, tools-only and
  commands-only provides, tui-only and web-only surfaces, github-only
  install commands, statusNote-less removals
- lifecycle arms: fiber-unload gates at each await (load, findDshBin,
  install, refresh), locale re-render with and without the service,
  in-flight serialization, bare-context installs
- installer arms: missing workspace file allowBuilds merge, no-trailing-
  newline patch append, regex unquote; catalog arms: non-Error
  rejections, cache shape guards; defensive arms carry v8 ignore notes

Co-Authored-By: Claude Code <noreply@anthropic.com>
@GeekCmore
GeekCmore force-pushed the feat/plugin-market-command branch from 3d792e8 to 333ad53 Compare September 4, 2026 16:07
@GeekCmore

Copy link
Copy Markdown
Collaborator Author

Follow-up hardening is now on this PR.

  • Parse and mutate pnpm-workspace.yaml / cordis.patch.yml structurally; real commented empty patch files, existing allowBuilds: false, later top-level keys, !!js, idempotent updates, exact-row removal, conflicts, and write failures are covered.
  • Default to GitHub when all rows are GitHub-only, reject partial mixed-source installs, quote copyable shell specs, dedupe multi-row installed listings, and gate competing catalog reloads by generation.
  • Bind cache entries to their index URL, retain matching stale data on forced refresh failure, and validate the complete remote index shape before render/install.
  • Parsed the 14-entry catalog from dsh-plugins#8 successfully.

Verification: pnpm run verify:full passes; 169 test files / 2841 tests, repository coverage 100% statements, branches, functions, and lines; build, lib closure, examples, agent docs, and happy smoke pass. Dedicated acceptance profile: mayfly-pr7-market.

@GeekCmore

Copy link
Copy Markdown
Collaborator Author

Additional profile acceptance found and fixed the Cordis activation shape: profile-patch rows now live under a top-level - insert: patch instead of being emitted as id-targeted overrides. The installer reconciles exact rows across existing insert blocks and removes only its matching nested rows.

Real dedicated-profile acceptance on mayfly-pr7-market now passes:

  • GitHub-only codex-terminal installed from the reviewed marketplace commit; node-pty: false became true under the existing allowBuilds map.
  • npm acp installed into the real commented empty patch file as - insert: [{ id: acp, name: @deepseek-ai/dsh-acp }].
  • repeated install, uninstall, and reinstall left exactly one row.
  • dsh --profile mayfly-pr7-market --dump-config resolves both plugins with no missing-entry warning.
  • PTY catalog and installed-list panels rendered the 14-entry reviewed index and the installed ACP/Codex Terminal rows.

The final follow-up is commit a4717fb; pnpm run verify:full remains green at 100% repository coverage.

@GeekCmore

Copy link
Copy Markdown
Collaborator Author

Critical acceptance follow-up: ACP Server is an automation-only JSON-RPC stdio frontend. Loading it beside Mayfly causes both plugins to own the same stdin/stdout, making the TUI appear frozen. The acceptance profile has been recovered by removing ACP.

Commit b167feb now prevents recurrence at three layers: the command path, catalog hotkey, and installer all refuse ACP in the current Mayfly profile. It remains visible as Automation, its detail points to a dedicated non-Mayfly profile, and already-installed copies remain removable. A live-index install attempt was verified to return the compatibility error without changing package.json, pnpm-workspace.yaml, or cordis.patch.yml. Full coverage and happy smoke remain green.

@GeekCmore

Copy link
Copy Markdown
Collaborator Author

Discoverability follow-up a1f920e: the /plugin browser now contributes an explicit contextual key hint. At normal widths it renders i install · u remove · r refresh; at narrow widths it compacts to i/u/r. Renderer output was verified at 80 and 36 columns and through a real PTY. Latest PR checks pass.

@GeekCmore

Copy link
Copy Markdown
Collaborator Author

Workflow redesign from acceptance feedback is now in a13e796.

  • /plugin is a counted two-tab browser: Installed and Not installed; /plugin starts on Not installed and /plugin list starts on Installed.
  • Each row exposes Details plus the applicable Install/Uninstall action, while i/u/r remain available and visible.
  • Progress, compatibility checking, rollback, success, and failure render in the panel header instead of the hidden prompt-editor notice. Successful operations reread the profile immediately and move the row/update both counts.
  • / explicitly enters search; once search editing starts, i/u/r are text rather than shortcuts, avoiding one-letter search/action conflicts.
  • New installs run a fresh-process import compatibility check. A failure automatically removes the packages and restores package.json, lockfile, workspace, and Cordis patch. This caught the live market's incompatible dsh-loop (CallId import) and rolled it back instead of leaving the profile unable to boot.

Verification: 2848 tests pass, full repository coverage is 100%, width scan/build/lib/examples/happy smoke pass. The dedicated acceptance profile is back to Installed 0 / Not installed 14.

@GeekCmore

Copy link
Copy Markdown
Collaborator Author

Acceptance completed

Accepted against the dedicated mayfly-pr7-market profile after the final interaction redesign.

Exercised scenarios:

  • /plugin opens the counted Installed / Not installed tabs and defaults to Not installed.
  • Tab and arrow navigation, row selection, Enter details, visible Install/Uninstall actions, and i / u / r shortcuts.
  • Install progress, compatibility validation, success/failure reporting, installed-state refresh, and automatic rollback for an incompatible package.
  • /plugin list opens the installed view.
  • Search entry/exit behavior and adjacent panel navigation remain responsive.

Automated acceptance:

  • pnpm run verify:full passed locally (2848 tests passed, 6 skipped; 100% coverage).
  • GitHub build and typecheck / lint / test (coverage) checks passed.

Known catalog/package issue: the live loop release imports a missing CallId export from @deepseek-ai/dsh-llm. Mayfly detects this after install and restores the profile snapshot; no partial installation remains.

@GeekCmore
GeekCmore merged commit 440aef7 into main Sep 5, 2026
4 checks passed
@GeekCmore
GeekCmore deleted the feat/plugin-market-command branch September 20, 2026 08:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant