OpenCLI supports community-contributed plugins. Install third-party adapters from GitHub, and they're automatically discovered alongside built-in commands.
# 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-trendingPlugins live in ~/.opencli/plugins/<name>/. Each subdirectory is scanned at startup for .yaml, .ts, or .js command files — the same formats used by built-in adapters.
opencli plugin install github:user/repo
opencli plugin install https://github.com/user/repoThe repo name prefix opencli-plugin- is automatically stripped for the local directory name. For example, opencli-plugin-hot-digest becomes hot-digest.
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.
Zero dependencies, no build step. Just create a .yaml file:
my-plugin/
├── my-command.yaml
└── README.md
Example my-command.yaml:
site: my-plugin
name: my-command
description: My custom command
strategy: public
browser: false
args:
limit:
type: int
default: 10
pipeline:
- fetch:
url: https://api.example.com/data
- map:
title: ${{ item.title }}
score: ${{ item.score }}
- limit: ${{ args.limit }}
columns: [title, score]For richer logic (multi-source aggregation, custom transformations, etc.):
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,
}));
},
});When you run opencli plugin install, TS plugins are automatically set up:
- Clone —
git clone --depth 1from GitHub - npm install — Resolves regular dependencies
- Host symlink — Links the running
@jackwener/opencliinto the plugin'snode_modules/soimport from '@jackwener/opencli/registry'always resolves against the host - Transpile — Compiles
.ts→.jsviaesbuild(productionnodecannot load.tsdirectly)
On startup, if both my-command.ts and my-command.js exist, the .js version is loaded to avoid duplicate registration.
| Repo | Type | Description |
|---|---|---|
| opencli-plugin-github-trending | YAML | GitHub Trending repositories |
| opencli-plugin-hot-digest | TS | Multi-platform trending aggregator (zhihu, weibo, bilibili, v2ex, stackoverflow, reddit, linux-do) |
| opencli-plugin-juejin | YAML | 稀土掘金 (Juejin) hot articles, categories, and article feed |
Restart opencli (or open a new terminal) — plugins are discovered at startup.
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