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 @@ -58,7 +58,7 @@ gracefully when one is absent (each says so in its prompt):
- **`spec-reviewer`, `xref-auditor`** — need a `specifications-XX` checkout and (for attribute resolution) the sibling `specifications-AA_GLOBAL`.
- **`xref-auditor`, `identifier-grounding`** — use **WebFetch** to read spec Markdown twins (`.html` → `.md`); `identifier-grounding` additionally prefers the **`openehr-assistant` MCP** (`type_specification_get`) when connected, falling back to the twin.
- **`regen-classes`** — needs **Docker** (`ghcr.io/openehr/bmm-publisher`).
- **`scaffold`** — needs **`python3`** (standard library only) to run its bundled `scripts/scaffold.py`, and says so and stops when it is missing. It writes into the repository it is run in, so it plans first and never overwrites a hand-edited file without being told to.
- **`scaffold`** — needs **`python3`** (standard library only) to run its bundled `scripts/scaffold.py`, and says so and stops when it is missing. It writes into the repository it is run in, so it plans first and never overwrites a hand-edited file without being told to. For a new repository's BMM schema it asks **Docker** for the schemas the local `ghcr.io/openehr/bmm-publisher` image bundles, and never pulls the image. Without Docker or the image that one file is reported as blocked, and the rest are still written.
- **`publish`** — needs **Docker** and sibling `specifications-AA_GLOBAL` and component checkouts. It runs the published `ghcr.io/openehr/asciidoctor` image, which bundles the toolchain and `spec_publish.sh`; the boilerplate and references are still read from the AA_GLOBAL checkout. It checks that each HTML output was rebuilt and that the log has no missing includes, because the script reports success even when it built nothing or dropped the class tables.

None are bundled (the `openehr-assistant` MCP is interactively authenticated, not redistributable); document them, don't assume them.
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,15 @@ Notable changes to the plugins in this repository. The format follows [Keep a Ch

## Unreleased

### Added

- `scaffold` template set revision 3: a new repository also gets `computable/BMM/<id>.bmm.json` (file id `bmm`, strategy `bmm-seed`).
- The script lists the schemas the local `ghcr.io/openehr/bmm-publisher` image bundles (`docker run --pull never`, so nothing is downloaded) and copies the highest version for the component.
- When the image bundles none, it writes an empty schema `openehr_<component>_<first_release>`. The schema has one root package `org.openehr.<name>`, no classes, and BASE in `includes` for other components. bmm-publisher refuses a schema without a package.
- Without Docker or the image the file is reported as `blocked` and the other files are still written; `--var bmm_schema_id=<id>` creates the empty schema anyway.
- Existing repositories are not offered a schema; `--overwrite bmm` asks for one.
- Derived variables can take a regex group (`match` + `group`), which gives the new `bmm_schema_name` and `bmm_rm_release`.

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

### Changed
Expand Down
2 changes: 1 addition & 1 deletion plugins/openehr-specs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ 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`. `.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`; written for Claude Code (its script path comes from `${CLAUDE_SKILL_DIR}`) |
| `/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: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
34 changes: 26 additions & 8 deletions plugins/openehr-specs/skills/scaffold/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: scaffold
description: Initialise a specification repository, or bring an existing one up to the current standard file set (AGENTS.md, .claude settings, manifest.json, .gitignore, LICENSE, README)
description: Initialise a specification repository, or bring an existing one up to the current standard file set (AGENTS.md, .claude settings, manifest.json, .gitignore, LICENSE, README, and in a new repository a computable/BMM schema)
argument-hint: "[component id, e.g. BASE]"
allowed-tools:
- "Bash(python3 ${CLAUDE_SKILL_DIR}/scripts/scaffold.py plan *)"
Expand All @@ -22,7 +22,14 @@ 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.
AsciiDoc-specific. Pin `bmm` too when a new repo has no model.

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
and copies the highest version for the component. When the image bundles none, it writes an empty
schema: one root package `org.openehr.<name>`, no classes, BASE included for other components, and
`rm_release` equal to `first_release`. The check needs Docker and the image, and never pulls it.
Existing repositories are not offered a schema.

## Steps

Expand All @@ -39,7 +46,9 @@ AsciiDoc-specific.
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
`manifest.json`, the one file that uses it) and list the rest in one line. Never invent a
`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
title, a Jira key or a filter id. Values the user confirmed earlier are recorded in
`.claude/scaffold.json` and kept; a recorded default is not, so a BMM schema added later shows up
as a new guess to confirm. `--overwrite` and `--pin` accept only the file ids and `agents:<region>`
Expand All @@ -53,7 +62,14 @@ AsciiDoc-specific.
If the user wants the `git add` and `git commit` rules gone, remove them from the file after
applying: later runs leave out what the user removed, and add only what a newer revision
introduces.
- `exists`, `exists-alternative` (for example `README.adoc`), `unchanged`, `pinned`: nothing to do.
- `create` for `bmm`: say from its `detail` whether the schema is copied from the image or new and
empty.
- `blocked` (only `bmm`): the image could not be checked, and `detail` says why. Offer to start
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.
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 All @@ -69,19 +85,21 @@ AsciiDoc-specific.
`<!-- openehr-scaffold:begin <id> -->` markers, or to pin it.
- `migration_steps`: show them, with each `note` as a manual step. The file actions shown are for
the tree as it is now; `apply` re-plans after running the steps and returns each step's `result`.
- Nothing but `unchanged`, `exists`, `exists-alternative` and `pinned`, and no `migration_steps`:
report "up to date" and stop.
- Nothing but `unchanged`, `exists`, `exists-alternative`, `pinned` and `skipped`, and no
`migration_steps`: report "up to date" and stop.
- A directory that is not a git repository: offer `git init -b <default_branch>`; if git itself is
missing, say so.
4. **Apply.** Run the same command with `apply`, repeating the `--var` options and adding the
`--overwrite` and `--pin` options the user chose. Check that the output has `applied: true`;
`status: needs_input` means nothing was written. The script never changes an existing seed file
(`manifest.json`, `LICENSE`, `README.md`), never rewrites a hand-edited file or region without
(`manifest.json`, `LICENSE`, `README.md`, a BMM schema), never rewrites a hand-edited file or region without
`--overwrite`, and records the revision, variables and file hashes in `.claude/scaffold.json`.
5. **Finish.** List `written`. For each `todo_markers` entry (a `TODO(scaffold)` comment in
`AGENTS.md`), offer to fill it from the repo's content and delete the comment once it is filled.
When `manifest.json` was created, point out that its `specifications` list starts empty (skill
`openehr-specs:authoring`). Do not commit; if the user asks for a commit message, follow the
`openehr-specs:authoring`). When an empty BMM schema was created, point out that classes go into 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
Conventions section of the repo's `AGENTS.md`.

## Rules
Expand Down
35 changes: 33 additions & 2 deletions plugins/openehr-specs/skills/scaffold/assets/template-set.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"id": "openehr-specs/scaffold",
"revision": 2,
"revision": 3,
"revisions": [
{
"revision": 1,
Expand All @@ -13,6 +13,12 @@
"released": "2026-10-08",
"summary": "AGENTS.md build region: no longer claims the repo has no build tooling, and the class-table command loads the sibling BASE BMM with -d (new variable base_bmm_schema_id); a TODO(scaffold) marker asks new repos to describe their own tooling. .claude/CLAUDE.md says where repo guidance goes.",
"digest": "e973f4d599169188dcef2ae965e6f642ad58f3cea9d09ef930e923a4ca54e5b7"
},
{
"revision": 3,
"released": "2026-10-09",
"summary": "A new repository gets computable/BMM/<id>.bmm.json: a copy of the schema the bmm-publisher image bundles for the component, or an empty schema (one root package, BASE included) when it bundles none. Existing repositories are not offered one.",
"digest": "c78b965d50608d5937bd6dfcc0b0953cba4c91d51ad347bd46b647b7cdc7281f"
}
],
"variables": {
Expand Down Expand Up @@ -87,13 +93,29 @@
"bmm_schema_id": {
"default": "",
"pattern": "^$|^openehr_[a-z0-9_]+_[0-9]+\\.[0-9]+\\.[0-9]+$",
"description": "BMM schema id (openehr_base_1.3.0) when the repo holds computable/BMM/<id>.bmm.json; empty otherwise. Switches on the class-generation sections."
"description": "BMM schema id (openehr_base_1.3.0) of computable/BMM/<id>.bmm.json, read from the repo. When a new repository is initialised without one: the highest schema the bmm-publisher image bundles for the component, else openehr_<component>_<first_release> for a new empty schema. Empty otherwise. Switches on the class-generation sections."
},
"base_bmm_schema_id": {
"default": "",
"pattern": "^$|^openehr_base_[0-9]+\\.[0-9]+\\.[0-9]+$",
"description": "BASE BMM schema id (openehr_base_1.3.0) that the component's classes refer to, read from the sibling specifications-BASE clone; loaded with -d when the class tables are regenerated. Empty for BASE itself and for repos without a BMM."
},
"bmm_schema_name": {
"derived": {
"from": "bmm_schema_id",
"match": "^openehr_(.+)_[0-9]+\\.[0-9]+\\.[0-9]+$",
"group": 1
},
"description": "schema_name of the BMM schema (base in openehr_base_1.3.0); empty without one."
},
"bmm_rm_release": {
"derived": {
"from": "bmm_schema_id",
"match": "^openehr_.+_([0-9]+\\.[0-9]+\\.[0-9]+)$",
"group": 1
},
"description": "rm_release of the BMM schema (1.3.0 in openehr_base_1.3.0); empty without one."
},
"documents": {
"default": "none yet (add them to `manifest.json`)",
"description": "Published document ids, read from manifest.json when present."
Expand Down Expand Up @@ -124,6 +146,15 @@
"template": "manifest-json.tmpl",
"strategy": "seed"
},
{
"id": "bmm",
"target": "computable/BMM/{{bmm_schema_id}}.bmm.json",
"template": "bmm-json.tmpl",
"strategy": "bmm-seed",
"present_glob": "computable/BMM/*.bmm.json",
"image": "ghcr.io/openehr/bmm-publisher",
"image_dir": "/app/resources"
},
{
"id": "gitignore",
"target": ".gitignore",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
{
"bmm_version": "2.4",
"rm_publisher": "openehr",
"schema_name": "{{bmm_schema_name}}",
"rm_release": "{{bmm_rm_release}}",
"schema_revision": "{{bmm_rm_release}}.1",
"schema_lifecycle_state": "development",
"schema_description": "{{component_description}}",
"schema_author": "openEHR Foundation",
{{#if base_bmm_schema_id}}
"includes": {
"{{base_bmm_schema_id}}": {
"id": "{{base_bmm_schema_id}}"
}
},
{{/if}}
"packages": {
"org.openehr.{{bmm_schema_name}}": {
"name": "org.openehr.{{bmm_schema_name}}",
"classes": []
}
},
"class_definitions": {}
}
5 changes: 5 additions & 0 deletions plugins/openehr-specs/skills/scaffold/migrations/0003.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"from": 2,
"to": 3,
"steps": []
}
Loading
Loading