container-superposition 0.1.4 → 0.1.6

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 (98) hide show
  1. package/README.md +74 -1370
  2. package/dist/scripts/init.js +350 -185
  3. package/dist/scripts/init.js.map +1 -1
  4. package/dist/tool/commands/adopt.d.ts +63 -0
  5. package/dist/tool/commands/adopt.d.ts.map +1 -0
  6. package/dist/tool/commands/adopt.js +1104 -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 +17 -0
  17. package/dist/tool/schema/project-config.d.ts.map +1 -0
  18. package/dist/tool/schema/project-config.js +441 -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 +202 -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 +70 -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 +213 -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 +136 -0
  54. package/docs/specs/002-superposition-config-file/tasks.md +215 -0
  55. package/docs/team-workflow.md +33 -1
  56. package/docs/workflows.md +139 -0
  57. package/features/cross-distro-packages/README.md +18 -0
  58. package/features/cross-distro-packages/devcontainer-feature.json +3 -3
  59. package/features/cross-distro-packages/install.sh +49 -7
  60. package/overlays/.presets/sdd.yml +84 -0
  61. package/overlays/README.md +7 -1
  62. package/overlays/amp/README.md +70 -0
  63. package/overlays/amp/devcontainer.patch.json +3 -0
  64. package/overlays/amp/overlay.yml +15 -0
  65. package/overlays/amp/setup.sh +21 -0
  66. package/overlays/amp/verify.sh +21 -0
  67. package/overlays/claude-code/README.md +83 -0
  68. package/overlays/claude-code/devcontainer.patch.json +3 -0
  69. package/overlays/claude-code/overlay.yml +15 -0
  70. package/overlays/claude-code/setup.sh +21 -0
  71. package/overlays/claude-code/verify.sh +21 -0
  72. package/overlays/gemini-cli/README.md +77 -0
  73. package/overlays/gemini-cli/devcontainer.patch.json +3 -0
  74. package/overlays/gemini-cli/overlay.yml +15 -0
  75. package/overlays/gemini-cli/setup.sh +21 -0
  76. package/overlays/gemini-cli/verify.sh +21 -0
  77. package/overlays/opencode/README.md +76 -0
  78. package/overlays/opencode/devcontainer.patch.json +3 -0
  79. package/overlays/opencode/overlay.yml +14 -0
  80. package/overlays/opencode/setup.sh +21 -0
  81. package/overlays/opencode/verify.sh +21 -0
  82. package/overlays/pandoc/README.md +279 -0
  83. package/overlays/pandoc/devcontainer.patch.json +14 -0
  84. package/overlays/pandoc/overlay.yml +19 -0
  85. package/overlays/pandoc/setup.sh +94 -0
  86. package/overlays/pandoc/verify.sh +13 -0
  87. package/overlays/spec-kit/README.md +181 -0
  88. package/overlays/spec-kit/devcontainer.patch.json +6 -0
  89. package/overlays/spec-kit/overlay.yml +19 -0
  90. package/overlays/spec-kit/setup.sh +45 -0
  91. package/overlays/spec-kit/verify.sh +33 -0
  92. package/overlays/windsurf-cli/README.md +69 -0
  93. package/overlays/windsurf-cli/devcontainer.patch.json +3 -0
  94. package/overlays/windsurf-cli/overlay.yml +15 -0
  95. package/overlays/windsurf-cli/setup.sh +21 -0
  96. package/overlays/windsurf-cli/verify.sh +21 -0
  97. package/package.json +1 -1
  98. package/tool/schema/config.schema.json +138 -9
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Backup utilities — shared by init/regen and adopt commands.
3
+ */
4
+ import * as fs from 'fs';
5
+ import * as path from 'path';
6
+ import { execSync } from 'child_process';
7
+ import chalk from 'chalk';
8
+ import { appendGitignoreSection } from './gitignore.js';
9
+ /**
10
+ * Detect whether a directory (or any of its parents) is inside a git repository.
11
+ * First tries `git rev-parse --git-dir`; if git is unavailable, falls back to
12
+ * walking up the directory tree looking for a `.git` entry.
13
+ */
14
+ export function isInsideGitRepo(dirPath) {
15
+ try {
16
+ execSync('git rev-parse --git-dir', { cwd: dirPath, stdio: 'ignore' });
17
+ return true;
18
+ }
19
+ catch {
20
+ // git command failed (not a repo) or git is not installed — walk up looking for .git
21
+ let current = path.resolve(dirPath);
22
+ while (true) {
23
+ if (fs.existsSync(path.join(current, '.git'))) {
24
+ return true;
25
+ }
26
+ const parent = path.dirname(current);
27
+ if (parent === current) {
28
+ break; // reached filesystem root
29
+ }
30
+ current = parent;
31
+ }
32
+ return false;
33
+ }
34
+ }
35
+ /**
36
+ * Recursively copy a directory.
37
+ */
38
+ export async function copyDirectory(src, dest) {
39
+ fs.mkdirSync(dest, { recursive: true });
40
+ const entries = fs.readdirSync(src, { withFileTypes: true });
41
+ for (const entry of entries) {
42
+ const srcPath = path.join(src, entry.name);
43
+ const destPath = path.join(dest, entry.name);
44
+ if (entry.isDirectory()) {
45
+ await copyDirectory(srcPath, destPath);
46
+ }
47
+ else {
48
+ fs.copyFileSync(srcPath, destPath);
49
+ }
50
+ }
51
+ }
52
+ /**
53
+ * Create a timestamped backup of an existing devcontainer directory.
54
+ * Returns the path of the backup directory, or null if there was nothing to back up.
55
+ */
56
+ export async function createBackup(outputPath, backupDir) {
57
+ const devcontainerJsonPath = path.join(outputPath, 'devcontainer.json');
58
+ const dockerComposePath = path.join(outputPath, 'docker-compose.yml');
59
+ const devcontainerSubdir = path.join(outputPath, '.devcontainer');
60
+ const manifestPath = path.join(outputPath, 'superposition.json');
61
+ const hasDevcontainerJson = fs.existsSync(devcontainerJsonPath);
62
+ const hasDockerCompose = fs.existsSync(dockerComposePath);
63
+ const hasDevcontainerSubdir = fs.existsSync(devcontainerSubdir) && fs.statSync(devcontainerSubdir).isDirectory();
64
+ const hasManifest = fs.existsSync(manifestPath);
65
+ if (!hasDevcontainerJson && !hasDockerCompose && !hasDevcontainerSubdir && !hasManifest) {
66
+ return null; // Nothing to backup
67
+ }
68
+ const timestamp = new Date()
69
+ .toISOString()
70
+ .replace(/:/g, '-')
71
+ .replace(/\..+/, '')
72
+ .replace('T', '-');
73
+ const resolvedOutputPath = path.resolve(outputPath);
74
+ const outputParentDir = path.dirname(resolvedOutputPath);
75
+ const outputBaseName = path.basename(resolvedOutputPath);
76
+ const backupBaseName = outputBaseName === '.devcontainer' ? '.devcontainer' : outputBaseName;
77
+ const backupPath = backupDir
78
+ ? path.resolve(backupDir)
79
+ : path.join(outputParentDir, `${backupBaseName}.backup-${timestamp}`);
80
+ fs.mkdirSync(backupPath, { recursive: true });
81
+ if (hasDevcontainerJson) {
82
+ fs.copyFileSync(devcontainerJsonPath, path.join(backupPath, 'devcontainer.json'));
83
+ }
84
+ if (hasDockerCompose) {
85
+ fs.copyFileSync(dockerComposePath, path.join(backupPath, 'docker-compose.yml'));
86
+ }
87
+ if (hasDevcontainerSubdir) {
88
+ await copyDirectory(devcontainerSubdir, path.join(backupPath, '.devcontainer'));
89
+ }
90
+ if (hasManifest) {
91
+ fs.copyFileSync(manifestPath, path.join(backupPath, 'superposition.json'));
92
+ }
93
+ const otherFiles = ['.env', '.env.example', '.gitignore', 'features', 'scripts'];
94
+ for (const file of otherFiles) {
95
+ const srcPath = path.join(outputPath, file);
96
+ if (fs.existsSync(srcPath)) {
97
+ const destPath = path.join(backupPath, file);
98
+ if (fs.statSync(srcPath).isDirectory()) {
99
+ await copyDirectory(srcPath, destPath);
100
+ }
101
+ else {
102
+ fs.copyFileSync(srcPath, destPath);
103
+ }
104
+ }
105
+ }
106
+ return backupPath;
107
+ }
108
+ /**
109
+ * Append backup glob patterns to the project-root .gitignore (idempotent).
110
+ */
111
+ export function ensureBackupPatternsInGitignore(outputPath) {
112
+ const projectRoot = path.dirname(path.resolve(outputPath));
113
+ const gitignorePath = path.join(projectRoot, '.gitignore');
114
+ const written = appendGitignoreSection(gitignorePath, 'container-superposition backups', [
115
+ '.devcontainer.backup-*/',
116
+ '*.backup-*',
117
+ 'superposition.json.backup-*',
118
+ ]);
119
+ if (written) {
120
+ console.log(chalk.dim(' 📝 Updated .gitignore with backup patterns'));
121
+ }
122
+ }
123
+ //# sourceMappingURL=backup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.js","sourceRoot":"","sources":["../../../tool/utils/backup.ts"],"names":[],"mappings":"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"}
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,202 @@
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. **`.superposition.yml`** — an optional repository-root project file when you pass `--project-file`, using the same inferred stack, overlays, output path, and supported customizations
9
+ 3. **`.devcontainer/custom/devcontainer.patch.json`** — any config that has no overlay equivalent (custom features, extensions, mounts, remoteEnv, etc.)
10
+ 4. **`.devcontainer/custom/docker-compose.patch.yml`** — any compose services that have no overlay equivalent
11
+
12
+ The custom patches in `custom/` are automatically merged on every `regen`, so your project-specific configuration is never lost.
13
+
14
+ ## Quick Start
15
+
16
+ ```bash
17
+ # Analyse your existing .devcontainer/ (prints a report, writes nothing)
18
+ npx container-superposition adopt --dry-run
19
+
20
+ # Run the analysis and write the generated files
21
+ npx container-superposition adopt
22
+
23
+ # Also write a repository-root project file for project-config workflows
24
+ npx container-superposition adopt --project-file
25
+
26
+ # Force-overwrite any existing superposition.json / custom/ files
27
+ npx container-superposition adopt --force
28
+ ```
29
+
30
+ ## Docker Compose Devcontainers
31
+
32
+ `adopt` fully supports compose-based devcontainers. It reads the
33
+ `dockerComposeFile` field in `devcontainer.json` to locate the right compose
34
+ file(s), including:
35
+
36
+ - **Single file** (string): `"dockerComposeFile": "docker-compose.yml"`
37
+ - **Multiple files** (array): `"dockerComposeFile": ["base.yml", "override.yml"]`
38
+ - **Relative paths** pointing outside the `.devcontainer/` directory:
39
+ `"dockerComposeFile": "../docker-compose.yml"`
40
+
41
+ All referenced compose files are analysed. Every service with a recognised
42
+ image (postgres, redis, grafana, jaeger, …) is mapped to the corresponding
43
+ overlay. Services with no overlay equivalent are written to
44
+ `custom/docker-compose.patch.yml` so they are preserved across regenerations.
45
+
46
+ ## How It Works
47
+
48
+ ### Detection
49
+
50
+ The command builds its detection tables **dynamically from the overlay registry** — there are no hardcoded overlay names. For every overlay it reads:
51
+
52
+ | Source | What is extracted |
53
+ | -------------------------------------------------------------- | ------------------------- |
54
+ | `devcontainer.patch.json` → `features` | Feature URI → overlay ID |
55
+ | `devcontainer.patch.json` → `customizations.vscode.extensions` | Extension ID → overlay ID |
56
+ | `docker-compose.yml` → service `image` | Image prefix → overlay ID |
57
+
58
+ When multiple overlays share the same feature (e.g. both `nodejs` and `bun`
59
+ include the Node.js devcontainer feature), the one whose ID best matches the
60
+ feature's own name wins.
61
+
62
+ ### Detection signals (in priority order)
63
+
64
+ 1. **Devcontainer features** — e.g. `ghcr.io/devcontainers/features/node:1` → `nodejs` (confidence: **exact**)
65
+ 2. **Docker Compose service images** — e.g. `postgres:16-alpine` → `postgres` (confidence: **exact**)
66
+ 3. **VS Code extensions** — e.g. `ms-python.python` → `python` (confidence: **heuristic**)
67
+ 4. **Remote environment variables** — e.g. `POSTGRES_*` → `postgres` (confidence: **heuristic**)
68
+
69
+ ### Unmatched items → `custom/`
70
+
71
+ Anything that cannot be mapped to an overlay is preserved in the `custom/`
72
+ directory, which is merged automatically on every `regen`:
73
+
74
+ | Unmatched item | Written to |
75
+ | ----------------------------------------------- | --------------------------------------------------------------------- |
76
+ | Unknown features | `custom/devcontainer.patch.json` → `features` |
77
+ | Unknown VS Code extensions | `custom/devcontainer.patch.json` → `customizations.vscode.extensions` |
78
+ | Custom `mounts` | `custom/devcontainer.patch.json` → `mounts` |
79
+ | Non-default `remoteUser` | `custom/devcontainer.patch.json` → `remoteUser` |
80
+ | Custom `postCreateCommand` / `postStartCommand` | `custom/devcontainer.patch.json` |
81
+ | Unknown compose services | `custom/docker-compose.patch.yml` → `services` |
82
+
83
+ ## Backup Behaviour
84
+
85
+ The same backup logic as `regen` is used:
86
+
87
+ | Condition | What happens |
88
+ | ---------------------------- | ------------------------------ |
89
+ | Inside a git repo (default) | No backup — git tracks history |
90
+ | Outside a git repo (default) | Backup created automatically |
91
+ | `--backup` flag | Backup always created |
92
+ | `--no-backup` flag | Backup always skipped |
93
+
94
+ Backups are placed next to the `.devcontainer/` directory as
95
+ `.devcontainer.backup-<timestamp>/`, and the corresponding glob patterns are
96
+ automatically added to `.gitignore`.
97
+
98
+ ## Options
99
+
100
+ | Option | Description |
101
+ | --------------------- | ----------------------------------------------------------------------------- |
102
+ | `-d, --dir <path>` | Path to the existing `.devcontainer/` directory (default: `./.devcontainer`) |
103
+ | `--dry-run` | Print the analysis without writing any files |
104
+ | `--force` | Overwrite existing `superposition.json` / `custom/` files |
105
+ | `--backup` | Force a backup even when inside a git repo |
106
+ | `--no-backup` | Disable backup creation even when it would normally be performed |
107
+ | `--backup-dir <path>` | Custom backup directory location |
108
+ | `--project-file` | Also write a repository-root project config (`.superposition.yml` by default) |
109
+ | `--json` | Output analysis as JSON (useful for scripting) |
110
+
111
+ ## Example Output
112
+
113
+ ```
114
+ ╭──────────────────────╮
115
+ │ 🔍 Adopt Analysis │
116
+ ╰──────────────────────╯
117
+
118
+ Analysing .devcontainer/devcontainer.json...
119
+ Analysing .devcontainer/docker-compose.yml...
120
+
121
+ Detected features / services → suggested overlays
122
+ ────────────────────────────────────────────────────────────────────────────────
123
+ Source → Overlay Confidence
124
+ ────────────────────────────────────────────────────────────────────────────────────────────────
125
+ ghcr.io/devcontainers/features/node:1 → nodejs exact
126
+ service: postgres (image: postgres:16-alpine) → postgres exact
127
+ service: redis (image: redis:7-alpine) → redis exact
128
+
129
+ Items with no overlay equivalent → custom/
130
+ ────────────────────────────────────────────────────────────────────────────────
131
+ Source Action
132
+ ────────────────────────────────────────────────────────────────────────────────────────────────
133
+ ghcr.io/corp/internal-tools:1 No overlay covers this feature — preserve in custom/devcontainer.patch.json
134
+ service: my-app (image: my-registry/my-app:latest) No overlay covers this service — preserve in custom/docker-compose.patch.yml
135
+
136
+ Suggested command:
137
+ container-superposition init --stack compose --language nodejs --database postgres,redis
138
+
139
+ 💡 Custom patches will be written to .devcontainer/custom/ to preserve
140
+ any configuration that has no overlay equivalent.
141
+ ```
142
+
143
+ ## After Adopt
144
+
145
+ Once `adopt` has run:
146
+
147
+ 1. **Review `superposition.json`** — verify the detected overlays are correct; add or remove as needed.
148
+ 2. **Review `.superposition.yml` if you generated one** — it captures the same inferred setup as a repository-root project file, including inline supported customizations.
149
+ 3. **Review `custom/` patches** — inspect what was preserved and trim anything no longer needed.
150
+ 4. **Regenerate** — rebuild your `.devcontainer/` from the manifest or project file:
151
+ ```bash
152
+ npx container-superposition regen
153
+ ```
154
+ 5. **Commit `superposition.json`**, the optional project file, and, if applicable, `custom/` patches.
155
+
156
+ ## JSON Output
157
+
158
+ Use `--json` to get machine-readable output for scripting or CI workflows:
159
+
160
+ ```bash
161
+ npx container-superposition adopt --dry-run --json | jq .suggestedOverlays
162
+ ```
163
+
164
+ The JSON object contains:
165
+
166
+ ```jsonc
167
+ {
168
+ "dir": "/absolute/path/to/.devcontainer",
169
+ "detections": [
170
+ {
171
+ "source": "ghcr.io/devcontainers/features/node:1",
172
+ "overlayId": "nodejs",
173
+ "confidence": "exact",
174
+ "sourceType": "feature",
175
+ },
176
+ // ...
177
+ ],
178
+ "unmatchedItems": [
179
+ {
180
+ "source": "ghcr.io/corp/internal-tools:1",
181
+ "reason": "No overlay covers this feature — preserve in custom/devcontainer.patch.json",
182
+ },
183
+ // ...
184
+ ],
185
+ "customDevcontainerPatch": {
186
+ /* or null */
187
+ },
188
+ "customComposePatch": {
189
+ /* or null */
190
+ },
191
+ "suggestedStack": "compose",
192
+ "suggestedOverlays": ["nodejs", "postgres", "redis"],
193
+ "suggestedCommand": "container-superposition init --stack compose --language nodejs --database postgres,redis",
194
+ }
195
+ ```
196
+
197
+ ## See Also
198
+
199
+ - [Team Workflow](team-workflow.md) — Manifest-first team collaboration workflow
200
+ - [Custom Patches](custom-patches.md) — How `custom/` patches are merged
201
+ - [Workflows and Regeneration](workflows.md) — Regeneration and backup details
202
+ - [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
+ ```