agentleFS
lbednerlbedner/aegis-stack/.claude/skills/create-plugin/SKILL.md

create-plugin

Skill144 starsChanged 15 months agoMITView on GitHub

Use when building an Aegis Stack plugin, a separate package (aegis-stack-<name>) that renders files into a project through `aegis add <name>`. Covers the scaffold, the plugin spec, the file conventions a project finds on disk, testing inside a generated project, and publishing.

  • Installs packages

We split this skill into the rules it gives an agent

5 rules. 0 are common, 2 are distinctive, and 3 are very specific to this project.

  • 0

    Common

    Most of the 12 edited versions we read keep these.

  • 2

    Distinctive

    This author's own choices. Worth a look.

  • 3

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Use when the work is a new optional capability shipped outside this repo: an integration (a source for the research service, a scraper), or …

    line 16
  2. Very specific:

    Do NOT use for a capability that ships with the framework itself (use the add-service or add-component skill), or for changing the plugin …

    line 20
  3. Very specific:

    Write the template's failing tests first (against the fixtures, with an httpx.MockTransport or a module-level _transport seam, never the …

    line 63
  4. Very specific:

    Write the template modules and declare every owned path in files=FileManifest(primary=[...]).

    line 66
  5. Distinctive:

    Run the plugin for real against the live service once, through the project's CLI or API.

    line 73

“Common” is measured: kept by at least 6 in 10 of the 12 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
SKILL.md

Create plugin

A plugin is a separate Python package whose get_spec() returns a PluginSpec (the same declaration built-in services use) and whose templates render into a project on aegis add <name>. Much of what a plugin contributes needs no wiring: the project finds a module of a known name in every service package on disk.

When to use

Use when the work is a new optional capability shipped outside this repo: an integration (a source for the research service, a scraper), or a service a project opts into by installing a package.

Do NOT use for a capability that ships with the framework itself (use the add-service or add-component skill), or for changing the plugin system in this repo (that is ordinary work in aegis/core/plugins/).

Files that change

In the plugin repository (created by aegis plugins create <name>):

  • pyproject.toml: the aegis.plugins entry point, keywords (aegis-stack-plugin lists it in the directory), the aegis-stack floor.
  • src/aegis_stack_<name>/plugin.py: get_spec(): required_services / required_components, files=FileManifest(primary=[...]), wiring= only for what a convention module does not cover, migrations= only if it owns tables.
  • src/aegis_stack_<name>/templates/{{ project_slug }}/: the files a project receives. Convention modules in app/services/<service>/:
    • __init__.py: CATEGORY = Category.<X> (app.core.parts), the service's concern in the system model (app.core.ontology); App when left out. First-party parts declare it on their spec instead.
    • models.py (or models/): tables, picked up by the model registry.
    • change_types.py: changes a model proposes and a user approves.
    • scheduled_jobs.py: JOBS, a tuple of ServiceJob (app.core.schedule).
    • tools.py: register_tool(...) calls (app.core.tools), for agents and MCP clients.
    • sources.py: register_source(...) (app.services.research.registry), for a research source.
  • src/aegis_stack_<name>/templates/{{ project_slug }}/tests/: the template's tests, which run inside a generated project.
  • tests/test_plugin.py: tests that pin the spec.
  • README.md, LICENSE.

In this repo, only when a plugin needs a hook no convention covers: the convention's discovery in the template (app/core/discovery.py import_modules_named) and docs/plugins/creating.md's convention table.

Procedure

  1. Scaffold: aegis plugins create <name> -d <parent> --author "..." --description "...".
  2. Pick the convention modules the plugin needs; prefer them over wiring=, which a project only honours for the hooks PluginWiring declares.
  3. Record real third-party responses as fixtures under the template's tests/fixtures/<name>/ before writing any mapping code; build the model from what the API actually returns.
  4. Write the template's failing tests first (against the fixtures, with an httpx.MockTransport or a module-level _transport seam, never the network).
  5. Write the template modules and declare every owned path in files=FileManifest(primary=[...]).
  6. Install the plugin into the aegis environment that renders it: uv pip install -e <plugin>; confirm with aegis plugins list.
  7. Generate a project without the plugin's dependencies, run aegis add <name>, and run the template's tests there under the project's gate: uv run pytest <tests> --queryspy-strict --queryspy-baseline .queryspy-baseline.json.
  8. Run the plugin for real against the live service once, through the project's CLI or API.
  9. Copy back anything the project's make fix reformatted in the rendered files, so the template matches what a project receives.
  10. In the plugin repo, make check; then create the repository with the aegis-stack-plugin topic and push. Publishing to PyPI is the separate step in docs/plugins/publishing.md.

Gates

  • make check in the plugin repository (ruff and the spec tests).
  • The template's tests passing inside a generated project after aegis add <name>, under the queryspy strict gate.
  • make lint and make typecheck in that generated project.

Pitfalls

  • Template files that are plain .py (not .jinja) are seen by the plugin repo's own ruff and pytest; the scaffold's extend-exclude and testpaths keep them out, because their import order is the project's and their tests import app.
  • A path rendered by the template but missing from FileManifest.primary stays behind after aegis remove <name>, because removal walks only the manifest.
  • required_services is the real version guard for a plugin that builds on a service: an older aegis-stack without that service refuses the add, while an aegis_version floor set ahead of a release blocks local development on the unreleased version.
  • A timestamp a third-party API returns is often timezone-aware; the project's columns are naive UTC (app.core.time.as_stored), and Postgres refuses an aware value where SQLite accepts it, so test the mapping, not only the SQLite suite.
  • A rendered test that hits the network makes the project's suite flaky and slow; every template test runs on recorded fixtures.

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.