@su-record/vibe 2.16.4 → 3.1.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 (607) hide show
  1. package/CLAUDE.md +8 -8
  2. package/README.en.md +46 -29
  3. package/README.md +41 -24
  4. package/agents/acceptance-tester.md +56 -0
  5. package/agents/architect.md +25 -67
  6. package/agents/build-error-resolver.md +28 -97
  7. package/agents/code-reviewer.md +89 -0
  8. package/agents/diagrammer.md +34 -169
  9. package/agents/documenter.md +43 -0
  10. package/agents/e2e-tester.md +46 -281
  11. package/agents/event/event-ops.md +54 -77
  12. package/agents/event/event-planner.md +63 -0
  13. package/agents/figma/figma-engineer.md +76 -0
  14. package/agents/implementer.md +26 -41
  15. package/agents/security-reviewer.md +56 -0
  16. package/agents/tester.md +37 -33
  17. package/agents/ui/design-reviewer.md +62 -0
  18. package/agents/ui/design-system-gen.md +63 -0
  19. package/dist/cli/commands/remove.d.ts.map +1 -1
  20. package/dist/cli/commands/remove.js +8 -10
  21. package/dist/cli/commands/remove.js.map +1 -1
  22. package/dist/cli/index.d.ts +0 -4
  23. package/dist/cli/index.d.ts.map +1 -1
  24. package/dist/cli/index.js +0 -4
  25. package/dist/cli/index.js.map +1 -1
  26. package/dist/cli/llm/model-refresh.js +1 -1
  27. package/dist/cli/llm/model-refresh.js.map +1 -1
  28. package/dist/cli/postinstall/claude-agents.d.ts.map +1 -1
  29. package/dist/cli/postinstall/claude-agents.js +16 -77
  30. package/dist/cli/postinstall/claude-agents.js.map +1 -1
  31. package/dist/cli/postinstall/constants.d.ts.map +1 -1
  32. package/dist/cli/postinstall/constants.js +80 -296
  33. package/dist/cli/postinstall/constants.js.map +1 -1
  34. package/dist/cli/postinstall/cursor-agents.d.ts +5 -0
  35. package/dist/cli/postinstall/cursor-agents.d.ts.map +1 -1
  36. package/dist/cli/postinstall/cursor-agents.js +26 -68
  37. package/dist/cli/postinstall/cursor-agents.js.map +1 -1
  38. package/dist/cli/postinstall/cursor-skills.d.ts.map +1 -1
  39. package/dist/cli/postinstall/cursor-skills.js +26 -30
  40. package/dist/cli/postinstall/cursor-skills.js.map +1 -1
  41. package/dist/cli/postinstall/main.d.ts.map +1 -1
  42. package/dist/cli/postinstall/main.js +0 -2
  43. package/dist/cli/postinstall/main.js.map +1 -1
  44. package/dist/cli/setup/Provisioner.d.ts.map +1 -1
  45. package/dist/cli/setup/Provisioner.js +19 -21
  46. package/dist/cli/setup/Provisioner.js.map +1 -1
  47. package/dist/cli/utils.d.ts +4 -1
  48. package/dist/cli/utils.d.ts.map +1 -1
  49. package/dist/cli/utils.js +4 -1
  50. package/dist/cli/utils.js.map +1 -1
  51. package/dist/infra/lib/FrameworkDetector.js +4 -4
  52. package/dist/infra/lib/FrameworkDetector.js.map +1 -1
  53. package/dist/infra/lib/OrchestrateWorkflow.js +2 -2
  54. package/dist/infra/lib/OrchestrateWorkflow.js.map +1 -1
  55. package/dist/infra/lib/browser/launch.js +1 -1
  56. package/dist/infra/lib/browser/launch.js.map +1 -1
  57. package/dist/infra/lib/browser/types.d.ts +1 -1
  58. package/dist/infra/lib/browser/types.js +1 -1
  59. package/dist/infra/lib/constants.d.ts +1 -1
  60. package/dist/infra/lib/constants.d.ts.map +1 -1
  61. package/dist/infra/lib/constants.js +4 -5
  62. package/dist/infra/lib/constants.js.map +1 -1
  63. package/dist/infra/lib/evolution/__tests__/integration.test.js +7 -7
  64. package/dist/infra/lib/evolution/__tests__/integration.test.js.map +1 -1
  65. package/dist/infra/lib/memory/index.d.ts +0 -2
  66. package/dist/infra/lib/memory/index.d.ts.map +1 -1
  67. package/dist/infra/lib/memory/index.js +0 -2
  68. package/dist/infra/lib/memory/index.js.map +1 -1
  69. package/dist/infra/lib/telemetry/SkillTelemetry.test.js +1 -1
  70. package/dist/infra/lib/telemetry/SkillTelemetry.test.js.map +1 -1
  71. package/dist/tools/index.d.ts +0 -4
  72. package/dist/tools/index.d.ts.map +1 -1
  73. package/dist/tools/index.js +0 -4
  74. package/dist/tools/index.js.map +1 -1
  75. package/dist/tools/memory/autoSaveContext.d.ts +0 -1
  76. package/dist/tools/memory/autoSaveContext.d.ts.map +1 -1
  77. package/dist/tools/memory/autoSaveContext.js +13 -27
  78. package/dist/tools/memory/autoSaveContext.js.map +1 -1
  79. package/hooks/scripts/__tests__/.vibe/command-log.txt +3 -78
  80. package/hooks/scripts/__tests__/.vibe/memories/memories.db +0 -0
  81. package/hooks/scripts/__tests__/.vibe/memories/memories.db-shm +0 -0
  82. package/hooks/scripts/__tests__/.vibe/memories/memories.db-wal +0 -0
  83. package/hooks/scripts/__tests__/clone-behaviors.test.js +64 -0
  84. package/hooks/scripts/__tests__/clone-merge-responsive.test.js +106 -0
  85. package/hooks/scripts/__tests__/code-check-detectors.test.js +2 -2
  86. package/hooks/scripts/__tests__/run-ledger.test.js +1 -1
  87. package/hooks/scripts/__tests__/scope-from-spec.test.js +12 -12
  88. package/hooks/scripts/__tests__/step-counter.test.js +1 -1
  89. package/hooks/scripts/clone-extract.js +242 -28
  90. package/hooks/scripts/clone-merge-responsive.js +184 -0
  91. package/hooks/scripts/clone-spec.js +33 -8
  92. package/hooks/scripts/clone-validate.js +15 -9
  93. package/hooks/scripts/code-check.js +16 -203
  94. package/hooks/scripts/context-save.js +0 -11
  95. package/hooks/scripts/lib/scope-from-spec.js +9 -6
  96. package/hooks/scripts/post-edit-dispatcher.js +1 -1
  97. package/hooks/scripts/prompt-dispatcher.js +7 -18
  98. package/hooks/scripts/session-start.js +25 -35
  99. package/package.json +6 -5
  100. package/skills/agents-md/SKILL.md +62 -15
  101. package/skills/arch-guard/SKILL.md +1 -1
  102. package/skills/brand-assets/SKILL.md +2 -2
  103. package/skills/capability-loop/SKILL.md +1 -1
  104. package/skills/chub-usage/SKILL.md +1 -1
  105. package/skills/clone/SKILL.md +132 -55
  106. package/skills/commerce-patterns/SKILL.md +2 -2
  107. package/skills/commit-push-pr/SKILL.md +0 -1
  108. package/skills/context7-usage/SKILL.md +1 -1
  109. package/skills/design-refine/SKILL.md +140 -0
  110. package/skills/{design-polish → design-refine}/templates/polish-report.md +1 -1
  111. package/skills/{design-normalize → design-refine}/templates/token-audit.md +3 -3
  112. package/skills/design-review/SKILL.md +143 -0
  113. package/skills/{design-audit → design-review}/agents/a11y-auditor.md +1 -1
  114. package/skills/{design-audit → design-review}/agents/performance-auditor.md +1 -1
  115. package/skills/{design-audit → design-review}/agents/responsive-auditor.md +1 -1
  116. package/skills/{design-audit → design-review}/agents/slop-detector.md +1 -1
  117. package/skills/{design-audit → design-review}/frameworks/core-web-vitals.md +1 -1
  118. package/skills/{design-audit → design-review}/frameworks/wcag-checklist.md +1 -1
  119. package/skills/{design-audit → design-review}/orchestrator.md +3 -3
  120. package/skills/{design-critique → design-review}/templates/critique-report.md +3 -3
  121. package/skills/{design-audit → design-review}/templates/report.md +3 -3
  122. package/skills/design-teach/SKILL.md +11 -11
  123. package/skills/docs/SKILL.md +14 -16
  124. package/skills/e2e-commerce/SKILL.md +1 -1
  125. package/skills/event-comms/SKILL.md +1 -1
  126. package/skills/event-ops/SKILL.md +2 -2
  127. package/skills/event-planning/SKILL.md +1 -1
  128. package/skills/exec-plan/SKILL.md +2 -2
  129. package/skills/figma/SKILL.md +240 -148
  130. package/skills/figma/rubrics/css-mapping.md +125 -0
  131. package/skills/git-worktree/SKILL.md +1 -1
  132. package/skills/handoff/SKILL.md +1 -1
  133. package/skills/parallel-research/SKILL.md +14 -28
  134. package/skills/priority-todos/SKILL.md +1 -1
  135. package/skills/regress/SKILL.md +11 -0
  136. package/skills/restraint/SKILL.md +72 -0
  137. package/skills/seo-checklist/SKILL.md +2 -2
  138. package/skills/spec/SKILL.md +65 -836
  139. package/skills/test/SKILL.md +3 -3
  140. package/skills/tool-fallback/SKILL.md +2 -2
  141. package/skills/vercel-react-best-practices/SKILL.md +1 -1
  142. package/skills/vibe/SKILL.md +20 -35
  143. package/skills/vibe.analyze/SKILL.md +7 -9
  144. package/skills/vibe.clone/SKILL.md +39 -19
  145. package/skills/vibe.docs/SKILL.md +1 -1
  146. package/skills/vibe.event/SKILL.md +27 -27
  147. package/skills/vibe.figma/SKILL.md +14 -18
  148. package/skills/vibe.harness/SKILL.md +5 -5
  149. package/skills/vibe.reason/SKILL.md +0 -2
  150. package/skills/vibe.review/SKILL.md +45 -49
  151. package/skills/vibe.run/SKILL.md +2 -5
  152. package/skills/vibe.run/references/parallel-agents.md +33 -98
  153. package/skills/vibe.spec/SKILL.md +38 -535
  154. package/skills/vibe.utils/SKILL.md +3 -3
  155. package/skills/vibe.verify/SKILL.md +70 -542
  156. package/skills/video-production/SKILL.md +1 -1
  157. package/vibe/rules/orchestrator-contract.md +5 -4
  158. package/vibe/rules/quality/performance.md +1 -1
  159. package/vibe/templates/claudemd-template.md +1 -1
  160. package/vibe/templates/spec-template.md +35 -185
  161. package/agents/architect-low.md +0 -41
  162. package/agents/architect-medium.md +0 -59
  163. package/agents/compounder.md +0 -265
  164. package/agents/docs/api-documenter.md +0 -99
  165. package/agents/docs/changelog-writer.md +0 -93
  166. package/agents/event/event-comms.md +0 -78
  167. package/agents/event/event-content.md +0 -68
  168. package/agents/event/event-image.md +0 -95
  169. package/agents/event/event-scheduler.md +0 -69
  170. package/agents/event/event-speaker.md +0 -86
  171. package/agents/explorer-low.md +0 -42
  172. package/agents/explorer-medium.md +0 -59
  173. package/agents/explorer.md +0 -48
  174. package/agents/figma/figma-analyst.md +0 -56
  175. package/agents/figma/figma-architect.md +0 -116
  176. package/agents/figma/figma-auditor.md +0 -86
  177. package/agents/figma/figma-builder.md +0 -104
  178. package/agents/implementer-low.md +0 -43
  179. package/agents/implementer-medium.md +0 -52
  180. package/agents/junior-mentor.md +0 -141
  181. package/agents/planning/requirements-analyst.md +0 -84
  182. package/agents/planning/ux-advisor.md +0 -83
  183. package/agents/qa/acceptance-tester.md +0 -86
  184. package/agents/qa/edge-case-finder.md +0 -93
  185. package/agents/qa/qa-coordinator.md +0 -131
  186. package/agents/refactor-cleaner.md +0 -143
  187. package/agents/research/best-practices.md +0 -199
  188. package/agents/research/codebase-patterns.md +0 -157
  189. package/agents/research/framework-docs.md +0 -188
  190. package/agents/research/security-advisory.md +0 -213
  191. package/agents/review/architecture-reviewer.md +0 -107
  192. package/agents/review/complexity-reviewer.md +0 -116
  193. package/agents/review/data-integrity-reviewer.md +0 -88
  194. package/agents/review/git-history-reviewer.md +0 -103
  195. package/agents/review/performance-reviewer.md +0 -86
  196. package/agents/review/python-reviewer.md +0 -150
  197. package/agents/review/rails-reviewer.md +0 -139
  198. package/agents/review/react-reviewer.md +0 -144
  199. package/agents/review/security-reviewer.md +0 -80
  200. package/agents/review/simplicity-reviewer.md +0 -140
  201. package/agents/review/test-coverage-reviewer.md +0 -116
  202. package/agents/review/typescript-reviewer.md +0 -127
  203. package/agents/searcher.md +0 -54
  204. package/agents/simplifier.md +0 -124
  205. package/agents/teams/debug-team.md +0 -74
  206. package/agents/teams/dev-team.md +0 -92
  207. package/agents/teams/docs-team.md +0 -84
  208. package/agents/teams/figma-team.md +0 -89
  209. package/agents/teams/fullstack-team.md +0 -87
  210. package/agents/teams/lite-team.md +0 -73
  211. package/agents/teams/migration-team.md +0 -82
  212. package/agents/teams/refactor-team.md +0 -98
  213. package/agents/teams/research-team.md +0 -90
  214. package/agents/teams/review-debate-team.md +0 -129
  215. package/agents/teams/security-team.md +0 -85
  216. package/agents/ui/ui-a11y-auditor.md +0 -93
  217. package/agents/ui/ui-antipattern-detector.md +0 -102
  218. package/agents/ui/ui-dataviz-advisor.md +0 -69
  219. package/agents/ui/ui-design-system-gen.md +0 -57
  220. package/agents/ui/ui-industry-analyzer.md +0 -49
  221. package/agents/ui/ui-layout-architect.md +0 -65
  222. package/agents/ui/ui-stack-implementer.md +0 -68
  223. package/agents/ui/ux-compliance-reviewer.md +0 -81
  224. package/agents/ui-previewer.md +0 -262
  225. package/dist/cli/llm/gemini-commands.d.ts +0 -25
  226. package/dist/cli/llm/gemini-commands.d.ts.map +0 -1
  227. package/dist/cli/llm/gemini-commands.js +0 -143
  228. package/dist/cli/llm/gemini-commands.js.map +0 -1
  229. package/dist/cli/postinstall/codex-agents.d.ts +0 -24
  230. package/dist/cli/postinstall/codex-agents.d.ts.map +0 -1
  231. package/dist/cli/postinstall/codex-agents.js +0 -114
  232. package/dist/cli/postinstall/codex-agents.js.map +0 -1
  233. package/dist/cli/postinstall/codex-instruction.d.ts +0 -10
  234. package/dist/cli/postinstall/codex-instruction.d.ts.map +0 -1
  235. package/dist/cli/postinstall/codex-instruction.js +0 -56
  236. package/dist/cli/postinstall/codex-instruction.js.map +0 -1
  237. package/dist/cli/postinstall/gemini-agents.d.ts +0 -12
  238. package/dist/cli/postinstall/gemini-agents.d.ts.map +0 -1
  239. package/dist/cli/postinstall/gemini-agents.js +0 -80
  240. package/dist/cli/postinstall/gemini-agents.js.map +0 -1
  241. package/dist/cli/postinstall/gemini-instruction.d.ts +0 -10
  242. package/dist/cli/postinstall/gemini-instruction.d.ts.map +0 -1
  243. package/dist/cli/postinstall/gemini-instruction.js +0 -59
  244. package/dist/cli/postinstall/gemini-instruction.js.map +0 -1
  245. package/dist/infra/lib/ContextCompressor.d.ts +0 -104
  246. package/dist/infra/lib/ContextCompressor.d.ts.map +0 -1
  247. package/dist/infra/lib/ContextCompressor.js +0 -297
  248. package/dist/infra/lib/ContextCompressor.js.map +0 -1
  249. package/dist/infra/lib/ContextCompressor.test.d.ts +0 -2
  250. package/dist/infra/lib/ContextCompressor.test.d.ts.map +0 -1
  251. package/dist/infra/lib/ContextCompressor.test.js +0 -25
  252. package/dist/infra/lib/ContextCompressor.test.js.map +0 -1
  253. package/dist/infra/lib/ProgressTracker.d.ts +0 -73
  254. package/dist/infra/lib/ProgressTracker.d.ts.map +0 -1
  255. package/dist/infra/lib/ProgressTracker.js +0 -267
  256. package/dist/infra/lib/ProgressTracker.js.map +0 -1
  257. package/dist/infra/lib/TokenBudgetTracker.d.ts +0 -68
  258. package/dist/infra/lib/TokenBudgetTracker.d.ts.map +0 -1
  259. package/dist/infra/lib/TokenBudgetTracker.js +0 -191
  260. package/dist/infra/lib/TokenBudgetTracker.js.map +0 -1
  261. package/dist/infra/lib/autonomy/AuditStore.d.ts +0 -63
  262. package/dist/infra/lib/autonomy/AuditStore.d.ts.map +0 -1
  263. package/dist/infra/lib/autonomy/AuditStore.js +0 -185
  264. package/dist/infra/lib/autonomy/AuditStore.js.map +0 -1
  265. package/dist/infra/lib/autonomy/AutonomyConfig.d.ts +0 -84
  266. package/dist/infra/lib/autonomy/AutonomyConfig.d.ts.map +0 -1
  267. package/dist/infra/lib/autonomy/AutonomyConfig.js +0 -49
  268. package/dist/infra/lib/autonomy/AutonomyConfig.js.map +0 -1
  269. package/dist/infra/lib/autonomy/AutonomyOrchestrator.d.ts +0 -78
  270. package/dist/infra/lib/autonomy/AutonomyOrchestrator.d.ts.map +0 -1
  271. package/dist/infra/lib/autonomy/AutonomyOrchestrator.js +0 -246
  272. package/dist/infra/lib/autonomy/AutonomyOrchestrator.js.map +0 -1
  273. package/dist/infra/lib/autonomy/BackgroundMonitor.d.ts +0 -36
  274. package/dist/infra/lib/autonomy/BackgroundMonitor.d.ts.map +0 -1
  275. package/dist/infra/lib/autonomy/BackgroundMonitor.js +0 -138
  276. package/dist/infra/lib/autonomy/BackgroundMonitor.js.map +0 -1
  277. package/dist/infra/lib/autonomy/CollaborationProtocol.d.ts +0 -56
  278. package/dist/infra/lib/autonomy/CollaborationProtocol.d.ts.map +0 -1
  279. package/dist/infra/lib/autonomy/CollaborationProtocol.js +0 -122
  280. package/dist/infra/lib/autonomy/CollaborationProtocol.js.map +0 -1
  281. package/dist/infra/lib/autonomy/ConfirmationManager.d.ts +0 -42
  282. package/dist/infra/lib/autonomy/ConfirmationManager.d.ts.map +0 -1
  283. package/dist/infra/lib/autonomy/ConfirmationManager.js +0 -200
  284. package/dist/infra/lib/autonomy/ConfirmationManager.js.map +0 -1
  285. package/dist/infra/lib/autonomy/ConfirmationStore.d.ts +0 -55
  286. package/dist/infra/lib/autonomy/ConfirmationStore.d.ts.map +0 -1
  287. package/dist/infra/lib/autonomy/ConfirmationStore.js +0 -130
  288. package/dist/infra/lib/autonomy/ConfirmationStore.js.map +0 -1
  289. package/dist/infra/lib/autonomy/EventBus.d.ts +0 -20
  290. package/dist/infra/lib/autonomy/EventBus.d.ts.map +0 -1
  291. package/dist/infra/lib/autonomy/EventBus.js +0 -119
  292. package/dist/infra/lib/autonomy/EventBus.js.map +0 -1
  293. package/dist/infra/lib/autonomy/EventOutbox.d.ts +0 -32
  294. package/dist/infra/lib/autonomy/EventOutbox.d.ts.map +0 -1
  295. package/dist/infra/lib/autonomy/EventOutbox.js +0 -152
  296. package/dist/infra/lib/autonomy/EventOutbox.js.map +0 -1
  297. package/dist/infra/lib/autonomy/NotificationDispatcher.d.ts +0 -24
  298. package/dist/infra/lib/autonomy/NotificationDispatcher.d.ts.map +0 -1
  299. package/dist/infra/lib/autonomy/NotificationDispatcher.js +0 -112
  300. package/dist/infra/lib/autonomy/NotificationDispatcher.js.map +0 -1
  301. package/dist/infra/lib/autonomy/PolicyEngine.d.ts +0 -76
  302. package/dist/infra/lib/autonomy/PolicyEngine.d.ts.map +0 -1
  303. package/dist/infra/lib/autonomy/PolicyEngine.js +0 -208
  304. package/dist/infra/lib/autonomy/PolicyEngine.js.map +0 -1
  305. package/dist/infra/lib/autonomy/ProactiveAnalyzer.d.ts +0 -61
  306. package/dist/infra/lib/autonomy/ProactiveAnalyzer.d.ts.map +0 -1
  307. package/dist/infra/lib/autonomy/ProactiveAnalyzer.js +0 -236
  308. package/dist/infra/lib/autonomy/ProactiveAnalyzer.js.map +0 -1
  309. package/dist/infra/lib/autonomy/RiskClassifier.d.ts +0 -35
  310. package/dist/infra/lib/autonomy/RiskClassifier.d.ts.map +0 -1
  311. package/dist/infra/lib/autonomy/RiskClassifier.js +0 -147
  312. package/dist/infra/lib/autonomy/RiskClassifier.js.map +0 -1
  313. package/dist/infra/lib/autonomy/SecuritySentinel.d.ts +0 -51
  314. package/dist/infra/lib/autonomy/SecuritySentinel.d.ts.map +0 -1
  315. package/dist/infra/lib/autonomy/SecuritySentinel.js +0 -162
  316. package/dist/infra/lib/autonomy/SecuritySentinel.js.map +0 -1
  317. package/dist/infra/lib/autonomy/SuggestionStore.d.ts +0 -54
  318. package/dist/infra/lib/autonomy/SuggestionStore.d.ts.map +0 -1
  319. package/dist/infra/lib/autonomy/SuggestionStore.js +0 -144
  320. package/dist/infra/lib/autonomy/SuggestionStore.js.map +0 -1
  321. package/dist/infra/lib/autonomy/TaskDecomposer.d.ts +0 -38
  322. package/dist/infra/lib/autonomy/TaskDecomposer.d.ts.map +0 -1
  323. package/dist/infra/lib/autonomy/TaskDecomposer.js +0 -175
  324. package/dist/infra/lib/autonomy/TaskDecomposer.js.map +0 -1
  325. package/dist/infra/lib/autonomy/__tests__/audit-store.test.d.ts +0 -2
  326. package/dist/infra/lib/autonomy/__tests__/audit-store.test.d.ts.map +0 -1
  327. package/dist/infra/lib/autonomy/__tests__/audit-store.test.js +0 -198
  328. package/dist/infra/lib/autonomy/__tests__/audit-store.test.js.map +0 -1
  329. package/dist/infra/lib/autonomy/__tests__/event-bus.test.d.ts +0 -2
  330. package/dist/infra/lib/autonomy/__tests__/event-bus.test.d.ts.map +0 -1
  331. package/dist/infra/lib/autonomy/__tests__/event-bus.test.js +0 -225
  332. package/dist/infra/lib/autonomy/__tests__/event-bus.test.js.map +0 -1
  333. package/dist/infra/lib/autonomy/__tests__/event-core.test.d.ts +0 -2
  334. package/dist/infra/lib/autonomy/__tests__/event-core.test.d.ts.map +0 -1
  335. package/dist/infra/lib/autonomy/__tests__/event-core.test.js +0 -194
  336. package/dist/infra/lib/autonomy/__tests__/event-core.test.js.map +0 -1
  337. package/dist/infra/lib/autonomy/__tests__/governance.test.d.ts +0 -2
  338. package/dist/infra/lib/autonomy/__tests__/governance.test.d.ts.map +0 -1
  339. package/dist/infra/lib/autonomy/__tests__/governance.test.js +0 -540
  340. package/dist/infra/lib/autonomy/__tests__/governance.test.js.map +0 -1
  341. package/dist/infra/lib/autonomy/__tests__/integration.test.d.ts +0 -2
  342. package/dist/infra/lib/autonomy/__tests__/integration.test.d.ts.map +0 -1
  343. package/dist/infra/lib/autonomy/__tests__/integration.test.js +0 -480
  344. package/dist/infra/lib/autonomy/__tests__/integration.test.js.map +0 -1
  345. package/dist/infra/lib/autonomy/__tests__/proactive.test.d.ts +0 -2
  346. package/dist/infra/lib/autonomy/__tests__/proactive.test.d.ts.map +0 -1
  347. package/dist/infra/lib/autonomy/__tests__/proactive.test.js +0 -410
  348. package/dist/infra/lib/autonomy/__tests__/proactive.test.js.map +0 -1
  349. package/dist/infra/lib/autonomy/__tests__/sentinel.test.d.ts +0 -2
  350. package/dist/infra/lib/autonomy/__tests__/sentinel.test.d.ts.map +0 -1
  351. package/dist/infra/lib/autonomy/__tests__/sentinel.test.js +0 -486
  352. package/dist/infra/lib/autonomy/__tests__/sentinel.test.js.map +0 -1
  353. package/dist/infra/lib/autonomy/index.d.ts +0 -18
  354. package/dist/infra/lib/autonomy/index.d.ts.map +0 -1
  355. package/dist/infra/lib/autonomy/index.js +0 -22
  356. package/dist/infra/lib/autonomy/index.js.map +0 -1
  357. package/dist/infra/lib/autonomy/schemas.d.ts +0 -317
  358. package/dist/infra/lib/autonomy/schemas.d.ts.map +0 -1
  359. package/dist/infra/lib/autonomy/schemas.js +0 -90
  360. package/dist/infra/lib/autonomy/schemas.js.map +0 -1
  361. package/dist/infra/lib/claude/index.d.ts +0 -5
  362. package/dist/infra/lib/claude/index.d.ts.map +0 -1
  363. package/dist/infra/lib/claude/index.js +0 -5
  364. package/dist/infra/lib/claude/index.js.map +0 -1
  365. package/dist/infra/lib/claude/storage.d.ts +0 -12
  366. package/dist/infra/lib/claude/storage.d.ts.map +0 -1
  367. package/dist/infra/lib/claude/storage.js +0 -54
  368. package/dist/infra/lib/claude/storage.js.map +0 -1
  369. package/dist/infra/lib/gemini/auth.d.ts +0 -18
  370. package/dist/infra/lib/gemini/auth.d.ts.map +0 -1
  371. package/dist/infra/lib/gemini/auth.js +0 -32
  372. package/dist/infra/lib/gemini/auth.js.map +0 -1
  373. package/dist/infra/lib/gemini/capabilities.d.ts +0 -28
  374. package/dist/infra/lib/gemini/capabilities.d.ts.map +0 -1
  375. package/dist/infra/lib/gemini/capabilities.js +0 -178
  376. package/dist/infra/lib/gemini/capabilities.js.map +0 -1
  377. package/dist/infra/lib/gemini/chat.d.ts +0 -35
  378. package/dist/infra/lib/gemini/chat.d.ts.map +0 -1
  379. package/dist/infra/lib/gemini/chat.js +0 -161
  380. package/dist/infra/lib/gemini/chat.js.map +0 -1
  381. package/dist/infra/lib/gemini/completion.d.ts +0 -12
  382. package/dist/infra/lib/gemini/completion.d.ts.map +0 -1
  383. package/dist/infra/lib/gemini/completion.js +0 -13
  384. package/dist/infra/lib/gemini/completion.js.map +0 -1
  385. package/dist/infra/lib/gemini/constants.d.ts +0 -8
  386. package/dist/infra/lib/gemini/constants.d.ts.map +0 -1
  387. package/dist/infra/lib/gemini/constants.js +0 -11
  388. package/dist/infra/lib/gemini/constants.js.map +0 -1
  389. package/dist/infra/lib/gemini/index.d.ts +0 -13
  390. package/dist/infra/lib/gemini/index.d.ts.map +0 -1
  391. package/dist/infra/lib/gemini/index.js +0 -12
  392. package/dist/infra/lib/gemini/index.js.map +0 -1
  393. package/dist/infra/lib/gemini/models.d.ts +0 -11
  394. package/dist/infra/lib/gemini/models.d.ts.map +0 -1
  395. package/dist/infra/lib/gemini/models.js +0 -38
  396. package/dist/infra/lib/gemini/models.js.map +0 -1
  397. package/dist/infra/lib/gemini/orchestration.d.ts +0 -33
  398. package/dist/infra/lib/gemini/orchestration.d.ts.map +0 -1
  399. package/dist/infra/lib/gemini/orchestration.js +0 -62
  400. package/dist/infra/lib/gemini/orchestration.js.map +0 -1
  401. package/dist/infra/lib/gemini/types.d.ts +0 -115
  402. package/dist/infra/lib/gemini/types.d.ts.map +0 -1
  403. package/dist/infra/lib/gemini/types.js +0 -5
  404. package/dist/infra/lib/gemini/types.js.map +0 -1
  405. package/dist/infra/orchestrator/AgentAnnouncer.d.ts +0 -64
  406. package/dist/infra/orchestrator/AgentAnnouncer.d.ts.map +0 -1
  407. package/dist/infra/orchestrator/AgentAnnouncer.js +0 -127
  408. package/dist/infra/orchestrator/AgentAnnouncer.js.map +0 -1
  409. package/dist/infra/orchestrator/AgentAnnouncer.test.d.ts +0 -5
  410. package/dist/infra/orchestrator/AgentAnnouncer.test.d.ts.map +0 -1
  411. package/dist/infra/orchestrator/AgentAnnouncer.test.js +0 -119
  412. package/dist/infra/orchestrator/AgentAnnouncer.test.js.map +0 -1
  413. package/dist/infra/orchestrator/AgentExecutor.d.ts +0 -22
  414. package/dist/infra/orchestrator/AgentExecutor.d.ts.map +0 -1
  415. package/dist/infra/orchestrator/AgentExecutor.js +0 -272
  416. package/dist/infra/orchestrator/AgentExecutor.js.map +0 -1
  417. package/dist/infra/orchestrator/AgentManager.d.ts +0 -72
  418. package/dist/infra/orchestrator/AgentManager.d.ts.map +0 -1
  419. package/dist/infra/orchestrator/AgentManager.js +0 -183
  420. package/dist/infra/orchestrator/AgentManager.js.map +0 -1
  421. package/dist/infra/orchestrator/AgentManifestCache.d.ts +0 -54
  422. package/dist/infra/orchestrator/AgentManifestCache.d.ts.map +0 -1
  423. package/dist/infra/orchestrator/AgentManifestCache.js +0 -191
  424. package/dist/infra/orchestrator/AgentManifestCache.js.map +0 -1
  425. package/dist/infra/orchestrator/AgentRegistry.d.ts +0 -76
  426. package/dist/infra/orchestrator/AgentRegistry.d.ts.map +0 -1
  427. package/dist/infra/orchestrator/AgentRegistry.js +0 -217
  428. package/dist/infra/orchestrator/AgentRegistry.js.map +0 -1
  429. package/dist/infra/orchestrator/AgentRegistry.test.d.ts +0 -5
  430. package/dist/infra/orchestrator/AgentRegistry.test.d.ts.map +0 -1
  431. package/dist/infra/orchestrator/AgentRegistry.test.js +0 -122
  432. package/dist/infra/orchestrator/AgentRegistry.test.js.map +0 -1
  433. package/dist/infra/orchestrator/BackgroundManager.d.ts +0 -128
  434. package/dist/infra/orchestrator/BackgroundManager.d.ts.map +0 -1
  435. package/dist/infra/orchestrator/BackgroundManager.js +0 -620
  436. package/dist/infra/orchestrator/BackgroundManager.js.map +0 -1
  437. package/dist/infra/orchestrator/BackgroundManager.test.d.ts +0 -6
  438. package/dist/infra/orchestrator/BackgroundManager.test.d.ts.map +0 -1
  439. package/dist/infra/orchestrator/BackgroundManager.test.js +0 -162
  440. package/dist/infra/orchestrator/BackgroundManager.test.js.map +0 -1
  441. package/dist/infra/orchestrator/CheckpointManager.d.ts +0 -54
  442. package/dist/infra/orchestrator/CheckpointManager.d.ts.map +0 -1
  443. package/dist/infra/orchestrator/CheckpointManager.js +0 -121
  444. package/dist/infra/orchestrator/CheckpointManager.js.map +0 -1
  445. package/dist/infra/orchestrator/CodexAgentRuntime.d.ts +0 -12
  446. package/dist/infra/orchestrator/CodexAgentRuntime.d.ts.map +0 -1
  447. package/dist/infra/orchestrator/CodexAgentRuntime.js +0 -94
  448. package/dist/infra/orchestrator/CodexAgentRuntime.js.map +0 -1
  449. package/dist/infra/orchestrator/CodexAgentRuntime.test.d.ts +0 -2
  450. package/dist/infra/orchestrator/CodexAgentRuntime.test.d.ts.map +0 -1
  451. package/dist/infra/orchestrator/CodexAgentRuntime.test.js +0 -37
  452. package/dist/infra/orchestrator/CodexAgentRuntime.test.js.map +0 -1
  453. package/dist/infra/orchestrator/LLMCluster.d.ts +0 -82
  454. package/dist/infra/orchestrator/LLMCluster.d.ts.map +0 -1
  455. package/dist/infra/orchestrator/LLMCluster.js +0 -146
  456. package/dist/infra/orchestrator/LLMCluster.js.map +0 -1
  457. package/dist/infra/orchestrator/MultiLlmResearch.d.ts +0 -31
  458. package/dist/infra/orchestrator/MultiLlmResearch.d.ts.map +0 -1
  459. package/dist/infra/orchestrator/MultiLlmResearch.js +0 -160
  460. package/dist/infra/orchestrator/MultiLlmResearch.js.map +0 -1
  461. package/dist/infra/orchestrator/PhasePipeline.d.ts +0 -122
  462. package/dist/infra/orchestrator/PhasePipeline.d.ts.map +0 -1
  463. package/dist/infra/orchestrator/PhasePipeline.js +0 -385
  464. package/dist/infra/orchestrator/PhasePipeline.js.map +0 -1
  465. package/dist/infra/orchestrator/PhaseResultStore.d.ts +0 -39
  466. package/dist/infra/orchestrator/PhaseResultStore.d.ts.map +0 -1
  467. package/dist/infra/orchestrator/PhaseResultStore.js +0 -151
  468. package/dist/infra/orchestrator/PhaseResultStore.js.map +0 -1
  469. package/dist/infra/orchestrator/SessionStore.d.ts +0 -40
  470. package/dist/infra/orchestrator/SessionStore.d.ts.map +0 -1
  471. package/dist/infra/orchestrator/SessionStore.js +0 -116
  472. package/dist/infra/orchestrator/SessionStore.js.map +0 -1
  473. package/dist/infra/orchestrator/SmartRouter.d.ts +0 -90
  474. package/dist/infra/orchestrator/SmartRouter.d.ts.map +0 -1
  475. package/dist/infra/orchestrator/SmartRouter.js +0 -277
  476. package/dist/infra/orchestrator/SmartRouter.js.map +0 -1
  477. package/dist/infra/orchestrator/SmartRouter.test.d.ts +0 -5
  478. package/dist/infra/orchestrator/SmartRouter.test.d.ts.map +0 -1
  479. package/dist/infra/orchestrator/SmartRouter.test.js +0 -484
  480. package/dist/infra/orchestrator/SmartRouter.test.js.map +0 -1
  481. package/dist/infra/orchestrator/SwarmOrchestrator.d.ts +0 -143
  482. package/dist/infra/orchestrator/SwarmOrchestrator.d.ts.map +0 -1
  483. package/dist/infra/orchestrator/SwarmOrchestrator.js +0 -378
  484. package/dist/infra/orchestrator/SwarmOrchestrator.js.map +0 -1
  485. package/dist/infra/orchestrator/SwarmOrchestrator.test.d.ts +0 -5
  486. package/dist/infra/orchestrator/SwarmOrchestrator.test.d.ts.map +0 -1
  487. package/dist/infra/orchestrator/SwarmOrchestrator.test.js +0 -95
  488. package/dist/infra/orchestrator/SwarmOrchestrator.test.js.map +0 -1
  489. package/dist/infra/orchestrator/agentDiscovery.d.ts +0 -26
  490. package/dist/infra/orchestrator/agentDiscovery.d.ts.map +0 -1
  491. package/dist/infra/orchestrator/agentDiscovery.js +0 -175
  492. package/dist/infra/orchestrator/agentDiscovery.js.map +0 -1
  493. package/dist/infra/orchestrator/backgroundAgent.d.ts +0 -9
  494. package/dist/infra/orchestrator/backgroundAgent.d.ts.map +0 -1
  495. package/dist/infra/orchestrator/backgroundAgent.js +0 -10
  496. package/dist/infra/orchestrator/backgroundAgent.js.map +0 -1
  497. package/dist/infra/orchestrator/index.d.ts +0 -207
  498. package/dist/infra/orchestrator/index.d.ts.map +0 -1
  499. package/dist/infra/orchestrator/index.js +0 -362
  500. package/dist/infra/orchestrator/index.js.map +0 -1
  501. package/dist/infra/orchestrator/orchestrator.d.ts +0 -60
  502. package/dist/infra/orchestrator/orchestrator.d.ts.map +0 -1
  503. package/dist/infra/orchestrator/orchestrator.js +0 -214
  504. package/dist/infra/orchestrator/orchestrator.js.map +0 -1
  505. package/dist/infra/orchestrator/parallelResearch.d.ts +0 -27
  506. package/dist/infra/orchestrator/parallelResearch.d.ts.map +0 -1
  507. package/dist/infra/orchestrator/parallelResearch.js +0 -285
  508. package/dist/infra/orchestrator/parallelResearch.js.map +0 -1
  509. package/dist/infra/orchestrator/types.d.ts +0 -205
  510. package/dist/infra/orchestrator/types.d.ts.map +0 -1
  511. package/dist/infra/orchestrator/types.js +0 -87
  512. package/dist/infra/orchestrator/types.js.map +0 -1
  513. package/dist/infra/orchestrator/types.test.d.ts +0 -2
  514. package/dist/infra/orchestrator/types.test.d.ts.map +0 -1
  515. package/dist/infra/orchestrator/types.test.js +0 -57
  516. package/dist/infra/orchestrator/types.test.js.map +0 -1
  517. package/dist/tools/semantic/analyzeDependencyGraph.d.ts +0 -12
  518. package/dist/tools/semantic/analyzeDependencyGraph.d.ts.map +0 -1
  519. package/dist/tools/semantic/analyzeDependencyGraph.js +0 -359
  520. package/dist/tools/semantic/analyzeDependencyGraph.js.map +0 -1
  521. package/dist/tools/semantic/astGrep.d.ts +0 -25
  522. package/dist/tools/semantic/astGrep.d.ts.map +0 -1
  523. package/dist/tools/semantic/astGrep.js +0 -324
  524. package/dist/tools/semantic/astGrep.js.map +0 -1
  525. package/dist/tools/semantic/astGrep.test.d.ts +0 -2
  526. package/dist/tools/semantic/astGrep.test.d.ts.map +0 -1
  527. package/dist/tools/semantic/astGrep.test.js +0 -74
  528. package/dist/tools/semantic/astGrep.test.js.map +0 -1
  529. package/dist/tools/semantic/findReferences.d.ts +0 -9
  530. package/dist/tools/semantic/findReferences.d.ts.map +0 -1
  531. package/dist/tools/semantic/findReferences.js +0 -200
  532. package/dist/tools/semantic/findReferences.js.map +0 -1
  533. package/dist/tools/semantic/findSymbol.d.ts +0 -8
  534. package/dist/tools/semantic/findSymbol.d.ts.map +0 -1
  535. package/dist/tools/semantic/findSymbol.js +0 -204
  536. package/dist/tools/semantic/findSymbol.js.map +0 -1
  537. package/dist/tools/semantic/index.d.ts +0 -9
  538. package/dist/tools/semantic/index.d.ts.map +0 -1
  539. package/dist/tools/semantic/index.js +0 -11
  540. package/dist/tools/semantic/index.js.map +0 -1
  541. package/dist/tools/semantic/lsp.d.ts +0 -68
  542. package/dist/tools/semantic/lsp.d.ts.map +0 -1
  543. package/dist/tools/semantic/lsp.js +0 -561
  544. package/dist/tools/semantic/lsp.js.map +0 -1
  545. package/dist/tools/spec/e2eTestGenerator.d.ts +0 -61
  546. package/dist/tools/spec/e2eTestGenerator.d.ts.map +0 -1
  547. package/dist/tools/spec/e2eTestGenerator.js +0 -255
  548. package/dist/tools/spec/e2eTestGenerator.js.map +0 -1
  549. package/dist/tools/spec/specVersioning.d.ts +0 -76
  550. package/dist/tools/spec/specVersioning.d.ts.map +0 -1
  551. package/dist/tools/spec/specVersioning.js +0 -236
  552. package/dist/tools/spec/specVersioning.js.map +0 -1
  553. package/hooks/scripts/__tests__/keyword-detector.test.js +0 -242
  554. package/hooks/scripts/keyword-detector.js +0 -226
  555. package/skills/characterization-test/SKILL.md +0 -209
  556. package/skills/characterization-test/agents/behavior-capturer.md +0 -50
  557. package/skills/characterization-test/agents/coverage-checker.md +0 -54
  558. package/skills/characterization-test/agents/reporter.md +0 -50
  559. package/skills/characterization-test/agents/test-writer.md +0 -49
  560. package/skills/characterization-test/rubrics/coverage-criteria.md +0 -53
  561. package/skills/characterization-test/templates/test-template.ts +0 -101
  562. package/skills/claude-md-guide/SKILL.md +0 -353
  563. package/skills/claude-md-guide/rubrics/anti-patterns.md +0 -88
  564. package/skills/design-audit/SKILL.md +0 -154
  565. package/skills/design-critique/SKILL.md +0 -140
  566. package/skills/design-distill/SKILL.md +0 -131
  567. package/skills/design-normalize/SKILL.md +0 -135
  568. package/skills/design-polish/SKILL.md +0 -132
  569. package/skills/figma-convert/SKILL.md +0 -236
  570. package/skills/figma-extract/SKILL.md +0 -242
  571. package/skills/interview/SKILL.md +0 -358
  572. package/skills/interview/checklists/api.md +0 -101
  573. package/skills/interview/checklists/feature.md +0 -88
  574. package/skills/interview/checklists/library.md +0 -95
  575. package/skills/interview/checklists/mobile.md +0 -89
  576. package/skills/interview/checklists/webapp.md +0 -97
  577. package/skills/interview/checklists/website.md +0 -99
  578. package/skills/plan/SKILL.md +0 -254
  579. package/skills/rob-pike/SKILL.md +0 -66
  580. package/skills/spec/references/askuser-examples.md +0 -57
  581. package/skills/spec/references/example-session.md +0 -87
  582. package/skills/spec/references/templates.md +0 -194
  583. package/skills/spec-review/SKILL.md +0 -725
  584. package/skills/systematic-debugging/SKILL.md +0 -142
  585. package/skills/techdebt/SKILL.md +0 -126
  586. package/skills/techdebt/agents/analyzer.md +0 -50
  587. package/skills/techdebt/agents/fixer.md +0 -41
  588. package/skills/techdebt/agents/reviewer.md +0 -47
  589. package/skills/techdebt/agents/scanner.md +0 -44
  590. package/skills/techdebt/orchestrator.md +0 -72
  591. package/skills/techdebt/rubrics/severity.md +0 -51
  592. package/skills/techdebt/scripts/scan.js +0 -90
  593. package/skills/techdebt/templates/report.md +0 -86
  594. package/skills/typescript-advanced-types/SKILL.md +0 -68
  595. package/skills/typescript-advanced-types/rubrics/type-patterns.md +0 -109
  596. package/skills/yagni-ladder/SKILL.md +0 -90
  597. /package/skills/{claude-md-guide → agents-md}/templates/claude-md.md +0 -0
  598. /package/skills/{design-polish → design-refine}/rubrics/polish-checklist.md +0 -0
  599. /package/skills/{design-normalize → design-refine}/rubrics/token-naming.md +0 -0
  600. /package/skills/{design-distill → design-refine}/templates/design-system.md +0 -0
  601. /package/skills/{design-audit → design-review}/agents/scorer.md +0 -0
  602. /package/skills/{design-audit → design-review}/rubrics/ai-slop-patterns.md +0 -0
  603. /package/skills/{design-audit → design-review}/rubrics/scoring.md +0 -0
  604. /package/skills/{design-critique → design-review}/rubrics/ux-heuristics.md +0 -0
  605. /package/skills/{figma-convert → figma}/rubrics/conversion-rules.md +0 -0
  606. /package/skills/{figma-extract → figma}/rubrics/image-rules.md +0 -0
  607. /package/skills/{figma-convert → figma}/templates/component.md +0 -0
@@ -650,34 +650,139 @@ const TAB_CONTENT_FP = `(function (g) {
650
650
  return (root.innerText || '').replace(/\\s+/g, ' ').trim().slice(0, 4000);
651
651
  })`;
652
652
 
653
- const wait = (ms) => new Promise((r) => setTimeout(r, ms));
653
+ // Hover diff props 색/그림자/변형 호버에서 실제로 바뀌는 것들
654
+ const HOVER_PROPS = [
655
+ 'color', 'background-color', 'border-color', 'box-shadow',
656
+ 'transform', 'opacity', 'text-decoration-line', 'filter', 'outline-width',
657
+ ];
654
658
 
655
- async function runInteractionSweep(page) {
656
- const behaviors = { scroll: [], interactive: [] };
659
+ // In-view 등장 애니메이션 diff props
660
+ const INVIEW_PROPS = ['opacity', 'transform', 'visibility', 'filter', 'clip-path'];
657
661
 
658
- // ── Scroll-triggered state (header/nav shrink, background, shadow on scroll) ──
659
- const candidates = await page.evaluate(`(${TAG_SCROLL_CANDIDATES})()`);
660
- if (candidates.length) {
661
- const before = await page.evaluate(`(${SNAP_PROBES})(${JSON.stringify(BEHAVIOR_PROPS)})`);
662
- const innerH = await page.evaluate('window.innerHeight');
663
- const triggerY = Math.min(700, Math.max(200, Math.round(innerH * 0.9)));
664
- await page.evaluate(`window.scrollTo(0, ${triggerY})`);
665
- await wait(500);
666
- const after = await page.evaluate(`(${SNAP_PROBES})(${JSON.stringify(BEHAVIOR_PROPS)})`);
667
- await page.evaluate('window.scrollTo(0, 0)');
668
- await wait(300);
669
- for (const c of candidates) {
670
- const changed = diffStyles(before[c.probe], after[c.probe]);
671
- if (Object.keys(changed).length) {
672
- behaviors.scroll.push({
673
- label: c.cls ? `${c.tag}.${c.cls}` : c.tag,
674
- tag: c.tag, cls: c.cls, triggerScrollY: triggerY, changed,
675
- });
662
+ // In-page: tag hover candidates. 같은 시그니처(tag+첫 클래스)는 1개만 샘플 nav 링크 30개 → 1개.
663
+ const TAG_HOVER_CANDIDATES = `(function () {
664
+ const els = Array.from(document.querySelectorAll('a, button, [role="button"], [class*="btn" i], [class*="card" i]'));
665
+ const seen = new Set(); const out = []; let i = 0;
666
+ for (const el of els) {
667
+ if (i >= 30) break;
668
+ const r = el.getBoundingClientRect();
669
+ if (r.width < 8 || r.height < 8) continue;
670
+ const cls = (el.getAttribute('class') || '').trim().split(/\\s+/).filter(Boolean)[0] || '';
671
+ const sig = el.tagName + '.' + cls;
672
+ if (seen.has(sig)) continue;
673
+ seen.add(sig);
674
+ el.setAttribute('data-clone-hover', 'h' + i);
675
+ out.push({ probe: 'h' + i, tag: el.tagName.toLowerCase(), cls, transition: getComputedStyle(el).transition });
676
+ i++;
677
+ }
678
+ return out;
679
+ })`;
680
+
681
+ // In-page: snapshot computed props of all elements tagged with an attribute.
682
+ const SNAP_ATTR = `(function (attr, props) {
683
+ const out = {};
684
+ document.querySelectorAll('[' + attr + ']').forEach((el) => {
685
+ const cs = getComputedStyle(el); const o = {};
686
+ for (const p of props) { const v = cs.getPropertyValue(p); if (v) o[p] = v.trim(); }
687
+ out[el.getAttribute(attr)] = o;
688
+ });
689
+ return out;
690
+ })`;
691
+
692
+ // In-page: tag below-the-fold elements that look "waiting to animate" (opacity 0 / transform offset).
693
+ const TAG_INVIEW = `(function () {
694
+ const innerH = window.innerHeight;
695
+ const all = Array.from(document.querySelectorAll('body *')).slice(0, 3000);
696
+ const out = []; let i = 0;
697
+ for (const el of all) {
698
+ if (i >= 20) break;
699
+ const r = el.getBoundingClientRect();
700
+ if (r.width < 40 || r.height < 40) continue;
701
+ if (r.top < innerH) continue;
702
+ const cs = getComputedStyle(el);
703
+ const hidden = parseFloat(cs.opacity) < 0.05;
704
+ const shifted = cs.transform && cs.transform !== 'none';
705
+ if (!hidden && !shifted) continue;
706
+ if (el.parentElement && el.parentElement.closest('[data-clone-inview]')) continue;
707
+ el.setAttribute('data-clone-inview', 'v' + i);
708
+ const cls = (el.getAttribute('class') || '').trim().split(/\\s+/).filter(Boolean)[0] || '';
709
+ out.push({ probe: 'v' + i, tag: el.tagName.toLowerCase(), cls, top: Math.round(r.top + window.scrollY) });
710
+ i++;
711
+ }
712
+ return out;
713
+ })`;
714
+
715
+ // In-page: observe class/style mutations for N ms with no input → time-driven candidates (carousels, cycling).
716
+ const OBSERVE_MUTATIONS = `(function (ms) {
717
+ return new Promise((resolve) => {
718
+ const counts = new Map();
719
+ const obs = new MutationObserver((muts) => {
720
+ for (const m of muts) {
721
+ const el = m.target;
722
+ if (!el || !el.tagName || el === document.body || el === document.documentElement) continue;
723
+ const tag = el.tagName.toLowerCase();
724
+ const cls = ((el.getAttribute && el.getAttribute('class')) || '').trim().split(/\\s+/).filter(Boolean)[0] || '';
725
+ const key = tag + (cls ? '.' + cls : '');
726
+ const e = counts.get(key) || { tag, cls, mutations: 0, kinds: new Set() };
727
+ e.mutations++; e.kinds.add(m.type === 'attributes' ? m.attributeName : m.type);
728
+ counts.set(key, e);
676
729
  }
730
+ });
731
+ obs.observe(document.body, { subtree: true, attributes: true, attributeFilter: ['class', 'style'], childList: true });
732
+ setTimeout(() => {
733
+ obs.disconnect();
734
+ resolve(Array.from(counts.entries())
735
+ .map(([label, e]) => ({ label, tag: e.tag, cls: e.cls, mutations: e.mutations, kinds: Array.from(e.kinds) }))
736
+ .sort((a, b) => b.mutations - a.mutations)
737
+ .slice(0, 10));
738
+ }, ms);
739
+ });
740
+ })`;
741
+
742
+ // In-page: smooth-scroll library detection (Lenis, Locomotive). 네이티브와 체감이 확연히 달라 놓치면 바로 티가 난다.
743
+ const DETECT_SCROLL_LIB = `(function () {
744
+ const checks = [
745
+ ['lenis', 'html.lenis, .lenis, [data-lenis]'],
746
+ ['locomotive-scroll', '.locomotive-scroll, [data-scroll-container]'],
747
+ ];
748
+ for (const [name, sel] of checks) {
749
+ if (document.querySelector(sel)) return { name, evidence: sel };
750
+ }
751
+ if (window.Lenis) return { name: 'lenis', evidence: 'window.Lenis' };
752
+ if (window.LocomotiveScroll || window.locomotive) return { name: 'locomotive-scroll', evidence: 'window.LocomotiveScroll' };
753
+ return null;
754
+ })`;
755
+
756
+ const wait = (ms) => new Promise((r) => setTimeout(r, ms));
757
+
758
+ // ── Scroll-triggered state (header/nav shrink, background, shadow on scroll) ──
759
+ async function sweepScrollState(page) {
760
+ const out = [];
761
+ const candidates = await page.evaluate(`(${TAG_SCROLL_CANDIDATES})()`);
762
+ if (!candidates.length) return out;
763
+ const before = await page.evaluate(`(${SNAP_PROBES})(${JSON.stringify(BEHAVIOR_PROPS)})`);
764
+ const innerH = await page.evaluate('window.innerHeight');
765
+ const triggerY = Math.min(700, Math.max(200, Math.round(innerH * 0.9)));
766
+ await page.evaluate(`window.scrollTo(0, ${triggerY})`);
767
+ await wait(500);
768
+ const after = await page.evaluate(`(${SNAP_PROBES})(${JSON.stringify(BEHAVIOR_PROPS)})`);
769
+ await page.evaluate('window.scrollTo(0, 0)');
770
+ await wait(300);
771
+ for (const c of candidates) {
772
+ const changed = diffStyles(before[c.probe], after[c.probe]);
773
+ if (Object.keys(changed).length) {
774
+ out.push({
775
+ label: c.cls ? `${c.tag}.${c.cls}` : c.tag,
776
+ tag: c.tag, cls: c.cls, triggerScrollY: triggerY, changed,
777
+ });
677
778
  }
678
779
  }
780
+ return out;
781
+ }
679
782
 
680
- // ── Click-driven tab groups (content swap detection) ──
783
+ // ── Click-driven tab groups (content swap detection) ──
784
+ async function sweepTabGroups(page) {
785
+ const out = [];
681
786
  const groups = await page.evaluate(`(${TAG_TAB_GROUPS})()`);
682
787
  for (const grp of groups) {
683
788
  try {
@@ -693,7 +798,7 @@ async function runInteractionSweep(page) {
693
798
  `(function (g) { const t = document.querySelector('[data-clone-tab="' + g + '_0"]'); if (t) t.click(); })(${grp.group})`
694
799
  );
695
800
  await wait(200);
696
- behaviors.interactive.push({
801
+ out.push({
697
802
  kind: 'tab-group',
698
803
  count: grp.count,
699
804
  tabLabels: grp.labels,
@@ -701,14 +806,105 @@ async function runInteractionSweep(page) {
701
806
  });
702
807
  } catch { /* one bad group must not abort the whole sweep */ }
703
808
  }
809
+ return out;
810
+ }
811
+
812
+ // ── Hover states set by JS (static :hover rules already land in states.json) ──
813
+ async function sweepHover(page) {
814
+ const out = [];
815
+ const candidates = await page.evaluate(`(${TAG_HOVER_CANDIDATES})()`);
816
+ for (const c of candidates) {
817
+ try {
818
+ const sel = `[data-clone-hover="${c.probe}"]`;
819
+ await page.evaluate(`document.querySelector('${sel}')?.scrollIntoView({ block: 'center' })`);
820
+ await wait(150);
821
+ const snap = `(${SNAP_ATTR})('data-clone-hover', ${JSON.stringify(HOVER_PROPS)})`;
822
+ const before = (await page.evaluate(snap))[c.probe];
823
+ await page.hover(sel);
824
+ await wait(350);
825
+ const after = (await page.evaluate(snap))[c.probe];
826
+ await page.mouse.move(0, 0);
827
+ await wait(150);
828
+ const changed = diffStyles(before, after);
829
+ if (Object.keys(changed).length) {
830
+ out.push({
831
+ label: c.cls ? `${c.tag}.${c.cls}` : c.tag,
832
+ tag: c.tag, cls: c.cls, transition: c.transition, changed,
833
+ });
834
+ }
835
+ } catch { /* fail-open: 요소 하나가 스윕 전체를 죽이면 안 된다 */ }
836
+ }
837
+ await page.evaluate('window.scrollTo(0, 0)');
838
+ await wait(200);
839
+ return out;
840
+ }
841
+
842
+ // ── In-view entrance animations (fade-up, slide-in) ──
843
+ // 1회성 등장 애니메이션은 lazy-load 스크롤에서 이미 발화했으므로 페이지를 새로 로드해 관찰한다.
844
+ async function sweepInView(page) {
845
+ const out = [];
846
+ await page.goto(page.url(), { waitUntil: 'networkidle2', timeout: 90000 });
847
+ await wait(800);
848
+ const candidates = await page.evaluate(`(${TAG_INVIEW})()`);
849
+ if (!candidates.length) return out;
850
+ const snap = `(${SNAP_ATTR})('data-clone-inview', ${JSON.stringify(INVIEW_PROPS)})`;
851
+ const before = await page.evaluate(snap);
852
+ for (const c of candidates) {
853
+ try {
854
+ await page.evaluate(`document.querySelector('[data-clone-inview="${c.probe}"]')?.scrollIntoView({ block: 'center' })`);
855
+ await wait(600);
856
+ } catch { /* fail-open */ }
857
+ }
858
+ const after = await page.evaluate(snap);
859
+ await page.evaluate('window.scrollTo(0, 0)');
860
+ await wait(300);
861
+ for (const c of candidates) {
862
+ const changed = diffStyles(before[c.probe], after[c.probe]);
863
+ if (Object.keys(changed).length) {
864
+ out.push({
865
+ label: c.cls ? `${c.tag}.${c.cls}` : c.tag,
866
+ tag: c.tag, cls: c.cls, triggerY: c.top, changed,
867
+ });
868
+ }
869
+ }
870
+ await page.evaluate('document.querySelectorAll("[data-clone-inview]").forEach((e) => e.removeAttribute("data-clone-inview"))');
871
+ return out;
872
+ }
873
+
874
+ async function runInteractionSweep(page) {
875
+ const behaviors = { scroll: [], interactive: [], hover: [], inview: [], timeDriven: [], scrollLib: null };
876
+
877
+ behaviors.scroll = await sweepScrollState(page);
878
+ behaviors.interactive = await sweepTabGroups(page);
879
+ behaviors.hover = await sweepHover(page);
880
+ // 무입력 3초 관찰 — 캐러셀/자동 사이클 후보
881
+ try { behaviors.timeDriven = await page.evaluate(`(${OBSERVE_MUTATIONS})(3000)`); } catch { /* fail-open */ }
882
+ try { behaviors.scrollLib = await page.evaluate(`(${DETECT_SCROLL_LIB})()`); } catch { /* fail-open */ }
704
883
 
705
884
  // Clean up probe attributes BEFORE static extraction so they don't pollute output.
706
885
  await page.evaluate(
707
- 'document.querySelectorAll("[data-clone-probe],[data-clone-tab]").forEach((e) => { e.removeAttribute("data-clone-probe"); e.removeAttribute("data-clone-tab"); })'
886
+ 'document.querySelectorAll("[data-clone-probe],[data-clone-tab],[data-clone-hover]").forEach((e) => { e.removeAttribute("data-clone-probe"); e.removeAttribute("data-clone-tab"); e.removeAttribute("data-clone-hover"); })'
708
887
  );
888
+
889
+ // 마지막: in-view 스윕은 리로드를 동반하므로 다른 스윕 뒤에 실행한다.
890
+ behaviors.inview = await sweepInView(page);
891
+ // 리로드로 lazy-load 상태가 초기화됐으니 정적 추출 전에 복원한다.
892
+ await scrollToBottom(page);
709
893
  return behaviors;
710
894
  }
711
895
 
896
+ // In-page: favicon / OG image / webmanifest URLs (Foundation 단계 — public/seo/ 배선용)
897
+ const SEO_EXTRACT = `(function () {
898
+ const abs = (u) => { try { return new URL(u, location.href).href; } catch { return null; } };
899
+ const icons = Array.from(document.querySelectorAll(
900
+ 'link[rel*="icon" i], link[rel="apple-touch-icon"], link[rel="manifest"]'
901
+ )).map((l) => abs(l.getAttribute('href'))).filter(Boolean);
902
+ const og = Array.from(document.querySelectorAll(
903
+ 'meta[property="og:image"], meta[name="twitter:image"]'
904
+ )).map((m) => abs(m.getAttribute('content'))).filter(Boolean);
905
+ return Array.from(new Set(icons.concat(og)));
906
+ })`;
907
+
712
908
  // HTML sanitization: strip scripts/analytics before saving
713
909
  function sanitizeHtml(html) {
714
910
  return html
@@ -739,8 +935,10 @@ async function capture({ url, opts }) {
739
935
  const assetsDir = path.join(opts.out, 'assets');
740
936
  const imagesDir = path.join(assetsDir, 'images');
741
937
  const fontsDir = path.join(assetsDir, 'fonts');
938
+ const seoDir = path.join(assetsDir, 'seo');
742
939
  fs.mkdirSync(imagesDir, { recursive: true });
743
940
  fs.mkdirSync(fontsDir, { recursive: true });
941
+ fs.mkdirSync(seoDir, { recursive: true });
744
942
 
745
943
  console.log(`[clone-extract] ${opts.bp || ''} ${viewport.width}x${viewport.height}@${viewport.deviceScaleFactor} ${url}`);
746
944
 
@@ -775,7 +973,12 @@ async function capture({ url, opts }) {
775
973
  if (opts.interact) {
776
974
  try {
777
975
  behaviors = await runInteractionSweep(page);
778
- console.log(`[clone-extract] interaction sweep: ${behaviors.scroll.length} scroll-state, ${behaviors.interactive.length} tab-group(s)`);
976
+ console.log(
977
+ `[clone-extract] interaction sweep: ${behaviors.scroll.length} scroll-state, ` +
978
+ `${behaviors.interactive.length} tab-group(s), ${behaviors.hover.length} hover, ` +
979
+ `${behaviors.inview.length} in-view, ${behaviors.timeDriven.length} time-driven` +
980
+ `${behaviors.scrollLib ? `, scroll-lib: ${behaviors.scrollLib.name}` : ''}`
981
+ );
779
982
  } catch (e) {
780
983
  console.log(`[clone-extract] interaction sweep skipped: ${e.message}`);
781
984
  }
@@ -805,9 +1008,14 @@ async function capture({ url, opts }) {
805
1008
  if (sorted[1]) fontDownloadSet.add(sorted[1].url);
806
1009
  }
807
1010
 
808
- console.log(`[clone-extract] downloading ${extracted.assets.images.length} images, ${fontDownloadSet.size} fonts (filtered from ${extracted.assets.fonts.length})`);
1011
+ // Favicon / OG / webmanifest Foundation 단계에서 public/seo/ 로 배선한다
1012
+ let seoUrls = [];
1013
+ try { seoUrls = await page.evaluate(`(${SEO_EXTRACT})()`); } catch { /* fail-open */ }
1014
+
1015
+ console.log(`[clone-extract] downloading ${extracted.assets.images.length} images, ${fontDownloadSet.size} fonts (filtered from ${extracted.assets.fonts.length}), ${seoUrls.length} seo assets`);
809
1016
  const imageMap = await downloadAssets(extracted.assets.images, imagesDir, 8);
810
1017
  const fontMap = await downloadAssets(Array.from(fontDownloadSet), fontsDir, 4);
1018
+ const seoMap = await downloadAssets(seoUrls, seoDir, 4);
811
1019
 
812
1020
  const assetMap = {};
813
1021
  for (const [u, info] of Object.entries(imageMap)) {
@@ -820,6 +1028,11 @@ async function capture({ url, opts }) {
820
1028
  ? { local: `assets/fonts/${info.local}`, status: 'ok', bytes: info.bytes, kind: 'font' }
821
1029
  : { ...info, kind: 'font' };
822
1030
  }
1031
+ for (const [u, info] of Object.entries(seoMap)) {
1032
+ assetMap[u] = info.status === 'ok'
1033
+ ? { local: `assets/seo/${info.local}`, status: 'ok', bytes: info.bytes, kind: 'seo' }
1034
+ : { ...info, kind: 'seo' };
1035
+ }
823
1036
 
824
1037
  // Rewrite HTML — replace remote URLs with local paths
825
1038
  let html = sanitizeHtml(extracted.html);
@@ -896,8 +1109,9 @@ async function capture({ url, opts }) {
896
1109
  );
897
1110
  fs.writeFileSync(path.join(opts.out, 'asset-map.json'), JSON.stringify(assetMap, null, 2));
898
1111
  if (behaviors) {
899
- // Rewrite any remote asset URLs captured in scroll-state background-image diffs.
900
- for (const s of behaviors.scroll) {
1112
+ // Rewrite any remote asset URLs captured in behavior diffs (scroll/hover/inview).
1113
+ const diffed = [...behaviors.scroll, ...(behaviors.hover || []), ...(behaviors.inview || [])];
1114
+ for (const s of diffed) {
901
1115
  for (const ch of Object.values(s.changed)) {
902
1116
  if (typeof ch.from === 'string') ch.from = rewriteCssValue(ch.from);
903
1117
  if (typeof ch.to === 'string') ch.to = rewriteCssValue(ch.to);
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * clone-merge-responsive.js — MO/PC per-BP SCSS → mobile-first responsive merge
5
+ *
6
+ * Usage:
7
+ * node clone-merge-responsive.js --mo=<styles-dir> --pc=<styles-dir> --out=<dir> [--breakpoint=1024]
8
+ *
9
+ * Why this exists (clone Phase 3C):
10
+ * Phase 3A/3B produce two fixed-viewport SCSS trees (styles/{feature}/mo, /pc).
11
+ * This merges them mobile-first: MO declarations become the base, and only the
12
+ * declarations where PC differs go into a single @media (min-width: <bp>) block.
13
+ * Deterministic text merge — no value invention. Post-merge correctness is judged
14
+ * by Phase 5 (pixel + computed-CSS verification at BOTH viewports).
15
+ *
16
+ * Fail-open:
17
+ * - PC-only selectors → whole rule inside the media block
18
+ * - MO-only selectors → base as-is (reported)
19
+ * - unparseable/missing files → reported and skipped, never a hard abort
20
+ */
21
+
22
+ import fs from 'fs';
23
+ import path from 'path';
24
+ import { parseScss, normalize } from './clone-validate.js';
25
+
26
+ // ─── CLI ────────────────────────────────────────────────────────────
27
+ function parseArgs(argv) {
28
+ const opts = { breakpoint: 1024 };
29
+ for (const a of argv.slice(2)) {
30
+ if (a.startsWith('--mo=')) opts.mo = a.slice(5);
31
+ else if (a.startsWith('--pc=')) opts.pc = a.slice(5);
32
+ else if (a.startsWith('--out=')) opts.out = a.slice(6);
33
+ else if (a.startsWith('--breakpoint=')) opts.breakpoint = Number(a.slice(13)) || 1024;
34
+ }
35
+ return opts;
36
+ }
37
+
38
+ // ─── Merge core (pure — exported for tests) ─────────────────────────
39
+ // moRules/pcRules: Map<selector, {prop: value}> (parseScss output)
40
+ // → { base: Map, media: Map, stats }
41
+ function mergeRules(moRules, pcRules) {
42
+ const base = new Map();
43
+ const media = new Map();
44
+ const stats = { selectors: 0, moOnly: 0, pcOnly: 0, overrides: 0, shared: 0 };
45
+
46
+ const selectors = new Set([...moRules.keys(), ...pcRules.keys()]);
47
+ for (const sel of selectors) {
48
+ stats.selectors++;
49
+ const mo = moRules.get(sel);
50
+ const pc = pcRules.get(sel);
51
+ if (mo && Object.keys(mo).length) base.set(sel, mo);
52
+
53
+ if (!pc || !Object.keys(pc).length) { if (mo) stats.moOnly++; continue; }
54
+ if (!mo || !Object.keys(mo).length) {
55
+ // PC 전용 셀렉터 — 데스크탑에서만 존재하는 노드
56
+ media.set(sel, pc);
57
+ stats.pcOnly++;
58
+ continue;
59
+ }
60
+
61
+ const diff = {};
62
+ for (const [prop, val] of Object.entries(pc)) {
63
+ if (mo[prop] === undefined || normalize(mo[prop]) !== normalize(val)) diff[prop] = val;
64
+ }
65
+ if (Object.keys(diff).length) { media.set(sel, diff); stats.overrides++; }
66
+ else stats.shared++;
67
+ }
68
+ return { base, media, stats };
69
+ }
70
+
71
+ function emitRules(rules, indent) {
72
+ const out = [];
73
+ for (const [sel, decls] of rules) {
74
+ const body = Object.entries(decls).map(([p, v]) => `${indent} ${p}: ${v};`).join('\n');
75
+ if (!body) continue;
76
+ out.push(`${indent}${sel} {\n${body}\n${indent}}`);
77
+ }
78
+ return out.join('\n\n');
79
+ }
80
+
81
+ // Merged partial: MO base rules + one @media (min-width) block with PC diffs.
82
+ function emitMerged(moRules, pcRules, breakpoint, header) {
83
+ const { base, media, stats } = mergeRules(moRules, pcRules);
84
+ const parts = [header, '', emitRules(base, '')];
85
+ if (media.size) {
86
+ parts.push('');
87
+ parts.push(`@media (min-width: ${breakpoint}px) {`);
88
+ parts.push(emitRules(media, ' '));
89
+ parts.push('}');
90
+ }
91
+ return { scss: parts.filter((p) => p !== null).join('\n') + '\n', stats };
92
+ }
93
+
94
+ // ─── File-level orchestration ───────────────────────────────────────
95
+ function readScssRules(file) {
96
+ if (!fs.existsSync(file)) return null;
97
+ try { return parseScss(fs.readFileSync(file, 'utf8')); }
98
+ catch (e) {
99
+ console.error(`[clone-merge] parse skipped (${e.message}): ${file}`);
100
+ return null;
101
+ }
102
+ }
103
+
104
+ function listSections(dir) {
105
+ const d = path.join(dir, 'sections');
106
+ if (!fs.existsSync(d)) return [];
107
+ return fs.readdirSync(d).filter((f) => f.startsWith('_') && f.endsWith('.scss'));
108
+ }
109
+
110
+ function mergeTokens(moDir, pcDir, outDir, breakpoint) {
111
+ const mo = readScssRules(path.join(moDir, '_tokens.scss')) || new Map();
112
+ const pc = readScssRules(path.join(pcDir, '_tokens.scss')) || new Map();
113
+ const { scss } = emitMerged(mo, pc, breakpoint, '// Auto-merged by clone-merge-responsive.js (mobile-first)');
114
+ fs.writeFileSync(path.join(outDir, '_tokens.scss'), scss);
115
+ }
116
+
117
+ function copyIfExists(src, dst) {
118
+ if (fs.existsSync(src)) fs.copyFileSync(src, dst);
119
+ }
120
+
121
+ function mergeClassPlans(moDir, pcDir, outDir) {
122
+ const read = (d) => {
123
+ const f = path.join(d, 'class-plan.json');
124
+ return fs.existsSync(f) ? JSON.parse(fs.readFileSync(f, 'utf8')) : {};
125
+ };
126
+ // MO 우선 — 동일 노드 id가 다른 클래스면 MO가 base이므로 MO를 채택
127
+ const merged = { ...read(pcDir), ...read(moDir) };
128
+ fs.writeFileSync(path.join(outDir, 'class-plan.json'), JSON.stringify(merged, null, 2));
129
+ }
130
+
131
+ function main() {
132
+ const opts = parseArgs(process.argv);
133
+ if (!opts.mo || !opts.pc || !opts.out) {
134
+ console.error('Usage: node clone-merge-responsive.js --mo=<styles-dir> --pc=<styles-dir> --out=<dir> [--breakpoint=1024]');
135
+ process.exit(2);
136
+ }
137
+
138
+ fs.mkdirSync(path.join(opts.out, 'sections'), { recursive: true });
139
+
140
+ const files = [...new Set([...listSections(opts.mo), ...listSections(opts.pc)])].sort();
141
+ const totals = { selectors: 0, moOnly: 0, pcOnly: 0, overrides: 0, shared: 0 };
142
+ const merged = [];
143
+
144
+ for (const f of files) {
145
+ const mo = readScssRules(path.join(opts.mo, 'sections', f)) || new Map();
146
+ const pc = readScssRules(path.join(opts.pc, 'sections', f)) || new Map();
147
+ if (!mo.size && !pc.size) { console.error(`[clone-merge] empty on both BPs, skipped: ${f}`); continue; }
148
+ const { scss, stats } = emitMerged(mo, pc, opts.breakpoint, `// Section: ${f} — merged mobile-first`);
149
+ fs.writeFileSync(path.join(opts.out, 'sections', f), scss);
150
+ for (const k of Object.keys(totals)) totals[k] += stats[k];
151
+ merged.push(f);
152
+ }
153
+
154
+ mergeTokens(opts.mo, opts.pc, opts.out, opts.breakpoint);
155
+ copyIfExists(path.join(opts.mo, '_base.scss'), path.join(opts.out, '_base.scss'));
156
+ copyIfExists(path.join(opts.mo, '_shared.scss'), path.join(opts.out, '_shared.scss'));
157
+ mergeClassPlans(opts.mo, opts.pc, opts.out);
158
+
159
+ const index = [
160
+ '// Auto-merged by clone-merge-responsive.js — mobile-first responsive',
161
+ '',
162
+ `@use './tokens';`,
163
+ `@use './base';`,
164
+ `@use './shared';`,
165
+ ...merged.map((f) => `@use './sections/${f.replace(/^_|\.scss$/g, '')}';`),
166
+ ].join('\n') + '\n';
167
+ fs.writeFileSync(path.join(opts.out, 'index.scss'), index);
168
+
169
+ console.log(`[clone-merge] done → ${opts.out}`);
170
+ console.log(` sections: ${merged.length}, selectors: ${totals.selectors} (shared ${totals.shared}, overrides ${totals.overrides}, mo-only ${totals.moOnly}, pc-only ${totals.pcOnly})`);
171
+ console.log(` breakpoint: @media (min-width: ${opts.breakpoint}px)`);
172
+ }
173
+
174
+ const isMain = process.argv[1] && process.argv[1].replace(/\\/g, '/').endsWith('clone-merge-responsive.js');
175
+ if (isMain) {
176
+ try { main(); }
177
+ catch (e) {
178
+ console.error(`[clone-merge] FAIL: ${e.message}`);
179
+ if (process.env.DEBUG) console.error(e.stack);
180
+ process.exit(1);
181
+ }
182
+ }
183
+
184
+ export { mergeRules, emitMerged };
@@ -28,6 +28,7 @@ function parseArgs(argv) {
28
28
  else if (a.startsWith('--section=')) opts.section = a.slice(10);
29
29
  else if (a.startsWith('--feature=')) opts.feature = a.slice(10);
30
30
  else if (a.startsWith('--behaviors=')) opts.behaviors = a.slice(12);
31
+ else if (a === '--real-content') opts.realContent = true;
31
32
  }
32
33
  return { sectionsPath, opts };
33
34
  }
@@ -101,20 +102,28 @@ function sectionHasNode(section, tag, cls) {
101
102
  return (section.children || []).some(hit);
102
103
  }
103
104
 
105
+ function diffLines(changed) {
106
+ return Object.entries(changed)
107
+ .map(([k, v]) => ` - ${k}: \`${v.from}\` → \`${v.to}\``).join('\n');
108
+ }
109
+
104
110
  function behaviorsBlock(section, behaviors) {
105
111
  if (!behaviors) return null;
106
- const scroll = (behaviors.scroll || []).filter((b) => sectionHasNode(section, b.tag, b.cls));
112
+ const inSection = (list) => (list || []).filter((b) => sectionHasNode(section, b.tag, b.cls));
113
+ const scroll = inSection(behaviors.scroll);
114
+ const hover = inSection(behaviors.hover);
115
+ const inview = inSection(behaviors.inview);
116
+ const timed = inSection(behaviors.timeDriven);
107
117
  // Tab groups aren't tag-keyed; attach to any section that contains a [role=tab]-ish node.
108
118
  const tabs = (behaviors.interactive || []).filter(() =>
109
119
  sectionHasNode(section, 'button') || sectionHasNode(section, 'li') || sectionHasNode(section, 'a'),
110
120
  );
111
- if (!scroll.length && !tabs.length) return null;
121
+ const lib = behaviors.scrollLib;
122
+ if (!scroll.length && !tabs.length && !hover.length && !inview.length && !timed.length && !lib) return null;
112
123
 
113
124
  const lines = [];
114
125
  for (const b of scroll) {
115
- const diffs = Object.entries(b.changed)
116
- .map(([k, v]) => ` - ${k}: \`${v.from}\` → \`${v.to}\``).join('\n');
117
- lines.push(`- **Scroll-triggered** on \`${b.label}\` (past ~${b.triggerScrollY}px scroll):\n${diffs}`);
126
+ lines.push(`- **Scroll-triggered** on \`${b.label}\` (past ~${b.triggerScrollY}px scroll):\n${diffLines(b.changed)}`);
118
127
  }
119
128
  for (const t of tabs) {
120
129
  const labels = (t.tabLabels || []).map((l) => `"${l}"`).join(', ') || '(unlabeled)';
@@ -123,6 +132,19 @@ function behaviorsBlock(section, behaviors) {
123
132
  : 'no content swap detected (styling-only tabs)';
124
133
  lines.push(`- **Tab group** ×${t.count} [${labels}]: ${swap}`);
125
134
  }
135
+ for (const b of hover) {
136
+ const tr = b.transition && b.transition !== 'all 0s ease 0s' ? ` (transition: \`${b.transition}\`)` : '';
137
+ lines.push(`- **Hover** on \`${b.label}\`${tr}:\n${diffLines(b.changed)}`);
138
+ }
139
+ for (const b of inview) {
140
+ lines.push(`- **In-view entrance** on \`${b.label}\` (enters at ~${b.triggerY}px):\n${diffLines(b.changed)}`);
141
+ }
142
+ for (const b of timed) {
143
+ lines.push(`- **Time-driven** \`${b.label}\` — ${b.mutations} mutations/3s [${(b.kinds || []).join(', ')}] → carousel/auto-cycle candidate`);
144
+ }
145
+ if (lib) {
146
+ lines.push(`- **Smooth-scroll library (page-level):** ${lib.name} (evidence: \`${lib.evidence}\`) → wire in globals, not per-section`);
147
+ }
126
148
  return lines.join('\n');
127
149
  }
128
150
 
@@ -171,7 +193,9 @@ ${cssBlock(section.css)}
171
193
  ## Assets (local paths only — never hotlink)
172
194
  ${assetsBlock(section.images)}
173
195
 
174
- ## Text content (replace copyrighted copy with placeholders)
196
+ ## Text content ${meta.realContent
197
+ ? '(verbatim — user confirmed rights to reuse this copy)'
198
+ : '(replace copyrighted copy with placeholders)'}
175
199
  ${textBlock}
176
200
 
177
201
  ## Component candidates
@@ -179,7 +203,7 @@ ${componentsBlock(section.components)}
179
203
 
180
204
  ## Build checklist
181
205
  - [ ] HTML semantics + final tags chosen
182
- - [ ] SCSS via clone-to-scss.js (computed values as-is no eyeballing)
206
+ - [ ] SCSS started from the clone-to-scss.js draft value edits only with evidence cited from computed.json/states.json/behaviors.json (no eyeballing); clone-validate.js is the judge
183
207
  - [ ] Every state above implemented
184
208
  - [ ] Interaction model confirmed on live site & wired
185
209
  - [ ] All assets local (public/images/${meta.feature}/) — no hotlinks
@@ -196,6 +220,7 @@ function main() {
196
220
  url: data.meta && data.meta.url,
197
221
  viewport: data.meta && data.meta.viewport,
198
222
  bp: opts.section ? (data.meta && data.meta.bp) : (data.meta && data.meta.bp) || '?',
223
+ realContent: !!opts.realContent,
199
224
  };
200
225
  // behaviors.json (active interaction sweep) lives next to the source computed.json.
201
226
  const behaviorsPath = opts.behaviors || path.join(path.dirname(sectionsPath), 'behaviors.json');
@@ -223,7 +248,7 @@ function main() {
223
248
  const isMain = process.argv[1] && process.argv[1].replace(/\\/g, '/').endsWith('clone-spec.js');
224
249
  if (isMain) {
225
250
  if (!sectionsPath || !opts.out) {
226
- console.error('Usage: node clone-spec.js <sections.json> --out=<dir> [--section=Name] [--feature=name] [--behaviors=path]');
251
+ console.error('Usage: node clone-spec.js <sections.json> --out=<dir> [--section=Name] [--feature=name] [--behaviors=path] [--real-content]');
227
252
  process.exit(1);
228
253
  }
229
254
  try { main(); }
@@ -34,10 +34,6 @@ function parseArgs(argv) {
34
34
  }
35
35
 
36
36
  const { stylesDir, sectionsPath, opts } = parseArgs(process.argv);
37
- if (!stylesDir || !sectionsPath) {
38
- console.error('Usage: node clone-validate.js <styles-dir> <sections.json> [--section=<name>]');
39
- process.exit(2);
40
- }
41
37
 
42
38
  // ─── Minimal SCSS parser ────────────────────────────────────────────
43
39
  // Strips comments, expands `&::pseudo` (one level), flattens nested rules.
@@ -272,9 +268,19 @@ function main() {
272
268
  process.exit(0);
273
269
  }
274
270
 
275
- try { main(); }
276
- catch (e) {
277
- console.error(`[clone-validate] FAIL: ${e.message}`);
278
- if (process.env.DEBUG) console.error(e.stack);
279
- process.exit(2);
271
+ // isMain 가드 — clone-merge-responsive.js가 parseScss를 import할 때 main() 돌면 안 된다
272
+ const isMain = process.argv[1] && process.argv[1].replace(/\\/g, '/').endsWith('clone-validate.js');
273
+ if (isMain) {
274
+ if (!stylesDir || !sectionsPath) {
275
+ console.error('Usage: node clone-validate.js <styles-dir> <sections.json> [--section=<name>]');
276
+ process.exit(2);
277
+ }
278
+ try { main(); }
279
+ catch (e) {
280
+ console.error(`[clone-validate] FAIL: ${e.message}`);
281
+ if (process.env.DEBUG) console.error(e.stack);
282
+ process.exit(2);
283
+ }
280
284
  }
285
+
286
+ export { parseScss, normalize, compareValue };