@su-record/vibe 2.8.30 → 2.8.32
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/README.md +3 -3
- package/dist/cli/commands/figma.d.ts +2 -2
- package/dist/cli/commands/figma.d.ts.map +1 -1
- package/dist/cli/commands/figma.js +3 -7
- package/dist/cli/commands/figma.js.map +1 -1
- package/dist/cli/commands/skills.d.ts.map +1 -1
- package/dist/cli/commands/skills.js +10 -0
- package/dist/cli/commands/skills.js.map +1 -1
- package/dist/infra/lib/figma/api.d.ts +9 -0
- package/dist/infra/lib/figma/api.d.ts.map +1 -0
- package/dist/infra/lib/figma/api.js +73 -0
- package/dist/infra/lib/figma/api.js.map +1 -0
- package/dist/infra/lib/figma/extract.d.ts +12 -0
- package/dist/infra/lib/figma/extract.d.ts.map +1 -0
- package/dist/infra/lib/figma/extract.js +285 -0
- package/dist/infra/lib/figma/extract.js.map +1 -0
- package/dist/infra/lib/figma/index.d.ts +4 -0
- package/dist/infra/lib/figma/index.d.ts.map +1 -0
- package/dist/infra/lib/figma/index.js +3 -0
- package/dist/infra/lib/figma/index.js.map +1 -0
- package/dist/infra/lib/figma/types.d.ts +40 -0
- package/dist/infra/lib/figma/types.d.ts.map +1 -0
- package/dist/infra/lib/figma/types.js +5 -0
- package/dist/infra/lib/figma/types.js.map +1 -0
- package/dist/infra/lib/llm-availability.d.ts.map +1 -1
- package/dist/infra/lib/llm-availability.js +2 -0
- package/dist/infra/lib/llm-availability.js.map +1 -1
- package/dist/infra/lib/memory/ReflectionStore.d.ts.map +1 -1
- package/dist/infra/lib/memory/ReflectionStore.js +3 -8
- package/dist/infra/lib/memory/ReflectionStore.js.map +1 -1
- package/hooks/scripts/figma-extract.js +225 -0
- package/package.json +1 -2
- package/skills/vibe.figma/SKILL.md +68 -60
- package/skills/vibe.figma.extract/SKILL.md +76 -114
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/infra/lib/figma/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-availability.d.ts","sourceRoot":"","sources":["../../../src/infra/lib/llm-availability.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACjB;
|
|
1
|
+
{"version":3,"file":"llm-availability.d.ts","sourceRoot":"","sources":["../../../src/infra/lib/llm-availability.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,OAAO,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;CACjB;AAsBD;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,eAAe,CAkBvD;AAED,uBAAuB;AACvB,wBAAgB,gBAAgB,IAAI,OAAO,CAE1C;AAED,wBAAwB;AACxB,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED,oBAAoB;AACpB,wBAAgB,yBAAyB,IAAI,IAAI,CAEhD"}
|
|
@@ -11,6 +11,8 @@ import os from 'os';
|
|
|
11
11
|
let cached = null;
|
|
12
12
|
function checkCliInstalled(binName, configDir) {
|
|
13
13
|
const hasDir = fs.existsSync(configDir);
|
|
14
|
+
if (!/^[a-z]+$/.test(binName))
|
|
15
|
+
return hasDir;
|
|
14
16
|
let hasBin = false;
|
|
15
17
|
try {
|
|
16
18
|
execSync(`which ${binName}`, { stdio: 'ignore' });
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-availability.js","sourceRoot":"","sources":["../../../src/infra/lib/llm-availability.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,MAAM,IAAI,CAAC;AAOpB,IAAI,MAAM,GAA2B,IAAI,CAAC;AAE1C,SAAS,iBAAiB,CAAC,OAAe,EAAE,SAAiB;IAC3D,MAAM,MAAM,GAAG,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,CAAC;QACH,QAAQ,CAAC,SAAS,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAClD,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,YAAY;IACd,CAAC;IACD,OAAO,MAAM,IAAI,MAAM,CAAC;AAC1B,CAAC;AAED,SAAS,eAAe,CAAC,SAAiB,EAAE,YAAoB;IAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IACpD,OAAO,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB;IACnC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC7E,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAErD,MAAM,cAAc,GAAG,iBAAiB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC5D,MAAM,eAAe,GAAG,iBAAiB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAE/D,0BAA0B;IAC1B,MAAM,KAAK,GAAG,cAAc,IAAI,eAAe,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,eAAe,IAAI,CAChC,eAAe,CAAC,SAAS,EAAE,WAAW,CAAC;QACvC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAC7B,CAAC;IAEF,MAAM,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,gBAAgB;IAC9B,OAAO,qBAAqB,EAAE,CAAC,KAAK,CAAC;AACvC,CAAC;AAED,wBAAwB;AACxB,MAAM,UAAU,iBAAiB;IAC/B,OAAO,qBAAqB,EAAE,CAAC,MAAM,CAAC;AACxC,CAAC;AAED,oBAAoB;AACpB,MAAM,UAAU,yBAAyB;IACvC,MAAM,GAAG,IAAI,CAAC;AAChB,CAAC"}
|
|
1
|
+
{"version":3,"file":"llm-availability.js","sourceRoot":"","sources":["../../../src/infra/lib/llm-availability.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AACzC,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,EAAE,MAAM,IAAI,CAAC;AAOpB,IAAI,MAAM,GAA2B,IAAI,CAAC;AAE1C,SAAS,iBAAiB,CAAC,OAAe,EAAE,SAAiB;IAC3D,MAAM,MAAM,GAAG,EAAE,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACxC,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC;QAAE,OAAO,MAAM,CAAC;IAC7C,IAAI,MAAM,GAAG,KAAK,CAAC;IACnB,IAAI,CAAC;QACH,QAAQ,CAAC,SAAS,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;QAClD,MAAM,GAAG,IAAI,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,YAAY;IACd,CAAC;IACD,OAAO,MAAM,IAAI,MAAM,CAAC;AAC1B,CAAC;AAED,SAAS,eAAe,CAAC,SAAiB,EAAE,YAAoB;IAC9D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,YAAY,CAAC,CAAC;IACpD,OAAO,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB;IACnC,IAAI,MAAM;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,QAAQ,CAAC,CAAC;IAC7E,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,EAAE,SAAS,CAAC,CAAC;IAErD,MAAM,cAAc,GAAG,iBAAiB,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC5D,MAAM,eAAe,GAAG,iBAAiB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAE/D,0BAA0B;IAC1B,MAAM,KAAK,GAAG,cAAc,IAAI,eAAe,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,eAAe,IAAI,CAChC,eAAe,CAAC,SAAS,EAAE,WAAW,CAAC;QACvC,CAAC,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAC7B,CAAC;IAEF,MAAM,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;IAC3B,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,uBAAuB;AACvB,MAAM,UAAU,gBAAgB;IAC9B,OAAO,qBAAqB,EAAE,CAAC,KAAK,CAAC;AACvC,CAAC;AAED,wBAAwB;AACxB,MAAM,UAAU,iBAAiB;IAC/B,OAAO,qBAAqB,EAAE,CAAC,MAAM,CAAC;AACxC,CAAC;AAED,oBAAoB;AACpB,MAAM,UAAU,yBAAyB;IACvC,MAAM,GAAG,IAAI,CAAC;AAChB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReflectionStore.d.ts","sourceRoot":"","sources":["../../../../src/infra/lib/memory/ReflectionStore.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,CAAC;AAC/C,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,GAAG,aAAa,GAAG,QAAQ,CAAC;AAE9E,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4CD,qBAAa,eAAe;IAC1B,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,aAAa,CAAU;gBAEnB,OAAO,EAAE,aAAa;IAKlC;;OAEG;IACI,IAAI,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM;
|
|
1
|
+
{"version":3,"file":"ReflectionStore.d.ts","sourceRoot":"","sources":["../../../../src/infra/lib/memory/ReflectionStore.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AAEnD,MAAM,MAAM,cAAc,GAAG,OAAO,GAAG,OAAO,CAAC;AAC/C,MAAM,MAAM,iBAAiB,GAAG,kBAAkB,GAAG,aAAa,GAAG,QAAQ,CAAC;AAE9E,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,eAAe;IAC9B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,cAAc,CAAC;IACrB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IACrB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AA4CD,qBAAa,eAAe;IAC1B,OAAO,CAAC,EAAE,CAAoB;IAC9B,OAAO,CAAC,aAAa,CAAU;gBAEnB,OAAO,EAAE,aAAa;IAKlC;;OAEG;IACI,IAAI,CAAC,KAAK,EAAE,eAAe,GAAG,MAAM;IAgE3C;;OAEG;IACI,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,GAAE,MAAW,GAAG,UAAU,EAAE;IA2B9D;;OAEG;IACI,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,UAAU,EAAE;IAQpD;;OAEG;IACI,SAAS,CAAC,KAAK,GAAE,MAAW,GAAG,UAAU,EAAE;IASlD;;OAEG;IACI,YAAY,CAAC,QAAQ,GAAE,MAAY,EAAE,KAAK,GAAE,MAAW,GAAG,UAAU,EAAE;IAU7E;;OAEG;IACI,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI;IAK7C;;OAEG;IACI,QAAQ,IAAI,MAAM;IAKzB,OAAO,CAAC,eAAe;CAcxB"}
|
|
@@ -60,14 +60,9 @@ export class ReflectionStore {
|
|
|
60
60
|
catch (error) {
|
|
61
61
|
const sqliteError = error;
|
|
62
62
|
if (sqliteError.code === 'SQLITE_BUSY') {
|
|
63
|
-
// Retry once after 100ms
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
execSync('sleep 0.1 2>nul || timeout /t 0 >nul 2>&1', { timeout: 200 });
|
|
67
|
-
}
|
|
68
|
-
catch {
|
|
69
|
-
// sleep not available, just continue
|
|
70
|
-
}
|
|
63
|
+
// Retry once after 100ms (synchronous busy-wait for better-sqlite3 sync API)
|
|
64
|
+
const end = Date.now() + 100;
|
|
65
|
+
while (Date.now() < end) { /* busy wait */ }
|
|
71
66
|
try {
|
|
72
67
|
this.db.prepare(`
|
|
73
68
|
INSERT INTO reflections (id, sessionId, type, trigger, insights, decisions, patterns, filesContext, score, createdAt)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ReflectionStore.js","sourceRoot":"","sources":["../../../../src/infra/lib/memory/ReflectionStore.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"ReflectionStore.js","sourceRoot":"","sources":["../../../../src/infra/lib/memory/ReflectionStore.ts"],"names":[],"mappings":"AAAA,yDAAyD;AACzD,sEAAsE;AAGtE,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AA2CpC,MAAM,eAAe,GAAG,IAAI,CAAC;AAC7B,MAAM,eAAe,GAAG,IAAI,CAAC;AAE7B,SAAS,UAAU;IACjB,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IAC1C,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzD,OAAO,GAAG,SAAS,IAAI,IAAI,EAAE,CAAC;AAChC,CAAC;AAED,SAAS,aAAa,CAAC,GAAa,EAAE,QAAgB;IACpD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACjC,IAAI,IAAI,CAAC,MAAM,IAAI,QAAQ;QAAE,OAAO,GAAG,CAAC;IAExC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,QAAQ,GAAG,CAAC,CAAC,CAAC,SAAS;IAC3B,KAAK,MAAM,IAAI,IAAI,GAAG,EAAE,CAAC;QACvB,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC1E,IAAI,QAAQ,GAAG,OAAO,GAAG,QAAQ;YAAE,MAAM;QACzC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAClB,QAAQ,IAAI,OAAO,CAAC;IACtB,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAc;IACzC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IACrC,OAAO,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAkB,EAAE,CAAC,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC;AAC1E,CAAC;AAED,MAAM,OAAO,eAAe;IAClB,EAAE,CAAoB;IACtB,aAAa,CAAU;IAE/B,YAAY,OAAsB;QAChC,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;QAChC,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC;IACjD,CAAC;IAED;;OAEG;IACI,IAAI,CAAC,KAAsB;QAChC,MAAM,EAAE,GAAG,UAAU,EAAE,CAAC;QACxB,MAAM,SAAS,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,KAAK,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC,CAAC,CAAC,eAAe,CAAC;QAE5E,oCAAoC;QACpC,MAAM,QAAQ,GAAG,aAAa,CAAC,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC9E,MAAM,SAAS,GAAG,aAAa,CAAC,mBAAmB,CAAC,KAAK,CAAC,SAAS,CAAC,EAAE,QAAQ,CAAC,CAAC;QAChF,MAAM,QAAQ,GAAG,aAAa,CAAC,mBAAmB,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;QAC9E,MAAM,YAAY,GAAG,mBAAmB,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;QAE7D,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ;YAC3C,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC;YACvC,CAAC,CAAC,GAAG,CAAC;QAER,IAAI,CAAC;YACH,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;OAGf,CAAC,CAAC,GAAG,CACJ,EAAE,EACF,KAAK,CAAC,SAAS,IAAI,IAAI,EACvB,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,OAAO,EACb,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EACxB,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EACzB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EACxB,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAC5B,KAAK,EACL,SAAS,CACV,CAAC;YACF,OAAO,EAAE,CAAC;QACZ,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,WAAW,GAAG,KAA0B,CAAC;YAC/C,IAAI,WAAW,CAAC,IAAI,KAAK,aAAa,EAAE,CAAC;gBACvC,6EAA6E;gBAC7E,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,CAAC;gBAC7B,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,GAAG,EAAE,CAAC,CAAC,eAAe,CAAC,CAAC;gBAC5C,IAAI,CAAC;oBACH,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;WAGf,CAAC,CAAC,GAAG,CACJ,EAAE,EACF,KAAK,CAAC,SAAS,IAAI,IAAI,EACvB,KAAK,CAAC,IAAI,EACV,KAAK,CAAC,OAAO,EACb,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EACxB,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,EACzB,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,EACxB,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,EAC5B,KAAK,EACL,SAAS,CACV,CAAC;oBACF,OAAO,EAAE,CAAC;gBACZ,CAAC;gBAAC,MAAM,CAAC;oBACP,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,CAAC,CAAC;oBACrE,MAAM,KAAK,CAAC;gBACd,CAAC;YACH,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACI,MAAM,CAAC,KAAa,EAAE,QAAgB,EAAE;QAC7C,IAAI,IAAI,CAAC,aAAa,EAAE,CAAC;YACvB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;;;;;SAO5B,CAAC,CAAC,GAAG,CAAC,KAAK,EAAE,KAAK,CAAoB,CAAC;gBACxC,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;YACxC,CAAC;YAAC,MAAM,CAAC;gBACP,sCAAsC;YACxC,CAAC;QACH,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,KAAK,GAAG,CAAC;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;;;KAK5B,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,CAAoB,CAAC;QAC5D,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACI,YAAY,CAAC,SAAiB;QACnC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;KAG5B,CAAC,CAAC,GAAG,CAAC,SAAS,CAAoB,CAAC;QACrC,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACI,SAAS,CAAC,QAAgB,EAAE;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;;KAI5B,CAAC,CAAC,GAAG,CAAC,KAAK,CAAoB,CAAC;QACjC,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACI,YAAY,CAAC,WAAmB,GAAG,EAAE,QAAgB,EAAE;QAC5D,MAAM,IAAI,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC;;;;;KAK5B,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,KAAK,CAAoB,CAAC;QAC3C,OAAO,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC;IACxC,CAAC;IAED;;OAEG;IACI,OAAO,CAAC,EAAU;QACvB,MAAM,GAAG,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,wCAAwC,CAAC,CAAC,GAAG,CAAC,EAAE,CAA8B,CAAC;QAC3G,OAAO,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IAChD,CAAC;IAED;;OAEG;IACI,QAAQ;QACb,MAAM,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC,OAAO,CAAC,yCAAyC,CAAC,CAAC,GAAG,EAAqB,CAAC;QACnG,OAAO,MAAM,CAAC,GAAG,CAAC;IACpB,CAAC;IAEO,eAAe,CAAC,GAAkB;QACxC,OAAO;YACL,EAAE,EAAE,GAAG,CAAC,EAAE;YACV,SAAS,EAAE,GAAG,CAAC,SAAS;YACxB,IAAI,EAAE,GAAG,CAAC,IAAsB;YAChC,OAAO,EAAE,GAAG,CAAC,OAA4B;YACzC,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE;YACtD,SAAS,EAAE,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,EAAE;YACzD,QAAQ,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE;YACtD,YAAY,EAAE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,EAAE;YAClE,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,SAAS,EAAE,GAAG,CAAC,SAAS;SACzB,CAAC;IACJ,CAAC;CACF"}
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* figma-extract.js — Figma REST API 디자인 추출 도구
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* node figma-extract.js tree <fileKey> <nodeId> [--depth=10]
|
|
8
|
+
* node figma-extract.js images <fileKey> <nodeId> --out=<dir> [--depth=10]
|
|
9
|
+
* node figma-extract.js screenshot <fileKey> <nodeId> --out=<path>
|
|
10
|
+
*
|
|
11
|
+
* Token: ~/.vibe/config.json → credentials.figma.accessToken
|
|
12
|
+
* 또는 FIGMA_ACCESS_TOKEN env
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import fs from 'fs';
|
|
16
|
+
import path from 'path';
|
|
17
|
+
import os from 'os';
|
|
18
|
+
|
|
19
|
+
// ─── Config ─────────────────────────────────────────────────────────
|
|
20
|
+
|
|
21
|
+
const FIGMA_API = 'https://api.figma.com/v1';
|
|
22
|
+
const MAX_RETRIES = 3;
|
|
23
|
+
const INITIAL_DELAY_MS = 2000;
|
|
24
|
+
|
|
25
|
+
function loadToken() {
|
|
26
|
+
if (process.env.FIGMA_ACCESS_TOKEN) return process.env.FIGMA_ACCESS_TOKEN;
|
|
27
|
+
const configPath = path.join(os.homedir(), '.vibe', 'config.json');
|
|
28
|
+
try {
|
|
29
|
+
const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
30
|
+
const token = config?.credentials?.figma?.accessToken;
|
|
31
|
+
if (token) return token;
|
|
32
|
+
} catch { /* ignore */ }
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function fail(msg) { console.error(JSON.stringify({ error: msg })); process.exit(1); }
|
|
37
|
+
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
38
|
+
|
|
39
|
+
// ─── HTTP ───────────────────────────────────────────────────────────
|
|
40
|
+
|
|
41
|
+
async function apiFetch(endpoint, token) {
|
|
42
|
+
let lastErr = '';
|
|
43
|
+
for (let i = 0; i < MAX_RETRIES; i++) {
|
|
44
|
+
try {
|
|
45
|
+
const res = await fetch(`${FIGMA_API}${endpoint}`, { headers: { 'X-Figma-Token': token } });
|
|
46
|
+
if (res.status === 429) { await sleep(INITIAL_DELAY_MS * 2 ** i); continue; }
|
|
47
|
+
if (res.status === 403) fail('403 Forbidden — check token permissions');
|
|
48
|
+
if (res.status === 404) fail('404 — check fileKey/nodeId');
|
|
49
|
+
if (!res.ok) {
|
|
50
|
+
lastErr = `HTTP ${res.status}`;
|
|
51
|
+
if (res.status >= 500) { await sleep(INITIAL_DELAY_MS * 2 ** i); continue; }
|
|
52
|
+
fail(lastErr);
|
|
53
|
+
}
|
|
54
|
+
return await res.json();
|
|
55
|
+
} catch (e) {
|
|
56
|
+
lastErr = e.message;
|
|
57
|
+
if (i < MAX_RETRIES - 1) await sleep(INITIAL_DELAY_MS * 2 ** i);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
fail(`Failed after ${MAX_RETRIES} retries: ${lastErr}`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ─── Color ──────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
function toCSS(c) {
|
|
66
|
+
if (!c) return null;
|
|
67
|
+
const r = Math.round(c.r * 255), g = Math.round(c.g * 255), b = Math.round(c.b * 255), a = c.a ?? 1;
|
|
68
|
+
if (a === 1) return `#${r.toString(16).padStart(2,'0')}${g.toString(16).padStart(2,'0')}${b.toString(16).padStart(2,'0')}`;
|
|
69
|
+
return `rgba(${r}, ${g}, ${b}, ${+a.toFixed(2)})`;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ─── CSS Extraction ─────────────────────────────────────────────────
|
|
73
|
+
|
|
74
|
+
function extractCSS(n) {
|
|
75
|
+
const css = {};
|
|
76
|
+
// Layout
|
|
77
|
+
if (n.layoutMode === 'VERTICAL') { css.display = 'flex'; css.flexDirection = 'column'; }
|
|
78
|
+
else if (n.layoutMode === 'HORIZONTAL') { css.display = 'flex'; css.flexDirection = 'row'; }
|
|
79
|
+
const axM = { MIN:'flex-start', CENTER:'center', MAX:'flex-end', SPACE_BETWEEN:'space-between' };
|
|
80
|
+
const crM = { MIN:'flex-start', CENTER:'center', MAX:'flex-end', BASELINE:'baseline' };
|
|
81
|
+
if (n.primaryAxisAlignItems && axM[n.primaryAxisAlignItems]) css.justifyContent = axM[n.primaryAxisAlignItems];
|
|
82
|
+
if (n.counterAxisAlignItems && crM[n.counterAxisAlignItems]) css.alignItems = crM[n.counterAxisAlignItems];
|
|
83
|
+
if (n.itemSpacing > 0) css.gap = `${n.itemSpacing}px`;
|
|
84
|
+
// Padding
|
|
85
|
+
const pt=n.paddingTop||0, pr=n.paddingRight||0, pb=n.paddingBottom||0, pl=n.paddingLeft||0;
|
|
86
|
+
if (pt||pr||pb||pl) css.padding = `${pt}px ${pr}px ${pb}px ${pl}px`;
|
|
87
|
+
// Size
|
|
88
|
+
if (n.absoluteBoundingBox) { css.width = `${Math.round(n.absoluteBoundingBox.width)}px`; css.height = `${Math.round(n.absoluteBoundingBox.height)}px`; }
|
|
89
|
+
// Position / overflow / opacity
|
|
90
|
+
if (n.layoutPositioning === 'ABSOLUTE') css.position = 'absolute';
|
|
91
|
+
if (n.clipsContent) css.overflow = 'hidden';
|
|
92
|
+
if (n.opacity != null && n.opacity < 1) css.opacity = n.opacity.toFixed(2);
|
|
93
|
+
// Blend
|
|
94
|
+
const bm = { MULTIPLY:'multiply', SCREEN:'screen', OVERLAY:'overlay', DARKEN:'darken', LIGHTEN:'lighten', COLOR_DODGE:'color-dodge', COLOR_BURN:'color-burn', HARD_LIGHT:'hard-light', SOFT_LIGHT:'soft-light', DIFFERENCE:'difference', EXCLUSION:'exclusion', HUE:'hue', SATURATION:'saturation', COLOR:'color', LUMINOSITY:'luminosity' };
|
|
95
|
+
if (n.blendMode && bm[n.blendMode]) css.mixBlendMode = bm[n.blendMode];
|
|
96
|
+
// Radius
|
|
97
|
+
if (n.cornerRadius > 0) css.borderRadius = `${n.cornerRadius}px`;
|
|
98
|
+
else if (n.rectangleCornerRadii) { const [a,b,c,d] = n.rectangleCornerRadii; css.borderRadius = `${a}px ${b}px ${c}px ${d}px`; }
|
|
99
|
+
// Fills
|
|
100
|
+
let imgRef;
|
|
101
|
+
for (const f of (n.fills||[]).filter(f=>f.visible!==false)) {
|
|
102
|
+
if (f.type === 'SOLID') css.backgroundColor = toCSS({ ...f.color, a: f.opacity ?? f.color?.a ?? 1 });
|
|
103
|
+
else if (f.type === 'IMAGE') imgRef = f.imageRef;
|
|
104
|
+
}
|
|
105
|
+
// Strokes
|
|
106
|
+
const stroke = (n.strokes||[]).find(s=>s.visible!==false&&s.type==='SOLID');
|
|
107
|
+
if (stroke && n.strokeWeight) css.border = `${n.strokeWeight}px solid ${toCSS(stroke.color)}`;
|
|
108
|
+
// Effects
|
|
109
|
+
const shadows = [];
|
|
110
|
+
for (const e of (n.effects||[]).filter(e=>e.visible!==false)) {
|
|
111
|
+
if (e.type==='DROP_SHADOW'||e.type==='INNER_SHADOW') {
|
|
112
|
+
const ins = e.type==='INNER_SHADOW'?'inset ':'';
|
|
113
|
+
shadows.push(`${ins}${e.offset?.x||0}px ${e.offset?.y||0}px ${e.radius||0}px ${e.spread||0}px ${toCSS(e.color)}`);
|
|
114
|
+
} else if (e.type==='LAYER_BLUR') css.filter = `blur(${e.radius}px)`;
|
|
115
|
+
else if (e.type==='BACKGROUND_BLUR') css.backdropFilter = `blur(${e.radius}px)`;
|
|
116
|
+
}
|
|
117
|
+
if (shadows.length) css.boxShadow = shadows.join(', ');
|
|
118
|
+
// Text
|
|
119
|
+
if (n.type === 'TEXT' && n.style) {
|
|
120
|
+
const s = n.style;
|
|
121
|
+
if (s.fontFamily) css.fontFamily = `'${s.fontFamily}', sans-serif`;
|
|
122
|
+
if (s.fontSize) css.fontSize = `${s.fontSize}px`;
|
|
123
|
+
if (s.fontWeight) css.fontWeight = String(s.fontWeight);
|
|
124
|
+
if (s.lineHeightPx) css.lineHeight = `${s.lineHeightPx}px`;
|
|
125
|
+
if (s.letterSpacing) css.letterSpacing = `${s.letterSpacing}px`;
|
|
126
|
+
const ta = { LEFT:'left', CENTER:'center', RIGHT:'right', JUSTIFIED:'justify' };
|
|
127
|
+
if (s.textAlignHorizontal && ta[s.textAlignHorizontal]) css.textAlign = ta[s.textAlignHorizontal];
|
|
128
|
+
const tf = (n.fills||[]).find(f=>f.visible!==false&&f.type==='SOLID');
|
|
129
|
+
if (tf) css.color = toCSS(tf.color);
|
|
130
|
+
}
|
|
131
|
+
return imgRef ? { ...css, _imageRef: imgRef } : css;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// ─── Tree ───────────────────────────────────────────────────────────
|
|
135
|
+
|
|
136
|
+
function walk(node) {
|
|
137
|
+
const css = extractCSS(node);
|
|
138
|
+
const r = { nodeId: node.id, name: node.name||'', type: node.type, size: null, css: {...css}, children: [] };
|
|
139
|
+
if (node.type==='TEXT' && node.characters) r.text = node.characters;
|
|
140
|
+
if (node.absoluteBoundingBox) r.size = { width: Math.round(node.absoluteBoundingBox.width), height: Math.round(node.absoluteBoundingBox.height) };
|
|
141
|
+
if (css._imageRef) { r.imageRef = css._imageRef; delete r.css._imageRef; }
|
|
142
|
+
if (node.children?.length) r.children = node.children.map(walk);
|
|
143
|
+
return r;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
function collectRefs(node, set = new Set()) {
|
|
147
|
+
if (node.imageRef) set.add(node.imageRef);
|
|
148
|
+
(node.children||[]).forEach(c => collectRefs(c, set));
|
|
149
|
+
return set;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ─── Commands ───────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
async function cmdTree(token, fk, nid, depth) {
|
|
155
|
+
const dp = depth ? `&depth=${depth}` : '';
|
|
156
|
+
const data = await apiFetch(`/files/${fk}/nodes?ids=${nid}${dp}`, token);
|
|
157
|
+
const nd = data.nodes?.[nid];
|
|
158
|
+
if (!nd?.document) fail(`Node ${nid} not found`);
|
|
159
|
+
console.log(JSON.stringify(walk(nd.document), null, 2));
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
async function cmdImages(token, fk, nid, outDir, depth) {
|
|
163
|
+
if (!outDir) fail('--out required');
|
|
164
|
+
if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
|
|
165
|
+
// tree → refs
|
|
166
|
+
const dp = depth ? `&depth=${depth}` : '';
|
|
167
|
+
const data = await apiFetch(`/files/${fk}/nodes?ids=${nid}${dp}`, token);
|
|
168
|
+
const nd = data.nodes?.[nid];
|
|
169
|
+
if (!nd?.document) fail(`Node ${nid} not found`);
|
|
170
|
+
const tree = walk(nd.document);
|
|
171
|
+
const refs = collectRefs(tree);
|
|
172
|
+
if (!refs.size) { console.log(JSON.stringify({ total: 0, images: {} })); return; }
|
|
173
|
+
// download
|
|
174
|
+
const allImg = await apiFetch(`/files/${fk}/images`, token);
|
|
175
|
+
const urls = allImg.meta?.images || {};
|
|
176
|
+
const imageMap = {};
|
|
177
|
+
const dl = [];
|
|
178
|
+
for (const ref of refs) {
|
|
179
|
+
const url = urls[ref];
|
|
180
|
+
if (!url) continue;
|
|
181
|
+
const out = path.join(outDir, ref.slice(0,16) + '.png');
|
|
182
|
+
dl.push(fetch(url).then(r=>r.arrayBuffer()).then(b=>{
|
|
183
|
+
fs.writeFileSync(out, Buffer.from(b));
|
|
184
|
+
const sz = fs.statSync(out).size;
|
|
185
|
+
if (sz > 0) imageMap[ref] = out;
|
|
186
|
+
}).catch(()=>{}));
|
|
187
|
+
}
|
|
188
|
+
await Promise.all(dl);
|
|
189
|
+
console.log(JSON.stringify({ total: Object.keys(imageMap).length, images: imageMap }, null, 2));
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function cmdScreenshot(token, fk, nid, outPath) {
|
|
193
|
+
if (!outPath) fail('--out required');
|
|
194
|
+
const data = await apiFetch(`/images/${fk}?ids=${nid}&format=png&scale=2`, token);
|
|
195
|
+
const url = data.images?.[nid];
|
|
196
|
+
if (!url) fail(`No image for ${nid}`);
|
|
197
|
+
const dir = path.dirname(outPath);
|
|
198
|
+
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
|
|
199
|
+
const buf = Buffer.from(await (await fetch(url)).arrayBuffer());
|
|
200
|
+
fs.writeFileSync(outPath, buf);
|
|
201
|
+
console.log(JSON.stringify({ path: outPath, size: buf.length }));
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// ─── CLI ────────────────────────────────────────────────────────────
|
|
205
|
+
|
|
206
|
+
const args = process.argv.slice(2);
|
|
207
|
+
const flags = {};
|
|
208
|
+
const pos = [];
|
|
209
|
+
for (const a of args) {
|
|
210
|
+
if (a.startsWith('--')) { const [k,v] = a.slice(2).split('='); flags[k] = v ?? ''; }
|
|
211
|
+
else pos.push(a);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const token = loadToken();
|
|
215
|
+
if (!token) fail('Figma token not found. Run: vibe figma setup <token>');
|
|
216
|
+
|
|
217
|
+
const [cmd, fk, nidRaw] = pos;
|
|
218
|
+
const nid = nidRaw?.replace(/-/g, ':');
|
|
219
|
+
|
|
220
|
+
switch (cmd) {
|
|
221
|
+
case 'tree': await cmdTree(token, fk, nid, flags.depth ? +flags.depth : undefined); break;
|
|
222
|
+
case 'images': await cmdImages(token, fk, nid, flags.out, flags.depth ? +flags.depth : 10); break;
|
|
223
|
+
case 'screenshot': await cmdScreenshot(token, fk, nid, flags.out); break;
|
|
224
|
+
default: console.log('Usage: node figma-extract.js <tree|images|screenshot> <fileKey> <nodeId> [flags]');
|
|
225
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@su-record/vibe",
|
|
3
|
-
"version": "2.8.
|
|
3
|
+
"version": "2.8.32",
|
|
4
4
|
"description": "AI Coding Framework for Claude Code — 49 agents, 41+ tools, multi-LLM orchestration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/cli/index.js",
|
|
@@ -93,7 +93,6 @@
|
|
|
93
93
|
"skills/",
|
|
94
94
|
"hooks/",
|
|
95
95
|
".env.example",
|
|
96
|
-
".env",
|
|
97
96
|
"CLAUDE.md",
|
|
98
97
|
"README.md",
|
|
99
98
|
"LICENSE"
|
|
@@ -68,14 +68,18 @@ tier: standard
|
|
|
68
68
|
|
|
69
69
|
```
|
|
70
70
|
URL에서 fileKey, nodeId 추출
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
71
|
+
getTree(fileKey, nodeId, depth=2) → 프레임 목록
|
|
72
|
+
|
|
73
|
+
Bash:
|
|
74
|
+
node -e "
|
|
75
|
+
import { getTree } from './dist/infra/lib/figma/index.js';
|
|
76
|
+
const tree = await getTree({ fileKey: '{fileKey}', nodeId: '{nodeId}', depth: 2 });
|
|
77
|
+
console.log(JSON.stringify(tree));
|
|
78
|
+
"
|
|
79
|
+
|
|
80
|
+
프레임 분류 (name 패턴 기반):
|
|
81
|
+
SPEC — "기능 정의서", "정책" → depth 높여서 텍스트 추출
|
|
82
|
+
CONFIG — "해상도", "브라우저" → 스케일 팩터 계산
|
|
79
83
|
SHARED — "공통", "GNB", "Footer", "Popup" → 공통 컴포넌트 파악
|
|
80
84
|
PAGE — "화면설계", "메인 -" → 섹션 목록 + 인터랙션 스펙
|
|
81
85
|
|
|
@@ -87,8 +91,8 @@ get_metadata(fileKey, nodeId) → 프레임 목록
|
|
|
87
91
|
4순위: SHARED (공통 요소, Popup) — 필요 시
|
|
88
92
|
|
|
89
93
|
높이 1500px 이상 프레임:
|
|
90
|
-
→
|
|
91
|
-
→ 또는
|
|
94
|
+
→ getScreenshot으로 시각 파악
|
|
95
|
+
→ 또는 depth 높여서 하위 분할 조회
|
|
92
96
|
```
|
|
93
97
|
|
|
94
98
|
### 1-2. 레이아웃 + 컴포넌트 구성 (코드 생성)
|
|
@@ -224,35 +228,44 @@ SCSS 파일 기본 내용 Write:
|
|
|
224
228
|
|
|
225
229
|
**각 섹션을 순서대로, 한 섹션을 완전히 완료한 후 다음으로.**
|
|
226
230
|
|
|
227
|
-
#### a.
|
|
231
|
+
#### a. 노드 트리 + CSS 추출
|
|
228
232
|
|
|
229
233
|
```
|
|
230
|
-
|
|
234
|
+
Figma REST API로 노드 트리와 CSS 속성을 직접 추출한다.
|
|
235
|
+
MCP 플러그인(get_design_context/get_metadata)은 사용하지 않는다.
|
|
236
|
+
|
|
237
|
+
Bash:
|
|
238
|
+
# [FIGMA_SCRIPT] = ~/.vibe/hooks/scripts/figma-extract.js
|
|
239
|
+
node "[FIGMA_SCRIPT]" tree {fileKey} {섹션.nodeId} --depth=10
|
|
231
240
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
241
|
+
반환 (JSON):
|
|
242
|
+
{
|
|
243
|
+
nodeId, name, type, size: {width, height},
|
|
244
|
+
css: { display, flexDirection, gap, fontSize, color, ... },
|
|
245
|
+
text: "텍스트 내용" (TEXT 노드),
|
|
246
|
+
imageRef: "abc123" (이미지 fill),
|
|
247
|
+
children: [...]
|
|
248
|
+
}
|
|
237
249
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
250
|
+
CSS는 Figma 노드 속성에서 직접 추출 — Tailwind 역변환 불필요:
|
|
251
|
+
fills → background-color effects → box-shadow, filter
|
|
252
|
+
strokes → border style → font-family, font-size, color
|
|
253
|
+
layoutMode → display:flex itemSpacing → gap
|
|
254
|
+
padding* → padding cornerRadius → border-radius
|
|
255
|
+
|
|
256
|
+
인스턴스 내부 자식도 depth로 전부 조회 가능 (MCP 한계 해결).
|
|
241
257
|
```
|
|
242
258
|
|
|
243
259
|
#### b. 이미지 다운로드 (BLOCKING)
|
|
244
260
|
|
|
245
261
|
```
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
변수명 → 파일명: imgSnowParticle12 → snow-particle-12.webp
|
|
249
|
-
다운로드: curl -sL "{url}" -o images/{feature}/{파일명}
|
|
250
|
-
검증: ls -la → 모든 파일 존재 + 0byte 아닌지
|
|
262
|
+
트리에서 imageRef가 있는 노드를 수집 → Figma API로 다운로드.
|
|
251
263
|
|
|
252
|
-
|
|
253
|
-
|
|
264
|
+
Bash:
|
|
265
|
+
node "[FIGMA_SCRIPT]" images {fileKey} {섹션.nodeId} --out=images/{feature}/ --depth=10
|
|
254
266
|
|
|
255
|
-
|
|
267
|
+
검증: result.total = refs.size (누락 0)
|
|
268
|
+
전부 완료해야 c 단계로 진행.
|
|
256
269
|
```
|
|
257
270
|
|
|
258
271
|
#### c. 클래스 매핑 테이블 생성
|
|
@@ -262,38 +275,33 @@ SCSS 작성 전에 반드시 매핑 테이블을 먼저 출력한다.
|
|
|
262
275
|
이 테이블 없이 SCSS를 작성하지 않는다.
|
|
263
276
|
|
|
264
277
|
1. Phase 1 컴포넌트의 클래스 목록을 Read로 수집
|
|
265
|
-
2.
|
|
278
|
+
2. 트리의 name + css 속성을 분석
|
|
266
279
|
3. 매핑 테이블 출력:
|
|
267
280
|
|
|
268
|
-
|
|
269
|
-
│ Phase 1 클래스 │
|
|
270
|
-
|
|
271
|
-
│ .kidSection │ (root)
|
|
272
|
-
│ .kidBg │ BG │ absolute
|
|
273
|
-
│ .kidLoginBtn │ Btn_Login │ border shadow
|
|
274
|
-
│ .kidLoginBtnText │ (
|
|
275
|
-
│ .kidDivider │ Divider │
|
|
276
|
-
│ .kidSteamLink │ steam_account │
|
|
277
|
-
│ .kidSteamNote │ (하위
|
|
278
|
-
|
|
281
|
+
┌─────────────────────┬──────────────────┬────────────────────────────────────┐
|
|
282
|
+
│ Phase 1 클래스 │ 트리 노드 name │ 추출된 CSS 값 │
|
|
283
|
+
├─────────────────────┼──────────────────┼────────────────────────────────────┤
|
|
284
|
+
│ .kidSection │ KID (root) │ flex, column, gap:32px, pad:48px │
|
|
285
|
+
│ .kidBg │ BG │ absolute, 720x800 │
|
|
286
|
+
│ .kidLoginBtn │ Btn_Login │ flex, border, shadow, 640x148 │
|
|
287
|
+
│ .kidLoginBtnText │ (TEXT 노드) │ fontSize:36px, color:#fff, w:700 │
|
|
288
|
+
│ .kidDivider │ Divider │ 640x1 │
|
|
289
|
+
│ .kidSteamLink │ steam_account │ fontSize:24px, w:600 │
|
|
290
|
+
│ .kidSteamNote │ (하위 TEXT) │ fontSize:20px, color:#dadce3 │
|
|
291
|
+
└─────────────────────┴──────────────────┴────────────────────────────────────┘
|
|
279
292
|
|
|
280
293
|
매핑 기준:
|
|
281
|
-
|
|
282
|
-
|
|
294
|
+
name 일치 → 직접 매핑
|
|
295
|
+
name 없음 → 트리 위치/text 내용으로 판단
|
|
283
296
|
Phase 1에 없는 요소 → 클래스 신규 추가 (template에도 반영)
|
|
284
|
-
|
|
297
|
+
트리에 없는 클래스 → 스타일 없이 유지
|
|
285
298
|
```
|
|
286
299
|
|
|
287
300
|
#### d. SCSS 작성
|
|
288
301
|
|
|
289
302
|
```
|
|
290
|
-
매핑 테이블의 각
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
CSS 변수 패턴 처리:
|
|
294
|
-
font-[family-name:var(--font/family/pretendard,...)] → fallback 값 사용
|
|
295
|
-
text-[length:var(--font/size/heading/24,24px)] → 24px
|
|
296
|
-
text-[color:var(--color/grayscale/300,#dadce3)] → #dadce3
|
|
303
|
+
매핑 테이블의 각 행에 대해, 트리의 css 속성을 SCSS로 작성.
|
|
304
|
+
CSS 값은 트리에서 이미 추출되어 있으므로 변환 불필요 — 그대로 사용.
|
|
297
305
|
|
|
298
306
|
scaleFactor 적용:
|
|
299
307
|
px 값 → × scaleFactor (font-size, padding, margin, gap, width, height, border-radius)
|
|
@@ -308,7 +316,7 @@ scaleFactor 적용:
|
|
|
308
316
|
styles/{feature}/_tokens.scss
|
|
309
317
|
→ 새로 발견된 색상/폰트/스페이싱 토큰 추가
|
|
310
318
|
|
|
311
|
-
BG 레이어 패턴 (
|
|
319
|
+
BG 레이어 패턴 (트리에서 position:absolute + 이미지 fill):
|
|
312
320
|
.{section}Bg → position: absolute; inset: 0; z-index: 0;
|
|
313
321
|
.{section}Content → position: relative; z-index: 1;
|
|
314
322
|
|
|
@@ -318,14 +326,14 @@ index.scss에 새 섹션 @import 추가.
|
|
|
318
326
|
#### e. template 업데이트
|
|
319
327
|
|
|
320
328
|
```
|
|
321
|
-
Phase 1 컴포넌트의 template을
|
|
329
|
+
Phase 1 컴포넌트의 template을 트리 구조 기반으로 리팩토링.
|
|
322
330
|
script(JSDoc, 인터페이스, 목 데이터, 핸들러)는 보존.
|
|
323
331
|
|
|
324
|
-
1.
|
|
325
|
-
|
|
332
|
+
1. 트리의 HTML 구조를 프로젝트 스택으로 변환:
|
|
333
|
+
FRAME → <div>, TEXT → <p>/<span>, VECTOR/RECTANGLE with imageRef → <img>
|
|
326
334
|
|
|
327
335
|
2. 이미지 경로를 imageMap으로 교체:
|
|
328
|
-
|
|
336
|
+
imageRef "abc123" → src="/images/{feature}/abc123.png"
|
|
329
337
|
|
|
330
338
|
3. BG 레이어 구조 적용:
|
|
331
339
|
.{section}Bg div (배경) + .{section}Content div (콘텐츠)
|
|
@@ -334,7 +342,7 @@ script(JSDoc, 인터페이스, 목 데이터, 핸들러)는 보존.
|
|
|
334
342
|
v-for, @click, v-if, $emit 등을 새 구조의 적절한 위치에 배치
|
|
335
343
|
|
|
336
344
|
5. 접근성:
|
|
337
|
-
장식 이미지 → alt="" aria-hidden="true"
|
|
345
|
+
장식 이미지 (BG 내) → alt="" aria-hidden="true"
|
|
338
346
|
콘텐츠 이미지 → alt="설명적 텍스트"
|
|
339
347
|
|
|
340
348
|
컴포넌트에 <style> 블록 없음. 스타일은 전부 외부 SCSS.
|
|
@@ -344,13 +352,12 @@ script(JSDoc, 인터페이스, 목 데이터, 핸들러)는 보존.
|
|
|
344
352
|
|
|
345
353
|
```
|
|
346
354
|
Grep 체크:
|
|
347
|
-
□ "figma.com/api" in 생성 파일 → 0건
|
|
348
355
|
□ 'src=""' in 컴포넌트 파일 → 0건
|
|
349
356
|
□ "<style" in 컴포넌트 파일 → 0건
|
|
350
357
|
|
|
351
358
|
Read 체크:
|
|
352
359
|
□ 외부 SCSS 파일에 font-size, color 존재 (브라우저 기본 스타일 방지)
|
|
353
|
-
□ 이미지 파일 수 =
|
|
360
|
+
□ 이미지 파일 수 = imageRef 수 (누락 0)
|
|
354
361
|
|
|
355
362
|
실패 → 수정 → 재검증
|
|
356
363
|
```
|
|
@@ -373,13 +380,14 @@ Read 체크:
|
|
|
373
380
|
|
|
374
381
|
```
|
|
375
382
|
Grep 체크:
|
|
376
|
-
□ "figma.com/api" in 모든 생성 파일 → 0건
|
|
377
383
|
□ "<style" in components/{feature}/ → 0건
|
|
378
384
|
□ 'src=""' in components/{feature}/ → 0건
|
|
379
385
|
□ Glob: images/{feature}/ → 이미지 파일 존재
|
|
380
386
|
|
|
381
387
|
시각 검증:
|
|
382
|
-
각
|
|
388
|
+
각 섹션 스크린샷:
|
|
389
|
+
node "[FIGMA_SCRIPT]" screenshot {fileKey} {nodeId} --out=/tmp/{section}.png
|
|
390
|
+
→ dev 서버/preview와 비교
|
|
383
391
|
P1 (필수): 이미지 누락, 레이아웃 구조 다름, 텍스트 스타일 미적용
|
|
384
392
|
P2 (권장): 미세 간격, 미세 색상 차이
|
|
385
393
|
→ P1 수정 → 재검증 (P1=0 될 때까지)
|