container-superposition 0.1.4 → 0.1.5

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.
Files changed (90) hide show
  1. package/README.md +72 -1370
  2. package/dist/scripts/init.js +333 -185
  3. package/dist/scripts/init.js.map +1 -1
  4. package/dist/tool/commands/adopt.d.ts +62 -0
  5. package/dist/tool/commands/adopt.d.ts.map +1 -0
  6. package/dist/tool/commands/adopt.js +767 -0
  7. package/dist/tool/commands/adopt.js.map +1 -0
  8. package/dist/tool/commands/hash.d.ts +36 -0
  9. package/dist/tool/commands/hash.d.ts.map +1 -0
  10. package/dist/tool/commands/hash.js +242 -0
  11. package/dist/tool/commands/hash.js.map +1 -0
  12. package/dist/tool/commands/plan.d.ts +2 -0
  13. package/dist/tool/commands/plan.d.ts.map +1 -1
  14. package/dist/tool/commands/plan.js +262 -42
  15. package/dist/tool/commands/plan.js.map +1 -1
  16. package/dist/tool/schema/project-config.d.ts +15 -0
  17. package/dist/tool/schema/project-config.d.ts.map +1 -0
  18. package/dist/tool/schema/project-config.js +359 -0
  19. package/dist/tool/schema/project-config.js.map +1 -0
  20. package/dist/tool/schema/types.d.ts +39 -1
  21. package/dist/tool/schema/types.d.ts.map +1 -1
  22. package/dist/tool/utils/backup.d.ts +23 -0
  23. package/dist/tool/utils/backup.d.ts.map +1 -0
  24. package/dist/tool/utils/backup.js +123 -0
  25. package/dist/tool/utils/backup.js.map +1 -0
  26. package/docs/README.md +12 -2
  27. package/docs/adopt.md +196 -0
  28. package/docs/custom-patches.md +1 -1
  29. package/docs/discovery-commands.md +55 -3
  30. package/docs/examples.md +40 -6
  31. package/docs/filesystem-contract.md +58 -0
  32. package/docs/hash.md +183 -0
  33. package/docs/minimal-and-editor.md +1 -1
  34. package/docs/overlays.md +60 -0
  35. package/docs/presets-architecture.md +1 -1
  36. package/docs/presets.md +1 -1
  37. package/docs/publishing.md +36 -23
  38. package/docs/security.md +43 -0
  39. package/docs/specs/001-verbose-plan-graph/checklists/requirements.md +36 -0
  40. package/docs/specs/001-verbose-plan-graph/contracts/plan-verbose-output.md +96 -0
  41. package/docs/specs/001-verbose-plan-graph/data-model.md +111 -0
  42. package/docs/specs/001-verbose-plan-graph/plan.md +127 -0
  43. package/docs/specs/001-verbose-plan-graph/quickstart.md +106 -0
  44. package/docs/specs/001-verbose-plan-graph/research.md +100 -0
  45. package/docs/specs/001-verbose-plan-graph/spec.md +128 -0
  46. package/docs/specs/001-verbose-plan-graph/tasks.md +223 -0
  47. package/docs/specs/002-superposition-config-file/checklists/requirements.md +36 -0
  48. package/docs/specs/002-superposition-config-file/contracts/init-project-config.md +98 -0
  49. package/docs/specs/002-superposition-config-file/data-model.md +126 -0
  50. package/docs/specs/002-superposition-config-file/plan.md +208 -0
  51. package/docs/specs/002-superposition-config-file/quickstart.md +140 -0
  52. package/docs/specs/002-superposition-config-file/research.md +144 -0
  53. package/docs/specs/002-superposition-config-file/spec.md +130 -0
  54. package/docs/specs/002-superposition-config-file/tasks.md +213 -0
  55. package/docs/team-workflow.md +27 -1
  56. package/docs/workflows.md +136 -0
  57. package/overlays/.presets/sdd.yml +84 -0
  58. package/overlays/README.md +7 -1
  59. package/overlays/amp/README.md +70 -0
  60. package/overlays/amp/devcontainer.patch.json +3 -0
  61. package/overlays/amp/overlay.yml +15 -0
  62. package/overlays/amp/setup.sh +21 -0
  63. package/overlays/amp/verify.sh +21 -0
  64. package/overlays/claude-code/README.md +83 -0
  65. package/overlays/claude-code/devcontainer.patch.json +3 -0
  66. package/overlays/claude-code/overlay.yml +15 -0
  67. package/overlays/claude-code/setup.sh +21 -0
  68. package/overlays/claude-code/verify.sh +21 -0
  69. package/overlays/gemini-cli/README.md +77 -0
  70. package/overlays/gemini-cli/devcontainer.patch.json +3 -0
  71. package/overlays/gemini-cli/overlay.yml +15 -0
  72. package/overlays/gemini-cli/setup.sh +21 -0
  73. package/overlays/gemini-cli/verify.sh +21 -0
  74. package/overlays/opencode/README.md +76 -0
  75. package/overlays/opencode/devcontainer.patch.json +3 -0
  76. package/overlays/opencode/overlay.yml +14 -0
  77. package/overlays/opencode/setup.sh +21 -0
  78. package/overlays/opencode/verify.sh +21 -0
  79. package/overlays/spec-kit/README.md +181 -0
  80. package/overlays/spec-kit/devcontainer.patch.json +6 -0
  81. package/overlays/spec-kit/overlay.yml +19 -0
  82. package/overlays/spec-kit/setup.sh +45 -0
  83. package/overlays/spec-kit/verify.sh +33 -0
  84. package/overlays/windsurf-cli/README.md +69 -0
  85. package/overlays/windsurf-cli/devcontainer.patch.json +3 -0
  86. package/overlays/windsurf-cli/overlay.yml +15 -0
  87. package/overlays/windsurf-cli/setup.sh +21 -0
  88. package/overlays/windsurf-cli/verify.sh +21 -0
  89. package/package.json +1 -1
  90. package/tool/schema/config.schema.json +138 -9
package/docs/README.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  Complete documentation for the container-superposition devcontainer scaffolding system.
4
4
 
5
+ ## Spec-First Development
6
+
7
+ Feature work is governed by specs committed under `docs/specs/`. Implementation
8
+ must not begin until the relevant spec is committed and reviewed.
9
+
5
10
  ## 📚 Documentation Index
6
11
 
7
12
  ### Getting Started
@@ -19,10 +24,15 @@ Complete documentation for the container-superposition devcontainer scaffolding
19
24
 
20
25
  ### User Guides
21
26
 
27
+ - **[Adopt Command](adopt.md)** - Migrate an existing `.devcontainer/` to the overlay-based workflow
28
+ - **[Hash Command](hash.md)** - Deterministic environment fingerprint for drift detection and reproducibility
22
29
  - **[Presets Guide](presets.md)** - Using stack presets for common development scenarios
23
30
  - **[Messaging Comparison](messaging-comparison.md)** - Choosing between RabbitMQ, Redpanda, and NATS
24
31
  - **[Messaging Quick Start](messaging-quick-start.md)** - Getting started with messaging overlays
25
32
  - **[Observability Workflow](observability-workflow.md)** - Setting up monitoring and tracing
33
+ - **[Workflows and Regeneration](workflows.md)** - Regeneration, backups, and manifest-based workflows
34
+ - **[Filesystem Contract](filesystem-contract.md)** - What the tool writes and what to edit
35
+ - **[Security Considerations](security.md)** - Development-only risks and best practices
26
36
 
27
37
  ### Development
28
38
 
@@ -209,7 +219,7 @@ When published to npm, includes:
209
219
  - ✅ Type definitions and schema (`tool/schema/`)
210
220
  - ✅ Documentation
211
221
 
212
- **Package size**: ~122 KB compressed, ~462 KB unpacked
222
+ **Package size**: Varies by release (use `npm pack --dry-run`)
213
223
 
214
224
  ## 🤝 Contributing
215
225
 
@@ -228,7 +238,7 @@ MIT - See [LICENSE](../LICENSE)
228
238
 
229
239
  - **Repository**: <https://github.com/veggerby/container-superposition>
230
240
  - **Issues**: <https://github.com/veggerby/container-superposition/issues>
231
- - **npm Package**: <https://www.npmjs.com/package/container-superposition> (once published)
241
+ - **npm Package**: <https://www.npmjs.com/package/container-superposition>
232
242
 
233
243
  ## 🆘 Support
234
244
 
package/docs/adopt.md ADDED
@@ -0,0 +1,196 @@
1
+ # Adopt Command
2
+
3
+ The `adopt` command helps you **migrate an existing `.devcontainer/` configuration** to Container Superposition's overlay-based workflow.
4
+
5
+ It scans your current `devcontainer.json` and any linked `docker-compose.yml` files, matches their contents against all available overlays, and produces:
6
+
7
+ 1. **`superposition.json`** — the manifest written to the **project root** (next to your `src/`, `package.json`, etc.), ready to commit and share with your team
8
+ 2. **`.devcontainer/custom/devcontainer.patch.json`** — any config that has no overlay equivalent (custom features, extensions, mounts, remoteEnv, etc.)
9
+ 3. **`.devcontainer/custom/docker-compose.patch.yml`** — any compose services that have no overlay equivalent
10
+
11
+ The custom patches in `custom/` are automatically merged on every `regen`, so your project-specific configuration is never lost.
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ # Analyse your existing .devcontainer/ (prints a report, writes nothing)
17
+ npx container-superposition adopt --dry-run
18
+
19
+ # Run the analysis and write the generated files
20
+ npx container-superposition adopt
21
+
22
+ # Force-overwrite any existing superposition.json / custom/ files
23
+ npx container-superposition adopt --force
24
+ ```
25
+
26
+ ## Docker Compose Devcontainers
27
+
28
+ `adopt` fully supports compose-based devcontainers. It reads the
29
+ `dockerComposeFile` field in `devcontainer.json` to locate the right compose
30
+ file(s), including:
31
+
32
+ - **Single file** (string): `"dockerComposeFile": "docker-compose.yml"`
33
+ - **Multiple files** (array): `"dockerComposeFile": ["base.yml", "override.yml"]`
34
+ - **Relative paths** pointing outside the `.devcontainer/` directory:
35
+ `"dockerComposeFile": "../docker-compose.yml"`
36
+
37
+ All referenced compose files are analysed. Every service with a recognised
38
+ image (postgres, redis, grafana, jaeger, …) is mapped to the corresponding
39
+ overlay. Services with no overlay equivalent are written to
40
+ `custom/docker-compose.patch.yml` so they are preserved across regenerations.
41
+
42
+ ## How It Works
43
+
44
+ ### Detection
45
+
46
+ The command builds its detection tables **dynamically from the overlay registry** — there are no hardcoded overlay names. For every overlay it reads:
47
+
48
+ | Source | What is extracted |
49
+ | -------------------------------------------------------------- | ------------------------- |
50
+ | `devcontainer.patch.json` → `features` | Feature URI → overlay ID |
51
+ | `devcontainer.patch.json` → `customizations.vscode.extensions` | Extension ID → overlay ID |
52
+ | `docker-compose.yml` → service `image` | Image prefix → overlay ID |
53
+
54
+ When multiple overlays share the same feature (e.g. both `nodejs` and `bun`
55
+ include the Node.js devcontainer feature), the one whose ID best matches the
56
+ feature's own name wins.
57
+
58
+ ### Detection signals (in priority order)
59
+
60
+ 1. **Devcontainer features** — e.g. `ghcr.io/devcontainers/features/node:1` → `nodejs` (confidence: **exact**)
61
+ 2. **Docker Compose service images** — e.g. `postgres:16-alpine` → `postgres` (confidence: **exact**)
62
+ 3. **VS Code extensions** — e.g. `ms-python.python` → `python` (confidence: **heuristic**)
63
+ 4. **Remote environment variables** — e.g. `POSTGRES_*` → `postgres` (confidence: **heuristic**)
64
+
65
+ ### Unmatched items → `custom/`
66
+
67
+ Anything that cannot be mapped to an overlay is preserved in the `custom/`
68
+ directory, which is merged automatically on every `regen`:
69
+
70
+ | Unmatched item | Written to |
71
+ | ----------------------------------------------- | --------------------------------------------------------------------- |
72
+ | Unknown features | `custom/devcontainer.patch.json` → `features` |
73
+ | Unknown VS Code extensions | `custom/devcontainer.patch.json` → `customizations.vscode.extensions` |
74
+ | Custom `mounts` | `custom/devcontainer.patch.json` → `mounts` |
75
+ | Non-default `remoteUser` | `custom/devcontainer.patch.json` → `remoteUser` |
76
+ | Custom `postCreateCommand` / `postStartCommand` | `custom/devcontainer.patch.json` |
77
+ | Unknown compose services | `custom/docker-compose.patch.yml` → `services` |
78
+
79
+ ## Backup Behaviour
80
+
81
+ The same backup logic as `regen` is used:
82
+
83
+ | Condition | What happens |
84
+ | ---------------------------- | ------------------------------ |
85
+ | Inside a git repo (default) | No backup — git tracks history |
86
+ | Outside a git repo (default) | Backup created automatically |
87
+ | `--backup` flag | Backup always created |
88
+ | `--no-backup` flag | Backup always skipped |
89
+
90
+ Backups are placed next to the `.devcontainer/` directory as
91
+ `.devcontainer.backup-<timestamp>/`, and the corresponding glob patterns are
92
+ automatically added to `.gitignore`.
93
+
94
+ ## Options
95
+
96
+ | Option | Description |
97
+ | --------------------- | ---------------------------------------------------------------------------- |
98
+ | `-d, --dir <path>` | Path to the existing `.devcontainer/` directory (default: `./.devcontainer`) |
99
+ | `--dry-run` | Print the analysis without writing any files |
100
+ | `--force` | Overwrite existing `superposition.json` / `custom/` files |
101
+ | `--backup` | Force a backup even when inside a git repo |
102
+ | `--no-backup` | Disable backup creation even when it would normally be performed |
103
+ | `--backup-dir <path>` | Custom backup directory location |
104
+ | `--json` | Output analysis as JSON (useful for scripting) |
105
+
106
+ ## Example Output
107
+
108
+ ```
109
+ ╭──────────────────────╮
110
+ │ 🔍 Adopt Analysis │
111
+ ╰──────────────────────╯
112
+
113
+ Analysing .devcontainer/devcontainer.json...
114
+ Analysing .devcontainer/docker-compose.yml...
115
+
116
+ Detected features / services → suggested overlays
117
+ ────────────────────────────────────────────────────────────────────────────────
118
+ Source → Overlay Confidence
119
+ ────────────────────────────────────────────────────────────────────────────────────────────────
120
+ ghcr.io/devcontainers/features/node:1 → nodejs exact
121
+ service: postgres (image: postgres:16-alpine) → postgres exact
122
+ service: redis (image: redis:7-alpine) → redis exact
123
+
124
+ Items with no overlay equivalent → custom/
125
+ ────────────────────────────────────────────────────────────────────────────────
126
+ Source Action
127
+ ────────────────────────────────────────────────────────────────────────────────────────────────
128
+ ghcr.io/corp/internal-tools:1 No overlay covers this feature — preserve in custom/devcontainer.patch.json
129
+ service: my-app (image: my-registry/my-app:latest) No overlay covers this service — preserve in custom/docker-compose.patch.yml
130
+
131
+ Suggested command:
132
+ container-superposition init --stack compose --language nodejs --database postgres,redis
133
+
134
+ 💡 Custom patches will be written to .devcontainer/custom/ to preserve
135
+ any configuration that has no overlay equivalent.
136
+ ```
137
+
138
+ ## After Adopt
139
+
140
+ Once `adopt` has run:
141
+
142
+ 1. **Review `superposition.json`** — verify the detected overlays are correct; add or remove as needed.
143
+ 2. **Review `custom/` patches** — inspect what was preserved and trim anything no longer needed.
144
+ 3. **Regenerate** — rebuild your `.devcontainer/` from the manifest:
145
+ ```bash
146
+ npx container-superposition regen
147
+ ```
148
+ 4. **Commit `superposition.json`** and, if applicable, `custom/` patches.
149
+
150
+ ## JSON Output
151
+
152
+ Use `--json` to get machine-readable output for scripting or CI workflows:
153
+
154
+ ```bash
155
+ npx container-superposition adopt --dry-run --json | jq .suggestedOverlays
156
+ ```
157
+
158
+ The JSON object contains:
159
+
160
+ ```jsonc
161
+ {
162
+ "dir": "/absolute/path/to/.devcontainer",
163
+ "detections": [
164
+ {
165
+ "source": "ghcr.io/devcontainers/features/node:1",
166
+ "overlayId": "nodejs",
167
+ "confidence": "exact",
168
+ "sourceType": "feature",
169
+ },
170
+ // ...
171
+ ],
172
+ "unmatchedItems": [
173
+ {
174
+ "source": "ghcr.io/corp/internal-tools:1",
175
+ "reason": "No overlay covers this feature — preserve in custom/devcontainer.patch.json",
176
+ },
177
+ // ...
178
+ ],
179
+ "customDevcontainerPatch": {
180
+ /* or null */
181
+ },
182
+ "customComposePatch": {
183
+ /* or null */
184
+ },
185
+ "suggestedStack": "compose",
186
+ "suggestedOverlays": ["nodejs", "postgres", "redis"],
187
+ "suggestedCommand": "container-superposition init --stack compose --language nodejs --database postgres,redis",
188
+ }
189
+ ```
190
+
191
+ ## See Also
192
+
193
+ - [Team Workflow](team-workflow.md) — Manifest-first team collaboration workflow
194
+ - [Custom Patches](custom-patches.md) — How `custom/` patches are merged
195
+ - [Workflows and Regeneration](workflows.md) — Regeneration and backup details
196
+ - [Quick Reference](quick-reference.md) — All commands at a glance
@@ -417,7 +417,7 @@ The `superposition.json` manifest tracks whether customizations are present:
417
417
 
418
418
  ```json
419
419
  {
420
- "version": "0.1.0",
420
+ "version": "X.Y.Z",
421
421
  "baseTemplate": "compose",
422
422
  "overlays": ["nodejs", "postgres"],
423
423
  "customizations": {
@@ -223,6 +223,9 @@ The `plan` command shows a dry-run preview of what will be generated, including
223
223
  ```bash
224
224
  # Preview generation for postgres and grafana
225
225
  npx container-superposition plan --stack compose --overlays postgres,grafana
226
+
227
+ # Preview generation from an existing manifest
228
+ npx container-superposition plan --from-manifest .devcontainer/superposition.json
226
229
  ```
227
230
 
228
231
  **Output:**
@@ -265,12 +268,18 @@ Files to Create/Modify:
265
268
 
266
269
  ### Required Options
267
270
 
268
- - `--stack <type>` - Base template: `plain` or `compose`
271
+ - `--stack <type>` - Base template: `plain` or `compose` when planning from explicit overlays
269
272
  - `--overlays <list>` - Comma-separated list of overlay IDs
270
273
 
274
+ ### Manifest Input
275
+
276
+ - `--from-manifest <path>` - Load stack and overlay roots from an existing `superposition.json`
277
+ - Use either `--overlays` or `--from-manifest` for a given `plan` invocation
278
+
271
279
  ### Optional Options
272
280
 
273
281
  - `--port-offset <number>` - Add offset to all exposed ports
282
+ - `--verbose` - Explain why each overlay was included in the resolved plan
274
283
  - `--json` - Output as JSON
275
284
 
276
285
  ### Port Offset Example
@@ -308,6 +317,35 @@ Auto-Added Dependencies:
308
317
  + prometheus (Prometheus)
309
318
  ```
310
319
 
320
+ ### Verbose Dependency Narration
321
+
322
+ Add `--verbose` when you want the plan to explain why each overlay appears in the final result:
323
+
324
+ ```bash
325
+ npx container-superposition plan --stack compose --overlays grafana --verbose
326
+ ```
327
+
328
+ **Additional output:**
329
+
330
+ ```text
331
+ Dependency Resolution:
332
+ grafana (Grafana)
333
+ - selected directly by the user
334
+ prometheus (Prometheus)
335
+ - required by grafana
336
+ - path: grafana -> prometheus
337
+ ```
338
+
339
+ This keeps the standard summary intact and adds the reasoning behind direct selections, auto-added dependencies, and failure notes when resolution cannot complete cleanly.
340
+
341
+ The same verbose explanation works for existing manifests:
342
+
343
+ ```bash
344
+ npx container-superposition plan --from-manifest .devcontainer/superposition.json --verbose
345
+ ```
346
+
347
+ In manifest mode, overlays loaded from `superposition.json` are treated as the explicit root set and auto-added dependencies are still explained separately.
348
+
311
349
  ### Conflict Detection
312
350
 
313
351
  The `plan` command detects conflicts before generation:
@@ -334,6 +372,12 @@ npx container-superposition plan --stack compose --overlays docker-in-docker,doc
334
372
  ```bash
335
373
  # Get plan as JSON
336
374
  npx container-superposition plan --stack compose --overlays postgres --json
375
+
376
+ # Include structured dependency explanations
377
+ npx container-superposition plan --stack compose --overlays grafana --json --verbose
378
+
379
+ # Include structured dependency explanations from a manifest
380
+ npx container-superposition plan --from-manifest .devcontainer/superposition.json --json --verbose
337
381
  ```
338
382
 
339
383
  **Example output:**
@@ -357,11 +401,19 @@ npx container-superposition plan --stack compose --overlays postgres --json
357
401
  ".devcontainer/docker-compose.yml",
358
402
  ".devcontainer/.env.example",
359
403
  ".devcontainer/README.md"
360
- ],
361
- "portOffset": 0
404
+ ]
362
405
  }
363
406
  ```
364
407
 
408
+ When `--verbose` is also present, the JSON output includes a `verbose` object with:
409
+
410
+ - one entry per included overlay
411
+ - direct-vs-dependency selection type
412
+ - input mode metadata for overlay-list vs manifest planning
413
+ - parent reasons for inclusion
414
+ - ordered dependency paths for transitive inclusions
415
+ - resolution notes for skipped overlays or conflicts
416
+
365
417
  ## Scripting Examples
366
418
 
367
419
  ### Export All Overlays to JSON
package/docs/examples.md CHANGED
@@ -10,6 +10,37 @@ npm run init
10
10
 
11
11
  Follow the prompts to select your stack, database, tools, and output location.
12
12
 
13
+ ## Declarative Project Config
14
+
15
+ ```yaml
16
+ stack: compose
17
+ language:
18
+ - nodejs
19
+ database:
20
+ - postgres
21
+ outputPath: ./.devcontainer
22
+ customizations:
23
+ environment:
24
+ APP_ENV: development
25
+ devcontainerPatch:
26
+ features:
27
+ ghcr.io/devcontainers-extra/features/apt-get-packages:1:
28
+ packages: jq
29
+ ```
30
+
31
+ ```bash
32
+ npm run init -- --no-interactive
33
+ ```
34
+
35
+ Creates the same generated output you would get from equivalent clean-generation
36
+ input, while keeping the setup intent in version control.
37
+
38
+ ```bash
39
+ npm run init -- regen
40
+ ```
41
+
42
+ Regenerates from the repository project file by default when one exists.
43
+
13
44
  ## Non-Interactive Examples
14
45
 
15
46
  ### .NET with PostgreSQL
@@ -411,11 +442,14 @@ npm run init -- --from-manifest ./superposition.json
411
442
 
412
443
  ### Non-Interactive Regeneration
413
444
 
414
- Regenerate exact same setup (useful for updating to latest overlay versions):
445
+ Use `regen` when you want deterministic replay of a persisted source:
415
446
 
416
447
  ```bash
417
- # Regenerate with exact same selections, skip confirmation, no backup
418
- npm run init -- --from-manifest ./superposition.json --yes --no-backup
448
+ # Regenerate with exact same selections from a manifest, no backup
449
+ npm run init -- regen --from-manifest ./superposition.json --no-backup
450
+
451
+ # Or let regen use the repository project file when present
452
+ npm run init -- regen
419
453
  ```
420
454
 
421
455
  **Use cases:**
@@ -463,7 +497,7 @@ git push
463
497
  # Developer 2: Clone and regenerate from manifest
464
498
  git clone <repo>
465
499
  npm install
466
- npm run init -- --from-manifest ./superposition.json --yes
500
+ npm run init -- regen --from-manifest ./superposition.json
467
501
  # Gets exact same devcontainer setup
468
502
  ```
469
503
 
@@ -481,7 +515,7 @@ The manifest stores and restores:
481
515
 
482
516
  ```json
483
517
  {
484
- "version": "0.1.0",
518
+ "version": "X.Y.Z",
485
519
  "generated": "2026-02-08T10:00:00Z",
486
520
  "baseTemplate": "compose",
487
521
  "baseImage": "bookworm",
@@ -572,5 +606,5 @@ mv prod/superposition.json prod-superposition.json
572
606
 
573
607
  # Now you have three manifests for different environments
574
608
  # Regenerate any environment from its manifest
575
- npm run init -- --from-manifest ./dev-superposition.json --yes --output ./dev
609
+ npm run init -- regen --from-manifest ./dev-superposition.json --output ./dev
576
610
  ```
@@ -0,0 +1,58 @@
1
+ # Filesystem Contract
2
+
3
+ This describes what the tool writes and which files are safe to edit.
4
+
5
+ ## What Gets Written
6
+
7
+ ```
8
+ your-project/
9
+ ├── .devcontainer/ # Main devcontainer directory
10
+ │ ├── devcontainer.json # Container configuration
11
+ │ ├── docker-compose.yml # Services (compose stack only)
12
+ │ ├── .env.example # Environment variable templates
13
+ │ ├── ports.json # Port documentation and connection strings
14
+ │ ├── scripts/ # Setup and verification scripts
15
+ │ │ ├── post-create.sh # Runs once when container is created
16
+ │ │ └── post-start.sh # Runs every time container starts
17
+ │ └── custom/ # Your customizations (preserved across regen)
18
+ │ ├── devcontainer.patch.json
19
+ │ └── docker-compose.patch.yml
20
+ ├── superposition.json # Manifest file (enables regeneration)
21
+ └── .devcontainer.backup-*/ # Automatic backups (gitignored)
22
+ ```
23
+
24
+ ## Files You Should Customize
25
+
26
+ - `.devcontainer/.env` or `.env` (copied from `.env.example`)
27
+ - `.devcontainer/custom/` (your patches and scripts)
28
+
29
+ ## Files Safe to Edit Directly
30
+
31
+ - `.devcontainer/custom/devcontainer.patch.json`
32
+ - `.devcontainer/custom/docker-compose.patch.yml`
33
+ - `.devcontainer/custom/environment.env`
34
+ - `.devcontainer/custom/scripts/*`
35
+
36
+ ## Files Regenerated (Do Not Edit Directly)
37
+
38
+ - `.devcontainer/devcontainer.json`
39
+ - `.devcontainer/docker-compose.yml`
40
+ - `.devcontainer/scripts/*`
41
+
42
+ ## Files You Should Commit
43
+
44
+ - `superposition.json`
45
+ - `.devcontainer/` (generated configuration)
46
+ - `.devcontainer/custom/` (project-specific patches)
47
+ - `.devcontainer/.env.example`
48
+
49
+ ## Files in .gitignore
50
+
51
+ ```
52
+ # Environment secrets (never commit)
53
+ .env
54
+ .devcontainer/.env
55
+
56
+ # Regeneration backups (local only)
57
+ .devcontainer.backup-*
58
+ ```
package/docs/hash.md ADDED
@@ -0,0 +1,183 @@
1
+ # Hash Command
2
+
3
+ The `hash` command produces a stable, deterministic fingerprint for a devcontainer configuration.
4
+ It lets you verify that two environments are equivalent, detect drift in CI, and commit a reproducible
5
+ stamp alongside your `superposition.json` manifest.
6
+
7
+ ## Overview
8
+
9
+ ```bash
10
+ # From CLI options
11
+ npx container-superposition hash --stack compose --overlays dotnet,postgres,redis
12
+
13
+ # Reading from an existing manifest (auto-discovers superposition.json)
14
+ npx container-superposition hash
15
+
16
+ # Machine-readable output
17
+ npx container-superposition hash --stack compose --overlays dotnet,postgres,redis --json
18
+
19
+ # Write full hash to .devcontainer/superposition.hash
20
+ npx container-superposition hash --write
21
+ ```
22
+
23
+ ## What Is Hashed
24
+
25
+ The fingerprint is a SHA-256 digest of a canonical JSON object with the following fields:
26
+
27
+ | Field | Source | Notes |
28
+ | ---------- | --------------------------------------------- | ------------------------------------------------------ |
29
+ | `stack` | `--stack` flag or manifest `baseTemplate` | `plain` or `compose` |
30
+ | `overlays` | Resolved overlay list (alphabetically sorted) | Includes auto-resolved dependencies |
31
+ | `preset` | `--preset` flag or manifest `preset` | `null` when no preset |
32
+ | `base` | `--base` flag or manifest `baseImage` | e.g. `bookworm`, `alpine` |
33
+ | `tool` | Tool version (major.minor only) | Stable across patch releases; truncated before hashing |
34
+
35
+ Keys in the canonical object are sorted alphabetically and the overlay list is sorted before hashing,
36
+ so the result is identical regardless of the order overlays are provided.
37
+
38
+ ## Output
39
+
40
+ ### Text Output (default)
41
+
42
+ ```
43
+ ╭ Environment Fingerprint ──────────────────────────╮
44
+ │ │
45
+ │ stack compose │
46
+ │ overlays dotnet, postgres, redis │
47
+ │ preset (none) │
48
+ │ base bookworm │
49
+ │ tool 0.1.3 │
50
+ │ │
51
+ │ hash 53ed972d │
52
+ │ │
53
+ ╰───────────────────────────────────────────────────╯
54
+ ```
55
+
56
+ Auto-resolved dependencies are shown with an `(auto)` label:
57
+
58
+ ```
59
+ overlays grafana, prometheus (auto)
60
+ ```
61
+
62
+ ### JSON Output (`--json`)
63
+
64
+ ```json
65
+ {
66
+ "stack": "compose",
67
+ "overlays": ["dotnet", "postgres", "redis"],
68
+ "preset": null,
69
+ "base": "bookworm",
70
+ "tool": "0.1.3",
71
+ "hash": "53ed972d",
72
+ "hashFull": "53ed972da2ba0712ae15b4003aa46234e7eeba2e977e7a397453740202ebbea4"
73
+ }
74
+ ```
75
+
76
+ - `hash` — first 8 hex characters, suitable for display and badges
77
+ - `hashFull` — full 64-character SHA-256 hex digest, recommended for CI comparisons
78
+
79
+ ## Options
80
+
81
+ | Option | Description |
82
+ | --------------------- | -------------------------------------------------- |
83
+ | `--stack <type>` | Base template: `plain` or `compose` |
84
+ | `--overlays <list>` | Comma-separated overlay IDs |
85
+ | `--preset <id>` | Preset ID (optional, reflected in hash) |
86
+ | `--base <image>` | Base image/distro (e.g. `bookworm`, `alpine`) |
87
+ | `--manifest <path>` | Path to a specific `superposition.json` |
88
+ | `-o, --output <path>` | Directory to write hash file (used with `--write`) |
89
+ | `--write` | Write hash to `.devcontainer/superposition.hash` |
90
+ | `--json` | Output as JSON for scripting |
91
+
92
+ When `--stack`/`--overlays` are omitted the command searches for `superposition.json` in:
93
+
94
+ 1. Current directory (`superposition.json`)
95
+ 2. `.devcontainer/superposition.json`
96
+ 3. Parent directory (`../superposition.json`)
97
+
98
+ ## The `--write` Flag
99
+
100
+ ```bash
101
+ npx container-superposition hash --write
102
+ ```
103
+
104
+ Writes the **full** 64-character hash to `.devcontainer/superposition.hash` (one line, no trailing
105
+ whitespace). Commit this file alongside `superposition.json`:
106
+
107
+ ```
108
+ .devcontainer/
109
+ ├── devcontainer.json
110
+ ├── superposition.json
111
+ └── superposition.hash ← commit this
112
+ ```
113
+
114
+ Use a custom output directory with `-o`:
115
+
116
+ ```bash
117
+ npx container-superposition hash --write -o ./infra/.devcontainer
118
+ ```
119
+
120
+ ## CI Drift Detection
121
+
122
+ Detect when the environment has changed since the last commit:
123
+
124
+ ```yaml
125
+ - name: Verify environment fingerprint
126
+ run: |
127
+ EXPECTED=$(cat .devcontainer/superposition.hash)
128
+ ACTUAL=$(npx container-superposition hash --json | jq -r .hashFull)
129
+ [ "$EXPECTED" = "$ACTUAL" ] || (echo "Environment drift detected" && exit 1)
130
+ ```
131
+
132
+ Or using the short hash stored in a badge/README:
133
+
134
+ ```yaml
135
+ - name: Verify short fingerprint
136
+ run: |
137
+ EXPECTED="53ed972d"
138
+ ACTUAL=$(npx container-superposition hash --json | jq -r .hash)
139
+ [ "$EXPECTED" = "$ACTUAL" ] || (echo "Environment drift detected" && exit 1)
140
+ ```
141
+
142
+ ## Stability Guarantees
143
+
144
+ - **Same inputs → same hash** — guaranteed for any given tool version series
145
+ - **Overlay order is irrelevant** — `postgres,redis` hashes identically to `redis,postgres`
146
+ - **Dependencies are included** — auto-resolved dependencies affect the hash (as they affect the environment)
147
+ - **Patch versions are ignored** — `0.1.3` and `0.1.99` produce the same hash; `0.2.0` does not
148
+ - **Hash changes when any input changes** — stack, overlays, preset, base image, or minor/major tool version
149
+
150
+ ## Examples
151
+
152
+ ### Fingerprint a Standard Web API Stack
153
+
154
+ ```bash
155
+ npx container-superposition hash \
156
+ --stack compose \
157
+ --overlays nodejs,postgres,redis
158
+ ```
159
+
160
+ ### Read from Manifest and Write Hash File
161
+
162
+ ```bash
163
+ # Run from the project root (manifest at .devcontainer/superposition.json)
164
+ npx container-superposition hash --write
165
+ ```
166
+
167
+ ### Compare Two Configurations
168
+
169
+ ```bash
170
+ # Project A
171
+ HASH_A=$(npx container-superposition hash --stack compose --overlays nodejs,postgres --json | jq -r .hash)
172
+
173
+ # Project B
174
+ HASH_B=$(npx container-superposition hash --manifest ./project-b/.devcontainer/superposition.json --json | jq -r .hash)
175
+
176
+ [ "$HASH_A" = "$HASH_B" ] && echo "Environments are equivalent" || echo "Environments differ"
177
+ ```
178
+
179
+ ## Related Commands
180
+
181
+ - [`plan`](discovery-commands.md#plan-command) — Preview what will be generated
182
+ - [`list`](discovery-commands.md#list-command) — Browse available overlays
183
+ - [`explain`](discovery-commands.md#explain-command) — Deep dive into a specific overlay
@@ -243,7 +243,7 @@ Both settings are stored in `superposition.json`:
243
243
 
244
244
  ```json
245
245
  {
246
- "version": "0.1.0",
246
+ "version": "X.Y.Z",
247
247
  "generated": "2026-02-13T09:00:00.000Z",
248
248
  "minimal": true,
249
249
  "editor": "none",