@hybridlabor-api/aos 4.6.5 → 4.7.0

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 (486) hide show
  1. package/.agents/agents.md +1 -0
  2. package/.agents/nodes.json +5 -1
  3. package/.agents/state.schema.json +2 -1
  4. package/.agents/vendor-manifest.json +50 -0
  5. package/.claude/agents/architect.md +4 -0
  6. package/.claude/agents/security-reviewer.md +1 -1
  7. package/.claude/hooks/memb-inject.mjs +257 -135
  8. package/.claude/hooks/trail-relay.mjs +18 -0
  9. package/.claude/settings.json +70 -0
  10. package/.claude/workflows/startcycle-dispatch.mjs +29 -1
  11. package/.opencode/agents/architect.md +4 -0
  12. package/.opencode/plugins/bdb-aos.js +75 -0
  13. package/THIRD_PARTY_NOTICES.md +48 -0
  14. package/bin/aos-config.mjs +3 -1
  15. package/bin/aos-uninstall.mjs +55 -1
  16. package/docs/handover/agenttrail-for-ao.md +58 -0
  17. package/docs/handover/deja-for-ao.md +51 -0
  18. package/installer.js +257 -56
  19. package/mcp_config.json +4 -0
  20. package/mcps/mcsc/README.md +24 -0
  21. package/mcps/mcsc/package-lock.json +1234 -0
  22. package/mcps/mcsc/package.json +14 -0
  23. package/mcps/mcsc/packages/core/bin/mcsc.js +162 -0
  24. package/mcps/mcsc/packages/core/rulebook.default.yaml +49 -0
  25. package/mcps/mcsc/packages/core/schema/rulebook.schema.json +56 -0
  26. package/mcps/mcsc/packages/core/src/adapters/agy.js +107 -0
  27. package/mcps/mcsc/packages/core/src/adapters/codex.js +56 -0
  28. package/mcps/mcsc/packages/core/src/adapters/native.js +14 -0
  29. package/mcps/mcsc/packages/core/src/adapters/opencode.js +183 -0
  30. package/mcps/mcsc/packages/core/src/adapters/types.js +21 -0
  31. package/mcps/mcsc/packages/core/src/capabilities/buildOfferTable.js +34 -0
  32. package/mcps/mcsc/packages/core/src/capabilities/registry.js +99 -0
  33. package/mcps/mcsc/packages/core/src/capabilities/types.js +24 -0
  34. package/mcps/mcsc/packages/core/src/detect/cache.js +47 -0
  35. package/mcps/mcsc/packages/core/src/detect/inventory.js +73 -0
  36. package/mcps/mcsc/packages/core/src/detect/probes.js +158 -0
  37. package/mcps/mcsc/packages/core/src/detect/types.js +26 -0
  38. package/mcps/mcsc/packages/core/src/rulebook/load.js +53 -0
  39. package/mcps/mcsc/packages/core/src/rulebook/resolve.js +227 -0
  40. package/mcps/mcsc/packages/core/src/rulebook/rulebook.types.js +89 -0
  41. package/mcps/mcsc/packages/core/src/trail.js +17 -0
  42. package/mcps/mcsc/packages/mcp/server.js +302 -0
  43. package/mcps/mcsc/server.js +2 -0
  44. package/package.json +3 -1
  45. package/skills/basic/bdbmediastorm/SKILL.md +1 -1
  46. package/skills/basic/godmode-ui-ux/SKILL.md +2 -2
  47. package/skills/basic/godmode-ui-ux/references/anti-slop-rules.md +44 -40
  48. package/skills/basic/godmode-ui-ux/references/taste-archetypes.md +49 -0
  49. package/skills/basic/startcycle/SKILL.md +7 -0
  50. package/skills/basic/startcycle-graph/SKILL.md +12 -0
  51. package/skills/basic/startcycle-graph-user/SKILL.md +3 -0
  52. package/skills/basic/teamwork-preview/SKILL.md +2 -0
  53. package/skills/bdbrainstorm/SKILL.md +1 -0
  54. package/skills/global_config/agenttrail/LICENSE +21 -0
  55. package/skills/global_config/agenttrail/SKILL.md +48 -0
  56. package/skills/global_config/agenttrail/bin/agenttrail.mjs +1002 -0
  57. package/skills/global_config/agenttrail/public/index.html +661 -0
  58. package/skills/global_config/aos-project-init/SKILL.md +25 -5
  59. package/skills/global_config/aos-project-init/scripts/aos-project-doctor.mjs +11 -0
  60. package/skills/global_config/aos-setup/SKILL.md +34 -1
  61. package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +115 -1
  62. package/skills/global_config/archify/LICENSE +22 -0
  63. package/skills/global_config/archify/SKILL.md +140 -0
  64. package/skills/global_config/archify/THIRD_PARTY_NOTICES.md +69 -0
  65. package/skills/global_config/archify/assets/JetBrainsMono-OFL.txt +93 -0
  66. package/skills/global_config/archify/assets/template.html +14937 -0
  67. package/skills/global_config/archify/bin/archify.mjs +2139 -0
  68. package/skills/global_config/archify/bin/open-artifact.mjs +86 -0
  69. package/skills/global_config/archify/bin/preview.mjs +653 -0
  70. package/skills/global_config/archify/bin/visual-check.mjs +829 -0
  71. package/skills/global_config/archify/brand-marks/README.md +31 -0
  72. package/skills/global_config/archify/brand-marks/catalog.json +131 -0
  73. package/skills/global_config/archify/delta/architecture-delta.mjs +1232 -0
  74. package/skills/global_config/archify/examples/agent-run.lifecycle.json +60 -0
  75. package/skills/global_config/archify/examples/agent-tool-call.workflow.json +94 -0
  76. package/skills/global_config/archify/examples/async-job-roundtrip.sequence.json +61 -0
  77. package/skills/global_config/archify/examples/brand-aware-delivery.architecture.json +47 -0
  78. package/skills/global_config/archify/examples/cache-miss-request.sequence.json +82 -0
  79. package/skills/global_config/archify/examples/checkout-platform.base.architecture.json +31 -0
  80. package/skills/global_config/archify/examples/checkout-platform.head.architecture.json +31 -0
  81. package/skills/global_config/archify/examples/deployment-release.lifecycle.json +49 -0
  82. package/skills/global_config/archify/examples/event-stream.dataflow.json +57 -0
  83. package/skills/global_config/archify/examples/incident-response.workflow.json +64 -0
  84. package/skills/global_config/archify/examples/product-analytics.dataflow.json +76 -0
  85. package/skills/global_config/archify/examples/production-deployment.architecture.json +71 -0
  86. package/skills/global_config/archify/examples/release-delivery.workflow.json +62 -0
  87. package/skills/global_config/archify/examples/web-app.architecture.json +46 -0
  88. package/skills/global_config/archify/migrations/workflow-v2.mjs +279 -0
  89. package/skills/global_config/archify/package.json +39 -0
  90. package/skills/global_config/archify/recipes/scenarios.mjs +391 -0
  91. package/skills/global_config/archify/references/authoring-contract.md +247 -0
  92. package/skills/global_config/archify/references/brand-marks.md +65 -0
  93. package/skills/global_config/archify/references/delivery-contract.md +138 -0
  94. package/skills/global_config/archify/references/viewer-runtime.md +45 -0
  95. package/skills/global_config/archify/renderers/architecture/grid.mjs +62 -0
  96. package/skills/global_config/archify/renderers/architecture/render-architecture.mjs +1078 -0
  97. package/skills/global_config/archify/renderers/dataflow/README.md +104 -0
  98. package/skills/global_config/archify/renderers/dataflow/render-dataflow.mjs +483 -0
  99. package/skills/global_config/archify/renderers/lifecycle/README.md +115 -0
  100. package/skills/global_config/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
  101. package/skills/global_config/archify/renderers/sequence/README.md +114 -0
  102. package/skills/global_config/archify/renderers/sequence/render-sequence.mjs +464 -0
  103. package/skills/global_config/archify/renderers/shared/brand-marks.mjs +563 -0
  104. package/skills/global_config/archify/renderers/shared/cli.mjs +218 -0
  105. package/skills/global_config/archify/renderers/shared/desktop-readability.mjs +26 -0
  106. package/skills/global_config/archify/renderers/shared/diagnostics.mjs +160 -0
  107. package/skills/global_config/archify/renderers/shared/engineering-profiles.mjs +157 -0
  108. package/skills/global_config/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
  109. package/skills/global_config/archify/renderers/shared/generated-validators.mjs +13 -0
  110. package/skills/global_config/archify/renderers/shared/geometry.mjs +1423 -0
  111. package/skills/global_config/archify/renderers/shared/i18n.mjs +595 -0
  112. package/skills/global_config/archify/renderers/shared/layout-report.mjs +40 -0
  113. package/skills/global_config/archify/renderers/shared/legend.mjs +217 -0
  114. package/skills/global_config/archify/renderers/shared/output-path.mjs +340 -0
  115. package/skills/global_config/archify/renderers/shared/repository-evidence.mjs +239 -0
  116. package/skills/global_config/archify/renderers/shared/repository-location.mjs +58 -0
  117. package/skills/global_config/archify/renderers/shared/text-fit.mjs +49 -0
  118. package/skills/global_config/archify/renderers/shared/utils.mjs +232 -0
  119. package/skills/global_config/archify/renderers/shared/validator.mjs +86 -0
  120. package/skills/global_config/archify/renderers/workflow/README.md +223 -0
  121. package/skills/global_config/archify/renderers/workflow/render-workflow.mjs +35 -0
  122. package/skills/global_config/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
  123. package/skills/global_config/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
  124. package/skills/global_config/archify/schemas/README.md +211 -0
  125. package/skills/global_config/archify/schemas/architecture.schema.json +178 -0
  126. package/skills/global_config/archify/schemas/common.schema.json +115 -0
  127. package/skills/global_config/archify/schemas/dataflow.schema.json +243 -0
  128. package/skills/global_config/archify/schemas/lifecycle.schema.json +266 -0
  129. package/skills/global_config/archify/schemas/sequence.schema.json +223 -0
  130. package/skills/global_config/archify/schemas/workflow.schema.json +428 -0
  131. package/skills/global_config/archify/scripts/check-render-output.mjs +916 -0
  132. package/skills/global_config/archify/scripts/generate-brand-marks.mjs +141 -0
  133. package/skills/global_config/archify/scripts/generate-validators.mjs +66 -0
  134. package/skills/global_config/archify/scripts/render-examples.mjs +26 -0
  135. package/skills/global_config/archify/skill-release.json +10 -0
  136. package/skills/global_config/bdb-security-audit/SKILL.md +109 -0
  137. package/skills/global_config/bdb-security-audit/references/audit-checklists.md +172 -0
  138. package/skills/global_config/bdb-security-audit/references/skylos-guide.md +139 -0
  139. package/skills/global_config/bdb-security-audit/references/xalgorix-bridge.md +110 -0
  140. package/skills/global_config/bdbdesignpro/SKILL.md +4 -4
  141. package/skills/global_config/bdbdesignpro/references/bdb-motion-tokens.md +52 -31
  142. package/skills/global_config/bdbresilience/SKILL.md +10 -0
  143. package/skills/global_config/brag/SKILL.md +179 -0
  144. package/skills/global_config/brag/assets/music/README.md +20 -0
  145. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-1-by-ende-dot-app.music-cues.json +3846 -0
  146. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-1-by-ende-dot-app.music-cues.md +35 -0
  147. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-10-by-ende-dot-app.music-cues.json +1826 -0
  148. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-10-by-ende-dot-app.music-cues.md +35 -0
  149. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-11-by-ende-dot-app.music-cues.json +2266 -0
  150. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-11-by-ende-dot-app.music-cues.md +35 -0
  151. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-12-by-ende-dot-app.music-cues.json +2806 -0
  152. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-12-by-ende-dot-app.music-cues.md +35 -0
  153. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-9-by-ende-dot-app.music-cues.json +2796 -0
  154. package/skills/global_config/brag/assets/music/cues/happy-beats-business-moves-vol-9-by-ende-dot-app.music-cues.md +35 -0
  155. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-1-by-ende-dot-app.mp3 +0 -0
  156. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-10-by-ende-dot-app.mp3 +0 -0
  157. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-11-by-ende-dot-app.mp3 +0 -0
  158. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-12-by-ende-dot-app.mp3 +0 -0
  159. package/skills/global_config/brag/assets/music/happy-beats-business-moves-vol-9-by-ende-dot-app.mp3 +0 -0
  160. package/skills/global_config/brag/assets/sfx/casino/card-fan-1.ogg +0 -0
  161. package/skills/global_config/brag/assets/sfx/casino/card-fan-2.ogg +0 -0
  162. package/skills/global_config/brag/assets/sfx/casino/card-place-1.ogg +0 -0
  163. package/skills/global_config/brag/assets/sfx/casino/card-place-2.ogg +0 -0
  164. package/skills/global_config/brag/assets/sfx/casino/card-place-3.ogg +0 -0
  165. package/skills/global_config/brag/assets/sfx/casino/card-place-4.ogg +0 -0
  166. package/skills/global_config/brag/assets/sfx/casino/card-shove-1.ogg +0 -0
  167. package/skills/global_config/brag/assets/sfx/casino/card-shove-2.ogg +0 -0
  168. package/skills/global_config/brag/assets/sfx/casino/card-shove-3.ogg +0 -0
  169. package/skills/global_config/brag/assets/sfx/casino/card-shove-4.ogg +0 -0
  170. package/skills/global_config/brag/assets/sfx/casino/card-shuffle.ogg +0 -0
  171. package/skills/global_config/brag/assets/sfx/casino/card-slide-1.ogg +0 -0
  172. package/skills/global_config/brag/assets/sfx/casino/card-slide-2.ogg +0 -0
  173. package/skills/global_config/brag/assets/sfx/casino/card-slide-3.ogg +0 -0
  174. package/skills/global_config/brag/assets/sfx/casino/card-slide-4.ogg +0 -0
  175. package/skills/global_config/brag/assets/sfx/casino/card-slide-5.ogg +0 -0
  176. package/skills/global_config/brag/assets/sfx/casino/card-slide-6.ogg +0 -0
  177. package/skills/global_config/brag/assets/sfx/casino/card-slide-7.ogg +0 -0
  178. package/skills/global_config/brag/assets/sfx/casino/card-slide-8.ogg +0 -0
  179. package/skills/global_config/brag/assets/sfx/casino/cards-pack-open-1.ogg +0 -0
  180. package/skills/global_config/brag/assets/sfx/casino/cards-pack-open-2.ogg +0 -0
  181. package/skills/global_config/brag/assets/sfx/casino/chip-lay-1.ogg +0 -0
  182. package/skills/global_config/brag/assets/sfx/casino/chip-lay-2.ogg +0 -0
  183. package/skills/global_config/brag/assets/sfx/casino/chip-lay-3.ogg +0 -0
  184. package/skills/global_config/brag/assets/sfx/casino/chips-collide-1.ogg +0 -0
  185. package/skills/global_config/brag/assets/sfx/casino/chips-collide-2.ogg +0 -0
  186. package/skills/global_config/brag/assets/sfx/casino/chips-collide-3.ogg +0 -0
  187. package/skills/global_config/brag/assets/sfx/casino/chips-collide-4.ogg +0 -0
  188. package/skills/global_config/brag/assets/sfx/casino/chips-handle-1.ogg +0 -0
  189. package/skills/global_config/brag/assets/sfx/casino/chips-handle-2.ogg +0 -0
  190. package/skills/global_config/brag/assets/sfx/casino/chips-handle-3.ogg +0 -0
  191. package/skills/global_config/brag/assets/sfx/casino/chips-handle-4.ogg +0 -0
  192. package/skills/global_config/brag/assets/sfx/casino/chips-handle-6.ogg +0 -0
  193. package/skills/global_config/brag/assets/sfx/casino/chips-stack-1.ogg +0 -0
  194. package/skills/global_config/brag/assets/sfx/casino/chips-stack-2.ogg +0 -0
  195. package/skills/global_config/brag/assets/sfx/casino/chips-stack-3.ogg +0 -0
  196. package/skills/global_config/brag/assets/sfx/casino/chips-stack-4.ogg +0 -0
  197. package/skills/global_config/brag/assets/sfx/casino/chips-stack-5.ogg +0 -0
  198. package/skills/global_config/brag/assets/sfx/casino/chips-stack-6.ogg +0 -0
  199. package/skills/global_config/brag/assets/sfx/casino/dice-grab-1.ogg +0 -0
  200. package/skills/global_config/brag/assets/sfx/casino/dice-grab-2.ogg +0 -0
  201. package/skills/global_config/brag/assets/sfx/casino/dice-shake-1.ogg +0 -0
  202. package/skills/global_config/brag/assets/sfx/casino/dice-shake-2.ogg +0 -0
  203. package/skills/global_config/brag/assets/sfx/casino/dice-shake-3.ogg +0 -0
  204. package/skills/global_config/brag/assets/sfx/casino/dice-throw-1.ogg +0 -0
  205. package/skills/global_config/brag/assets/sfx/casino/dice-throw-2.ogg +0 -0
  206. package/skills/global_config/brag/assets/sfx/casino/dice-throw-3.ogg +0 -0
  207. package/skills/global_config/brag/assets/sfx/casino/die-throw-1.ogg +0 -0
  208. package/skills/global_config/brag/assets/sfx/casino/die-throw-2.ogg +0 -0
  209. package/skills/global_config/brag/assets/sfx/casino/die-throw-3.ogg +0 -0
  210. package/skills/global_config/brag/assets/sfx/casino/die-throw-4.ogg +0 -0
  211. package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_000.ogg +0 -0
  212. package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_003.ogg +0 -0
  213. package/skills/global_config/brag/assets/sfx/impact/footstep_carpet_004.ogg +0 -0
  214. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_000.ogg +0 -0
  215. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_001.ogg +0 -0
  216. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_002.ogg +0 -0
  217. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_003.ogg +0 -0
  218. package/skills/global_config/brag/assets/sfx/impact/footstep_concrete_004.ogg +0 -0
  219. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_000.ogg +0 -0
  220. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_001.ogg +0 -0
  221. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_002.ogg +0 -0
  222. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_003.ogg +0 -0
  223. package/skills/global_config/brag/assets/sfx/impact/footstep_grass_004.ogg +0 -0
  224. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_000.ogg +0 -0
  225. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_001.ogg +0 -0
  226. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_002.ogg +0 -0
  227. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_003.ogg +0 -0
  228. package/skills/global_config/brag/assets/sfx/impact/footstep_snow_004.ogg +0 -0
  229. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_000.ogg +0 -0
  230. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_001.ogg +0 -0
  231. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_002.ogg +0 -0
  232. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_003.ogg +0 -0
  233. package/skills/global_config/brag/assets/sfx/impact/footstep_wood_004.ogg +0 -0
  234. package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_000.ogg +0 -0
  235. package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_003.ogg +0 -0
  236. package/skills/global_config/brag/assets/sfx/impact/impactBell_heavy_004.ogg +0 -0
  237. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_000.ogg +0 -0
  238. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_001.ogg +0 -0
  239. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_002.ogg +0 -0
  240. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_003.ogg +0 -0
  241. package/skills/global_config/brag/assets/sfx/impact/impactGeneric_light_004.ogg +0 -0
  242. package/skills/global_config/brag/assets/sfx/impact/impactGlass_heavy_002.ogg +0 -0
  243. package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_001.ogg +0 -0
  244. package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_002.ogg +0 -0
  245. package/skills/global_config/brag/assets/sfx/impact/impactGlass_light_003.ogg +0 -0
  246. package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_000.ogg +0 -0
  247. package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_002.ogg +0 -0
  248. package/skills/global_config/brag/assets/sfx/impact/impactGlass_medium_004.ogg +0 -0
  249. package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_000.ogg +0 -0
  250. package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_002.ogg +0 -0
  251. package/skills/global_config/brag/assets/sfx/impact/impactMetal_heavy_004.ogg +0 -0
  252. package/skills/global_config/brag/assets/sfx/impact/impactMetal_light_002.ogg +0 -0
  253. package/skills/global_config/brag/assets/sfx/impact/impactMetal_light_003.ogg +0 -0
  254. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_000.ogg +0 -0
  255. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_001.ogg +0 -0
  256. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_002.ogg +0 -0
  257. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_003.ogg +0 -0
  258. package/skills/global_config/brag/assets/sfx/impact/impactMetal_medium_004.ogg +0 -0
  259. package/skills/global_config/brag/assets/sfx/impact/impactMining_001.ogg +0 -0
  260. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_000.ogg +0 -0
  261. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_001.ogg +0 -0
  262. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_002.ogg +0 -0
  263. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_003.ogg +0 -0
  264. package/skills/global_config/brag/assets/sfx/impact/impactPlank_medium_004.ogg +0 -0
  265. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_000.ogg +0 -0
  266. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_001.ogg +0 -0
  267. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_002.ogg +0 -0
  268. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_003.ogg +0 -0
  269. package/skills/global_config/brag/assets/sfx/impact/impactPlate_heavy_004.ogg +0 -0
  270. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_000.ogg +0 -0
  271. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_001.ogg +0 -0
  272. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_002.ogg +0 -0
  273. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_003.ogg +0 -0
  274. package/skills/global_config/brag/assets/sfx/impact/impactPlate_light_004.ogg +0 -0
  275. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_000.ogg +0 -0
  276. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_001.ogg +0 -0
  277. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_002.ogg +0 -0
  278. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_003.ogg +0 -0
  279. package/skills/global_config/brag/assets/sfx/impact/impactPlate_medium_004.ogg +0 -0
  280. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_000.ogg +0 -0
  281. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_001.ogg +0 -0
  282. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_002.ogg +0 -0
  283. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_003.ogg +0 -0
  284. package/skills/global_config/brag/assets/sfx/impact/impactPunch_heavy_004.ogg +0 -0
  285. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_000.ogg +0 -0
  286. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_001.ogg +0 -0
  287. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_002.ogg +0 -0
  288. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_003.ogg +0 -0
  289. package/skills/global_config/brag/assets/sfx/impact/impactPunch_medium_004.ogg +0 -0
  290. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_000.ogg +0 -0
  291. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_001.ogg +0 -0
  292. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_002.ogg +0 -0
  293. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_003.ogg +0 -0
  294. package/skills/global_config/brag/assets/sfx/impact/impactSoft_heavy_004.ogg +0 -0
  295. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_000.ogg +0 -0
  296. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_001.ogg +0 -0
  297. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_002.ogg +0 -0
  298. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_003.ogg +0 -0
  299. package/skills/global_config/brag/assets/sfx/impact/impactSoft_medium_004.ogg +0 -0
  300. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_000.ogg +0 -0
  301. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_001.ogg +0 -0
  302. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_002.ogg +0 -0
  303. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_003.ogg +0 -0
  304. package/skills/global_config/brag/assets/sfx/impact/impactTin_medium_004.ogg +0 -0
  305. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_000.ogg +0 -0
  306. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_001.ogg +0 -0
  307. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_002.ogg +0 -0
  308. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_003.ogg +0 -0
  309. package/skills/global_config/brag/assets/sfx/impact/impactWood_heavy_004.ogg +0 -0
  310. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_000.ogg +0 -0
  311. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_001.ogg +0 -0
  312. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_002.ogg +0 -0
  313. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_003.ogg +0 -0
  314. package/skills/global_config/brag/assets/sfx/impact/impactWood_light_004.ogg +0 -0
  315. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_000.ogg +0 -0
  316. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_001.ogg +0 -0
  317. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_002.ogg +0 -0
  318. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_003.ogg +0 -0
  319. package/skills/global_config/brag/assets/sfx/impact/impactWood_medium_004.ogg +0 -0
  320. package/skills/global_config/brag/assets/sfx/interface/bong_001.ogg +0 -0
  321. package/skills/global_config/brag/assets/sfx/interface/click_001.ogg +0 -0
  322. package/skills/global_config/brag/assets/sfx/interface/click_002.ogg +0 -0
  323. package/skills/global_config/brag/assets/sfx/interface/click_003.ogg +0 -0
  324. package/skills/global_config/brag/assets/sfx/interface/click_004.ogg +0 -0
  325. package/skills/global_config/brag/assets/sfx/interface/click_005.ogg +0 -0
  326. package/skills/global_config/brag/assets/sfx/interface/drop_001.ogg +0 -0
  327. package/skills/global_config/brag/assets/sfx/interface/drop_002.ogg +0 -0
  328. package/skills/global_config/brag/assets/sfx/interface/drop_003.ogg +0 -0
  329. package/skills/global_config/brag/assets/sfx/interface/error_005.ogg +0 -0
  330. package/skills/global_config/brag/assets/sfx/interface/error_006.ogg +0 -0
  331. package/skills/global_config/brag/assets/sfx/interface/glitch_002.ogg +0 -0
  332. package/skills/global_config/brag/assets/sfx/interface/glitch_004.ogg +0 -0
  333. package/skills/global_config/brag/assets/sfx/interface/select_008.ogg +0 -0
  334. package/skills/global_config/brag/assets/sfx/interface/switch_001.ogg +0 -0
  335. package/skills/global_config/brag/assets/sfx/interface/switch_002.ogg +0 -0
  336. package/skills/global_config/brag/assets/sfx/interface/switch_004.ogg +0 -0
  337. package/skills/global_config/brag/assets/sfx/interface/switch_005.ogg +0 -0
  338. package/skills/global_config/brag/assets/sfx/interface/switch_006.ogg +0 -0
  339. package/skills/global_config/brag/assets/sfx/interface/switch_007.ogg +0 -0
  340. package/skills/global_config/brag/assets/sfx/keyboard/keypress-001.wav +0 -0
  341. package/skills/global_config/brag/assets/sfx/keyboard/keypress-002.wav +0 -0
  342. package/skills/global_config/brag/assets/sfx/keyboard/keypress-003.wav +0 -0
  343. package/skills/global_config/brag/assets/sfx/keyboard/keypress-004.wav +0 -0
  344. package/skills/global_config/brag/assets/sfx/keyboard/keypress-005.wav +0 -0
  345. package/skills/global_config/brag/assets/sfx/keyboard/keypress-006.wav +0 -0
  346. package/skills/global_config/brag/assets/sfx/keyboard/keypress-007.wav +0 -0
  347. package/skills/global_config/brag/assets/sfx/keyboard/keypress-008.wav +0 -0
  348. package/skills/global_config/brag/assets/sfx/keyboard/keypress-009.wav +0 -0
  349. package/skills/global_config/brag/assets/sfx/keyboard/keypress-010.wav +0 -0
  350. package/skills/global_config/brag/assets/sfx/keyboard/keypress-011.wav +0 -0
  351. package/skills/global_config/brag/assets/sfx/keyboard/keypress-012.wav +0 -0
  352. package/skills/global_config/brag/assets/sfx/keyboard/keypress-013.wav +0 -0
  353. package/skills/global_config/brag/assets/sfx/keyboard/keypress-014.wav +0 -0
  354. package/skills/global_config/brag/assets/sfx/keyboard/keypress-015.wav +0 -0
  355. package/skills/global_config/brag/assets/sfx/keyboard/keypress-016.wav +0 -0
  356. package/skills/global_config/brag/assets/sfx/keyboard/keypress-017.wav +0 -0
  357. package/skills/global_config/brag/assets/sfx/keyboard/keypress-018.wav +0 -0
  358. package/skills/global_config/brag/assets/sfx/keyboard/keypress-019.wav +0 -0
  359. package/skills/global_config/brag/assets/sfx/keyboard/keypress-020.wav +0 -0
  360. package/skills/global_config/brag/assets/sfx/keyboard/keypress-021.wav +0 -0
  361. package/skills/global_config/brag/assets/sfx/keyboard/keypress-022.wav +0 -0
  362. package/skills/global_config/brag/assets/sfx/keyboard/keypress-023.wav +0 -0
  363. package/skills/global_config/brag/assets/sfx/keyboard/keypress-024.wav +0 -0
  364. package/skills/global_config/brag/assets/sfx/keyboard/keypress-025.wav +0 -0
  365. package/skills/global_config/brag/assets/sfx/keyboard/keypress-026.wav +0 -0
  366. package/skills/global_config/brag/assets/sfx/keyboard/keypress-027.wav +0 -0
  367. package/skills/global_config/brag/assets/sfx/keyboard/keypress-028.wav +0 -0
  368. package/skills/global_config/brag/assets/sfx/keyboard/keypress-029.wav +0 -0
  369. package/skills/global_config/brag/assets/sfx/keyboard/keypress-030.wav +0 -0
  370. package/skills/global_config/brag/assets/sfx/keyboard/keypress-031.wav +0 -0
  371. package/skills/global_config/brag/assets/sfx/keyboard/keypress-032.wav +0 -0
  372. package/skills/global_config/brag/assets/sfx/sfx-analysis.json +15584 -0
  373. package/skills/global_config/brag/assets/sfx/sfx-analysis.md +75 -0
  374. package/skills/global_config/brag/assets/sfx/ui/click1.ogg +0 -0
  375. package/skills/global_config/brag/assets/sfx/ui/click2.ogg +0 -0
  376. package/skills/global_config/brag/assets/sfx/ui/click3.ogg +0 -0
  377. package/skills/global_config/brag/assets/sfx/ui/click4.ogg +0 -0
  378. package/skills/global_config/brag/assets/sfx/ui/click5.ogg +0 -0
  379. package/skills/global_config/brag/assets/sfx/ui/mouseclick1.ogg +0 -0
  380. package/skills/global_config/brag/assets/sfx/ui/rollover1.ogg +0 -0
  381. package/skills/global_config/brag/assets/sfx/ui/rollover2.ogg +0 -0
  382. package/skills/global_config/brag/assets/sfx/ui/rollover4.ogg +0 -0
  383. package/skills/global_config/brag/assets/sfx/ui/rollover5.ogg +0 -0
  384. package/skills/global_config/brag/assets/sfx/ui/switch1.ogg +0 -0
  385. package/skills/global_config/brag/assets/sfx/ui/switch10.ogg +0 -0
  386. package/skills/global_config/brag/assets/sfx/ui/switch11.ogg +0 -0
  387. package/skills/global_config/brag/assets/sfx/ui/switch12.ogg +0 -0
  388. package/skills/global_config/brag/assets/sfx/ui/switch13.ogg +0 -0
  389. package/skills/global_config/brag/assets/sfx/ui/switch14.ogg +0 -0
  390. package/skills/global_config/brag/assets/sfx/ui/switch15.ogg +0 -0
  391. package/skills/global_config/brag/assets/sfx/ui/switch16.ogg +0 -0
  392. package/skills/global_config/brag/assets/sfx/ui/switch17.ogg +0 -0
  393. package/skills/global_config/brag/assets/sfx/ui/switch18.ogg +0 -0
  394. package/skills/global_config/brag/assets/sfx/ui/switch19.ogg +0 -0
  395. package/skills/global_config/brag/assets/sfx/ui/switch2.ogg +0 -0
  396. package/skills/global_config/brag/assets/sfx/ui/switch20.ogg +0 -0
  397. package/skills/global_config/brag/assets/sfx/ui/switch21.ogg +0 -0
  398. package/skills/global_config/brag/assets/sfx/ui/switch22.ogg +0 -0
  399. package/skills/global_config/brag/assets/sfx/ui/switch23.ogg +0 -0
  400. package/skills/global_config/brag/assets/sfx/ui/switch24.ogg +0 -0
  401. package/skills/global_config/brag/assets/sfx/ui/switch25.ogg +0 -0
  402. package/skills/global_config/brag/assets/sfx/ui/switch26.ogg +0 -0
  403. package/skills/global_config/brag/assets/sfx/ui/switch27.ogg +0 -0
  404. package/skills/global_config/brag/assets/sfx/ui/switch28.ogg +0 -0
  405. package/skills/global_config/brag/assets/sfx/ui/switch29.ogg +0 -0
  406. package/skills/global_config/brag/assets/sfx/ui/switch3.ogg +0 -0
  407. package/skills/global_config/brag/assets/sfx/ui/switch30.ogg +0 -0
  408. package/skills/global_config/brag/assets/sfx/ui/switch31.ogg +0 -0
  409. package/skills/global_config/brag/assets/sfx/ui/switch32.ogg +0 -0
  410. package/skills/global_config/brag/assets/sfx/ui/switch33.ogg +0 -0
  411. package/skills/global_config/brag/assets/sfx/ui/switch34.ogg +0 -0
  412. package/skills/global_config/brag/assets/sfx/ui/switch35.ogg +0 -0
  413. package/skills/global_config/brag/assets/sfx/ui/switch36.ogg +0 -0
  414. package/skills/global_config/brag/assets/sfx/ui/switch37.ogg +0 -0
  415. package/skills/global_config/brag/assets/sfx/ui/switch38.ogg +0 -0
  416. package/skills/global_config/brag/assets/sfx/ui/switch4.ogg +0 -0
  417. package/skills/global_config/brag/assets/sfx/ui/switch5.ogg +0 -0
  418. package/skills/global_config/brag/assets/sfx/ui/switch6.ogg +0 -0
  419. package/skills/global_config/brag/assets/sfx/ui/switch7.ogg +0 -0
  420. package/skills/global_config/brag/assets/sfx/ui/switch8.ogg +0 -0
  421. package/skills/global_config/brag/assets/sfx/ui/switch9.ogg +0 -0
  422. package/skills/global_config/brag/references/audio.md +307 -0
  423. package/skills/global_config/brag/references/step-1-inspect.md +133 -0
  424. package/skills/global_config/brag/references/step-2-plan.md +234 -0
  425. package/skills/global_config/brag/references/step-3-compose.md +210 -0
  426. package/skills/global_config/brag/references/step-4-deliver.md +168 -0
  427. package/skills/global_config/brag/references/tones.md +231 -0
  428. package/skills/global_config/brag/scripts/analyze_music_cues.py +309 -0
  429. package/skills/global_config/brag/scripts/pyproject.toml +14 -0
  430. package/skills/global_config/brag/scripts/uv.lock +819 -0
  431. package/skills/global_config/deja-memory/SKILL.md +42 -0
  432. package/skills/global_config/editable-design/LICENSE +202 -0
  433. package/skills/global_config/editable-design/README.md +34 -0
  434. package/skills/global_config/editable-design/SKILL.md +630 -0
  435. package/skills/global_config/editable-design/THIRD_PARTY_NOTICES.md +10 -0
  436. package/skills/global_config/editable-design/agents/openai.yaml +4 -0
  437. package/skills/global_config/editable-design/assets/editor/layer-editor.css +262 -0
  438. package/skills/global_config/editable-design/assets/editor/layer-editor.js +1929 -0
  439. package/skills/global_config/editable-design/assets/poster-starter/README.md +92 -0
  440. package/skills/global_config/editable-design/assets/poster-starter/assets/.gitkeep +0 -0
  441. package/skills/global_config/editable-design/assets/poster-starter/index.html +91 -0
  442. package/skills/global_config/editable-design/assets/poster-starter/poster.json +6 -0
  443. package/skills/global_config/editable-design/assets/poster-starter/reference/.gitkeep +1 -0
  444. package/skills/global_config/editable-design/assets/replay/icons/assets.png +0 -0
  445. package/skills/global_config/editable-design/assets/replay/icons/html.png +0 -0
  446. package/skills/global_config/editable-design/assets/replay/icons/input.png +0 -0
  447. package/skills/global_config/editable-design/assets/replay/icons/layers.png +0 -0
  448. package/skills/global_config/editable-design/assets/replay/icons/plan.png +0 -0
  449. package/skills/global_config/editable-design/assets/replay/icons/reference.png +0 -0
  450. package/skills/global_config/editable-design/assets/replay/icons/review.png +0 -0
  451. package/skills/global_config/editable-design/assets/replay/index.html +59 -0
  452. package/skills/global_config/editable-design/assets/replay/replay-motion.js +410 -0
  453. package/skills/global_config/editable-design/assets/replay/replay.css +380 -0
  454. package/skills/global_config/editable-design/assets/replay/replay.js +374 -0
  455. package/skills/global_config/editable-design/font-kit/package-lock.json +115 -0
  456. package/skills/global_config/editable-design/font-kit/package.json +19 -0
  457. package/skills/global_config/editable-design/pack.sh +20 -0
  458. package/skills/global_config/editable-design/references/asset-architecture.md +72 -0
  459. package/skills/global_config/editable-design/references/editor-pitfalls.md +70 -0
  460. package/skills/global_config/editable-design/references/editor-runtime.md +131 -0
  461. package/skills/global_config/editable-design/references/font-system.md +89 -0
  462. package/skills/global_config/editable-design/references/imagery.md +145 -0
  463. package/skills/global_config/editable-design/references/layout-typography.md +116 -0
  464. package/skills/global_config/editable-design/references/replay-contract.md +169 -0
  465. package/skills/global_config/editable-design/scripts/_browser.mjs +247 -0
  466. package/skills/global_config/editable-design/scripts/bake.mjs +87 -0
  467. package/skills/global_config/editable-design/scripts/build-replay.mjs +316 -0
  468. package/skills/global_config/editable-design/scripts/check-contract.mjs +293 -0
  469. package/skills/global_config/editable-design/scripts/check-fonts.sh +252 -0
  470. package/skills/global_config/editable-design/scripts/check-poster.sh +70 -0
  471. package/skills/global_config/editable-design/scripts/doctor.sh +78 -0
  472. package/skills/global_config/editable-design/scripts/explode.mjs +115 -0
  473. package/skills/global_config/editable-design/scripts/font-kit.mjs +187 -0
  474. package/skills/global_config/editable-design/scripts/import-asset.sh +23 -0
  475. package/skills/global_config/editable-design/scripts/init-poster.sh +64 -0
  476. package/skills/global_config/editable-design/scripts/install-font-kit.sh +13 -0
  477. package/skills/global_config/editable-design/scripts/package-lock.json +334 -0
  478. package/skills/global_config/editable-design/scripts/package.json +14 -0
  479. package/skills/global_config/editable-design/scripts/remove-chroma-key.py +201 -0
  480. package/skills/global_config/editable-design/scripts/render-poster.sh +202 -0
  481. package/skills/global_config/editable-design/scripts/trace.sh +159 -0
  482. package/skills/global_config/editable-design/scripts/verify-replay.mjs +97 -0
  483. package/skills/global_config/editable-design/scripts/verify.mjs +782 -0
  484. package/skills/global_config/editable-design/scripts/wire-editor.mjs +104 -0
  485. package/skills/global_config/editable-design/tests/fixtures/small-explode/index.html +86 -0
  486. package/skills/global_config/plan-canvas/SKILL.md +8 -0
@@ -0,0 +1,40 @@
1
+ /** Serialize computed layout for dry-run / inspect (#9). */
2
+
3
+ export function componentBox(c) {
4
+ return {
5
+ id: c.id,
6
+ type: c.type,
7
+ label: c.label,
8
+ x: Math.round(c.x),
9
+ y: Math.round(c.y),
10
+ width: c.width,
11
+ height: c.height,
12
+ ...(Number.isInteger(c.row) ? { row: c.row } : {}),
13
+ ...(Number.isInteger(c.col) ? { col: c.col } : {}),
14
+ ...(Array.isArray(c.pos) ? { pos: c.pos.map(Math.round) } : {}),
15
+ };
16
+ }
17
+
18
+ export function boundaryBox(b) {
19
+ return {
20
+ kind: b.kind,
21
+ label: b.label,
22
+ x: Math.round(b.x),
23
+ y: Math.round(b.y),
24
+ width: Math.round(b.width),
25
+ height: Math.round(b.height),
26
+ wraps: b.wraps,
27
+ };
28
+ }
29
+
30
+ export function connectionPath(conn, routed, labelAt) {
31
+ return {
32
+ from: conn.from,
33
+ to: conn.to,
34
+ label: conn.label ?? null,
35
+ variant: conn.variant ?? 'default',
36
+ route: conn.route ?? 'auto',
37
+ points: routed.points.map(([x, y]) => [Math.round(x), Math.round(y)]),
38
+ ...(labelAt ? { labelAt: labelAt.map(Math.round) } : {}),
39
+ };
40
+ }
@@ -0,0 +1,217 @@
1
+ import { throwDiagnosticError } from './diagnostics.mjs';
2
+ import { rectsOverlap, segmentIntersectsRect } from './geometry.mjs';
3
+ import { esc, textUnits } from './utils.mjs';
4
+ import { translateMessage } from './i18n.mjs';
5
+
6
+ const DEFAULT_FONT_SIZE = 8;
7
+ const DEFAULT_ITEM_GAP = 22;
8
+ const DEFAULT_LINE_GAP = 22;
9
+ const DEFAULT_SWATCH_GAP = 8;
10
+ const TEXT_ADVANCE_EM = 0.62;
11
+ const INTERACTIVE_BADGE_ALLOWANCE = 21;
12
+
13
+ export function relationshipLegendObstacles(relations, { pointsFor, labelRectFor } = {}) {
14
+ const obstacles = [];
15
+ for (const [index, relation] of (Array.isArray(relations) ? relations : []).entries()) {
16
+ const points = typeof pointsFor === 'function' ? pointsFor(relation, index) : [];
17
+ const finitePoints = (Array.isArray(points) ? points : []).filter((point) => (
18
+ Array.isArray(point) && point.length === 2 && point.every(Number.isFinite)
19
+ ));
20
+ for (let pointIndex = 0; pointIndex < finitePoints.length - 1; pointIndex += 1) {
21
+ obstacles.push({
22
+ kind: 'relationship-segment',
23
+ start: finitePoints[pointIndex],
24
+ end: finitePoints[pointIndex + 1],
25
+ });
26
+ }
27
+ const labelRect = typeof labelRectFor === 'function' ? labelRectFor(relation, index) : null;
28
+ if (labelRect && [labelRect.x, labelRect.y, labelRect.width, labelRect.height].every(Number.isFinite)) {
29
+ obstacles.push({ kind: 'relationship-label', ...labelRect });
30
+ }
31
+ }
32
+ return obstacles;
33
+ }
34
+
35
+ export function resolveLegend(config, catalog, presentKinds) {
36
+ const mode = config?.mode || 'auto';
37
+ if (mode === 'hidden') return [];
38
+ const present = presentKinds instanceof Set ? presentKinds : new Set(presentKinds || []);
39
+ const overrides = config?.entries || {};
40
+
41
+ return catalog.flatMap((catalogEntry) => {
42
+ const override = overrides[catalogEntry.kind] || {};
43
+ const selectedByMode = mode === 'all' || present.has(catalogEntry.kind);
44
+ const visible = override.visible === true || (selectedByMode && override.visible !== false);
45
+ if (!visible) return [];
46
+ return [{
47
+ ...catalogEntry,
48
+ label: override.label || catalogEntry.label,
49
+ present: present.has(catalogEntry.kind),
50
+ interactive: catalogEntry.interactive !== false && present.has(catalogEntry.kind),
51
+ }];
52
+ });
53
+ }
54
+
55
+ function measuredEntryWidth(entry, fontSize, swatchGap) {
56
+ const swatchWidth = entry.swatchWidth ?? 14;
57
+ return Math.ceil(
58
+ swatchWidth
59
+ + swatchGap
60
+ + textUnits(entry.label) * fontSize * TEXT_ADVANCE_EM
61
+ + (entry.interactive ? INTERACTIVE_BADGE_ALLOWANCE : 0),
62
+ );
63
+ }
64
+
65
+ // One pure footprint calculation owns both auto-viewBox sizing and final SVG
66
+ // placement. Callers must not maintain a second approximation of legend width
67
+ // or row count; that would make generated geometry disagree with validation.
68
+ export function legendFootprint(entries, {
69
+ width,
70
+ fontSize = DEFAULT_FONT_SIZE,
71
+ itemGap = DEFAULT_ITEM_GAP,
72
+ lineGap = DEFAULT_LINE_GAP,
73
+ swatchGap = DEFAULT_SWATCH_GAP,
74
+ } = {}) {
75
+ if (!entries.length) {
76
+ return { measured: [], rows: [], rowCount: 0, minWidth: 0, extraHeight: 0 };
77
+ }
78
+ const measured = entries.map((entry) => ({
79
+ ...entry,
80
+ width: measuredEntryWidth(entry, fontSize, entry.swatchGap ?? swatchGap),
81
+ }));
82
+ const rows = [[]];
83
+ let cursor = 0;
84
+ for (const entry of measured) {
85
+ const row = rows.at(-1);
86
+ const required = (row.length ? itemGap : 0) + entry.width;
87
+ if (row.length && cursor + required > width) {
88
+ rows.push([entry]);
89
+ cursor = entry.width;
90
+ } else {
91
+ row.push(entry);
92
+ cursor += required;
93
+ }
94
+ }
95
+ return {
96
+ measured,
97
+ rows,
98
+ rowCount: rows.length,
99
+ minWidth: Math.max(...measured.map((entry) => entry.width)),
100
+ extraHeight: (rows.length - 1) * lineGap,
101
+ };
102
+ }
103
+
104
+ export function measureLegend(entries, {
105
+ x,
106
+ baselineY,
107
+ width,
108
+ fontSize = DEFAULT_FONT_SIZE,
109
+ itemGap = DEFAULT_ITEM_GAP,
110
+ lineGap = DEFAULT_LINE_GAP,
111
+ swatchGap = DEFAULT_SWATCH_GAP,
112
+ minTitleY = 0,
113
+ obstacles = [],
114
+ unfit = 'error',
115
+ diagramType = 'diagram',
116
+ } = {}) {
117
+ if (!entries.length) return { entries: [], rowCount: 0, titleY: null };
118
+ const footprint = legendFootprint(entries, { width, fontSize, itemGap, lineGap, swatchGap });
119
+ const tooWide = footprint.measured.find((entry) => entry.width > width);
120
+ if (tooWide) {
121
+ if (unfit === 'hide') return null;
122
+ const message = `[legend/label-too-wide] ${diagramType} legend label for "${tooWide.kind}" needs ${tooWide.width}px but only ${width}px is available.`;
123
+ throwDiagnosticError(message, [{
124
+ code: 'legend/label-too-wide',
125
+ severity: 'error',
126
+ message,
127
+ subject: { diagramType, path: `/meta/legend/entries/${tooWide.kind}/label` },
128
+ evidence: { kind: tooWide.kind, measuredWidthPx: tooWide.width, availableWidthPx: width },
129
+ supportedFixes: ['shorten the legend label or use a wider viewBox'],
130
+ }]);
131
+ }
132
+
133
+ const titleY = baselineY - footprint.extraHeight - 20;
134
+ const legendTopY = titleY - 10;
135
+ if (legendTopY < minTitleY) {
136
+ if (unfit === 'hide') return null;
137
+ const message = `[legend/vertical-overflow] ${diagramType} legend needs ${footprint.rowCount} rows, which would start at y=${legendTopY} above the available legend band at y=${minTitleY}.`;
138
+ throwDiagnosticError(message, [{
139
+ code: 'legend/vertical-overflow',
140
+ severity: 'error',
141
+ message,
142
+ subject: { diagramType, path: '/meta/legend' },
143
+ evidence: { rowCount: footprint.rowCount, requiredTopY: legendTopY, availableTopY: minTitleY },
144
+ supportedFixes: ['shorten legend labels, hide nonessential entries, or use a wider viewBox'],
145
+ }]);
146
+ }
147
+
148
+ const positioned = [];
149
+ footprint.rows.forEach((row, rowIndex) => {
150
+ let entryX = x;
151
+ const baseline = baselineY - (footprint.rowCount - rowIndex - 1) * lineGap;
152
+ for (const entry of row) {
153
+ positioned.push({ ...entry, x: entryX, baseline, row: rowIndex });
154
+ entryX += entry.width + itemGap;
155
+ }
156
+ });
157
+
158
+ const legendRects = [
159
+ { kind: 'title', x, y: legendTopY, width: 48, height: 14 },
160
+ ...positioned.map((entry) => ({
161
+ kind: entry.kind,
162
+ x: entry.x,
163
+ y: entry.baseline - 10,
164
+ width: entry.width,
165
+ height: 14,
166
+ })),
167
+ ];
168
+ const collision = legendRects.find((legendRect) => obstacles.some((obstacle) => (
169
+ Array.isArray(obstacle.start) && Array.isArray(obstacle.end)
170
+ ? segmentIntersectsRect({ start: obstacle.start, end: obstacle.end }, legendRect)
171
+ : rectsOverlap(obstacle, legendRect)
172
+ )));
173
+ if (collision) {
174
+ if (unfit === 'hide') return null;
175
+ const message = `[legend/content-overlap] ${diagramType} legend entry "${collision.kind}" overlaps authored relationship geometry.`;
176
+ throwDiagnosticError(message, [{
177
+ code: 'legend/content-overlap',
178
+ severity: 'error',
179
+ message,
180
+ subject: { diagramType, path: '/meta/legend' },
181
+ evidence: { legendKind: collision.kind, legendRect: collision },
182
+ supportedFixes: ['shorten or hide legend entries, use a wider viewBox, or move the authored relationship route/label out of the legend band'],
183
+ }]);
184
+ }
185
+
186
+ return {
187
+ entries: positioned,
188
+ rowCount: footprint.rowCount,
189
+ titleY,
190
+ fontSize,
191
+ };
192
+ }
193
+
194
+ export function renderLegend({ entries, layout, renderSwatch, locale }) {
195
+ if (!entries.length) return '';
196
+ const measured = measureLegend(entries, layout);
197
+ if (!measured) return '';
198
+ const hasInteractiveEntries = measured.entries.some((entry) => entry.interactive);
199
+ const renderedFontSize = measured.fontSize < 8 ? measured.fontSize + 0.5 : measured.fontSize + 2;
200
+ const rootAttributes = hasInteractiveEntries ? ' data-legend="" data-legend-bridge=""' : ' data-legend=""';
201
+ const parts = [
202
+ ` <g${rootAttributes}>`,
203
+ ` <text x="${layout.x}" y="${measured.titleY}" class="t-primary" font-size="12" font-weight="650">${esc(translateMessage(locale, 'legend.title'))}</text>`,
204
+ ];
205
+
206
+ for (const entry of measured.entries) {
207
+ const interactive = entry.interactive
208
+ ? ` data-legend-kind="${esc(entry.kind)}" data-legend-label="${esc(entry.label)}"`
209
+ : '';
210
+ parts.push(` <g data-legend-semantic-kind="${esc(entry.kind)}"${interactive} data-legend-x="${entry.x}" data-legend-baseline="${entry.baseline}" data-legend-width="${entry.width}">`);
211
+ parts.push(` ${renderSwatch(entry)}`);
212
+ parts.push(` <text x="${entry.x + (entry.swatchWidth ?? 14) + (entry.swatchGap ?? DEFAULT_SWATCH_GAP)}" y="${entry.baseline}" class="t-muted" font-size="${renderedFontSize}" font-weight="500">${esc(entry.label)}</text>`);
213
+ parts.push(' </g>');
214
+ }
215
+ parts.push(' </g>');
216
+ return parts.join('\n');
217
+ }
@@ -0,0 +1,340 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ const MAX_SYMLINK_DEPTH = 64;
5
+ const directorySemanticsCache = new Map();
6
+ let semanticsProbeSequence = 0;
7
+
8
+ function splitAbsolute(absolutePath) {
9
+ const root = path.parse(absolutePath).root;
10
+ return {
11
+ root,
12
+ segments: absolutePath.slice(root.length).split(path.sep).filter(Boolean),
13
+ };
14
+ }
15
+
16
+ function canonicalize(targetPath, depth) {
17
+ const absolutePath = path.resolve(targetPath);
18
+ const { root, segments } = splitAbsolute(absolutePath);
19
+ let current = root;
20
+
21
+ for (let index = 0; index < segments.length; index += 1) {
22
+ const candidate = path.join(current, segments[index]);
23
+ let stat;
24
+ try {
25
+ stat = fs.lstatSync(candidate);
26
+ } catch (error) {
27
+ if (error.code === 'ENOENT' || error.code === 'ENOTDIR') {
28
+ return path.resolve(current, ...segments.slice(index));
29
+ }
30
+ throw error;
31
+ }
32
+
33
+ if (stat.isSymbolicLink()) {
34
+ if (depth >= MAX_SYMLINK_DEPTH) {
35
+ const error = new Error(`Could not resolve path because a symbolic-link cycle includes "${candidate}".`);
36
+ error.code = 'ELOOP';
37
+ error.path = candidate;
38
+ throw error;
39
+ }
40
+ const link = fs.readlinkSync(candidate);
41
+ const linkTarget = path.isAbsolute(link) ? link : path.resolve(path.dirname(candidate), link);
42
+ return canonicalize(path.join(linkTarget, ...segments.slice(index + 1)), depth + 1);
43
+ }
44
+
45
+ current = fs.realpathSync.native(candidate);
46
+ }
47
+
48
+ return path.normalize(current);
49
+ }
50
+
51
+ export function canonicalFuturePath(targetPath) {
52
+ try {
53
+ return canonicalize(targetPath, 0);
54
+ } catch (error) {
55
+ if (error?.code !== 'ELOOP') throw error;
56
+ const output = path.resolve(targetPath);
57
+ throw new OutputPathError(`Output path contains a symbolic-link cycle: "${output}".`, {
58
+ code: 'output/symlink-cycle',
59
+ message: 'Output path could not be resolved because it contains a symbolic-link cycle.',
60
+ subject: { output },
61
+ evidence: {
62
+ systemCode: 'ELOOP',
63
+ ...(error.path ? { cycleAt: path.resolve(error.path) } : {}),
64
+ },
65
+ supportedFixes: ['remove the symbolic-link cycle or choose an output path outside it'],
66
+ });
67
+ }
68
+ }
69
+
70
+ function hasFileIdentity(stat) {
71
+ return stat.ino !== 0 && stat.ino !== 0n;
72
+ }
73
+
74
+ function sameFileIdentity(left, right) {
75
+ return hasFileIdentity(left)
76
+ && hasFileIdentity(right)
77
+ && left.dev === right.dev
78
+ && left.ino === right.ino;
79
+ }
80
+
81
+ function nearestExistingDirectory(targetPath) {
82
+ let directory = path.dirname(targetPath);
83
+ while (true) {
84
+ try {
85
+ const stat = fs.statSync(directory);
86
+ if (stat.isDirectory()) {
87
+ return {
88
+ path: fs.realpathSync.native(directory),
89
+ stat,
90
+ };
91
+ }
92
+ } catch (error) {
93
+ if (error.code !== 'ENOENT' && error.code !== 'ENOTDIR') return null;
94
+ }
95
+ const parent = path.dirname(directory);
96
+ if (parent === directory) return null;
97
+ directory = parent;
98
+ }
99
+ }
100
+
101
+ function directoryIdentityKey(directory) {
102
+ if (!hasFileIdentity(directory.stat)) return null;
103
+ return `${directory.stat.dev}:${directory.stat.ino}`;
104
+ }
105
+
106
+ function probeNamesAlias(directoryPath, authoredName, lookupName) {
107
+ let fileDescriptor;
108
+ let created = false;
109
+ let result = null;
110
+ let cleaned = true;
111
+ const authoredPath = path.join(directoryPath, authoredName);
112
+ const lookupPath = path.join(directoryPath, lookupName);
113
+ try {
114
+ fileDescriptor = fs.openSync(authoredPath, 'wx', 0o600);
115
+ created = true;
116
+ fs.closeSync(fileDescriptor);
117
+ fileDescriptor = undefined;
118
+
119
+ let authored;
120
+ let lookup;
121
+ try {
122
+ authored = fs.statSync(authoredPath);
123
+ lookup = fs.statSync(lookupPath);
124
+ } catch (error) {
125
+ if (error.code === 'ENOENT') result = false;
126
+ }
127
+ if (authored && lookup) {
128
+ if (sameFileIdentity(authored, lookup)) {
129
+ result = true;
130
+ } else {
131
+ try {
132
+ result = fs.realpathSync.native(authoredPath) === fs.realpathSync.native(lookupPath);
133
+ } catch {
134
+ result = null;
135
+ }
136
+ }
137
+ }
138
+ } catch {
139
+ result = null;
140
+ } finally {
141
+ if (fileDescriptor !== undefined) {
142
+ try {
143
+ fs.closeSync(fileDescriptor);
144
+ } catch {
145
+ cleaned = false;
146
+ }
147
+ }
148
+ if (created) {
149
+ try {
150
+ fs.unlinkSync(authoredPath);
151
+ } catch {
152
+ cleaned = false;
153
+ }
154
+ }
155
+ }
156
+ return cleaned ? result : null;
157
+ }
158
+
159
+ function probeDirectorySemantics(directory) {
160
+ const cacheKey = directoryIdentityKey(directory);
161
+ if (cacheKey && directorySemanticsCache.has(cacheKey)) {
162
+ return directorySemanticsCache.get(cacheKey);
163
+ }
164
+
165
+ semanticsProbeSequence += 1;
166
+ const suffix = `${process.pid}-${Date.now().toString(36)}-${semanticsProbeSequence}`;
167
+ const caseAuthored = `.archify-Case-Probe-${suffix}`;
168
+ const normalizationAuthored = `.archify-norm-\u00e9-probe-${suffix}`;
169
+ const semantics = {
170
+ caseInsensitive: probeNamesAlias(
171
+ directory.path,
172
+ caseAuthored,
173
+ caseAuthored.toLowerCase(),
174
+ ),
175
+ normalizationInsensitive: probeNamesAlias(
176
+ directory.path,
177
+ normalizationAuthored,
178
+ normalizationAuthored.normalize('NFD'),
179
+ ),
180
+ };
181
+ if (
182
+ cacheKey
183
+ && semantics.caseInsensitive !== null
184
+ && semantics.normalizationInsensitive !== null
185
+ ) {
186
+ directorySemanticsCache.set(cacheKey, semantics);
187
+ }
188
+ return semantics;
189
+ }
190
+
191
+ function sameDirectory(left, right) {
192
+ return left.path === right.path || sameFileIdentity(left.stat, right.stat);
193
+ }
194
+
195
+ function futurePathsAlias(leftPath, rightPath) {
196
+ const left = canonicalFuturePath(leftPath);
197
+ const right = canonicalFuturePath(rightPath);
198
+ if (left === right) return true;
199
+
200
+ const leftDirectory = nearestExistingDirectory(left);
201
+ const rightDirectory = nearestExistingDirectory(right);
202
+ if (!leftDirectory || !rightDirectory || !sameDirectory(leftDirectory, rightDirectory)) {
203
+ return false;
204
+ }
205
+
206
+ const semantics = probeDirectorySemantics(leftDirectory);
207
+ let comparableLeft = path.relative(leftDirectory.path, left);
208
+ let comparableRight = path.relative(rightDirectory.path, right);
209
+ if (semantics.normalizationInsensitive !== false) {
210
+ comparableLeft = comparableLeft.normalize('NFC');
211
+ comparableRight = comparableRight.normalize('NFC');
212
+ }
213
+ if (semantics.caseInsensitive !== false) {
214
+ comparableLeft = comparableLeft.toLowerCase();
215
+ comparableRight = comparableRight.toLowerCase();
216
+ }
217
+ return comparableLeft === comparableRight;
218
+ }
219
+
220
+ export function pathsAlias(leftPath, rightPath) {
221
+ if (futurePathsAlias(leftPath, rightPath)) return true;
222
+ try {
223
+ const left = fs.statSync(leftPath);
224
+ const right = fs.statSync(rightPath);
225
+ return sameFileIdentity(left, right);
226
+ } catch {
227
+ return false;
228
+ }
229
+ }
230
+
231
+ function pathIsInside(directoryPath, targetPath) {
232
+ const relative = path.relative(canonicalFuturePath(directoryPath), canonicalFuturePath(targetPath));
233
+ return relative === '' || (!path.isAbsolute(relative) && relative !== '..' && !relative.startsWith(`..${path.sep}`));
234
+ }
235
+
236
+ export class OutputPathError extends Error {
237
+ constructor(message, diagnostic) {
238
+ super(message);
239
+ this.name = 'OutputPathError';
240
+ this.archifyDiagnostics = [{
241
+ severity: 'error',
242
+ subject: {},
243
+ evidence: {},
244
+ supportedFixes: [],
245
+ ...diagnostic,
246
+ }];
247
+ }
248
+ }
249
+
250
+ export function resolveOutputPath({
251
+ requestedOutput,
252
+ authoredOutput,
253
+ defaultOutput,
254
+ inputPaths = [],
255
+ inputDescription = 'an input',
256
+ otherOutputPaths = [],
257
+ cwd = process.cwd(),
258
+ requiredExtension = '.html',
259
+ }) {
260
+ const rawOutput = requestedOutput || authoredOutput || defaultOutput;
261
+ const source = requestedOutput ? 'cli' : (authoredOutput ? 'meta' : 'default');
262
+ if (
263
+ source === 'meta'
264
+ && (path.isAbsolute(rawOutput) || path.posix.isAbsolute(rawOutput) || path.win32.isAbsolute(rawOutput))
265
+ ) {
266
+ throw new OutputPathError('meta.output must be a relative path.', {
267
+ code: 'output/meta-absolute',
268
+ message: 'meta.output must be a relative path resolved from the current working directory.',
269
+ subject: { output: rawOutput },
270
+ supportedFixes: ['set meta.output to a relative .html path inside the current working directory'],
271
+ });
272
+ }
273
+ if (source === 'meta' && path.extname(rawOutput).toLowerCase() !== '.html') {
274
+ throw new OutputPathError('meta.output must target an .html file.', {
275
+ code: 'output/meta-extension',
276
+ message: 'meta.output must target an .html file.',
277
+ subject: { output: rawOutput },
278
+ supportedFixes: ['change meta.output to a path ending in .html'],
279
+ });
280
+ }
281
+ const outputPath = path.resolve(cwd, rawOutput);
282
+ if (source === 'meta' && path.extname(canonicalFuturePath(outputPath)).toLowerCase() !== '.html') {
283
+ throw new OutputPathError('meta.output must resolve to an .html file.', {
284
+ code: 'output/meta-resolved-extension',
285
+ message: 'meta.output must resolve to an .html file after symbolic links are followed.',
286
+ subject: { output: rawOutput },
287
+ supportedFixes: ['remove the symbolic-link alias or point it to an .html target inside the current working directory'],
288
+ });
289
+ }
290
+ if (source === 'meta' && !pathIsInside(cwd, outputPath)) {
291
+ throw new OutputPathError('meta.output must stay inside the current working directory.', {
292
+ code: 'output/meta-outside-cwd',
293
+ message: 'meta.output must stay inside the current working directory after symbolic links are resolved.',
294
+ subject: { output: rawOutput, cwd: path.resolve(cwd) },
295
+ supportedFixes: ['set meta.output to a relative .html path inside the current working directory'],
296
+ });
297
+ }
298
+
299
+ for (const inputPath of inputPaths) {
300
+ if (!pathsAlias(outputPath, inputPath)) continue;
301
+ throw new OutputPathError(`Output must not replace ${inputDescription}.`, {
302
+ code: 'output/input-alias',
303
+ message: `Output must not replace ${inputDescription}, including through a symbolic-link or future-path alias.`,
304
+ subject: { output: outputPath, input: path.resolve(inputPath) },
305
+ supportedFixes: ['choose an output path that is distinct from every input path'],
306
+ });
307
+ }
308
+ for (const otherOutputPath of otherOutputPaths) {
309
+ if (!pathsAlias(outputPath, otherOutputPath)) continue;
310
+ throw new OutputPathError('Output targets must use distinct paths.', {
311
+ code: 'output/target-alias',
312
+ message: 'Output targets must use distinct paths, including symbolic-link and future-path aliases.',
313
+ subject: { output: outputPath, conflictingOutput: path.resolve(otherOutputPath) },
314
+ supportedFixes: ['choose distinct paths for every generated output'],
315
+ });
316
+ }
317
+
318
+ // Keep explicit CLI directories unrestricted, but reject mistaken file types.
319
+ // Alias checks above retain priority when a target would overwrite an input.
320
+ if (source === 'cli') {
321
+ const resolvedOutput = canonicalFuturePath(outputPath);
322
+ const authoredMatches = path.extname(rawOutput).toLowerCase() === requiredExtension;
323
+ const resolvedMatches = path.extname(resolvedOutput).toLowerCase() === requiredExtension;
324
+ if (!authoredMatches || !resolvedMatches) {
325
+ const message = `CLI output must ${authoredMatches ? 'resolve to' : 'target'} a ${requiredExtension} file.`;
326
+ throw new OutputPathError(message, {
327
+ code: authoredMatches ? 'output/cli-resolved-extension' : 'output/cli-extension',
328
+ message,
329
+ subject: { output: rawOutput },
330
+ evidence: { resolvedOutput, requiredExtension },
331
+ supportedFixes: [`choose a path ending in ${requiredExtension} whose symbolic-link target also ends in ${requiredExtension}`],
332
+ });
333
+ }
334
+ }
335
+
336
+ return {
337
+ outputPath,
338
+ source,
339
+ };
340
+ }