@softspark/ai-toolkit 2.7.1 → 2.7.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,25 @@ Versioning follows [Semantic Versioning](https://semver.org/).
7
7
 
8
8
  ---
9
9
 
10
+ ## v2.7.3 — Regenerate llms after Medplum Merge (2026-04-17)
11
+
12
+ ### Fixed
13
+ - **`llms.txt` + `llms-full.txt` stale after PR #7 (Medplum/FHIR rules)** — contributor did not re-run `npm run generate:all` before merge, so the machine-readable catalogs did not list `kb/reference/medplum-docs-map.md`, and `kb/reference/language-rules.md` still advertised `13 languages / 68 rule files` instead of `14 languages / 73 rule files`. Validator passes regardless (it does not diff generated text), so the drift slipped past CI. Regenerated from source; added Medplum row to the languages table.
14
+
15
+ ---
16
+
17
+ ## v2.7.2 — Doc & Model-ID Consistency (2026-04-17)
18
+
19
+ ### Changed
20
+ - **Centralized Claude model IDs** — introduced `DEFAULT_CLAUDE_MODELS` dict in `scripts/_common.py` (single source of truth: `claude-opus-4-7`, `claude-sonnet-4-6`, `claude-haiku-4-5`). `scripts/generate_aider_conf.py` now imports the constant instead of hardcoding `claude-sonnet-4-6`. Next Anthropic model bump touches one file. Agent frontmatter aliases (`opus`/`sonnet`/`haiku`) stay version-free and are resolved at runtime.
21
+ - **`app/agents/ai-engineer.md`** now points to the `model-routing-patterns` skill as the single source of truth for current Claude IDs, cost tiers, and fallback chains instead of duplicating a tier table that would drift with every Anthropic release.
22
+
23
+ ### Fixed
24
+ - **`GEMINI.md` missed 5 skills from v2.7.0** — `generate:all` was not re-run before the v2.7.0 release tag, so `GEMINI.md` did not list `prompt-caching-patterns`, `json-mode-patterns`, `content-moderation-patterns`, `model-routing-patterns`, or `/mcp-builder`. Regenerated.
25
+ - **`manifest.json` stale skill counts** — `components.skills.description` and `modules.skills.description` still reported `94 skills (31 task + 31 hybrid + 32 knowledge)` instead of the current `99 skills (32 task + 31 hybrid + 36 knowledge)`. Fixed in 2 places.
26
+
27
+ ---
28
+
10
29
  ## v2.7.1 — Eject Output-Styles Fix (2026-04-17)
11
30
 
12
31
  ### Fixed
package/README.md CHANGED
@@ -10,10 +10,9 @@
10
10
 
11
11
  ---
12
12
 
13
- ## What's New in v2.7.1
13
+ ## What's New in v2.7.3
14
14
 
15
- - **`ai-toolkit eject` now copies `app/output-styles/*.md`** pre-existing gap since output-styles were introduced: eject produced a standalone `.claude/` without any system-prompt styles, so `golden-rules`, `learning`, and `explanatory` silently dropped out after ejecting. Now eject writes `.claude/output-styles/` with all source `.md` files and reports the count in the summary line: `Ejected: N agents, N skills, N rules, N output style(s)`.
16
- - **2 new `tests/test_eject.bats` cases** covering output-styles: directory created + file count matches source + `golden-rules.md` baseline, and summary line reports correct count. Test count: 664 → 666.
15
+ - **`llms.txt` + `llms-full.txt` regenerated** after PR #7 (Medplum/FHIR rules) catalogs now list `medplum-docs-map.md` and the language-rules reference advertises the correct counts: `14 languages / 73 rule files` (was `13 / 68`).
17
16
 
18
17
  See [CHANGELOG.md](CHANGELOG.md) for full history.
19
18
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "ai-toolkit",
3
3
  "description": "Professional-grade Claude Code toolkit with persona presets, skill security auditor, expanded lifecycle hooks, experimental opt-in plugin packs, benchmark harvesting, and multi-tool support.",
4
- "version": "2.7.1",
4
+ "version": "2.7.3",
5
5
  "author": {
6
6
  "name": "SoftSpark",
7
7
  "url": "https://github.com/softspark"
@@ -64,6 +64,8 @@ AI/ML integration specialist for production systems, including RAG pipeline desi
64
64
  | Complex reasoning | Large | Claude Opus, GPT-4 |
65
65
  | Local/private | Open | Llama, Mistral |
66
66
 
67
+ For current Claude model IDs, cost tiers, and fallback chains see the `model-routing-patterns` skill — the single source of truth that gets bumped with each Anthropic release.
68
+
67
69
  ### Embedding Selection
68
70
  | Use Case | Model |
69
71
  |----------|-------|
@@ -0,0 +1,56 @@
1
+ ---
2
+ language: medplum
3
+ category: coding-style
4
+ version: "1.0.0"
5
+ ---
6
+
7
+ # Medplum / FHIR Coding Style
8
+
9
+ ## Resource Structure
10
+ - Every FHIR object must include `resourceType` as first field.
11
+ - Use PascalCase for resource types (`Patient`, `ServiceRequest`), camelCase for fields (`birthDate`, `valueQuantity`).
12
+ - Never hardcode resource IDs. Let the server assign them on create.
13
+ - Include `meta.profile` when creating resources that must conform to a StructureDefinition.
14
+
15
+ ## References
16
+ - Use `createReference(resource)` from `@medplum/core` to build Reference objects.
17
+ - Always include `display` on references for human readability.
18
+ - Use `getReferenceString(resource)` for comparisons and logging — returns `ResourceType/id`.
19
+ - Use `parseReference(ref)` to extract resourceType and id from a reference string.
20
+ - Never concatenate strings to build references manually.
21
+
22
+ ## CodeableConcepts & Coding
23
+ - Always include `system`, `code`, and `display` in every Coding element.
24
+ - Use standard terminology URIs: `http://loinc.org`, `http://snomed.info/sct`, `http://hl7.org/fhir/sid/icd-10-cm`.
25
+ - Use `getCodeBySystem(cc, system)` to find codes; `setCodeBySystem(cc, system, code)` to set them.
26
+ - Prefer `CodeableConcept` over plain `Coding` when the FHIR spec allows both — it supports multiple codings and free text.
27
+
28
+ ## Identifiers
29
+ - Use `identifier` arrays with `system` + `value` for external IDs (MRN, NPI, SSN).
30
+ - Use `getIdentifier(resource, system)` and `setIdentifier(resource, system, value)` helpers.
31
+ - Identifier systems must be absolute URIs (e.g., `http://hl7.org/fhir/sid/us-npi`).
32
+ - Use `createResourceIfNoneExist(resource, 'identifier=system|value')` for idempotent creates.
33
+
34
+ ## Extensions
35
+ - Use the `extension` array with `url` and typed `value[x]` fields.
36
+ - Prefer official HL7/US Core extensions over custom ones where they exist.
37
+ - Use `getExtension(resource, url)` and `getExtensionValue(resource, url)` helpers.
38
+
39
+ ## Bundles
40
+ - Use `urn:uuid:<uuid>` for internal references between entries in a transaction Bundle.
41
+ - Every Bundle entry must have `request.method` (`POST`, `PUT`, `DELETE`) and `request.url`.
42
+ - Include `fullUrl` on entries that are referenced by other entries.
43
+ - Use conditional references (`Practitioner?identifier=npi|123`) for existing resources.
44
+
45
+ ## HIPAA-Aware Coding
46
+ - Identifiers like SSN, MRN, and insurance IDs are PHI — never log raw values to console or external services.
47
+ - Use a safe logging utility (e.g., `safeLog()`) for any output that might contain patient data. Never `console.log` raw FHIR resources.
48
+ - Reference `display` strings may contain patient names — treat as PHI in logs and error messages.
49
+ - Every new data access path or admin operation must include corresponding `AuditEvent` creation. No exceptions.
50
+ - When audit logging fails (Medplum unreachable), write to a fallback store — audit events must never be silently dropped.
51
+ - See `security.md` rules for full HIPAA, access policy, and PHI handling requirements.
52
+
53
+ ## Formatting
54
+ - Use `formatHumanName()`, `formatAddress()`, `formatDate()`, `formatQuantity()` for display strings.
55
+ - Use `getDisplayString(resource)` as a universal fallback for any resource's display name.
56
+ - Never manually concatenate name parts — FHIR names have `given[]`, `family`, `prefix[]`, `suffix[]`.
@@ -0,0 +1,51 @@
1
+ ---
2
+ language: medplum
3
+ category: frameworks
4
+ version: "1.0.0"
5
+ ---
6
+
7
+ # Medplum Frameworks
8
+
9
+ ## @medplum/core — SDK Client
10
+ - Use `MedplumClient` for all FHIR operations. Never use raw `fetch` against Medplum endpoints.
11
+ - Use `medplum.createResource()`, `readResource()`, `updateResource()`, `deleteResource()` for CRUD.
12
+ - Use `medplum.searchResources()` for typed arrays. Use `medplum.searchOne()` when expecting a single result.
13
+ - Use `medplum.executeBatch()` for transaction Bundles — groups multiple operations atomically.
14
+ - Use `medplum.upsertResource(resource, query)` for atomic create-or-update.
15
+ - Use `medplum.createResourceIfNoneExist(resource, query)` for idempotent creation.
16
+ - Configure `autoBatchTime` on MedplumClient to auto-batch concurrent GET requests. Use `Promise.all()` instead of sequential `await` to benefit from batching.
17
+
18
+ ## @medplum/fhirtypes — Type Safety
19
+ - Import FHIR types directly: `import { Patient, Observation } from '@medplum/fhirtypes'`.
20
+ - Use TypeScript types for all FHIR resources — never use `any` for resource data.
21
+ - Cast `event.input` in bot handlers: `const patient = event.input as Patient`.
22
+ - Use optional chaining for nested FHIR fields: `patient.name?.[0]?.given?.[0]`.
23
+
24
+ ## @medplum/react — UI Components
25
+ - Wrap app with `<MedplumProvider client={medplum}>` at the root.
26
+ - Use `useMedplum()` hook to access the MedplumClient instance in components.
27
+ - Use `useMedplumContext()` for client + profile + loading state together.
28
+ - Use `<ResourceForm>` for auto-generated CRUD forms, `<ResourceTable>` for display.
29
+ - Use `<SearchControl>` for searchable/filterable resource lists.
30
+ - Use `<QuestionnaireForm>` to render FHIR Questionnaires and capture responses.
31
+ - Use `useSubscription(criteria)` for real-time WebSocket data in React components.
32
+ - Requires Mantine 7+ and PostCSS with Mantine preset. Import `@mantine/core/styles.css`.
33
+
34
+ ## Bot Development
35
+ - Export a single `handler` function: `export async function handler(medplum: MedplumClient, event: BotEvent)`.
36
+ - Access trigger resource via `event.input`. Access secrets via `event.secrets`.
37
+ - Use `event.contentType` to determine input format (`application/fhir+json`, `text/plain`, `x-application/hl7-v2+er7`).
38
+ - Deploy bots via CLI for CI/CD: `medplum bot deploy <bot-name>`.
39
+ - Apply AccessPolicies to bots — restrict to minimum required resource types.
40
+ - Use Subscriptions with `channel.type: 'rest-hook'` and `channel.endpoint: 'Bot/<ID>'` for event-driven execution.
41
+
42
+ ## GraphQL
43
+ - Append `List` to resource type for searches: `PatientList(name: "Eve")`.
44
+ - Use snake_case for search parameters in GraphQL (not kebab-case): `address_city`, not `address-city`.
45
+ - Use inline fragments for reference resolution: `... on Observation { valueQuantity { value } }`.
46
+ - Use `_reference` for reverse lookups: `EncounterList(_reference: patient)`.
47
+
48
+ ## CLI (@medplum/cli)
49
+ - Use `medplum login` for auth, `medplum get`/`medplum post` for FHIR operations.
50
+ - Use `medplum bot deploy` for bot deployment in CI/CD pipelines.
51
+ - Use `medplum bulk export` for bulk data operations.
@@ -0,0 +1,61 @@
1
+ ---
2
+ language: medplum
3
+ category: patterns
4
+ version: "1.0.0"
5
+ ---
6
+
7
+ # Medplum / FHIR Patterns
8
+
9
+ ## Bundle Transactions
10
+ - Use `type: 'transaction'` for atomic multi-resource operations — all-or-nothing.
11
+ - Use `type: 'batch'` when operations are independent and partial failure is acceptable.
12
+ - Use `urn:uuid:<uuid>` in `fullUrl` for forward references between entries.
13
+ - Reference other entries via `{ reference: 'urn:uuid:<uuid>' }`.
14
+ - Use `ifNoneExist` on POST entries for conditional creation (idempotent).
15
+ - Use `ifMatch: 'W/"versionId"'` on PUT entries for optimistic concurrency.
16
+ - Use conditional references for existing resources: `Practitioner?identifier=http://hl7.org/fhir/sid/us-npi|123`.
17
+ - For large bundles (>50MB), use `Prefer: respond-async` header.
18
+
19
+ ## Search Patterns
20
+ - Use `_include=ResourceType:searchParam` to fetch referenced resources in one call.
21
+ - Use `_revinclude=ResourceType:searchParam` to fetch resources referencing your results.
22
+ - Use `:iterate` modifier for multi-hop traversal: `_include:iterate=Patient:general-practitioner`.
23
+ - Use `_count` + `_offset` for pagination; use `searchResourcePages()` for async iteration.
24
+ - Use `:contains` modifier for substring search on string params: `name:contains=eve`.
25
+ - Use `:not` modifier to exclude: `status:not=completed`.
26
+ - Use comma-separated values for OR: `status=active,on-hold`.
27
+ - Use multiple parameters for AND: `name=Simpson&birthdate=1940-03-29`.
28
+ - Prefer `searchResources()` over `search()` — returns typed array, not raw Bundle.
29
+
30
+ ## Subscription & Bot Workflows
31
+ - Create a `Subscription` resource with `criteria` (FHIR search query) and `channel.type: 'rest-hook'`.
32
+ - Point `channel.endpoint` to `Bot/<BOT_ID>` for automated processing.
33
+ - Use `subscribeToCriteria()` client-side for WebSocket real-time updates.
34
+ - Never subscribe to `AuditEvent` changes — prevents notification spirals.
35
+ - Use cron-based bots for scheduled tasks (e.g., daily reports, batch processing).
36
+
37
+ ## Access Policies
38
+ - Define `AccessPolicy.resource[]` with `resourceType` and optional `criteria`, `readonly`, `hiddenFields`, `readonlyFields`.
39
+ - Use `%profile` variable to scope data to the current user: `Observation?performer=%profile`.
40
+ - Use `%patient` variable for patient-portal access: `Observation?subject=%patient`.
41
+ - Use compartment-based access for patient-scoped isolation.
42
+ - Use `writeConstraint` with FHIRPath for state machine enforcement (e.g., prevent status rollback).
43
+ - Apply least privilege: start with no access, add specific resource types.
44
+
45
+ ## Conditional Operations
46
+ - Use `createResourceIfNoneExist(resource, query)` for idempotent creates keyed on identifier.
47
+ - Use `upsertResource(resource, query)` for atomic create-or-update in a single request.
48
+ - Use `If-None-Exist` header on POST for server-side conditional creation.
49
+
50
+ ## Patient Deduplication
51
+ - Match on `identifier` systems (MRN, SSN, insurance ID) for deterministic matching.
52
+ - Use probabilistic matching on name + birthdate + address for fuzzy matches.
53
+ - Use Patient `link` field with `type: 'replaced-by'` for merge workflows.
54
+ - Prefer `createResourceIfNoneExist()` at ingestion to prevent duplicates.
55
+
56
+ ## Questionnaire Workflows
57
+ - Create `Questionnaire` resources for form definitions. Use `linkId` for question identification.
58
+ - Use `QuestionnaireResponse` for captured answers. Link to Questionnaire via `questionnaire` field.
59
+ - Use `getQuestionnaireAnswers(response)` to extract answers as a map keyed by `linkId`.
60
+ - Automate post-submission processing with a Bot subscribed to `QuestionnaireResponse` creation.
61
+ - Use SDC (Structured Data Capture) extensions for advanced rendering and extraction.
@@ -0,0 +1,58 @@
1
+ ---
2
+ language: medplum
3
+ category: security
4
+ version: "1.0.0"
5
+ ---
6
+
7
+ # Medplum / FHIR Security
8
+
9
+ ## Authentication
10
+ - Use client credentials flow (`startClientLogin`) for backend services and integrations.
11
+ - Use authorization code flow (`startLogin` + `processCode`) for user-facing web apps.
12
+ - Never store tokens in `localStorage` in production — use secure HTTP-only cookies or server-side sessions.
13
+ - Use `refreshIfExpired()` before critical operations. Set `gracePeriod` to refresh proactively.
14
+ - Use `setBasicAuth(clientId, clientSecret)` only for server-side code, never in browser.
15
+ - Rotate client secrets via `$rotate-client-secret` operation periodically.
16
+
17
+ ## Access Policies
18
+ - Every non-admin user must have an AccessPolicy. Never leave users with default full access.
19
+ - Scope to specific resource types: list only the types the user needs.
20
+ - Use `readonly: true` or explicit `interaction` arrays to restrict write access.
21
+ - Use `criteria` with FHIR search syntax to filter visible resources (e.g., `Patient?organization=Organization/123`).
22
+ - Use `hiddenFields` to prevent sensitive fields from being returned (e.g., SSN).
23
+ - Use `readonlyFields` to allow viewing but prevent modification of specific fields.
24
+ - Use `writeConstraint` FHIRPath expressions for business rules (e.g., prevent status rollback on finalized resources).
25
+ - Test access policies by logging in as a test user with the policy applied.
26
+
27
+ ## HIPAA & Audit Logging
28
+ - Medplum automatically creates AuditEvent resources for all FHIR operations.
29
+ - Never log PHI (patient names, identifiers, health data) to application console or external services.
30
+ - Use structured audit references: reference the Patient and the accessing Practitioner in audit records.
31
+ - For custom audit trails, create AuditEvent resources with `type`, `agent`, `entity`, and `outcome`.
32
+ - Ensure audit events are never silently dropped — if Medplum is unreachable, write to a fallback store.
33
+
34
+ ## PHI Handling
35
+ - Never include PHI in URLs, query parameters, or HTTP headers.
36
+ - Use POST-based search for queries containing sensitive criteria.
37
+ - Use `Binary` resources with `securityContext` for sensitive file attachments.
38
+ - Encrypt data at rest and in transit (TLS 1.2+). Medplum hosted handles this automatically.
39
+ - Apply data retention policies — use `$expunge` operation for permanent deletion when required.
40
+
41
+ ## SMART Scopes
42
+ - Use `patient/*.read` style scopes for patient-facing apps.
43
+ - Use `user/*.read` style scopes for practitioner-facing apps.
44
+ - Validate scopes server-side on every request — do not trust client-side scope claims.
45
+ - Use `launch/patient` context for apps launched within a patient context.
46
+ - Define minimal scopes: request only the resource types and operations needed.
47
+
48
+ ## Multi-Tenant Isolation
49
+ - Use Medplum Projects for tenant isolation — each project is a separate data silo.
50
+ - Never share AccessPolicies across projects/tenants.
51
+ - Validate `meta.project` on operations when building multi-tenant middleware.
52
+ - Use separate ClientApplications per tenant for backend integrations.
53
+
54
+ ## Secrets Management
55
+ - Use Bot secrets (`event.secrets`) for API keys, connection strings, and credentials.
56
+ - Never hardcode secrets in bot source code or resource data.
57
+ - Use Medplum project-level secrets storage — accessible only by project admins.
58
+ - Rotate secrets on a regular schedule and after any suspected compromise.
@@ -0,0 +1,56 @@
1
+ ---
2
+ language: medplum
3
+ category: testing
4
+ version: "1.0.0"
5
+ ---
6
+
7
+ # Medplum / FHIR Testing
8
+
9
+ ## MockClient
10
+ - Use `MockClient` from `@medplum/mock` for unit tests — it simulates the full MedplumClient API in memory.
11
+ - Pre-populate test data with `mockClient.createResource()` before running test assertions.
12
+ - MockClient supports `search`, `searchResources`, `searchOne`, `readResource`, `updateResource`, `deleteResource`.
13
+ - MockClient does not require network access — tests run fast and offline.
14
+ - Use `new MockClient()` per test to ensure isolation between test cases.
15
+
16
+ ## Bot Unit Testing
17
+ - Test the handler function directly: `await handler(mockClient, mockEvent)`.
18
+ - Create mock `BotEvent` objects with `input`, `contentType`, `secrets`, and `bot` fields.
19
+ - Verify resource creation: call `mockClient.searchResources()` after handler execution.
20
+ - Test error paths: pass invalid input resources and assert the handler throws or returns errors.
21
+ - Test different content types: `application/fhir+json`, `text/plain`, `x-application/hl7-v2+er7`.
22
+ - Mock `event.secrets` for bots that depend on external API keys.
23
+
24
+ ## Resource Validation
25
+ - Use `validateResource(resource)` from `@medplum/core` to check resources against StructureDefinitions.
26
+ - Test that required fields produce `OperationOutcome` errors when missing.
27
+ - Test custom profiles: create a `StructureDefinition` resource, then validate resources against it.
28
+ - Use the `$validate` operation for server-side validation in integration tests.
29
+ - Test the Data Absent Reason extension when required fields may legitimately be empty.
30
+
31
+ ## Search Testing
32
+ - Test search parameter behavior: exact match vs prefix match vs substring (`name`, `name:exact`, `name:contains`).
33
+ - Verify `_include` returns related resources in the Bundle.
34
+ - Test pagination with `_count` and `_offset` parameters.
35
+ - Test token search with and without system namespace: `identifier=value` vs `identifier=system|value`.
36
+ - Test date range searches with comparison prefixes: `ge`, `le`, `gt`, `lt`.
37
+
38
+ ## Integration Testing
39
+ - Use Medplum Docker image (`medplum/medplum-server`) for local integration tests.
40
+ - Test full workflows end-to-end: create patient → create observation → search → verify.
41
+ - Verify Bundle transactions are atomic: intentionally fail one entry and confirm rollback.
42
+ - Test access policies by authenticating as users with different policies.
43
+ - Test Subscription triggers: create a Subscription, modify a matching resource, verify Bot execution.
44
+
45
+ ## Test Data Factories
46
+ - Create typed factory functions: `createTestPatient(overrides?)`, `createTestObservation(overrides?)`.
47
+ - Use realistic but synthetic data — never use real patient data in tests.
48
+ - Include only minimal required fields by default. Let tests add specific fields via overrides.
49
+ - Use `generateId()` from `@medplum/core` for unique test identifiers.
50
+ - Use standard test identifier systems: `http://example.com/test-mrn` to avoid collision with real systems.
51
+
52
+ ## Assertions
53
+ - Assert on `OperationOutcome` for error responses: check `issue[].severity`, `issue[].code`, `issue[].expression`.
54
+ - Use `isOk(outcome)` and `isNotFound(outcome)` from `@medplum/core` for status checks.
55
+ - Use `deepEquals(a, b)` for resource comparison (ignores `meta.versionId` and `meta.lastUpdated`).
56
+ - Assert reference integrity: verify `subject.reference` matches expected `Patient/id` format.
@@ -6,14 +6,14 @@ tags: [rules, languages, coding-style, testing, patterns, security]
6
6
  version: "1.0.0"
7
7
  created: "2026-04-07"
8
8
  last_updated: "2026-04-07"
9
- description: "Reference for the language-specific rules system: 13 languages, 5 categories per language, auto-detection."
9
+ description: "Reference for the language-specific rules system: 14 languages, 5 categories per language, auto-detection."
10
10
  ---
11
11
 
12
12
  # Language Rules System
13
13
 
14
14
  ## Overview
15
15
 
16
- ai-toolkit ships language-specific rule files covering 13 programming languages plus a common set (see README.md for current count). Rules are plain Markdown files injected into `CLAUDE.md` via `ai-toolkit install --local`. They provide coding-style, testing, patterns, frameworks, and security guidance specific to each language.
16
+ ai-toolkit ships language-specific rule files covering 14 languages/platforms plus a common set (see README.md for current count). Rules are plain Markdown files injected into `CLAUDE.md` via `ai-toolkit install --local`. They provide coding-style, testing, patterns, frameworks, and security guidance specific to each language.
17
17
 
18
18
  Rules are distinct from skills: rules are injected as static text into `CLAUDE.md` and are always visible to Claude, whereas skills are loaded contextually by agents.
19
19
 
@@ -48,10 +48,11 @@ app/rules/
48
48
  ├── csharp/
49
49
  ├── php/
50
50
  ├── cpp/
51
- └── ruby/
51
+ ├── ruby/
52
+ └── medplum/
52
53
  ```
53
54
 
54
- **Total: 13 directories × 5 files each + 3 standalone = 68 rule files** (see README.md for canonical count)
55
+ **Total: 14 directories × 5 files each + 3 standalone = 73 rule files** (see README.md for canonical count)
55
56
 
56
57
  ## Supported Languages
57
58
 
@@ -70,6 +71,7 @@ app/rules/
70
71
  | PHP | `rules/php/` | `composer.json` |
71
72
  | C++ | `rules/cpp/` | `CMakeLists.txt`, `Makefile`, `*.cpp` |
72
73
  | Ruby | `rules/ruby/` | `Gemfile`, `*.gemspec` |
74
+ | Medplum | `rules/medplum/` | `medplum.config.mts`, `medplum.config.ts` |
73
75
 
74
76
  ## Rule Categories
75
77
 
@@ -107,6 +109,7 @@ Scans for configuration files defined in each module's `auto_detect` list in `ma
107
109
  10. `Package.swift` → Swift
108
110
  11. `*.csproj` or `*.sln` → C#
109
111
  12. `CMakeLists.txt` or `Makefile` → C++
112
+ 13. `medplum.config.mts` or `medplum.config.ts` → Medplum
110
113
 
111
114
  ### Phase 2: Source file extensions (actual code presence)
112
115
 
@@ -163,6 +166,7 @@ Language rules are tracked as modules in `manifest.json`:
163
166
  | `rules-php` | PHP-specific rules |
164
167
  | `rules-cpp` | C++-specific rules |
165
168
  | `rules-ruby` | Ruby-specific rules |
169
+ | `rules-medplum` | Medplum/FHIR healthcare platform rules |
166
170
 
167
171
  ## Rules vs Skills
168
172