@su-record/vibe 3.2.13 → 3.2.15

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 (305) hide show
  1. package/CLAUDE.md +15 -14
  2. package/README.en.md +1 -1
  3. package/README.md +1 -1
  4. package/dist/cli/setup/ProjectSetup.d.ts +6 -0
  5. package/dist/cli/setup/ProjectSetup.d.ts.map +1 -1
  6. package/dist/cli/setup/ProjectSetup.js +19 -13
  7. package/dist/cli/setup/ProjectSetup.js.map +1 -1
  8. package/dist/infra/lib/codex-proxy.d.ts +126 -0
  9. package/dist/infra/lib/codex-proxy.d.ts.map +1 -1
  10. package/dist/infra/lib/codex-proxy.js +16 -16
  11. package/dist/infra/lib/codex-proxy.js.map +1 -1
  12. package/dist/infra/lib/figma/audit.d.ts.map +1 -1
  13. package/dist/infra/lib/figma/audit.js +0 -2
  14. package/dist/infra/lib/figma/audit.js.map +1 -1
  15. package/dist/infra/lib/figma/extract.d.ts +6 -1
  16. package/dist/infra/lib/figma/extract.d.ts.map +1 -1
  17. package/dist/infra/lib/figma/extract.js +17 -15
  18. package/dist/infra/lib/figma/extract.js.map +1 -1
  19. package/dist/infra/lib/figma/types.d.ts +103 -0
  20. package/dist/infra/lib/figma/types.d.ts.map +1 -1
  21. package/dist/infra/lib/figma/types.js +8 -0
  22. package/dist/infra/lib/figma/types.js.map +1 -1
  23. package/hooks/hooks.json +1 -1
  24. package/hooks/scripts/clone-extract.js +16 -1
  25. package/hooks/scripts/code-check.js +72 -55
  26. package/hooks/scripts/lib/anchor.js +74 -0
  27. package/hooks/scripts/lib/console-allow.js +66 -0
  28. package/hooks/scripts/lib/dispatcher.js +71 -19
  29. package/hooks/scripts/lib/inbox.js +60 -0
  30. package/hooks/scripts/loop-ledger.js +24 -1
  31. package/hooks/scripts/post-edit-dispatcher.js +4 -3
  32. package/hooks/scripts/post-edit.js +2 -2
  33. package/package.json +13 -9
  34. package/skills/vibe/SKILL.md +2 -1
  35. package/skills/vibe.clone/references/verification-loops.md +6 -3
  36. package/skills/vibe.loop/SKILL.md +7 -4
  37. package/skills/vibe.review/SKILL.md +5 -1
  38. package/skills/vibe.run/references/e2e-and-autofix.md +1 -1
  39. package/skills/vibe.run/references/process-steps.md +1 -1
  40. package/vibe/constitution.md +1 -1
  41. package/vibe/rules/loop-contract.md +40 -3
  42. package/vibe/rules/quality/checklist.md +3 -3
  43. package/vibe/templates/constitution-template.md +1 -1
  44. package/dist/__tests__/agent-model-sync.test.d.ts +0 -2
  45. package/dist/__tests__/agent-model-sync.test.d.ts.map +0 -1
  46. package/dist/__tests__/agent-model-sync.test.js +0 -71
  47. package/dist/__tests__/agent-model-sync.test.js.map +0 -1
  48. package/dist/__tests__/architecture.test.d.ts +0 -2
  49. package/dist/__tests__/architecture.test.d.ts.map +0 -1
  50. package/dist/__tests__/architecture.test.js +0 -207
  51. package/dist/__tests__/architecture.test.js.map +0 -1
  52. package/dist/__tests__/skill-namespace.test.d.ts +0 -2
  53. package/dist/__tests__/skill-namespace.test.d.ts.map +0 -1
  54. package/dist/__tests__/skill-namespace.test.js +0 -60
  55. package/dist/__tests__/skill-namespace.test.js.map +0 -1
  56. package/dist/__tests__/stakes-contract.test.d.ts +0 -2
  57. package/dist/__tests__/stakes-contract.test.d.ts.map +0 -1
  58. package/dist/__tests__/stakes-contract.test.js +0 -166
  59. package/dist/__tests__/stakes-contract.test.js.map +0 -1
  60. package/dist/__tests__/stuck-semantics.test.d.ts +0 -2
  61. package/dist/__tests__/stuck-semantics.test.d.ts.map +0 -1
  62. package/dist/__tests__/stuck-semantics.test.js +0 -109
  63. package/dist/__tests__/stuck-semantics.test.js.map +0 -1
  64. package/dist/__tests__/wiring-integrity.test.d.ts +0 -2
  65. package/dist/__tests__/wiring-integrity.test.d.ts.map +0 -1
  66. package/dist/__tests__/wiring-integrity.test.js +0 -160
  67. package/dist/__tests__/wiring-integrity.test.js.map +0 -1
  68. package/dist/cli/commands/info.test.d.ts +0 -2
  69. package/dist/cli/commands/info.test.d.ts.map +0 -1
  70. package/dist/cli/commands/info.test.js +0 -74
  71. package/dist/cli/commands/info.test.js.map +0 -1
  72. package/dist/cli/commands/upgrade.test.d.ts +0 -2
  73. package/dist/cli/commands/upgrade.test.d.ts.map +0 -1
  74. package/dist/cli/commands/upgrade.test.js +0 -99
  75. package/dist/cli/commands/upgrade.test.js.map +0 -1
  76. package/dist/cli/design/design-md-parser.test.d.ts +0 -2
  77. package/dist/cli/design/design-md-parser.test.d.ts.map +0 -1
  78. package/dist/cli/design/design-md-parser.test.js +0 -149
  79. package/dist/cli/design/design-md-parser.test.js.map +0 -1
  80. package/dist/cli/detect.characterization.test.d.ts +0 -7
  81. package/dist/cli/detect.characterization.test.d.ts.map +0 -1
  82. package/dist/cli/detect.characterization.test.js +0 -294
  83. package/dist/cli/detect.characterization.test.js.map +0 -1
  84. package/dist/cli/postinstall/fs-utils.test.d.ts +0 -2
  85. package/dist/cli/postinstall/fs-utils.test.d.ts.map +0 -1
  86. package/dist/cli/postinstall/fs-utils.test.js +0 -259
  87. package/dist/cli/postinstall/fs-utils.test.js.map +0 -1
  88. package/dist/cli/setup/CodexHooks.test.d.ts +0 -2
  89. package/dist/cli/setup/CodexHooks.test.d.ts.map +0 -1
  90. package/dist/cli/setup/CodexHooks.test.js +0 -52
  91. package/dist/cli/setup/CodexHooks.test.js.map +0 -1
  92. package/dist/cli/setup/GlobalInstaller.test.d.ts +0 -2
  93. package/dist/cli/setup/GlobalInstaller.test.d.ts.map +0 -1
  94. package/dist/cli/setup/GlobalInstaller.test.js +0 -16
  95. package/dist/cli/setup/GlobalInstaller.test.js.map +0 -1
  96. package/dist/cli/utils/cli-detector.test.d.ts +0 -2
  97. package/dist/cli/utils/cli-detector.test.d.ts.map +0 -1
  98. package/dist/cli/utils/cli-detector.test.js +0 -52
  99. package/dist/cli/utils/cli-detector.test.js.map +0 -1
  100. package/dist/infra/lib/SkillFrontmatter.test.d.ts +0 -2
  101. package/dist/infra/lib/SkillFrontmatter.test.d.ts.map +0 -1
  102. package/dist/infra/lib/SkillFrontmatter.test.js +0 -60
  103. package/dist/infra/lib/SkillFrontmatter.test.js.map +0 -1
  104. package/dist/infra/lib/__tests__/AutomationLevel.test.d.ts +0 -2
  105. package/dist/infra/lib/__tests__/AutomationLevel.test.d.ts.map +0 -1
  106. package/dist/infra/lib/__tests__/AutomationLevel.test.js +0 -297
  107. package/dist/infra/lib/__tests__/AutomationLevel.test.js.map +0 -1
  108. package/dist/infra/lib/__tests__/ComponentRegistry.test.d.ts +0 -2
  109. package/dist/infra/lib/__tests__/ComponentRegistry.test.d.ts.map +0 -1
  110. package/dist/infra/lib/__tests__/ComponentRegistry.test.js +0 -99
  111. package/dist/infra/lib/__tests__/ComponentRegistry.test.js.map +0 -1
  112. package/dist/infra/lib/__tests__/DecisionTracer.test.d.ts +0 -2
  113. package/dist/infra/lib/__tests__/DecisionTracer.test.d.ts.map +0 -1
  114. package/dist/infra/lib/__tests__/DecisionTracer.test.js +0 -274
  115. package/dist/infra/lib/__tests__/DecisionTracer.test.js.map +0 -1
  116. package/dist/infra/lib/__tests__/InteractiveCheckpoint.test.d.ts +0 -2
  117. package/dist/infra/lib/__tests__/InteractiveCheckpoint.test.d.ts.map +0 -1
  118. package/dist/infra/lib/__tests__/InteractiveCheckpoint.test.js +0 -350
  119. package/dist/infra/lib/__tests__/InteractiveCheckpoint.test.js.map +0 -1
  120. package/dist/infra/lib/__tests__/LoopBreaker.test.d.ts +0 -2
  121. package/dist/infra/lib/__tests__/LoopBreaker.test.d.ts.map +0 -1
  122. package/dist/infra/lib/__tests__/LoopBreaker.test.js +0 -340
  123. package/dist/infra/lib/__tests__/LoopBreaker.test.js.map +0 -1
  124. package/dist/infra/lib/__tests__/VerificationLoop.test.d.ts +0 -2
  125. package/dist/infra/lib/__tests__/VerificationLoop.test.d.ts.map +0 -1
  126. package/dist/infra/lib/__tests__/VerificationLoop.test.js +0 -486
  127. package/dist/infra/lib/__tests__/VerificationLoop.test.js.map +0 -1
  128. package/dist/infra/lib/config/GlobalConfigManager.test.d.ts +0 -2
  129. package/dist/infra/lib/config/GlobalConfigManager.test.d.ts.map +0 -1
  130. package/dist/infra/lib/config/GlobalConfigManager.test.js +0 -9
  131. package/dist/infra/lib/config/GlobalConfigManager.test.js.map +0 -1
  132. package/dist/infra/lib/embedding/__tests__/EmbeddingProvider.test.d.ts +0 -2
  133. package/dist/infra/lib/embedding/__tests__/EmbeddingProvider.test.d.ts.map +0 -1
  134. package/dist/infra/lib/embedding/__tests__/EmbeddingProvider.test.js +0 -43
  135. package/dist/infra/lib/embedding/__tests__/EmbeddingProvider.test.js.map +0 -1
  136. package/dist/infra/lib/embedding/__tests__/VectorStore.test.d.ts +0 -2
  137. package/dist/infra/lib/embedding/__tests__/VectorStore.test.d.ts.map +0 -1
  138. package/dist/infra/lib/embedding/__tests__/VectorStore.test.js +0 -94
  139. package/dist/infra/lib/embedding/__tests__/VectorStore.test.js.map +0 -1
  140. package/dist/infra/lib/embedding/__tests__/cosine.test.d.ts +0 -2
  141. package/dist/infra/lib/embedding/__tests__/cosine.test.d.ts.map +0 -1
  142. package/dist/infra/lib/embedding/__tests__/cosine.test.js +0 -73
  143. package/dist/infra/lib/embedding/__tests__/cosine.test.js.map +0 -1
  144. package/dist/infra/lib/evolution/__tests__/deprecation.test.d.ts +0 -2
  145. package/dist/infra/lib/evolution/__tests__/deprecation.test.d.ts.map +0 -1
  146. package/dist/infra/lib/evolution/__tests__/deprecation.test.js +0 -251
  147. package/dist/infra/lib/evolution/__tests__/deprecation.test.js.map +0 -1
  148. package/dist/infra/lib/evolution/__tests__/eval.test.d.ts +0 -2
  149. package/dist/infra/lib/evolution/__tests__/eval.test.d.ts.map +0 -1
  150. package/dist/infra/lib/evolution/__tests__/eval.test.js +0 -603
  151. package/dist/infra/lib/evolution/__tests__/eval.test.js.map +0 -1
  152. package/dist/infra/lib/evolution/__tests__/generation.test.d.ts +0 -2
  153. package/dist/infra/lib/evolution/__tests__/generation.test.d.ts.map +0 -1
  154. package/dist/infra/lib/evolution/__tests__/generation.test.js +0 -396
  155. package/dist/infra/lib/evolution/__tests__/generation.test.js.map +0 -1
  156. package/dist/infra/lib/evolution/__tests__/insight.test.d.ts +0 -2
  157. package/dist/infra/lib/evolution/__tests__/insight.test.d.ts.map +0 -1
  158. package/dist/infra/lib/evolution/__tests__/insight.test.js +0 -204
  159. package/dist/infra/lib/evolution/__tests__/insight.test.js.map +0 -1
  160. package/dist/infra/lib/evolution/__tests__/integration.test.d.ts +0 -2
  161. package/dist/infra/lib/evolution/__tests__/integration.test.d.ts.map +0 -1
  162. package/dist/infra/lib/evolution/__tests__/integration.test.js +0 -206
  163. package/dist/infra/lib/evolution/__tests__/integration.test.js.map +0 -1
  164. package/dist/infra/lib/evolution/__tests__/lifecycle.test.d.ts +0 -2
  165. package/dist/infra/lib/evolution/__tests__/lifecycle.test.d.ts.map +0 -1
  166. package/dist/infra/lib/evolution/__tests__/lifecycle.test.js +0 -350
  167. package/dist/infra/lib/evolution/__tests__/lifecycle.test.js.map +0 -1
  168. package/dist/infra/lib/evolution/__tests__/parity.test.d.ts +0 -2
  169. package/dist/infra/lib/evolution/__tests__/parity.test.d.ts.map +0 -1
  170. package/dist/infra/lib/evolution/__tests__/parity.test.js +0 -319
  171. package/dist/infra/lib/evolution/__tests__/parity.test.js.map +0 -1
  172. package/dist/infra/lib/llm/auth/AuthProfileManager.test.d.ts +0 -5
  173. package/dist/infra/lib/llm/auth/AuthProfileManager.test.d.ts.map +0 -1
  174. package/dist/infra/lib/llm/auth/AuthProfileManager.test.js +0 -168
  175. package/dist/infra/lib/llm/auth/AuthProfileManager.test.js.map +0 -1
  176. package/dist/infra/lib/llm/auth/TokenRefresher.test.d.ts +0 -2
  177. package/dist/infra/lib/llm/auth/TokenRefresher.test.d.ts.map +0 -1
  178. package/dist/infra/lib/llm/auth/TokenRefresher.test.js +0 -120
  179. package/dist/infra/lib/llm/auth/TokenRefresher.test.js.map +0 -1
  180. package/dist/infra/lib/llm/timeout.test.d.ts +0 -2
  181. package/dist/infra/lib/llm/timeout.test.d.ts.map +0 -1
  182. package/dist/infra/lib/llm/timeout.test.js +0 -50
  183. package/dist/infra/lib/llm/timeout.test.js.map +0 -1
  184. package/dist/infra/lib/llm-availability.test.d.ts +0 -2
  185. package/dist/infra/lib/llm-availability.test.d.ts.map +0 -1
  186. package/dist/infra/lib/llm-availability.test.js +0 -37
  187. package/dist/infra/lib/llm-availability.test.js.map +0 -1
  188. package/dist/infra/lib/memory/KnowledgeGraph.test.d.ts +0 -2
  189. package/dist/infra/lib/memory/KnowledgeGraph.test.d.ts.map +0 -1
  190. package/dist/infra/lib/memory/KnowledgeGraph.test.js +0 -189
  191. package/dist/infra/lib/memory/KnowledgeGraph.test.js.map +0 -1
  192. package/dist/infra/lib/memory/MemorySearch.test.d.ts +0 -2
  193. package/dist/infra/lib/memory/MemorySearch.test.d.ts.map +0 -1
  194. package/dist/infra/lib/memory/MemorySearch.test.js +0 -149
  195. package/dist/infra/lib/memory/MemorySearch.test.js.map +0 -1
  196. package/dist/infra/lib/memory/MemoryStorage.test.d.ts +0 -2
  197. package/dist/infra/lib/memory/MemoryStorage.test.d.ts.map +0 -1
  198. package/dist/infra/lib/memory/MemoryStorage.test.js +0 -198
  199. package/dist/infra/lib/memory/MemoryStorage.test.js.map +0 -1
  200. package/dist/infra/lib/memory/SessionRAGRetriever.test.d.ts +0 -2
  201. package/dist/infra/lib/memory/SessionRAGRetriever.test.d.ts.map +0 -1
  202. package/dist/infra/lib/memory/SessionRAGRetriever.test.js +0 -180
  203. package/dist/infra/lib/memory/SessionRAGRetriever.test.js.map +0 -1
  204. package/dist/infra/lib/memory/SessionRAGStore.test.d.ts +0 -2
  205. package/dist/infra/lib/memory/SessionRAGStore.test.d.ts.map +0 -1
  206. package/dist/infra/lib/memory/SessionRAGStore.test.js +0 -326
  207. package/dist/infra/lib/memory/SessionRAGStore.test.js.map +0 -1
  208. package/dist/infra/lib/memory/__tests__/InMemoryStorage.test.d.ts +0 -2
  209. package/dist/infra/lib/memory/__tests__/InMemoryStorage.test.d.ts.map +0 -1
  210. package/dist/infra/lib/memory/__tests__/InMemoryStorage.test.js +0 -114
  211. package/dist/infra/lib/memory/__tests__/InMemoryStorage.test.js.map +0 -1
  212. package/dist/infra/lib/memory/__tests__/ReflectionStore.test.d.ts +0 -2
  213. package/dist/infra/lib/memory/__tests__/ReflectionStore.test.d.ts.map +0 -1
  214. package/dist/infra/lib/memory/__tests__/ReflectionStore.test.js +0 -196
  215. package/dist/infra/lib/memory/__tests__/ReflectionStore.test.js.map +0 -1
  216. package/dist/infra/lib/memory/index.test.d.ts +0 -2
  217. package/dist/infra/lib/memory/index.test.d.ts.map +0 -1
  218. package/dist/infra/lib/memory/index.test.js +0 -23
  219. package/dist/infra/lib/memory/index.test.js.map +0 -1
  220. package/dist/infra/lib/telemetry/SkillTelemetry.test.d.ts +0 -2
  221. package/dist/infra/lib/telemetry/SkillTelemetry.test.d.ts.map +0 -1
  222. package/dist/infra/lib/telemetry/SkillTelemetry.test.js +0 -124
  223. package/dist/infra/lib/telemetry/SkillTelemetry.test.js.map +0 -1
  224. package/dist/infra/lib/ui-ux/Bm25Engine.test.d.ts +0 -2
  225. package/dist/infra/lib/ui-ux/Bm25Engine.test.d.ts.map +0 -1
  226. package/dist/infra/lib/ui-ux/Bm25Engine.test.js +0 -159
  227. package/dist/infra/lib/ui-ux/Bm25Engine.test.js.map +0 -1
  228. package/dist/infra/lib/ui-ux/CsvDataLoader.test.d.ts +0 -2
  229. package/dist/infra/lib/ui-ux/CsvDataLoader.test.d.ts.map +0 -1
  230. package/dist/infra/lib/ui-ux/CsvDataLoader.test.js +0 -141
  231. package/dist/infra/lib/ui-ux/CsvDataLoader.test.js.map +0 -1
  232. package/dist/infra/lib/ui-ux/DesignSystemGenerator.test.d.ts +0 -2
  233. package/dist/infra/lib/ui-ux/DesignSystemGenerator.test.d.ts.map +0 -1
  234. package/dist/infra/lib/ui-ux/DesignSystemGenerator.test.js +0 -370
  235. package/dist/infra/lib/ui-ux/DesignSystemGenerator.test.js.map +0 -1
  236. package/dist/infra/lib/ui-ux/SearchService.test.d.ts +0 -2
  237. package/dist/infra/lib/ui-ux/SearchService.test.d.ts.map +0 -1
  238. package/dist/infra/lib/ui-ux/SearchService.test.js +0 -215
  239. package/dist/infra/lib/ui-ux/SearchService.test.js.map +0 -1
  240. package/dist/test-helpers/index.d.ts +0 -36
  241. package/dist/test-helpers/index.d.ts.map +0 -1
  242. package/dist/test-helpers/index.js +0 -85
  243. package/dist/test-helpers/index.js.map +0 -1
  244. package/dist/test-helpers/index.test.d.ts +0 -2
  245. package/dist/test-helpers/index.test.d.ts.map +0 -1
  246. package/dist/test-helpers/index.test.js +0 -92
  247. package/dist/test-helpers/index.test.js.map +0 -1
  248. package/dist/tests/curated-release-notes.test.d.ts +0 -2
  249. package/dist/tests/curated-release-notes.test.d.ts.map +0 -1
  250. package/dist/tests/curated-release-notes.test.js +0 -160
  251. package/dist/tests/curated-release-notes.test.js.map +0 -1
  252. package/dist/tools/convention/analyzeComplexity.test.d.ts +0 -2
  253. package/dist/tools/convention/analyzeComplexity.test.d.ts.map +0 -1
  254. package/dist/tools/convention/analyzeComplexity.test.js +0 -207
  255. package/dist/tools/convention/analyzeComplexity.test.js.map +0 -1
  256. package/dist/tools/convention/validateCodeQuality.test.d.ts +0 -2
  257. package/dist/tools/convention/validateCodeQuality.test.d.ts.map +0 -1
  258. package/dist/tools/convention/validateCodeQuality.test.js +0 -230
  259. package/dist/tools/convention/validateCodeQuality.test.js.map +0 -1
  260. package/dist/tools/loop/validateLoopDefinition.test.d.ts +0 -14
  261. package/dist/tools/loop/validateLoopDefinition.test.d.ts.map +0 -1
  262. package/dist/tools/loop/validateLoopDefinition.test.js +0 -229
  263. package/dist/tools/loop/validateLoopDefinition.test.js.map +0 -1
  264. package/dist/tools/spec/executionPacket.test.d.ts +0 -2
  265. package/dist/tools/spec/executionPacket.test.d.ts.map +0 -1
  266. package/dist/tools/spec/executionPacket.test.js +0 -374
  267. package/dist/tools/spec/executionPacket.test.js.map +0 -1
  268. package/dist/tools/spec/prdParser.test.d.ts +0 -6
  269. package/dist/tools/spec/prdParser.test.d.ts.map +0 -1
  270. package/dist/tools/spec/prdParser.test.js +0 -332
  271. package/dist/tools/spec/prdParser.test.js.map +0 -1
  272. package/dist/tools/spec/specGenerator.test.d.ts +0 -2
  273. package/dist/tools/spec/specGenerator.test.d.ts.map +0 -1
  274. package/dist/tools/spec/specGenerator.test.js +0 -130
  275. package/dist/tools/spec/specGenerator.test.js.map +0 -1
  276. package/dist/tools/spec/traceabilityMatrix.path-resolution.test.d.ts +0 -10
  277. package/dist/tools/spec/traceabilityMatrix.path-resolution.test.d.ts.map +0 -1
  278. package/dist/tools/spec/traceabilityMatrix.path-resolution.test.js +0 -120
  279. package/dist/tools/spec/traceabilityMatrix.path-resolution.test.js.map +0 -1
  280. package/dist/tools/spec/traceabilityMatrix.test.d.ts +0 -6
  281. package/dist/tools/spec/traceabilityMatrix.test.d.ts.map +0 -1
  282. package/dist/tools/spec/traceabilityMatrix.test.js +0 -359
  283. package/dist/tools/spec/traceabilityMatrix.test.js.map +0 -1
  284. package/hooks/scripts/__tests__/.vibe/command-log.txt +0 -3
  285. package/hooks/scripts/__tests__/auto-test-debounce.test.js +0 -145
  286. package/hooks/scripts/__tests__/clone-behaviors.test.js +0 -152
  287. package/hooks/scripts/__tests__/clone-merge-responsive.test.js +0 -106
  288. package/hooks/scripts/__tests__/code-check-detectors.test.js +0 -213
  289. package/hooks/scripts/__tests__/code-check-memory-write.test.js +0 -81
  290. package/hooks/scripts/__tests__/codex-hook-adapter.test.js +0 -64
  291. package/hooks/scripts/__tests__/curation-index.test.js +0 -157
  292. package/hooks/scripts/__tests__/dispatcher-inprocess.test.js +0 -99
  293. package/hooks/scripts/__tests__/figma-extract.test.js +0 -338
  294. package/hooks/scripts/__tests__/hook-payload.test.js +0 -29
  295. package/hooks/scripts/__tests__/llm-orchestrate-antigravity.test.js +0 -66
  296. package/hooks/scripts/__tests__/loop-ledger.test.js +0 -321
  297. package/hooks/scripts/__tests__/post-edit-dispatcher.test.js +0 -139
  298. package/hooks/scripts/__tests__/pre-tool-guard.test.js +0 -523
  299. package/hooks/scripts/__tests__/recipe-extractor.test.js +0 -244
  300. package/hooks/scripts/__tests__/run-ledger-verify-required.test.js +0 -151
  301. package/hooks/scripts/__tests__/run-ledger.test.js +0 -481
  302. package/hooks/scripts/__tests__/scope-from-spec.test.js +0 -215
  303. package/hooks/scripts/__tests__/sentinel-guard.test.js +0 -263
  304. package/hooks/scripts/__tests__/step-counter.test.js +0 -459
  305. package/hooks/scripts/__tests__/utils-npm-root.test.js +0 -99
@@ -1,157 +0,0 @@
1
- /**
2
- * curation-index.js — Phase 3 인덱스 로더 테스트
3
- *
4
- * session-start.js 가 다음 세션에 prepend 할 1줄 요약을 만드는 모듈.
5
- * 본격 YAML parser 의존 없이 우리가 작성한 frontmatter 형식만 처리한다.
6
- */
7
- import { describe, it, expect, beforeEach } from 'vitest';
8
- import fs from 'fs';
9
- import os from 'os';
10
- import path from 'path';
11
- import { loadCurationIndex, _internal } from '../lib/curation-index.js';
12
-
13
- const { parseFrontmatter } = _internal;
14
-
15
- function makeTmpProject() {
16
- return fs.mkdtempSync(path.join(os.tmpdir(), 'vibe-curation-index-'));
17
- }
18
-
19
- function writeRecipe(projectDir, filename, frontmatter, body = 'body text') {
20
- const dir = path.join(projectDir, '.vibe', 'recipes');
21
- fs.mkdirSync(dir, { recursive: true });
22
- const fm = Object.entries(frontmatter)
23
- .map(([k, v]) => typeof v === 'string' ? `${k}: "${v}"` : `${k}: ${v}`)
24
- .join('\n');
25
- fs.writeFileSync(path.join(dir, filename), `---\n${fm}\n---\n\n${body}\n`);
26
- }
27
-
28
- function writeAntiPattern(projectDir, filename, frontmatter, body = 'body') {
29
- const dir = path.join(projectDir, '.vibe', 'anti-patterns');
30
- fs.mkdirSync(dir, { recursive: true });
31
- const fm = Object.entries(frontmatter)
32
- .map(([k, v]) => typeof v === 'string' ? `${k}: "${v}"` : `${k}: ${v}`)
33
- .join('\n');
34
- fs.writeFileSync(path.join(dir, filename), `---\n${fm}\n---\n\n${body}\n`);
35
- }
36
-
37
- describe('parseFrontmatter', () => {
38
- it('단순 key-value 파싱', () => {
39
- const input = `---\nslug: foo\ntype: recipe\n---\nbody`;
40
- expect(parseFrontmatter(input)).toEqual({ slug: 'foo', type: 'recipe' });
41
- });
42
-
43
- it('따옴표로 감싼 값', () => {
44
- const input = `---\nrecipe: "use claude --model"\n---\n`;
45
- expect(parseFrontmatter(input).recipe).toBe('use claude --model');
46
- });
47
-
48
- it('이스케이프된 따옴표', () => {
49
- const input = `---\nmsg: "say \\"hi\\""\n---\n`;
50
- expect(parseFrontmatter(input).msg).toBe('say "hi"');
51
- });
52
-
53
- it('frontmatter 가 없으면 null', () => {
54
- expect(parseFrontmatter('no frontmatter here')).toBeNull();
55
- });
56
-
57
- it('알 수 없는 라인 무시', () => {
58
- const input = `---\nslug: foo\n# comment line\nrandom garbage\ntype: recipe\n---\n`;
59
- expect(parseFrontmatter(input)).toEqual({ slug: 'foo', type: 'recipe' });
60
- });
61
- });
62
-
63
- describe('loadCurationIndex', () => {
64
- let projectDir;
65
-
66
- beforeEach(() => {
67
- projectDir = makeTmpProject();
68
- });
69
-
70
- it('빈 .vibe/ 면 빈 인덱스', () => {
71
- const idx = loadCurationIndex(projectDir);
72
- expect(idx.recipes).toEqual([]);
73
- expect(idx.antiPatterns).toEqual([]);
74
- });
75
-
76
- it('recipe 1개 로드', () => {
77
- writeRecipe(projectDir, 'a.md', {
78
- slug: 'login__20260507-100000',
79
- type: 'recipe',
80
- 'symptom-context': 'login flow',
81
- recipe: 'pin model and retry transient',
82
- created: '2026-05-07',
83
- });
84
- const idx = loadCurationIndex(projectDir);
85
- expect(idx.recipes).toHaveLength(1);
86
- expect(idx.recipes[0]).toEqual({
87
- slug: 'login__20260507-100000',
88
- summary: 'pin model and retry transient',
89
- });
90
- });
91
-
92
- it('recipe 정렬: created 내림차순', () => {
93
- writeRecipe(projectDir, 'old.md', { slug: 'old', recipe: 'old', created: '2026-01-01' });
94
- writeRecipe(projectDir, 'mid.md', { slug: 'mid', recipe: 'mid', created: '2026-03-01' });
95
- writeRecipe(projectDir, 'new.md', { slug: 'new', recipe: 'new', created: '2026-05-01' });
96
- const idx = loadCurationIndex(projectDir);
97
- expect(idx.recipes.map((r) => r.slug)).toEqual(['new', 'mid', 'old']);
98
- });
99
-
100
- it('limit 적용', () => {
101
- for (let i = 0; i < 10; i++) {
102
- writeRecipe(projectDir, `r${i}.md`, {
103
- slug: `r${i}`,
104
- recipe: `recipe ${i}`,
105
- created: `2026-05-${String(i + 1).padStart(2, '0')}`,
106
- });
107
- }
108
- const idx = loadCurationIndex(projectDir, { recipeLimit: 3 });
109
- expect(idx.recipes).toHaveLength(3);
110
- expect(idx.recipes[0].slug).toBe('r9');
111
- });
112
-
113
- it('anti-pattern 로드 — tag + suggested-stop', () => {
114
- writeAntiPattern(projectDir, 'a.md', {
115
- slug: 'nullability__src__20260507',
116
- type: 'anti-pattern',
117
- 'root-cause-tag': 'nullability',
118
- 'trigger-signature': '(file=src/x.ts, category=nullability)',
119
- 'suggested-stop': '같은 위치 null 처리 반복 — 타입 가드 필요',
120
- created: '2026-05-07',
121
- });
122
- const idx = loadCurationIndex(projectDir);
123
- expect(idx.antiPatterns).toHaveLength(1);
124
- expect(idx.antiPatterns[0].tag).toBe('nullability');
125
- expect(idx.antiPatterns[0].summary).toBe('같은 위치 null 처리 반복 — 타입 가드 필요');
126
- });
127
-
128
- it('frontmatter 손상 파일은 스킵', () => {
129
- writeRecipe(projectDir, 'good.md', { slug: 'good', recipe: 'works', created: '2026-05-07' });
130
- fs.writeFileSync(path.join(projectDir, '.vibe', 'recipes', 'broken.md'), 'no frontmatter');
131
- fs.writeFileSync(path.join(projectDir, '.vibe', 'recipes', 'no-slug.md'), '---\ntype: recipe\n---\n');
132
- const idx = loadCurationIndex(projectDir);
133
- expect(idx.recipes).toHaveLength(1);
134
- expect(idx.recipes[0].slug).toBe('good');
135
- });
136
-
137
- it('README.md, _cluster*.md 등 메타 파일 제외', () => {
138
- writeRecipe(projectDir, 'r.md', { slug: 'r', recipe: 'good', created: '2026-05-07' });
139
- fs.writeFileSync(path.join(projectDir, '.vibe', 'recipes', 'README.md'), '---\nslug: should-skip\n---\n');
140
- fs.writeFileSync(path.join(projectDir, '.vibe', 'recipes', '_cluster-x.md'), '---\nslug: should-skip\n---\n');
141
- const idx = loadCurationIndex(projectDir);
142
- expect(idx.recipes.map((r) => r.slug)).toEqual(['r']);
143
- });
144
-
145
- it('recipe + anti-pattern 동시 로드 (독립)', () => {
146
- writeRecipe(projectDir, 'r.md', { slug: 'r', recipe: 'do this', created: '2026-05-07' });
147
- writeAntiPattern(projectDir, 'a.md', {
148
- slug: 'a',
149
- 'root-cause-tag': 'auth',
150
- 'suggested-stop': 'check tokens',
151
- created: '2026-05-07',
152
- });
153
- const idx = loadCurationIndex(projectDir);
154
- expect(idx.recipes).toHaveLength(1);
155
- expect(idx.antiPatterns).toHaveLength(1);
156
- });
157
- });
@@ -1,99 +0,0 @@
1
- /**
2
- * in-process 디스패처 외부 계약 테스트
3
- *
4
- * SPEC: .vibe/specs/hook-dispatcher-inprocess.md
5
- * 디스패처를 Claude Code와 동일하게 CLI로 spawn하여 검증한다 —
6
- * 내부 구현(in-process)이 아니라 외부 계약(exit code, stdout 주입)이 대상.
7
- */
8
- import { describe, it, expect } from 'vitest';
9
- import { execFileSync } from 'child_process';
10
- import path from 'path';
11
- import { fileURLToPath } from 'url';
12
-
13
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
14
- const PRE_DISPATCHER = path.resolve(__dirname, '..', 'pre-tool-dispatcher.js');
15
- const POST_DISPATCHER = path.resolve(__dirname, '..', 'post-edit-dispatcher.js');
16
-
17
- /**
18
- * 디스패처를 stdin 페이로드와 함께 실행. { stdout, exitCode } 반환.
19
- */
20
- function runDispatcher(script, args, payload) {
21
- const input = typeof payload === 'string' ? payload : JSON.stringify(payload);
22
- try {
23
- const stdout = execFileSync('node', [script, ...args], {
24
- encoding: 'utf-8',
25
- input,
26
- timeout: 15000,
27
- // scope.json이 없는 격리된 cwd에서 실행 (scope-guard no-op 보장)
28
- env: { ...process.env, CLAUDE_PROJECT_DIR: __dirname },
29
- });
30
- return { stdout: stdout.trim(), exitCode: 0 };
31
- } catch (err) {
32
- return { stdout: (err.stdout || '').trim(), exitCode: err.status };
33
- }
34
- }
35
-
36
- describe('pre-tool-dispatcher (in-process)', () => {
37
- it('AC-1: 위험 Bash 명령은 exit 2로 deny', () => {
38
- const { exitCode } = runDispatcher(PRE_DISPATCHER, ['Bash'], {
39
- tool_name: 'Bash',
40
- tool_input: { command: 'rm -rf /' },
41
- });
42
- expect(exitCode).toBe(2);
43
- });
44
-
45
- it('AC-2: sentinel 경로 Edit은 exit 2 + stdout에 block JSON 주입', () => {
46
- // 실제 sentinel 경로: src/infra/lib/evolution/ (autonomy/ 는 팬텀 경로였음)
47
- const { stdout, exitCode } = runDispatcher(PRE_DISPATCHER, ['Edit'], {
48
- tool_name: 'Edit',
49
- tool_input: { file_path: 'src/infra/lib/evolution/GuardAnalyzer.ts', old_string: 'a', new_string: 'b' },
50
- });
51
- expect(exitCode).toBe(2);
52
- expect(stdout).toContain('"decision":"block"');
53
- });
54
-
55
- it('AC-3: 안전한 Bash 명령은 exit 0', () => {
56
- const { exitCode } = runDispatcher(PRE_DISPATCHER, ['Bash'], {
57
- tool_name: 'Bash',
58
- tool_input: { command: 'ls -la' },
59
- });
60
- expect(exitCode).toBe(0);
61
- });
62
-
63
- it('AC-3: 안전한 Edit은 exit 0 (scope.json 없음 → scope-guard no-op)', () => {
64
- const { exitCode } = runDispatcher(PRE_DISPATCHER, ['Edit'], {
65
- tool_name: 'Edit',
66
- tool_input: { file_path: 'src/cli/index.ts', old_string: 'a', new_string: 'b' },
67
- });
68
- expect(exitCode).toBe(0);
69
- });
70
-
71
- it('AC-2 변형: 위험 명령이 sentinel 경로를 노리면 sentinel-guard가 차단', () => {
72
- // 실제 sentinel 경로: src/infra/lib/evolution/
73
- const { exitCode } = runDispatcher(PRE_DISPATCHER, ['Bash'], {
74
- tool_name: 'Bash',
75
- tool_input: { command: 'rm -rf src/infra/lib/evolution/' },
76
- });
77
- expect(exitCode).toBe(2);
78
- });
79
- });
80
-
81
- describe('post-edit-dispatcher (in-process)', () => {
82
- it('AC-4: 손상된 stdin(비JSON)에도 exit 0 (fail-open)', () => {
83
- const { exitCode } = runDispatcher(POST_DISPATCHER, [], 'not-json');
84
- expect(exitCode).toBe(0);
85
- });
86
-
87
- it('AC-4: 빈 페이로드에도 exit 0', () => {
88
- const { exitCode } = runDispatcher(POST_DISPATCHER, [], {});
89
- expect(exitCode).toBe(0);
90
- });
91
-
92
- it('존재하지 않는 파일 경로 페이로드에도 exit 0 (step 내부 격리)', () => {
93
- const { exitCode } = runDispatcher(POST_DISPATCHER, [], {
94
- tool_name: 'Edit',
95
- tool_input: { file_path: '/nonexistent/__vibe_test__.xyz' },
96
- });
97
- expect(exitCode).toBe(0);
98
- });
99
- });
@@ -1,338 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import {
3
- walk,
4
- isDesignTextNode,
5
- hasTextDescendantRaw,
6
- hasRepeatingInstancesRaw,
7
- vectorChildCountRaw,
8
- shouldRenderImageAsBg,
9
- resolveImageFills,
10
- toHTML,
11
- } from '../figma-extract.js';
12
-
13
- // ─── Fixture helpers ────────────────────────────────────────────────
14
-
15
- const bbox = (w = 100, h = 100, x = 0, y = 0) => ({ x, y, width: w, height: h });
16
-
17
- function textNode(overrides = {}) {
18
- return {
19
- id: 'text-1', name: 'heading', type: 'TEXT', characters: 'Hello',
20
- absoluteBoundingBox: bbox(200, 40),
21
- style: { fontFamily: 'Inter', fontSize: 24, fontWeight: 600 },
22
- fills: [{ type: 'SOLID', color: { r: 0, g: 0, b: 0, a: 1 } }],
23
- ...overrides,
24
- };
25
- }
26
-
27
- function frame(name, children = [], overrides = {}) {
28
- return {
29
- id: `frame-${name}`, name, type: 'FRAME',
30
- absoluteBoundingBox: bbox(800, 400),
31
- children,
32
- ...overrides,
33
- };
34
- }
35
-
36
- function imageFillNode(name, overrides = {}) {
37
- return {
38
- id: `img-${name}`, name, type: 'RECTANGLE',
39
- absoluteBoundingBox: bbox(400, 300),
40
- fills: [{ type: 'IMAGE', imageRef: 'ref-' + name, scaleMode: 'FILL' }],
41
- ...overrides,
42
- };
43
- }
44
-
45
- // ─── isDesignTextNode (D1-D3) ───────────────────────────────────────
46
-
47
- describe('isDesignTextNode', () => {
48
- it('returns false for plain SOLID-filled text', () => {
49
- expect(isDesignTextNode(textNode())).toBe(false);
50
- });
51
-
52
- it('D1: detects 2+ visible fills', () => {
53
- const n = textNode({
54
- fills: [
55
- { type: 'SOLID', color: { r: 0, g: 0, b: 0, a: 1 } },
56
- { type: 'SOLID', color: { r: 1, g: 0, b: 0, a: 0.5 } },
57
- ],
58
- });
59
- expect(isDesignTextNode(n)).toBe(true);
60
- });
61
-
62
- it('D3: detects GRADIENT_LINEAR fill', () => {
63
- const n = textNode({
64
- fills: [{
65
- type: 'GRADIENT_LINEAR',
66
- gradientStops: [
67
- { position: 0, color: { r: 1, g: 0, b: 0, a: 1 } },
68
- { position: 1, color: { r: 0, g: 0, b: 1, a: 1 } },
69
- ],
70
- gradientHandlePositions: [{ x: 0, y: 0 }, { x: 1, y: 0 }],
71
- }],
72
- });
73
- expect(isDesignTextNode(n)).toBe(true);
74
- });
75
-
76
- it('D2: detects DROP_SHADOW effect', () => {
77
- const n = textNode({ effects: [{ type: 'DROP_SHADOW', visible: true, radius: 4 }] });
78
- expect(isDesignTextNode(n)).toBe(true);
79
- });
80
-
81
- it('D2: detects visible stroke', () => {
82
- const n = textNode({ strokes: [{ type: 'SOLID', visible: true, color: { r: 1, g: 1, b: 1, a: 1 } }] });
83
- expect(isDesignTextNode(n)).toBe(true);
84
- });
85
-
86
- it('ignores invisible fills', () => {
87
- const n = textNode({
88
- fills: [
89
- { type: 'SOLID', color: { r: 0, g: 0, b: 0, a: 1 } },
90
- { type: 'GRADIENT_LINEAR', visible: false },
91
- ],
92
- });
93
- expect(isDesignTextNode(n)).toBe(false);
94
- });
95
-
96
- it('returns false for non-TEXT nodes', () => {
97
- expect(isDesignTextNode({ type: 'FRAME', fills: [] })).toBe(false);
98
- });
99
- });
100
-
101
- // ─── hasTextDescendantRaw (Q1) ──────────────────────────────────────
102
-
103
- describe('hasTextDescendantRaw', () => {
104
- it('true when node itself is non-empty TEXT', () => {
105
- expect(hasTextDescendantRaw(textNode())).toBe(true);
106
- });
107
-
108
- it('false when TEXT node has only whitespace', () => {
109
- expect(hasTextDescendantRaw(textNode({ characters: ' ' }))).toBe(false);
110
- });
111
-
112
- it('true when any descendant is TEXT', () => {
113
- const tree = frame('BG', [
114
- { id: 'g', name: 'group', type: 'GROUP', children: [textNode()] },
115
- ]);
116
- expect(hasTextDescendantRaw(tree)).toBe(true);
117
- });
118
-
119
- it('false for purely decorative VECTOR/RECTANGLE subtree', () => {
120
- const tree = frame('BG', [
121
- { id: 'v1', name: 'sparkle', type: 'VECTOR', children: [] },
122
- { id: 'v2', name: 'glow', type: 'RECTANGLE', children: [] },
123
- ]);
124
- expect(hasTextDescendantRaw(tree)).toBe(false);
125
- });
126
- });
127
-
128
- // ─── hasRepeatingInstancesRaw (Q2) ──────────────────────────────────
129
-
130
- describe('hasRepeatingInstancesRaw', () => {
131
- it('true for 2+ INSTANCE children with same componentId', () => {
132
- const tree = frame('cards', [
133
- { id: 'i1', name: 'Card 1', type: 'INSTANCE', componentId: 'C:1', children: [] },
134
- { id: 'i2', name: 'Card 2', type: 'INSTANCE', componentId: 'C:1', children: [] },
135
- ]);
136
- expect(hasRepeatingInstancesRaw(tree)).toBe(true);
137
- });
138
-
139
- it('true for 2+ INSTANCE children with same name stem (trailing digits stripped)', () => {
140
- const tree = frame('list', [
141
- { id: 'i1', name: 'item 1', type: 'INSTANCE', children: [] },
142
- { id: 'i2', name: 'item 2', type: 'INSTANCE', children: [] },
143
- ]);
144
- expect(hasRepeatingInstancesRaw(tree)).toBe(true);
145
- });
146
-
147
- it('false for single INSTANCE', () => {
148
- const tree = frame('solo', [
149
- { id: 'i1', name: 'hero', type: 'INSTANCE', children: [] },
150
- ]);
151
- expect(hasRepeatingInstancesRaw(tree)).toBe(false);
152
- });
153
-
154
- it('false for mixed non-repeating INSTANCEs', () => {
155
- const tree = frame('mix', [
156
- { id: 'i1', name: 'hero', type: 'INSTANCE', componentId: 'C:A', children: [] },
157
- { id: 'i2', name: 'footer', type: 'INSTANCE', componentId: 'C:B', children: [] },
158
- ]);
159
- expect(hasRepeatingInstancesRaw(tree)).toBe(false);
160
- });
161
- });
162
-
163
- // ─── vectorChildCountRaw (D4 helper) ────────────────────────────────
164
-
165
- describe('vectorChildCountRaw', () => {
166
- it('counts direct VECTOR-family children', () => {
167
- const tree = frame('parent', [
168
- { id: 'v1', type: 'VECTOR', children: [] },
169
- { id: 'v2', type: 'LINE', children: [] },
170
- { id: 'v3', type: 'BOOLEAN_OPERATION', children: [] },
171
- { id: 'r1', type: 'RECTANGLE', children: [] },
172
- ]);
173
- expect(vectorChildCountRaw(tree)).toBe(3);
174
- });
175
-
176
- it('returns 0 for leaf', () => {
177
- expect(vectorChildCountRaw({ id: 'x', type: 'FRAME' })).toBe(0);
178
- });
179
- });
180
-
181
- // ─── walk() integration: metadata propagates into tree ──────────────
182
-
183
- describe('walk() metadata', () => {
184
- it('emits hasTextChildren on ancestors of TEXT nodes', () => {
185
- const raw = frame('section', [
186
- frame('BG', [textNode({ name: 'title' })]),
187
- ]);
188
- const tree = walk(raw);
189
- expect(tree.hasTextChildren).toBe(true);
190
- expect(tree.children[0].hasTextChildren).toBe(true); // BG frame too
191
- });
192
-
193
- it('omits hasTextChildren for pure decoration', () => {
194
- const raw = frame('section', [
195
- frame('BG', [
196
- { id: 'v1', name: 'glow', type: 'VECTOR', children: [] },
197
- ]),
198
- ]);
199
- const tree = walk(raw);
200
- expect(tree.hasTextChildren).toBeUndefined();
201
- });
202
-
203
- it('emits hasInstanceRepeat on parents of repeating INSTANCEs', () => {
204
- const raw = frame('list', [
205
- { id: 'c1', name: 'card 1', type: 'INSTANCE', componentId: 'C:1', children: [] },
206
- { id: 'c2', name: 'card 2', type: 'INSTANCE', componentId: 'C:1', children: [] },
207
- ]);
208
- const tree = walk(raw);
209
- expect(tree.hasInstanceRepeat).toBe(true);
210
- });
211
-
212
- it('emits isDesignText on gradient-filled TEXT', () => {
213
- const raw = textNode({
214
- fills: [{
215
- type: 'GRADIENT_LINEAR',
216
- gradientStops: [
217
- { position: 0, color: { r: 1, g: 0, b: 0, a: 1 } },
218
- { position: 1, color: { r: 0, g: 0, b: 1, a: 1 } },
219
- ],
220
- gradientHandlePositions: [{ x: 0, y: 0 }, { x: 1, y: 0 }],
221
- }],
222
- });
223
- const tree = walk(raw);
224
- expect(tree.isDesignText).toBe(true);
225
- });
226
- });
227
-
228
- // ─── shouldRenderImageAsBg ──────────────────────────────────────────
229
-
230
- describe('shouldRenderImageAsBg', () => {
231
- it('container with TEXT descendants → CSS bg', () => {
232
- const node = { name: 'hero', imageRef: 'r1', hasTextChildren: true, children: [{}] };
233
- expect(shouldRenderImageAsBg(node)).toBe(true);
234
- });
235
-
236
- it('container with repeating instances → CSS bg', () => {
237
- const node = { name: 'grid', imageRef: 'r1', hasInstanceRepeat: true, children: [{}, {}] };
238
- expect(shouldRenderImageAsBg(node)).toBe(true);
239
- });
240
-
241
- it('node named BG → CSS bg', () => {
242
- const node = { name: 'BG', imageRef: 'r1', children: [] };
243
- expect(shouldRenderImageAsBg(node)).toBe(true);
244
- });
245
-
246
- it('node named 배경 → CSS bg', () => {
247
- const node = { name: '배경', imageRef: 'r1', children: [] };
248
- expect(shouldRenderImageAsBg(node)).toBe(true);
249
- });
250
-
251
- it('container with non-vector children → CSS bg', () => {
252
- const node = {
253
- name: 'card', imageRef: 'r1',
254
- children: [{ type: 'FRAME' }],
255
- };
256
- expect(shouldRenderImageAsBg(node)).toBe(true);
257
- });
258
-
259
- it('leaf with image-like name → <img>', () => {
260
- const node = { name: 'photo-1', imageRef: 'r1', children: [] };
261
- expect(shouldRenderImageAsBg(node)).toBe(false);
262
- });
263
-
264
- it('leaf with generic name → <img> (default)', () => {
265
- const node = { name: 'Rectangle 5', imageRef: 'r1', children: [] };
266
- expect(shouldRenderImageAsBg(node)).toBe(false);
267
- });
268
-
269
- it('container with only VECTOR children still → CSS bg (parent is not a leaf image)', () => {
270
- const node = {
271
- name: 'hero',
272
- imageRef: 'r1',
273
- children: [{ type: 'VECTOR' }, { type: 'VECTOR' }],
274
- };
275
- expect(shouldRenderImageAsBg(node)).toBe(true);
276
- });
277
- });
278
-
279
- // ─── resolveImageFills + toHTML integration ─────────────────────────
280
-
281
- describe('resolveImageFills', () => {
282
- it('converts BG-named leaf with IMAGE fill to CSS bg (no <img>)', () => {
283
- const tree = walk(frame('section', [imageFillNode('BG')]));
284
- const imageMap = { 'ref-BG': 'images/bg.webp' };
285
- resolveImageFills(tree, imageMap);
286
- const bgChild = tree.children[0];
287
- expect(bgChild.imageRef).toBeUndefined();
288
- expect(bgChild.renderAsBg).toBe(true);
289
- expect(bgChild.css.backgroundImage).toBe("url('images/bg.webp')");
290
- expect(bgChild.css.backgroundSize).toBe('cover');
291
- });
292
-
293
- it('keeps <img> for leaf named like a photo', () => {
294
- const tree = walk(frame('section', [imageFillNode('photo-hero')]));
295
- const imageMap = { 'ref-photo-hero': 'images/photo.webp' };
296
- resolveImageFills(tree, imageMap);
297
- const leaf = tree.children[0];
298
- expect(leaf.imageRef).toBe('ref-photo-hero');
299
- expect(leaf.renderAsBg).toBeUndefined();
300
- });
301
-
302
- it('converts IMAGE-fill container with TEXT descendant to CSS bg', () => {
303
- const container = imageFillNode('card', { type: 'FRAME' });
304
- container.children = [textNode({ name: 'title' })];
305
- const tree = walk(frame('section', [container]));
306
- const imageMap = { 'ref-card': 'images/card.webp' };
307
- resolveImageFills(tree, imageMap);
308
- const card = tree.children[0];
309
- expect(card.imageRef).toBeUndefined();
310
- expect(card.renderAsBg).toBe(true);
311
- });
312
-
313
- it('uses contain for FIT scale mode', () => {
314
- const tree = walk(frame('s', [imageFillNode('BG', { fills: [{ type: 'IMAGE', imageRef: 'ref-BG', scaleMode: 'FIT' }] })]));
315
- const imageMap = { 'ref-BG': 'images/bg.webp' };
316
- resolveImageFills(tree, imageMap);
317
- expect(tree.children[0].css.backgroundSize).toBe('contain');
318
- });
319
- });
320
-
321
- describe('toHTML after resolveImageFills', () => {
322
- it('BG-named image node renders as <div>, not <img>', () => {
323
- const tree = walk(frame('section', [imageFillNode('BG')]));
324
- const imageMap = { 'ref-BG': 'images/bg.webp' };
325
- resolveImageFills(tree, imageMap);
326
- const html = toHTML(tree, '', imageMap);
327
- expect(html).not.toMatch(/<img[^>]+images\/bg\.webp/);
328
- expect(html).toMatch(/<div class="section-bg"/);
329
- });
330
-
331
- it('standalone photo leaf renders as <img>', () => {
332
- const tree = walk(frame('section', [imageFillNode('photo-hero')]));
333
- const imageMap = { 'ref-photo-hero': 'images/photo.webp' };
334
- resolveImageFills(tree, imageMap);
335
- const html = toHTML(tree, '', imageMap);
336
- expect(html).toMatch(/<img[^>]+images\/photo\.webp/);
337
- });
338
- });
@@ -1,29 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import {
3
- extractToolInput,
4
- extractToolName,
5
- normalizeHookPayload,
6
- } from '../hook-payload.js';
7
-
8
- describe('hook-payload', () => {
9
- it('normalizes stdin JSON payloads from native hook runners', () => {
10
- const payload = normalizeHookPayload(
11
- JSON.stringify({
12
- tool_name: 'Bash',
13
- tool_input: { command: 'npm test' },
14
- }),
15
- {},
16
- );
17
-
18
- expect(extractToolName(payload)).toBe('Bash');
19
- expect(extractToolInput(payload).command).toBe('npm test');
20
- });
21
-
22
- it('falls back to legacy TOOL_INPUT env for Claude-style scripts', () => {
23
- const payload = normalizeHookPayload('', {
24
- TOOL_INPUT: JSON.stringify({ command: 'npm run build' }),
25
- });
26
-
27
- expect(extractToolInput(payload).command).toBe('npm run build');
28
- });
29
- });
@@ -1,66 +0,0 @@
1
- import { describe, it, expect, afterEach } from 'vitest';
2
- import fs from 'fs';
3
- import os from 'os';
4
- import path from 'path';
5
- import { execFileSync } from 'child_process';
6
-
7
- describe('llm-orchestrate Antigravity provider', () => {
8
- const tempDirs = [];
9
-
10
- afterEach(() => {
11
- for (const dir of tempDirs.splice(0)) {
12
- fs.rmSync(dir, { recursive: true, force: true });
13
- }
14
- });
15
-
16
- // win32 skip: spawnCli가 cmd.exe /c 로 실행하는데 cmd.exe는 인자 내 LF에서 명령을
17
- // 절단하므로 멀티라인 프롬프트의 argv 전송 자체가 불가능하다 (프로덕션 결함 — codex/claude
18
- // 경로처럼 stdin 전송으로 전환할지는 agy CLI의 stdin 지원 확인 후 결정).
19
- it.skipIf(process.platform === 'win32')('calls agy -p for Antigravity text orchestration', () => {
20
- const root = fs.mkdtempSync(path.join(os.tmpdir(), 'vibe-agy-'));
21
- const binDir = path.join(root, 'bin');
22
- const homeDir = path.join(root, 'home');
23
- const capturePath = path.join(root, 'capture.json');
24
- fs.mkdirSync(binDir, { recursive: true });
25
- fs.mkdirSync(homeDir, { recursive: true });
26
- tempDirs.push(root);
27
-
28
- fs.writeFileSync(
29
- path.join(binDir, 'agy'),
30
- [
31
- `#!${process.execPath}`,
32
- 'const fs = require("fs");',
33
- 'const stdin = fs.readFileSync(0, "utf8");',
34
- 'fs.writeFileSync(process.env.AGY_CAPTURE, JSON.stringify({ argv: process.argv.slice(2), stdin }));',
35
- 'process.stdout.write("antigravity ok");',
36
- '',
37
- ].join('\n'),
38
- { mode: 0o755 }
39
- );
40
-
41
- const scriptPath = path.resolve('hooks/scripts/llm-orchestrate.js');
42
- const output = execFileSync(
43
- process.execPath,
44
- [scriptPath, 'antigravity', 'orchestrate-json', 'System prompt', 'User prompt'],
45
- {
46
- cwd: root,
47
- env: {
48
- ...process.env,
49
- HOME: homeDir,
50
- PATH: [binDir, process.env.PATH || ''].join(path.delimiter),
51
- AGY_CAPTURE: capturePath,
52
- },
53
- encoding: 'utf8',
54
- timeout: 5000,
55
- }
56
- );
57
-
58
- const capture = JSON.parse(fs.readFileSync(capturePath, 'utf8'));
59
- expect(output).toContain('Antigravity response: antigravity ok');
60
- expect(capture.argv[0]).toBe('-p');
61
- expect(capture.argv[1]).toContain('[System]\nSystem prompt');
62
- expect(capture.argv[1]).toContain('[User]\nUser prompt');
63
- expect(capture.argv[1]).toContain('valid JSON only');
64
- expect(capture.stdin).toBe('');
65
- });
66
- });