@ai-setting/roy-task-plugin
OpenCode plugin for business-semantic task management, plus the roy-task
CLI that bridges Feishu (Lark) messages to an OpenCode agent.
Requires only Node.js ≥ 20. The
roy-taskCLI runs on plain node — no bun, no compilation, no build step.npm iand you are done.
Install
npm install @ai-setting/roy-task-plugin
That gives you two things:
| Artifact | Entry | Runtime |
|---|---|---|
| OpenCode plugin | dist/index.js | loaded by opencode (bun) |
roy-task CLI | dist/cli.js | plain node ≥ 20 |
roy-task — Feishu ↔ agent bridge
npx roy-task --help
npx roy-task version
npx roy-task status --profile default
npx roy-task serve --profile default
| Command | What it does |
|---|---|
roy-task status | Read-only pre-flight: is an event consumer already running for this app? is the opencode roy agent visible? |
roy-task serve | Consume Feishu events and dispatch them to opencode run --agent roy, then reply. |
roy-task serve --dry-run | Listen and log, but never spawn an agent and never reply. |
roy-task serve --once | Handle exactly one event, then exit (testing / CI). |
⚠️ One consumer per (app, EventKey)
lark-cli allows exactly one event consume per app+EventKey. A second one
does not error — it silently splits the event stream, so messages get
delivered to the wrong handler or dropped. That is why --profile is a
first-class flag: pick an app whose events nothing else is consuming.
roy-task status checks this for you before you start.
--profile matters for more than event routing
Feishu openId / chatId are per-app isolated. A reply sent through the
wrong app produces 99992361 open_id cross app. Session ids are therefore
derived from (profile, chatId), never from chatId alone.
Plugin usage
The plugin half is unchanged — see the repository README for hook behaviour, persistence modes (local SQLite / remote HTTP) and the audit chain.
Notes on the build
dist/index.jsis bundled (--packages=bundle): opencode loads it with nonode_modulesresolution needed.dist/cli.jsis external (--target=node --packages=external, 35 KB): it keeps@ai-setting/roy-agent-coreas a real dependency so npm can dedupe it, instead of vendoring ~1 MB of core into this package.stdinis forced to EOF (stdio[0] = "ignore") for every child process.opencode runcallsreadStdin(), which only resolves at EOF — on an inherited socket it hangs for ~45 s. The long-livedlark-cli event consumeneeds the opposite (stdin must stay open); it is spawned by roy-agent-core, not by this CLI.
License
MIT