Skip to content
Back to skills

Plugin Maintenance

ASecurity

Guide for modifying the Chorus plugin (Claude Code, Codex, OpenClaw, Kiro, Pi, dsh, and Hermes ports), updating skill documentation, and releasing new plugin versions.

  • 1,192 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 27, 2026
data-aitypescriptpythonrustgoshellbashnodetestingcode-reviewgit

Works with

  • claude code
  • cursor
  • cli
  • api
  • mcp

Security analysis

A96/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro shows the line behind each finding and how to fix it

Scanned October 7, 2026

npx -y skills add Chorus-AIDLC/Chorus --skill plugin-maintenance --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Plugin Maintenance?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Plugin Maintenance
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/chorus-aidlc-plugin-maintenance/badge)](https://www.skillsdirectory.com/skills/chorus-aidlc-plugin-maintenance)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: plugin-maintenance
description: Guide for modifying the Chorus plugin (Claude Code, Codex, OpenClaw, Kiro, Pi, dsh, and Hermes ports), updating skill documentation, and releasing new plugin versions.
license: AGPL-3.0
metadata:
  author: chorus
  version: "0.5.0"
  category: development
---

# Chorus Plugin & Skill Maintenance

How to modify the Chorus plugin, update skill documentation, and release new versions. **Seven plugin packages** are maintained in parallel — Claude Code, Codex, OpenClaw, Kiro, Pi, dsh (DeepSeek Harness), and Hermes Agent — plus the standalone skill surface. That is **eight skill surfaces total**; when you change skill content, sweep all eight (see [Skill Content Changes — Seven Surfaces](#skill-content-changes--seven-surfaces), which also lists the Hermes copy). The Hermes port has its own section: [Hermes Agent port](#hermes-agent-port).

## File Structure

```
.claude-plugin/
  marketplace.json              ← Claude Code marketplace registry (version here)

public/chorus-plugin/           ← Claude Code plugin package
  .claude-plugin/
    plugin.json                 ← Plugin metadata (version here)
  hooks.json                    ← Hook definitions (SubagentStart, etc.)
  bin/                          ← Hook scripts (bash) — stateful via API state-get/set
  skills/
    chorus/SKILL.md             ← Core skill
    develop/ idea/ proposal/ quick-dev/ review/ yolo/SKILL.md
  agents/                       ← Reviewer agents as .md (Claude Code style)
    proposal-reviewer.md
    task-reviewer.md

plugins/chorus/                 ← Codex plugin package (separate from Claude Code)
  .codex-plugin/
    plugin.json                 ← Plugin metadata (version here)
  hooks.json
  hooks/                        ← Hook scripts (bash) — intentionally stateless
    on-session-start.sh
    on-post-submit-proposal.sh
    on-post-submit-for-verify.sh
    chorus-mcp-call.sh          ← MCP helper (has hardcoded clientInfo version)
    hook-output.sh
  skills/
    chorus/SKILL.md             ← Codex port — mentions $skill syntax, ~/.codex/config.toml
    develop/ idea/ proposal/ quick-dev/ review/ yolo/SKILL.md
    chorus-proposal-reviewer/SKILL.md  ← Reviewers as skills in Codex (no agents/)
    chorus-task-reviewer/SKILL.md

packages/openclaw-plugin/       ← OpenClaw plugin package (TS runtime + skills)
  openclaw.plugin.json          ← Plugin manifest (id, skills dir, activation, configSchema) — NO version field
  package.json                  ← npm package (version here; `openclaw` block: extensions, runtimeExtensions, install/compat)
  src/                          ← TypeScript runtime (index.ts, mcp-client.ts, sse-listener.ts, event-router.ts, wake.ts)
                                  — real-time SSE event bridge + MCP registration; NOT bash hooks
  dist/                         ← Compiled JS (npm install loads this; linked install loads src/ via jiti)
  skills/
    chorus/SKILL.md             ← OpenClaw port — tools namespaced `chorus__<tool>`, inline OpenSpec detection
    develop/ idea/ proposal/ quick-dev/ review/ yolo/ brainstorm/ openspec-aware/SKILL.md
    proposal-reviewer/SKILL.md  ← Reviewers as skills (like Codex, no agents/)
    task-reviewer/SKILL.md

public/kiro-plugin/             ← Kiro CLI plugin (loose .kiro/ template tree + install script)
  .kiro/
    settings/mcp.json           ← Chorus remote MCP server (${env:CHORUS_API_KEY} bearer, disabled:false)
    skills/chorus-*/SKILL.md    ← the chorus-PREFIXED skills (no bare names — global-install distinctiveness)
    agents/chorus.json          ← main agent (.json — Kiro CLI, NOT .md); hosts all hooks via __CHORUS_BIN__ placeholder
    agents/chorus.md            ← main-agent system-prompt sidecar (file://./chorus.md)
    agents/chorus-*-reviewer.json  ← 3 read-only reviewer subagents (tools:["read","@chorus"])
    steering/chorus.md          ← platform overview + AI-DLC context (folds in the `chorus` overview skill)
  bin/                          ← Hook scripts (bash, 3.2-safe) + chorus-api.sh + test-syntax.sh
                                  installer copies these into <KIRO_DIR>/chorus-bin/ and resolves __CHORUS_BIN__
(public/install-kiro.sh)        ← deprecation stub → redirects to `chorus agents add`; the .kiro/ tree is installed by cli/init/file-template.mjs (asset list: public/kiro-plugin/manifest.txt)

packages/chorus-pi/             ← Pi coding agent package (TS extension + skills)
  package.json                  ← npm package + Pi manifest (version here)
  extensions/chorus.ts          ← Native Pi event handlers; auto checkin/session lifecycle/reviewer nudges
  lib/lib.ts                    ← Pure helpers used by the extension and unit tests
  bin/chorus-mcp-call.sh        ← MCP helper for byte-exact OpenSpec document mirroring
  skills/
    chorus/SKILL.md             ← Core skill; Pi `/skill:<name>` syntax
    develop/ idea/ proposal/ quick-dev/ review/ yolo/ openspec-aware/SKILL.md
  agents/                       ← 3 read-only reviewer agents copied to ~/.pi/agent/agents/
  test/                         ← Static, helper-unit, extension-event, and manual-session verification

packages/chorus-dsh/            ← DeepSeek Harness (dsh) plugin — published npm bundle @chorus-aidlc/chorus-dsh
  package.json                  ← npm package + dsh bundle patch (version here; tracks the APP version, e.g. 0.16.3)
  src/index.ts                  ← Cordis plugin apply(): injects skills/persona/MCP wrapper; daemon-backend gate
  cordis.patch.yml              ← dsh composition patch shipped in the bundle
  bin/chorus-mcp-call.mjs       ← Node MCP-over-HTTP helper (clientInfo.version auto-read from package.json — no manual bump)
  skills/
    chorus/SKILL.md             ← Core overview (dsh HAS a chorus/ overview, unlike Kiro)
    <stage>-chorus/SKILL.md     ← 13 stage/reviewer skills, `-chorus` SUFFIX (brainstorm/develop/idea/
                                  proposal/quick-dev/review/yolo/docs/orchestrate/openspec-aware/
                                  code-reviewer/proposal-reviewer/task-reviewer)

packages/chorus-hermes/         ← Hermes Agent (Nous Research) port — NOT on npm/PyPI; installed from a git subdir pinned to a SHA
  chorus/                       ← native plugin (plugin.yaml + register(ctx)); see "Hermes Agent port"
  chorus-mcp/                   ← portable Agent Plugins v1 package (plugin.json + mcp.json): the chorus MCP server

public/skill/                   ← Standalone skill (any MCP-compatible agent)
  chorus/SKILL.md               ← Same structure, softer language, IDE-agnostic
  proposal-chorus/SKILL.md
  quick-dev-chorus/SKILL.md
```

### Plugin key differences

| Aspect | Claude Code | Codex | OpenClaw | Kiro CLI | Pi |
|--------|-------------|-------|----------|----------|----|
| Skill invocation | `/chorus:develop` | `$develop` | `/develop` | `/chorus-develop` | `/skill:develop` |
| Tool names | `chorus_<tool>` | `chorus_<tool>` | `chorus__<tool>` | `chorus_<tool>` / `@chorus` matcher | Native `mcp__chorus__chorus_<tool>` (direct/codemode); legacy adapter5 direct `chorus_chorus_<tool>` or bare; extension uses backend names |
| MCP config | `.mcp.json` | `~/.codex/config.toml` | Plugin config | `~/.kiro/settings/mcp.json` | Native global `~/.pi/agent/mcp.json` / trusted project `.pi/mcp.json`; legacy adapter5 global `mcp-adapter.json` |
| Session lifecycle | SubagentStart/Stop hooks | Manual/stateless | Manual | `agentSpawn`/`stop` hooks | Automatic via mutable `subagent_spawn` and `subagent_manage` events |
| Reviewers | `agents/*.md` via Task | Skills mounted in `spawn_agent` | Reviewer skills via `sessions_spawn` | Native JSON subagents | `agents/chorus-*-reviewer.md` via `pi-subagents` |
| User interaction | `AskUserQuestion` | Plain text | Plain text | Plain text | Plain text |
| OpenSpec detection | SessionStart hook | SessionStart hook | Inline | `agentSpawn` hook | `session_start` extension event |
| Runtime/hooks | Stateful bash hooks | Stateless bash hooks | TypeScript SSE runtime | Bash 3.2 hooks in `chorus.json` | TypeScript native extension; shell only for OpenSpec mirroring |
| Task execution | Agent Teams waves | `spawn_agent` | Main-agent waves | Kiro subagents | `subagent_spawn` workers |
| Install | Marketplace | `install-codex.sh` | OpenClaw plugin manager | `install-kiro.sh` | npm (`pi install npm:@chorus-aidlc/chorus-pi`) |

When porting a change between plugins, preserve these intentional differences. Don't add state files to the Codex/OpenClaw plugins, don't use `$`-prefix outside Codex, keep OpenClaw's `chorus__` names and manual-session/inline-detection wording, and preserve Kiro's `chorus-` skill prefix, `@chorus/<tool>` matchers, and `__CHORUS_BIN__` placeholder. For Pi, use `/skill:<name>`, `subagent_spawn`, native extension events, and plain-text interaction; do not introduce Claude hooks or Codex session wording.

**dsh (DeepSeek Harness)** is a published npm bundle (`@chorus-aidlc/chorus-dsh`) added to a dsh profile via `dsh plugin --profile <name> add -w`. It is Cordis-based: `src/index.ts`'s `apply()` injects the `-chorus`-suffixed skills, persona, and a Node MCP wrapper. Skills use the `-chorus` **suffix** (like standalone) but it keeps a `chorus/SKILL.md` overview. Its version tracks the **app version** (0.16.3+), NOT the 0.9.x skill sequence. The dsh **daemon backend** (`cli/dsh-spawner.mjs` / `dsh-managed-config.mjs`) is currently **de-listed / offline** — kept dormant, not advertised; do NOT re-add it to the daemon install menu, CLI `--agent` help, or `DAEMON.md` / `MCP_TOOLS.md` without bringing the backend back online.

## When to Update What

"All six plugin skills" = Claude Code + Codex + OpenClaw + Kiro + Pi + dsh. "All seven surfaces" additionally includes standalone `public/skill/`. Note the Kiro surface has **no `chorus/SKILL.md`** — its overview lives in `steering/chorus.md`, and its stage skills are `chorus-`prefixed; dsh keeps a `chorus/SKILL.md` overview and uses `-chorus`-**suffixed** stage skills.

| Change | Files to update |
|--------|----------------|
| New MCP tool added | `src/mcp/tools/*.ts` + `docs/MCP_TOOLS.md` + all six plugin overviews (Kiro: `steering/chorus.md`) + standalone overview |
| MCP tool description changed | `src/mcp/tools/*.ts` only (skill docs reference tool names, not descriptions) |
| Skill content / wording (e.g. AC now required) | The matching stage skill in **all seven surfaces** |
| New workflow step | All six plugin stage skills + standalone equivalent |
| New Idea/Task status | All six plugin overviews + standalone overview + locale messages |
| New execution rule | All six plugin overviews + standalone overview (softer wording) |
| Permission model change | All six plugin overviews + affected stage skills + runtime checks where applicable |
| Hook script change (Claude Code) | `public/chorus-plugin/bin/*.sh` + `hooks.json` if new hook. **Never copy hook changes blindly into `plugins/chorus/hooks/`** — Codex hooks are intentionally stateless and lack subagent events. OpenClaw has no bash hooks at all. |
| Hook script change (Codex) | `plugins/chorus/hooks/*.sh` — rarely needed; session-start, post-submit-proposal, post-submit-for-verify are the only three. If bumping plugin version, also update the hardcoded `clientInfo.version` in `chorus-mcp-call.sh`. |
| OpenClaw runtime change | `packages/openclaw-plugin/src/*.ts` (TS SSE/MCP runtime) + `npm run typecheck` + `npm run test`. Not bash hooks — this is a compiled TypeScript extension. |
| Pi runtime change | `packages/chorus-pi/extensions/*.ts` + `lib/*.ts`; run `bash test/all.sh` from the package directory |
| Any plugin change | Bump version in every file for that package (see Version Bump Checklist) |

## Version Bump Checklist

Every time **any** plugin package changes, bump the version in **all** of that package's locations. There are two version sequences in play:

- **Skill-frontmatter sequence** — shared by corresponding `SKILL.md` files across the six plugin ports. When the same skill content ships to multiple plugins, bump them together.
- **Per-package plugin sequences** — Claude Code + Codex share one (`marketplace.json` / both `plugin.json`, currently `0.9.x`); OpenClaw's `package.json` has its **own** sequence (currently `0.5.x`). These are independent files — edit each.

### Claude Code plugin — bump together
1. `.claude-plugin/marketplace.json` — `"version": "X.Y.Z"`
2. `public/chorus-plugin/.claude-plugin/plugin.json` — `"version": "X.Y.Z"`
3. Every skill under `public/chorus-plugin/skills/*/SKILL.md` — `metadata.version: "X.Y.Z"` (all skills, including `quick-dev/`, now use the standard nested `metadata:` block)

### Codex plugin — bump together
4. `plugins/chorus/.codex-plugin/plugin.json` — `"version": "X.Y.Z"`
5. Every skill under `plugins/chorus/skills/*/SKILL.md` — `metadata.version: "X.Y.Z"` (all skills, including `quick-dev/`, now use the standard nested `metadata:` block). **Don't forget the two reviewer skills**: `chorus-proposal-reviewer/SKILL.md` and `chorus-task-reviewer/SKILL.md`.
6. `plugins/chorus/hooks/chorus-mcp-call.sh` — hardcoded `clientInfo.version` string in the JSON-RPC `initialize` payload

### OpenClaw plugin — bump together
7. `packages/openclaw-plugin/package.json` — `"version": "X.Y.Z"` using OpenClaw's **own** sequence (`0.5.x`), NOT the skill sequence. `openclaw.plugin.json` has **no** version field — nothing to edit there.
8. Every skill under `packages/openclaw-plugin/skills/*/SKILL.md` — `metadata.version: "X.Y.Z"` on the **skill sequence** (`0.9.x`, matching the other plugins' skills). Includes the two reviewer skills `proposal-reviewer/SKILL.md` and `task-reviewer/SKILL.md`.
   - **Do NOT** touch `src/mcp-client.ts`'s `clientInfo.version` (`0.1.0`) — it is a static MCP client identifier, not the plugin version.

### Kiro plugin — bump together
Kiro has **no plugin.json / marketplace registry** (it reads loose `.kiro/` files), so the only versioned files are the skill frontmatters.
10. Every skill under `public/kiro-plugin/.kiro/skills/chorus-*/SKILL.md` — `metadata.version: "X.Y.Z"` on the shared skill sequence (all `chorus-*` skills). The `agents/*.json` and `steering/chorus.md` carry **no** version field — nothing to edit there.
11. `public/kiro-plugin/bin/chorus-api.sh` — hardcoded `clientInfo.version` string in the JSON-RPC `initialize` payload (same as the Codex `chorus-mcp-call.sh` helper).

### Pi package — bump together
12. `packages/chorus-pi/package.json` — `"version": "X.Y.Z"`.
13. Every skill under `packages/chorus-pi/skills/*/SKILL.md` — `metadata.version: "X.Y.Z"`, including `openspec-aware`.
14. `packages/chorus-pi/extensions/chorus.ts` — hardcoded MCP `clientInfo.version`.

### dsh (DeepSeek Harness) plugin — bump together
dsh tracks the **app version** (currently `0.16.3`), NOT the 0.9.x skill sequence.
16. `packages/chorus-dsh/package.json` — `"version": "X.Y.Z"` (the published `@chorus-aidlc/chorus-dsh` bundle).
17. Every skill under `packages/chorus-dsh/skills/*/SKILL.md` — `metadata.version: "X.Y.Z"` (the `chorus/` overview + all `-chorus`-suffixed stage/reviewer skills).
   - **Do NOT** hardcode a version in `bin/chorus-mcp-call.mjs` — its `clientInfo.version` is auto-read from `package.json`, so it never drifts.

### Coordinated npm release identity

When cutting an application release, the four public npm packages are one
lockstep release unit. Set the same `X.Y.Z` in:

1. root `package.json` (`@chorus-aidlc/chorus`);
2. `packages/openclaw-plugin/package.json`
   (`@chorus-aidlc/chorus-openclaw-plugin`) and refresh its standalone
   `package-lock.json`;
3. `packages/chorus-dsh/package.json`
   (`@chorus-aidlc/chorus-dsh`);
4. `packages/chorus-pi/package.json`
   (`@chorus-aidlc/chorus-pi`) — no standalone lockfile to refresh (it rides the
   workspace `pnpm-lock.yaml`); a plain version bump does not dirty the lockfile,
   but if you changed its deps run `pnpm install --lockfile-only` at the repo
   root (the release preflight runs `pnpm install --frozen-lockfile`).

Publishing GitHub Release `vX.Y.Z` triggers
`.github/workflows/publish-npm.yml`. The workflow completes all four package
build/pack contracts before any upload, then publishes CLI → OpenClaw → dsh →
chorus-pi. The older interactive package publish scripts are maintenance tools,
not the CI release entry point.

For all four npm package settings, Trusted Publisher must use the exact
workflow filename `publish-npm.yml` in the matching GitHub repository. Leave
the npm Environment field blank while the workflow job has no `environment`;
if an Environment is introduced, configure the exact same case-sensitive name
on npm and on the workflow job. Keep `id-token: write`, do not add
`NPM_TOKEN`, `NODE_AUTH_TOKEN`, or a setup-node token placeholder, and do not
disable npm's automatic provenance. A zero exit status from `npm publish`
completes the upload and is recorded as `accepted-by-npm`. Registry visibility
and provenance metadata can lag behind acceptance; the workflow does not poll
them or use them to gate subsequent packages.
`@chorus-aidlc/chorus-pi` is the newest package: its Trusted Publisher must be
registered on npmjs.org before its first coordinated publish, or the run stops
at chorus-pi with the first three already published — a human/ops step, not
something to fake or token-shim around.

For partial publication, repair the external problem and choose **Re-run jobs**
on the same failed Actions run. Do not create a replacement Release/version.
The rerun skips exact versions already confirmed in npm and resumes in fixed
order. A failed registry lookup is not a skip and must stop the run; the job
summary identifies `failed` and `not-attempted` packages for diagnosis.

### Standalone skills — independent versioning
15. `public/skill/*/SKILL.md` — bump only the standalone skills that changed, using their own version sequence.

Quick way to check all versions:
```bash
grep -rn '"version"\|^  version:\|^version:\|clientInfo' \
  .claude-plugin/marketplace.json \
  public/chorus-plugin/.claude-plugin/plugin.json \
  public/chorus-plugin/skills/*/SKILL.md \
  plugins/chorus/.codex-plugin/plugin.json \
  plugins/chorus/skills/*/SKILL.md \
  plugins/chorus/hooks/chorus-mcp-call.sh \
  packages/openclaw-plugin/package.json \
  packages/openclaw-plugin/skills/*/SKILL.md \
  public/kiro-plugin/.kiro/skills/*/SKILL.md \
  public/kiro-plugin/bin/chorus-api.sh \
  packages/chorus-pi/package.json \
  packages/chorus-pi/skills/*/SKILL.md \
  packages/chorus-pi/extensions/chorus.ts \
  packages/chorus-dsh/package.json \
  packages/chorus-dsh/skills/*/SKILL.md \
  public/skill/*/SKILL.md
```

Users update via:
```bash
/plugin update chorus@chorus-plugins           # Claude Code
codex plugin update chorus@chorus-plugins      # Codex
# OpenClaw: reinstall/update via the OpenClaw plugin manager (npm spec @chorus-aidlc/chorus-openclaw-plugin)
# Kiro: re-run `chorus agents add --agents kiro` (idempotent; installs the .kiro/ tree via the file-template installer)
# Pi: pi install npm:@chorus-aidlc/chorus-pi   (reinstall to pull the latest published extension)
```

Pi is published to npm as `@chorus-aidlc/chorus-pi` and installs with `pi install npm:@chorus-aidlc/chorus-pi` — the same path `chorus agents add` automates (select Pi, or `--agents pi`). The old sparse-git-checkout / local-path workaround is no longer needed; local-path `pi install ./packages/chorus-pi` is now only for developing chorus-pi from the repo checkout.

## Skill Content Changes — Seven Surfaces

Chorus skill content lives in **seven parallel surfaces**. A content change must sweep all seven:

1. `public/chorus-plugin/skills/<skill>/SKILL.md` — Claude Code
2. `plugins/chorus/skills/<skill>/SKILL.md` — Codex
3. `packages/openclaw-plugin/skills/<skill>/SKILL.md` — OpenClaw
4. `public/kiro-plugin/.kiro/skills/chorus-<skill>/SKILL.md` — Kiro (note the `chorus-` **prefix**; the overview lives in `steering/chorus.md`, not a `chorus/SKILL.md`)
5. `packages/chorus-pi/skills/<skill>/SKILL.md` — Pi
6. `packages/chorus-dsh/skills/<skill>-chorus/SKILL.md` — dsh (note the `-chorus` **suffix**; keeps a `chorus/SKILL.md` overview)
   - plus `packages/chorus-hermes/chorus/skills/<skill>/SKILL.md` — Hermes (bare names, loaded as `skill_view("chorus:<skill>")`; reviewers are `chorus-<kind>-reviewer` skills, not agent files). See [Hermes Agent port](#hermes-agent-port).
7. `public/skill/<skill>-chorus/SKILL.md` — standalone (note the `-chorus` suffix and flatter set)

**Sweep command** — find every occurrence before editing so nothing is missed:
```bash
grep -rniE "<your-search-term>" \
  public/chorus-plugin/skills/ plugins/chorus/skills/ \
  packages/openclaw-plugin/skills/ public/kiro-plugin/.kiro/skills/ \
  packages/chorus-pi/skills/ packages/chorus-dsh/skills/ \
  packages/chorus-hermes/chorus/skills/ public/skill/
```

Then bump the relevant version sequences (shared skill frontmatter across plugin surfaces 1–5, independent standalone version for #6, and per-package versions as needed).

## Porting Changes Between Plugins

Whenever you change content in one plugin, mirror it into the other plugin surfaces unless the difference is intentional. Typical workflow:

1. Make the change in `public/chorus-plugin/skills/<skill>/SKILL.md`
2. Diff-check the counterparts:
   - `diff public/chorus-plugin/skills/<skill>/SKILL.md plugins/chorus/skills/<skill>/SKILL.md`
   - `diff public/chorus-plugin/skills/<skill>/SKILL.md packages/openclaw-plugin/skills/<skill>/SKILL.md`
   - `diff public/chorus-plugin/skills/<skill>/SKILL.md packages/chorus-pi/skills/<skill>/SKILL.md`
3. Apply the same semantic change to every port, but **preserve** intentional phrasing:
   - **Codex**: `.mcp.json` → `~/.codex/config.toml`, `Task tool` → `spawn_agent`, `/chorus:X` → `$X`, "sessions auto-managed" → "sessions are optional / stateless port"
   - **OpenClaw**: tool names `chorus_<tool>` → `chorus__<tool>`, `AskUserQuestion` → plain-text prompt, reviewers via `sessions_spawn`, OpenSpec detection is inline (no SessionStart hook), sessions are manual
   - **Kiro**: `chorus-` skill prefix, native JSON subagents, `@chorus` tools, and hook-driven context
   - **Pi**: `/skill:X`, `subagent_spawn`, plain-text prompts, reviewer `.md` agents, extension-managed worker sessions
4. Bump all affected plugins' versions (all files in the Version Bump Checklist)
5. For the Codex plugin, also verify `chorus-mcp-call.sh` `clientInfo.version` matches

## Plugin vs Standalone Skill: Tone Differences

The plugin skill targets Claude Code specifically. The standalone skill targets any MCP-compatible agent (Cursor, Kiro, etc.).

| Aspect | Plugin (`public/chorus-plugin/skills/`) | Standalone (`public/skill/`) |
|--------|----------------------------------------|------------------------------|
| AskUserQuestion | "ALWAYS use... NEVER display as text" | "prefer your IDE's interactive prompt if available" |
| Session management | "Do NOT create sessions — plugin handles it" | "Create or reopen a session before starting work" |
| Skip elaboration | "you MUST ask the user for permission first" | "confirm with the user first" |
| Hook references | References specific hooks (SubagentStart, etc.) | No hook references |

**Rule of thumb**: Plugin version uses MUST/NEVER/ALWAYS. Standalone version uses "prefer", "confirm", "consider".

## Adding a New MCP Tool — Full Checklist

1. Implement in `src/mcp/tools/*.ts` (pm.ts, public.ts, etc.)
2. Add to `docs/MCP_TOOLS.md`
3. Update permission tables and tool lists in all seven overview surfaces:
   - `public/chorus-plugin/skills/chorus/SKILL.md`
   - `plugins/chorus/skills/chorus/SKILL.md`
   - `packages/openclaw-plugin/skills/chorus/SKILL.md` (use the `chorus__<tool>` namespaced form)
   - `public/kiro-plugin/.kiro/steering/chorus.md` (Kiro has no `chorus/SKILL.md` — the overview is the steering doc)
   - `packages/chorus-pi/skills/chorus/SKILL.md`
   - `packages/chorus-dsh/skills/chorus/SKILL.md`
   - `public/skill/chorus/SKILL.md`
4. If it changes a stage workflow, update the matching stage skill in all seven locations.
5. Bump every affected plugin's versions (see Version Bump Checklist)
6. Run `npx tsc --noEmit` to verify

## Modifying Hook Scripts

### Claude Code plugin hooks (`public/chorus-plugin/bin/`)
- `on-session-start.sh` — SessionStart hook (caches `agent_permissions` via `"$API" state-set`)
- `on-user-prompt.sh` — UserPromptSubmit hook
- `on-subagent-start.sh` — SubagentStart hook
- `on-subagent-stop.sh` — SubagentStop hook (reads `agent_permissions` via `state-get`)
- `on-teammate-idle.sh` — TeammateIdle hook
- `on-pre-enter-plan.sh`, `on-pre-exit-plan.sh` — Plan mode hooks
- `on-task-completed.sh` — TaskCompleted hook
- `on-post-submit-proposal.sh`, `on-post-submit-for-verify.sh` — PostToolUse reviewer reminders

### Codex plugin hooks (`plugins/chorus/hooks/`)
- `on-session-start.sh` — SessionStart hook (stateless; no caching)
- `on-post-submit-proposal.sh` — PostToolUse for `chorus_pm_submit_proposal`
- `on-post-submit-for-verify.sh` — PostToolUse for `chorus_submit_for_verify`
- `chorus-mcp-call.sh` — shared MCP-over-HTTP helper (bump `clientInfo.version` on release)
- `hook-output.sh` — stdout-formatting helper

**Codex has no SubagentStart/Stop events** — do not try to port lifecycle hooks from the Claude Code plugin. Instead, session management is documented as a main-agent responsibility in `plugins/chorus/skills/develop/SKILL.md` and `$yolo`.

### OpenClaw plugin — TypeScript runtime, not bash hooks (`packages/openclaw-plugin/src/`)

OpenClaw has **no bash hooks at all**. Its real-time behavior is a compiled TypeScript extension declared in `package.json`'s `openclaw` block (`extensions: ["./src/index.ts"]`, `runtimeExtensions: ["./dist/index.js"]`):

- `index.ts` — entry point / activation (`activation.onStartup` in `openclaw.plugin.json`)
- `mcp-client.ts`, `mcp-registration.ts` — registers Chorus MCP tools (namespaced `chorus__<tool>`)
- `sse-listener.ts`, `event-router.ts`, `wake.ts` — SSE event stream → agent wake (the OpenClaw analogue of the other plugins' notification hooks)
- `config.ts`, `commands.ts` — config schema handling and slash commands

After modifying the runtime: `cd packages/openclaw-plugin && npm run typecheck && npm run test`. A **linked** install loads `src/` directly via jiti; an **npm** install requires the compiled `dist/` (`npm run build`). Bash 3.2 rules do **not** apply here (it's TypeScript, not shell). Two known runtime gotchas: a linked install loads TS via jiti (no `dist`) while an npm install needs compiled `dist` + `runtimeExtensions`; and SSE→agent wake requires `activation.onStartup` + `runEmbeddedAgent` with an explicit provider/model.

### Kiro plugin hooks (`public/kiro-plugin/bin/`)

Kiro hooks are bash (Bash-3.2 rules apply) but declared **inside `agents/chorus.json`** (Kiro has no standalone `hooks.json`), and each hook's STDOUT is added to the agent context as **plain text** (there is no `additionalContext` JSON envelope like Claude Code). Scripts:

- `on-agent-spawn.sh` — `agentSpawn` hook: `chorus_checkin` → startup context (owner/permissions/idea-tracker). "Not configured" if Chorus env is unset; never aborts the spawn.
- `on-stop.sh` — `stop` hook: best-effort session heartbeat/checkout; never blocks the turn.
- `on-post-submit-proposal.sh` / `on-post-submit-for-verify.sh` / `on-post-verify-task.sh` — `postToolUse` hooks matched to `@chorus/chorus_pm_submit_proposal` / `@chorus/chorus_submit_for_verify` / `@chorus/chorus_admin_verify_task`; emit a nudge to spawn the matching reviewer subagent. Exit 0 with no output if no parseable UUID.
- `chorus-api.sh` — the reused MCP-over-HTTP wrapper (bump the hardcoded `clientInfo.version` on release, like Codex's `chorus-mcp-call.sh`). Hook scripts reference it via a path relative to their own location, never a hard-coded repo path.
- `test-syntax.sh` — Bash-3.2 parse + mock-event smoke test harness.

Hook `command` strings in the repo `chorus.json` use the `__CHORUS_BIN__` placeholder; `public/install-kiro.sh` copies `bin/*.sh` into `<KIRO_DIR>/chorus-bin/`, `chmod +x`, and substitutes `__CHORUS_BIN__` with that absolute path. **Never commit a concrete machine path in the repo copy** — only the installed copy is concretized.

### Pi extension (`packages/chorus-pi/extensions/`)

Pi uses a native TypeScript extension instead of lifecycle bash hooks:

- `session_start` performs checkin, OpenSpec detection, and startup notification.
- `before_agent_start` injects checkin context once.
- Mutable `tool_call` on `subagent_spawn` creates a session only for `worker` and injects its UUID/workflow.
- `tool_result` and `tool_execution_end` map spawned agent IDs, close sessions, and emit reviewer nudges.
- `session_shutdown` retries retained session closures.

Keep pure parsing/path/banner helpers in `lib/lib.ts`. Preserve failed-close mappings for shutdown retry, and do not create sessions for scout/planner/reviewer/custom agents. The OpenSpec shell wrapper must resolve both env and `.mcp.json` configuration, but it is not a lifecycle hook.

After modifying the Pi package, run `cd packages/chorus-pi && bash test/all.sh`. This covers static checks, pure helper tests, and extension-event tests; update `test/verify-pi-session.md` when behavior requires live Pi verification.

**CRITICAL: All hook scripts MUST be compatible with Bash 3.2.** macOS ships with `/bin/bash` 3.2 (due to GPL licensing) and Claude Code + Codex + Kiro all use it to execute hooks. Do NOT use Bash 4+ features:

| Bash 4+ (FORBIDDEN) | Bash 3.2 alternative |
|---------------------|---------------------|
| `${VAR,,}` (lowercase) | `$(printf '%s' "$VAR" \| tr '[:upper:]' '[:lower:]')` |
| `${VAR^^}` (uppercase) | `$(printf '%s' "$VAR" \| tr '[:lower:]' '[:upper:]')` |
| `declare -A` (associative arrays) | Use separate variables or `jq` |
| `readarray` / `mapfile` | `while IFS= read -r line` loop |
| `\|&` (pipe stderr) | `2>&1 \|` |
| `&>>` (append both) | `>> file 2>&1` |

After modifying:
1. Run `/bin/bash public/chorus-plugin/bin/test-syntax.sh` (Claude Code + Codex hooks) and `/bin/bash public/kiro-plugin/bin/test-syntax.sh` (Kiro hooks) on macOS to verify Bash 3.2 compatibility — OpenClaw and Pi lifecycle code are TypeScript
2. Test locally: `claude --plugin-dir public/chorus-plugin` (Claude Code), install via `codex plugin install` and reload (Codex), re-run `install-kiro.sh` (Kiro), or `pi install "$PWD/packages/chorus-pi"` from the Chorus checkout (Pi)
3. Bump plugin version for whichever packages changed (all affected packages)
4. Users must restart the affected agent after updating (Kiro: re-run the installer; Pi: reinstall the package)

## Testing Plugin Changes

```bash
# Claude Code — load plugin locally (no install needed)
claude --plugin-dir public/chorus-plugin
# Or update installed plugin
/plugin update chorus@chorus-plugins
# Verify plugin loaded
/plugin list

# OpenClaw — typecheck + test the TypeScript runtime
cd packages/openclaw-plugin && npm run typecheck && npm run test

# Kiro — dry-run the installer into a throwaway HOME, then assert the tree
HOME=$(mktemp -d) CHORUS_URL=https://example.com CHORUS_API_KEY=cho_test \
  bash public/install-kiro.sh < /dev/null
# verify hook scripts parse under Bash 3.2
bash public/kiro-plugin/bin/test-syntax.sh

# Pi — static checks + helper and extension-event tests
cd packages/chorus-pi && bash test/all.sh

# dsh — typecheck + lint + unit tests + published-bundle validation
cd packages/chorus-dsh && pnpm run typecheck && pnpm run lint && pnpm test && pnpm run check:package

# Hermes — pytest (fakes only; node for prompt parity; no Hermes install needed)
python -m pytest packages/chorus-hermes/chorus/tests
```

## Hermes Agent port

Source: `packages/chorus-hermes/`. User docs: `packages/chorus-hermes/README.md` (install, configure,
troubleshooting, Hermes compatibility notes with file:line evidence) and `docs/CONNECT_HERMES{,.zh}.md`.
Design/spec: `openspec/changes/add-hermes-plugin/`. Verified against Hermes commit `2b52acc2d`.

### Layout — two directories, installed separately

```
packages/chorus-hermes/
  README.md
  chorus/                       ← native plugin, installed as ~/.hermes/plugins/chorus
    plugin.yaml                 ← name: chorus, kind: standalone (no "general" kind), version, provides_hooks
    __init__.py                 ← register(ctx): hooks, skills, gateway platform, approval transport
    chorus_hermes/              ← config, mcp_client, rest, hooks, reminders, spec_mode, skills (reviewer guard),
                                  sse, router, prompts (port of cli/prompts.mjs), adapter, turns, approval
    skills/<name>/SKILL.md      ← independent Hermes copy (see below)
    tests/                      ← pytest; fixtures/ holds the prompt-parity fixtures + node renderer
  chorus-mcp/                   ← portable package, installed as ~/.hermes/plugins/chorus-mcp
    plugin.json                 ← name: chorus-mcp, version
    mcp.json                    ← chorus server, LITERAL http://localhost:8637/api/mcp + Bearer ${CHORUS_API_KEY}
```

Why two: Hermes declares MCP servers only in portable packages, and only a native plugin can register
hooks, skills, a platform and an approval transport. Keep `mcp.json` out of `chorus/` and Python out of
`chorus-mcp/` (enforced by `test_package.py`). Portable `mcp.json` URLs are not `${VAR}`-expanded, so do
NOT "fix" the literal loopback URL; remote Chorus uses a native `mcp_servers.chorus` entry, which
`chorus agents add` writes (`cli/init/hermes-mcp-config.mjs`). Keep `provides_hooks` in `plugin.yaml`
equal to what `register()` registers, or `hermes plugins validate` fails. Both directories must keep
scanning `safe` — git installs are `community` trust and a `caution` verdict blocks the install (the
scanner flags phrases like "you are … now"; see the `_NOW` workaround in `prompts.py`).

Runtime model: online scheduling through the `chorus` gateway platform (SSE client type `hermes`), like
OpenClaw — **not** the Chorus daemon (`agent-type-map.mjs` maps hermes → `offline`). Operation turns
(`research_requested`, `idea_creation_requested`) are skipped by the router, same as OpenClaw.

### Version sync

Hermes manifests are not npm packages, but they track the **app version** (root `package.json`) and
pytest fails on drift. On every release bump, set `X.Y.Z` in:
- `packages/chorus-hermes/chorus/plugin.yaml` — `version:`
- `packages/chorus-hermes/chorus-mcp/plugin.json` — `"version"`
- every `packages/chorus-hermes/chorus/skills/*/SKILL.md` — `metadata.version` (`test_skills.py::test_frontmatter`)

The gateway's SSE `clientVersion` is read from `plugin.yaml` at runtime (`adapter._client_version`), and `mcp_client.py` sends no `initialize`/`clientInfo` — no version is hardcoded in Python.

### Independent skill copy

`chorus/skills/` is its own copy (started from `packages/chorus-pi/skills`), registered with
`ctx.register_skill` and resolved as `chorus:<name>`. These skills are not listed in `<available_skills>`,
so the session-start Quick Reference tells the model to `skill_view("chorus:<name>")`. When porting a
skill change:
- human questions → elaboration rounds / Chorus comments (no `AskUserQuestion`);
- sub-agents → `delegate_task(goal, context)` (no `Task`/`Agent`/`spawn_agent`/`subagent`);
- no `${CLAUDE_PLUGIN_ROOT}`, Codex paths or `/skill:` syntax;
- reviewers are skills `chorus-{proposal,task,code}-reviewer`. The parent's `delegate_task` context
  starts with `[chorus-reviewer:<kind>]`, and `skills.py` makes that child read-only. Keep the marker kinds
  equal to the skill names (`test_reviewer_marker_kinds_match_skill_names`).
`test_skills.py` greps for foreign-tool references and scan-triggering phrases. Run it after every edit.

### Prompt parity

`chorus_hermes/prompts.py` is a byte-exact Python port of `cli/prompts.mjs` (wake prompts for every
`WAKE_ACTIONS` action). `tests/test_prompts_parity.py` renders `tests/fixtures/prompt_fixtures.json` with
`node tests/fixtures/render_prompts.mjs` and compares the output with the Python output. **Any change to
`cli/prompts.mjs` must be mirrored in `prompts.py`** and, for a new action or edge case, gets a fixture.
The test skips without `node`. CI/dev must have it.

Also mirror changes in `cli/event-router.mjs` (`NOTIFICATION_ACTION_TO_TURN_TRIGGER`) into `router.py`
`ACTION_TO_TURN_TRIGGER`. Reminder text mirrors `plugins/chorus/hooks/on-post-*.sh` (Codex parity).

### Release = the tag's SHA is the install ref

Hermes `--ref` accepts only a full 40-hex commit SHA, and a release commit cannot contain its own SHA. So
**never commit a SHA** (`test_no_commit_sha_committed`). The release tag `vX.Y.Z` *is* the release:
`chorus agents add --agents hermes` (`installHermes` in `cli/init/install-methods.mjs`) resolves
`v<CLI version>` with `git ls-remote` — peeled `^{}` first, then the unpeeled ref (Chorus tags are
lightweight) — and fails closed if the tag is missing. The tag must therefore be pushed to
`github.com/Chorus-AIDLC/Chorus` before the matching CLI is published to npm, or new installs fail.
Users update by re-running `chorus agents add --agents hermes`, which offers to reinstall with
`--force` at the new tag's SHA (automatic under `--yes`), then `hermes gateway restart`.
`chorus upgrade --plugins` does NOT cover Hermes: it only reinstalls daemon-backed agents.

If you change the post-install checklist (`hermesFollowUpChecklist`), update the README "Configure"
section, `docs/CONNECT_HERMES{,.zh}.md`, and the Hermes tab in
`src/components/install-guide/AgentInstallGuide.tsx` (+ `messages/{en,zh,ja,ko}.json`).

An end-to-end run against a real Hermes is optional and not part of version alignment. If the `hermes`
CLI happens to be installed, `hermes plugins validate packages/chorus-hermes/chorus` (and `…/chorus-mcp`)
is a cheap extra manifest check.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.