@su-record/vibe 3.1.0 → 3.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (183) hide show
  1. package/CLAUDE.md +2 -2
  2. package/README.en.md +9 -6
  3. package/README.md +9 -4
  4. package/dist/__tests__/wiring-integrity.test.d.ts +2 -0
  5. package/dist/__tests__/wiring-integrity.test.d.ts.map +1 -0
  6. package/dist/__tests__/wiring-integrity.test.js +160 -0
  7. package/dist/__tests__/wiring-integrity.test.js.map +1 -0
  8. package/dist/cli/commands/info.d.ts.map +1 -1
  9. package/dist/cli/commands/info.js +2 -1
  10. package/dist/cli/commands/info.js.map +1 -1
  11. package/dist/cli/commands/init.d.ts.map +1 -1
  12. package/dist/cli/commands/init.js +4 -2
  13. package/dist/cli/commands/init.js.map +1 -1
  14. package/dist/cli/design/design-md-parser.test.js +2 -2
  15. package/dist/cli/design/design-md-parser.test.js.map +1 -1
  16. package/dist/cli/index.js +102 -128
  17. package/dist/cli/index.js.map +1 -1
  18. package/dist/cli/postinstall/claude-agents.d.ts.map +1 -1
  19. package/dist/cli/postinstall/claude-agents.js +0 -5
  20. package/dist/cli/postinstall/claude-agents.js.map +1 -1
  21. package/dist/cli/postinstall/constants.d.ts +6 -0
  22. package/dist/cli/postinstall/constants.d.ts.map +1 -1
  23. package/dist/cli/postinstall/constants.js +42 -60
  24. package/dist/cli/postinstall/constants.js.map +1 -1
  25. package/dist/cli/postinstall/inline-skills.js +2 -2
  26. package/dist/cli/postinstall/inline-skills.js.map +1 -1
  27. package/dist/cli/postinstall/main.d.ts.map +1 -1
  28. package/dist/cli/postinstall/main.js +2 -0
  29. package/dist/cli/postinstall/main.js.map +1 -1
  30. package/dist/cli/postinstall.d.ts +6 -1
  31. package/dist/cli/postinstall.d.ts.map +1 -1
  32. package/dist/cli/postinstall.js +6 -1
  33. package/dist/cli/postinstall.js.map +1 -1
  34. package/dist/cli/setup/GlobalInstaller.d.ts +1 -15
  35. package/dist/cli/setup/GlobalInstaller.d.ts.map +1 -1
  36. package/dist/cli/setup/GlobalInstaller.js +16 -115
  37. package/dist/cli/setup/GlobalInstaller.js.map +1 -1
  38. package/dist/cli/setup/GlobalInstaller.test.d.ts +2 -0
  39. package/dist/cli/setup/GlobalInstaller.test.d.ts.map +1 -0
  40. package/dist/cli/setup/GlobalInstaller.test.js +16 -0
  41. package/dist/cli/setup/GlobalInstaller.test.js.map +1 -0
  42. package/dist/cli/setup/ProjectSetup.js +2 -2
  43. package/dist/cli/setup/ProjectSetup.js.map +1 -1
  44. package/dist/cli/setup/Provisioner.d.ts.map +1 -1
  45. package/dist/cli/setup/Provisioner.js +23 -7
  46. package/dist/cli/setup/Provisioner.js.map +1 -1
  47. package/dist/cli/setup.d.ts +1 -1
  48. package/dist/cli/setup.d.ts.map +1 -1
  49. package/dist/cli/setup.js +1 -1
  50. package/dist/cli/setup.js.map +1 -1
  51. package/dist/cli/utils/cli-detector.d.ts +3 -3
  52. package/dist/cli/utils/cli-detector.d.ts.map +1 -1
  53. package/dist/cli/utils/cli-detector.js +7 -27
  54. package/dist/cli/utils/cli-detector.js.map +1 -1
  55. package/dist/cli/utils/cli-detector.test.js +3 -2
  56. package/dist/cli/utils/cli-detector.test.js.map +1 -1
  57. package/dist/infra/lib/SkillRepository.js +1 -1
  58. package/dist/infra/lib/evolution/__tests__/integration.test.js +0 -42
  59. package/dist/infra/lib/evolution/__tests__/integration.test.js.map +1 -1
  60. package/dist/infra/lib/llm-availability.d.ts.map +1 -1
  61. package/dist/infra/lib/llm-availability.js +2 -10
  62. package/dist/infra/lib/llm-availability.js.map +1 -1
  63. package/dist/infra/lib/llm-availability.test.js +3 -2
  64. package/dist/infra/lib/llm-availability.test.js.map +1 -1
  65. package/dist/infra/lib/utils.d.ts +6 -0
  66. package/dist/infra/lib/utils.d.ts.map +1 -1
  67. package/dist/infra/lib/utils.js +33 -0
  68. package/dist/infra/lib/utils.js.map +1 -1
  69. package/dist/tools/convention/validateCodeQuality.d.ts +1 -0
  70. package/dist/tools/convention/validateCodeQuality.d.ts.map +1 -1
  71. package/dist/tools/convention/validateCodeQuality.js +3 -2
  72. package/dist/tools/convention/validateCodeQuality.js.map +1 -1
  73. package/dist/tools/index.d.ts +2 -2
  74. package/dist/tools/index.d.ts.map +1 -1
  75. package/dist/tools/index.js +2 -0
  76. package/dist/tools/index.js.map +1 -1
  77. package/dist/tools/spec/executionPacket.d.ts +101 -0
  78. package/dist/tools/spec/executionPacket.d.ts.map +1 -0
  79. package/dist/tools/spec/executionPacket.js +394 -0
  80. package/dist/tools/spec/executionPacket.js.map +1 -0
  81. package/dist/tools/spec/executionPacket.test.d.ts +2 -0
  82. package/dist/tools/spec/executionPacket.test.d.ts.map +1 -0
  83. package/dist/tools/spec/executionPacket.test.js +360 -0
  84. package/dist/tools/spec/executionPacket.test.js.map +1 -0
  85. package/dist/tools/spec/index.d.ts +2 -0
  86. package/dist/tools/spec/index.d.ts.map +1 -1
  87. package/dist/tools/spec/index.js +2 -0
  88. package/dist/tools/spec/index.js.map +1 -1
  89. package/dist/tools/spec/specGenerator.d.ts +12 -0
  90. package/dist/tools/spec/specGenerator.d.ts.map +1 -1
  91. package/dist/tools/spec/specGenerator.js +96 -14
  92. package/dist/tools/spec/specGenerator.js.map +1 -1
  93. package/dist/tools/spec/specGenerator.test.d.ts +2 -0
  94. package/dist/tools/spec/specGenerator.test.d.ts.map +1 -0
  95. package/dist/tools/spec/specGenerator.test.js +130 -0
  96. package/dist/tools/spec/specGenerator.test.js.map +1 -0
  97. package/hooks/scripts/__tests__/.vibe/command-log.txt +3 -3
  98. package/hooks/scripts/__tests__/clone-behaviors.test.js +33 -1
  99. package/hooks/scripts/__tests__/code-check-memory-write.test.js +81 -0
  100. package/hooks/scripts/__tests__/llm-orchestrate-antigravity.test.js +5 -2
  101. package/hooks/scripts/__tests__/run-ledger-verify-required.test.js +7 -2
  102. package/hooks/scripts/__tests__/run-ledger.test.js +161 -10
  103. package/hooks/scripts/__tests__/step-counter.test.js +21 -0
  104. package/hooks/scripts/__tests__/utils-npm-root.test.js +5 -4
  105. package/hooks/scripts/auto-format.js +2 -11
  106. package/hooks/scripts/auto-test.js +2 -11
  107. package/hooks/scripts/clone-extract.js +146 -10
  108. package/hooks/scripts/code-check.js +27 -55
  109. package/hooks/scripts/lib/glob.js +32 -0
  110. package/hooks/scripts/lib/hook-context.js +19 -2
  111. package/hooks/scripts/lib/run-ledger.js +123 -21
  112. package/hooks/scripts/post-edit.js +2 -3
  113. package/hooks/scripts/prompt-dispatcher.js +1 -1
  114. package/hooks/scripts/scope-guard.js +3 -42
  115. package/hooks/scripts/sentinel-guard.js +5 -20
  116. package/hooks/scripts/session-start.js +2 -2
  117. package/hooks/scripts/step-counter.js +35 -57
  118. package/hooks/scripts/verify-ledger.js +23 -3
  119. package/package.json +2 -3
  120. package/skills/clone/SKILL.md +10 -216
  121. package/skills/clone/references/capture-rules.md +48 -0
  122. package/skills/clone/references/legal-and-error-recovery.md +40 -0
  123. package/skills/clone/references/refine-rules.md +61 -0
  124. package/skills/clone/references/scaffold-phases.md +59 -0
  125. package/skills/clone/references/setup-and-layout.md +30 -0
  126. package/skills/docs/SKILL.md +39 -4
  127. package/skills/docs/references/api-docs-changelog.md +34 -0
  128. package/skills/docs/references/codemaps-output.md +23 -0
  129. package/{agents/diagrammer.md → skills/docs/references/diagram-spec.md} +2 -15
  130. package/skills/exec-plan/SKILL.md +1 -1
  131. package/skills/exec-plan/templates/plan.md +1 -1
  132. package/skills/handoff/SKILL.md +3 -3
  133. package/skills/handoff/templates/handoff.md +1 -1
  134. package/skills/spec/SKILL.md +7 -0
  135. package/skills/vibe/SKILL.md +3 -2
  136. package/skills/vibe.analyze/SKILL.md +12 -249
  137. package/skills/vibe.analyze/references/output-templates.md +188 -0
  138. package/skills/vibe.analyze/references/quality-gate.md +93 -0
  139. package/skills/vibe.clone/SKILL.md +14 -1
  140. package/skills/vibe.continue/SKILL.md +32 -0
  141. package/skills/vibe.design/SKILL.md +23 -1
  142. package/skills/vibe.design/references/ui-preview.md +20 -0
  143. package/skills/vibe.docs/SKILL.md +4 -2
  144. package/skills/vibe.figma/SKILL.md +33 -260
  145. package/skills/vibe.figma/references/branch-phases.md +113 -0
  146. package/skills/vibe.figma/references/state-schema.md +42 -0
  147. package/skills/vibe.figma/references/step-algorithms.md +204 -0
  148. package/skills/vibe.image/SKILL.md +62 -0
  149. package/skills/vibe.image/references/image-generation-examples.md +39 -0
  150. package/skills/vibe.reason/SKILL.md +3 -183
  151. package/skills/vibe.reason/references/output-format-template.md +57 -0
  152. package/skills/vibe.reason/references/quality-rubrics.md +94 -0
  153. package/skills/vibe.reason/references/worked-example.md +45 -0
  154. package/skills/vibe.review/SKILL.md +13 -348
  155. package/skills/vibe.review/references/boundary-check.md +25 -0
  156. package/skills/vibe.review/references/output-template.md +74 -0
  157. package/skills/vibe.review/references/quality-gate.md +84 -0
  158. package/skills/vibe.review/references/race-mode.md +117 -0
  159. package/skills/vibe.review/references/worked-examples.md +94 -0
  160. package/skills/vibe.run/SKILL.md +29 -6
  161. package/skills/vibe.trace/SKILL.md +3 -3
  162. package/skills/vibe.verify/SKILL.md +10 -4
  163. package/vibe/rules/loop-contract.md +7 -3
  164. package/vibe/templates/claudemd-template.md +1 -1
  165. package/vibe/templates/spec-template.md +32 -5
  166. package/agents/acceptance-tester.md +0 -56
  167. package/agents/documenter.md +0 -43
  168. package/agents/figma/figma-engineer.md +0 -76
  169. package/dist/cli/postinstall/index.d.ts +0 -23
  170. package/dist/cli/postinstall/index.d.ts.map +0 -1
  171. package/dist/cli/postinstall/index.js +0 -23
  172. package/dist/cli/postinstall/index.js.map +0 -1
  173. package/dist/cli/setup/index.d.ts +0 -9
  174. package/dist/cli/setup/index.d.ts.map +0 -1
  175. package/dist/cli/setup/index.js +0 -12
  176. package/dist/cli/setup/index.js.map +0 -1
  177. package/hooks/scripts/__tests__/.vibe/memories/memories.db +0 -0
  178. package/hooks/scripts/__tests__/.vibe/memories/memories.db-shm +0 -0
  179. package/hooks/scripts/__tests__/.vibe/memories/memories.db-wal +0 -0
  180. package/hooks/scripts/evolution-engine.js +0 -91
  181. package/hooks/scripts/hud-status.js +0 -321
  182. package/hooks/scripts/skill-injector.js +0 -83
  183. package/skills/vibe.utils/SKILL.md +0 -415
@@ -25,6 +25,7 @@
25
25
  import fs from 'fs';
26
26
  import path from 'path';
27
27
  import { PROJECT_DIR, projectVibePath } from './utils.js';
28
+ import { buildCliCtx } from './lib/hook-context.js';
28
29
 
29
30
  const METRICS_DIR = projectVibePath(PROJECT_DIR, 'metrics');
30
31
  const ANTI_PATTERNS_DIR = projectVibePath(PROJECT_DIR, 'anti-patterns');
@@ -44,36 +45,14 @@ const READ_ONLY_TOOLS = new Set([
44
45
  'TodoWrite', 'ToolSearch', 'ListMcpResourcesTool',
45
46
  ]);
46
47
 
47
- // ─────────────────────────────────────────────────────
48
- // stdin / env 에서 PostToolUse payload 추출
49
- // ─────────────────────────────────────────────────────
50
- function readStdinSync() {
51
- try {
52
- if (process.stdin.isTTY) return null;
53
- const buf = Buffer.alloc(65536);
54
- const bytesRead = fs.readSync(0, buf, 0, buf.length, null);
55
- if (bytesRead > 0) return JSON.parse(buf.toString('utf-8', 0, bytesRead));
56
- } catch { /* ignore */ }
57
- return null;
58
- }
59
-
60
- function parseToolInput(raw) {
61
- if (!raw) return {};
62
- if (typeof raw === 'string') {
63
- try { return JSON.parse(raw); } catch { return {}; }
64
- }
65
- return raw;
66
- }
67
-
68
- function extractTargetFile(toolInput) {
69
- const fp = toolInput.file_path || toolInput.notebook_path || toolInput.path || null;
70
- if (!fp) return null;
48
+ function extractTargetFile(filePath) {
49
+ if (!filePath) return null;
71
50
  try {
72
- const abs = path.isAbsolute(fp) ? fp : path.resolve(PROJECT_DIR, fp);
51
+ const abs = path.isAbsolute(filePath) ? filePath : path.resolve(PROJECT_DIR, filePath);
73
52
  const rel = path.relative(path.resolve(PROJECT_DIR), abs).replace(/\\/g, '/');
74
- return rel || path.basename(fp);
53
+ return rel || path.basename(filePath);
75
54
  } catch {
76
- return fp;
55
+ return filePath;
77
56
  }
78
57
  }
79
58
 
@@ -180,19 +159,18 @@ function bumpCounter(toolName, currentLineCount) {
180
159
  // 책임 2: current-run.jsonl append + error_category 채움
181
160
  // 항상 즉시 append — 스로틀 없음 (3-fail detector, recipe-extractor 가 소비)
182
161
  // ─────────────────────────────────────────────────────
183
- function appendJsonl(stdinPayload) {
184
- const toolName = stdinPayload?.tool_name || process.argv[2] || '';
162
+ function appendJsonl(ctx) {
163
+ const toolName = ctx.toolName;
185
164
  if (!toolName) return null;
186
165
 
187
- const toolInput = parseToolInput(stdinPayload?.tool_input ?? process.env.TOOL_INPUT);
188
- const ok = !isResponseError(stdinPayload?.tool_response);
189
- const errorCategory = ok ? null : classifyError(stdinPayload?.tool_response);
166
+ const ok = !isResponseError(ctx.payload?.tool_response);
167
+ const errorCategory = ok ? null : classifyError(ctx.payload?.tool_response);
190
168
 
191
169
  const record = {
192
170
  ts: new Date().toISOString(),
193
171
  tool: toolName,
194
172
  ok,
195
- target_file: extractTargetFile(toolInput),
173
+ target_file: extractTargetFile(ctx.filePath),
196
174
  error_category: errorCategory,
197
175
  };
198
176
 
@@ -214,18 +192,27 @@ function appendJsonl(stdinPayload) {
214
192
  }
215
193
 
216
194
  /**
217
- * jsonl 현재 라인 수를 반환 (stat 으로만 전체 파일 읽기 없음).
218
- * 경합 안전 정확한 값이 아니어도 스로틀 판단에 충분하다.
195
+ * current-run.jsonl 1회만 읽어 (a) 스로틀 판단용 라인 수와
196
+ * (b) 3-fail detector 최근 FAIL_WINDOW 줄을 함께 파생한다.
197
+ *
198
+ * 이전에는 jsonlLineCount()·readTailWindow() 가 각자 파일 전체를 다시 읽어
199
+ * 실패 이벤트마다 이벤트당 최대 2회 풀리드가 발생했다 — 여기서 1회로 통합.
200
+ * 파일은 회전 상한(2MB / MAX_JSONL_LINES)을 벗어나지 않도록 유지되므로
201
+ * (appendJsonl의 회전 로직 참고) 이 read 로도 기존과 동일하게 정확한 라인 수를
202
+ * 얻는다 — 스로틀/3-fail 판정 결과는 회전 상한 이내 파일에서 이전과 동일하다.
203
+ * @returns {{ lineCount: number, tail: object[] }}
219
204
  */
220
- function jsonlLineCount() {
205
+ function readJsonlState() {
206
+ let lines;
221
207
  try {
222
- // 라인 근사: 파일 크기 / 평균 라인 길이(100바이트) — 정확도 불필요
223
- // 실제 라인 수는 appendJsonl 직후 여기서 읽으면 +1 반영됨
224
- const content = fs.readFileSync(CURRENT_RUN_JSONL, 'utf-8');
225
- return content.split('\n').filter(Boolean).length;
208
+ lines = fs.readFileSync(CURRENT_RUN_JSONL, 'utf-8').split('\n').filter(Boolean);
226
209
  } catch {
227
- return 1; // 파일 없음 → 첫 이벤트로 간주
210
+ return { lineCount: 1, tail: [] }; // 파일 없음 → 첫 이벤트로 간주
228
211
  }
212
+ const tail = lines.slice(-FAIL_WINDOW).map((l) => {
213
+ try { return JSON.parse(l); } catch { return null; }
214
+ }).filter(Boolean);
215
+ return { lineCount: lines.length, tail };
229
216
  }
230
217
 
231
218
  // ─────────────────────────────────────────────────────
@@ -248,16 +235,7 @@ function fileSlug(targetFile) {
248
235
  return targetFile.replace(/[\/\.]/g, '-');
249
236
  }
250
237
 
251
- function readTailWindow() {
252
- if (!fs.existsSync(CURRENT_RUN_JSONL)) return [];
253
- const raw = fs.readFileSync(CURRENT_RUN_JSONL, 'utf-8').split('\n').filter(Boolean);
254
- return raw.slice(-FAIL_WINDOW).map((l) => {
255
- try { return JSON.parse(l); } catch { return null; }
256
- }).filter(Boolean);
257
- }
258
-
259
- function detectThreeFail() {
260
- const window = readTailWindow();
238
+ function detectThreeFail(window) {
261
239
  const groups = new Map();
262
240
  for (const r of window) {
263
241
  if (r.ok || !r.error_category) continue;
@@ -318,8 +296,8 @@ try {
318
296
  fs.mkdirSync(METRICS_DIR, { recursive: true });
319
297
  }
320
298
 
321
- const stdinPayload = readStdinSync();
322
- const toolName = stdinPayload?.tool_name || process.argv[2] || '';
299
+ const ctx = buildCliCtx();
300
+ const toolName = ctx.toolName;
323
301
 
324
302
  // defense-in-depth: 읽기 전용 툴은 조기 종료 (matcher 단에서 이미 제거됨)
325
303
  if (toolName && READ_ONLY_TOOLS.has(toolName)) {
@@ -328,16 +306,16 @@ try {
328
306
 
329
307
  // 책임 2를 먼저 실행 — jsonl 라인 수가 bumpCounter 스로틀 판단에 쓰임
330
308
  let lastRecord = null;
331
- try { lastRecord = appendJsonl(stdinPayload); } catch { /* 로깅 실패 무시 */ }
309
+ try { lastRecord = appendJsonl(ctx); } catch { /* 로깅 실패 무시 */ }
332
310
 
333
- // 책임 1: jsonl append 후 라인 수로 스로틀 판단
334
- const lineCount = jsonlLineCount();
311
+ // 책임 1: jsonl append 후 라인 수로 스로틀 판단 (책임 3의 tail window 와 1회 읽기 공유)
312
+ const { lineCount, tail } = readJsonlState();
335
313
  try { bumpCounter(toolName, lineCount); } catch { /* 카운터 실패 무시 */ }
336
314
 
337
315
  // 책임 3
338
316
  try {
339
317
  if (lastRecord && !lastRecord.ok && lastRecord.error_category) {
340
- const trip = detectThreeFail();
318
+ const trip = detectThreeFail(tail);
341
319
  if (trip) writeAntiPattern(trip);
342
320
  }
343
321
  } catch { /* anti-pattern 작성 실패 무시 */ }
@@ -2,7 +2,7 @@
2
2
  /**
3
3
  * verify-ledger CLI — vibe.verify 결과를 run-ledger에 기록.
4
4
  *
5
- * 사용법: node hooks/scripts/verify-ledger.js pass|fail
5
+ * 사용법: node hooks/scripts/verify-ledger.js pass|fail <run-id> <results-json-path>
6
6
  *
7
7
  * vibe.verify/SKILL.md 에서 검증 완료 시 호출.
8
8
  * stdout 출력은 에이전트가 Bash로 실행하는 컨텍스트이므로 허용.
@@ -10,13 +10,33 @@
10
10
  */
11
11
 
12
12
  import { recordVerify } from './lib/run-ledger.js';
13
+ import fs from 'fs';
13
14
 
14
15
  const arg = process.argv[2];
16
+ const runId = process.argv[3];
17
+ const resultsPath = process.argv[4];
15
18
  const passed = arg === 'pass';
16
19
  const projectDir = process.env.CLAUDE_PROJECT_DIR || process.cwd();
17
20
 
18
- recordVerify(projectDir, passed);
21
+ function readResults(filePath) {
22
+ try {
23
+ if (!filePath) return [];
24
+ const parsed = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
25
+ return Array.isArray(parsed) ? parsed : [];
26
+ } catch {
27
+ return [];
28
+ }
29
+ }
30
+
31
+ const recorded = recordVerify(projectDir, passed, {
32
+ runId,
33
+ verificationResults: readResults(resultsPath),
34
+ });
19
35
 
20
36
  const status = passed ? 'pass' : 'fail';
21
- process.stdout.write(`[verify-ledger] recorded: verifyPassed=${passed} (${status})\n`);
37
+ if (recorded) {
38
+ process.stdout.write(`[verify-ledger] recorded: verifyPassed=${passed} (${status})\n`);
39
+ } else {
40
+ process.stderr.write('[verify-ledger] WARNING: run-ledger or evidence.json write failed\n');
41
+ }
22
42
  process.exit(0);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@su-record/vibe",
3
- "version": "3.1.0",
4
- "description": "AI Coding Framework for Claude Code — 10+ agents, 59 skills, multi-LLM orchestration",
3
+ "version": "3.2.1",
4
+ "description": "AI Coding Framework for Claude Code — 7+ agents, 60 skills, multi-LLM orchestration",
5
5
  "type": "module",
6
6
  "main": "dist/cli/index.js",
7
7
  "exports": {
@@ -94,7 +94,6 @@
94
94
  "dist/",
95
95
  "vibe/",
96
96
  "languages/",
97
- "commands/",
98
97
  "agents/",
99
98
  "skills/",
100
99
  "hooks/",
@@ -71,36 +71,15 @@ Input: a URL (or multiple URLs for multi-page clones)
71
71
  → Phase 3C: Responsive merge (clone-merge-responsive.js — mobile-first @media)
72
72
  → Phase 4: Compile gate
73
73
  → Phase 5: Pixel verification loop (BOTH viewports after merge)
74
-
75
- Working directory:
76
- /tmp/{feature}/
77
- ├── mo/ (375×812) — rendered.html, computed.json, screenshot.png, assets/, sections.json
78
- ├── pc/ (1440×900) — rendered.html, computed.json, screenshot.png, assets/, sections.json
79
- └── tokens.json — extracted design tokens (colors/fonts/spacing)
80
-
81
- Code output: placed directly in the project directory per detected stack
82
- components/{feature}/, components/{feature}/_specs/{mo,pc}/,
83
- styles/{feature}/{mo,pc}/ (per-BP drafts) → styles/{feature}/ (Phase 3C merged),
84
- public/images/{feature}/, public/ (favicons/OG from assets/seo/)
85
74
  ```
86
75
 
76
+ > Read `references/setup-and-layout.md` for the working-directory layout and code-output paths.
77
+
87
78
  ---
88
79
 
89
80
  ## Phase 0: Setup
90
81
 
91
- ```
92
- 1. Stack detection:
93
- - .vibe/config.json → stack (react/vue/next/svelte/vanilla, scss/tailwind/css-modules)
94
- - Fallback: package.json deps
95
- 2. Feature name: URL hostname → kebab-case (e.g. stripe.com → stripe-clone)
96
- - User may override with --name=<custom>
97
- 3. Directories:
98
- - components/{feature}/, styles/{feature}/, public/images/{feature}/
99
- 4. Component indexing → /tmp/{feature}/component-index.json
100
- (scan up to 50 existing components, extract props/slots/classes, within 2 minutes)
101
- 5. Design token scan → /tmp/{feature}/project-tokens.json
102
- (SCSS > CSS Variables > Tailwind > CSS-in-JS)
103
- ```
82
+ > Read `references/setup-and-layout.md` for the full Phase 0 setup steps (stack detection, feature naming, directories, component indexing, design token scan).
104
83
 
105
84
  ---
106
85
 
@@ -130,50 +109,7 @@ node {{VIBE_PATH}}/hooks/scripts/clone-extract.js capture <URL> \
130
109
  ⛔ **Do NOT use WebFetch or curl** — they cannot render JS-driven SPAs.
131
110
  ✅ Use clone-extract.js. If output is unsatisfactory, modify the script.
132
111
 
133
- ### Output per breakpoint
134
-
135
- ```
136
- /tmp/{feature}/{bp}/
137
- ├── rendered.html — final DOM after JS execution
138
- ├── computed.json — per-element computed CSS + bounding box
139
- ├── screenshot.png — full-page screenshot
140
- ├── states.json — non-default state rules (hover/focus/active/checked/tab/aria/data-state)
141
- ├── behaviors.json — ACTIVE sweep: scroll / tab / hover / in-view / time-driven + scrollLib
142
- ├── assets/
143
- │ ├── images/ — all <img>, background-image, <picture> sources
144
- │ ├── fonts/ — @font-face srcs
145
- │ └── seo/ — favicons, apple-touch-icons, OG images, webmanifest
146
- └── asset-map.json — remote URL → local path mapping
147
- ```
148
-
149
- ### Capture rules
150
-
151
- ```
152
- 1. Wait for `networkidle2` (no in-flight requests for 500ms) before snapshot
153
- 2. Scroll to bottom slowly to trigger lazy-loaded content
154
- 3. Resolve all <img src>, srcset, and computed background-image URLs
155
- 4. Resolve @font-face src() URLs from all stylesheets
156
- 5. Download assets in parallel (concurrency=8), preserve original extensions
157
- 6. Rewrite asset URLs in rendered.html and computed.json to local paths
158
- 7. Strip inline analytics/tracking scripts before saving rendered.html
159
- 8. Harvest non-default state rules from all stylesheets → states.json
160
- (deterministic: read :hover/:focus/:active/:checked/[aria-*]/[data-state]/.is-*/.active/
161
- .scrolled/.sticky/.pinned/… declarations straight from CSS — NO scripted clicking)
162
- 9. ACTIVE interaction sweep → behaviors.json (runs after lazy-load, before freeze).
163
- Drives the live page to capture JS-set state that CSS harvesting can't see:
164
- - Scroll-state diff: tag sticky/fixed/top-bar headers+nav, snapshot computed CSS at
165
- scroll 0, scroll past threshold, re-snapshot, diff → {prop: {from, to}, triggerScrollY}
166
- - Tab groups: click tab-like sibling sets, detect whether content swaps on click
167
- - Hover diff: hover interactive elements (1 sample per tag+class signature, max 30),
168
- diff computed styles → JS-set hover states that :hover harvesting misses
169
- - In-view entrance: fresh reload, tag below-fold "waiting to animate" nodes
170
- (opacity 0 / transform offset), scroll each into view, diff → fade-up/slide-in
171
- - Time-driven: 3s no-input MutationObserver watch → carousel/auto-cycle candidates
172
- - Scroll-lib detection: Lenis / Locomotive Scroll (native scrolling feels different —
173
- the user spots it immediately; wire the lib page-level, not per-section)
174
- Disable with --no-interact (restores the old fully-deterministic, screenshot-stable capture).
175
- 10. SEO asset harvest: favicon / apple-touch-icon / webmanifest / og:image → assets/seo/
176
- ```
112
+ > Read `references/capture-rules.md` for the full per-breakpoint output directory listing and the deterministic capture rule set (networkidle wait, asset resolution, states/behaviors harvesting, SEO asset harvest).
177
113
 
178
114
  ---
179
115
 
@@ -203,63 +139,7 @@ node {{VIBE_PATH}}/hooks/scripts/clone-refine.js \
203
139
  ⛔ **Do NOT parse rendered.html with custom Python/Node scripts.**
204
140
  ✅ Use clone-refine.js output as-is. If unsatisfactory, modify the script.
205
141
 
206
- ### Refinement rules
207
-
208
- ```
209
- Refinement applied when converting rendered.html + computed.json → sections.json:
210
- 1. Strip <script>, <noscript>, <style>, tracking pixels
211
- 2. Strip nodes with display:none, visibility:hidden, opacity:0, size 0
212
- 3. Detect sections: <header>, <nav>, top-level <section>/<main> children, <footer>
213
- Fallback: top-level children of <body> with height > 100px
214
- 4. Detect repeated patterns (sibling nodes with same tag+class signature, count >= 3)
215
- → mark as component candidates
216
- 5. Extract design tokens:
217
- - colors: unique color/background-color values, sorted by frequency
218
- - typography: font-family/size/weight combinations
219
- - spacing: padding/margin values (px), bucketed to nearest 4px
220
- 6. Background images (background-image: url) → images.bg
221
- 7. Inline <img> → images.content
222
- 8. Keep CSS subset: layout (display/flex/grid/position/inset/margin/padding/width/height/gap),
223
- typography (font-*, line-height, letter-spacing, text-*, color),
224
- decoration (background, border, border-radius, box-shadow, opacity),
225
- transform/transition
226
- 9. Classify interaction model per section (static-DOM heuristic) → section.interaction
227
- = { model, confidence, signals[], note }. model ∈ static | click-driven | scroll-driven
228
- | time-driven | hover. This is a best-guess + ranked SIGNALS, NOT a silent decision —
229
- the builder confirms it against the live site (Phase 5). Misidentifying the interaction
230
- model is the #1 clone failure mode (scroll-driven original built as a click UI, etc.).
231
- 10. Attach matching state rules from states.json to each section → section.states
232
- (rules whose selector references a class/leaf-tag inside that section's subtree)
233
- ```
234
-
235
- ### Output
236
-
237
- ```
238
- /tmp/{feature}/{bp}/sections.json:
239
- {
240
- meta: { feature, url, viewport, bp },
241
- tokens: { colors: [...], typography: [...], spacing: [...] },
242
- sections: [
243
- {
244
- name: "Header" | "Hero" | "Features" | ...,
245
- nodeRef, tag, size, css,
246
- interaction: { // static-DOM heuristic — builder confirms in Phase 5
247
- model: "static" | "click-driven" | "scroll-driven" | "time-driven" | "hover",
248
- confidence: "high" | "medium" | "low",
249
- signals: [...], // ranked evidence (sticky, infinite animation, role=tab, …)
250
- note
251
- },
252
- text, // text content (placeholder candidates)
253
- components: [...], // detected repeated patterns
254
- states: [ // non-default state rules scoped to this section
255
- { selector, media, css }
256
- ],
257
- children: [...], // full recursive subtree
258
- images: { bg, content: [...] }
259
- }
260
- ]
261
- }
262
- ```
142
+ > Read `references/refine-rules.md` for the full refinement rule set and the sections.json output schema.
263
143
 
264
144
  ---
265
145
 
@@ -324,44 +204,8 @@ node {{VIBE_PATH}}/hooks/scripts/clone-validate.js \
324
204
  ⛔ **Do NOT write custom SCSS / spec generation scripts.**
325
205
  ⛔ **Do NOT proceed past a section without a clone-validate.js PASS for it.**
326
206
 
327
- ```
328
207
  Phase 3A: MO Scaffold — parallel builder dispatch
329
208
  Input: /tmp/{feature}/mo/sections.json
330
- Prep (sequential, once):
331
- a. Step 0: clone-spec.js → emit _specs/mo/{Section}.spec.md for every section
332
- b. Step A: clone-to-scss.js → SCSS draft in styles/{feature}/mo/ (computed values)
333
- c. Orchestrator resolves every spec TODO BEFORE dispatch:
334
- ├─ Confirm the interaction model (spec.interaction is a heuristic guess;
335
- │ the spec's active-capture "Dynamic behaviors" block overrides it on conflict)
336
- ├─ List the states to implement (spec.states + Dynamic behaviors)
337
- ├─ Mark copyrighted text for placeholder replacement (skip with --real-content)
338
- └─ Map component candidates against component-index.json (reuse vs create)
339
- d. 150-line split rule (mechanical — wc -l, don't override with "but it's related"):
340
- a completed spec over 150 lines means the section is too big for one builder →
341
- split into sub-component specs (one per card variant / repeated pattern) + one
342
- wrapper spec that imports them.
343
- Dispatch (parallel):
344
- ⛔ Every builder prompt embeds the FULL spec text INLINE — never "go read the file".
345
- The spec file stays on disk as the audit trail; the prompt must be self-sufficient.
346
- ⛔ Builders that write files in parallel run in worktree isolation; sub-component
347
- builders complete before their wrapper builder starts.
348
- Builder contract (each agent):
349
- 1. Build HTML structure + semantic tags + framework-specific component file
350
- ⛔ No CSS in <style> blocks — only @import/@use of styles/{feature}/mo/
351
- ⛔ Build for the CONFIRMED interaction model in the spec; wire every state
352
- Framework mapping:
353
- - React/Next → .tsx with CSS Modules or styled-components per stack
354
- - Vue/Nuxt → .vue with scoped <style lang="scss"> @import only
355
- - Svelte → .svelte with <style> @import only
356
- - Vanilla → .html + linked .scss
357
- 2. Asset references → public/images/{feature}/ (already populated in Phase 1)
358
- 3. SCSS edits per Immutable Rule 1 (evidence-cited only)
359
- 4. Verify compile (tsc --noEmit or stack equivalent) before finishing
360
- Merge (orchestrator, as builders complete):
361
- 5. Merge worktrees; resolve conflicts (you have full context on every spec)
362
- 6. clone-validate.js per section — Step B
363
- ├─ PASS → section done
364
- └─ FAIL → fix discrepancies → re-run step 6 (loop until P1=0, no round cap)
365
209
  → Phase 4 (MO compile) → Phase 5 (MO pixel verification)
366
210
 
367
211
  Phase 3B: PC Scaffold
@@ -375,27 +219,12 @@ Phase 3C: Responsive Integration (after both MO+PC pass Phase 5)
375
219
  --out=/path/to/project/styles/{feature}/ \
376
220
  [--breakpoint=1024]
377
221
  → mobile-first merge: MO declarations = base, PC diffs → @media (min-width) block
378
- 2. Switch component style imports from styles/{feature}/{bp}/index.scss to the merged
379
- styles/{feature}/index.scss
380
- 3. Review pc-only/mo-only selectors in the merge report — hide/show them with the
381
- media query if the original does (evidence: the two sections.json trees)
382
- 4. → Phase 4 (compile) → Phase 5 at BOTH viewports against each BP's screenshot.
222
+ Phase 4 (compile) Phase 5 at BOTH viewports against each BP's screenshot.
383
223
  ⛔ The clone is NOT complete until the MERGED build passes Phase 5 at both.
384
-
385
- Claude's role:
386
- ✅ Component candidates: decide which patterns become reusable components
387
- ✅ HTML semantics: section/h1/p/button/nav tag selection
388
- ✅ Text replacement: substitute copyrighted copy with placeholders or user-supplied text
389
- ✅ Interaction model: confirm the spec's heuristic guess, then build for the real model
390
- ✅ Interactions: implement every state in the spec (hover/focus/active/open), click handlers
391
- ✅ SCSS value edits WITH cited evidence (computed.json/states.json/behaviors.json) —
392
- clone-validate.js PASS is the judge
393
- ❌ Do NOT invent CSS values without extraction evidence
394
- ❌ Do NOT write CSS directly in <style> blocks
395
- ❌ Do NOT hand-write vw/clamp/@media in Phase 3A/3B (responsive comes from Phase 3C merge)
396
- ❌ Do NOT hotlink remote URLs — all assets must use local public/images/ paths
397
224
  ```
398
225
 
226
+ > Read `references/scaffold-phases.md` for the full Phase 3A prep/dispatch/merge builder contract (framework mapping, 150-line split rule, clone-validate.js PASS/FAIL loop detail), Phase 3C steps 2–3 (import switch, pc-only/mo-only selector review), and Claude's role checklist.
227
+
399
228
  ---
400
229
 
401
230
  ## Phase 4: Compile Gate
@@ -462,41 +291,6 @@ Cleanup: shut down browser + dev server
462
291
 
463
292
  ---
464
293
 
465
- ## Legal & Ethical Notes
466
-
467
- ```
468
- This skill is intended for:
469
- ✅ "Clone coding" learning exercises (markup/layout study)
470
- ✅ Rebuilding the user's own previously-deployed sites
471
- ✅ Authorized redesigns where the user has rights to the source
472
-
473
- NOT intended for:
474
- ❌ Republishing copyrighted content (text, images, logos) without permission
475
- ❌ Deceptive look-alike sites (phishing, brand impersonation)
476
- ❌ Bypassing robots.txt or rate-limiting protections
477
-
478
- Claude must:
479
- - Replace copyrighted text content with placeholders (e.g. "[Lorem ipsum]") by default
480
- - --real-content: ask ONE explicit confirmation (site ownership / permission) before
481
- Phase 3; on confirmation keep text verbatim (clone-spec.js --real-content)
482
- - Skip and warn when robots.txt disallows fetching the target path
483
- - Refuse if the user's stated intent is brand impersonation or deception
484
- ```
485
-
486
- ---
294
+ ## Legal, Ethical & Error Recovery Reference
487
295
 
488
- ## Error Recovery
489
-
490
- | Failure | Recovery |
491
- |---------|----------|
492
- | clone-extract.js Puppeteer launch failure | Verify Node ≥18 and that Chromium is installed (`npx puppeteer browsers install chrome`). Retry once. |
493
- | Target site blocks headless (403/Cloudflare) | Retry with `--stealth` flag (uses puppeteer-extra stealth plugin). If still blocked, report to user. |
494
- | Asset download 404 | Log to asset-map.json with `status: missing`. Use a 1×1 transparent placeholder. Continue. |
495
- | robots.txt disallows path | Halt Phase 1. Inform user; require explicit `--ignore-robots` flag to proceed. |
496
- | clone-refine.js produces empty sections | Site likely uses Shadow DOM or canvas rendering. Report and ask whether to fall back to screenshot-only mode. |
497
- | Pixel diff stuck > 0.05 after 5 rounds | Likely font fallback or anti-aliasing. Report metric, allow user to accept threshold. |
498
- | Interaction model guess wrong (Phase 5) | section.interaction is a static-DOM heuristic. Re-observe the live site, correct the model in the spec, rebuild the section for the confirmed model. |
499
- | states.json empty but site has hover/tabs | States may be set via inline JS, not CSS rules. Check behaviors.json (active sweep captures scroll/tab JS state). If still missing, note in the spec and capture manually during Phase 5. |
500
- | behaviors.json missing or empty | Active sweep was disabled (--no-interact), hit no sticky/tab elements, or errored (logged, non-fatal). Falls back to static states.json. Re-run without --no-interact to retry. |
501
- | clone-merge-responsive.js reports many mo-only/pc-only selectors | Section detection diverged between BPs (layout differs structurally). Expected for hidden-on-mobile elements; verify each against the two sections.json trees and gate with the media query manually if needed. |
502
- | Merged build fails Phase 5 at one viewport only | The merge dropped or mis-bucketed a declaration. Diff the failing viewport's computed CSS against its sections.json; fix in the merged SCSS with that evidence. |
296
+ > Read `references/legal-and-error-recovery.md` for the full legal/ethical usage notes (intended use, prohibited use, --real-content flow) and the Error Recovery troubleshooting table.
@@ -0,0 +1,48 @@
1
+ # Clone — Phase 1 Capture Output & Rules
2
+
3
+ > Loaded by vibe.clone SKILL.md Phase 1 (Capture) — full per-breakpoint output directory listing and the deterministic capture rule set.
4
+
5
+ ### Output per breakpoint
6
+
7
+ ```
8
+ /tmp/{feature}/{bp}/
9
+ ├── rendered.html — final DOM after JS execution
10
+ ├── computed.json — per-element computed CSS + bounding box
11
+ ├── screenshot.png — full-page screenshot
12
+ ├── states.json — non-default state rules (hover/focus/active/checked/tab/aria/data-state)
13
+ ├── behaviors.json — ACTIVE sweep: scroll / tab / hover / in-view / time-driven + scrollLib
14
+ ├── assets/
15
+ │ ├── images/ — all <img>, background-image, <picture> sources
16
+ │ ├── fonts/ — @font-face srcs
17
+ │ └── seo/ — favicons, apple-touch-icons, OG images, webmanifest
18
+ └── asset-map.json — remote URL → local path mapping
19
+ ```
20
+
21
+ ### Capture rules
22
+
23
+ ```
24
+ 1. Wait for `networkidle2` (no in-flight requests for 500ms) before snapshot
25
+ 2. Scroll to bottom slowly to trigger lazy-loaded content
26
+ 3. Resolve all <img src>, srcset, and computed background-image URLs
27
+ 4. Resolve @font-face src() URLs from all stylesheets
28
+ 5. Download assets in parallel (concurrency=8), preserve original extensions
29
+ 6. Rewrite asset URLs in rendered.html and computed.json to local paths
30
+ 7. Strip inline analytics/tracking scripts before saving rendered.html
31
+ 8. Harvest non-default state rules from all stylesheets → states.json
32
+ (deterministic: read :hover/:focus/:active/:checked/[aria-*]/[data-state]/.is-*/.active/
33
+ .scrolled/.sticky/.pinned/… declarations straight from CSS — NO scripted clicking)
34
+ 9. ACTIVE interaction sweep → behaviors.json (runs after lazy-load, before freeze).
35
+ Drives the live page to capture JS-set state that CSS harvesting can't see:
36
+ - Scroll-state diff: tag sticky/fixed/top-bar headers+nav, snapshot computed CSS at
37
+ scroll 0, scroll past threshold, re-snapshot, diff → {prop: {from, to}, triggerScrollY}
38
+ - Tab groups: click tab-like sibling sets, detect whether content swaps on click
39
+ - Hover diff: hover interactive elements (1 sample per tag+class signature, max 30),
40
+ diff computed styles → JS-set hover states that :hover harvesting misses
41
+ - In-view entrance: fresh reload, tag below-fold "waiting to animate" nodes
42
+ (opacity 0 / transform offset), scroll each into view, diff → fade-up/slide-in
43
+ - Time-driven: 3s no-input MutationObserver watch → carousel/auto-cycle candidates
44
+ - Scroll-lib detection: Lenis / Locomotive Scroll (native scrolling feels different —
45
+ the user spots it immediately; wire the lib page-level, not per-section)
46
+ Disable with --no-interact (restores the old fully-deterministic, screenshot-stable capture).
47
+ 10. SEO asset harvest: favicon / apple-touch-icon / webmanifest / og:image → assets/seo/
48
+ ```
@@ -0,0 +1,40 @@
1
+ # Clone — Legal & Ethical Notes + Error Recovery
2
+
3
+ > Loaded by vibe.clone SKILL.md — full legal/ethical usage notes and the Error Recovery troubleshooting table.
4
+
5
+ ## Legal & Ethical Notes
6
+
7
+ ```
8
+ This skill is intended for:
9
+ ✅ "Clone coding" learning exercises (markup/layout study)
10
+ ✅ Rebuilding the user's own previously-deployed sites
11
+ ✅ Authorized redesigns where the user has rights to the source
12
+
13
+ NOT intended for:
14
+ ❌ Republishing copyrighted content (text, images, logos) without permission
15
+ ❌ Deceptive look-alike sites (phishing, brand impersonation)
16
+ ❌ Bypassing robots.txt or rate-limiting protections
17
+
18
+ Claude must:
19
+ - Replace copyrighted text content with placeholders (e.g. "[Lorem ipsum]") by default
20
+ - --real-content: ask ONE explicit confirmation (site ownership / permission) before
21
+ Phase 3; on confirmation keep text verbatim (clone-spec.js --real-content)
22
+ - Skip and warn when robots.txt disallows fetching the target path
23
+ - Refuse if the user's stated intent is brand impersonation or deception
24
+ ```
25
+
26
+ ## Error Recovery
27
+
28
+ | Failure | Recovery |
29
+ |---------|----------|
30
+ | clone-extract.js Puppeteer launch failure | Verify Node ≥18 and that Chromium is installed (`npx puppeteer browsers install chrome`). Retry once. |
31
+ | Target site blocks headless (403/Cloudflare) | Retry with `--stealth` flag (uses puppeteer-extra stealth plugin). If still blocked, report to user. |
32
+ | Asset download 404 | Log to asset-map.json with `status: missing`. Use a 1×1 transparent placeholder. Continue. |
33
+ | robots.txt disallows path | Halt Phase 1. Inform user; require explicit `--ignore-robots` flag to proceed. |
34
+ | clone-refine.js produces empty sections | Site likely uses Shadow DOM or canvas rendering. Report and ask whether to fall back to screenshot-only mode. |
35
+ | Pixel diff stuck > 0.05 after 5 rounds | Likely font fallback or anti-aliasing. Report metric, allow user to accept threshold. |
36
+ | Interaction model guess wrong (Phase 5) | section.interaction is a static-DOM heuristic. Re-observe the live site, correct the model in the spec, rebuild the section for the confirmed model. |
37
+ | states.json empty but site has hover/tabs | States may be set via inline JS, not CSS rules. Check behaviors.json (active sweep captures scroll/tab JS state). If still missing, note in the spec and capture manually during Phase 5. |
38
+ | behaviors.json missing or empty | Active sweep was disabled (--no-interact), hit no sticky/tab elements, or errored (logged, non-fatal). Falls back to static states.json. Re-run without --no-interact to retry. |
39
+ | clone-merge-responsive.js reports many mo-only/pc-only selectors | Section detection diverged between BPs (layout differs structurally). Expected for hidden-on-mobile elements; verify each against the two sections.json trees and gate with the media query manually if needed. |
40
+ | Merged build fails Phase 5 at one viewport only | The merge dropped or mis-bucketed a declaration. Diff the failing viewport's computed CSS against its sections.json; fix in the merged SCSS with that evidence. |
@@ -0,0 +1,61 @@
1
+ # Clone — Phase 2 Refinement Rules & Output Schema
2
+
3
+ > Loaded by vibe.clone SKILL.md Phase 2 (Refine) — full refinement rule set and the sections.json output schema.
4
+
5
+ ### Refinement rules
6
+
7
+ ```
8
+ Refinement applied when converting rendered.html + computed.json → sections.json:
9
+ 1. Strip <script>, <noscript>, <style>, tracking pixels
10
+ 2. Strip nodes with display:none, visibility:hidden, opacity:0, size 0
11
+ 3. Detect sections: <header>, <nav>, top-level <section>/<main> children, <footer>
12
+ Fallback: top-level children of <body> with height > 100px
13
+ 4. Detect repeated patterns (sibling nodes with same tag+class signature, count >= 3)
14
+ → mark as component candidates
15
+ 5. Extract design tokens:
16
+ - colors: unique color/background-color values, sorted by frequency
17
+ - typography: font-family/size/weight combinations
18
+ - spacing: padding/margin values (px), bucketed to nearest 4px
19
+ 6. Background images (background-image: url) → images.bg
20
+ 7. Inline <img> → images.content
21
+ 8. Keep CSS subset: layout (display/flex/grid/position/inset/margin/padding/width/height/gap),
22
+ typography (font-*, line-height, letter-spacing, text-*, color),
23
+ decoration (background, border, border-radius, box-shadow, opacity),
24
+ transform/transition
25
+ 9. Classify interaction model per section (static-DOM heuristic) → section.interaction
26
+ = { model, confidence, signals[], note }. model ∈ static | click-driven | scroll-driven
27
+ | time-driven | hover. This is a best-guess + ranked SIGNALS, NOT a silent decision —
28
+ the builder confirms it against the live site (Phase 5). Misidentifying the interaction
29
+ model is the #1 clone failure mode (scroll-driven original built as a click UI, etc.).
30
+ 10. Attach matching state rules from states.json to each section → section.states
31
+ (rules whose selector references a class/leaf-tag inside that section's subtree)
32
+ ```
33
+
34
+ ### Output
35
+
36
+ ```
37
+ /tmp/{feature}/{bp}/sections.json:
38
+ {
39
+ meta: { feature, url, viewport, bp },
40
+ tokens: { colors: [...], typography: [...], spacing: [...] },
41
+ sections: [
42
+ {
43
+ name: "Header" | "Hero" | "Features" | ...,
44
+ nodeRef, tag, size, css,
45
+ interaction: { // static-DOM heuristic — builder confirms in Phase 5
46
+ model: "static" | "click-driven" | "scroll-driven" | "time-driven" | "hover",
47
+ confidence: "high" | "medium" | "low",
48
+ signals: [...], // ranked evidence (sticky, infinite animation, role=tab, …)
49
+ note
50
+ },
51
+ text, // text content (placeholder candidates)
52
+ components: [...], // detected repeated patterns
53
+ states: [ // non-default state rules scoped to this section
54
+ { selector, media, css }
55
+ ],
56
+ children: [...], // full recursive subtree
57
+ images: { bg, content: [...] }
58
+ }
59
+ ]
60
+ }
61
+ ```