@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.
- package/CLAUDE.md +2 -2
- package/README.en.md +9 -6
- package/README.md +9 -4
- package/dist/__tests__/wiring-integrity.test.d.ts +2 -0
- package/dist/__tests__/wiring-integrity.test.d.ts.map +1 -0
- package/dist/__tests__/wiring-integrity.test.js +160 -0
- package/dist/__tests__/wiring-integrity.test.js.map +1 -0
- package/dist/cli/commands/info.d.ts.map +1 -1
- package/dist/cli/commands/info.js +2 -1
- package/dist/cli/commands/info.js.map +1 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +4 -2
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/design/design-md-parser.test.js +2 -2
- package/dist/cli/design/design-md-parser.test.js.map +1 -1
- package/dist/cli/index.js +102 -128
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/postinstall/claude-agents.d.ts.map +1 -1
- package/dist/cli/postinstall/claude-agents.js +0 -5
- package/dist/cli/postinstall/claude-agents.js.map +1 -1
- package/dist/cli/postinstall/constants.d.ts +6 -0
- package/dist/cli/postinstall/constants.d.ts.map +1 -1
- package/dist/cli/postinstall/constants.js +42 -60
- package/dist/cli/postinstall/constants.js.map +1 -1
- package/dist/cli/postinstall/inline-skills.js +2 -2
- package/dist/cli/postinstall/inline-skills.js.map +1 -1
- package/dist/cli/postinstall/main.d.ts.map +1 -1
- package/dist/cli/postinstall/main.js +2 -0
- package/dist/cli/postinstall/main.js.map +1 -1
- package/dist/cli/postinstall.d.ts +6 -1
- package/dist/cli/postinstall.d.ts.map +1 -1
- package/dist/cli/postinstall.js +6 -1
- package/dist/cli/postinstall.js.map +1 -1
- package/dist/cli/setup/GlobalInstaller.d.ts +1 -15
- package/dist/cli/setup/GlobalInstaller.d.ts.map +1 -1
- package/dist/cli/setup/GlobalInstaller.js +16 -115
- package/dist/cli/setup/GlobalInstaller.js.map +1 -1
- package/dist/cli/setup/GlobalInstaller.test.d.ts +2 -0
- package/dist/cli/setup/GlobalInstaller.test.d.ts.map +1 -0
- package/dist/cli/setup/GlobalInstaller.test.js +16 -0
- package/dist/cli/setup/GlobalInstaller.test.js.map +1 -0
- package/dist/cli/setup/ProjectSetup.js +2 -2
- package/dist/cli/setup/ProjectSetup.js.map +1 -1
- package/dist/cli/setup/Provisioner.d.ts.map +1 -1
- package/dist/cli/setup/Provisioner.js +23 -7
- package/dist/cli/setup/Provisioner.js.map +1 -1
- package/dist/cli/setup.d.ts +1 -1
- package/dist/cli/setup.d.ts.map +1 -1
- package/dist/cli/setup.js +1 -1
- package/dist/cli/setup.js.map +1 -1
- package/dist/cli/utils/cli-detector.d.ts +3 -3
- package/dist/cli/utils/cli-detector.d.ts.map +1 -1
- package/dist/cli/utils/cli-detector.js +7 -27
- package/dist/cli/utils/cli-detector.js.map +1 -1
- package/dist/cli/utils/cli-detector.test.js +3 -2
- package/dist/cli/utils/cli-detector.test.js.map +1 -1
- package/dist/infra/lib/SkillRepository.js +1 -1
- package/dist/infra/lib/evolution/__tests__/integration.test.js +0 -42
- package/dist/infra/lib/evolution/__tests__/integration.test.js.map +1 -1
- package/dist/infra/lib/llm-availability.d.ts.map +1 -1
- package/dist/infra/lib/llm-availability.js +2 -10
- package/dist/infra/lib/llm-availability.js.map +1 -1
- package/dist/infra/lib/llm-availability.test.js +3 -2
- package/dist/infra/lib/llm-availability.test.js.map +1 -1
- package/dist/infra/lib/utils.d.ts +6 -0
- package/dist/infra/lib/utils.d.ts.map +1 -1
- package/dist/infra/lib/utils.js +33 -0
- package/dist/infra/lib/utils.js.map +1 -1
- package/dist/tools/convention/validateCodeQuality.d.ts +1 -0
- package/dist/tools/convention/validateCodeQuality.d.ts.map +1 -1
- package/dist/tools/convention/validateCodeQuality.js +3 -2
- package/dist/tools/convention/validateCodeQuality.js.map +1 -1
- package/dist/tools/index.d.ts +2 -2
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/spec/executionPacket.d.ts +101 -0
- package/dist/tools/spec/executionPacket.d.ts.map +1 -0
- package/dist/tools/spec/executionPacket.js +394 -0
- package/dist/tools/spec/executionPacket.js.map +1 -0
- package/dist/tools/spec/executionPacket.test.d.ts +2 -0
- package/dist/tools/spec/executionPacket.test.d.ts.map +1 -0
- package/dist/tools/spec/executionPacket.test.js +360 -0
- package/dist/tools/spec/executionPacket.test.js.map +1 -0
- package/dist/tools/spec/index.d.ts +2 -0
- package/dist/tools/spec/index.d.ts.map +1 -1
- package/dist/tools/spec/index.js +2 -0
- package/dist/tools/spec/index.js.map +1 -1
- package/dist/tools/spec/specGenerator.d.ts +12 -0
- package/dist/tools/spec/specGenerator.d.ts.map +1 -1
- package/dist/tools/spec/specGenerator.js +96 -14
- package/dist/tools/spec/specGenerator.js.map +1 -1
- package/dist/tools/spec/specGenerator.test.d.ts +2 -0
- package/dist/tools/spec/specGenerator.test.d.ts.map +1 -0
- package/dist/tools/spec/specGenerator.test.js +130 -0
- package/dist/tools/spec/specGenerator.test.js.map +1 -0
- package/hooks/scripts/__tests__/.vibe/command-log.txt +3 -3
- package/hooks/scripts/__tests__/clone-behaviors.test.js +33 -1
- package/hooks/scripts/__tests__/code-check-memory-write.test.js +81 -0
- package/hooks/scripts/__tests__/llm-orchestrate-antigravity.test.js +5 -2
- package/hooks/scripts/__tests__/run-ledger-verify-required.test.js +7 -2
- package/hooks/scripts/__tests__/run-ledger.test.js +161 -10
- package/hooks/scripts/__tests__/step-counter.test.js +21 -0
- package/hooks/scripts/__tests__/utils-npm-root.test.js +5 -4
- package/hooks/scripts/auto-format.js +2 -11
- package/hooks/scripts/auto-test.js +2 -11
- package/hooks/scripts/clone-extract.js +146 -10
- package/hooks/scripts/code-check.js +27 -55
- package/hooks/scripts/lib/glob.js +32 -0
- package/hooks/scripts/lib/hook-context.js +19 -2
- package/hooks/scripts/lib/run-ledger.js +123 -21
- package/hooks/scripts/post-edit.js +2 -3
- package/hooks/scripts/prompt-dispatcher.js +1 -1
- package/hooks/scripts/scope-guard.js +3 -42
- package/hooks/scripts/sentinel-guard.js +5 -20
- package/hooks/scripts/session-start.js +2 -2
- package/hooks/scripts/step-counter.js +35 -57
- package/hooks/scripts/verify-ledger.js +23 -3
- package/package.json +2 -3
- package/skills/clone/SKILL.md +10 -216
- package/skills/clone/references/capture-rules.md +48 -0
- package/skills/clone/references/legal-and-error-recovery.md +40 -0
- package/skills/clone/references/refine-rules.md +61 -0
- package/skills/clone/references/scaffold-phases.md +59 -0
- package/skills/clone/references/setup-and-layout.md +30 -0
- package/skills/docs/SKILL.md +39 -4
- package/skills/docs/references/api-docs-changelog.md +34 -0
- package/skills/docs/references/codemaps-output.md +23 -0
- package/{agents/diagrammer.md → skills/docs/references/diagram-spec.md} +2 -15
- package/skills/exec-plan/SKILL.md +1 -1
- package/skills/exec-plan/templates/plan.md +1 -1
- package/skills/handoff/SKILL.md +3 -3
- package/skills/handoff/templates/handoff.md +1 -1
- package/skills/spec/SKILL.md +7 -0
- package/skills/vibe/SKILL.md +3 -2
- package/skills/vibe.analyze/SKILL.md +12 -249
- package/skills/vibe.analyze/references/output-templates.md +188 -0
- package/skills/vibe.analyze/references/quality-gate.md +93 -0
- package/skills/vibe.clone/SKILL.md +14 -1
- package/skills/vibe.continue/SKILL.md +32 -0
- package/skills/vibe.design/SKILL.md +23 -1
- package/skills/vibe.design/references/ui-preview.md +20 -0
- package/skills/vibe.docs/SKILL.md +4 -2
- package/skills/vibe.figma/SKILL.md +33 -260
- package/skills/vibe.figma/references/branch-phases.md +113 -0
- package/skills/vibe.figma/references/state-schema.md +42 -0
- package/skills/vibe.figma/references/step-algorithms.md +204 -0
- package/skills/vibe.image/SKILL.md +62 -0
- package/skills/vibe.image/references/image-generation-examples.md +39 -0
- package/skills/vibe.reason/SKILL.md +3 -183
- package/skills/vibe.reason/references/output-format-template.md +57 -0
- package/skills/vibe.reason/references/quality-rubrics.md +94 -0
- package/skills/vibe.reason/references/worked-example.md +45 -0
- package/skills/vibe.review/SKILL.md +13 -348
- package/skills/vibe.review/references/boundary-check.md +25 -0
- package/skills/vibe.review/references/output-template.md +74 -0
- package/skills/vibe.review/references/quality-gate.md +84 -0
- package/skills/vibe.review/references/race-mode.md +117 -0
- package/skills/vibe.review/references/worked-examples.md +94 -0
- package/skills/vibe.run/SKILL.md +29 -6
- package/skills/vibe.trace/SKILL.md +3 -3
- package/skills/vibe.verify/SKILL.md +10 -4
- package/vibe/rules/loop-contract.md +7 -3
- package/vibe/templates/claudemd-template.md +1 -1
- package/vibe/templates/spec-template.md +32 -5
- package/agents/acceptance-tester.md +0 -56
- package/agents/documenter.md +0 -43
- package/agents/figma/figma-engineer.md +0 -76
- package/dist/cli/postinstall/index.d.ts +0 -23
- package/dist/cli/postinstall/index.d.ts.map +0 -1
- package/dist/cli/postinstall/index.js +0 -23
- package/dist/cli/postinstall/index.js.map +0 -1
- package/dist/cli/setup/index.d.ts +0 -9
- package/dist/cli/setup/index.d.ts.map +0 -1
- package/dist/cli/setup/index.js +0 -12
- package/dist/cli/setup/index.js.map +0 -1
- package/hooks/scripts/__tests__/.vibe/memories/memories.db +0 -0
- package/hooks/scripts/__tests__/.vibe/memories/memories.db-shm +0 -0
- package/hooks/scripts/__tests__/.vibe/memories/memories.db-wal +0 -0
- package/hooks/scripts/evolution-engine.js +0 -91
- package/hooks/scripts/hud-status.js +0 -321
- package/hooks/scripts/skill-injector.js +0 -83
- 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
|
-
|
|
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(
|
|
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(
|
|
53
|
+
return rel || path.basename(filePath);
|
|
75
54
|
} catch {
|
|
76
|
-
return
|
|
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(
|
|
184
|
-
const toolName =
|
|
162
|
+
function appendJsonl(ctx) {
|
|
163
|
+
const toolName = ctx.toolName;
|
|
185
164
|
if (!toolName) return null;
|
|
186
165
|
|
|
187
|
-
const
|
|
188
|
-
const
|
|
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(
|
|
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
|
|
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
|
|
205
|
+
function readJsonlState() {
|
|
206
|
+
let lines;
|
|
221
207
|
try {
|
|
222
|
-
|
|
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
|
|
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
|
|
322
|
-
const toolName =
|
|
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(
|
|
309
|
+
try { lastRecord = appendJsonl(ctx); } catch { /* 로깅 실패 무시 */ }
|
|
332
310
|
|
|
333
|
-
// 책임 1: jsonl append 후 라인 수로 스로틀 판단
|
|
334
|
-
const lineCount =
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
4
|
-
"description": "AI Coding Framework for Claude Code —
|
|
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/",
|
package/skills/clone/SKILL.md
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 &
|
|
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
|
-
|
|
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
|
+
```
|