[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill-aws-labs-cao-plugin":3,"mdc--n9ztlj-key":36,"related-org-aws-labs-cao-plugin":2135,"related-repo-aws-labs-cao-plugin":2315},{"slug":4,"name":4,"fn":5,"description":6,"org":7,"tags":12,"stars":26,"repoUrl":27,"updatedAt":28,"license":29,"forks":30,"topics":31,"repo":32,"sourceUrl":34,"mdContent":35},"cao-plugin","scaffold CAO agent plugins","Create a new CAO (CLI Agent Orchestrator) plugin. Use this skill whenever the user wants to add a plugin that reacts to CAO lifecycle or messaging events, scaffold a plugin package, understand plugin requirements, or integrate an external system (Discord, Slack, dashboards, logging, metrics) with CAO. Also use when the user asks what plugin events are available, how plugin discovery works, or how to install a plugin into a CAO environment.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},"aws-labs","AWS Labs","https:\u002F\u002Fpexgzepcugksgbtrxkhf.supabase.co\u002Fstorage\u002Fv1\u002Fobject\u002Fpublic\u002Forg-logos\u002Faws-labs.png","awslabs",[13,17,20,23],{"name":14,"slug":15,"type":16},"Automation","automation","tag",{"name":18,"slug":19,"type":16},"CLI","cli",{"name":21,"slug":22,"type":16},"Plugin Development","plugin-development",{"name":24,"slug":25,"type":16},"Agents","agents",871,"https:\u002F\u002Fgithub.com\u002Fawslabs\u002Fcli-agent-orchestrator","2026-07-12T08:36:49.784639",null,164,[],{"repoUrl":27,"stars":26,"forks":30,"topics":33,"description":29},[],"https:\u002F\u002Fgithub.com\u002Fawslabs\u002Fcli-agent-orchestrator\u002Ftree\u002FHEAD\u002Fskills\u002Fcao-plugin","---\nname: cao-plugin\ndescription: Create a new CAO (CLI Agent Orchestrator) plugin. Use this skill whenever the user wants to add a plugin that reacts to CAO lifecycle or messaging events, scaffold a plugin package, understand plugin requirements, or integrate an external system (Discord, Slack, dashboards, logging, metrics) with CAO. Also use when the user asks what plugin events are available, how plugin discovery works, or how to install a plugin into a CAO environment.\n---\n\n# CAO Plugin Creator\n\nGuide for creating a new CAO plugin. A \"plugin\" is a Python package installed alongside CAO that subscribes to CAO lifecycle and messaging events via typed async hooks.\n\n## What You're Building\n\nA CAO plugin is a standalone Python package that:\n\n1. **Subclasses `CaoPlugin`** from `cli_agent_orchestrator.plugins`\n2. **Registers async hook methods** with `@hook(\"\u003Cevent_type>\")` decorators\n3. **Is discovered via the `cao.plugins` Python entry-point group** at `cao-server` startup\n4. **Runs fire-and-forget** — plugin exceptions are caught and logged as warnings, never propagated back into CAO\n\nTypical uses: forwarding inter-agent messages to chat apps, logging\u002Fobservability, external dashboards, metrics export, alerting on session or terminal lifecycle.\n\n## Before You Start\n\nGather this information:\n\n- Which events do you need? See `references\u002Fhook-events.md` for the full catalog.\n- Does the plugin need persistent state across events? (HTTP client, DB connection, buffer) — if so, use `setup()` \u002F `teardown()`.\n- How is it configured? v1 has no injected config API — read env vars in `setup()`, optionally via `python-dotenv`.\n- What are the failure semantics of your integration? Remember CAO swallows hook exceptions — you must decide whether to log, retry, or drop on your own.\n\n## Hard Requirements\n\nThese are the non-negotiable contracts a plugin must satisfy to be loaded and dispatched to. Verify each one before calling your plugin complete.\n\n### 1. Package layout\n\nMinimum viable layout:\n\n```\nmy-cao-plugin\u002F\n├── pyproject.toml          # Build config + entry-point declaration\n├── my_cao_plugin\u002F\n│   ├── __init__.py         # Can be empty\n│   └── plugin.py           # Contains the CaoPlugin subclass\n├── tests\u002F                  # Optional but strongly recommended\n│   └── test_plugin.py\n├── env.template            # Optional; only if the plugin reads env vars\n└── README.md               # Optional; install + config instructions for users\n```\n\nSee `examples\u002Fplugins\u002Fcao-discord\u002F` in this repo for a complete reference implementation.\n\n### 2. Plugin class contract\n\n- Must subclass `CaoPlugin` from `cli_agent_orchestrator.plugins`.\n- Must be zero-arg constructible — the registry instantiates plugins with `cls()`. Do NOT define `__init__` with required parameters.\n- May override `async def setup(self) -> None` — called once at `cao-server` startup after instantiation.\n- May override `async def teardown(self) -> None` — called once at `cao-server` shutdown.\n- No other methods are required. Hooks are opt-in via the `@hook` decorator.\n\nA raising `setup()` disables that plugin for the lifetime of the server process (warning logged, other plugins continue to load). A raising `teardown()` is logged and does not stop other plugins from tearing down.\n\n### 3. Hook method contract\n\nA hook method must:\n\n- Be `async def` — sync hooks are not supported in v1.\n- Be decorated with `@hook(\"\u003Cevent_type>\")` using the exact event-type string from `references\u002Fhook-events.md`.\n- Accept exactly one positional argument: the typed event dataclass matching that event type.\n- Return `None`.\n- Be a regular method on the plugin class (the registry discovers hooks via `inspect.getmembers` on the instance).\n\nMultiple hook methods on the same plugin may subscribe to the same event type — each is dispatched independently. Execution order across hooks is not guaranteed.\n\nExceptions raised inside a hook are caught by the registry and logged as warnings. They do not affect CAO's primary operation and they do not stop other hooks for the same event from running.\n\n### 4. Entry-point registration\n\nDeclare the plugin class under the `cao.plugins` entry-point group in `pyproject.toml`:\n\n```toml\n[project.entry-points.\"cao.plugins\"]\nmy_plugin = \"my_cao_plugin.plugin:MyPlugin\"\n```\n\n- The key (`my_plugin`) is the plugin name used in CAO's startup log (`Loaded CAO plugin: my_plugin`). It has no other runtime effect.\n- The value must resolve to a class that is a subclass of `CaoPlugin`. Entry points whose target is not a `CaoPlugin` subclass are skipped with a warning.\n- A single package may declare multiple entry points under `cao.plugins` if it ships multiple plugin classes.\n\nNo CAO-side configuration is required to enable a plugin — installation plus entry-point declaration is sufficient.\n\n### 5. Build system\n\n- Use `hatchling` as the build backend to match CAO's toolchain.\n- Target Python `>=3.10`.\n- Declare `cli-agent-orchestrator` as a runtime dependency so `CaoPlugin`, `hook`, and the event dataclasses are importable.\n- Declare any external libraries (e.g. `httpx`, `aiohttp`, `python-dotenv`) your plugin uses.\n\nMinimal `pyproject.toml`:\n\n```toml\n[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \"my-cao-plugin\"\nversion = \"0.1.0\"\nrequires-python = \">=3.10\"\ndependencies = [\n    \"cli-agent-orchestrator\",\n    # ... your plugin's deps\n]\n\n[project.entry-points.\"cao.plugins\"]\nmy_plugin = \"my_cao_plugin.plugin:MyPlugin\"\n```\n\n### 6. Configuration\n\nCAO does not inject configuration into plugins in v1. Options:\n\n- **Environment variables** — read inside `setup()` with `os.environ.get(...)`. Raise `RuntimeError` with a clear message if a required var is missing so the startup log points the user at the misconfiguration.\n- **`.env` files** — use `python-dotenv` (`load_dotenv(find_dotenv(usecwd=True))`) inside `setup()`. Process-level env vars override `.env` values, which is the expected precedence.\n- **Config files** — load any path you like inside `setup()`; you own the format.\n\nShip an `env.template` alongside the plugin if it reads env vars, documenting every variable, whether it's required, and its default.\n\n### 7. Lifecycle guarantees\n\n- `setup()` is awaited exactly once, after the plugin class is instantiated at server startup.\n- `teardown()` is awaited exactly once at server shutdown, only for plugins whose `setup()` succeeded.\n- There is no hot reload — changes to an installed plugin require restarting `cao-server`.\n- Event dispatch only happens *after* the underlying CAO operation succeeds (e.g. `post_create_terminal` fires after the terminal is persisted, not before).\n- There are no `pre_*` hooks today — you cannot veto or mutate an operation from a plugin.\n\n### 8. Dispatch semantics\n\n- All dispatch is async — hooks are awaited sequentially per event but no ordering is guaranteed across plugins or within a plugin.\n- No filtering API — every hook registered for an event type receives every event of that type; filter inside the handler if you need to.\n- No delivery guarantees beyond \"invoked once per successful dispatch\" — plugins are responsible for their own retries, buffering, and error handling.\n\n## Step-by-Step Implementation\n\n### Step 1: Scaffold the package\n\nCreate the layout from §1. Populate `pyproject.toml` per §5. `references\u002Fplugin-template.md` has a copy-paste skeleton.\n\n### Step 2: Implement the plugin class\n\nIn `my_cao_plugin\u002Fplugin.py`:\n\n```python\nfrom cli_agent_orchestrator.plugins import CaoPlugin, PostSendMessageEvent, hook\n\n\nclass MyPlugin(CaoPlugin):\n    async def setup(self) -> None:\n        # Read config, open clients. Raise on misconfiguration.\n        ...\n\n    async def teardown(self) -> None:\n        # Close clients, flush buffers. Safe to call after failed setup.\n        ...\n\n    @hook(\"post_send_message\")\n    async def on_message(self, event: PostSendMessageEvent) -> None:\n        ...\n```\n\nKeep `teardown()` robust to partial `setup()` failures — guard any resource access with `hasattr` or an initialized flag. See `examples\u002Fplugins\u002Fcao-discord\u002Fcao_discord\u002Fplugin.py` for the pattern.\n\n### Step 3: Pick events and write handlers\n\nConsult `references\u002Fhook-events.md` for the current event catalog, each event type's string, the matching dataclass, and available fields. Import event dataclasses from `cli_agent_orchestrator.plugins`.\n\n### Step 4: Register the entry point\n\nAdd the `[project.entry-points.\"cao.plugins\"]` section to `pyproject.toml` per §4.\n\n### Step 5: Install into the CAO environment\n\nThe plugin must be importable by the same Python environment that runs `cao-server`.\n\n```bash\n# Editable install into the CAO dev virtual environment\nuv pip install -e .\u002Fmy-cao-plugin\n\n# Or, if CAO was installed as a tool:\nuv tool install --reinstall cli-agent-orchestrator \\\n    --with-editable .\u002Fmy-cao-plugin\n```\n\n### Step 6: Verify the plugin loads\n\nRestart `cao-server` and check the startup log for one of:\n\n- `Loaded CAO plugin: my_plugin` — success.\n- `Failed to load plugin 'my_plugin'` — `setup()` raised; check the traceback logged alongside.\n- `Plugin entry point 'my_plugin' is not a CaoPlugin subclass, skipping` — the entry-point target is wrong.\n- `No CAO plugins registered (cao.plugins entry point group is empty)` — the entry point is not declared, or the package was not installed into the same environment as CAO.\n\n### Step 7: Write unit tests\n\nUnit tests for a plugin are straightforward because event dataclasses are zero-arg constructible:\n\n```python\nimport pytest\nfrom cli_agent_orchestrator.plugins import PostSendMessageEvent\nfrom my_cao_plugin.plugin import MyPlugin\n\n\n@pytest.mark.asyncio\nasync def test_on_message_dispatches(monkeypatch):\n    plugin = MyPlugin()\n    await plugin.setup()\n    await plugin.on_message(\n        PostSendMessageEvent(\n            sender=\"a\",\n            receiver=\"b\",\n            message=\"hi\",\n            orchestration_type=\"send_message\",\n        )\n    )\n    await plugin.teardown()\n```\n\nFor plugins that make HTTP calls, use `httpx.MockTransport` (see `examples\u002Fplugins\u002Fcao-discord\u002Ftests\u002Ftest_plugin.py`) rather than real network calls. Assert side-effects (requests sent, logs emitted) — do not assert CAO-side dispatch wiring, which is covered by CAO's own registry tests.\n\n## Install & Verify\n\nReload the plugin after code changes:\n\n```bash\n# Stop cao-server, then reinstall if dependencies changed:\nuv pip install -e .\u002Fmy-cao-plugin --force-reinstall --no-deps\n# Then restart cao-server.\n```\n\nTroubleshooting checklist if `Loaded CAO plugin:` does not appear:\n\n- [ ] Is the package installed in the same venv that runs `cao-server`? (`uv pip list | grep my-cao-plugin`)\n- [ ] Does `pyproject.toml` contain `[project.entry-points.\"cao.plugins\"]`?\n- [ ] Does the entry-point value point to an actual `CaoPlugin` subclass? (`python -c \"from my_cao_plugin.plugin import MyPlugin; print(MyPlugin.__mro__)\"`)\n- [ ] Did `setup()` raise? Check `cao-server` logs for a `Failed to load plugin` warning.\n- [ ] If using `.env`, is it in the directory where `cao-server` was launched (or a parent)?\n\n## File Checklist\n\nWhen your plugin is complete, verify:\n\n- [ ] `pyproject.toml` — `hatchling` build backend, `cli-agent-orchestrator` dependency, `cao.plugins` entry point\n- [ ] `my_cao_plugin\u002F__init__.py` — present (can be empty)\n- [ ] `my_cao_plugin\u002Fplugin.py` — `CaoPlugin` subclass with zero-arg construction and at least one `@hook`-decorated async method\n- [ ] `env.template` — if any env vars are read\n- [ ] `tests\u002Ftest_plugin.py` — setup\u002Fteardown happy and failure paths; at least one test per hook method\n- [ ] `README.md` — install commands, config vars, troubleshooting\n- [ ] Plugin installs and shows `Loaded CAO plugin:` in `cao-server` startup logs\n\n## References\n\n- `references\u002Fhook-events.md` — Full catalog of supported event types, their string identifiers, and event dataclass fields.\n- `references\u002Fplugin-template.md` — Annotated minimal plugin skeleton.\n- `examples\u002Fplugins\u002Fcao-discord\u002F` — Complete reference plugin (webhook forwarder with `.env` config, HTTP client lifecycle, unit tests).\n- `docs\u002Ffeat-plugin-hooks-design.md` — Full design document for the plugin system.\n",{"data":37,"body":38},{"name":4,"description":6},{"type":39,"children":40},"root",[41,50,56,63,68,151,156,162,167,229,235,240,247,252,264,277,283,375,394,400,405,469,474,479,485,505,534,589,594,600,682,693,831,837,842,939,952,958,1033,1039,1057,1063,1069,1089,1095,1107,1230,1265,1271,1289,1295,1315,1321,1332,1439,1445,1457,1510,1516,1521,1673,1694,1700,1705,1762,1775,1907,1913,1918,2072,2078,2129],{"type":42,"tag":43,"props":44,"children":46},"element","h1",{"id":45},"cao-plugin-creator",[47],{"type":48,"value":49},"text","CAO Plugin Creator",{"type":42,"tag":51,"props":52,"children":53},"p",{},[54],{"type":48,"value":55},"Guide for creating a new CAO plugin. A \"plugin\" is a Python package installed alongside CAO that subscribes to CAO lifecycle and messaging events via typed async hooks.",{"type":42,"tag":57,"props":58,"children":60},"h2",{"id":59},"what-youre-building",[61],{"type":48,"value":62},"What You're Building",{"type":42,"tag":51,"props":64,"children":65},{},[66],{"type":48,"value":67},"A CAO plugin is a standalone Python package that:",{"type":42,"tag":69,"props":70,"children":71},"ol",{},[72,97,115,141],{"type":42,"tag":73,"props":74,"children":75},"li",{},[76,89,91],{"type":42,"tag":77,"props":78,"children":79},"strong",{},[80,82],{"type":48,"value":81},"Subclasses ",{"type":42,"tag":83,"props":84,"children":86},"code",{"className":85},[],[87],{"type":48,"value":88},"CaoPlugin",{"type":48,"value":90}," from ",{"type":42,"tag":83,"props":92,"children":94},{"className":93},[],[95],{"type":48,"value":96},"cli_agent_orchestrator.plugins",{"type":42,"tag":73,"props":98,"children":99},{},[100,105,107,113],{"type":42,"tag":77,"props":101,"children":102},{},[103],{"type":48,"value":104},"Registers async hook methods",{"type":48,"value":106}," with ",{"type":42,"tag":83,"props":108,"children":110},{"className":109},[],[111],{"type":48,"value":112},"@hook(\"\u003Cevent_type>\")",{"type":48,"value":114}," decorators",{"type":42,"tag":73,"props":116,"children":117},{},[118,131,133,139],{"type":42,"tag":77,"props":119,"children":120},{},[121,123,129],{"type":48,"value":122},"Is discovered via the ",{"type":42,"tag":83,"props":124,"children":126},{"className":125},[],[127],{"type":48,"value":128},"cao.plugins",{"type":48,"value":130}," Python entry-point group",{"type":48,"value":132}," at ",{"type":42,"tag":83,"props":134,"children":136},{"className":135},[],[137],{"type":48,"value":138},"cao-server",{"type":48,"value":140}," startup",{"type":42,"tag":73,"props":142,"children":143},{},[144,149],{"type":42,"tag":77,"props":145,"children":146},{},[147],{"type":48,"value":148},"Runs fire-and-forget",{"type":48,"value":150}," — plugin exceptions are caught and logged as warnings, never propagated back into CAO",{"type":42,"tag":51,"props":152,"children":153},{},[154],{"type":48,"value":155},"Typical uses: forwarding inter-agent messages to chat apps, logging\u002Fobservability, external dashboards, metrics export, alerting on session or terminal lifecycle.",{"type":42,"tag":57,"props":157,"children":159},{"id":158},"before-you-start",[160],{"type":48,"value":161},"Before You Start",{"type":42,"tag":51,"props":163,"children":164},{},[165],{"type":48,"value":166},"Gather this information:",{"type":42,"tag":168,"props":169,"children":170},"ul",{},[171,184,205,224],{"type":42,"tag":73,"props":172,"children":173},{},[174,176,182],{"type":48,"value":175},"Which events do you need? See ",{"type":42,"tag":83,"props":177,"children":179},{"className":178},[],[180],{"type":48,"value":181},"references\u002Fhook-events.md",{"type":48,"value":183}," for the full catalog.",{"type":42,"tag":73,"props":185,"children":186},{},[187,189,195,197,203],{"type":48,"value":188},"Does the plugin need persistent state across events? (HTTP client, DB connection, buffer) — if so, use ",{"type":42,"tag":83,"props":190,"children":192},{"className":191},[],[193],{"type":48,"value":194},"setup()",{"type":48,"value":196}," \u002F ",{"type":42,"tag":83,"props":198,"children":200},{"className":199},[],[201],{"type":48,"value":202},"teardown()",{"type":48,"value":204},".",{"type":42,"tag":73,"props":206,"children":207},{},[208,210,215,217,223],{"type":48,"value":209},"How is it configured? v1 has no injected config API — read env vars in ",{"type":42,"tag":83,"props":211,"children":213},{"className":212},[],[214],{"type":48,"value":194},{"type":48,"value":216},", optionally via ",{"type":42,"tag":83,"props":218,"children":220},{"className":219},[],[221],{"type":48,"value":222},"python-dotenv",{"type":48,"value":204},{"type":42,"tag":73,"props":225,"children":226},{},[227],{"type":48,"value":228},"What are the failure semantics of your integration? Remember CAO swallows hook exceptions — you must decide whether to log, retry, or drop on your own.",{"type":42,"tag":57,"props":230,"children":232},{"id":231},"hard-requirements",[233],{"type":48,"value":234},"Hard Requirements",{"type":42,"tag":51,"props":236,"children":237},{},[238],{"type":48,"value":239},"These are the non-negotiable contracts a plugin must satisfy to be loaded and dispatched to. Verify each one before calling your plugin complete.",{"type":42,"tag":241,"props":242,"children":244},"h3",{"id":243},"_1-package-layout",[245],{"type":48,"value":246},"1. Package layout",{"type":42,"tag":51,"props":248,"children":249},{},[250],{"type":48,"value":251},"Minimum viable layout:",{"type":42,"tag":253,"props":254,"children":258},"pre",{"className":255,"code":257,"language":48},[256],"language-text","my-cao-plugin\u002F\n├── pyproject.toml          # Build config + entry-point declaration\n├── my_cao_plugin\u002F\n│   ├── __init__.py         # Can be empty\n│   └── plugin.py           # Contains the CaoPlugin subclass\n├── tests\u002F                  # Optional but strongly recommended\n│   └── test_plugin.py\n├── env.template            # Optional; only if the plugin reads env vars\n└── README.md               # Optional; install + config instructions for users\n",[259],{"type":42,"tag":83,"props":260,"children":262},{"__ignoreMap":261},"",[263],{"type":48,"value":257},{"type":42,"tag":51,"props":265,"children":266},{},[267,269,275],{"type":48,"value":268},"See ",{"type":42,"tag":83,"props":270,"children":272},{"className":271},[],[273],{"type":48,"value":274},"examples\u002Fplugins\u002Fcao-discord\u002F",{"type":48,"value":276}," in this repo for a complete reference implementation.",{"type":42,"tag":241,"props":278,"children":280},{"id":279},"_2-plugin-class-contract",[281],{"type":48,"value":282},"2. Plugin class contract",{"type":42,"tag":168,"props":284,"children":285},{},[286,303,324,344,362],{"type":42,"tag":73,"props":287,"children":288},{},[289,291,296,297,302],{"type":48,"value":290},"Must subclass ",{"type":42,"tag":83,"props":292,"children":294},{"className":293},[],[295],{"type":48,"value":88},{"type":48,"value":90},{"type":42,"tag":83,"props":298,"children":300},{"className":299},[],[301],{"type":48,"value":96},{"type":48,"value":204},{"type":42,"tag":73,"props":304,"children":305},{},[306,308,314,316,322],{"type":48,"value":307},"Must be zero-arg constructible — the registry instantiates plugins with ",{"type":42,"tag":83,"props":309,"children":311},{"className":310},[],[312],{"type":48,"value":313},"cls()",{"type":48,"value":315},". Do NOT define ",{"type":42,"tag":83,"props":317,"children":319},{"className":318},[],[320],{"type":48,"value":321},"__init__",{"type":48,"value":323}," with required parameters.",{"type":42,"tag":73,"props":325,"children":326},{},[327,329,335,337,342],{"type":48,"value":328},"May override ",{"type":42,"tag":83,"props":330,"children":332},{"className":331},[],[333],{"type":48,"value":334},"async def setup(self) -> None",{"type":48,"value":336}," — called once at ",{"type":42,"tag":83,"props":338,"children":340},{"className":339},[],[341],{"type":48,"value":138},{"type":48,"value":343}," startup after instantiation.",{"type":42,"tag":73,"props":345,"children":346},{},[347,348,354,355,360],{"type":48,"value":328},{"type":42,"tag":83,"props":349,"children":351},{"className":350},[],[352],{"type":48,"value":353},"async def teardown(self) -> None",{"type":48,"value":336},{"type":42,"tag":83,"props":356,"children":358},{"className":357},[],[359],{"type":48,"value":138},{"type":48,"value":361}," shutdown.",{"type":42,"tag":73,"props":363,"children":364},{},[365,367,373],{"type":48,"value":366},"No other methods are required. Hooks are opt-in via the ",{"type":42,"tag":83,"props":368,"children":370},{"className":369},[],[371],{"type":48,"value":372},"@hook",{"type":48,"value":374}," decorator.",{"type":42,"tag":51,"props":376,"children":377},{},[378,380,385,387,392],{"type":48,"value":379},"A raising ",{"type":42,"tag":83,"props":381,"children":383},{"className":382},[],[384],{"type":48,"value":194},{"type":48,"value":386}," disables that plugin for the lifetime of the server process (warning logged, other plugins continue to load). A raising ",{"type":42,"tag":83,"props":388,"children":390},{"className":389},[],[391],{"type":48,"value":202},{"type":48,"value":393}," is logged and does not stop other plugins from tearing down.",{"type":42,"tag":241,"props":395,"children":397},{"id":396},"_3-hook-method-contract",[398],{"type":48,"value":399},"3. Hook method contract",{"type":42,"tag":51,"props":401,"children":402},{},[403],{"type":48,"value":404},"A hook method must:",{"type":42,"tag":168,"props":406,"children":407},{},[408,421,439,444,456],{"type":42,"tag":73,"props":409,"children":410},{},[411,413,419],{"type":48,"value":412},"Be ",{"type":42,"tag":83,"props":414,"children":416},{"className":415},[],[417],{"type":48,"value":418},"async def",{"type":48,"value":420}," — sync hooks are not supported in v1.",{"type":42,"tag":73,"props":422,"children":423},{},[424,426,431,433,438],{"type":48,"value":425},"Be decorated with ",{"type":42,"tag":83,"props":427,"children":429},{"className":428},[],[430],{"type":48,"value":112},{"type":48,"value":432}," using the exact event-type string from ",{"type":42,"tag":83,"props":434,"children":436},{"className":435},[],[437],{"type":48,"value":181},{"type":48,"value":204},{"type":42,"tag":73,"props":440,"children":441},{},[442],{"type":48,"value":443},"Accept exactly one positional argument: the typed event dataclass matching that event type.",{"type":42,"tag":73,"props":445,"children":446},{},[447,449,455],{"type":48,"value":448},"Return ",{"type":42,"tag":83,"props":450,"children":452},{"className":451},[],[453],{"type":48,"value":454},"None",{"type":48,"value":204},{"type":42,"tag":73,"props":457,"children":458},{},[459,461,467],{"type":48,"value":460},"Be a regular method on the plugin class (the registry discovers hooks via ",{"type":42,"tag":83,"props":462,"children":464},{"className":463},[],[465],{"type":48,"value":466},"inspect.getmembers",{"type":48,"value":468}," on the instance).",{"type":42,"tag":51,"props":470,"children":471},{},[472],{"type":48,"value":473},"Multiple hook methods on the same plugin may subscribe to the same event type — each is dispatched independently. Execution order across hooks is not guaranteed.",{"type":42,"tag":51,"props":475,"children":476},{},[477],{"type":48,"value":478},"Exceptions raised inside a hook are caught by the registry and logged as warnings. They do not affect CAO's primary operation and they do not stop other hooks for the same event from running.",{"type":42,"tag":241,"props":480,"children":482},{"id":481},"_4-entry-point-registration",[483],{"type":48,"value":484},"4. Entry-point registration",{"type":42,"tag":51,"props":486,"children":487},{},[488,490,495,497,503],{"type":48,"value":489},"Declare the plugin class under the ",{"type":42,"tag":83,"props":491,"children":493},{"className":492},[],[494],{"type":48,"value":128},{"type":48,"value":496}," entry-point group in ",{"type":42,"tag":83,"props":498,"children":500},{"className":499},[],[501],{"type":48,"value":502},"pyproject.toml",{"type":48,"value":504},":",{"type":42,"tag":253,"props":506,"children":510},{"className":507,"code":508,"language":509,"meta":261,"style":261},"language-toml shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","[project.entry-points.\"cao.plugins\"]\nmy_plugin = \"my_cao_plugin.plugin:MyPlugin\"\n","toml",[511],{"type":42,"tag":83,"props":512,"children":513},{"__ignoreMap":261},[514,525],{"type":42,"tag":515,"props":516,"children":519},"span",{"class":517,"line":518},"line",1,[520],{"type":42,"tag":515,"props":521,"children":522},{},[523],{"type":48,"value":524},"[project.entry-points.\"cao.plugins\"]\n",{"type":42,"tag":515,"props":526,"children":528},{"class":517,"line":527},2,[529],{"type":42,"tag":515,"props":530,"children":531},{},[532],{"type":48,"value":533},"my_plugin = \"my_cao_plugin.plugin:MyPlugin\"\n",{"type":42,"tag":168,"props":535,"children":536},{},[537,558,577],{"type":42,"tag":73,"props":538,"children":539},{},[540,542,548,550,556],{"type":48,"value":541},"The key (",{"type":42,"tag":83,"props":543,"children":545},{"className":544},[],[546],{"type":48,"value":547},"my_plugin",{"type":48,"value":549},") is the plugin name used in CAO's startup log (",{"type":42,"tag":83,"props":551,"children":553},{"className":552},[],[554],{"type":48,"value":555},"Loaded CAO plugin: my_plugin",{"type":48,"value":557},"). It has no other runtime effect.",{"type":42,"tag":73,"props":559,"children":560},{},[561,563,568,570,575],{"type":48,"value":562},"The value must resolve to a class that is a subclass of ",{"type":42,"tag":83,"props":564,"children":566},{"className":565},[],[567],{"type":48,"value":88},{"type":48,"value":569},". Entry points whose target is not a ",{"type":42,"tag":83,"props":571,"children":573},{"className":572},[],[574],{"type":48,"value":88},{"type":48,"value":576}," subclass are skipped with a warning.",{"type":42,"tag":73,"props":578,"children":579},{},[580,582,587],{"type":48,"value":581},"A single package may declare multiple entry points under ",{"type":42,"tag":83,"props":583,"children":585},{"className":584},[],[586],{"type":48,"value":128},{"type":48,"value":588}," if it ships multiple plugin classes.",{"type":42,"tag":51,"props":590,"children":591},{},[592],{"type":48,"value":593},"No CAO-side configuration is required to enable a plugin — installation plus entry-point declaration is sufficient.",{"type":42,"tag":241,"props":595,"children":597},{"id":596},"_5-build-system",[598],{"type":48,"value":599},"5. Build system",{"type":42,"tag":168,"props":601,"children":602},{},[603,616,628,656],{"type":42,"tag":73,"props":604,"children":605},{},[606,608,614],{"type":48,"value":607},"Use ",{"type":42,"tag":83,"props":609,"children":611},{"className":610},[],[612],{"type":48,"value":613},"hatchling",{"type":48,"value":615}," as the build backend to match CAO's toolchain.",{"type":42,"tag":73,"props":617,"children":618},{},[619,621,627],{"type":48,"value":620},"Target Python ",{"type":42,"tag":83,"props":622,"children":624},{"className":623},[],[625],{"type":48,"value":626},">=3.10",{"type":48,"value":204},{"type":42,"tag":73,"props":629,"children":630},{},[631,633,639,641,646,648,654],{"type":48,"value":632},"Declare ",{"type":42,"tag":83,"props":634,"children":636},{"className":635},[],[637],{"type":48,"value":638},"cli-agent-orchestrator",{"type":48,"value":640}," as a runtime dependency so ",{"type":42,"tag":83,"props":642,"children":644},{"className":643},[],[645],{"type":48,"value":88},{"type":48,"value":647},", ",{"type":42,"tag":83,"props":649,"children":651},{"className":650},[],[652],{"type":48,"value":653},"hook",{"type":48,"value":655},", and the event dataclasses are importable.",{"type":42,"tag":73,"props":657,"children":658},{},[659,661,667,668,674,675,680],{"type":48,"value":660},"Declare any external libraries (e.g. ",{"type":42,"tag":83,"props":662,"children":664},{"className":663},[],[665],{"type":48,"value":666},"httpx",{"type":48,"value":647},{"type":42,"tag":83,"props":669,"children":671},{"className":670},[],[672],{"type":48,"value":673},"aiohttp",{"type":48,"value":647},{"type":42,"tag":83,"props":676,"children":678},{"className":677},[],[679],{"type":48,"value":222},{"type":48,"value":681},") your plugin uses.",{"type":42,"tag":51,"props":683,"children":684},{},[685,687,692],{"type":48,"value":686},"Minimal ",{"type":42,"tag":83,"props":688,"children":690},{"className":689},[],[691],{"type":48,"value":502},{"type":48,"value":504},{"type":42,"tag":253,"props":694,"children":696},{"className":507,"code":695,"language":509,"meta":261,"style":261},"[build-system]\nrequires = [\"hatchling\"]\nbuild-backend = \"hatchling.build\"\n\n[project]\nname = \"my-cao-plugin\"\nversion = \"0.1.0\"\nrequires-python = \">=3.10\"\ndependencies = [\n    \"cli-agent-orchestrator\",\n    # ... your plugin's deps\n]\n\n[project.entry-points.\"cao.plugins\"]\nmy_plugin = \"my_cao_plugin.plugin:MyPlugin\"\n",[697],{"type":42,"tag":83,"props":698,"children":699},{"__ignoreMap":261},[700,708,716,725,735,744,753,762,771,780,789,798,807,815,823],{"type":42,"tag":515,"props":701,"children":702},{"class":517,"line":518},[703],{"type":42,"tag":515,"props":704,"children":705},{},[706],{"type":48,"value":707},"[build-system]\n",{"type":42,"tag":515,"props":709,"children":710},{"class":517,"line":527},[711],{"type":42,"tag":515,"props":712,"children":713},{},[714],{"type":48,"value":715},"requires = [\"hatchling\"]\n",{"type":42,"tag":515,"props":717,"children":719},{"class":517,"line":718},3,[720],{"type":42,"tag":515,"props":721,"children":722},{},[723],{"type":48,"value":724},"build-backend = \"hatchling.build\"\n",{"type":42,"tag":515,"props":726,"children":728},{"class":517,"line":727},4,[729],{"type":42,"tag":515,"props":730,"children":732},{"emptyLinePlaceholder":731},true,[733],{"type":48,"value":734},"\n",{"type":42,"tag":515,"props":736,"children":738},{"class":517,"line":737},5,[739],{"type":42,"tag":515,"props":740,"children":741},{},[742],{"type":48,"value":743},"[project]\n",{"type":42,"tag":515,"props":745,"children":747},{"class":517,"line":746},6,[748],{"type":42,"tag":515,"props":749,"children":750},{},[751],{"type":48,"value":752},"name = \"my-cao-plugin\"\n",{"type":42,"tag":515,"props":754,"children":756},{"class":517,"line":755},7,[757],{"type":42,"tag":515,"props":758,"children":759},{},[760],{"type":48,"value":761},"version = \"0.1.0\"\n",{"type":42,"tag":515,"props":763,"children":765},{"class":517,"line":764},8,[766],{"type":42,"tag":515,"props":767,"children":768},{},[769],{"type":48,"value":770},"requires-python = \">=3.10\"\n",{"type":42,"tag":515,"props":772,"children":774},{"class":517,"line":773},9,[775],{"type":42,"tag":515,"props":776,"children":777},{},[778],{"type":48,"value":779},"dependencies = [\n",{"type":42,"tag":515,"props":781,"children":783},{"class":517,"line":782},10,[784],{"type":42,"tag":515,"props":785,"children":786},{},[787],{"type":48,"value":788},"    \"cli-agent-orchestrator\",\n",{"type":42,"tag":515,"props":790,"children":792},{"class":517,"line":791},11,[793],{"type":42,"tag":515,"props":794,"children":795},{},[796],{"type":48,"value":797},"    # ... your plugin's deps\n",{"type":42,"tag":515,"props":799,"children":801},{"class":517,"line":800},12,[802],{"type":42,"tag":515,"props":803,"children":804},{},[805],{"type":48,"value":806},"]\n",{"type":42,"tag":515,"props":808,"children":810},{"class":517,"line":809},13,[811],{"type":42,"tag":515,"props":812,"children":813},{"emptyLinePlaceholder":731},[814],{"type":48,"value":734},{"type":42,"tag":515,"props":816,"children":818},{"class":517,"line":817},14,[819],{"type":42,"tag":515,"props":820,"children":821},{},[822],{"type":48,"value":524},{"type":42,"tag":515,"props":824,"children":826},{"class":517,"line":825},15,[827],{"type":42,"tag":515,"props":828,"children":829},{},[830],{"type":48,"value":533},{"type":42,"tag":241,"props":832,"children":834},{"id":833},"_6-configuration",[835],{"type":48,"value":836},"6. Configuration",{"type":42,"tag":51,"props":838,"children":839},{},[840],{"type":48,"value":841},"CAO does not inject configuration into plugins in v1. Options:",{"type":42,"tag":168,"props":843,"children":844},{},[845,877,922],{"type":42,"tag":73,"props":846,"children":847},{},[848,853,855,860,861,867,869,875],{"type":42,"tag":77,"props":849,"children":850},{},[851],{"type":48,"value":852},"Environment variables",{"type":48,"value":854}," — read inside ",{"type":42,"tag":83,"props":856,"children":858},{"className":857},[],[859],{"type":48,"value":194},{"type":48,"value":106},{"type":42,"tag":83,"props":862,"children":864},{"className":863},[],[865],{"type":48,"value":866},"os.environ.get(...)",{"type":48,"value":868},". Raise ",{"type":42,"tag":83,"props":870,"children":872},{"className":871},[],[873],{"type":48,"value":874},"RuntimeError",{"type":48,"value":876}," with a clear message if a required var is missing so the startup log points the user at the misconfiguration.",{"type":42,"tag":73,"props":878,"children":879},{},[880,891,893,898,900,906,908,913,915,920],{"type":42,"tag":77,"props":881,"children":882},{},[883,889],{"type":42,"tag":83,"props":884,"children":886},{"className":885},[],[887],{"type":48,"value":888},".env",{"type":48,"value":890}," files",{"type":48,"value":892}," — use ",{"type":42,"tag":83,"props":894,"children":896},{"className":895},[],[897],{"type":48,"value":222},{"type":48,"value":899}," (",{"type":42,"tag":83,"props":901,"children":903},{"className":902},[],[904],{"type":48,"value":905},"load_dotenv(find_dotenv(usecwd=True))",{"type":48,"value":907},") inside ",{"type":42,"tag":83,"props":909,"children":911},{"className":910},[],[912],{"type":48,"value":194},{"type":48,"value":914},". Process-level env vars override ",{"type":42,"tag":83,"props":916,"children":918},{"className":917},[],[919],{"type":48,"value":888},{"type":48,"value":921}," values, which is the expected precedence.",{"type":42,"tag":73,"props":923,"children":924},{},[925,930,932,937],{"type":42,"tag":77,"props":926,"children":927},{},[928],{"type":48,"value":929},"Config files",{"type":48,"value":931}," — load any path you like inside ",{"type":42,"tag":83,"props":933,"children":935},{"className":934},[],[936],{"type":48,"value":194},{"type":48,"value":938},"; you own the format.",{"type":42,"tag":51,"props":940,"children":941},{},[942,944,950],{"type":48,"value":943},"Ship an ",{"type":42,"tag":83,"props":945,"children":947},{"className":946},[],[948],{"type":48,"value":949},"env.template",{"type":48,"value":951}," alongside the plugin if it reads env vars, documenting every variable, whether it's required, and its default.",{"type":42,"tag":241,"props":953,"children":955},{"id":954},"_7-lifecycle-guarantees",[956],{"type":48,"value":957},"7. Lifecycle guarantees",{"type":42,"tag":168,"props":959,"children":960},{},[961,971,988,999,1020],{"type":42,"tag":73,"props":962,"children":963},{},[964,969],{"type":42,"tag":83,"props":965,"children":967},{"className":966},[],[968],{"type":48,"value":194},{"type":48,"value":970}," is awaited exactly once, after the plugin class is instantiated at server startup.",{"type":42,"tag":73,"props":972,"children":973},{},[974,979,981,986],{"type":42,"tag":83,"props":975,"children":977},{"className":976},[],[978],{"type":48,"value":202},{"type":48,"value":980}," is awaited exactly once at server shutdown, only for plugins whose ",{"type":42,"tag":83,"props":982,"children":984},{"className":983},[],[985],{"type":48,"value":194},{"type":48,"value":987}," succeeded.",{"type":42,"tag":73,"props":989,"children":990},{},[991,993,998],{"type":48,"value":992},"There is no hot reload — changes to an installed plugin require restarting ",{"type":42,"tag":83,"props":994,"children":996},{"className":995},[],[997],{"type":48,"value":138},{"type":48,"value":204},{"type":42,"tag":73,"props":1000,"children":1001},{},[1002,1004,1010,1012,1018],{"type":48,"value":1003},"Event dispatch only happens ",{"type":42,"tag":1005,"props":1006,"children":1007},"em",{},[1008],{"type":48,"value":1009},"after",{"type":48,"value":1011}," the underlying CAO operation succeeds (e.g. ",{"type":42,"tag":83,"props":1013,"children":1015},{"className":1014},[],[1016],{"type":48,"value":1017},"post_create_terminal",{"type":48,"value":1019}," fires after the terminal is persisted, not before).",{"type":42,"tag":73,"props":1021,"children":1022},{},[1023,1025,1031],{"type":48,"value":1024},"There are no ",{"type":42,"tag":83,"props":1026,"children":1028},{"className":1027},[],[1029],{"type":48,"value":1030},"pre_*",{"type":48,"value":1032}," hooks today — you cannot veto or mutate an operation from a plugin.",{"type":42,"tag":241,"props":1034,"children":1036},{"id":1035},"_8-dispatch-semantics",[1037],{"type":48,"value":1038},"8. Dispatch semantics",{"type":42,"tag":168,"props":1040,"children":1041},{},[1042,1047,1052],{"type":42,"tag":73,"props":1043,"children":1044},{},[1045],{"type":48,"value":1046},"All dispatch is async — hooks are awaited sequentially per event but no ordering is guaranteed across plugins or within a plugin.",{"type":42,"tag":73,"props":1048,"children":1049},{},[1050],{"type":48,"value":1051},"No filtering API — every hook registered for an event type receives every event of that type; filter inside the handler if you need to.",{"type":42,"tag":73,"props":1053,"children":1054},{},[1055],{"type":48,"value":1056},"No delivery guarantees beyond \"invoked once per successful dispatch\" — plugins are responsible for their own retries, buffering, and error handling.",{"type":42,"tag":57,"props":1058,"children":1060},{"id":1059},"step-by-step-implementation",[1061],{"type":48,"value":1062},"Step-by-Step Implementation",{"type":42,"tag":241,"props":1064,"children":1066},{"id":1065},"step-1-scaffold-the-package",[1067],{"type":48,"value":1068},"Step 1: Scaffold the package",{"type":42,"tag":51,"props":1070,"children":1071},{},[1072,1074,1079,1081,1087],{"type":48,"value":1073},"Create the layout from §1. Populate ",{"type":42,"tag":83,"props":1075,"children":1077},{"className":1076},[],[1078],{"type":48,"value":502},{"type":48,"value":1080}," per §5. ",{"type":42,"tag":83,"props":1082,"children":1084},{"className":1083},[],[1085],{"type":48,"value":1086},"references\u002Fplugin-template.md",{"type":48,"value":1088}," has a copy-paste skeleton.",{"type":42,"tag":241,"props":1090,"children":1092},{"id":1091},"step-2-implement-the-plugin-class",[1093],{"type":48,"value":1094},"Step 2: Implement the plugin class",{"type":42,"tag":51,"props":1096,"children":1097},{},[1098,1100,1106],{"type":48,"value":1099},"In ",{"type":42,"tag":83,"props":1101,"children":1103},{"className":1102},[],[1104],{"type":48,"value":1105},"my_cao_plugin\u002Fplugin.py",{"type":48,"value":504},{"type":42,"tag":253,"props":1108,"children":1112},{"className":1109,"code":1110,"language":1111,"meta":261,"style":261},"language-python shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","from cli_agent_orchestrator.plugins import CaoPlugin, PostSendMessageEvent, hook\n\n\nclass MyPlugin(CaoPlugin):\n    async def setup(self) -> None:\n        # Read config, open clients. Raise on misconfiguration.\n        ...\n\n    async def teardown(self) -> None:\n        # Close clients, flush buffers. Safe to call after failed setup.\n        ...\n\n    @hook(\"post_send_message\")\n    async def on_message(self, event: PostSendMessageEvent) -> None:\n        ...\n","python",[1113],{"type":42,"tag":83,"props":1114,"children":1115},{"__ignoreMap":261},[1116,1124,1131,1138,1146,1154,1162,1170,1177,1185,1193,1200,1207,1215,1223],{"type":42,"tag":515,"props":1117,"children":1118},{"class":517,"line":518},[1119],{"type":42,"tag":515,"props":1120,"children":1121},{},[1122],{"type":48,"value":1123},"from cli_agent_orchestrator.plugins import CaoPlugin, PostSendMessageEvent, hook\n",{"type":42,"tag":515,"props":1125,"children":1126},{"class":517,"line":527},[1127],{"type":42,"tag":515,"props":1128,"children":1129},{"emptyLinePlaceholder":731},[1130],{"type":48,"value":734},{"type":42,"tag":515,"props":1132,"children":1133},{"class":517,"line":718},[1134],{"type":42,"tag":515,"props":1135,"children":1136},{"emptyLinePlaceholder":731},[1137],{"type":48,"value":734},{"type":42,"tag":515,"props":1139,"children":1140},{"class":517,"line":727},[1141],{"type":42,"tag":515,"props":1142,"children":1143},{},[1144],{"type":48,"value":1145},"class MyPlugin(CaoPlugin):\n",{"type":42,"tag":515,"props":1147,"children":1148},{"class":517,"line":737},[1149],{"type":42,"tag":515,"props":1150,"children":1151},{},[1152],{"type":48,"value":1153},"    async def setup(self) -> None:\n",{"type":42,"tag":515,"props":1155,"children":1156},{"class":517,"line":746},[1157],{"type":42,"tag":515,"props":1158,"children":1159},{},[1160],{"type":48,"value":1161},"        # Read config, open clients. Raise on misconfiguration.\n",{"type":42,"tag":515,"props":1163,"children":1164},{"class":517,"line":755},[1165],{"type":42,"tag":515,"props":1166,"children":1167},{},[1168],{"type":48,"value":1169},"        ...\n",{"type":42,"tag":515,"props":1171,"children":1172},{"class":517,"line":764},[1173],{"type":42,"tag":515,"props":1174,"children":1175},{"emptyLinePlaceholder":731},[1176],{"type":48,"value":734},{"type":42,"tag":515,"props":1178,"children":1179},{"class":517,"line":773},[1180],{"type":42,"tag":515,"props":1181,"children":1182},{},[1183],{"type":48,"value":1184},"    async def teardown(self) -> None:\n",{"type":42,"tag":515,"props":1186,"children":1187},{"class":517,"line":782},[1188],{"type":42,"tag":515,"props":1189,"children":1190},{},[1191],{"type":48,"value":1192},"        # Close clients, flush buffers. Safe to call after failed setup.\n",{"type":42,"tag":515,"props":1194,"children":1195},{"class":517,"line":791},[1196],{"type":42,"tag":515,"props":1197,"children":1198},{},[1199],{"type":48,"value":1169},{"type":42,"tag":515,"props":1201,"children":1202},{"class":517,"line":800},[1203],{"type":42,"tag":515,"props":1204,"children":1205},{"emptyLinePlaceholder":731},[1206],{"type":48,"value":734},{"type":42,"tag":515,"props":1208,"children":1209},{"class":517,"line":809},[1210],{"type":42,"tag":515,"props":1211,"children":1212},{},[1213],{"type":48,"value":1214},"    @hook(\"post_send_message\")\n",{"type":42,"tag":515,"props":1216,"children":1217},{"class":517,"line":817},[1218],{"type":42,"tag":515,"props":1219,"children":1220},{},[1221],{"type":48,"value":1222},"    async def on_message(self, event: PostSendMessageEvent) -> None:\n",{"type":42,"tag":515,"props":1224,"children":1225},{"class":517,"line":825},[1226],{"type":42,"tag":515,"props":1227,"children":1228},{},[1229],{"type":48,"value":1169},{"type":42,"tag":51,"props":1231,"children":1232},{},[1233,1235,1240,1242,1247,1249,1255,1257,1263],{"type":48,"value":1234},"Keep ",{"type":42,"tag":83,"props":1236,"children":1238},{"className":1237},[],[1239],{"type":48,"value":202},{"type":48,"value":1241}," robust to partial ",{"type":42,"tag":83,"props":1243,"children":1245},{"className":1244},[],[1246],{"type":48,"value":194},{"type":48,"value":1248}," failures — guard any resource access with ",{"type":42,"tag":83,"props":1250,"children":1252},{"className":1251},[],[1253],{"type":48,"value":1254},"hasattr",{"type":48,"value":1256}," or an initialized flag. See ",{"type":42,"tag":83,"props":1258,"children":1260},{"className":1259},[],[1261],{"type":48,"value":1262},"examples\u002Fplugins\u002Fcao-discord\u002Fcao_discord\u002Fplugin.py",{"type":48,"value":1264}," for the pattern.",{"type":42,"tag":241,"props":1266,"children":1268},{"id":1267},"step-3-pick-events-and-write-handlers",[1269],{"type":48,"value":1270},"Step 3: Pick events and write handlers",{"type":42,"tag":51,"props":1272,"children":1273},{},[1274,1276,1281,1283,1288],{"type":48,"value":1275},"Consult ",{"type":42,"tag":83,"props":1277,"children":1279},{"className":1278},[],[1280],{"type":48,"value":181},{"type":48,"value":1282}," for the current event catalog, each event type's string, the matching dataclass, and available fields. Import event dataclasses from ",{"type":42,"tag":83,"props":1284,"children":1286},{"className":1285},[],[1287],{"type":48,"value":96},{"type":48,"value":204},{"type":42,"tag":241,"props":1290,"children":1292},{"id":1291},"step-4-register-the-entry-point",[1293],{"type":48,"value":1294},"Step 4: Register the entry point",{"type":42,"tag":51,"props":1296,"children":1297},{},[1298,1300,1306,1308,1313],{"type":48,"value":1299},"Add the ",{"type":42,"tag":83,"props":1301,"children":1303},{"className":1302},[],[1304],{"type":48,"value":1305},"[project.entry-points.\"cao.plugins\"]",{"type":48,"value":1307}," section to ",{"type":42,"tag":83,"props":1309,"children":1311},{"className":1310},[],[1312],{"type":48,"value":502},{"type":48,"value":1314}," per §4.",{"type":42,"tag":241,"props":1316,"children":1318},{"id":1317},"step-5-install-into-the-cao-environment",[1319],{"type":48,"value":1320},"Step 5: Install into the CAO environment",{"type":42,"tag":51,"props":1322,"children":1323},{},[1324,1326,1331],{"type":48,"value":1325},"The plugin must be importable by the same Python environment that runs ",{"type":42,"tag":83,"props":1327,"children":1329},{"className":1328},[],[1330],{"type":48,"value":138},{"type":48,"value":204},{"type":42,"tag":253,"props":1333,"children":1337},{"className":1334,"code":1335,"language":1336,"meta":261,"style":261},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","# Editable install into the CAO dev virtual environment\nuv pip install -e .\u002Fmy-cao-plugin\n\n# Or, if CAO was installed as a tool:\nuv tool install --reinstall cli-agent-orchestrator \\\n    --with-editable .\u002Fmy-cao-plugin\n","bash",[1338],{"type":42,"tag":83,"props":1339,"children":1340},{"__ignoreMap":261},[1341,1350,1380,1387,1395,1427],{"type":42,"tag":515,"props":1342,"children":1343},{"class":517,"line":518},[1344],{"type":42,"tag":515,"props":1345,"children":1347},{"style":1346},"--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic",[1348],{"type":48,"value":1349},"# Editable install into the CAO dev virtual environment\n",{"type":42,"tag":515,"props":1351,"children":1352},{"class":517,"line":527},[1353,1359,1365,1370,1375],{"type":42,"tag":515,"props":1354,"children":1356},{"style":1355},"--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B",[1357],{"type":48,"value":1358},"uv",{"type":42,"tag":515,"props":1360,"children":1362},{"style":1361},"--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D",[1363],{"type":48,"value":1364}," pip",{"type":42,"tag":515,"props":1366,"children":1367},{"style":1361},[1368],{"type":48,"value":1369}," install",{"type":42,"tag":515,"props":1371,"children":1372},{"style":1361},[1373],{"type":48,"value":1374}," -e",{"type":42,"tag":515,"props":1376,"children":1377},{"style":1361},[1378],{"type":48,"value":1379}," .\u002Fmy-cao-plugin\n",{"type":42,"tag":515,"props":1381,"children":1382},{"class":517,"line":718},[1383],{"type":42,"tag":515,"props":1384,"children":1385},{"emptyLinePlaceholder":731},[1386],{"type":48,"value":734},{"type":42,"tag":515,"props":1388,"children":1389},{"class":517,"line":727},[1390],{"type":42,"tag":515,"props":1391,"children":1392},{"style":1346},[1393],{"type":48,"value":1394},"# Or, if CAO was installed as a tool:\n",{"type":42,"tag":515,"props":1396,"children":1397},{"class":517,"line":737},[1398,1402,1407,1411,1416,1421],{"type":42,"tag":515,"props":1399,"children":1400},{"style":1355},[1401],{"type":48,"value":1358},{"type":42,"tag":515,"props":1403,"children":1404},{"style":1361},[1405],{"type":48,"value":1406}," tool",{"type":42,"tag":515,"props":1408,"children":1409},{"style":1361},[1410],{"type":48,"value":1369},{"type":42,"tag":515,"props":1412,"children":1413},{"style":1361},[1414],{"type":48,"value":1415}," --reinstall",{"type":42,"tag":515,"props":1417,"children":1418},{"style":1361},[1419],{"type":48,"value":1420}," cli-agent-orchestrator",{"type":42,"tag":515,"props":1422,"children":1424},{"style":1423},"--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8",[1425],{"type":48,"value":1426}," \\\n",{"type":42,"tag":515,"props":1428,"children":1429},{"class":517,"line":746},[1430,1435],{"type":42,"tag":515,"props":1431,"children":1432},{"style":1361},[1433],{"type":48,"value":1434},"    --with-editable",{"type":42,"tag":515,"props":1436,"children":1437},{"style":1361},[1438],{"type":48,"value":1379},{"type":42,"tag":241,"props":1440,"children":1442},{"id":1441},"step-6-verify-the-plugin-loads",[1443],{"type":48,"value":1444},"Step 6: Verify the plugin loads",{"type":42,"tag":51,"props":1446,"children":1447},{},[1448,1450,1455],{"type":48,"value":1449},"Restart ",{"type":42,"tag":83,"props":1451,"children":1453},{"className":1452},[],[1454],{"type":48,"value":138},{"type":48,"value":1456}," and check the startup log for one of:",{"type":42,"tag":168,"props":1458,"children":1459},{},[1460,1470,1488,1499],{"type":42,"tag":73,"props":1461,"children":1462},{},[1463,1468],{"type":42,"tag":83,"props":1464,"children":1466},{"className":1465},[],[1467],{"type":48,"value":555},{"type":48,"value":1469}," — success.",{"type":42,"tag":73,"props":1471,"children":1472},{},[1473,1479,1481,1486],{"type":42,"tag":83,"props":1474,"children":1476},{"className":1475},[],[1477],{"type":48,"value":1478},"Failed to load plugin 'my_plugin'",{"type":48,"value":1480}," — ",{"type":42,"tag":83,"props":1482,"children":1484},{"className":1483},[],[1485],{"type":48,"value":194},{"type":48,"value":1487}," raised; check the traceback logged alongside.",{"type":42,"tag":73,"props":1489,"children":1490},{},[1491,1497],{"type":42,"tag":83,"props":1492,"children":1494},{"className":1493},[],[1495],{"type":48,"value":1496},"Plugin entry point 'my_plugin' is not a CaoPlugin subclass, skipping",{"type":48,"value":1498}," — the entry-point target is wrong.",{"type":42,"tag":73,"props":1500,"children":1501},{},[1502,1508],{"type":42,"tag":83,"props":1503,"children":1505},{"className":1504},[],[1506],{"type":48,"value":1507},"No CAO plugins registered (cao.plugins entry point group is empty)",{"type":48,"value":1509}," — the entry point is not declared, or the package was not installed into the same environment as CAO.",{"type":42,"tag":241,"props":1511,"children":1513},{"id":1512},"step-7-write-unit-tests",[1514],{"type":48,"value":1515},"Step 7: Write unit tests",{"type":42,"tag":51,"props":1517,"children":1518},{},[1519],{"type":48,"value":1520},"Unit tests for a plugin are straightforward because event dataclasses are zero-arg constructible:",{"type":42,"tag":253,"props":1522,"children":1524},{"className":1109,"code":1523,"language":1111,"meta":261,"style":261},"import pytest\nfrom cli_agent_orchestrator.plugins import PostSendMessageEvent\nfrom my_cao_plugin.plugin import MyPlugin\n\n\n@pytest.mark.asyncio\nasync def test_on_message_dispatches(monkeypatch):\n    plugin = MyPlugin()\n    await plugin.setup()\n    await plugin.on_message(\n        PostSendMessageEvent(\n            sender=\"a\",\n            receiver=\"b\",\n            message=\"hi\",\n            orchestration_type=\"send_message\",\n        )\n    )\n    await plugin.teardown()\n",[1525],{"type":42,"tag":83,"props":1526,"children":1527},{"__ignoreMap":261},[1528,1536,1544,1552,1559,1566,1574,1582,1590,1598,1606,1614,1622,1630,1638,1646,1655,1664],{"type":42,"tag":515,"props":1529,"children":1530},{"class":517,"line":518},[1531],{"type":42,"tag":515,"props":1532,"children":1533},{},[1534],{"type":48,"value":1535},"import pytest\n",{"type":42,"tag":515,"props":1537,"children":1538},{"class":517,"line":527},[1539],{"type":42,"tag":515,"props":1540,"children":1541},{},[1542],{"type":48,"value":1543},"from cli_agent_orchestrator.plugins import PostSendMessageEvent\n",{"type":42,"tag":515,"props":1545,"children":1546},{"class":517,"line":718},[1547],{"type":42,"tag":515,"props":1548,"children":1549},{},[1550],{"type":48,"value":1551},"from my_cao_plugin.plugin import MyPlugin\n",{"type":42,"tag":515,"props":1553,"children":1554},{"class":517,"line":727},[1555],{"type":42,"tag":515,"props":1556,"children":1557},{"emptyLinePlaceholder":731},[1558],{"type":48,"value":734},{"type":42,"tag":515,"props":1560,"children":1561},{"class":517,"line":737},[1562],{"type":42,"tag":515,"props":1563,"children":1564},{"emptyLinePlaceholder":731},[1565],{"type":48,"value":734},{"type":42,"tag":515,"props":1567,"children":1568},{"class":517,"line":746},[1569],{"type":42,"tag":515,"props":1570,"children":1571},{},[1572],{"type":48,"value":1573},"@pytest.mark.asyncio\n",{"type":42,"tag":515,"props":1575,"children":1576},{"class":517,"line":755},[1577],{"type":42,"tag":515,"props":1578,"children":1579},{},[1580],{"type":48,"value":1581},"async def test_on_message_dispatches(monkeypatch):\n",{"type":42,"tag":515,"props":1583,"children":1584},{"class":517,"line":764},[1585],{"type":42,"tag":515,"props":1586,"children":1587},{},[1588],{"type":48,"value":1589},"    plugin = MyPlugin()\n",{"type":42,"tag":515,"props":1591,"children":1592},{"class":517,"line":773},[1593],{"type":42,"tag":515,"props":1594,"children":1595},{},[1596],{"type":48,"value":1597},"    await plugin.setup()\n",{"type":42,"tag":515,"props":1599,"children":1600},{"class":517,"line":782},[1601],{"type":42,"tag":515,"props":1602,"children":1603},{},[1604],{"type":48,"value":1605},"    await plugin.on_message(\n",{"type":42,"tag":515,"props":1607,"children":1608},{"class":517,"line":791},[1609],{"type":42,"tag":515,"props":1610,"children":1611},{},[1612],{"type":48,"value":1613},"        PostSendMessageEvent(\n",{"type":42,"tag":515,"props":1615,"children":1616},{"class":517,"line":800},[1617],{"type":42,"tag":515,"props":1618,"children":1619},{},[1620],{"type":48,"value":1621},"            sender=\"a\",\n",{"type":42,"tag":515,"props":1623,"children":1624},{"class":517,"line":809},[1625],{"type":42,"tag":515,"props":1626,"children":1627},{},[1628],{"type":48,"value":1629},"            receiver=\"b\",\n",{"type":42,"tag":515,"props":1631,"children":1632},{"class":517,"line":817},[1633],{"type":42,"tag":515,"props":1634,"children":1635},{},[1636],{"type":48,"value":1637},"            message=\"hi\",\n",{"type":42,"tag":515,"props":1639,"children":1640},{"class":517,"line":825},[1641],{"type":42,"tag":515,"props":1642,"children":1643},{},[1644],{"type":48,"value":1645},"            orchestration_type=\"send_message\",\n",{"type":42,"tag":515,"props":1647,"children":1649},{"class":517,"line":1648},16,[1650],{"type":42,"tag":515,"props":1651,"children":1652},{},[1653],{"type":48,"value":1654},"        )\n",{"type":42,"tag":515,"props":1656,"children":1658},{"class":517,"line":1657},17,[1659],{"type":42,"tag":515,"props":1660,"children":1661},{},[1662],{"type":48,"value":1663},"    )\n",{"type":42,"tag":515,"props":1665,"children":1667},{"class":517,"line":1666},18,[1668],{"type":42,"tag":515,"props":1669,"children":1670},{},[1671],{"type":48,"value":1672},"    await plugin.teardown()\n",{"type":42,"tag":51,"props":1674,"children":1675},{},[1676,1678,1684,1686,1692],{"type":48,"value":1677},"For plugins that make HTTP calls, use ",{"type":42,"tag":83,"props":1679,"children":1681},{"className":1680},[],[1682],{"type":48,"value":1683},"httpx.MockTransport",{"type":48,"value":1685}," (see ",{"type":42,"tag":83,"props":1687,"children":1689},{"className":1688},[],[1690],{"type":48,"value":1691},"examples\u002Fplugins\u002Fcao-discord\u002Ftests\u002Ftest_plugin.py",{"type":48,"value":1693},") rather than real network calls. Assert side-effects (requests sent, logs emitted) — do not assert CAO-side dispatch wiring, which is covered by CAO's own registry tests.",{"type":42,"tag":57,"props":1695,"children":1697},{"id":1696},"install-verify",[1698],{"type":48,"value":1699},"Install & Verify",{"type":42,"tag":51,"props":1701,"children":1702},{},[1703],{"type":48,"value":1704},"Reload the plugin after code changes:",{"type":42,"tag":253,"props":1706,"children":1708},{"className":1334,"code":1707,"language":1336,"meta":261,"style":261},"# Stop cao-server, then reinstall if dependencies changed:\nuv pip install -e .\u002Fmy-cao-plugin --force-reinstall --no-deps\n# Then restart cao-server.\n",[1709],{"type":42,"tag":83,"props":1710,"children":1711},{"__ignoreMap":261},[1712,1720,1754],{"type":42,"tag":515,"props":1713,"children":1714},{"class":517,"line":518},[1715],{"type":42,"tag":515,"props":1716,"children":1717},{"style":1346},[1718],{"type":48,"value":1719},"# Stop cao-server, then reinstall if dependencies changed:\n",{"type":42,"tag":515,"props":1721,"children":1722},{"class":517,"line":527},[1723,1727,1731,1735,1739,1744,1749],{"type":42,"tag":515,"props":1724,"children":1725},{"style":1355},[1726],{"type":48,"value":1358},{"type":42,"tag":515,"props":1728,"children":1729},{"style":1361},[1730],{"type":48,"value":1364},{"type":42,"tag":515,"props":1732,"children":1733},{"style":1361},[1734],{"type":48,"value":1369},{"type":42,"tag":515,"props":1736,"children":1737},{"style":1361},[1738],{"type":48,"value":1374},{"type":42,"tag":515,"props":1740,"children":1741},{"style":1361},[1742],{"type":48,"value":1743}," .\u002Fmy-cao-plugin",{"type":42,"tag":515,"props":1745,"children":1746},{"style":1361},[1747],{"type":48,"value":1748}," --force-reinstall",{"type":42,"tag":515,"props":1750,"children":1751},{"style":1361},[1752],{"type":48,"value":1753}," --no-deps\n",{"type":42,"tag":515,"props":1755,"children":1756},{"class":517,"line":718},[1757],{"type":42,"tag":515,"props":1758,"children":1759},{"style":1346},[1760],{"type":48,"value":1761},"# Then restart cao-server.\n",{"type":42,"tag":51,"props":1763,"children":1764},{},[1765,1767,1773],{"type":48,"value":1766},"Troubleshooting checklist if ",{"type":42,"tag":83,"props":1768,"children":1770},{"className":1769},[],[1771],{"type":48,"value":1772},"Loaded CAO plugin:",{"type":48,"value":1774}," does not appear:",{"type":42,"tag":168,"props":1776,"children":1779},{"className":1777},[1778],"contains-task-list",[1780,1807,1830,1853,1884],{"type":42,"tag":73,"props":1781,"children":1784},{"className":1782},[1783],"task-list-item",[1785,1790,1792,1797,1799,1805],{"type":42,"tag":1786,"props":1787,"children":1789},"input",{"disabled":731,"type":1788},"checkbox",[],{"type":48,"value":1791}," Is the package installed in the same venv that runs ",{"type":42,"tag":83,"props":1793,"children":1795},{"className":1794},[],[1796],{"type":48,"value":138},{"type":48,"value":1798},"? (",{"type":42,"tag":83,"props":1800,"children":1802},{"className":1801},[],[1803],{"type":48,"value":1804},"uv pip list | grep my-cao-plugin",{"type":48,"value":1806},")",{"type":42,"tag":73,"props":1808,"children":1810},{"className":1809},[1783],[1811,1814,1816,1821,1823,1828],{"type":42,"tag":1786,"props":1812,"children":1813},{"disabled":731,"type":1788},[],{"type":48,"value":1815}," Does ",{"type":42,"tag":83,"props":1817,"children":1819},{"className":1818},[],[1820],{"type":48,"value":502},{"type":48,"value":1822}," contain ",{"type":42,"tag":83,"props":1824,"children":1826},{"className":1825},[],[1827],{"type":48,"value":1305},{"type":48,"value":1829},"?",{"type":42,"tag":73,"props":1831,"children":1833},{"className":1832},[1783],[1834,1837,1839,1844,1846,1852],{"type":42,"tag":1786,"props":1835,"children":1836},{"disabled":731,"type":1788},[],{"type":48,"value":1838}," Does the entry-point value point to an actual ",{"type":42,"tag":83,"props":1840,"children":1842},{"className":1841},[],[1843],{"type":48,"value":88},{"type":48,"value":1845}," subclass? (",{"type":42,"tag":83,"props":1847,"children":1849},{"className":1848},[],[1850],{"type":48,"value":1851},"python -c \"from my_cao_plugin.plugin import MyPlugin; print(MyPlugin.__mro__)\"",{"type":48,"value":1806},{"type":42,"tag":73,"props":1854,"children":1856},{"className":1855},[1783],[1857,1860,1862,1867,1869,1874,1876,1882],{"type":42,"tag":1786,"props":1858,"children":1859},{"disabled":731,"type":1788},[],{"type":48,"value":1861}," Did ",{"type":42,"tag":83,"props":1863,"children":1865},{"className":1864},[],[1866],{"type":48,"value":194},{"type":48,"value":1868}," raise? Check ",{"type":42,"tag":83,"props":1870,"children":1872},{"className":1871},[],[1873],{"type":48,"value":138},{"type":48,"value":1875}," logs for a ",{"type":42,"tag":83,"props":1877,"children":1879},{"className":1878},[],[1880],{"type":48,"value":1881},"Failed to load plugin",{"type":48,"value":1883}," warning.",{"type":42,"tag":73,"props":1885,"children":1887},{"className":1886},[1783],[1888,1891,1893,1898,1900,1905],{"type":42,"tag":1786,"props":1889,"children":1890},{"disabled":731,"type":1788},[],{"type":48,"value":1892}," If using ",{"type":42,"tag":83,"props":1894,"children":1896},{"className":1895},[],[1897],{"type":48,"value":888},{"type":48,"value":1899},", is it in the directory where ",{"type":42,"tag":83,"props":1901,"children":1903},{"className":1902},[],[1904],{"type":48,"value":138},{"type":48,"value":1906}," was launched (or a parent)?",{"type":42,"tag":57,"props":1908,"children":1910},{"id":1909},"file-checklist",[1911],{"type":48,"value":1912},"File Checklist",{"type":42,"tag":51,"props":1914,"children":1915},{},[1916],{"type":48,"value":1917},"When your plugin is complete, verify:",{"type":42,"tag":168,"props":1919,"children":1921},{"className":1920},[1778],[1922,1958,1974,2002,2017,2033,2049],{"type":42,"tag":73,"props":1923,"children":1925},{"className":1924},[1783],[1926,1929,1931,1936,1937,1942,1944,1949,1951,1956],{"type":42,"tag":1786,"props":1927,"children":1928},{"disabled":731,"type":1788},[],{"type":48,"value":1930}," ",{"type":42,"tag":83,"props":1932,"children":1934},{"className":1933},[],[1935],{"type":48,"value":502},{"type":48,"value":1480},{"type":42,"tag":83,"props":1938,"children":1940},{"className":1939},[],[1941],{"type":48,"value":613},{"type":48,"value":1943}," build backend, ",{"type":42,"tag":83,"props":1945,"children":1947},{"className":1946},[],[1948],{"type":48,"value":638},{"type":48,"value":1950}," dependency, ",{"type":42,"tag":83,"props":1952,"children":1954},{"className":1953},[],[1955],{"type":48,"value":128},{"type":48,"value":1957}," entry point",{"type":42,"tag":73,"props":1959,"children":1961},{"className":1960},[1783],[1962,1965,1966,1972],{"type":42,"tag":1786,"props":1963,"children":1964},{"disabled":731,"type":1788},[],{"type":48,"value":1930},{"type":42,"tag":83,"props":1967,"children":1969},{"className":1968},[],[1970],{"type":48,"value":1971},"my_cao_plugin\u002F__init__.py",{"type":48,"value":1973}," — present (can be empty)",{"type":42,"tag":73,"props":1975,"children":1977},{"className":1976},[1783],[1978,1981,1982,1987,1988,1993,1995,2000],{"type":42,"tag":1786,"props":1979,"children":1980},{"disabled":731,"type":1788},[],{"type":48,"value":1930},{"type":42,"tag":83,"props":1983,"children":1985},{"className":1984},[],[1986],{"type":48,"value":1105},{"type":48,"value":1480},{"type":42,"tag":83,"props":1989,"children":1991},{"className":1990},[],[1992],{"type":48,"value":88},{"type":48,"value":1994}," subclass with zero-arg construction and at least one ",{"type":42,"tag":83,"props":1996,"children":1998},{"className":1997},[],[1999],{"type":48,"value":372},{"type":48,"value":2001},"-decorated async method",{"type":42,"tag":73,"props":2003,"children":2005},{"className":2004},[1783],[2006,2009,2010,2015],{"type":42,"tag":1786,"props":2007,"children":2008},{"disabled":731,"type":1788},[],{"type":48,"value":1930},{"type":42,"tag":83,"props":2011,"children":2013},{"className":2012},[],[2014],{"type":48,"value":949},{"type":48,"value":2016}," — if any env vars are read",{"type":42,"tag":73,"props":2018,"children":2020},{"className":2019},[1783],[2021,2024,2025,2031],{"type":42,"tag":1786,"props":2022,"children":2023},{"disabled":731,"type":1788},[],{"type":48,"value":1930},{"type":42,"tag":83,"props":2026,"children":2028},{"className":2027},[],[2029],{"type":48,"value":2030},"tests\u002Ftest_plugin.py",{"type":48,"value":2032}," — setup\u002Fteardown happy and failure paths; at least one test per hook method",{"type":42,"tag":73,"props":2034,"children":2036},{"className":2035},[1783],[2037,2040,2041,2047],{"type":42,"tag":1786,"props":2038,"children":2039},{"disabled":731,"type":1788},[],{"type":48,"value":1930},{"type":42,"tag":83,"props":2042,"children":2044},{"className":2043},[],[2045],{"type":48,"value":2046},"README.md",{"type":48,"value":2048}," — install commands, config vars, troubleshooting",{"type":42,"tag":73,"props":2050,"children":2052},{"className":2051},[1783],[2053,2056,2058,2063,2065,2070],{"type":42,"tag":1786,"props":2054,"children":2055},{"disabled":731,"type":1788},[],{"type":48,"value":2057}," Plugin installs and shows ",{"type":42,"tag":83,"props":2059,"children":2061},{"className":2060},[],[2062],{"type":48,"value":1772},{"type":48,"value":2064}," in ",{"type":42,"tag":83,"props":2066,"children":2068},{"className":2067},[],[2069],{"type":48,"value":138},{"type":48,"value":2071}," startup logs",{"type":42,"tag":57,"props":2073,"children":2075},{"id":2074},"references",[2076],{"type":48,"value":2077},"References",{"type":42,"tag":168,"props":2079,"children":2080},{},[2081,2091,2101,2118],{"type":42,"tag":73,"props":2082,"children":2083},{},[2084,2089],{"type":42,"tag":83,"props":2085,"children":2087},{"className":2086},[],[2088],{"type":48,"value":181},{"type":48,"value":2090}," — Full catalog of supported event types, their string identifiers, and event dataclass fields.",{"type":42,"tag":73,"props":2092,"children":2093},{},[2094,2099],{"type":42,"tag":83,"props":2095,"children":2097},{"className":2096},[],[2098],{"type":48,"value":1086},{"type":48,"value":2100}," — Annotated minimal plugin skeleton.",{"type":42,"tag":73,"props":2102,"children":2103},{},[2104,2109,2111,2116],{"type":42,"tag":83,"props":2105,"children":2107},{"className":2106},[],[2108],{"type":48,"value":274},{"type":48,"value":2110}," — Complete reference plugin (webhook forwarder with ",{"type":42,"tag":83,"props":2112,"children":2114},{"className":2113},[],[2115],{"type":48,"value":888},{"type":48,"value":2117}," config, HTTP client lifecycle, unit tests).",{"type":42,"tag":73,"props":2119,"children":2120},{},[2121,2127],{"type":42,"tag":83,"props":2122,"children":2124},{"className":2123},[],[2125],{"type":48,"value":2126},"docs\u002Ffeat-plugin-hooks-design.md",{"type":48,"value":2128}," — Full design document for the plugin system.",{"type":42,"tag":2130,"props":2131,"children":2132},"style",{},[2133],{"type":48,"value":2134},"html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"items":2136,"total":2314},[2137,2158,2179,2189,2202,2215,2225,2235,2256,2271,2286,2299],{"slug":2138,"name":2138,"fn":2139,"description":2140,"org":2141,"tags":2142,"stars":2155,"repoUrl":2156,"updatedAt":2157},"agentcore-investigation","investigate Bedrock AgentCore runtime sessions","Investigate Bedrock AgentCore runtime sessions via CloudWatch Logs Insights — resolve session\u002Ftrace IDs, query OTEL spans, filter noise, build timelines. Use when debugging AgentCore agent sessions, tracing tool calls, or analyzing latency.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2143,2146,2149,2152],{"name":2144,"slug":2145,"type":16},"AWS","aws",{"name":2147,"slug":2148,"type":16},"Debugging","debugging",{"name":2150,"slug":2151,"type":16},"Logs","logs",{"name":2153,"slug":2154,"type":16},"Observability","observability",9427,"https:\u002F\u002Fgithub.com\u002Fawslabs\u002Fmcp","2026-07-12T08:37:22.601527",{"slug":2159,"name":2160,"fn":2161,"description":2162,"org":2163,"tags":2164,"stars":2155,"repoUrl":2156,"updatedAt":2178},"amazon-aurora-dsql","amazon aurora dsql","build applications with Aurora DSQL","Build with Aurora DSQL — manage schemas, execute queries, handle migrations, diagnose query plans, load data, and develop applications with a serverless, distributed SQL database. Covers IAM auth, multi-tenant patterns, MySQL-to-DSQL and PostgreSQL-to-DSQL schema conversion, FK replacement code generation, OCC retry patterns, ORM migration (Django\u002FHibernate\u002FRails), DDL operations, query plan explainability, SQL compatibility validation, and bulk data loading. Triggers on phrases like: DSQL, Aurora DSQL, create DSQL table, DSQL schema, migrate to DSQL, distributed SQL database, serverless PostgreSQL-compatible database, DSQL query plan, DSQL EXPLAIN ANALYZE, why is my DSQL query slow, DSQL foreign key, DSQL OCC retry, DSQL multi-region, load into DSQL, load CSV into DSQL, bulk load DSQL, aurora-dsql-loader.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2165,2168,2169,2172,2175],{"name":2166,"slug":2167,"type":16},"Aurora","aurora",{"name":2144,"slug":2145,"type":16},{"name":2170,"slug":2171,"type":16},"Database","database",{"name":2173,"slug":2174,"type":16},"Serverless","serverless",{"name":2176,"slug":2177,"type":16},"SQL","sql","2026-07-12T08:36:45.053393",{"slug":2180,"name":2181,"fn":2161,"description":2162,"org":2182,"tags":2183,"stars":2155,"repoUrl":2156,"updatedAt":2188},"aurora-dsql","aurora dsql",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2184,2185,2186,2187],{"name":2144,"slug":2145,"type":16},{"name":2170,"slug":2171,"type":16},{"name":2173,"slug":2174,"type":16},{"name":2176,"slug":2177,"type":16},"2026-07-12T08:36:42.694299",{"slug":2190,"name":2191,"fn":2161,"description":2162,"org":2192,"tags":2193,"stars":2155,"repoUrl":2156,"updatedAt":2201},"aws-dsql","aws dsql",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2194,2195,2196,2199,2200],{"name":2144,"slug":2145,"type":16},{"name":2170,"slug":2171,"type":16},{"name":2197,"slug":2198,"type":16},"Migration","migration",{"name":2173,"slug":2174,"type":16},{"name":2176,"slug":2177,"type":16},"2026-07-12T08:36:38.584057",{"slug":2203,"name":2204,"fn":2161,"description":2162,"org":2205,"tags":2206,"stars":2155,"repoUrl":2156,"updatedAt":2214},"distributed-postgres","distributed postgres",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2207,2208,2209,2212,2213],{"name":2144,"slug":2145,"type":16},{"name":2170,"slug":2171,"type":16},{"name":2210,"slug":2211,"type":16},"PostgreSQL","postgresql",{"name":2173,"slug":2174,"type":16},{"name":2176,"slug":2177,"type":16},"2026-07-12T08:36:46.530743",{"slug":2216,"name":2217,"fn":2161,"description":2162,"org":2218,"tags":2219,"stars":2155,"repoUrl":2156,"updatedAt":2224},"distributed-sql","distributed sql",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2220,2221,2222,2223],{"name":2144,"slug":2145,"type":16},{"name":2170,"slug":2171,"type":16},{"name":2173,"slug":2174,"type":16},{"name":2176,"slug":2177,"type":16},"2026-07-12T08:36:48.104182",{"slug":2226,"name":2226,"fn":2161,"description":2162,"org":2227,"tags":2228,"stars":2155,"repoUrl":2156,"updatedAt":2234},"dsql",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2229,2230,2231,2232,2233],{"name":2144,"slug":2145,"type":16},{"name":2170,"slug":2171,"type":16},{"name":2197,"slug":2198,"type":16},{"name":2173,"slug":2174,"type":16},{"name":2176,"slug":2177,"type":16},"2026-07-12T08:36:36.374512",{"slug":2236,"name":2236,"fn":2237,"description":2238,"org":2239,"tags":2240,"stars":2253,"repoUrl":2254,"updatedAt":2255},"cost-efficiency-analyzer","analyze cost efficiency and expenses","Analyzes cost structure, cost efficiency, and expense management from P&L data. Use when the user asks about costs, expenses, COGS, operating expenses, cost ratios, cost control, spending efficiency, margin compression from cost side, or wants to understand where money is going. Also use for \"are we spending too much\", \"cost breakdown\", \"expense analysis\", or \"how efficient are our operations\". NOT for revenue or top-line analysis.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2241,2244,2247,2250],{"name":2242,"slug":2243,"type":16},"Accounting","accounting",{"name":2245,"slug":2246,"type":16},"Analytics","analytics",{"name":2248,"slug":2249,"type":16},"Cost Optimization","cost-optimization",{"name":2251,"slug":2252,"type":16},"Finance","finance",3176,"https:\u002F\u002Fgithub.com\u002Fawslabs\u002Fagentcore-samples","2026-07-12T08:40:03.29555",{"slug":2257,"name":2257,"fn":2258,"description":2259,"org":2260,"tags":2261,"stars":2253,"repoUrl":2254,"updatedAt":2270},"executive-financial-briefing","generate executive financial briefings","Generates a concise executive-level financial briefing or summary suitable for a CEO, CFO, or board presentation. Use when the user asks for a summary, briefing, executive summary, board update, financial overview, financial health check, or \"how is the business doing\". Covers the full P&L picture in one page. Also use for \"give me the highlights\", \"what do I need to know\", or \"quick financial update\".",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2262,2263,2264,2267],{"name":2144,"slug":2145,"type":16},{"name":2251,"slug":2252,"type":16},{"name":2265,"slug":2266,"type":16},"Management","management",{"name":2268,"slug":2269,"type":16},"Reporting","reporting","2026-07-12T08:40:02.066471",{"slug":2272,"name":2272,"fn":2273,"description":2274,"org":2275,"tags":2276,"stars":2253,"repoUrl":2254,"updatedAt":2285},"multi-quarter-trend-analysis","analyze multi-quarter financial trends","Analyzes financial trends across multiple quarters by comparing P&L metrics over time. Use when the user wants to see trends, patterns, trajectories, or directional movement across 3 or more quarters. Also use for \"how are we trending\", \"show me the trend\", \"track performance over time\", \"quarter over quarter comparison across all quarters\", or any multi-period longitudinal analysis.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2277,2278,2279,2282],{"name":2245,"slug":2246,"type":16},{"name":2251,"slug":2252,"type":16},{"name":2280,"slug":2281,"type":16},"Financial Statements","financial-statements",{"name":2283,"slug":2284,"type":16},"Variance Analysis","variance-analysis","2026-07-12T08:40:00.79141",{"slug":2287,"name":2287,"fn":2288,"description":2289,"org":2290,"tags":2291,"stars":2253,"repoUrl":2254,"updatedAt":2298},"pdf","process and manipulate PDF documents","Use this skill whenever the user wants to do anything with PDF files. This includes reading or extracting text\u002Ftables from PDFs, combining or merging multiple PDFs into one, splitting PDFs apart, rotating pages, adding watermarks, creating new PDFs, filling PDF forms, encrypting\u002Fdecrypting PDFs, extracting images, and OCR on scanned PDFs to make them searchable. If the user mentions a .pdf file or asks to produce one, use this skill.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2292,2293,2296],{"name":14,"slug":15,"type":16},{"name":2294,"slug":2295,"type":16},"Documents","documents",{"name":2297,"slug":2287,"type":16},"PDF","2026-07-12T08:41:44.135656",{"slug":2300,"name":2300,"fn":2301,"description":2302,"org":2303,"tags":2304,"stars":2253,"repoUrl":2254,"updatedAt":2313},"quarterly-kpi-calculator","calculate quarterly financial KPIs","Calculates quarterly financial KPIs from P&L data. P&L figures can be provided directly by the user or fetched from the financial data MCP server. Use when the user wants KPI calculations such as Gross Margin %, EBITDA Margin %, Operating Expense Ratio, or Revenue Growth % QoQ. Also use for quarterly performance review, P&L analysis, or interpreting financial ratios against benchmarks.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2305,2306,2309,2310],{"name":2242,"slug":2243,"type":16},{"name":2307,"slug":2308,"type":16},"Data Analysis","data-analysis",{"name":2251,"slug":2252,"type":16},{"name":2311,"slug":2312,"type":16},"KPI","kpi","2026-07-12T08:39:59.54971",150,{"items":2316,"total":1648},[2317,2334,2347,2359,2375,2386,2400],{"slug":2318,"name":2318,"fn":2319,"description":2320,"org":2321,"tags":2322,"stars":26,"repoUrl":27,"updatedAt":2333},"add-app-to-server","add interactive UI to MCP servers","This skill should be used when the user asks to \"add an app to my MCP server\", \"add UI to my MCP server\", \"add a view to my MCP tool\", \"enrich MCP tools with UI\", \"add interactive UI to existing server\", \"add MCP Apps to my server\", or needs to add interactive UI capabilities to an existing MCP server that already has tools. Provides guidance for analyzing existing tools and adding MCP Apps UI resources.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2323,2326,2329,2330],{"name":2324,"slug":2325,"type":16},"Frontend","frontend",{"name":2327,"slug":2328,"type":16},"MCP","mcp",{"name":21,"slug":22,"type":16},{"name":2331,"slug":2332,"type":16},"UI Components","ui-components","2026-07-12T08:41:40.4008",{"slug":2335,"name":2335,"fn":2336,"description":2337,"org":2338,"tags":2339,"stars":26,"repoUrl":27,"updatedAt":2346},"agui-author","emit interactive dashboard UI components","Author live dashboard UI from an agent via the `emit_ui` MCP tool. Emit one of six allow-listed components (approval_card, choice_prompt, diff_summary, progress, metric, agent_card) with JSON props and it renders in any AG-UI client watching the fleet. Use when you want the operator to see a decision, a diff, or a status readout instead of scrolling terminal text. Arbitrary HTML\u002Fmarkup is refused.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2340,2341,2344,2345],{"name":24,"slug":25,"type":16},{"name":2342,"slug":2343,"type":16},"Dashboards","dashboards",{"name":2327,"slug":2328,"type":16},{"name":2331,"slug":2332,"type":16},"2026-07-22T05:35:50.851108",{"slug":2348,"name":2348,"fn":2349,"description":2350,"org":2351,"tags":2352,"stars":26,"repoUrl":27,"updatedAt":2358},"cao-agent-routing","route tasks to appropriate CAO agents","Find and select the best installed CAO agent profile for a task before delegating with assign or handoff. Use when a supervisor needs to route coding, documentation, infrastructure, review, research, or other specialist work and the user has not already chosen an agent profile.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2353,2354,2355],{"name":24,"slug":25,"type":16},{"name":2144,"slug":2145,"type":16},{"name":2356,"slug":2357,"type":16},"Orchestration","orchestration","2026-07-25T05:56:34.255071",{"slug":2360,"name":2360,"fn":2361,"description":2362,"org":2363,"tags":2364,"stars":26,"repoUrl":27,"updatedAt":2374},"cao-learning","report task outcomes and distill lessons","Report task outcomes and distill lessons so the team improves across runs — report_outcome after each unit of work, retrospector handoffs at natural boundaries, and applying injected lessons. Use in workflows that run repeatedly over similar work items. Requires memory.learning_enabled; degrade silently when the tools report disabled.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2365,2368,2371],{"name":2366,"slug":2367,"type":16},"Best Practices","best-practices",{"name":2369,"slug":2370,"type":16},"Engineering","engineering",{"name":2372,"slug":2373,"type":16},"Operations","operations","2026-07-29T06:00:28.147989",{"slug":2376,"name":2376,"fn":2377,"description":2378,"org":2379,"tags":2380,"stars":26,"repoUrl":27,"updatedAt":2385},"cao-mcp-apps","operate CAO MCP application surfaces","Enable, operate, and extend CAO's MCP Apps surface — the host-rendered fleet dashboard visible inside MCP App hosts (Claude Desktop, ChatGPT, VS Code Copilot, Goose, Postman). Use when the user says \"enable MCP Apps in CAO\", \"the ui:\u002F\u002Fcao views aren't rendering\", \"rebuild MCP Apps bundles\", \"add a new ui:\u002F\u002Fcao\u002F* view\", or \"configure the MCP Apps OAuth scope layer\". Operates on the CAO_MCP_APPS_ENABLED surface and cao_mcp_apps\u002F build system. Not for the localhost:9889 browser dashboard, not for plugins, providers, or session management.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2381,2382,2383,2384],{"name":24,"slug":25,"type":16},{"name":18,"slug":19,"type":16},{"name":2327,"slug":2328,"type":16},{"name":2331,"slug":2332,"type":16},"2026-07-22T05:35:52.952289",{"slug":2387,"name":2387,"fn":2388,"description":2389,"org":2390,"tags":2391,"stars":26,"repoUrl":27,"updatedAt":2399},"cao-memory","manage durable agent memory and preferences","Store, recall, and forget durable facts with CAO memory — user preferences, project conventions, decisions, and corrections that should persist across sessions and agents. Use proactively to check memory before asking the user, and to save anything worth remembering. Distinct from any provider-native memory.",{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2392,2393,2396],{"name":24,"slug":25,"type":16},{"name":2394,"slug":2395,"type":16},"Memory","memory",{"name":2397,"slug":2398,"type":16},"Productivity","productivity","2026-07-12T08:37:03.180421",{"slug":4,"name":4,"fn":5,"description":6,"org":2401,"tags":2402,"stars":26,"repoUrl":27,"updatedAt":28},{"slug":8,"name":9,"logoUrl":10,"githubOrg":11},[2403,2404,2405,2406],{"name":24,"slug":25,"type":16},{"name":14,"slug":15,"type":16},{"name":18,"slug":19,"type":16},{"name":21,"slug":22,"type":16}]