Files
jackwener--opencli/docs/guide/plugins.md
T
jakevin 70b1145b5e refactor: migrate all CLI adapters from YAML to TypeScript (#887)
* refactor: remove version field from GenerateOutcome and EarlyHint

All consumers are in the same repo and evolve together — version field
adds ceremony without practical value at this stage.

Keeps schema_version in VerifiedArtifactMetadata (sidecar file format).

* refactor: migrate all 123 CLI adapters from YAML to TypeScript

Remove YAML as an adapter format entirely. All adapters now use
TypeScript with cli() from @jackwener/opencli/registry.

- Convert 123 YAML adapter files to TypeScript via batch script
- Remove YAML scanning from discovery.ts (registerYamlCli, yaml import)
- Remove scanYaml() and shouldReplaceManifestEntry() from build-manifest.ts
- Change synthesize.ts to output JSON candidates (internal format)
- Change generate-verified.ts to write .ts adapter files instead of .yaml
- Delete yaml-schema.ts (dead code) and scripts/yaml-to-ts.mjs (one-time tool)
- Update all tests to match new format

Closes discussion in #OpenCLI thread 47ddba82.

* fix: close YAML migration gaps in plugin scaffold, validation, and scan

- plugin-scaffold.ts: generate hello.ts (TS pipeline) instead of hello.yaml
- plugin.ts validatePluginStructure: no longer accept .yaml as valid command file
- plugin.ts scanPluginCommands: remove .yaml/.yml from scanned extensions
- discovery.ts: add explicit log.warn() when YAML files detected in clis/ or plugins/
- plugin.test.ts: update all test fixtures from .yaml to .js
- plugin-scaffold.test.ts: update hello.yaml references to hello.ts
- Delete dead src/yaml-schema.ts

Resolves PR #887 review blockers from @mbp-codex-pr0.

* refactor: complete YAML removal across docs, skills, record, and binance adapters

Code changes:
- record.ts: candidate output changed from .yaml (yaml.dump) to .json (JSON.stringify), removed js-yaml import
- src/clis/binance: convert all 11 YAML adapters to TypeScript cli() format
- binance/commands.test.ts: rewrite to use registry instead of yaml.load
- skill-generate.test.ts, diagnostic.test.ts: update mock paths from .yaml to .ts
- build-manifest.ts, synthesize.ts: update stale YAML comments

Documentation:
- README.md: remove .yaml from Dynamic Loader, fix plugin types, fix synthesize comment
- README.zh-CN.md: fix synthesize comment
- CONTRIBUTING.md: replace YAML Adapter section with Pipeline Adapter (TS), update arg examples
- docs/developer/yaml-adapter.md: replaced with deprecation redirect
- docs/developer/architecture.md: remove YAML pipeline references
- docs/developer/contributing.md: remove YAML adapter section
- docs/developer/ai-workflow.md: YAML → TS in synthesize description
- docs/guide/getting-started.md: remove .yaml from loader, update engine description
- docs/guide/plugins.md: remove YAML plugin option, update plugin types
- docs/index.md, docs/comparison.md: remove YAML adapter references
- docs/zh/guide/plugins.md: remove .yaml from scan description

Skills:
- opencli-explorer/SKILL.md: rewrite YAML vs TS decision tree to TS-only
- opencli-oneshot/SKILL.md: replace YAML templates with TS cli() templates
- opencli-generate/SKILL.md: YAML artifact path → TS artifact path
- opencli-usage/SKILL.md, plugins.md: update adapter format references

* fix: clean up remaining YAML adapter references in docs

- docs/zh/guide/plugins.md: replace YAML plugin example with TS pipeline
- docs/developer/testing.md: YAML Adapter heading → Adapter, remove validate line
- TESTING.md: same fix in root testing doc
- CONTRIBUTING.md: remove "YAML validation" comment
- docs/.vitepress/config.mts: mark YAML Adapter Guide as (Deprecated) in nav
- docs/advanced/download.md: remove "YAML Adapters" from pipeline step heading
2026-04-08 23:01:08 +08:00

6.7 KiB

Plugins

OpenCLI supports community-contributed plugins. Install third-party adapters from GitHub, and they're automatically discovered alongside built-in commands.

Quick Start

# Install a plugin
opencli plugin install github:ByteYue/opencli-plugin-github-trending

# List installed plugins
opencli plugin list

# Update one plugin
opencli plugin update github-trending

# Update all installed plugins
opencli plugin update --all

# Use the plugin (it's just a regular command)
opencli github-trending repos --limit 10

# Remove a plugin
opencli plugin uninstall github-trending

How Plugins Work

Plugins live in ~/.opencli/plugins/<name>/. Each subdirectory is scanned at startup for .ts or .js command files — the same formats used by built-in adapters.

Supported Source Formats

# GitHub shorthand
opencli plugin install github:user/repo
opencli plugin install github:user/repo/subplugin   # install specific sub-plugin from monorepo
opencli plugin install https://github.com/user/repo

# Any git-cloneable URL
opencli plugin install https://gitlab.example.com/team/repo.git
opencli plugin install ssh://git@gitlab.example.com/team/repo.git
opencli plugin install git@gitlab.example.com:team/repo.git

# Local plugin (for development)
opencli plugin install file:///path/to/plugin
opencli plugin install /path/to/plugin

The repo name prefix opencli-plugin- is automatically stripped for the local directory name. For example, opencli-plugin-hot-digest becomes hot-digest.

Plugin Manifest (opencli-plugin.json)

Plugins can include an opencli-plugin.json manifest file at the repo root to declare metadata:

{
  "name": "my-plugin",
  "version": "1.0.0",
  "opencli": ">=1.0.0",
  "description": "My awesome plugin"
}
Field Description
name Plugin name (overrides repo-derived name)
version Semantic version
opencli Required opencli version range (e.g. >=1.0.0, ^1.2.0)
description Human-readable description
plugins Monorepo sub-plugin declarations (see below)

The manifest is optional — plugins without one continue to work exactly as before.

Monorepo Plugins

A single repository can contain multiple plugins by declaring a plugins field in opencli-plugin.json:

{
  "version": "1.0.0",
  "opencli": ">=1.0.0",
  "description": "My plugin collection",
  "plugins": {
    "polymarket": {
      "path": "packages/polymarket",
      "description": "Prediction market analysis",
      "version": "1.2.0"
    },
    "defi": {
      "path": "packages/defi",
      "description": "DeFi protocol data",
      "version": "0.8.0",
      "opencli": ">=1.2.0"
    },
    "experimental": {
      "path": "packages/experimental",
      "disabled": true
    }
  }
}

Installing

# Install ALL enabled sub-plugins from a monorepo
opencli plugin install github:user/opencli-plugins

# Install a SPECIFIC sub-plugin
opencli plugin install github:user/opencli-plugins/polymarket

How It Works

  • The monorepo is cloned once to ~/.opencli/monorepos/<repo>/
  • Each sub-plugin gets a symlink in ~/.opencli/plugins/<name>/ pointing to its subdirectory
  • Command discovery works transparently — symlinks are scanned just like regular directories
  • Disabled sub-plugins (with "disabled": true) are skipped during install
  • Sub-plugins can specify their own opencli compatibility range

Updating

Updating any sub-plugin from a monorepo pulls the entire repo and refreshes all sub-plugins:

opencli plugin update polymarket   # updates the monorepo, refreshes all

Uninstalling

opencli plugin uninstall polymarket   # removes just this sub-plugin's symlink

When the last sub-plugin from a monorepo is uninstalled, the monorepo clone is automatically cleaned up.

Version Tracking

OpenCLI records installed plugin versions in ~/.opencli/plugins.lock.json. Each entry stores the plugin source, current git commit hash, install time, and last update time. opencli plugin list shows the short commit hash when version metadata is available.

Creating a Plugin

Creating a TypeScript Plugin

my-plugin/
├── package.json
├── my-command.ts
└── README.md

package.json:

{
  "name": "opencli-plugin-my-plugin",
  "version": "0.1.0",
  "type": "module",
  "peerDependencies": {
    "@jackwener/opencli": ">=1.0.0"
  }
}

my-command.ts:

import { cli, Strategy } from '@jackwener/opencli/registry';

cli({
  site: 'my-plugin',
  name: 'my-command',
  description: 'My custom command',
  strategy: Strategy.PUBLIC,
  browser: false,
  args: [
    { name: 'limit', type: 'int', default: 10, help: 'Number of items' },
  ],
  columns: ['title', 'score'],
  func: async (_page, kwargs) => {
    const res = await fetch('https://api.example.com/data');
    const data = await res.json();
    return data.items.slice(0, kwargs.limit).map((item: any, i: number) => ({
      title: item.title,
      score: item.score,
    }));
  },
});

TS Plugin Install Lifecycle

When you run opencli plugin install, TS plugins are automatically set up:

  1. Clonegit clone --depth 1 from GitHub
  2. npm install — Resolves regular dependencies
  3. Host symlink — Links the running @jackwener/opencli into the plugin's node_modules/ so import from '@jackwener/opencli/registry' always resolves against the host
  4. Transpile — Compiles .ts.js via esbuild (production node cannot load .ts directly)

On startup, if both my-command.ts and my-command.js exist, the .js version is loaded to avoid duplicate registration.

Example Plugins

Repo Type Description
opencli-plugin-github-trending TS GitHub Trending repositories
opencli-plugin-hot-digest TS Multi-platform trending aggregator (zhihu, weibo, bilibili, v2ex, stackoverflow, reddit, linux-do)
opencli-plugin-juejin TS 稀土掘金 (Juejin) hot articles, categories, and article feed
opencli-plugin-rubysec TS RubySec advisory archive and advisory article reader

Troubleshooting

Command not found after install

Restart opencli (or open a new terminal) — plugins are discovered at startup.

TS plugin import errors

If you see Cannot find module '@jackwener/opencli/registry', the host symlink may be broken. Reinstall the plugin:

opencli plugin uninstall my-plugin
opencli plugin install github:user/opencli-plugin-my-plugin