Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ Full details: [docs/skill-authoring.md](docs/skill-authoring.md)

Pure-content repository (JSON manifests + markdown skills) — no build step or package manager. Two skills bundle a script, each with unit tests: `scaffold` (`scripts/scaffold.py`) and `bmm-authoring` (`scripts/check_bmm.py`). Run them with `python3 -m unittest discover -s scripts -p 'test_*.py'` (runs in CI).

- **Validation**: `python3 scripts/validate.py` (Claude + Cursor manifests, version sync, required skill/agent frontmatter fields, component paths, and the `scaffold` template set's integrity — runs in CI; it does not check description length, inventories, or the release tag)
- **Validation**: `python3 scripts/validate.py` (Claude + Cursor manifests, version sync, required skill/agent frontmatter fields, component paths, the `scaffold` template set's integrity, and that the BMM skills name every repo the template set excludes from BMM work — runs in CI; it does not check description length, inventories, or the release tag)
- **Local testing**: [docs/testing.md](docs/testing.md); **versioning/releases**: [docs/versioning.md](docs/versioning.md)
- **End-user installation**: [docs/install.md](docs/install.md)

Expand Down
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,9 +22,26 @@ Notable changes to the plugins in this repository. The format follows [Keep a Ch

### Changed

- `scaffold` template set revision 4, README: a root `README.adoc` no longer stands in for `README.md`. The plan reports it as `convert` (a rewrite, or a merge when `README.md` exists too). The skill then runs `git mv README.adoc README.md` and rewrites the markup as GitHub Markdown, by the new reference `references/readme-markdown.md`; the script writes nothing for it. `README`, `README.rst` and `README.txt` are still accepted as they are, and a repo without a README still gets one.
- `scaffold` revision 4, ITS repositories: ITS-XML, ITS-BMM, ITS-JSON and ITS-REST are excluded from BMM work, because they hold no model of their own.
- The scaffold never offers them a `computable/BMM` schema, even in a new repository, and does not ask Docker.
- A non-empty `bmm_schema_id` or `bmm_dependencies` is reported as invalid there.
- `bmm-authoring` creates and edits no schema there and sends the change to the component repo.
- `class-generation` and `regen-classes` run no `bmm-publisher` command there.
- `scripts/validate.py` checks that these four skills name every repo that the template set excludes (`files[bmm].excluded_components`).
- `class-generation`, `authoring` and `scaffold` send BMM schema edits to `bmm-authoring`.
- `docs/prompting-guide.md` sends the class-change prompts to `bmm-authoring` and adds a section for a new component's schema.

### Fixed

- `scaffold` ([#10](https://github.com/openEHR/ai-plugins/issues/10)): the class-table command in `AGENTS.md` loaded only BASE with `-d`, so AM 2.4.0, which also includes LANG, rendered its LANG types (`STATEMENT_SET`, `ASSERTION`, ...) as dangling `/classes/<Type>` links.
- It now mounts and loads every schema the BMM includes, from the sibling clone of that schema's component. The list is the new variable `bmm_dependencies`: BASE first for every component but BASE, then the other includes.
- BASE stays first even when a schema does not include it. TERM includes nothing, yet needs it for its `String` links.
- The list is read from the schema on every run (`prefer_inferred`), so a later include is picked up.
- An include whose sibling clone is missing is left out, with a warning.
- `base_bmm_schema_id` is now derived from that list and can no longer be passed with `--var`.
- The template engine gains `{{#each name}}…{{/each}}` over a comma-separated value, and a `component` filter (`openehr_lang_1.1.0` → `LANG`).

## openehr-specs 0.5.0 (2026-10-08)

### Changed
Expand Down
2 changes: 1 addition & 1 deletion docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ For Cursor, copy `plugins/openehr-specs` to `~/.cursor/plugins/local/openehr-spe

## Validation

- **Manifest and frontmatter validation**: `python3 scripts/validate.py` (CI runs it on every PR) checks the Claude and Cursor JSON manifests, name and version sync, declared component paths, and the marketplace `owner.name`. For every skill and subagent it checks that `name` and `description` exist and that `name` matches the directory or file name. It also runs the `scaffold` template-set check (`scaffold.py check`: templates exist and render, revisions are contiguous, every revision has a migration file, and the template digest matches the latest revision). It does not check description length, `disable-model-invocation`, the README inventories, or the release tag.
- **Manifest and frontmatter validation**: `python3 scripts/validate.py` (CI runs it on every PR) checks the Claude and Cursor JSON manifests, name and version sync, declared component paths, and the marketplace `owner.name`. For every skill and subagent it checks that `name` and `description` exist and that `name` matches the directory or file name. It also runs the `scaffold` template-set check (`scaffold.py check`: templates exist and render, revisions are contiguous, every revision has a migration file, and the template digest matches the latest revision), and that the `bmm-authoring`, `class-generation`, `regen-classes` and `scaffold` skills each name every repo the template set excludes from BMM work. It does not check description length, `disable-model-invocation`, the README inventories, or the release tag.
- **Script tests**: `python3 -m unittest discover -s scripts -p 'test_*.py'` (CI runs it on every PR) tests both bundled scripts in temporary directories. For `scaffold` it covers the template engine, each file strategy, variable inference, and upgrades across revisions. For the `bmm-authoring` checker it covers each finding and exit code, a clean run on the worked example, and a wrongly typed value at any node of the worked example, which must give a finding and not a traceback. Run it after changing either script, the template set, or the worked example.
- **Built-in manifest check**: `claude plugin validate plugins/openehr-specs` validates the plugin manifest; add `--strict` to treat warnings as errors.
- **Structural validation**: after creating or modifying plugin components, run the `plugin-dev:plugin-validator` agent. It checks `plugin.json`, the marketplace entry, directory layout, and frontmatter. The agent comes from the `plugin-dev` plugin, which this repo enables in `.claude/settings.json`.
Expand Down
4 changes: 2 additions & 2 deletions plugins/openehr-specs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ Three skills run only when you type them: they do not auto-trigger, and their de

| Skill | Argument | Action |
|-------|----------|--------|
| `/openehr-specs:regen-classes` | `<schema-id> [-d <dependency-schema> ...]` | Regenerate class tables and diagrams via `bmm-publisher` (needs Docker) |
| `/openehr-specs:scaffold` | `[component id]` | Initialise a specification repo, or upgrade an existing one, to the standard file set: `AGENTS.md` (plugin table, build, Jira-key commit conventions), `.claude/` settings, `manifest.json`, `.gitignore`, `.asciidoctorconfig`, `LICENSE`, `README.md`, and in a new repo `computable/BMM/<id>.bmm.json` (the schema the `bmm-publisher` image bundles for the component, or an empty one). `.claude/settings.json` registers the `openehr` marketplace, enables four plugins and pre-approves `git add`, `git commit`, `git diff`, WebFetch on `specifications.openehr.org` and three read-only Atlassian MCP calls. Plans before it writes, and records a revision in `.claude/scaffold.json` so later runs can upgrade the repo. Needs `python3`, plus Docker for the new-repo BMM check; written for Claude Code (its script path comes from `${CLAUDE_SKILL_DIR}`) |
| `/openehr-specs:regen-classes` | `<schema-id> [-d <dependency-schema> ...]` | Regenerate class tables and diagrams via `bmm-publisher` (needs Docker; stops in the ITS repos) |
| `/openehr-specs:scaffold` | `[component id]` | Initialise a specification repo, or upgrade an existing one, to the standard file set: `AGENTS.md` (plugin table, build, Jira-key commit conventions), `.claude/` settings, `manifest.json`, `.gitignore`, `.asciidoctorconfig`, `LICENSE`, `README.md` (a root `README.adoc` is rewritten as Markdown), and in a new repo `computable/BMM/<id>.bmm.json` (the schema the `bmm-publisher` image bundles for the component, or an empty one; never in ITS-XML, ITS-BMM, ITS-JSON or ITS-REST). `.claude/settings.json` registers the `openehr` marketplace, enables four plugins and pre-approves `git add`, `git commit`, `git diff`, WebFetch on `specifications.openehr.org` and three read-only Atlassian MCP calls. Plans before it writes, and records a revision in `.claude/scaffold.json` so later runs can upgrade the repo. Needs `python3`, plus Docker for the new-repo BMM check; written for Claude Code (its script path comes from `${CLAUDE_SKILL_DIR}`) |
| `/openehr-specs:publish` | `<component>` | Build a local HTML preview with the published `ghcr.io/openehr/asciidoctor` image (needs Docker and a sibling `specifications-AA_GLOBAL` checkout; checks that the HTML was rebuilt and the class tables resolved) |

## Subagents
Expand Down
14 changes: 12 additions & 2 deletions plugins/openehr-specs/skills/bmm-authoring/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,9 @@ description: >
`specifications-XX` repo. This skill should be used when the user asks to write a BMM schema from
scratch, add, rename or remove a class, attribute, function, invariant or enumeration in the
model, set the schema header or includes, or check or fix a `.bmm.json`. Not for regenerating class
tables (use class-generation), spec prose (use content-patterns), or archetype/template work
(openehr-assistant plugin).
tables (use class-generation), spec prose (use content-patterns), archetype/template work
(openehr-assistant plugin), or the ITS-XML, ITS-BMM, ITS-JSON and ITS-REST repos, which hold no
model of their own.
allowed-tools:
- "Bash(python3 ${CLAUDE_SKILL_DIR}/scripts/check_bmm.py *)"
---
Expand All @@ -25,6 +26,15 @@ ODIN and YAML. Change the model here, and only here.
- **Worked example**: `${CLAUDE_SKILL_DIR}/assets/openehr_demo_0.1.0.bmm.json`, one of each common construct.
- **Checker**: `${CLAUDE_SKILL_DIR}/scripts/check_bmm.py` (Python 3.8+, standard library only, read-only).

## Where It Applies

Only in a component repo. Check `manifest.json` `id` (or the directory name `specifications-<id>`)
first. In the ITS-XML, ITS-BMM, ITS-JSON and ITS-REST repos, create and edit no BMM schema. Say that
the repo holds no model of its own, and make the change in the component's
`specifications-<COMPONENT>/computable/BMM/` instead. ITS-BMM copies its JSON from there and
generates its ODIN and YAML with its own `make generate`. Running the read-only checker on a file
there is fine.

## Related Skills

- **class-generation**: regenerate and place the class tables after a change
Expand Down
7 changes: 6 additions & 1 deletion plugins/openehr-specs/skills/class-generation/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,8 @@ image::ROOT:uml/classes/COMPOSITION.svg[]

## Typical Workflow

1. **Locate the schema files.** Work from the component repo's root. The schema is
1. **Locate the schema files.** Work from the component repo's root; in an ITS repo, stop (see
Guardrails). The schema is
`computable/BMM/openehr_<component>_<version>.bmm.json`; each dependency comes from its sibling clone
(RM, AM, LANG and TERM classes refer to BASE types:
`../specifications-BASE/computable/BMM/openehr_base_<version>.bmm.json`). When a file is missing, say
Expand Down Expand Up @@ -130,6 +131,10 @@ the user can run `/openehr-specs:publish <component>`.

## Guardrails

- **Not in the ITS repos.** The ITS-XML, ITS-BMM, ITS-JSON and ITS-REST repos (`manifest.json` `id`,
or the directory name `specifications-<id>`) hold no model of their own. Run no `bmm-publisher`
command there, neither class tables nor any other format. Say so, and stop. ITS-BMM regenerates its
ODIN and YAML with its own `make generate`, which its `AGENTS.md` describes.
- **Generated output is never hand-edited.** Fix the BMM schema (see `bmm-authoring`) and regenerate.
A clean run does not prove the schema is right: `bmm-publisher` renders many modelling mistakes as
type `Any` or drops them without a message, so run the `bmm-authoring` checker first.
Expand Down
14 changes: 9 additions & 5 deletions plugins/openehr-specs/skills/regen-classes/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,19 @@ depends on BASE: `openehr_rm_1.2.0 -d openehr_base_1.3.0`). The layouts and the

If `$ARGUMENTS` is empty, ask for the schema id and stop.

1. Run `docker --version`. If Docker is missing, say it is required (the image and its dev container
1. Read `manifest.json` `id` (or take the directory name `specifications-<id>`). In the ITS-XML,
ITS-BMM, ITS-JSON and ITS-REST repos, say that they hold no model of their own and that
`bmm-publisher` is not run there, and stop. ITS-BMM regenerates its ODIN and YAML with its own
`make generate`.
2. Run `docker --version`. If Docker is missing, say it is required (the image and its dev container
both run on it) and stop.
2. Map each id to its file and Glob that it exists: the schema to `computable/BMM/<id>.bmm.json`, and a
3. Map each id to its file and Glob that it exists: the schema to `computable/BMM/<id>.bmm.json`, and a
dependency `openehr_<component>_<version>` to the sibling clone's
`../specifications-<COMPONENT>/computable/BMM/<id>.bmm.json` (component upper-cased). If one is
missing, name it and stop; never fall back to the bundled id.
3. Write to `./out` unless the user names another target. Ask before writing into a
4. Write to `./out` unless the user names another target. Ask before writing into a
`specifications-XX` repo's `docs/UML/`.
4. Pick the command by layout (step 2 of the `class-generation` workflow): `legacy-adoc -o
5. Pick the command by layout (step 2 of the `class-generation` workflow): `legacy-adoc -o
/app/output/UML/classes` when the spec's chapters contain `include::{uml_export_dir}/classes/`
(the tables land in `./out/UML/classes`), otherwise `asciidoc`. Mount each file read-only under
`/in/`, pass the paths, and map output ownership to the host user (RM with BASE shown):
Expand All @@ -40,6 +44,6 @@ If `$ARGUMENTS` is empty, ask for the schema id and stop.
```
Add one mount and one `-d /in/<id>.bmm.json` per dependency. The `-v` log lists each file read:
check that every one is under `/in/`.
5. List what was produced (`classes/`, `effective/`, diagram SVGs). Do not commit. Generated files are
6. List what was produced (`classes/`, `effective/`, diagram SVGs). Do not commit. Generated files are
never hand-edited: change the BMM and regenerate. To place the output, follow the
`class-generation` skill.
25 changes: 20 additions & 5 deletions plugins/openehr-specs/skills/scaffold/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ allowed-tools:
- "Bash(python3 ${CLAUDE_SKILL_DIR}/scripts/scaffold.py render *)"
- "Bash(python3 ${CLAUDE_SKILL_DIR}/scripts/scaffold.py diff *)"
- "Bash(git init *)"
- "Bash(git mv README.adoc README.md)"
- Read
disable-model-invocation: true
---
Expand All @@ -22,7 +23,12 @@ use the directory that holds this SKILL.md.

The set covers AsciiDoc specification repos, with an optional BMM schema. In an OpenAPI repo such as
`specifications-ITS-REST`, pin `agents`, `claude-md` and `asciidoctorconfig` (step 4): they are
AsciiDoc-specific. Pin `bmm` too when a new repo has no model.
AsciiDoc-specific. The script never offers a BMM schema to the ITS-XML, ITS-BMM, ITS-JSON and
ITS-REST repos, which hold no model of their own, and rejects a `bmm_schema_id` there. Pin `bmm` in
any other new repo that has no model.

The root README is `README.md`. A `README.adoc` in its place is planned as `convert` and rewritten as
Markdown by hand (step 5), because the script cannot translate AsciiDoc.

A new repository (plan `mode` `init`) also gets `computable/BMM/<bmm_schema_id>.bmm.json` (file id
`bmm`). The script asks the local `ghcr.io/openehr/bmm-publisher` image for the schemas it bundles
Expand All @@ -45,7 +51,7 @@ Existing repositories are not offered a schema.
2. **Variables.** When `status` is `needs_input`, ask for each missing or invalid variable, then plan
again with `--var name=value`. A variable whose source is `default` or starts with `inferred` is a
guess. Ask the user to confirm the ones that matter (`license`, `jira_project`, `bmm_schema_id`,
`base_bmm_schema_id`, `default_branch`, `published_url`, and `first_release` only when the plan creates
`bmm_dependencies`, `default_branch`, `published_url`, and `first_release` only when the plan creates
`manifest.json` or a new empty BMM schema, the files that use it) and list the rest in one line. In a
new repository, `bmm_schema_id` comes from `inferred: bmm-publisher image` or `inferred: new empty
schema`. Never invent a
Expand All @@ -68,8 +74,12 @@ Existing repositories are not offered a schema.
Docker, or to run `docker pull ghcr.io/openehr/bmm-publisher` (a large download; ask first), and
plan again. Or create the empty schema the `detail` names with `--var bmm_schema_id=<id>`. Applying
anyway writes the other files without a schema; `--overwrite bmm` adds one later.
- `exists`, `exists-alternative` (for example `README.adoc`), `unchanged`, `pinned`, `skipped` (no BMM
schema is offered to an existing repository; `--overwrite bmm` asks for one): nothing to do.
- `convert` (only `readme`): the `convert_from` file (`README.adoc`) stands where `README.md`
belongs. Say that step 5 rewrites it as Markdown, or merges it into the existing `README.md` when
`detail` says so, and ask.
- `exists`, `exists-alternative` (for example `README.rst`), `unchanged`, `pinned`, `skipped` (no BMM
schema for an existing repository, where `--overwrite bmm` asks for one, nor for an ITS
repository): nothing to do.
A `conflict` whose `detail` says the file is a symbolic link cannot be resolved with `--overwrite`:
the user has to replace the link first.
- `conflict`: `detail` says whether the file was edited since it was scaffolded or simply differs.
Expand Down Expand Up @@ -100,7 +110,12 @@ Existing repositories are not offered a schema.
`openehr-specs:authoring`). When an empty BMM schema was created, point out that classes go into it
(skill `openehr-specs:bmm-authoring`, which also checks it) and the class tables are generated
from it (skill `openehr-specs:class-generation`); until then
bmm-publisher warns about the empty package. Do not commit; if the user asks for a commit message, follow the
bmm-publisher warns about the empty package.
For a `convert` the user agreed to, run `git mv README.adoc README.md` (a plain rename outside git),
then rewrite the file as GitHub Markdown by `${CLAUDE_SKILL_DIR}/references/readme-markdown.md`,
keeping all of its content. When `README.md` already existed, merge the AsciiDoc file's content into
it instead, and delete the AsciiDoc file. Show `git diff -M`, then plan again: `README.md` is now
`exists`. Do not commit; if the user asks for a commit message, follow the
Conventions section of the repo's `AGENTS.md`.

## Rules
Expand Down
Loading
Loading