@mnstry/atelier 0.2.0-alpha.1

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 (260) hide show
  1. package/CHANGELOG.md +383 -0
  2. package/LICENSE +202 -0
  3. package/NOTICE +5 -0
  4. package/README.md +262 -0
  5. package/SECURITY.md +93 -0
  6. package/TRADEMARKS.md +88 -0
  7. package/announcements/2026-08-06-announcements-channel.v1.json +12 -0
  8. package/announcements/keys/mnstry-announcements.public.v1.json +9 -0
  9. package/bin/atelier.mjs +2 -0
  10. package/bin/mnstry-atelier.mjs +3 -0
  11. package/contracts/analysis-adapter.v1.schema.json +248 -0
  12. package/contracts/atelier-action-intent.v1.schema.json +143 -0
  13. package/contracts/atelier-analysis-adapter.v1.schema.json +360 -0
  14. package/contracts/atelier-attestation.v1.schema.json +239 -0
  15. package/contracts/atelier-boundary-policy.v1.schema.json +339 -0
  16. package/contracts/atelier-claim.v1.schema.json +97 -0
  17. package/contracts/atelier-export.v1.schema.json +1015 -0
  18. package/contracts/atelier-extension-pack.v1.schema.json +290 -0
  19. package/contracts/atelier-kit-manifest.v1.schema.json +273 -0
  20. package/contracts/atelier-lock.v1.schema.json +364 -0
  21. package/contracts/atelier-migration.v1.schema.json +162 -0
  22. package/contracts/atelier-project-config.v1.schema.json +352 -0
  23. package/contracts/atelier-readiness-protocol.v1.schema.json +517 -0
  24. package/contracts/atelier-readiness-run.v1.schema.json +229 -0
  25. package/contracts/atelier-readiness.v1.schema.json +457 -0
  26. package/contracts/compat-baseline.json +10 -0
  27. package/contracts/git-promote-event.v1.schema.json +145 -0
  28. package/contracts/knowledge-source-sidecar.v1.schema.json +157 -0
  29. package/contracts/mnstry-atelier-semantic-profile.v1.json +254 -0
  30. package/docs/announcements.md +101 -0
  31. package/docs/atelier-runtime.md +34 -0
  32. package/docs/attestation.md +201 -0
  33. package/docs/continuity.md +55 -0
  34. package/docs/contract-stability.md +111 -0
  35. package/docs/distributions.md +220 -0
  36. package/docs/install.md +140 -0
  37. package/docs/knowledge-graph.md +140 -0
  38. package/docs/ontology.md +206 -0
  39. package/docs/release-engineering.md +151 -0
  40. package/docs/repo-boundary-guard.md +199 -0
  41. package/docs/tenant-readiness.md +64 -0
  42. package/docs/upgrade.md +124 -0
  43. package/fixtures/analysis/invalid/native-graph.v1.json +4 -0
  44. package/fixtures/analysis/invalid/promoted-claim.v1.json +17 -0
  45. package/fixtures/analysis/invalid/runtime-import.v1.json +20 -0
  46. package/fixtures/analysis/valid/output.claims.v1.json +17 -0
  47. package/fixtures/analysis-adapter/invalid/manifest-hidden-provider.v1.json +24 -0
  48. package/fixtures/analysis-adapter/invalid/output-frontmatter-mutation.v1.json +21 -0
  49. package/fixtures/analysis-adapter/invalid/output-native-graph.v1.json +14 -0
  50. package/fixtures/analysis-adapter/manifest.disabled.v1.json +22 -0
  51. package/fixtures/analysis-adapter/manifest.enabled-local.v1.json +23 -0
  52. package/fixtures/analysis-adapter/output.claims.v1.json +21 -0
  53. package/fixtures/atelier-action-intent/invalid/runtime-mutation-authority.v1.json +16 -0
  54. package/fixtures/atelier-action-intent/invalid/unknown-top-level-field.v1.json +17 -0
  55. package/fixtures/atelier-action-intent/invalid/writes-beyond-clipboard.v1.json +16 -0
  56. package/fixtures/atelier-action-intent/valid/copy-agent-prompt.v1.json +16 -0
  57. package/fixtures/atelier-action-intent/valid/copy-repo-path.v1.json +22 -0
  58. package/fixtures/atelier-analysis-adapter/invalid/hidden-model-provider.v1.json +24 -0
  59. package/fixtures/atelier-analysis-adapter/invalid/inert-with-execute-command.v1.json +24 -0
  60. package/fixtures/atelier-analysis-adapter/invalid/proposals-with-runtime-import.v1.json +30 -0
  61. package/fixtures/atelier-analysis-adapter/valid/manifest-inert.v1.json +24 -0
  62. package/fixtures/atelier-analysis-adapter/valid/proposals-claim-only.v1.json +27 -0
  63. package/fixtures/atelier-attestation/invalid/bad-digest.v1.json +23 -0
  64. package/fixtures/atelier-attestation/invalid/conformance-scope.v1.json +23 -0
  65. package/fixtures/atelier-attestation/invalid/missing-signature.v1.json +22 -0
  66. package/fixtures/atelier-attestation/invalid/unknown-root-field.v1.json +24 -0
  67. package/fixtures/atelier-attestation/keys/roundtrip-issuer.public.v1.json +9 -0
  68. package/fixtures/atelier-attestation/valid/rejected-with-reasons.v1.json +49 -0
  69. package/fixtures/atelier-attestation/valid/signed-admitted.v1.json +35 -0
  70. package/fixtures/atelier-attestation/valid/signed-roundtrip.v1.json +29 -0
  71. package/fixtures/atelier-attestation/valid/unsigned-advisory.v1.json +23 -0
  72. package/fixtures/atelier-claim/invalid/unknown-provider.v1.json +15 -0
  73. package/fixtures/atelier-claim/invalid/unknown-top-level-field.v1.json +16 -0
  74. package/fixtures/atelier-claim/invalid/wrong-schema-const.v1.json +15 -0
  75. package/fixtures/atelier-claim/valid/adapter-style-proposed-relation.v1.json +19 -0
  76. package/fixtures/atelier-claim/valid/codex-proposed-relation.v1.json +16 -0
  77. package/fixtures/atelier-claim/valid/manual-proposed-relation.v1.json +16 -0
  78. package/fixtures/atelier-claim/valid/readiness-proposed-relation.v1.json +18 -0
  79. package/fixtures/atelier-export/invalid/dirty-unforced-empty-taint.v1.json +19 -0
  80. package/fixtures/atelier-export/invalid/object-local-audience-visibility.v1.json +11 -0
  81. package/fixtures/atelier-export/invalid/open-object-class-owner.v1.json +23 -0
  82. package/fixtures/atelier-export/invalid/pricing-legacy-amount.v1.json +23 -0
  83. package/fixtures/atelier-export/invalid/public-disguised-internal-source-ref.v1.json +11 -0
  84. package/fixtures/atelier-export/invalid/public-nested-private-source-ref.v1.json +17 -0
  85. package/fixtures/atelier-export/invalid/public-resolved-private-source-ref.v1.json +13 -0
  86. package/fixtures/atelier-export/invalid/public-resolved-sensitive-source-ref.v1.json +13 -0
  87. package/fixtures/atelier-export/invalid/public-source-ref-missing-provenance.v1.json +20 -0
  88. package/fixtures/atelier-export/invalid/public-source-team-kg.json +12 -0
  89. package/fixtures/atelier-export/invalid/source-node-legacy-visibility.v1.json +21 -0
  90. package/fixtures/atelier-export/invalid/source-node-unknown-field.v1.json +18 -0
  91. package/fixtures/atelier-export/invalid/unresolved-public-source-ref.v1.json +11 -0
  92. package/fixtures/atelier-export/sample-studio-offer.v1.json +397 -0
  93. package/fixtures/atelier-extension-pack/invalid/bundled-slug-collision.v1.json +22 -0
  94. package/fixtures/atelier-extension-pack/invalid/escapes-root.v1.json +22 -0
  95. package/fixtures/atelier-extension-pack/invalid/posture-tampered.v1.json +22 -0
  96. package/fixtures/atelier-extension-pack/invalid/protocols/autonomous-authority.v1.json +118 -0
  97. package/fixtures/atelier-extension-pack/invalid/protocols/offer-map.v1.json +109 -0
  98. package/fixtures/atelier-extension-pack/invalid/redefines-runtime-term.v1.json +19 -0
  99. package/fixtures/atelier-extension-pack/invalid/wrong-namespace.v1.json +19 -0
  100. package/fixtures/atelier-extension-pack/valid/protocols/contract-gate.v1.json +109 -0
  101. package/fixtures/atelier-extension-pack/valid/sample-pack.v1.json +48 -0
  102. package/fixtures/atelier-kit-manifest/invalid/runtime-copy-without-canonical-role.v1.json +53 -0
  103. package/fixtures/atelier-kit-manifest/invalid/telemetry-send-path.v1.json +21 -0
  104. package/fixtures/atelier-kit-manifest/valid/mnstry-atelier.valid.v1.json +76 -0
  105. package/fixtures/atelier-lock/invalid/absolute-contract-path.v1.json +40 -0
  106. package/fixtures/atelier-lock/invalid/runtime-mutation-enabled.v1.json +41 -0
  107. package/fixtures/atelier-lock/invalid/unknown-top-level-field.v1.json +42 -0
  108. package/fixtures/atelier-lock/valid/extension-pack.v1.json +55 -0
  109. package/fixtures/atelier-lock/valid/minimal.v1.json +44 -0
  110. package/fixtures/atelier-migration/invalid/live-apply-mode.v1.json +21 -0
  111. package/fixtures/atelier-migration/invalid/missing-review-evidence.v1.json +20 -0
  112. package/fixtures/atelier-migration/invalid/runtime-mutation-enabled.v1.json +21 -0
  113. package/fixtures/atelier-migration/valid/minimal.v1.json +32 -0
  114. package/fixtures/atelier-project-config/invalid/absolute-repo-path.v1.json +11 -0
  115. package/fixtures/atelier-project-config/invalid/ext-not-object.v1.json +12 -0
  116. package/fixtures/atelier-project-config/invalid/external-kind-read-boundary.v1.json +12 -0
  117. package/fixtures/atelier-project-config/invalid/missing-repos-and-alignment.v1.json +7 -0
  118. package/fixtures/atelier-project-config/invalid/unknown-top-level-field.v1.json +14 -0
  119. package/fixtures/atelier-project-config/valid/alignment-scaffold.v1.json +15 -0
  120. package/fixtures/atelier-project-config/valid/ext-extension-packs.v1.json +36 -0
  121. package/fixtures/atelier-project-config/valid/repo-workspace.v1.json +23 -0
  122. package/fixtures/atelier-project-config/validator-only/ext-member-not-object.v1.json +14 -0
  123. package/fixtures/atelier-readiness/invalid/dry-run-only-false.v1.json +87 -0
  124. package/fixtures/atelier-readiness/invalid/support-bundle-send-path.v1.json +87 -0
  125. package/fixtures/atelier-readiness/invalid/unknown-top-level-field.v1.json +88 -0
  126. package/fixtures/atelier-readiness/valid/ready-workspace.v1.json +84 -0
  127. package/fixtures/boundary-policy/invalid/content-rule-exception-blanket-path.v1.json +67 -0
  128. package/fixtures/boundary-policy/invalid/invalid-actor-private-repo-mapping.v1.json +20 -0
  129. package/fixtures/boundary-policy/invalid/legacy-warning-missing-warning.v1.json +20 -0
  130. package/fixtures/boundary-policy/invalid/missing-repo-coverage.v1.json +19 -0
  131. package/fixtures/boundary-policy/invalid/unknown-key.v1.json +12 -0
  132. package/fixtures/boundary-policy/valid/content-rule-exception.v1.json +67 -0
  133. package/fixtures/boundary-policy/valid/legacy-warning.v1.json +33 -0
  134. package/fixtures/boundary-policy/valid/strict-private-shared.v1.json +40 -0
  135. package/fixtures/git-promote/invalid/provision-named-revocable.v1.json +29 -0
  136. package/fixtures/git-promote/private-to-team.v1.json +29 -0
  137. package/fixtures/knowledge-source-sidecar/invalid/legacy-visibility-field.v1.json +17 -0
  138. package/fixtures/knowledge-source-sidecar/invalid/missing-audience.v1.json +15 -0
  139. package/fixtures/knowledge-source-sidecar/invalid/unknown-relation-kind.v1.json +18 -0
  140. package/fixtures/knowledge-source-sidecar/valid/html-source.v1.json +18 -0
  141. package/fixtures/knowledge-source-sidecar/valid/pdf-evidence.v1.json +18 -0
  142. package/fixtures/projects/neutral-project/atelier.project.json +15 -0
  143. package/fixtures/projects/neutral-project/ops/config/repo-access.v1.json +12 -0
  144. package/fixtures/projects/neutral-project/project-knowledge.graph.json +69 -0
  145. package/fixtures/projects/sample-workspace/atelier.project.json +23 -0
  146. package/fixtures/projects/sample-workspace/boundary-policy.v1.json +59 -0
  147. package/fixtures/projects/sample-workspace/content/README.md +16 -0
  148. package/fixtures/projects/sample-workspace/content/source.html +5 -0
  149. package/fixtures/projects/sample-workspace/content/source.html.kg.json +15 -0
  150. package/fixtures/projects/sample-workspace/repo-access.v1.json +9 -0
  151. package/fixtures/projects/source-formats-workspace/atelier.project.json +23 -0
  152. package/fixtures/projects/source-formats-workspace/content/README.md +17 -0
  153. package/fixtures/projects/source-formats-workspace/content/data.json +7 -0
  154. package/fixtures/projects/source-formats-workspace/content/data.json.kg.json +15 -0
  155. package/fixtures/projects/source-formats-workspace/content/logo.png.kg.json +15 -0
  156. package/fixtures/projects/source-formats-workspace/content/metrics.csv +3 -0
  157. package/fixtures/projects/source-formats-workspace/content/metrics.csv.kg.json +15 -0
  158. package/fixtures/projects/source-formats-workspace/content/pipeline.yaml +4 -0
  159. package/fixtures/projects/source-formats-workspace/content/pipeline.yaml.kg.json +15 -0
  160. package/fixtures/projects/source-formats-workspace/repo-access.v1.json +9 -0
  161. package/fixtures/readiness-protocols/extension-pack/invalid/protocol-missing-path.v1.json +20 -0
  162. package/fixtures/readiness-protocols/extension-pack/valid/with-protocol.v1.json +24 -0
  163. package/fixtures/readiness-protocols/protocol/invalid/missing-check-target.v1.json +103 -0
  164. package/fixtures/readiness-protocols/protocol/invalid/runtime-mutation-enabled.v1.json +107 -0
  165. package/fixtures/readiness-protocols/protocol/valid/minimal.v1.json +100 -0
  166. package/fixtures/readiness-protocols/run/invalid/missing-evidence.v1.json +37 -0
  167. package/fixtures/readiness-protocols/run/invalid/pass-with-blocker.v1.json +22 -0
  168. package/fixtures/readiness-protocols/run/invalid/runtime-mutation-enabled.v1.json +22 -0
  169. package/fixtures/readiness-protocols/run/valid/pass.v1.json +44 -0
  170. package/fixtures/support/invalid/leaky-support.v1.json +13 -0
  171. package/fixtures/support/valid/minimal-safe.v1.json +20 -0
  172. package/package.json +98 -0
  173. package/skills/claude/atelier-open/SKILL.md +68 -0
  174. package/skills/claude/mnstry-readiness/SKILL.md +41 -0
  175. package/skills/codex/atelier-open/SKILL.md +68 -0
  176. package/skills/codex/mnstry-readiness/SKILL.md +41 -0
  177. package/src/analysis/adapter.mjs +20 -0
  178. package/src/analysis/analysis-claim-contract.mjs +335 -0
  179. package/src/attestation/jcs.mjs +64 -0
  180. package/src/attestation/sign.mjs +305 -0
  181. package/src/boundary/content-rules.mjs +262 -0
  182. package/src/boundary/policy.mjs +806 -0
  183. package/src/check-atelier-export-contract.mjs +116 -0
  184. package/src/cli/run.mjs +283 -0
  185. package/src/collaboration/proposals.mjs +262 -0
  186. package/src/commands/analysis.mjs +8 -0
  187. package/src/commands/announcements.mjs +308 -0
  188. package/src/commands/attestation.mjs +198 -0
  189. package/src/commands/boundary.mjs +25 -0
  190. package/src/commands/config.mjs +4 -0
  191. package/src/commands/context.mjs +3 -0
  192. package/src/commands/distribution.mjs +224 -0
  193. package/src/commands/egress.mjs +11 -0
  194. package/src/commands/extension-pack.mjs +209 -0
  195. package/src/commands/feedback.mjs +3 -0
  196. package/src/commands/graph.mjs +3 -0
  197. package/src/commands/init.mjs +130 -0
  198. package/src/commands/lock.mjs +3 -0
  199. package/src/commands/project.mjs +3 -0
  200. package/src/commands/promote.mjs +4 -0
  201. package/src/commands/readiness.mjs +3 -0
  202. package/src/commands/server.mjs +6 -0
  203. package/src/commands/setup.mjs +250 -0
  204. package/src/commands/support.mjs +3 -0
  205. package/src/commands/upgrade.mjs +3 -0
  206. package/src/contracts/corpus.mjs +171 -0
  207. package/src/egress/check.mjs +45 -0
  208. package/src/egress/forbidden-egress.mjs +323 -0
  209. package/src/export/atelier-export-contract.mjs +722 -0
  210. package/src/extension-packs/loader.mjs +491 -0
  211. package/src/graph/graph.mjs +370 -0
  212. package/src/graph/knowledge-graph.mjs +887 -0
  213. package/src/harness/context-client.mjs +144 -0
  214. package/src/harness/context.mjs +27 -0
  215. package/src/index.mjs +88 -0
  216. package/src/project/config.mjs +557 -0
  217. package/src/project/file-class.mjs +113 -0
  218. package/src/project/git-ignore.mjs +31 -0
  219. package/src/project/path-match.mjs +30 -0
  220. package/src/project/repo-identity.mjs +176 -0
  221. package/src/projection/alignment-projection.mjs +169 -0
  222. package/src/projection/policy.mjs +90 -0
  223. package/src/projection/project.mjs +152 -0
  224. package/src/readiness/readiness.mjs +312 -0
  225. package/src/readiness-protocols/bundled-pack.mjs +766 -0
  226. package/src/readiness-protocols/runtime.mjs +419 -0
  227. package/src/server/local-sidecar.mjs +640 -0
  228. package/src/server/security.mjs +229 -0
  229. package/src/server/server.mjs +42 -0
  230. package/src/support/feedback-report.mjs +414 -0
  231. package/src/support/support-bundle.mjs +226 -0
  232. package/src/ui/html-primitives.mjs +104 -0
  233. package/src/upgrade/upgrade.mjs +701 -0
  234. package/src/validate-atelier-export-dry-run.mjs +75 -0
  235. package/templates/atelier.project.example.json +23 -0
  236. package/templates/distribution-workspace/.claude/launch.json +12 -0
  237. package/templates/distribution-workspace/README.md +86 -0
  238. package/templates/distribution-workspace/atelier.project.json +36 -0
  239. package/templates/distribution-workspace/boundary-policy.v1.json +61 -0
  240. package/templates/distribution-workspace/gitignore +29 -0
  241. package/templates/distribution-workspace/governance/repo-boundary-ledger.md +5 -0
  242. package/templates/distribution-workspace/project/README.md +19 -0
  243. package/templates/distribution-workspace/repo-access.v1.json +9 -0
  244. package/templates/launch.json +12 -0
  245. package/templates/private-domain-workspace/.claude/launch.json +12 -0
  246. package/templates/private-domain-workspace/README.md +50 -0
  247. package/templates/private-domain-workspace/atelier.project.json +28 -0
  248. package/templates/private-domain-workspace/boundary-policy.v1.json +54 -0
  249. package/templates/private-domain-workspace/domain/README.md +19 -0
  250. package/templates/private-domain-workspace/gitignore +29 -0
  251. package/templates/private-domain-workspace/governance/repo-boundary-ledger.md +4 -0
  252. package/templates/private-domain-workspace/repo-access.v1.json +9 -0
  253. package/templates/shared-project-workspace/.claude/launch.json +12 -0
  254. package/templates/shared-project-workspace/README.md +51 -0
  255. package/templates/shared-project-workspace/atelier.project.json +28 -0
  256. package/templates/shared-project-workspace/boundary-policy.v1.json +61 -0
  257. package/templates/shared-project-workspace/gitignore +29 -0
  258. package/templates/shared-project-workspace/governance/repo-boundary-ledger.md +5 -0
  259. package/templates/shared-project-workspace/project/README.md +19 -0
  260. package/templates/shared-project-workspace/repo-access.v1.json +9 -0
@@ -0,0 +1,224 @@
1
+ #!/usr/bin/env node
2
+ // atelier distribution check — verifies that a distribution package carries
3
+ // the required MNSTRY attribution markers. The normative attribution policy
4
+ // lives in TRADEMARKS.md under "Required attribution"; this command checks
5
+ // only the mechanical markers and points there rather than restating policy.
6
+ //
7
+ // Exit codes: 0 = checks passed, 1 = blocking attribution check failed,
8
+ // 2 = usage error (unknown subcommand or unusable target).
9
+ import { spawnSync } from 'node:child_process'
10
+ import fs from 'node:fs'
11
+ import path from 'node:path'
12
+ import { parseArgs } from '../project/config.mjs'
13
+
14
+ const ATTRIBUTION_BYTES = 'powered by MNSTRY Atelier'
15
+ const ATTRIBUTION_EXT_KEY = 'mnstry.atelier/attribution'
16
+ const PACK_MANIFEST_NAME = 'atelier.pack.json'
17
+ const PREFIX = '[atelier-distribution:check]'
18
+ const POLICY_POINTER = 'see TRADEMARKS.md ("Required attribution") and docs/attestation.md'
19
+
20
+ const log = (message) => console.log(`${PREFIX} ${message}`)
21
+ const logError = (message) => console.error(`${PREFIX} ${message}`)
22
+
23
+ // Paths in output stay relative to the checked distribution so logs never
24
+ // leak machine-local absolute paths.
25
+ const displayPath = (target, file) => path.relative(target, file) || path.basename(file)
26
+
27
+ function checkReadmeAttribution(target) {
28
+ const readmePath = path.join(target, 'README.md')
29
+ let bytes
30
+ try {
31
+ bytes = fs.readFileSync(readmePath)
32
+ } catch {
33
+ return { ok: false, reason: 'README.md is missing from the distribution root' }
34
+ }
35
+ if (!bytes.includes(ATTRIBUTION_BYTES)) {
36
+ return { ok: false, reason: `README.md does not contain the exact byte string "${ATTRIBUTION_BYTES}"` }
37
+ }
38
+ return { ok: true }
39
+ }
40
+
41
+ function manifestIn(dir) {
42
+ const file = path.join(dir, PACK_MANIFEST_NAME)
43
+ return fs.existsSync(file) ? file : null
44
+ }
45
+
46
+ function findPackManifests(target, packOverride) {
47
+ if (typeof packOverride === 'string' && packOverride.trim()) {
48
+ const resolved = path.resolve(packOverride)
49
+ let stat = null
50
+ try {
51
+ stat = fs.statSync(resolved)
52
+ } catch {
53
+ return { manifests: [], note: `--pack path does not exist: ${packOverride}` }
54
+ }
55
+ if (stat.isDirectory()) {
56
+ const manifest = manifestIn(resolved)
57
+ if (manifest) return { manifests: [manifest], note: null }
58
+ return { manifests: [], note: `--pack directory has no ${PACK_MANIFEST_NAME}: ${packOverride}` }
59
+ }
60
+ return { manifests: [resolved], note: null }
61
+ }
62
+
63
+ const packsRoot = path.join(target, 'packs')
64
+ let entries = []
65
+ try {
66
+ entries = fs.readdirSync(packsRoot, { withFileTypes: true })
67
+ } catch {
68
+ return { manifests: [], note: `no packs/ directory found; nothing to check for the pack-manifest attribution key` }
69
+ }
70
+ const manifests = entries
71
+ .filter((entry) => entry.isDirectory())
72
+ .map((entry) => manifestIn(path.join(packsRoot, entry.name)))
73
+ .filter(Boolean)
74
+ if (!manifests.length) {
75
+ return { manifests: [], note: `no ${PACK_MANIFEST_NAME} found under packs/` }
76
+ }
77
+ return { manifests, note: null }
78
+ }
79
+
80
+ // Advisory only: reported, never blocking, regardless of what it finds.
81
+ function reportPackAttribution(target, packOverride) {
82
+ const { manifests, note } = findPackManifests(target, packOverride)
83
+ if (note) log(`advisory: ${note}`)
84
+ for (const manifest of manifests) {
85
+ const shown = displayPath(target, manifest)
86
+ let doc
87
+ try {
88
+ doc = JSON.parse(fs.readFileSync(manifest, 'utf8'))
89
+ } catch {
90
+ log(`advisory: ${shown} is not readable JSON; attribution key not verified`)
91
+ continue
92
+ }
93
+ if (doc?.ext?.[ATTRIBUTION_EXT_KEY] === ATTRIBUTION_BYTES) {
94
+ log(`advisory: ${shown} declares ext["${ATTRIBUTION_EXT_KEY}"] — satisfied`)
95
+ } else {
96
+ log(`advisory: ${shown} does not declare ext["${ATTRIBUTION_EXT_KEY}"] — ${POLICY_POINTER}`)
97
+ }
98
+ }
99
+ }
100
+
101
+ // Blocking CLI probe. runCli accepts injected stdout/stderr writers, so a
102
+ // wrapper can swallow the attribution that help/version output carries. The
103
+ // probe therefore spawns the distribution's own declared bin with --version
104
+ // through real pipes (not the wrapper's writers) and requires the exact
105
+ // attribution byte string in the child's stdout.
106
+ //
107
+ // The probe is opt-out-proof for anything that looks like a distribution:
108
+ // a target that depends on @mnstry/atelier, ships packs/, or carries an
109
+ // atelier.project.json MUST declare a probe-able bin — a missing or empty
110
+ // bin, or a package.json that does not parse, is a blocking failure there.
111
+ // Only a target with none of those markers gets the advisory skip (packages
112
+ // without a CLI exist). A declared bin that cannot be resolved, or whose
113
+ // --version stdout lacks the attribution bytes, is always blocking.
114
+ function looksLikeDistribution(target, pkg) {
115
+ if (pkg && (pkg.dependencies?.['@mnstry/atelier'] || pkg.peerDependencies?.['@mnstry/atelier'] || pkg.devDependencies?.['@mnstry/atelier'])) return true
116
+ if (fs.existsSync(path.join(target, 'packs'))) return true
117
+ if (fs.existsSync(path.join(target, 'atelier.project.json'))) return true
118
+ return false
119
+ }
120
+
121
+ function declaredBinEntries(target) {
122
+ let pkg
123
+ try {
124
+ pkg = JSON.parse(fs.readFileSync(path.join(target, 'package.json'), 'utf8'))
125
+ } catch (error) {
126
+ if (error?.code === 'ENOENT') return { pkg: null, entries: null }
127
+ // A manifest that exists but does not parse must never be
128
+ // indistinguishable from an absent one.
129
+ return { pkg: null, entries: null, malformed: true }
130
+ }
131
+ const bin = pkg?.bin
132
+ if (typeof bin === 'string' && bin.trim()) {
133
+ return { pkg, entries: [{ name: typeof pkg.name === 'string' ? pkg.name : 'bin', file: bin.trim() }] }
134
+ }
135
+ if (bin && typeof bin === 'object' && !Array.isArray(bin)) {
136
+ const entries = Object.entries(bin)
137
+ .filter(([, file]) => typeof file === 'string' && file.trim())
138
+ .map(([name, file]) => ({ name, file: file.trim() }))
139
+ if (entries.length) return { pkg, entries }
140
+ }
141
+ return { pkg, entries: null }
142
+ }
143
+
144
+ function checkBinAttribution(target) {
145
+ const { pkg, entries, malformed } = declaredBinEntries(target)
146
+ if (malformed) {
147
+ return { ok: false, reason: 'package.json exists but does not parse; the CLI attribution probe cannot be skipped by shipping a malformed manifest' }
148
+ }
149
+ if (entries === null) {
150
+ if (looksLikeDistribution(target, pkg)) {
151
+ return { ok: false, reason: 'target looks like a distribution (declares @mnstry/atelier, ships packs/, or carries atelier.project.json) but declares no probe-able bin; a distribution CLI must exist and carry the attribution' }
152
+ }
153
+ return { ok: true, note: 'no package.json bin declared and no distribution markers; CLI attribution probe skipped' }
154
+ }
155
+ for (const { name, file } of entries) {
156
+ const resolved = path.resolve(target, file)
157
+ if (!fs.existsSync(resolved)) {
158
+ return { ok: false, reason: `declared bin "${name}" cannot be resolved: ${displayPath(target, resolved)}` }
159
+ }
160
+ const result = spawnSync(process.execPath, [resolved, '--version'], {
161
+ cwd: target,
162
+ encoding: 'utf8',
163
+ timeout: 30_000,
164
+ })
165
+ const stdout = typeof result.stdout === 'string' ? result.stdout : ''
166
+ if (result.error || !stdout.includes(ATTRIBUTION_BYTES)) {
167
+ return {
168
+ ok: false,
169
+ reason: `declared bin "${name}" --version stdout does not contain the exact byte string "${ATTRIBUTION_BYTES}"`,
170
+ }
171
+ }
172
+ }
173
+ return { ok: true, note: null }
174
+ }
175
+
176
+ function usageExit(message) {
177
+ logError(message)
178
+ logError('Usage: distribution check [--target DIR] [--pack DIR]')
179
+ process.exit(2)
180
+ }
181
+
182
+ function runCheck(args) {
183
+ if (args.target === true) usageExit('--target requires a value')
184
+ if (args.pack === true) usageExit('--pack requires a value')
185
+ const target = path.resolve(typeof args.target === 'string' ? args.target : '.')
186
+ let stat = null
187
+ try {
188
+ stat = fs.statSync(target)
189
+ } catch {
190
+ stat = null
191
+ }
192
+ if (!stat?.isDirectory()) {
193
+ logError(`target is not a directory: ${typeof args.target === 'string' ? args.target : target}`)
194
+ process.exit(2)
195
+ }
196
+
197
+ const readme = checkReadmeAttribution(target)
198
+ const binProbe = checkBinAttribution(target)
199
+ reportPackAttribution(target, args.pack)
200
+ if (binProbe.note) log(`advisory: ${binProbe.note}`)
201
+
202
+ if (!readme.ok) {
203
+ logError(`${readme.reason}; distributions must carry the MNSTRY attribution — ${POLICY_POINTER}`)
204
+ process.exit(1)
205
+ }
206
+ if (!binProbe.ok) {
207
+ logError(`${binProbe.reason}; distributions must carry the MNSTRY attribution — ${POLICY_POINTER}`)
208
+ process.exit(1)
209
+ }
210
+ log('required README.md attribution present')
211
+ if (!binProbe.note) log('declared bin --version output carries the required attribution')
212
+ log('distribution check passed')
213
+ process.exit(0)
214
+ }
215
+
216
+ const args = parseArgs(process.argv.slice(2))
217
+ const subcommand = args._[0] || 'check'
218
+ if (subcommand === 'check') {
219
+ runCheck(args)
220
+ } else {
221
+ logError(`Unknown distribution command: ${subcommand}`)
222
+ logError('Usage: distribution check [--target DIR] [--pack DIR]')
223
+ process.exit(2)
224
+ }
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import { checkForbiddenEgress } from '../egress/forbidden-egress.mjs'
3
+
4
+ const findings = checkForbiddenEgress()
5
+ if (findings.length) {
6
+ for (const finding of findings) {
7
+ console.error(`[atelier-egress] ${finding.file}:${finding.line} ${finding.type}: ${finding.detail}`)
8
+ }
9
+ process.exit(1)
10
+ }
11
+ console.log('[atelier-egress] no forbidden network egress found')
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env node
2
+ // atelier extension-pack validate|list
3
+ //
4
+ // Self-contained subcommand parser (no shared CLI plumbing) so the module can
5
+ // be invoked directly: node src/commands/extension-pack.mjs <subcommand> ...
6
+ //
7
+ // Exit codes: 0 every enabled pack loads clean (warnings do not fail),
8
+ // 1 an enabled pack failed to load, 2 usage or project resolution error.
9
+ // No network access anywhere in this command. Log-safety contract: output
10
+ // names pack ids, declared paths, and loader messages; it never echoes pack
11
+ // or protocol file contents.
12
+
13
+ import path from 'node:path'
14
+ import { ATELIER_EXT_NAMESPACE, loadExtensionPacks } from '../extension-packs/loader.mjs'
15
+ import { asObject, commandProject } from '../project/config.mjs'
16
+
17
+ const REPORT_SCHEMA = 'mnstry.atelier-extension-pack-report@v1'
18
+ const LIST_SCHEMA = 'mnstry.atelier-extension-pack-list@v1'
19
+
20
+ const USAGE = `Usage: atelier extension-pack <subcommand>
21
+
22
+ Subcommands:
23
+ validate [--json] [--project ./atelier.project.json]
24
+ Load every declared extension pack in report mode and print one line
25
+ per pack (ok, error, or skipped) with errors and warnings indented.
26
+ --json emits { schema: "${REPORT_SCHEMA}", packs, ok }.
27
+
28
+ list [--json] [--project ./atelier.project.json]
29
+ Show declared packs with id, version, enabled, config-relative path, digest,
30
+ protocol count, and lock status (locked, unlocked, or mismatch).
31
+ This is the default subcommand.
32
+
33
+ Packs are declared in the tracked project config under
34
+ ext["${ATELIER_EXT_NAMESPACE}"].extensionPacks. Pack protocols are resolvable
35
+ by full namespaced id only; the machine-local overlay can disable a declared
36
+ pack but can never enable an undeclared one.
37
+
38
+ Exit codes: 0 every enabled pack loads clean (warnings do not fail),
39
+ 1 an enabled pack failed to load, 2 usage or project resolution error.`
40
+
41
+ function fail(message) {
42
+ console.error(message)
43
+ process.exit(2)
44
+ }
45
+
46
+ // --project / --project-config (both --flag VALUE and --flag=VALUE forms) are
47
+ // recognized here for the unknown-option check only; commandProject reads the
48
+ // same process.argv itself, so the values need no forwarding.
49
+ const OPTION_SPEC = { json: 'flag', project: 'value', 'project-config': 'value' }
50
+
51
+ function parseOptions(argv) {
52
+ const options = { json: false }
53
+ for (let i = 0; i < argv.length; i += 1) {
54
+ const arg = argv[i]
55
+ if (arg === '--help' || arg === '-h') {
56
+ console.log(USAGE)
57
+ process.exit(0)
58
+ }
59
+ if (!arg.startsWith('--')) {
60
+ fail(`unexpected argument: ${arg}\n\n${USAGE}`)
61
+ }
62
+ const name = arg.slice(2).split('=')[0]
63
+ if (!(name in OPTION_SPEC)) fail(`unknown option --${name}\n\n${USAGE}`)
64
+ if (OPTION_SPEC[name] === 'flag') {
65
+ options[name] = true
66
+ } else if (!arg.includes('=')) {
67
+ i += 1
68
+ if (argv[i] === undefined) fail(`--${name} requires a value`)
69
+ }
70
+ }
71
+ return options
72
+ }
73
+
74
+ function resolveProject() {
75
+ try {
76
+ return commandProject()
77
+ } catch (error) {
78
+ fail(error.message)
79
+ }
80
+ return null
81
+ }
82
+
83
+ function declaredEntries(project) {
84
+ const container = asObject(asObject(asObject(project?.config).ext)[ATELIER_EXT_NAMESPACE])
85
+ return Array.isArray(container.extensionPacks) ? container.extensionPacks : []
86
+ }
87
+
88
+ const LOCK_MISMATCH_PATTERN = /^lock (version|digest) mismatch/
89
+
90
+ // One row per declared entry, in declaration order, joining the loader report
91
+ // back onto the declaration. Loader messages without a usable packId (project-
92
+ // level problems and malformed entries) surface separately as project rows.
93
+ function buildRows(project, result) {
94
+ const entries = declaredEntries(project)
95
+ const attributed = new Set()
96
+ const rows = entries.map((entry, index) => {
97
+ const id = typeof entry?.id === 'string' && entry.id ? entry.id : null
98
+ const label = id ?? `extensionPacks[${index}]`
99
+ const record = id ? result.packs.find((pack) => pack.id === id) ?? null : null
100
+ const skippedEntry = id ? result.skipped.find((item) => item.packId === id) ?? null : null
101
+ const errors = id ? result.errors.filter((item) => item.packId === id).map((item) => item.message) : []
102
+ const warnings = id ? result.warnings.filter((item) => item.packId === id).map((item) => item.message) : []
103
+ if (id) attributed.add(id)
104
+ const status = record ? 'ok' : skippedEntry ? 'skipped' : 'error'
105
+ const declaredPath = typeof entry?.path === 'string' && entry.path ? entry.path : null
106
+ // Log-safety: paths in output stay relative to the project config dir
107
+ // (same rationale as distribution.mjs) so neither text nor --json output
108
+ // ever leaks machine-local absolute paths.
109
+ const resolvedPath = record?.path ?? (declaredPath ? path.resolve(project.configDir, declaredPath) : null)
110
+ const displayPath = resolvedPath ? path.relative(project.configDir, resolvedPath) || '.' : null
111
+ return {
112
+ id: label,
113
+ status,
114
+ // Effective enablement: the declared flag minus any disable (config or
115
+ // machine-local overlay); skippedReason names which disable applied.
116
+ enabled: entry?.enabled === true && !skippedEntry,
117
+ skippedReason: skippedEntry?.reason ?? null,
118
+ version: record?.version ?? (typeof entry?.version === 'string' ? entry.version : null),
119
+ path: displayPath,
120
+ digest: record?.digest ?? null,
121
+ protocolCount: record ? record.protocols.length : null,
122
+ lock: record ? record.lock : errors.some((message) => LOCK_MISMATCH_PATTERN.test(message)) ? 'mismatch' : null,
123
+ errors,
124
+ warnings,
125
+ }
126
+ })
127
+ const projectErrors = result.errors.filter((item) => !item.packId || !attributed.has(item.packId)).map((item) => item.message)
128
+ const projectWarnings = result.warnings.filter((item) => !item.packId || !attributed.has(item.packId)).map((item) => item.message)
129
+ return { rows, projectErrors, projectWarnings }
130
+ }
131
+
132
+ function loadReport() {
133
+ const project = resolveProject()
134
+ const result = loadExtensionPacks(project, { report: true })
135
+ return { project, result, ...buildRows(project, result) }
136
+ }
137
+
138
+ function printIndented(row) {
139
+ for (const message of row.errors) console.log(` error: ${message}`)
140
+ for (const message of row.warnings) console.log(` warning: ${message}`)
141
+ }
142
+
143
+ function runValidate(argv) {
144
+ const options = parseOptions(argv)
145
+ const { result, rows, projectErrors, projectWarnings } = loadReport()
146
+ const ok = result.errors.length === 0
147
+ if (options.json) {
148
+ const packs = rows.map(({ id, status, enabled, skippedReason, version, path: packPath, digest, protocolCount, lock, errors, warnings }) => ({
149
+ id, status, enabled, skippedReason, version, path: packPath, digest, protocolCount, lock, errors, warnings,
150
+ }))
151
+ console.log(JSON.stringify({ schema: REPORT_SCHEMA, ok, packs, errors: projectErrors, warnings: projectWarnings }, null, 2))
152
+ } else {
153
+ if (!rows.length && !projectErrors.length) console.log('no extension packs declared')
154
+ for (const row of rows) {
155
+ const detail = row.status === 'ok'
156
+ ? ` (${row.version}, ${row.protocolCount} ${row.protocolCount === 1 ? 'protocol' : 'protocols'}, ${row.lock})`
157
+ : row.status === 'skipped' ? ` (${row.skippedReason})` : ''
158
+ console.log(`${row.id} ${row.status}${detail}`)
159
+ printIndented(row)
160
+ }
161
+ if (projectErrors.length || projectWarnings.length) {
162
+ console.log('project')
163
+ for (const message of projectErrors) console.log(` error: ${message}`)
164
+ for (const message of projectWarnings) console.log(` warning: ${message}`)
165
+ }
166
+ }
167
+ process.exit(ok ? 0 : 1)
168
+ }
169
+
170
+ function runList(argv) {
171
+ const options = parseOptions(argv)
172
+ const { result, rows, projectErrors, projectWarnings } = loadReport()
173
+ const ok = result.errors.length === 0
174
+ if (options.json) {
175
+ const packs = rows.map(({ id, status, enabled, skippedReason, version, path: packPath, digest, protocolCount, lock }) => ({
176
+ id, status, enabled, skippedReason, version, path: packPath, digest, protocolCount, lock,
177
+ }))
178
+ console.log(JSON.stringify({ schema: LIST_SCHEMA, ok, packs, errors: projectErrors, warnings: projectWarnings }, null, 2))
179
+ } else {
180
+ if (!rows.length) console.log('no extension packs declared')
181
+ for (const row of rows) {
182
+ console.log(row.id)
183
+ console.log(` status: ${row.status}${row.skippedReason ? ` (${row.skippedReason})` : ''}`)
184
+ console.log(` version: ${row.version ?? 'unknown'}`)
185
+ console.log(` enabled: ${row.enabled ? 'yes' : 'no'}`)
186
+ if (row.path) console.log(` path: ${row.path}`)
187
+ if (row.digest) console.log(` digest: ${row.digest}`)
188
+ if (row.protocolCount !== null) console.log(` protocols: ${row.protocolCount}`)
189
+ if (row.lock) console.log(` lock: ${row.lock}`)
190
+ }
191
+ if (projectErrors.length || projectWarnings.length) {
192
+ console.log('project')
193
+ for (const message of projectErrors) console.log(` error: ${message}`)
194
+ for (const message of projectWarnings) console.log(` warning: ${message}`)
195
+ }
196
+ }
197
+ process.exit(ok ? 0 : 1)
198
+ }
199
+
200
+ const argv = process.argv.slice(2)
201
+ const first = argv[0]
202
+ if (first === '--help' || first === '-h' || first === 'help') {
203
+ console.log(USAGE)
204
+ process.exit(0)
205
+ }
206
+ if (first === 'validate') runValidate(argv.slice(1))
207
+ else if (first === 'list') runList(argv.slice(1))
208
+ else if (first === undefined || first.startsWith('--')) runList(argv)
209
+ else fail(`unknown extension-pack subcommand: ${first}\n\n${USAGE}`)
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { runFeedbackCommand } from '../support/feedback-report.mjs'
3
+ process.exitCode = runFeedbackCommand()
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { runGraphCommand } from '../graph/graph.mjs'
3
+ runGraphCommand()
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from 'node:child_process'
3
+ import fs from 'node:fs'
4
+ import path from 'node:path'
5
+ import { parseArgs, resolveProjectConfig, writeJson } from '../project/config.mjs'
6
+ import { writeAtelierLock } from '../upgrade/upgrade.mjs'
7
+
8
+ const packageRoot = path.resolve(new URL('../..', import.meta.url).pathname)
9
+
10
+ function slug(value) {
11
+ return String(value || '')
12
+ .trim()
13
+ .toLowerCase()
14
+ .replace(/[^a-z0-9._-]+/g, '-')
15
+ .replace(/^-+|-+$/g, '') || 'owner'
16
+ }
17
+
18
+ function gitConfig(name) {
19
+ const result = spawnSync('git', ['config', '--get', name], { encoding: 'utf8' })
20
+ return result.status === 0 ? result.stdout.trim() : ''
21
+ }
22
+
23
+ function readJson(file) {
24
+ return JSON.parse(fs.readFileSync(file, 'utf8'))
25
+ }
26
+
27
+ function copyDir(source, target, { renameGitignore = true } = {}) {
28
+ fs.mkdirSync(target, { recursive: true })
29
+ for (const ent of fs.readdirSync(source, { withFileTypes: true })) {
30
+ const from = path.join(source, ent.name)
31
+ // npm pack strips files named .gitignore, so templates ship the file as
32
+ // `gitignore` at the template root; nested files keep their literal names.
33
+ const rename = renameGitignore && ent.name === 'gitignore' && ent.isFile()
34
+ const to = path.join(target, rename ? '.gitignore' : ent.name)
35
+ if (ent.isDirectory()) copyDir(from, to, { renameGitignore: false })
36
+ else fs.copyFileSync(from, to)
37
+ }
38
+ }
39
+
40
+ function personalizeBoundaryPolicy(target, { actor, githubLogin, gitEmail } = {}) {
41
+ const policyPath = path.join(target, 'boundary-policy.v1.json')
42
+ if (!fs.existsSync(policyPath)) return
43
+ const policy = readJson(policyPath)
44
+ const actorId = slug(actor || 'owner')
45
+ const privateDomainRepo = Object.entries(policy.repos || {}).find(([, repo]) => repo?.kind === 'private_domain')?.[0] || `mnstry-private-${actorId}`
46
+ const login = githubLogin || process.env.GITHUB_ACTOR || actorId
47
+ const email = gitEmail || gitConfig('user.email') || `${actorId}@example.invalid`
48
+
49
+ policy.actors = {
50
+ [actorId]: {
51
+ githubLogin: login,
52
+ gitEmails: [email],
53
+ privateDomainRepo,
54
+ },
55
+ }
56
+ if (policy.repos?.[privateDomainRepo]) policy.repos[privateDomainRepo].ownerActor = actorId
57
+ writeJson(policyPath, policy)
58
+ }
59
+
60
+ const args = parseArgs(process.argv.slice(2))
61
+ const target = path.resolve(args.target || process.cwd())
62
+ const template = args.template || args.fixture
63
+ let templateId = template || 'default'
64
+ if (template === 'sample-workspace') {
65
+ copyDir(path.join(packageRoot, 'fixtures/projects/sample-workspace'), target)
66
+ console.log(`created sample Atelier workspace at ${target}`)
67
+ } else if (template === 'private-domain') {
68
+ copyDir(path.join(packageRoot, 'templates/private-domain-workspace'), target)
69
+ personalizeBoundaryPolicy(target, {
70
+ actor: args.actor,
71
+ githubLogin: args['github-login'],
72
+ gitEmail: args['git-email'],
73
+ })
74
+ console.log(`created private domain Atelier workspace at ${target}`)
75
+ } else if (template === 'shared-project') {
76
+ copyDir(path.join(packageRoot, 'templates/shared-project-workspace'), target)
77
+ personalizeBoundaryPolicy(target, {
78
+ actor: args.actor,
79
+ githubLogin: args['github-login'],
80
+ gitEmail: args['git-email'],
81
+ })
82
+ console.log(`created shared project Atelier workspace at ${target}`)
83
+ } else if (template === 'distribution') {
84
+ copyDir(path.join(packageRoot, 'templates/distribution-workspace'), target)
85
+ personalizeBoundaryPolicy(target, {
86
+ actor: args.actor,
87
+ githubLogin: args['github-login'],
88
+ gitEmail: args['git-email'],
89
+ })
90
+ console.log(`created distribution Atelier workspace at ${target}`)
91
+ } else if (template) {
92
+ // Unknown template names fail closed: a typo must not silently produce a
93
+ // blank scaffold that lacks the boundary policy the caller asked for.
94
+ console.error(`Unknown template: ${template}. Valid templates: private-domain, shared-project, sample-workspace, distribution.`)
95
+ process.exit(1)
96
+ } else {
97
+ fs.mkdirSync(target, { recursive: true })
98
+ fs.mkdirSync(path.join(target, 'content'), { recursive: true })
99
+ writeJson(path.join(target, 'atelier.project.json'), {
100
+ schema: 'mnstry.atelier-project-config@v1',
101
+ name: path.basename(target),
102
+ roots: { workspace: '.' },
103
+ graph: { repoAccessPath: 'repo-access.v1.json', outputPath: 'atelier-output/knowledge.graph.json' },
104
+ projection: { outputRoot: 'atelier-output', readinessPath: 'atelier-output/atelier-readiness.json' },
105
+ repos: [{ name: 'content', path: 'content', readBoundary: 'private' }],
106
+ })
107
+ writeJson(path.join(target, 'repo-access.v1.json'), {
108
+ schema: 'mnstry.atelier-repo-access@v1',
109
+ defaultReadBoundary: 'private',
110
+ repos: { content: { readBoundary: 'private' } },
111
+ })
112
+ console.log(`created Atelier project scaffold at ${target}`)
113
+ }
114
+
115
+ // Every workspace gets a preview launch config, whichever template made it.
116
+ // autoPort matters: a workspace usually keeps a long-lived instance on the
117
+ // canonical port, and a preview that cannot take a free port evicts it.
118
+ const launchTemplate = path.join(packageRoot, 'templates/launch.json')
119
+ const launchTarget = path.join(target, '.claude/launch.json')
120
+ if (fs.existsSync(launchTemplate) && !fs.existsSync(launchTarget)) {
121
+ fs.mkdirSync(path.dirname(launchTarget), { recursive: true })
122
+ fs.copyFileSync(launchTemplate, launchTarget)
123
+ }
124
+
125
+ const projectPath = path.join(target, 'atelier.project.json')
126
+ if (fs.existsSync(projectPath)) {
127
+ const project = resolveProjectConfig({ cwd: target, argv: ['--project', projectPath] })
128
+ writeAtelierLock({ project, templateId })
129
+ console.log(`wrote Atelier lock at ${path.join(target, 'atelier.lock.json')}`)
130
+ }
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { runLockCommand } from '../upgrade/upgrade.mjs'
3
+ runLockCommand()
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { runProjectCommand } from '../projection/project.mjs'
3
+ runProjectCommand()
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env node
2
+ import { runPromoteCommand } from '../boundary/policy.mjs'
3
+
4
+ runPromoteCommand()
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ import { runReadinessCommand } from '../readiness/readiness.mjs'
3
+ runReadinessCommand()
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ import { runServerCommand } from '../server/server.mjs'
3
+ runServerCommand().catch((error) => {
4
+ console.error(error.message)
5
+ process.exit(1)
6
+ })