diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 424fd41..0da9a56 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -1,7 +1,7 @@ { "$schema": "https://anthropic.com/claude-code/marketplace.schema.json", "name": "openehr", - "description": "AI-assistant plugins for openEHR — official plugin marketplace of the openEHR Foundation.", + "description": "AI-assistant plugins for openEHR \u2014 official plugin marketplace of the openEHR Foundation.", "owner": { "name": "openEHR Foundation", "url": "https://openehr.org" @@ -10,7 +10,7 @@ { "name": "openehr-specs", "description": "Skills for creating, editing, and reviewing openEHR specification documents (AsciiDoc and OpenAPI/ITS-REST), managing the specification governance process, and ensuring content quality and convention compliance.", - "version": "0.1.0", + "version": "0.2.0", "author": { "name": "Sebastian Iancu" }, diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 1b93ca7..137a0c8 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -1,6 +1,6 @@ { "name": "openehr", - "description": "AI-assistant plugins for openEHR — official plugin marketplace of the openEHR Foundation.", + "description": "AI-assistant plugins for openEHR \u2014 official plugin marketplace of the openEHR Foundation.", "owner": { "name": "openEHR Foundation", "url": "https://openehr.org" @@ -9,7 +9,7 @@ { "name": "openehr-specs", "description": "Skills for creating, editing, and reviewing openEHR specification documents (AsciiDoc and OpenAPI/ITS-REST), managing the specification governance process, and ensuring content quality and convention compliance.", - "version": "0.1.0", + "version": "0.2.0", "author": { "name": "Sebastian Iancu" }, diff --git a/AGENTS.md b/AGENTS.md index 3ccecaa..1b4928b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -16,17 +16,20 @@ ai-plugins/ └── plugins// # One directory per plugin ├── .claude-plugin/plugin.json # Claude Code plugin manifest ├── .cursor-plugin/plugin.json # Cursor plugin manifest - ├── README.md # Plugin purpose, install, and skill inventory - └── skills// - ├── SKILL.md # Skill definition (YAML frontmatter + body) - └── references/ # Optional supplementary content + ├── README.md # Plugin purpose, install, and component inventory + ├── skills// # Knowledge/workflow skills (model- or user-invoked) + │ ├── SKILL.md # Skill definition (YAML frontmatter + body) + │ └── references/ # Optional supplementary content + ├── agents/.md # Autonomous subagents (context-isolated, multi-file) + └── commands/.md # User-invoked action commands (disable-model-invocation) ``` -Current plugins: `openehr-specs` (spec authoring, review, governance, content patterns, amendment records, ITS-REST, BMM class generation). +Current plugins: `openehr-specs` — 7 skills (spec authoring, review, governance, content patterns, amendment records, ITS-REST, BMM class generation), 3 subagents (`spec-reviewer`, `xref-auditor`, `identifier-grounding`), and 3 commands (`amend`, `regen-classes`, `publish`). ## Key Conventions - Plugin names: `openehr-` (mandatory prefix — flat global namespace); skill names: terse activity nouns with no prefix (auto-namespaced as `:`). +- Component choice follows the *nature of the work*: **skills** = knowledge/workflows (model- or user-invoked); **commands** (`commands/`) = user-initiated actions, marked `disable-model-invocation: true` so they don't compete with knowledge-skill triggering; **subagents** (`agents/`) = context-heavy, multi-file, or adversarial work that would otherwise pollute the main context. Commands/agents reference their sibling knowledge skill rather than duplicating it. - Skill `description` frontmatter is lean (~50–75 words): one what+scope sentence → a few representative triggers → short "Not for …" anti-triggers routing to the right sibling/plugin. - Keep each `SKILL.md` body a lean overview (quick-reference + pointers); push bulky detail (tables, templates, format specs) into `references/` so it loads only on demand. - Versions must stay in sync across both plugin manifests (`.claude-plugin/` and `.cursor-plugin/`), both marketplace entries, and the release tag (`{name}--v{version}`). @@ -39,11 +42,23 @@ Full details: [docs/skill-authoring.md](docs/skill-authoring.md) Pure-content repository (JSON manifests + markdown skills) — no build step, package manager, or test suite. -- **Validation**: `python3 scripts/validate.py` (Claude + Cursor manifests, version sync, skill frontmatter, component paths — runs in CI) +- **Validation**: `python3 scripts/validate.py` (Claude + Cursor manifests, version sync, skill/agent/command frontmatter, component paths — runs in CI) - **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) - **Generating/editing AsciiDoc for `specifications-XX` repos**: follow [docs/spec-style-guide.md](docs/spec-style-guide.md); skill bodies must agree with it — when changing one, check the other. +## Component Dependencies + +The skills are pure content; the subagents and commands rely on external tools and degrade +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`). +- **`publish`** — needs a sibling `specifications-AA_GLOBAL` checkout (`bin/spec_publish.sh`) or the `openehr/asciidoctor` Docker image. + +None are bundled (the `openehr-assistant` MCP is interactively authenticated, not redistributable); document them, don't assume them. + ## Relationship to Other Repos - **`specifications-AA_GLOBAL`**: shared infrastructure (boilerplate, publishing scripts, styles) consumed by all spec repos diff --git a/CHANGELOG.md b/CHANGELOG.md index be36efc..db1ee77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ Notable changes to the plugins in this repository. Format follows [Keep a Changelog](https://keepachangelog.com); versions refer to individual plugins (see [docs/versioning.md](docs/versioning.md)). +## openehr-specs 0.2.0 + +Adds autonomous subagents and user-invoked commands around the existing seven skills: + +- **Subagents** (`agents/`): `spec-reviewer` (runs the full review check catalog across a whole spec document), `xref-auditor` (verifies `{openehr_*}` attributes and cross-spec anchors, using the Markdown-twin of target specs), `identifier-grounding` (fact-checks RM/AM/BASE identifiers in a draft against the published spec). +- **Commands** (`commands/`, user-invoked): `/openehr-specs:amend`, `/openehr-specs:regen-classes`, `/openehr-specs:publish`. +- `scripts/validate.py` now also validates agent and command frontmatter. + ## openehr-specs 0.1.0 — 2026-06-05 Initial release. Seven skills for openEHR specification work: `authoring`, `content-patterns`, `amendment-record`, `review`, `governance`, `its-rest`, and `class-generation`. Packaged for the Claude Code and Cursor marketplaces. diff --git a/README.md b/README.md index e9d7357..4b073cb 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ Skills are namespaced by the plugin and invoked as `/openehr-specs:`: /openehr-specs:class-generation # regenerate class tables/diagrams from BMM ``` -Type `/openehr` in the slash menu to list all skills together, or just describe the task (e.g. "review this openEHR spec before release") and the matching skill activates automatically. See the [plugin README](plugins/openehr-specs/README.md) for the full skill list and invocation details. +Type `/openehr` in the slash menu to list the available skills and commands, or just describe the task (e.g. "review this openEHR spec before release") and the matching skill activates automatically. The plugin also ships **subagents** (`spec-reviewer`, `xref-auditor`, `identifier-grounding`) that Claude dispatches for heavy, multi-file, or verification work. See the [plugin README](plugins/openehr-specs/README.md) for the full inventory and invocation details. ## Documentation diff --git a/plugins/openehr-specs/.claude-plugin/plugin.json b/plugins/openehr-specs/.claude-plugin/plugin.json index e1c03a5..a042026 100644 --- a/plugins/openehr-specs/.claude-plugin/plugin.json +++ b/plugins/openehr-specs/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "openehr-specs", "displayName": "openEHR Specifications", - "version": "0.1.0", + "version": "0.2.0", "description": "Skills for creating, editing, and reviewing openEHR specification documents (AsciiDoc and OpenAPI/ITS-REST), managing the specification governance process, and ensuring content quality and convention compliance.", "author": { "name": "Sebastian Iancu" diff --git a/plugins/openehr-specs/.cursor-plugin/plugin.json b/plugins/openehr-specs/.cursor-plugin/plugin.json index a7a06aa..9025cb7 100644 --- a/plugins/openehr-specs/.cursor-plugin/plugin.json +++ b/plugins/openehr-specs/.cursor-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "openehr-specs", "displayName": "openEHR Specifications", - "version": "0.1.0", + "version": "0.2.0", "description": "Skills for creating, editing, and reviewing openEHR specification documents (AsciiDoc and OpenAPI/ITS-REST), managing the specification governance process, and ensuring content quality and convention compliance.", "author": { "name": "Sebastian Iancu" diff --git a/plugins/openehr-specs/README.md b/plugins/openehr-specs/README.md index b64fa81..0410a70 100644 --- a/plugins/openehr-specs/README.md +++ b/plugins/openehr-specs/README.md @@ -1,6 +1,6 @@ # openEHR Specifications -Skills for authors working in `specifications-XX` and `specifications-ITS-REST` repositories — AsciiDoc structure, amendment records, governance, quality review, prose patterns, ITS-REST OpenAPI sources, and BMM-based class documentation generation. +Skills, subagents, and commands for authors working in `specifications-XX` and `specifications-ITS-REST` repositories — AsciiDoc structure, amendment records, governance, quality review, prose patterns, ITS-REST OpenAPI sources, and BMM-based class documentation generation. ## Installation @@ -29,6 +29,26 @@ Add the marketplace from this repository (or a fork), then install `openehr-spec | `its-rest` | ITS-REST OpenAPI YAML and operation descriptions | | `class-generation` | Generate class tables and UML diagrams from BMM via `bmm-publisher` | +## Subagents + +Context-isolated agents for heavy, multi-file, or verification work — dispatched by Claude (or ask for them by name). Each degrades gracefully when an optional dependency is absent. + +| Agent | Purpose | Needs | +|-------|---------|-------| +| `spec-reviewer` | Runs the full `review` check catalog across an entire spec document, returns a findings report | `specifications-XX` (+ `AA_GLOBAL` for attribute checks) | +| `xref-auditor` | Verifies every `{openehr_*}` attribute and `<>` resolves; checks cross-spec deep-link anchors against the target's Markdown twin | WebFetch (optional) | +| `identifier-grounding` | Fact-checks every RM/AM/BASE class/attribute a draft names against the published spec; flags invented identifiers | `openehr-assistant` MCP or WebFetch (optional) | + +## Commands + +User-invoked actions (they do not auto-trigger). Invoke as `/openehr-specs:`. + +| Command | Argument | Action | +|---------|----------|--------| +| `/openehr-specs:amend` | `` | Add an amendment-record entry (version bump, anchors, Jira refs) | +| `/openehr-specs:regen-classes` | `` | Regenerate class tables/diagrams via `bmm-publisher` (needs Docker) | +| `/openehr-specs:publish` | `` | Build a local HTML preview via the `AA_GLOBAL` publisher | + ## Usage Each skill is namespaced by the plugin, so it is invoked as **`/openehr-specs:`**. There are three ways to reach a skill: @@ -39,8 +59,8 @@ Each skill is namespaced by the plugin, so it is invoked as **`/openehr-specs: + Context: The user drafted a new chapter describing several RM classes. + user: "I wrote the new versioning chapter — make sure I didn't make up any class or attribute names" + assistant: "I'll dispatch the identifier-grounding agent to extract every class/attribute it names and verify each against the published RM spec." + + Inventing identifiers is the most-violated spec-authoring rule; an adversarial verifier that defaults to "unverified" catches it. + + + + + Context: Reviewing a contributor's prose before merge. + user: "double-check the COMPOSITION attributes mentioned in this section actually exist" + assistant: "I'll launch the identifier-grounding agent to confirm each named attribute against the COMPOSITION class definition." + + Per-class attribute verification is precise lookup work best grounded in the BMM-backed spec, isolated from the main context. + + +model: inherit +color: yellow +--- + +You are an openEHR identifier fact-checker. You verify that every RM/AM/BASE/LANG identifier a +specification draft references actually exists in the published specifications, and you report +unverified or likely-invented identifiers. You are a verifier: **never modify files.** + +**Operating principle (adversarial):** default every identifier to `UNVERIFIED`. Promote it to +`VERIFIED` only when you positively find it in an authoritative source. Inventing or misspelling +class/attribute names is the single most damaging spec-authoring error, so bias toward flagging. + +**Sources, in order of preference:** +1. **`openehr-assistant` MCP** (if available) — use `type_specification_get` for per-class + attribute/function detail (BMM-backed, authoritative). Discover the tool via tool search. +2. **Markdown twin** — fetch the published spec page as Markdown: take the + `specifications.openehr.org/releases///.html` URL and swap + `.html` → `.md`, then search it for the identifier. (Note: Markdown omits some per-class + attribute tables; fall back to source 1 or the HTML for those.) +3. **Local sibling repos** — if the relevant `specifications-XX` source or BMM-generated + `docs/UML/classes/` files are present in the workspace, grep them. + +If none of these is reachable, report identifiers as `UNVERIFIED (no source available)` rather than guessing. + +**Your Core Responsibilities:** +1. Extract every openEHR identifier the draft claims: class/type names (UPPER_SNAKE like + `COMPOSITION`, `DV_QUANTITY`, generics like `VERSION`), attribute names (italic-monospace + like `_uid_`, `_commit_audit_`), and function names (`_function()_`). +2. For each, determine the owning component/spec and verify it exists — and, for attributes, + that it belongs to the class the draft attributes it to. +3. Report status per identifier with the source that confirmed (or failed to confirm) it. + +**Output Format:** +A table, then a summary. + +``` +| Identifier | Claimed context | Status | Source | +|------------|-----------------|--------|--------| +| COMPOSITION | RM ehr | VERIFIED | type_specification_get | +| VERSION._commit_audit_ | RM common | VERIFIED | common.md | +| COMPOSITION._signature_ | RM ehr | UNVERIFIED — not found on COMPOSITION | type_specification_get | +| DV_QUANTAS | RM data_types | LIKELY INVENTED — no such type (did you mean DV_QUANTITY?) | data_types.md | +``` + +End with: `N identifiers — X verified, Y unverified, Z likely invented`, and list the +unverified/invented ones first with the suggested correction where obvious. + +**Edge Cases:** +- Identifier defined locally in the same draft (a new type being introduced) → mark `NEW (defined in this draft)`, not invented. +- Ambiguous attribute shared by several classes → verify against the specific class named; if the draft doesn't qualify it, note the ambiguity. +- Non-openEHR identifiers (HL7, ISO, FHIR types) → out of scope; list them as `SKIPPED (external)`. diff --git a/plugins/openehr-specs/agents/spec-reviewer.md b/plugins/openehr-specs/agents/spec-reviewer.md new file mode 100644 index 0000000..789b592 --- /dev/null +++ b/plugins/openehr-specs/agents/spec-reviewer.md @@ -0,0 +1,78 @@ +--- +name: spec-reviewer +description: | + Use this agent to run a full convention-compliance review across an entire openEHR + specification document (the `master.adoc` + all `masterNN-*.adoc` chapters + `manifest.json`) + in a `specifications-XX` repository, returning a structured findings report. Dispatch it for + whole-document or pre-release reviews where reading every chapter inline would bloat the main + context. Examples: + + + Context: The user finished editing several chapters of the RM EHR IM and wants a quality pass. + user: "review the ehr spec in specifications-RM before I tag the release" + assistant: "I'll dispatch the spec-reviewer agent to run the full check catalog across docs/ehr/ and report findings." + + A pre-release review spans master.adoc plus many chapters; the context-isolated agent reads them all and returns only the findings table. + + + + + Context: The user asks for a convention lint of a whole component. + user: "check the whole BASE component for spec convention issues" + assistant: "I'll launch the spec-reviewer agent against each spec directory under specifications-BASE/docs/." + + Multi-directory review is exactly the heavy, parallelizable work an isolated subagent should own. + + +model: inherit +color: blue +tools: ["Read", "Grep", "Glob"] +--- + +You are an openEHR specification reviewer. You audit AsciiDoc specification documents in +`specifications-XX` repositories against the conventions of the openEHR specification library +and report findings — you do NOT modify files. + +**Authoritative check list:** If the `openehr-specs` plugin is installed, read its full check +catalog at `plugins/openehr-specs/skills/review/references/check-catalog.md` (or the installed +skill path) and apply every check. If it is not reachable, apply the eight categories summarised +below. Cross-reference attribute naming follows +`plugins/openehr-specs/skills/authoring/references/cross-references.md`. + +**Your Core Responsibilities:** +1. Discover the target — resolve the spec directory (e.g. `docs/ehr/`); if given a component, + enumerate each spec directory under `docs/`. +2. Read the key files: `master.adoc`, `manifest_vars.adoc`, `master00-amendment_record.adoc`, + `master01-preface.adoc`, every `masterNN-*.adoc` chapter, and the component `manifest.json`. + Also read the shared `specifications-AA_GLOBAL/docs/boilerplate/global_vars.adoc` and + `docs/references/reference_definitions.adoc` when checks need them. +3. Run every check, recording each finding as ERROR, WARNING, or INFO with a `file:line` location. +4. Report; propose fixes for ERRORs and WARNINGs but never edit files. + +**Check Categories (summary — the catalog file is authoritative):** +1. STRUCT — `master.adoc` include order, required files, Acknowledgements/References scaffolding +2. PREF — preface Purpose / Related Documents / Status / Feedback / Conformance sections +3. AMEND — `[[latest_issue]]`/`[[latest_issue_date]]` anchors, most-recent-first order, release boundaries, Jira refs +4. XREF — `{openehr_*}` attributes resolve; display text + `^` markers; no hardcoded URLs +5. FIG — `image::` `id=`/`align="center"`, titles, `[.text-center]`, `{uml_diagrams_uri}` vs `{diagrams_uri}` +6. ADOC — monospace class names, italic-monospace attribute names, generated (not hand-written) class tables, `[.tbd]`/`[.deprecated]` roles +7. MANIFEST — `manifest.json` entry present and consistent with `manifest_vars.adoc` +8. CONTENT — chapter level-1 headings, Overview subsections, no hardcoded `specifications.openehr.org` URLs + +**Output Format:** +A findings table followed by a summary. Do not include passing checks unless asked. + +``` +| ID | Severity | Location | Finding | +|----|----------|----------|---------| +| STRUCT-02 | ERROR | manifest_vars.adoc | Missing `:keywords:` attribute | +| FIG-01 | WARNING | master03-overview.adoc:42 | Image directive missing `id=` | +``` + +End with: `Total: N findings (E errors, W warnings, I info)` and a short, prioritised +recommended-actions list (group related findings; lead with ERRORs). + +**Edge Cases:** +- Spec directory not found → report which paths you tried and stop. +- `specifications-AA_GLOBAL` sibling absent → run all checks except those that require it (XREF-01 attribute existence), and note the limitation. +- Generated `docs/UML/` class tables → never flag their internal formatting; they are produced by `bmm-publisher` (see the class-generation skill). diff --git a/plugins/openehr-specs/agents/xref-auditor.md b/plugins/openehr-specs/agents/xref-auditor.md new file mode 100644 index 0000000..0a52551 --- /dev/null +++ b/plugins/openehr-specs/agents/xref-auditor.md @@ -0,0 +1,72 @@ +--- +name: xref-auditor +description: | + Use this agent to audit cross-references in openEHR specification documents — collecting every + `{openehr_*}` Asciidoctor attribute and `<>` reference, verifying each attribute is + defined in the shared reference files, and (when web access is available) confirming that + cross-spec deep-link anchors actually exist in the target spec. Dispatch it when links may have + drifted, before a release, or across a whole component. Examples: + + + Context: The user suspects broken cross-references after a large edit. + user: "did I break any cross-refs in the AOM2 spec? check the {openehr_*} links resolve" + assistant: "I'll dispatch the xref-auditor agent to collect every attribute and anchor in the spec and verify each resolves." + + reference_definitions.adoc has hundreds of entries; resolving each attribute is context-heavy and ideal to isolate. + + + + + Context: Pre-release link check. + user: "before we publish RM, make sure no deep links point at anchors that no longer exist" + assistant: "I'll launch the xref-auditor agent; it will fetch each target spec's Markdown twin to confirm the #anchors resolve." + + Broken cross-spec anchors fail silently at publish time — verifying them against the target needs the .md twin, which the agent fetches. + + +model: inherit +color: cyan +tools: ["Read", "Grep", "Glob", "WebFetch"] +--- + +You are an openEHR specification cross-reference auditor. You verify that every reference in an +AsciiDoc spec resolves, and report broken or non-conventional links — you do NOT modify files. + +Attribute naming conventions are documented in +`plugins/openehr-specs/skills/authoring/references/cross-references.md`; read it if reachable. + +**Your Core Responsibilities:** +1. Collect references from the target `.adoc` files: + - Asciidoctor attributes used as links: `{openehr_*}`, `{spec_tickets}`, `{classes_url_root}`, + `{uml_diagrams_uri}`, `{diagrams_uri}`, and any `{..._release}` attributes. + - Internal anchors: `<>` / `<>` and the anchors they target (`[[anchor]]`, `[#anchor]`, `anchor=`). + - Cross-spec deep links: an attribute followed by `#fragment`, e.g. `{openehr_rm_data_types}#dv_quantity[...]`. +2. Resolve each attribute against the shared definitions: + `specifications-AA_GLOBAL/docs/references/reference_definitions.adoc` and + `docs/boilerplate/global_vars.adoc`. Flag any attribute used but not defined. +3. Resolve internal anchors within the document set; flag `<>` with no matching target. +4. For cross-spec deep links, if `WebFetch` is available, fetch the target spec's **Markdown twin** + — take the resolved `specifications.openehr.org/...page.html` URL and swap `.html` → `.md` — and + confirm the `#fragment` anchor exists. If web access is unavailable, mark these `UNCHECKED` and say so. +5. Flag hardcoded `https://specifications.openehr.org/...` URLs that should use a `{openehr_*}` attribute. +6. Check link hygiene: cross-spec links should carry display text and the external-link marker, `{attr}[Display Text^]`. + +**Output Format:** +A table of references with status, then a summary. + +``` +| Reference | Kind | Location | Status | +|-----------|------|----------|--------| +| {openehr_rm_common} | attribute | master04-foo.adoc:88 | OK (defined) | +| {openehr_rm_madeup} | attribute | master05-bar.adoc:12 | UNDEFINED — not in reference_definitions.adoc/global_vars.adoc | +| {openehr_rm_data_types}#dv_quant | deep link | master06-baz.adoc:30 | BROKEN ANCHOR — #dv_quant absent from data_types.md | +| https://specifications.openehr.org/releases/RM/latest/ehr.html | hardcoded URL | master02.adoc:5 | NON-CONVENTIONAL — use {openehr_rm_ehr} | +``` + +End with counts: `N references — X OK, Y undefined, Z broken anchors, W hardcoded, V unchecked`, +and a prioritised fix list (undefined attributes and broken anchors first). + +**Edge Cases:** +- `specifications-AA_GLOBAL` absent → cannot resolve attributes; report that and audit only internal anchors + hardcoded URLs. +- Attribute defined but resolving to a `{nested_attribute}` → resolve transitively before judging. +- A `#fragment` may match either an explicit `[[id]]`/`[#id]` or an auto-generated heading id; treat a plausible heading-derived id as OK and note it as heading-derived. diff --git a/plugins/openehr-specs/commands/amend.md b/plugins/openehr-specs/commands/amend.md new file mode 100644 index 0000000..0e8f850 --- /dev/null +++ b/plugins/openehr-specs/commands/amend.md @@ -0,0 +1,35 @@ +--- +name: amend +description: Add an amendment-record entry to the current openEHR spec (version bump + Jira refs + anchor move) +argument-hint: "" +allowed-tools: ["Read", "Edit", "Bash"] +disable-model-invocation: true +--- + +Add a new entry to the amendment record of the openEHR specification being worked on. The +amendment-record conventions (anchors, Jira keys, release boundaries, version-bump rules) are +defined in the `openehr-specs:amendment-record` skill and its `references/conventions.md` — follow +them; this command is the action wrapper. + +Arguments: `$ARGUMENTS` — the Jira CR/PR reference(s) and a short description of the change +(e.g. `SPECRM-142 — add tags to LOCATABLE; addresses SPECPR-401`). + +Do the following: + +1. **Locate the amendment record.** Find the relevant `master00-amendment_record.adoc`. If the + spec directory is ambiguous, ask which spec; if a single spec is in scope, use it. +2. **Read it** to learn the current top issue number, format (`[cols=...]`), and raiser style. +3. **Classify the version bump** from the change description (patch = corrections/clarifications, + minor = new content/sections, major = restructuring) per the amendment-record skill's rubric. + If unclear, state your assumption and proceed. +4. **Optionally inspect the diff** for context: `git -C diff` (and `git status`) to see what + actually changed, so the entry describes it accurately. +5. **Insert the new entry at the top** of the table (immediately after the header row): + - Move the `[[latest_issue]]` and `[[latest_issue_date]]` anchors onto this new entry. + - Format Jira references as `{spec_tickets}/SPECXX-NN[SPECXX-NN^]: `; link any PRs + it addresses ("Addresses {spec_tickets}/SPECPR-NN[SPECPR-NN^]"). + - Use today's date in `dd Mon yyyy` form (ask if the date is uncertain — do not guess a date). + - If this is the first entry after a release, add the `4+^h|*XX Release N.N.N*` boundary row below it. +6. **Show the diff of the change** and stop. Do not publish or commit. + +Keep the Details cell concise and specific — state what changed, not why (the Jira ticket holds the rationale). diff --git a/plugins/openehr-specs/commands/publish.md b/plugins/openehr-specs/commands/publish.md new file mode 100644 index 0000000..a73a92b --- /dev/null +++ b/plugins/openehr-specs/commands/publish.md @@ -0,0 +1,36 @@ +--- +name: publish +description: Build a local HTML preview of an openEHR specification component via the AA_GLOBAL publisher +argument-hint: " [spec-id]" +allowed-tools: ["Bash", "Read", "Glob"] +disable-model-invocation: true +--- + +Produce a local HTML preview of an openEHR specification component using the shared publishing +toolchain in `specifications-AA_GLOBAL`. This is the publish step described in the +`openehr-specs:authoring` skill — this command is the action wrapper. It builds a **local preview +only**; it does not tag, commit, or deploy a release (that is the `governance` skill / release process). + +Arguments: `$ARGUMENTS` — the component abbreviation (e.g. `RM`, `BASE`, `AM`) and optionally a +single spec id to limit the build. + +Do the following: + +1. **Locate the publisher.** Confirm a sibling `specifications-AA_GLOBAL` checkout exists next to + the component repo (the parent directory should contain both `specifications-AA_GLOBAL/` and + `specifications-/`). If not, report what is missing and stop. +2. **Run from the parent directory** containing the `specifications-*` repos. Prefer the script, + fall back to Docker: + ```bash + ./specifications-AA_GLOBAL/bin/spec_publish.sh -f -v $ARGUMENTS + # or: + docker run -u $(id -u):$(id -g) -v "$(pwd):/documents/" openehr/asciidoctor development $ARGUMENTS + ``` +3. **Surface the result** — report the generated HTML output location and any Asciidoctor + warnings/errors (unresolved attributes, missing includes, missing images) verbatim, since those + are exactly what a pre-publish check should catch. +4. If the build fails, summarise the first actionable error and suggest the relevant skill + (`authoring` for include/attribute issues, `xref-auditor` agent for cross-reference failures). + +Do not pass release flags (`-r -t -q -l Release-N.N.N`) — releasing is a governed process; use the +`governance` skill for that. diff --git a/plugins/openehr-specs/commands/regen-classes.md b/plugins/openehr-specs/commands/regen-classes.md new file mode 100644 index 0000000..83511d1 --- /dev/null +++ b/plugins/openehr-specs/commands/regen-classes.md @@ -0,0 +1,36 @@ +--- +name: regen-classes +description: Regenerate BMM-derived class tables and UML diagrams for a component via bmm-publisher +argument-hint: " [-d ...]" +allowed-tools: ["Bash", "Read", "Glob"] +disable-model-invocation: true +--- + +Regenerate the class documentation (class-definition tables + UML class/package diagrams) for an +openEHR component from its BMM schema using the `bmm-publisher` tool. The tool, commands, output +layouts, and how the output wires into `docs/UML/` are documented in the +`openehr-specs:class-generation` skill and its `references/bmm-publisher.md` — follow them; this +command is the action wrapper. + +Arguments: `$ARGUMENTS` — the schema id (without `.bmm.json`, e.g. `openehr_rm_1.2.0`) and any +`-d ` flags for cross-reference resolution (e.g. RM depends on BASE: +`openehr_rm_1.2.0 -d openehr_base_1.3.0`). + +Do the following: + +1. **Confirm Docker is available** (`docker --version`). If not, point the user to the local + `bmm-publisher` dev-container path in `references/bmm-publisher.md` and stop. +2. **Determine the output target.** Default to a working directory the user can inspect (e.g. + `./out`); ask before writing directly into a `specifications-XX` repo's `docs/UML/`. +3. **Run the generator**, mapping output ownership to the host user: + ```bash + docker run --rm --user $(id -u):$(id -g) \ + -v ./out:/app/output \ + ghcr.io/openehr/bmm-publisher asciidoc -v $ARGUMENTS + ``` + Use `legacy-adoc -o ` instead when the component consumes the legacy `docs/UML/classes` + layout (per the class-generation skill). +4. **Report what was generated** (list the produced `classes/`, `effective/`, diagram SVGs) and + remind the user that generated files are never hand-edited — change the BMM and regenerate. +5. Do not commit. If the user wants the output wired into a spec repo, follow the placement steps + in the class-generation skill. diff --git a/plugins/openehr-specs/skills/authoring/references/cross-references.md b/plugins/openehr-specs/skills/authoring/references/cross-references.md index 71f4341..79a1fb4 100644 --- a/plugins/openehr-specs/skills/authoring/references/cross-references.md +++ b/plugins/openehr-specs/skills/authoring/references/cross-references.md @@ -201,6 +201,7 @@ The `{spec_tickets}` attribute resolves to the specifications site ticket URL pa 2. Search `global_vars.adoc` for infrastructure attributes 3. Follow the naming patterns above to construct the expected name, then verify it exists -The files are at: -- `/src/openehr/specifications-AA_GLOBAL/docs/boilerplate/global_vars.adoc` -- `/src/openehr/specifications-AA_GLOBAL/docs/references/reference_definitions.adoc` +The files live in the `openEHR/specifications-AA_GLOBAL` repository (use a local sibling checkout +if present, otherwise read them on GitHub): +- [`docs/boilerplate/global_vars.adoc`](https://github.com/openEHR/specifications-AA_GLOBAL/blob/master/docs/boilerplate/global_vars.adoc) +- [`docs/references/reference_definitions.adoc`](https://github.com/openEHR/specifications-AA_GLOBAL/blob/master/docs/references/reference_definitions.adoc) diff --git a/scripts/validate.py b/scripts/validate.py index 53161ff..d07ed3e 100644 --- a/scripts/validate.py +++ b/scripts/validate.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -"""Validate marketplace and plugin manifests, and SKILL.md frontmatter. +"""Validate marketplace and plugin manifests, and skill/agent/command frontmatter. Checks both Claude Code (``.claude-plugin/``) and Cursor (``.cursor-plugin/``) layouts. @@ -55,6 +55,27 @@ def validate_skills(plugin_dir: Path): err(f"{rel}: frontmatter name '{fm_name.group(1)}' != directory '{skill_dir.name}'") +def validate_md_components(plugin_dir: Path, subdir: str, *, require_name: bool): + """Validate flat .md components (agents/, commands/): frontmatter present with the + required fields, and any `name` matches the filename stem.""" + comp_dir = plugin_dir / subdir + if not comp_dir.is_dir(): + return + for md in sorted(comp_dir.glob("*.md")): + rel = md.relative_to(ROOT) + m = re.match(r"\A---\n(.*?)\n---\n", md.read_text(), re.DOTALL) + if not m: + err(f"{rel}: missing YAML frontmatter") + continue + front = m.group(1) + for field in (("name", "description") if require_name else ("description",)): + if not re.search(rf"^{field}:", front, re.MULTILINE): + err(f"{rel}: frontmatter missing '{field}'") + fm_name = re.search(r"^name:\s*(\S+)", front, re.MULTILINE) + if fm_name and fm_name.group(1) != md.stem: + err(f"{rel}: frontmatter name '{fm_name.group(1)}' != filename '{md.stem}'") + + def validate_manifest_paths(plugin_dir: Path, plugin_name: str, plugin: dict): for field in MANIFEST_FIELDS: value = plugin.get(field) @@ -101,6 +122,8 @@ def validate_plugin_entry(name: str, version: str, plugin_dir: Path, manifest_su validate_manifest_paths(plugin_dir, name, plugin) validate_skills(plugin_dir) + validate_md_components(plugin_dir, "agents", require_name=True) + validate_md_components(plugin_dir, "commands", require_name=False) def validate_marketplace(mp_path: Path, manifest_subdir: str, label: str): @@ -172,4 +195,4 @@ def main(): for e in errors: print(f" - {e}") sys.exit(1) - print("OK: Claude and Cursor manifests, plugin metadata, and skills are valid") + print("OK: Claude and Cursor manifests, plugin metadata, skills, agents, and commands are valid")