You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs(spec): add instructions assembly with base + per-host overlays
Replace the single-file instructions model with a base + overlay
assembly system. Overlays declare positioning (append, prepend, or
insert at named markers) via YAML frontmatter. Updates spec, schema,
and design document (Decision 15).
Fixes#5
Copy file name to clipboardExpand all lines: docs/plans/2026-02-14-ccpkg-design.md
+14Lines changed: 14 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -362,6 +362,20 @@ The ccpkg format targets multiple AI coding assistant hosts, but each host has f
362
362
363
363
---
364
364
365
+
### 15. Instructions Assembly — Base + Per-Host Overlays
366
+
367
+
**Decision**: The `components.instructions` field supports both a simple string form (single file) and a structured form declaring a base file with optional per-host overlay files. Overlays declare their assembly position (`append`, `prepend`, or `insert` at a named marker) via YAML frontmatter. The installer assembles the final instructions output per host at install time.
368
+
369
+
**Rationale**: The original "one file, copy everywhere" model is too rigid for real-world packages. Package authors need shared context that applies to all hosts (project conventions, error handling patterns, tool usage guidelines) combined with host-specific tuning (e.g., "use Claude Code's subagent spawning" or "enable Copilot agent mode"). Rather than maintaining entirely separate instruction files per host (which defeats the shared-base purpose), the assembly model lets authors write common content once and layer host-specific additions. Three positioning strategies cover real needs: append for additive content, prepend for prerequisite notices, and marker-based insertion for content that belongs mid-document. Overlay frontmatter keeps the positioning declaration co-located with the content it governs.
370
+
371
+
**Alternatives considered**:
372
+
373
+
1.**Separate instruction files per host** -- Each host gets its own complete file (`claude-instructions.md`, `copilot-instructions.md`). Simple to implement but leads to content duplication. When shared content changes, authors must update N files. Rejected because it undermines the DRY principle and scales poorly with host count.
374
+
2.**Template language with conditionals** -- Use a templating syntax (e.g., Handlebars, Jinja) with `{{#if host == "claude"}}` blocks. Powerful but introduces a template engine dependency, makes the raw files hard to read, and is overkill for what is typically "shared base + small per-host additions." Rejected for complexity.
375
+
3.**mappings.json only (previous design)** -- Map a single canonical file to host-specific filenames without any content variation. Already proven insufficient — the filename mapping exists via `targets.*.instructions_file`, but the content is identical everywhere. Superseded by the assembly model which adds content variation on top of filename mapping.
376
+
377
+
---
378
+
365
379
## Relationship to Existing Specifications
366
380
367
381
ccpkg does not replace existing standards. It composes them.
│ ├── base.md # Base instructions (shared across all hosts)
163
+
│ └── hosts/ # Per-host overlay files
164
+
│ ├── claude.md # Claude-specific overlay
165
+
│ ├── copilot.md # Copilot-specific overlay
166
+
│ └── gemini.md # Gemini-specific overlay
164
167
├── config.schema.json # OPTIONAL — JSON Schema for config
165
168
├── icon.png # OPTIONAL — package icon (PNG, max 512x512)
166
169
└── LICENSE # OPTIONAL — license file
@@ -224,7 +227,7 @@ The `components` object declares which component types are included in the archi
224
227
|`hooks`|`string`| Path to a `hooks.json` file within the archive. |
225
228
|`mcp`|`string`| Path to an `.mcp.json` template file within the archive. |
226
229
|`lsp`|`string`| Path to an `.lsp.json` template file within the archive. |
227
-
|`instructions`|`string`| Path to the canonical `INSTRUCTIONS.md`file within the archive. |
230
+
|`instructions`|`string`or `object`| Instructions declaration. A string is a path to a single instructions file. An object declares a base file and optional per-host overlays for assembly. See [Instructions](#instructions). |
228
231
229
232
Each component field that accepts an array (`skills`, `agents`, `commands`) supports two declaration forms:
230
233
@@ -318,7 +321,7 @@ The `targets` object supports the following standard fields. Tool-specific adapt
318
321
|`hook_events`|`object`| Map of canonical event names to host-native event names |
319
322
|`mcp_env_prefix`|`string`| Environment variable prefix for MCP server credential injection |
320
323
321
-
`instructions_file` — An OPTIONAL string specifying the filename to which the canonical `INSTRUCTIONS.md`is copied for that tool.
324
+
`instructions_file` — An OPTIONAL string specifying the filename to which the assembled instructions content is written for that tool.
322
325
323
326
`hook_events` — An OPTIONAL object that maps canonical event names to the host's native event type names. Keys are canonical event names (lowercase-hyphenated); values are the host's native event type strings.
324
327
@@ -408,7 +411,13 @@ Codex CLI has minimal hook support (only `AfterToolUse` and `notify`). OpenCode
408
411
],
409
412
"hooks": "hooks/hooks.json",
410
413
"mcp": "mcp/.mcp.json",
411
-
"instructions": "instructions/INSTRUCTIONS.md"
414
+
"instructions": {
415
+
"base": "instructions/base.md",
416
+
"hosts": {
417
+
"claude": "instructions/hosts/claude.md",
418
+
"copilot": "instructions/hosts/copilot.md"
419
+
}
420
+
}
412
421
},
413
422
"config": {
414
423
"API_BASE_URL": {
@@ -818,30 +827,109 @@ LSP (Language Server Protocol) server configurations enable packages to provide
818
827
819
828
### Instructions
820
829
821
-
Instructions are canonical documentation files that provide guidance to the AI coding assistant. The `instructions/INSTRUCTIONS.md` file contains the universal instruction content. The `instructions/mappings.json` file maps this content to tool-specific filenames.
830
+
Instructions are documentation files that provide guidance to the AI coding assistant. The `components.instructions` field declares the instruction content to be assembled and installed.
822
831
823
-
**Requirements:**
832
+
#### Simple Form
833
+
834
+
When `components.instructions` is a string, it is a path to a single instructions file. The installer copies this file to the host-specific filename defined in `targets.*.instructions_file`. This is equivalent to the base-only assembly model with no per-host overlays.
835
+
836
+
```json
837
+
"instructions": "instructions/base.md"
838
+
```
824
839
825
-
- If `components.instructions` is declared, the referenced file MUST exist in the archive.
826
-
- A `mappings.json` file SHOULD be present alongside the instructions file.
827
-
- Installers MUST copy the canonical instructions file to the appropriate tool-specific filename during installation.
840
+
#### Structured Form (Base + Overlay Assembly)
828
841
829
-
**mappings.json Format:**
842
+
When `components.instructions` is an object, it declares a base file and optional per-host overlay files. The installer assembles the final output by combining the base content with the overlay for the active host.
830
843
831
844
```json
832
-
{
833
-
"claude": "CLAUDE.md",
834
-
"codex": "AGENTS.md",
835
-
"copilot": ".github/copilot-instructions.md",
836
-
"gemini": "GEMINI.md"
845
+
"instructions": {
846
+
"base": "instructions/base.md",
847
+
"hosts": {
848
+
"claude": "instructions/hosts/claude.md",
849
+
"copilot": "instructions/hosts/copilot.md",
850
+
"gemini": "instructions/hosts/gemini.md"
851
+
}
837
852
}
838
853
```
839
854
840
-
The keys are tool identifiers matching those used in the `targets` manifest field. The values are relative file paths (from the project or user config root) where the instructions should be written.
855
+
| Field | Required | Type | Description |
856
+
|---|---|---|---|
857
+
|`base`| REQUIRED |`string`| Path to the base instructions file within the archive. |
858
+
|`hosts`| OPTIONAL |`object`| Map of host identifiers to overlay file paths within the archive. Keys match identifiers used in the `targets` manifest field. |
859
+
860
+
#### Overlay Files
861
+
862
+
Overlay files are Markdown files with YAML frontmatter that declares how the overlay content is positioned relative to the base content.
863
+
864
+
**Frontmatter fields:**
865
+
866
+
| Field | Required | Type | Description |
867
+
|---|---|---|---|
868
+
|`position`| OPTIONAL |`string`| One of `append`, `prepend`, or `insert`. Default: `append`. |
869
+
|`marker`| Conditional |`string`| Name of the insertion marker in the base file. REQUIRED when `position` is `insert`. |
870
+
871
+
**Example overlay — append (default):**
872
+
873
+
```markdown
874
+
---
875
+
position: append
876
+
---
877
+
## Claude-Specific Guidelines
878
+
879
+
Use Claude Code's native subagent spawning for parallel research tasks.
880
+
```
881
+
882
+
**Example overlay — prepend:**
883
+
884
+
```markdown
885
+
---
886
+
position: prepend
887
+
---
888
+
> This package requires Copilot agent mode. Enable it in VS Code settings.
889
+
```
890
+
891
+
**Example overlay — insert at marker:**
892
+
893
+
```markdown
894
+
---
895
+
position: insert
896
+
marker: host-tools
897
+
---
898
+
When using Gemini CLI, prefer the built-in extension system for tool management.
899
+
```
900
+
901
+
Where the base file contains a named marker at the desired insertion point:
902
+
903
+
```markdown
904
+
## Tool Usage
905
+
906
+
General tool guidelines here...
907
+
908
+
<!-- ccpkg:host-tools -->
909
+
910
+
## Error Handling
911
+
...
912
+
```
913
+
914
+
#### Assembly Rules
915
+
916
+
|`position`|`marker`| Behavior |
917
+
|---|---|---|
918
+
|`append`| ignored | Overlay content appended after base content |
919
+
|`prepend`| ignored | Overlay content prepended before base content |
920
+
|`insert`| REQUIRED | Replaces `<!-- ccpkg:{marker} -->` in base with overlay content |
921
+
922
+
- If no overlay exists for the active host, the base content is used as-is.
923
+
- If `position` is `insert` but the marker `<!-- ccpkg:{marker} -->` is not found in the base file, the installer MUST report an error.
924
+
- The overlay's YAML frontmatter MUST be stripped before assembly — only the Markdown body is included in the output.
925
+
- The assembled output is written to the host-specific filename defined in `targets.*.instructions_file`.
926
+
- If the active host is not present in either `hosts` or `targets`, the installer SHOULD write the base content as `INSTRUCTIONS.md` and emit a warning.
841
927
842
-
If `targets` includes an `instructions_file` override for a tool, that value takes precedence over `mappings.json`. Authors SHOULD use `targets.*.instructions_file` for simple cases. Use `mappings.json` when the package needs to support hosts not listed in targets.
928
+
#### Requirements
843
929
844
-
If the active host tool is not present in either `targets` or `mappings.json`, the installer SHOULD copy the file as `INSTRUCTIONS.md` and emit a warning.
930
+
- If `components.instructions` is declared (in either form), the referenced base file MUST exist in the archive.
931
+
- All overlay files declared in `hosts` MUST exist in the archive.
932
+
- Marker names MUST match the pattern `[a-z0-9]+(-[a-z0-9]+)*` (lowercase alphanumeric with hyphens).
845
933
846
934
---
847
935
@@ -1523,7 +1611,7 @@ Package authors SHOULD use per-component host scoping (see [Components Object](#
1523
1611
1524
1612
The following mechanisms handle tool-specific differences:
1525
1613
1526
-
1.**Instruction file mapping.** The `instructions/mappings.json` file and `targets.*.instructions_file`manifest field map canonical `INSTRUCTIONS.md` content to tool-specific filenames (`CLAUDE.md`, `AGENTS.md`, `.github/copilot-instructions.md`, `GEMINI.md`).
1614
+
1.**Instruction file assembly.** The `components.instructions` field supports base + per-host overlay assembly. Overlays declare positioning (`append`, `prepend`, `insert` at marker) via YAML frontmatter. The `targets.*.instructions_file` field maps assembled output to host-specific filenames (`CLAUDE.md`, `AGENTS.md`, `.github/copilot-instructions.md`, `GEMINI.md`).
1527
1615
1528
1616
2.**Targets object.** The `targets` field in `manifest.json` allows authors to declare tool-specific overrides. Each tool adapter defines its own schema for the target value object.
1529
1617
@@ -1533,7 +1621,7 @@ The following mechanisms handle tool-specific differences:
1533
1621
1534
1622
### Portability Guidelines for Authors
1535
1623
1536
-
- Use `INSTRUCTIONS.md` and `mappings.json` for instructions. Do not hardcode tool-specific filenames.
1624
+
- Use `instructions.base` with per-host overlays for instructions. Do not hardcode tool-specific filenames.
1537
1625
- Prefer MCP for tool integration over host-specific mechanisms.
1538
1626
- Use the `compatibility` field to declare minimum host versions rather than excluding hosts.
1539
1627
- Test packages across multiple hosts when possible.
0 commit comments