@thebackstoryis/engineering-with-ai 0.2.9

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 (334) hide show
  1. package/Docs/README.md +50 -0
  2. package/Docs/adoption/consultancy-and-multi-project-rollout.md +135 -0
  3. package/Docs/adoption/non-technical-team-guide.md +126 -0
  4. package/Docs/archaeology-technology-and-hosting-discovery.md +212 -0
  5. package/Docs/blast-radius-and-impact-routing-guide.md +325 -0
  6. package/Docs/blueprints/internal-blueprint-catalogue.md +146 -0
  7. package/Docs/blueprints/maintaining-organisation-blueprints.md +154 -0
  8. package/Docs/blueprints/validation-and-troubleshooting.md +168 -0
  9. package/Docs/cli-reference.md +113 -0
  10. package/Docs/completed-phase-evidence-amendments.md +74 -0
  11. package/Docs/consultancy-network-rollout-control-plane-guide.md +202 -0
  12. package/Docs/context-aware-delivery-companion-guide.md +198 -0
  13. package/Docs/context-aware-delivery-companion-user-guide.md +184 -0
  14. package/Docs/context-management-and-token-efficiency.md +113 -0
  15. package/Docs/design-systems/design-system-implementation-guide.md +85 -0
  16. package/Docs/design-systems/design-system-pack-authoring-guide.md +95 -0
  17. package/Docs/design-systems/design-system-review-guide.md +51 -0
  18. package/Docs/design-systems/design-system-user-guide.md +96 -0
  19. package/Docs/design-systems/product-owner-guide.md +49 -0
  20. package/Docs/designing-organisation-blueprint-packs.md +384 -0
  21. package/Docs/developer-delivery-guide.md +224 -0
  22. package/Docs/error-reporting-guide.md +110 -0
  23. package/Docs/error-reporting-provider-guide.md +49 -0
  24. package/Docs/examples/error-report-adapter.md +70 -0
  25. package/Docs/examples/meeting-review.md +76 -0
  26. package/Docs/examples/minimal-design-system.md +67 -0
  27. package/Docs/examples/prototype-review-inputs.md +175 -0
  28. package/Docs/examples/reproducible-archaeology-depth-example.md +144 -0
  29. package/Docs/examples/test-scenario-input.md +68 -0
  30. package/Docs/examples/worked-examples.md +147 -0
  31. package/Docs/existing-project-onboarding-guide.md +214 -0
  32. package/Docs/explanation/core-concepts.md +26 -0
  33. package/Docs/explanation/delivery-workflow.md +48 -0
  34. package/Docs/governance/governance-team-guide.md +139 -0
  35. package/Docs/governed-starter-project-materialisation-guide.md +284 -0
  36. package/Docs/guide-catalogue.md +117 -0
  37. package/Docs/guided-discovery-facilitator-guide.md +172 -0
  38. package/Docs/guided-intent-workspace-guide.md +109 -0
  39. package/Docs/guided-phase-evidence-drafting-guide.md +119 -0
  40. package/Docs/human-approval-and-assurance-guide.md +146 -0
  41. package/Docs/knowledge-proposals-implementer-guide.md +106 -0
  42. package/Docs/knowledge-proposals-user-guide.md +247 -0
  43. package/Docs/maintainers/context-benchmarks.md +29 -0
  44. package/Docs/maintainers/contributing.md +58 -0
  45. package/Docs/maintainers/evidence-depth-acceptance.md +72 -0
  46. package/Docs/maintainers/verification-walkthroughs.md +104 -0
  47. package/Docs/meeting-evidence-implementer-guide.md +132 -0
  48. package/Docs/meeting-evidence-user-guide.md +200 -0
  49. package/Docs/operations/dashboard-and-delivery-state.md +153 -0
  50. package/Docs/operations/dashboard-configuration.md +87 -0
  51. package/Docs/operations/installation-updating-and-entitlements.md +135 -0
  52. package/Docs/operations/premium-personas-setup.md +76 -0
  53. package/Docs/operations/troubleshooting-and-recovery.md +205 -0
  54. package/Docs/organisation-rollout-guide.md +142 -0
  55. package/Docs/persona-entitlement-provider-guide.md +199 -0
  56. package/Docs/persona-guided-prototype-iteration.md +129 -0
  57. package/Docs/personas/organisation-specific-personas.md +103 -0
  58. package/Docs/personas/persona-authoring-cookbook.md +176 -0
  59. package/Docs/personas/persona-engagement-ui.md +133 -0
  60. package/Docs/personas/persona-governance.md +118 -0
  61. package/Docs/platform-export-analysis-guide.md +336 -0
  62. package/Docs/policies/governance-owner-guide.md +36 -0
  63. package/Docs/policies/implementation-guide.md +42 -0
  64. package/Docs/policies/organisation-policy-design-gates.md +58 -0
  65. package/Docs/policies/policy-pack-authoring-guide.md +108 -0
  66. package/Docs/policies/product-owner-guide.md +43 -0
  67. package/Docs/policies/technical-owner-guide.md +37 -0
  68. package/Docs/product-owner-guide.md +327 -0
  69. package/Docs/project-portfolio-orchestration-guide.md +199 -0
  70. package/Docs/quality/manual-qa-and-acceptance.md +162 -0
  71. package/Docs/quality/persona-driven-test-scenarios.md +172 -0
  72. package/Docs/quality/reproducible-archaeology-depth-review-checklist.md +89 -0
  73. package/Docs/reference/capabilities-and-project-layout.md +678 -0
  74. package/Docs/reference/cli-and-configuration.md +398 -0
  75. package/Docs/reference/contributions-api.md +23 -0
  76. package/Docs/reference/security-adapter-authoring.md +81 -0
  77. package/Docs/reference/starter-adapter-authoring.md +74 -0
  78. package/Docs/repository-source-map-guide.md +381 -0
  79. package/Docs/reproducible-archaeology-and-discovery-depth.md +292 -0
  80. package/Docs/screen-prototype-creation-guide.md +324 -0
  81. package/Docs/security-validation-guide.md +353 -0
  82. package/Docs/solution-readiness-review-guide.md +123 -0
  83. package/Docs/standards/project-standards-authoring.md +157 -0
  84. package/Docs/team-hub-guide.md +162 -0
  85. package/Docs/team-hub-resource-registry-guide.md +167 -0
  86. package/Docs/tutorials/first-delivery.md +83 -0
  87. package/Docs/tutorials/first-session.md +62 -0
  88. package/Docs/using-lifecycle-hooks.md +381 -0
  89. package/Docs/working-with-personas.md +274 -0
  90. package/LICENSE +165 -0
  91. package/README.md +96 -0
  92. package/agents-src/claude/ewai-security-reviewer.md +15 -0
  93. package/bin/ewai +5 -0
  94. package/config/archaeology-record-families.yaml +59 -0
  95. package/config/delivery-artifacts.yaml +121 -0
  96. package/config/delivery-stages.yaml +77 -0
  97. package/config/design-system.schema.json +46 -0
  98. package/config/error-reporting.schema.json +79 -0
  99. package/config/evidence-depth.schema.json +53 -0
  100. package/config/intent.schema.json +90 -0
  101. package/config/knowledge-proposals-proposal.schema.json +34 -0
  102. package/config/lifecycle-event.schema.json +68 -0
  103. package/config/lifecycle-handler.schema.json +45 -0
  104. package/config/lifecycle-hook-ack.schema.json +19 -0
  105. package/config/meeting-evidence-candidate.schema.json +102 -0
  106. package/config/organisation-policy.schema.json +137 -0
  107. package/config/pack.schema.json +250 -0
  108. package/config/persona-pack.schema.json +21 -0
  109. package/config/persona.schema.json +17 -0
  110. package/config/policy-evaluation.schema.json +77 -0
  111. package/config/policy-facts.schema.json +140 -0
  112. package/config/portfolio.schema.json +68 -0
  113. package/config/project.schema.json +313 -0
  114. package/config/prototype-iteration.schema.json +128 -0
  115. package/config/rollout.schema.json +87 -0
  116. package/config/security-adapter.schema.json +31 -0
  117. package/config/security-scan-request.schema.json +64 -0
  118. package/config/security-scan-response.schema.json +52 -0
  119. package/config/security-validation-policy.schema.json +74 -0
  120. package/config/starter-source-acknowledgement.schema.json +13 -0
  121. package/config/starter-source-adapter.schema.json +38 -0
  122. package/config/starter-source-request.schema.json +61 -0
  123. package/package.json +77 -0
  124. package/packs/core/pack.yaml +7 -0
  125. package/packs/design-systems/default/experience-promise.md +9 -0
  126. package/packs/design-systems/default/intentional-review.md +10 -0
  127. package/packs/design-systems/default/interaction-and-entry.md +9 -0
  128. package/packs/design-systems/default/meaningful-content-and-states.md +9 -0
  129. package/packs/design-systems/default/pack.yaml +44 -0
  130. package/packs/design-systems/default/principles.md +10 -0
  131. package/packs/personas/core/pack.yaml +7 -0
  132. package/packs/personas/core/personas/archaeologist.md +37 -0
  133. package/packs/personas/core/personas/end-user.md +17 -0
  134. package/packs/personas/core/personas/maintainer.md +17 -0
  135. package/packs/personas/core/personas/operator.md +17 -0
  136. package/packs/personas/core/personas/specs-knowledge-curator.md +35 -0
  137. package/packs/technologies/laravel/pack.yaml +30 -0
  138. package/packs/technologies/laravel-nuxt/pack.yaml +30 -0
  139. package/packs/technologies/nuxt/pack.yaml +30 -0
  140. package/packs/technologies/power-platform/pack.yaml +31 -0
  141. package/packs/technologies/salesforce/pack.yaml +25 -0
  142. package/public/app.js +4896 -0
  143. package/public/apple-touch-icon.png +0 -0
  144. package/public/assets/backstory-icon.png +0 -0
  145. package/public/dashboard-navigation.js +98 -0
  146. package/public/favicon-16.png +0 -0
  147. package/public/favicon-32.png +0 -0
  148. package/public/favicon.ico +0 -0
  149. package/public/index.html +789 -0
  150. package/public/styles.css +2693 -0
  151. package/public/team-hub/app.js +202 -0
  152. package/public/team-hub/index.html +79 -0
  153. package/public/team-hub/styles.css +90 -0
  154. package/scripts/publication-check.mjs +140 -0
  155. package/scripts/setup.mjs +21 -0
  156. package/skills-src/ewai-archaeology/SKILL.md +334 -0
  157. package/skills-src/ewai-archaeology/agents/openai.yaml +4 -0
  158. package/skills-src/ewai-archaeology/references/archaeology-contract.md +201 -0
  159. package/skills-src/ewai-archaeology/references/lifecycle-reconstruction.md +177 -0
  160. package/skills-src/ewai-archaeology/references/maximum-detail-reconstruction.md +97 -0
  161. package/skills-src/ewai-archaeology/references/model-routing.md +26 -0
  162. package/skills-src/ewai-architecture/SKILL.md +108 -0
  163. package/skills-src/ewai-architecture/agents/openai.yaml +4 -0
  164. package/skills-src/ewai-architecture/references/architecture-contract.md +176 -0
  165. package/skills-src/ewai-context/SKILL.md +68 -0
  166. package/skills-src/ewai-context/agents/openai.yaml +4 -0
  167. package/skills-src/ewai-context-import/SKILL.md +118 -0
  168. package/skills-src/ewai-context-import/agents/openai.yaml +4 -0
  169. package/skills-src/ewai-context-import/references/context-import-contract.md +106 -0
  170. package/skills-src/ewai-dashboard-configuration/SKILL.md +20 -0
  171. package/skills-src/ewai-deliver/SKILL.md +122 -0
  172. package/skills-src/ewai-deliver/references/delivery-evidence.md +92 -0
  173. package/skills-src/ewai-deliver/references/phase-routing.md +31 -0
  174. package/skills-src/ewai-design-system-apply/SKILL.md +27 -0
  175. package/skills-src/ewai-design-system-apply/agents/openai.yaml +4 -0
  176. package/skills-src/ewai-design-system-apply/references/application-contract.md +36 -0
  177. package/skills-src/ewai-design-system-author/SKILL.md +28 -0
  178. package/skills-src/ewai-design-system-author/agents/openai.yaml +4 -0
  179. package/skills-src/ewai-design-system-author/references/authoring-contract.md +38 -0
  180. package/skills-src/ewai-design-system-review/SKILL.md +26 -0
  181. package/skills-src/ewai-design-system-review/agents/openai.yaml +4 -0
  182. package/skills-src/ewai-design-system-review/references/review-contract.md +40 -0
  183. package/skills-src/ewai-error-reporting/SKILL.md +46 -0
  184. package/skills-src/ewai-error-reporting/agents/openai.yaml +4 -0
  185. package/skills-src/ewai-error-reporting/references/provider-contract.md +74 -0
  186. package/skills-src/ewai-evidence-depth/SKILL.md +72 -0
  187. package/skills-src/ewai-evidence-depth/agents/openai.yaml +4 -0
  188. package/skills-src/ewai-evidence-depth/references/evidence-depth-contract.md +127 -0
  189. package/skills-src/ewai-intent/SKILL.md +68 -0
  190. package/skills-src/ewai-intent/agents/openai.yaml +4 -0
  191. package/skills-src/ewai-intent/references/intent-contract.md +42 -0
  192. package/skills-src/ewai-knowledge-proposals/SKILL.md +105 -0
  193. package/skills-src/ewai-knowledge-proposals/agents/openai.yaml +4 -0
  194. package/skills-src/ewai-knowledge-proposals/references/proposal-contract.md +59 -0
  195. package/skills-src/ewai-meeting-evidence/SKILL.md +106 -0
  196. package/skills-src/ewai-meeting-evidence/agents/openai.yaml +4 -0
  197. package/skills-src/ewai-meeting-evidence/references/candidate-contract.md +64 -0
  198. package/skills-src/ewai-organisation-policy/SKILL.md +62 -0
  199. package/skills-src/ewai-organisation-policy/agents/openai.yaml +4 -0
  200. package/skills-src/ewai-organisation-policy/references/policy-contract.md +94 -0
  201. package/skills-src/ewai-palace-housekeeping/SKILL.md +55 -0
  202. package/skills-src/ewai-palace-housekeeping/agents/openai.yaml +4 -0
  203. package/skills-src/ewai-persona-entitlement/SKILL.md +60 -0
  204. package/skills-src/ewai-persona-entitlement/agents/openai.yaml +4 -0
  205. package/skills-src/ewai-phase-evidence/SKILL.md +79 -0
  206. package/skills-src/ewai-phase-evidence/agents/openai.yaml +4 -0
  207. package/skills-src/ewai-pipeline/SKILL.md +130 -0
  208. package/skills-src/ewai-pipeline/agents/openai.yaml +4 -0
  209. package/skills-src/ewai-pipeline/references/cli.md +86 -0
  210. package/skills-src/ewai-pipeline/references/specs-contract.md +16 -0
  211. package/skills-src/ewai-portfolio/SKILL.md +70 -0
  212. package/skills-src/ewai-portfolio/agents/openai.yaml +4 -0
  213. package/skills-src/ewai-portfolio/references/portfolio-contract.md +67 -0
  214. package/skills-src/ewai-project-discovery/SKILL.md +95 -0
  215. package/skills-src/ewai-project-discovery/agents/openai.yaml +4 -0
  216. package/skills-src/ewai-project-discovery/references/discovery-contract.md +34 -0
  217. package/skills-src/ewai-prototype-iteration/SKILL.md +30 -0
  218. package/skills-src/ewai-prototype-iteration/agents/openai.yaml +4 -0
  219. package/skills-src/ewai-prototype-iteration/references/review-contract.md +49 -0
  220. package/skills-src/ewai-retro/SKILL.md +48 -0
  221. package/skills-src/ewai-retro/agents/openai.yaml +4 -0
  222. package/skills-src/ewai-retro/references/asset-routing.md +14 -0
  223. package/skills-src/ewai-rollout/SKILL.md +74 -0
  224. package/skills-src/ewai-rollout/agents/openai.yaml +4 -0
  225. package/skills-src/ewai-rollout/references/rollout-contract.md +74 -0
  226. package/skills-src/ewai-shape-intents/SKILL.md +84 -0
  227. package/skills-src/ewai-shape-intents/agents/openai.yaml +4 -0
  228. package/skills-src/ewai-shape-intents/references/intent-mapping-contract.md +109 -0
  229. package/skills-src/ewai-solution-readiness/SKILL.md +55 -0
  230. package/skills-src/ewai-solution-readiness/agents/openai.yaml +4 -0
  231. package/skills-src/ewai-standards-check/SKILL.md +93 -0
  232. package/skills-src/ewai-standards-check/agents/openai.yaml +4 -0
  233. package/skills-src/ewai-standards-check/references/report-contract.md +116 -0
  234. package/skills-src/ewai-test-scenarios/SKILL.md +94 -0
  235. package/skills-src/ewai-test-scenarios/agents/openai.yaml +4 -0
  236. package/skills-src/ewai-test-scenarios/references/scenario-contract.md +88 -0
  237. package/src/afk-worker.mjs +16 -0
  238. package/src/archaeology.mjs +1333 -0
  239. package/src/checkin.mjs +261 -0
  240. package/src/cli.mjs +2427 -0
  241. package/src/companion-guidance.mjs +257 -0
  242. package/src/companion-opening.mjs +62 -0
  243. package/src/companion.mjs +256 -0
  244. package/src/context.mjs +210 -0
  245. package/src/dashboard-preferences.mjs +80 -0
  246. package/src/delivery-artifacts.mjs +204 -0
  247. package/src/delivery-documents.mjs +248 -0
  248. package/src/delivery-gates.mjs +317 -0
  249. package/src/delivery.mjs +1433 -0
  250. package/src/design-system-application.mjs +291 -0
  251. package/src/design-system-authoring.mjs +101 -0
  252. package/src/design-systems.mjs +466 -0
  253. package/src/discovery.mjs +1314 -0
  254. package/src/error-reporting.mjs +323 -0
  255. package/src/evidence-depth.mjs +543 -0
  256. package/src/execution-state.mjs +243 -0
  257. package/src/install.mjs +166 -0
  258. package/src/intent-dependencies.mjs +117 -0
  259. package/src/intent-maps.mjs +402 -0
  260. package/src/intents.mjs +747 -0
  261. package/src/knowledge-proposals.mjs +717 -0
  262. package/src/launcher.mjs +51 -0
  263. package/src/meeting-evidence.mjs +703 -0
  264. package/src/network-rollout.mjs +386 -0
  265. package/src/organisation-blueprints.mjs +438 -0
  266. package/src/organisation-policies.mjs +448 -0
  267. package/src/packs.mjs +44 -0
  268. package/src/paths.mjs +62 -0
  269. package/src/persona-entitlements.mjs +438 -0
  270. package/src/persona-licence-config.mjs +98 -0
  271. package/src/persona-website-provider.mjs +134 -0
  272. package/src/persona-zip.mjs +87 -0
  273. package/src/personas.mjs +159 -0
  274. package/src/platform-metadata-analysis.mjs +314 -0
  275. package/src/policy-design-gates.mjs +623 -0
  276. package/src/policy-gate-integration.mjs +318 -0
  277. package/src/portfolio.mjs +509 -0
  278. package/src/power-platform-source-map.mjs +190 -0
  279. package/src/project.mjs +449 -0
  280. package/src/prototype-iterations.mjs +730 -0
  281. package/src/repository-source-map.mjs +603 -0
  282. package/src/runtime/afk-conductor.mjs +973 -0
  283. package/src/runtime/context-assembly.mjs +457 -0
  284. package/src/runtime/context-benchmarks.mjs +115 -0
  285. package/src/runtime/dashboard-actions.mjs +109 -0
  286. package/src/runtime/dashboard-handoffs.mjs +149 -0
  287. package/src/runtime/dashboard-server.mjs +1272 -0
  288. package/src/runtime/dashboard.mjs +197 -0
  289. package/src/runtime/database.mjs +789 -0
  290. package/src/runtime/error-reporting.mjs +581 -0
  291. package/src/runtime/evidence-depth-workspace.mjs +412 -0
  292. package/src/runtime/execution-leases.mjs +299 -0
  293. package/src/runtime/guided-discovery.mjs +350 -0
  294. package/src/runtime/guided-intents.mjs +517 -0
  295. package/src/runtime/impact-analysis.mjs +535 -0
  296. package/src/runtime/intents.mjs +222 -0
  297. package/src/runtime/knowledge.mjs +86 -0
  298. package/src/runtime/lifecycle-hooks.mjs +1239 -0
  299. package/src/runtime/mcp-config.mjs +110 -0
  300. package/src/runtime/mcp-server.mjs +1885 -0
  301. package/src/runtime/palace.mjs +362 -0
  302. package/src/runtime/paths.mjs +58 -0
  303. package/src/runtime/persona-engagement.mjs +255 -0
  304. package/src/runtime/phase-contributions.mjs +594 -0
  305. package/src/runtime/policy-workspace.mjs +170 -0
  306. package/src/runtime/prototype-iterations.mjs +235 -0
  307. package/src/runtime/provider-adapters.mjs +163 -0
  308. package/src/runtime/repository-index.mjs +838 -0
  309. package/src/runtime/runs.mjs +185 -0
  310. package/src/runtime/security-validation.mjs +1230 -0
  311. package/src/runtime/starter-materialisation.mjs +1155 -0
  312. package/src/runtime/team-hub-client.mjs +479 -0
  313. package/src/runtime/team-hub-database.mjs +288 -0
  314. package/src/runtime/team-hub-server.mjs +191 -0
  315. package/src/runtime/team-hub.mjs +110 -0
  316. package/src/runtime/tree-sitter-index.mjs +390 -0
  317. package/src/runtime/version.mjs +1 -0
  318. package/src/runtime/work.mjs +633 -0
  319. package/src/salesforce-source-map.mjs +212 -0
  320. package/src/security-validation-config.mjs +224 -0
  321. package/src/solution-readiness.mjs +620 -0
  322. package/src/starter-materialisation-contract.mjs +407 -0
  323. package/src/task-graph.mjs +544 -0
  324. package/src/team-hub-resources.mjs +239 -0
  325. package/src/team-hub.mjs +242 -0
  326. package/src/test-scenarios.mjs +622 -0
  327. package/src/validation-config.mjs +289 -0
  328. package/templates/SPECS/1.Scope/personas/registry.yaml +12 -0
  329. package/templates/SPECS/5.Strategy/patterns/context-packet.md +119 -0
  330. package/templates/SPECS/6.Build/_tracker-template.md +16 -0
  331. package/templates/SPECS/pipeline.yaml +62 -0
  332. package/templates/discovery-answers.yaml +86 -0
  333. package/templates/intent-body.md +21 -0
  334. package/tests/fixtures/context-benchmarks.json +9 -0
@@ -0,0 +1,466 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import {
3
+ existsSync,
4
+ lstatSync,
5
+ mkdirSync,
6
+ readFileSync,
7
+ readdirSync,
8
+ realpathSync,
9
+ renameSync,
10
+ statSync,
11
+ unlinkSync,
12
+ writeFileSync,
13
+ } from 'node:fs';
14
+ import { homedir } from 'node:os';
15
+ import { dirname, isAbsolute, relative, resolve } from 'node:path';
16
+ import { fileURLToPath } from 'node:url';
17
+ import YAML from 'yaml';
18
+ import { z } from 'zod';
19
+ import { defaultPackRoot } from './packs.mjs';
20
+ import { loadProjectConfig } from './project.mjs';
21
+ import {
22
+ listOrganisationBlueprints,
23
+ organisationDesignSystemRecommendations,
24
+ resolveOrganisationBlueprint,
25
+ } from './organisation-blueprints.mjs';
26
+
27
+ const moduleDirectory = dirname(fileURLToPath(import.meta.url));
28
+ const packageVersion = JSON.parse(readFileSync(resolve(moduleDirectory, '../package.json'), 'utf8')).version;
29
+ const currentEwaiMajor = Number(packageVersion.split('.')[0]);
30
+
31
+ export const DEFAULT_DESIGN_SYSTEM_ID = 'ewai.design-system.default';
32
+ export const DESIGN_SYSTEM_EVIDENCE_PATH = 'SPECS/5.Strategy/design-system.md';
33
+
34
+ const packId = z.string().regex(/^(?:ewai\.[a-z0-9.-]+|org\.[a-z0-9-]+\.[a-z0-9.-]+)$/);
35
+ const contributionId = z.string().regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/);
36
+ const relativeContentPath = z.string().min(1).max(240).refine((value) => {
37
+ if (isAbsolute(value) || value.includes('\0') || value.split(/[\\/]+/).includes('..')) return false;
38
+ try {
39
+ new URL(value);
40
+ return false;
41
+ } catch {
42
+ return !/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(value);
43
+ }
44
+ }, 'must be a bounded relative path and may not be a remote URL');
45
+
46
+ const contributionSchema = z.object({
47
+ id: contributionId,
48
+ kind: z.enum(['experience', 'principles', 'foundations', 'tokens', 'components', 'interaction', 'content', 'states', 'responsive', 'accessibility', 'motion', 'prohibited', 'review']),
49
+ title: z.string().min(1).max(160),
50
+ source: relativeContentPath,
51
+ applicability: z.array(contributionId).min(1).max(30).refine((values) => new Set(values).size === values.length, 'must not contain duplicates'),
52
+ required: z.boolean(),
53
+ replaces: z.array(z.string().regex(/^(?:ewai\.[a-z0-9.-]+|org\.[a-z0-9-]+\.[a-z0-9.-]+):[a-z0-9]+(?:-[a-z0-9]+)*$/)).max(30).default([]),
54
+ }).strict();
55
+
56
+ const designSystemPackSchema = z.object({
57
+ schema: z.literal('ewai.pack/v1'),
58
+ id: packId,
59
+ name: z.string().min(1),
60
+ description: z.string().min(1),
61
+ version: z.string().regex(/^\d+\.\d+\.\d+$/),
62
+ type: z.literal('design-system'),
63
+ requires: z.array(packId).default([]),
64
+ design_system: z.object({
65
+ compatibility: z.object({ ewai: z.string().regex(/^\d+\.x$/) }).strict(),
66
+ provenance: z.object({
67
+ kind: z.enum(['owner-declared', 'observed', 'inferred', 'adapted']),
68
+ summary: z.string().min(1).max(500),
69
+ source: z.string().min(1).max(500).optional(),
70
+ }).strict(),
71
+ contributions: z.array(contributionSchema).min(1).max(200),
72
+ }).strict(),
73
+ }).strict().superRefine((pack, context) => {
74
+ const requires = new Set();
75
+ for (const [index, id] of pack.requires.entries()) {
76
+ if (id === pack.id) context.addIssue({ code: 'custom', path: ['requires', index], message: 'a design system may not require itself' });
77
+ if (requires.has(id)) context.addIssue({ code: 'custom', path: ['requires', index], message: `duplicate dependency: ${id}` });
78
+ requires.add(id);
79
+ }
80
+ const contributions = new Set();
81
+ for (const [index, contribution] of pack.design_system.contributions.entries()) {
82
+ if (contributions.has(contribution.id)) context.addIssue({ code: 'custom', path: ['design_system', 'contributions', index, 'id'], message: `duplicate contribution id: ${contribution.id}` });
83
+ contributions.add(contribution.id);
84
+ if (new Set(contribution.replaces).size !== contribution.replaces.length) {
85
+ context.addIssue({ code: 'custom', path: ['design_system', 'contributions', index, 'replaces'], message: 'duplicate replacement target' });
86
+ }
87
+ }
88
+ });
89
+
90
+ export const DEFAULT_DESIGN_SYSTEM_LIMITS = Object.freeze({
91
+ manifestBytes: 256 * 1024,
92
+ itemBytes: 1024 * 1024,
93
+ totalBytes: 8 * 1024 * 1024,
94
+ maximumDepth: 8,
95
+ maximumFiles: 240,
96
+ });
97
+
98
+ function within(root, candidate) {
99
+ const path = relative(root, candidate);
100
+ return path === '' || (!path.startsWith('..') && !isAbsolute(path));
101
+ }
102
+
103
+ function rootRecords(roots) {
104
+ return roots.map((root, index) => typeof root === 'string'
105
+ ? { path: resolve(root), sourceClass: index === 0 ? 'bundled' : 'installed' }
106
+ : { path: resolve(root.path), sourceClass: String(root.sourceClass ?? (index === 0 ? 'bundled' : 'installed')) });
107
+ }
108
+
109
+ function packFiles(root, limits, depth = 0, usage = { files: 0 }) {
110
+ if (!existsSync(root) || depth > limits.maximumDepth) return [];
111
+ const found = [];
112
+ for (const entry of readdirSync(root, { withFileTypes: true }).sort((left, right) => left.name.localeCompare(right.name))) {
113
+ if (entry.isSymbolicLink()) continue;
114
+ const path = resolve(root, entry.name);
115
+ if (entry.isDirectory()) found.push(...packFiles(path, limits, depth + 1, usage));
116
+ if (entry.isFile() && entry.name === 'pack.yaml') {
117
+ usage.files += 1;
118
+ if (usage.files > limits.maximumFiles) throw new Error('Design-system discovery exceeds its file limit');
119
+ found.push(path);
120
+ }
121
+ }
122
+ return found;
123
+ }
124
+
125
+ function readContent(packRoot, source, limits, usage) {
126
+ const candidate = resolve(packRoot, source);
127
+ if (!within(packRoot, candidate)) throw new Error(`Design-system content may not escape its pack root: ${source}`);
128
+ if (!existsSync(candidate)) throw new Error(`Design-system content source does not exist: ${source}`);
129
+ if (lstatSync(candidate).isSymbolicLink()) throw new Error(`Design-system content may not be a symbolic link: ${source}`);
130
+ const realRoot = realpathSync(packRoot);
131
+ const realCandidate = realpathSync(candidate);
132
+ if (!within(realRoot, realCandidate)) throw new Error(`Design-system content may not escape its pack root: ${source}`);
133
+ const metadata = statSync(realCandidate);
134
+ if (!metadata.isFile()) throw new Error(`Design-system content source must be a file: ${source}`);
135
+ if (metadata.size > limits.itemBytes) throw new Error(`Design-system content exceeds its size limit: ${source}`);
136
+ usage.totalBytes += metadata.size;
137
+ usage.files += 1;
138
+ if (usage.files > limits.maximumFiles) throw new Error('Design-system referenced content exceeds its file limit');
139
+ if (usage.totalBytes > limits.totalBytes) throw new Error('Design-system referenced content exceeds its total size limit');
140
+ const bytes = readFileSync(realCandidate);
141
+ return {
142
+ source,
143
+ bytes,
144
+ content: bytes.toString('utf8'),
145
+ digest: `sha256:${createHash('sha256').update(bytes).digest('hex')}`,
146
+ };
147
+ }
148
+
149
+ export function parseDesignSystemPack(manifestPath, sourceClass = 'installed', options = {}) {
150
+ const limits = { ...DEFAULT_DESIGN_SYSTEM_LIMITS, ...(options.limits ?? {}) };
151
+ if (lstatSync(manifestPath).isSymbolicLink()) throw new Error(`Design-system manifest may not be a symbolic link: ${manifestPath}`);
152
+ const metadata = statSync(manifestPath);
153
+ if (metadata.size > limits.manifestBytes) throw new Error(`Design-system manifest exceeds its size limit: ${manifestPath}`);
154
+ const manifestBytes = readFileSync(manifestPath);
155
+ let parsed;
156
+ try {
157
+ parsed = YAML.parse(manifestBytes.toString('utf8'));
158
+ } catch (error) {
159
+ throw new Error(`Design-system manifest is invalid YAML: ${error.message}`);
160
+ }
161
+ if (parsed?.type !== 'design-system') return null;
162
+ let pack;
163
+ try {
164
+ pack = designSystemPackSchema.parse(parsed);
165
+ } catch (error) {
166
+ const details = error.issues?.map((issue) => `${issue.path.join('.') || 'manifest'} ${issue.message}`).join('; ') ?? error.message;
167
+ throw new Error(`Design-system manifest is invalid: ${details}`);
168
+ }
169
+ const packRoot = dirname(manifestPath);
170
+ const usage = { totalBytes: 0, files: 0 };
171
+ const files = new Map();
172
+ const contributions = pack.design_system.contributions.map((contribution) => {
173
+ if (!files.has(contribution.source)) files.set(contribution.source, readContent(packRoot, contribution.source, limits, usage));
174
+ const file = files.get(contribution.source);
175
+ return { ...contribution, content: file.content, contentDigest: file.digest };
176
+ });
177
+ const manifestDigest = `sha256:${createHash('sha256').update(manifestBytes).digest('hex')}`;
178
+ const hash = createHash('sha256').update(manifestBytes).update('\0');
179
+ for (const file of [...files.values()].sort((left, right) => left.source.localeCompare(right.source))) {
180
+ hash.update(file.source).update('\0').update(file.bytes).update('\0');
181
+ }
182
+ const requiredMajor = Number(pack.design_system.compatibility.ewai.split('.')[0]);
183
+ return {
184
+ ...pack,
185
+ design_system: { ...pack.design_system, contributions },
186
+ manifestPath,
187
+ packRoot,
188
+ sourceClass,
189
+ manifestDigest,
190
+ digest: `sha256:${hash.digest('hex')}`,
191
+ compatible: requiredMajor === currentEwaiMajor,
192
+ compatibilityReason: requiredMajor === currentEwaiMajor
193
+ ? `Compatible with EWAI ${currentEwaiMajor}.x`
194
+ : `Requires EWAI ${requiredMajor}.x; this runtime is ${packageVersion}`,
195
+ };
196
+ }
197
+
198
+ export function defaultDesignSystemRoots(projectRoot, options = {}) {
199
+ const home = resolve(options.home ?? homedir());
200
+ const roots = [
201
+ { path: defaultPackRoot, sourceClass: 'bundled' },
202
+ { path: resolve(home, '.ewai/packs'), sourceClass: 'personal' },
203
+ ];
204
+ if (projectRoot) roots.push({ path: resolve(projectRoot, '.ewai-pipeline/packs'), sourceClass: 'project' });
205
+ return roots;
206
+ }
207
+
208
+ export function listDesignSystems(options = {}) {
209
+ const limits = { ...DEFAULT_DESIGN_SYSTEM_LIMITS, ...(options.limits ?? {}) };
210
+ const roots = rootRecords(options.roots ?? defaultDesignSystemRoots(options.projectRoot, options));
211
+ const catalogue = [];
212
+ for (const root of roots) {
213
+ for (const manifestPath of packFiles(root.path, limits).sort()) {
214
+ const pack = parseDesignSystemPack(manifestPath, root.sourceClass, { limits });
215
+ if (pack) catalogue.push(pack);
216
+ }
217
+ }
218
+ const byId = new Map();
219
+ for (const pack of catalogue) {
220
+ if (byId.has(pack.id)) throw new Error(`Duplicate design-system id across installed roots: ${pack.id}`);
221
+ byId.set(pack.id, pack);
222
+ }
223
+ return catalogue.sort((left, right) => left.id.localeCompare(right.id) || left.version.localeCompare(right.version));
224
+ }
225
+
226
+ function safeContribution(contribution, pack) {
227
+ return {
228
+ id: contribution.id,
229
+ qualifiedId: `${pack.id}:${contribution.id}`,
230
+ kind: contribution.kind,
231
+ title: contribution.title,
232
+ source: contribution.source,
233
+ applicability: [...contribution.applicability],
234
+ required: contribution.required,
235
+ replaces: [...contribution.replaces],
236
+ contentDigest: contribution.contentDigest,
237
+ packId: pack.id,
238
+ packVersion: pack.version,
239
+ sourceClass: pack.sourceClass,
240
+ };
241
+ }
242
+
243
+ export function projectDesignSystem(pack) {
244
+ return {
245
+ id: pack.id,
246
+ name: pack.name,
247
+ description: pack.description,
248
+ version: pack.version,
249
+ type: pack.type,
250
+ requires: [...pack.requires],
251
+ sourceClass: pack.sourceClass,
252
+ manifestDigest: pack.manifestDigest,
253
+ digest: pack.digest,
254
+ compatible: pack.compatible,
255
+ compatibilityReason: pack.compatibilityReason,
256
+ provenance: { ...pack.design_system.provenance },
257
+ compatibility: { ...pack.design_system.compatibility },
258
+ contributions: pack.design_system.contributions.map((contribution) => safeContribution(contribution, pack)),
259
+ };
260
+ }
261
+
262
+ export function resolveDesignSystem(id, catalogue) {
263
+ const byId = new Map(catalogue.map((pack) => [pack.id, pack]));
264
+ const root = byId.get(id);
265
+ if (!root) throw new Error(`Unknown design system: ${id}`);
266
+ const packs = [];
267
+ const visited = new Set();
268
+ const visiting = [];
269
+ const visit = (candidateId) => {
270
+ if (visiting.includes(candidateId)) throw new Error(`Design-system dependency cycle: ${[...visiting, candidateId].join(' -> ')}`);
271
+ if (visited.has(candidateId)) return;
272
+ const pack = byId.get(candidateId);
273
+ if (!pack) throw new Error(`Design system has a missing dependency: ${candidateId}`);
274
+ if (!pack.compatible) throw new Error(`Design system ${candidateId} is incompatible: ${pack.compatibilityReason}`);
275
+ visiting.push(candidateId);
276
+ for (const dependency of [...pack.requires].sort()) visit(dependency);
277
+ visiting.pop();
278
+ visited.add(candidateId);
279
+ packs.push(pack);
280
+ };
281
+ visit(id);
282
+
283
+ const activeById = new Map();
284
+ const activeByQualified = new Map();
285
+ for (const pack of packs) {
286
+ for (const contribution of pack.design_system.contributions) {
287
+ const qualifiedId = `${pack.id}:${contribution.id}`;
288
+ for (const replaced of contribution.replaces) {
289
+ const existing = activeByQualified.get(replaced);
290
+ if (!existing) throw new Error(`Design-system replacement target does not exist: ${replaced}`);
291
+ activeByQualified.delete(replaced);
292
+ activeById.delete(existing.contribution.id);
293
+ }
294
+ if (activeById.has(contribution.id)) {
295
+ throw new Error(`Ambiguous contribution ${contribution.id} in resolved design system; declare a qualified replaces target`);
296
+ }
297
+ const record = { pack, contribution, qualifiedId };
298
+ activeById.set(contribution.id, record);
299
+ activeByQualified.set(qualifiedId, record);
300
+ }
301
+ }
302
+ const contributions = [...activeByQualified.values()].map(({ pack, contribution, qualifiedId }) => ({
303
+ ...contribution,
304
+ qualifiedId,
305
+ packId: pack.id,
306
+ packVersion: pack.version,
307
+ sourceClass: pack.sourceClass,
308
+ }));
309
+ const effectiveDigest = `sha256:${createHash('sha256').update(JSON.stringify({
310
+ root: root.id,
311
+ packs: packs.map((pack) => ({ id: pack.id, version: pack.version, digest: pack.digest })),
312
+ contributions: contributions.map(({ qualifiedId, contentDigest }) => ({ qualifiedId, contentDigest })),
313
+ })).digest('hex')}`;
314
+ return {
315
+ schema: 'ewai.resolved-design-system/v1',
316
+ root,
317
+ packs,
318
+ contributions,
319
+ effectiveDigest,
320
+ };
321
+ }
322
+
323
+ export function projectResolvedDesignSystem(resolved) {
324
+ return {
325
+ schema: resolved.schema,
326
+ root: projectDesignSystem(resolved.root),
327
+ packs: resolved.packs.map(projectDesignSystem),
328
+ contributions: resolved.contributions.map((contribution) => safeContribution(contribution, resolved.packs.find(({ id }) => id === contribution.packId))),
329
+ effectiveDigest: resolved.effectiveDigest,
330
+ };
331
+ }
332
+
333
+ function pin(pack) {
334
+ return { id: pack.id, version: pack.version, digest: pack.digest, source_class: pack.sourceClass };
335
+ }
336
+
337
+ function selectionStatus(resolved, selection = null, recommendationState = { status: 'none', recommendations: [] }) {
338
+ const selected = Boolean(selection);
339
+ return {
340
+ schema: 'ewai.design-system-status/v1',
341
+ status: 'ready',
342
+ mode: selected ? 'selected' : 'fallback',
343
+ approved: selected,
344
+ root: projectDesignSystem(resolved.root),
345
+ packs: resolved.packs.map(projectDesignSystem),
346
+ effectiveDigest: resolved.effectiveDigest,
347
+ approvedBy: selection?.approved_by ?? null,
348
+ approvedAt: selection?.approved_at ?? null,
349
+ evidence: selection?.evidence ?? null,
350
+ organisationRecommendations: recommendationState.recommendations,
351
+ organisationRecommendationStatus: recommendationState.status,
352
+ notice: selected
353
+ ? 'This project design system is explicitly selected and digest-pinned.'
354
+ : 'The bundled design system is an unapproved fallback until an accountable owner selects a project root.',
355
+ };
356
+ }
357
+
358
+ export function designSystemStatus(projectRoot, options = {}) {
359
+ const { config } = loadProjectConfig(projectRoot);
360
+ const catalogue = listDesignSystems({ ...options, projectRoot });
361
+ const selection = config.design_system ?? null;
362
+ const resolved = resolveDesignSystem(selection?.root?.id ?? DEFAULT_DESIGN_SYSTEM_ID, catalogue);
363
+ let recommendationState = { status: 'none', recommendations: [] };
364
+ const blueprint = config.blueprints?.organisation;
365
+ if (blueprint?.root?.id) {
366
+ try {
367
+ const organisationCatalogue = listOrganisationBlueprints({
368
+ projectRoot,
369
+ ...(options.organisationRoots ? { roots: options.organisationRoots } : {}),
370
+ ...(options.home ? { home: options.home } : {}),
371
+ });
372
+ const organisationResolved = resolveOrganisationBlueprint(blueprint.root.id, organisationCatalogue, {
373
+ enabledModules: blueprint.enabled_modules ?? [],
374
+ });
375
+ recommendationState = {
376
+ status: 'available',
377
+ recommendations: organisationDesignSystemRecommendations(organisationResolved),
378
+ };
379
+ } catch {
380
+ recommendationState = { status: 'unavailable', recommendations: [] };
381
+ }
382
+ }
383
+ if (selection) {
384
+ const pinned = JSON.stringify(selection.packs.map(({ id, version, digest, source_class }) => ({ id, version, digest, source_class })));
385
+ const current = JSON.stringify(resolved.packs.map(pin));
386
+ if (selection.effective_digest !== resolved.effectiveDigest || pinned !== current) {
387
+ return {
388
+ ...selectionStatus(resolved, selection, recommendationState),
389
+ status: 'stale',
390
+ approved: false,
391
+ notice: 'The installed design-system content has changed since approval. Re-resolve and obtain a new named selection approval.',
392
+ };
393
+ }
394
+ }
395
+ return selectionStatus(resolved, selection, recommendationState);
396
+ }
397
+
398
+ function strategyMarkdown(resolved, approvedBy, approvedAt) {
399
+ const rows = resolved.packs.map((pack) => `| \`${pack.id}\` | \`${pack.version}\` | ${pack.sourceClass} | \`${pack.digest}\` |`).join('\n');
400
+ const contributions = resolved.contributions.map(({ qualifiedId, kind, required }) => `- \`${qualifiedId}\` — ${kind}${required ? ' (required)' : ''}`).join('\n');
401
+ return `# Project Design System\n\n` +
402
+ `**Root:** \`${resolved.root.id}\`\n` +
403
+ `**Effective digest:** \`${resolved.effectiveDigest}\`\n` +
404
+ `**Approved by:** ${approvedBy}\n` +
405
+ `**Approved at:** ${approvedAt}\n\n` +
406
+ `## Resolved packs\n\n| Pack | Version | Source | Digest |\n|---|---|---|---|\n${rows}\n\n` +
407
+ `## Effective contributions\n\n${contributions}\n\n` +
408
+ `This records project selection authority. Prototype application emits a separate delivery-local receipt.\n`;
409
+ }
410
+
411
+ function atomicSelectionWrite(configPath, configContent, evidencePath, evidenceContent) {
412
+ mkdirSync(dirname(evidencePath), { recursive: true });
413
+ const suffix = `.tmp-${process.pid}-${randomUUID()}`;
414
+ const configTemp = `${configPath}${suffix}`;
415
+ const evidenceTemp = `${evidencePath}${suffix}`;
416
+ const priorConfig = readFileSync(configPath, 'utf8');
417
+ const hadEvidence = existsSync(evidencePath);
418
+ const priorEvidence = hadEvidence ? readFileSync(evidencePath, 'utf8') : null;
419
+ try {
420
+ writeFileSync(configTemp, configContent, { encoding: 'utf8', flag: 'wx' });
421
+ writeFileSync(evidenceTemp, evidenceContent, { encoding: 'utf8', flag: 'wx' });
422
+ renameSync(evidenceTemp, evidencePath);
423
+ try {
424
+ renameSync(configTemp, configPath);
425
+ } catch (error) {
426
+ if (hadEvidence) writeFileSync(evidencePath, priorEvidence, 'utf8');
427
+ else if (existsSync(evidencePath)) unlinkSync(evidencePath);
428
+ throw error;
429
+ }
430
+ } catch (error) {
431
+ if (existsSync(configTemp)) unlinkSync(configTemp);
432
+ if (existsSync(evidenceTemp)) unlinkSync(evidenceTemp);
433
+ if (readFileSync(configPath, 'utf8') !== priorConfig) writeFileSync(configPath, priorConfig, 'utf8');
434
+ throw error;
435
+ }
436
+ }
437
+
438
+ export function selectDesignSystem(projectRoot, id, options = {}) {
439
+ const approvedBy = String(options.approvedBy ?? '').trim();
440
+ if (!approvedBy) throw new Error('Design-system selection requires a named approver');
441
+ const expectedDigest = String(options.expectedDigest ?? '').trim();
442
+ if (!/^sha256:[a-f0-9]{64}$/.test(expectedDigest)) throw new Error('Design-system selection requires an exact expected digest');
443
+ const approvedAt = options.now ?? new Date().toISOString();
444
+ if (Number.isNaN(Date.parse(approvedAt))) throw new Error('Design-system approval time must be an ISO date-time');
445
+ const { config, paths } = loadProjectConfig(projectRoot);
446
+ const resolved = resolveDesignSystem(id, listDesignSystems({ ...options, projectRoot }));
447
+ if (resolved.effectiveDigest !== expectedDigest) {
448
+ throw new Error(`Design-system digest drift: expected ${expectedDigest}, resolved ${resolved.effectiveDigest}`);
449
+ }
450
+ config.design_system = {
451
+ root: pin(resolved.root),
452
+ packs: resolved.packs.map(pin),
453
+ effective_digest: resolved.effectiveDigest,
454
+ approved_by: approvedBy,
455
+ approved_at: approvedAt,
456
+ evidence: DESIGN_SYSTEM_EVIDENCE_PATH,
457
+ };
458
+ const evidencePath = resolve(paths.projectRoot, DESIGN_SYSTEM_EVIDENCE_PATH);
459
+ atomicSelectionWrite(
460
+ paths.configPath,
461
+ YAML.stringify(config, { lineWidth: 0 }),
462
+ evidencePath,
463
+ strategyMarkdown(resolved, approvedBy, approvedAt),
464
+ );
465
+ return selectionStatus(resolved, config.design_system);
466
+ }