codeloop-mcp-server 0.1.14 → 0.1.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/auth/local_mode.d.ts +32 -0
- package/dist/auth/local_mode.d.ts.map +1 -0
- package/dist/auth/local_mode.js +56 -0
- package/dist/auth/local_mode.js.map +1 -0
- package/dist/auth/usage_tracker.d.ts +11 -1
- package/dist/auth/usage_tracker.d.ts.map +1 -1
- package/dist/auth/usage_tracker.js +51 -4
- package/dist/auth/usage_tracker.js.map +1 -1
- package/dist/environment/presets.d.ts +46 -0
- package/dist/environment/presets.d.ts.map +1 -0
- package/dist/environment/presets.js +109 -0
- package/dist/environment/presets.js.map +1 -0
- package/dist/evidence/baseline_governance.d.ts +62 -0
- package/dist/evidence/baseline_governance.d.ts.map +1 -0
- package/dist/evidence/baseline_governance.js +113 -0
- package/dist/evidence/baseline_governance.js.map +1 -0
- package/dist/evidence/run_lineage.d.ts +66 -0
- package/dist/evidence/run_lineage.d.ts.map +1 -0
- package/dist/evidence/run_lineage.js +138 -0
- package/dist/evidence/run_lineage.js.map +1 -0
- package/dist/evidence/screenshot_diff.d.ts +6 -2
- package/dist/evidence/screenshot_diff.d.ts.map +1 -1
- package/dist/evidence/screenshot_diff.js +40 -3
- package/dist/evidence/screenshot_diff.js.map +1 -1
- package/dist/evidence/visual_attribution.d.ts +32 -0
- package/dist/evidence/visual_attribution.d.ts.map +1 -0
- package/dist/evidence/visual_attribution.js +88 -0
- package/dist/evidence/visual_attribution.js.map +1 -0
- package/dist/index.js +150 -0
- package/dist/index.js.map +1 -1
- package/dist/prompt_manager/index.d.ts +27 -0
- package/dist/prompt_manager/index.d.ts.map +1 -0
- package/dist/prompt_manager/index.js +28 -0
- package/dist/prompt_manager/index.js.map +1 -0
- package/dist/prompt_manager/state_machine.d.ts +55 -0
- package/dist/prompt_manager/state_machine.d.ts.map +1 -0
- package/dist/prompt_manager/state_machine.js +60 -0
- package/dist/prompt_manager/state_machine.js.map +1 -0
- package/dist/prompt_manager/templates.d.ts +43 -0
- package/dist/prompt_manager/templates.d.ts.map +1 -0
- package/dist/prompt_manager/templates.js +177 -0
- package/dist/prompt_manager/templates.js.map +1 -0
- package/dist/runners/base.d.ts +1 -1
- package/dist/runners/base.d.ts.map +1 -1
- package/dist/runners/base.js +2 -2
- package/dist/runners/base.js.map +1 -1
- package/dist/runners/figma_spec_generator.d.ts +54 -0
- package/dist/runners/figma_spec_generator.d.ts.map +1 -0
- package/dist/runners/figma_spec_generator.js +227 -0
- package/dist/runners/figma_spec_generator.js.map +1 -0
- package/dist/runners/playwright.d.ts +9 -1
- package/dist/runners/playwright.d.ts.map +1 -1
- package/dist/runners/playwright.js +18 -3
- package/dist/runners/playwright.js.map +1 -1
- package/dist/runners/plugin_sdk.d.ts +25 -0
- package/dist/runners/plugin_sdk.d.ts.map +1 -0
- package/dist/runners/plugin_sdk.js +86 -0
- package/dist/runners/plugin_sdk.js.map +1 -0
- package/dist/state/dependency_graph.d.ts +23 -0
- package/dist/state/dependency_graph.d.ts.map +1 -0
- package/dist/state/dependency_graph.js +127 -0
- package/dist/state/dependency_graph.js.map +1 -0
- package/dist/state/section_registry.d.ts.map +1 -1
- package/dist/state/section_registry.js +11 -5
- package/dist/state/section_registry.js.map +1 -1
- package/dist/tools/design_compare.js +2 -2
- package/dist/tools/design_compare.js.map +1 -1
- package/dist/tools/update_baseline.d.ts +3 -0
- package/dist/tools/update_baseline.d.ts.map +1 -1
- package/dist/tools/update_baseline.js +18 -2
- package/dist/tools/update_baseline.js.map +1 -1
- package/dist/tools/verify.d.ts.map +1 -1
- package/dist/tools/verify.js +66 -4
- package/dist/tools/verify.js.map +1 -1
- package/dist/tools/visual_review.js +1 -1
- package/dist/tools/visual_review.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Prompt Manager — five-layer template library.
|
|
3
|
+
*
|
|
4
|
+
* Layers correspond to the lifecycle of multi-section app construction
|
|
5
|
+
* (see CodeLoop_Development_Plan.md §19):
|
|
6
|
+
* 1. master_human — top-level user-facing instruction relayed to the agent
|
|
7
|
+
* 2. planning — translate master spec into section graph + checkpoints
|
|
8
|
+
* 3. section_implement — per-section build prompt
|
|
9
|
+
* 4. repair — issued when verification or gate_check fails
|
|
10
|
+
* 5. integration — issued at integration checkpoints to wire sections together
|
|
11
|
+
*
|
|
12
|
+
* Each template is plain text with `{{variable}}` placeholders that are filled
|
|
13
|
+
* from a context object at render time. Missing variables become the literal
|
|
14
|
+
* empty string (the renderer never throws); callers are expected to validate
|
|
15
|
+
* required variables via {@link describeTemplate}.
|
|
16
|
+
*/
|
|
17
|
+
const TEMPLATES = {
|
|
18
|
+
master_human: {
|
|
19
|
+
layer: "master_human",
|
|
20
|
+
id: "master_human.v1",
|
|
21
|
+
description: "Initial framing for the agent when the user issues a one-line application goal. Echoes back the goal, requires a master spec, and forces a planning pass.",
|
|
22
|
+
required_vars: ["user_goal", "project_name"],
|
|
23
|
+
body: [
|
|
24
|
+
"You are working on **{{project_name}}**.",
|
|
25
|
+
"",
|
|
26
|
+
"User goal:",
|
|
27
|
+
"> {{user_goal}}",
|
|
28
|
+
"",
|
|
29
|
+
"Before writing any code:",
|
|
30
|
+
"1. Confirm `docs/specs/_master.md` exists. If it does not, write one that breaks the goal into independently testable sections with explicit dependencies.",
|
|
31
|
+
"2. Call `codeloop_section_status` to bootstrap the section registry.",
|
|
32
|
+
"3. Call `codeloop_get_prompt` with `layer=\"planning\"` to produce a build plan, then proceed section by section.",
|
|
33
|
+
"",
|
|
34
|
+
"Constraints:",
|
|
35
|
+
"- Never declare the project complete without a passing `codeloop_gate_check` (confidence ≥ 94%).",
|
|
36
|
+
"- After every code change, call `codeloop_verify`. If `codeloop_diagnose` returns repair tasks, call `codeloop_get_prompt` with `layer=\"repair\"`.",
|
|
37
|
+
"- After every {{integration_cadence}} sections, call `codeloop_integration_check`.",
|
|
38
|
+
].join("\n"),
|
|
39
|
+
},
|
|
40
|
+
planning: {
|
|
41
|
+
layer: "planning",
|
|
42
|
+
id: "planning.v1",
|
|
43
|
+
description: "Translates the master spec into an ordered build plan and per-section acceptance criteria. Output is consumed by section_implement.",
|
|
44
|
+
required_vars: ["master_spec_path", "section_count"],
|
|
45
|
+
body: [
|
|
46
|
+
"Plan a section-by-section build for the spec at `{{master_spec_path}}`.",
|
|
47
|
+
"There are {{section_count}} sections. For each section, list:",
|
|
48
|
+
" - section_id (kebab-case, matches the spec heading)",
|
|
49
|
+
" - blocking dependencies (other section_ids that must be `READY` first)",
|
|
50
|
+
" - acceptance criteria (testable, max 6 bullets)",
|
|
51
|
+
" - integration risk (which earlier sections are most likely to regress)",
|
|
52
|
+
"",
|
|
53
|
+
"Order the sections via topological sort. When dependencies tie, prefer the section with the fewest acceptance criteria first to maximise early gate-check signal.",
|
|
54
|
+
"",
|
|
55
|
+
"Emit the result as JSON matching this shape:",
|
|
56
|
+
"```json",
|
|
57
|
+
"{",
|
|
58
|
+
" \"order\": [\"section-id-1\", \"section-id-2\", ...],",
|
|
59
|
+
" \"sections\": {",
|
|
60
|
+
" \"section-id-1\": { \"deps\": [], \"acceptance\": [...], \"risk\": [...] }",
|
|
61
|
+
" },",
|
|
62
|
+
" \"integration_checkpoints\": [\"section-id-2\", \"section-id-4\"]",
|
|
63
|
+
"}",
|
|
64
|
+
"```",
|
|
65
|
+
].join("\n"),
|
|
66
|
+
},
|
|
67
|
+
section_implement: {
|
|
68
|
+
layer: "section_implement",
|
|
69
|
+
id: "section_implement.v1",
|
|
70
|
+
description: "Per-section build prompt. Embeds acceptance criteria and forces a verify+gate loop before the section may be marked READY.",
|
|
71
|
+
required_vars: ["section_id", "section_name", "acceptance_criteria"],
|
|
72
|
+
body: [
|
|
73
|
+
"Implement section **{{section_id}} — {{section_name}}**.",
|
|
74
|
+
"",
|
|
75
|
+
"Acceptance criteria:",
|
|
76
|
+
"{{acceptance_criteria}}",
|
|
77
|
+
"",
|
|
78
|
+
"Dependencies already READY: {{ready_deps}}",
|
|
79
|
+
"",
|
|
80
|
+
"Workflow:",
|
|
81
|
+
"1. Make the smallest coherent code change that advances at least one acceptance criterion.",
|
|
82
|
+
"2. Call `codeloop_verify` with `section_id={{section_id}}`.",
|
|
83
|
+
"3. If verification fails, request the repair prompt (`codeloop_get_prompt` layer=\"repair\"). Do not skip to the next section.",
|
|
84
|
+
"4. When all acceptance criteria are met and `codeloop_gate_check` passes for this section, call `codeloop_section_status` to mark it READY.",
|
|
85
|
+
"",
|
|
86
|
+
"Do not modify files belonging to other sections. If you must, stop and request `layer=\"integration\"` instead.",
|
|
87
|
+
].join("\n"),
|
|
88
|
+
},
|
|
89
|
+
repair: {
|
|
90
|
+
layer: "repair",
|
|
91
|
+
id: "repair.v1",
|
|
92
|
+
description: "Repair prompt. Used when codeloop_verify or codeloop_gate_check fails. Forces a constrained, deterministic fix loop.",
|
|
93
|
+
required_vars: ["section_id", "failure_summary", "attempt", "max_attempts"],
|
|
94
|
+
body: [
|
|
95
|
+
"Repair attempt {{attempt}}/{{max_attempts}} for section **{{section_id}}**.",
|
|
96
|
+
"",
|
|
97
|
+
"Failure summary from `codeloop_diagnose`:",
|
|
98
|
+
"{{failure_summary}}",
|
|
99
|
+
"",
|
|
100
|
+
"Rules:",
|
|
101
|
+
"1. Address the highest-priority repair task first.",
|
|
102
|
+
"2. Do not introduce changes outside the failing section's directory unless the diagnose report explicitly says so.",
|
|
103
|
+
"3. After each fix, re-run `codeloop_verify` for this section only.",
|
|
104
|
+
"4. If attempt {{attempt}} >= {{max_attempts}}, stop and call `codeloop_replan` to escalate.",
|
|
105
|
+
"",
|
|
106
|
+
"Treat any new lint warnings or test regressions in already-READY sections as a hard stop — switch to the integration prompt instead.",
|
|
107
|
+
].join("\n"),
|
|
108
|
+
},
|
|
109
|
+
integration: {
|
|
110
|
+
layer: "integration",
|
|
111
|
+
id: "integration.v1",
|
|
112
|
+
description: "Integration prompt issued at checkpoints. Coordinates cross-section verification and regression hunting.",
|
|
113
|
+
required_vars: ["sections_in_scope", "checkpoint_index"],
|
|
114
|
+
body: [
|
|
115
|
+
"Integration checkpoint #{{checkpoint_index}}.",
|
|
116
|
+
"",
|
|
117
|
+
"Sections in scope: {{sections_in_scope}}",
|
|
118
|
+
"",
|
|
119
|
+
"Workflow:",
|
|
120
|
+
"1. Call `codeloop_integration_check` for the listed sections.",
|
|
121
|
+
"2. For each regression listed in the result, open the affected section, run `codeloop_get_prompt` with `layer=\"repair\"`, and fix it before continuing.",
|
|
122
|
+
"3. Capture cross-section screenshots and a short interaction recording with `codeloop_capture_screenshot` and `codeloop_start_recording`/`codeloop_stop_recording`.",
|
|
123
|
+
"4. Call `codeloop_gate_check` with `scope=\"integration\"`.",
|
|
124
|
+
"",
|
|
125
|
+
"Only return to per-section work when integration confidence is ≥ 94%.",
|
|
126
|
+
].join("\n"),
|
|
127
|
+
},
|
|
128
|
+
};
|
|
129
|
+
export const PROMPT_LAYERS = [
|
|
130
|
+
"master_human",
|
|
131
|
+
"planning",
|
|
132
|
+
"section_implement",
|
|
133
|
+
"repair",
|
|
134
|
+
"integration",
|
|
135
|
+
];
|
|
136
|
+
export function listTemplates() {
|
|
137
|
+
return PROMPT_LAYERS.map((layer) => TEMPLATES[layer]);
|
|
138
|
+
}
|
|
139
|
+
export function getTemplate(layer) {
|
|
140
|
+
const t = TEMPLATES[layer];
|
|
141
|
+
if (!t) {
|
|
142
|
+
throw new Error(`Unknown prompt layer: ${layer}`);
|
|
143
|
+
}
|
|
144
|
+
return t;
|
|
145
|
+
}
|
|
146
|
+
export function describeTemplate(layer) {
|
|
147
|
+
const t = getTemplate(layer);
|
|
148
|
+
return {
|
|
149
|
+
id: t.id,
|
|
150
|
+
description: t.description,
|
|
151
|
+
required_vars: t.required_vars,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Render a template using a context object. Missing variables are recorded in
|
|
156
|
+
* `missing_vars` and replaced with the empty string. Caller decides whether to
|
|
157
|
+
* treat missing required variables as an error.
|
|
158
|
+
*/
|
|
159
|
+
export function renderTemplate(layer, context = {}) {
|
|
160
|
+
const tpl = getTemplate(layer);
|
|
161
|
+
const missing = [];
|
|
162
|
+
const prompt = tpl.body.replace(/\{\{\s*([a-zA-Z0-9_]+)\s*\}\}/g, (_m, key) => {
|
|
163
|
+
const value = context[key];
|
|
164
|
+
if (value === undefined || value === null || value === "") {
|
|
165
|
+
if (tpl.required_vars.includes(key)) {
|
|
166
|
+
missing.push(key);
|
|
167
|
+
}
|
|
168
|
+
return "";
|
|
169
|
+
}
|
|
170
|
+
if (Array.isArray(value)) {
|
|
171
|
+
return value.map((v) => `- ${String(v)}`).join("\n");
|
|
172
|
+
}
|
|
173
|
+
return String(value);
|
|
174
|
+
});
|
|
175
|
+
return { prompt, missing_vars: [...new Set(missing)] };
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"templates.js","sourceRoot":"","sources":["../../src/prompt_manager/templates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAiBH,MAAM,SAAS,GAAwC;IACrD,YAAY,EAAE;QACZ,KAAK,EAAE,cAAc;QACrB,EAAE,EAAE,iBAAiB;QACrB,WAAW,EACT,2JAA2J;QAC7J,aAAa,EAAE,CAAC,WAAW,EAAE,cAAc,CAAC;QAC5C,IAAI,EAAE;YACJ,0CAA0C;YAC1C,EAAE;YACF,YAAY;YACZ,iBAAiB;YACjB,EAAE;YACF,0BAA0B;YAC1B,4JAA4J;YAC5J,sEAAsE;YACtE,mHAAmH;YACnH,EAAE;YACF,cAAc;YACd,kGAAkG;YAClG,qJAAqJ;YACrJ,oFAAoF;SACrF,CAAC,IAAI,CAAC,IAAI,CAAC;KACb;IACD,QAAQ,EAAE;QACR,KAAK,EAAE,UAAU;QACjB,EAAE,EAAE,aAAa;QACjB,WAAW,EACT,qIAAqI;QACvI,aAAa,EAAE,CAAC,kBAAkB,EAAE,eAAe,CAAC;QACpD,IAAI,EAAE;YACJ,yEAAyE;YACzE,+DAA+D;YAC/D,uDAAuD;YACvD,0EAA0E;YAC1E,mDAAmD;YACnD,0EAA0E;YAC1E,EAAE;YACF,mKAAmK;YACnK,EAAE;YACF,8CAA8C;YAC9C,SAAS;YACT,GAAG;YACH,yDAAyD;YACzD,mBAAmB;YACnB,gFAAgF;YAChF,MAAM;YACN,qEAAqE;YACrE,GAAG;YACH,KAAK;SACN,CAAC,IAAI,CAAC,IAAI,CAAC;KACb;IACD,iBAAiB,EAAE;QACjB,KAAK,EAAE,mBAAmB;QAC1B,EAAE,EAAE,sBAAsB;QAC1B,WAAW,EACT,4HAA4H;QAC9H,aAAa,EAAE,CAAC,YAAY,EAAE,cAAc,EAAE,qBAAqB,CAAC;QACpE,IAAI,EAAE;YACJ,0DAA0D;YAC1D,EAAE;YACF,sBAAsB;YACtB,yBAAyB;YACzB,EAAE;YACF,4CAA4C;YAC5C,EAAE;YACF,WAAW;YACX,4FAA4F;YAC5F,6DAA6D;YAC7D,gIAAgI;YAChI,6IAA6I;YAC7I,EAAE;YACF,iHAAiH;SAClH,CAAC,IAAI,CAAC,IAAI,CAAC;KACb;IACD,MAAM,EAAE;QACN,KAAK,EAAE,QAAQ;QACf,EAAE,EAAE,WAAW;QACf,WAAW,EACT,sHAAsH;QACxH,aAAa,EAAE,CAAC,YAAY,EAAE,iBAAiB,EAAE,SAAS,EAAE,cAAc,CAAC;QAC3E,IAAI,EAAE;YACJ,6EAA6E;YAC7E,EAAE;YACF,2CAA2C;YAC3C,qBAAqB;YACrB,EAAE;YACF,QAAQ;YACR,oDAAoD;YACpD,oHAAoH;YACpH,oEAAoE;YACpE,6FAA6F;YAC7F,EAAE;YACF,sIAAsI;SACvI,CAAC,IAAI,CAAC,IAAI,CAAC;KACb;IACD,WAAW,EAAE;QACX,KAAK,EAAE,aAAa;QACpB,EAAE,EAAE,gBAAgB;QACpB,WAAW,EACT,0GAA0G;QAC5G,aAAa,EAAE,CAAC,mBAAmB,EAAE,kBAAkB,CAAC;QACxD,IAAI,EAAE;YACJ,+CAA+C;YAC/C,EAAE;YACF,0CAA0C;YAC1C,EAAE;YACF,WAAW;YACX,+DAA+D;YAC/D,0JAA0J;YAC1J,qKAAqK;YACrK,6DAA6D;YAC7D,EAAE;YACF,uEAAuE;SACxE,CAAC,IAAI,CAAC,IAAI,CAAC;KACb;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,aAAa,GAA2B;IACnD,cAAc;IACd,UAAU;IACV,mBAAmB;IACnB,QAAQ;IACR,aAAa;CACd,CAAC;AAEF,MAAM,UAAU,aAAa;IAC3B,OAAO,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAkB;IAC5C,MAAM,CAAC,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;IAC3B,IAAI,CAAC,CAAC,EAAE,CAAC;QACP,MAAM,IAAI,KAAK,CAAC,yBAAyB,KAAK,EAAE,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,CAAC,CAAC;AACX,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAkB;IAKjD,MAAM,CAAC,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAC7B,OAAO;QACL,EAAE,EAAE,CAAC,CAAC,EAAE;QACR,WAAW,EAAE,CAAC,CAAC,WAAW;QAC1B,aAAa,EAAE,CAAC,CAAC,aAAa;KAC/B,CAAC;AACJ,CAAC;AAOD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAC5B,KAAkB,EAClB,UAAmC,EAAE;IAErC,MAAM,GAAG,GAAG,WAAW,CAAC,KAAK,CAAC,CAAC;IAC/B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,MAAM,GAAG,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,gCAAgC,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE;QAC5E,MAAM,KAAK,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC;QAC3B,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,EAAE,EAAE,CAAC;YAC1D,IAAI,GAAG,CAAC,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;gBACpC,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;YACpB,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;YACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvD,CAAC;QACD,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IACH,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;AACzD,CAAC"}
|
package/dist/runners/base.d.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface RunnerResult {
|
|
|
12
12
|
}
|
|
13
13
|
export declare function makeSkippedResult(runnerName: string, reason: string): RunnerResult;
|
|
14
14
|
export declare function checkToolAvailable(command: string): Promise<boolean>;
|
|
15
|
-
export declare function runCommand(cmd: string, args: string[], cwd: string, logPath?: string): Promise<{
|
|
15
|
+
export declare function runCommand(cmd: string, args: string[], cwd: string, logPath?: string, extraEnv?: Record<string, string>): Promise<{
|
|
16
16
|
exit_code: number;
|
|
17
17
|
stdout: string;
|
|
18
18
|
stderr: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/runners/base.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,YAAY,CAad;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ1E;AAED,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"base.d.ts","sourceRoot":"","sources":["../../src/runners/base.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,YAAY;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,OAAO,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,iBAAiB,CAC/B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,GACb,YAAY,CAad;AAED,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAQ1E;AAED,wBAAsB,UAAU,CAC9B,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,EAAE,MAAM,EACX,OAAO,CAAC,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,OAAO,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC,CAqDrF"}
|
package/dist/runners/base.js
CHANGED
|
@@ -25,7 +25,7 @@ export async function checkToolAvailable(command) {
|
|
|
25
25
|
return false;
|
|
26
26
|
}
|
|
27
27
|
}
|
|
28
|
-
export async function runCommand(cmd, args, cwd, logPath) {
|
|
28
|
+
export async function runCommand(cmd, args, cwd, logPath, extraEnv) {
|
|
29
29
|
const start = Date.now();
|
|
30
30
|
return new Promise((resolve) => {
|
|
31
31
|
const quotedArgs = args.map(a => (a.includes(" ") ? `"${a}"` : a));
|
|
@@ -33,7 +33,7 @@ export async function runCommand(cmd, args, cwd, logPath) {
|
|
|
33
33
|
cwd,
|
|
34
34
|
shell: true,
|
|
35
35
|
stdio: ["pipe", "pipe", "pipe"],
|
|
36
|
-
env: { ...process.env },
|
|
36
|
+
env: { ...process.env, ...(extraEnv ?? {}) },
|
|
37
37
|
});
|
|
38
38
|
const stdoutChunks = [];
|
|
39
39
|
const stderrChunks = [];
|
package/dist/runners/base.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/runners/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAe/B,MAAM,UAAU,iBAAiB,CAC/B,UAAkB,EAClB,MAAc;IAEd,OAAO;QACL,WAAW,EAAE,UAAU;QACvB,SAAS,EAAE,KAAK;QAChB,SAAS,EAAE,CAAC,CAAC;QACb,MAAM,EAAE,CAAC;QACT,MAAM,EAAE,CAAC;QACT,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE,EAAE;QACZ,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,MAAM;KACf,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAAe;IACtD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAClE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACpE,OAAO,MAAM,CAAC,SAAS,KAAK,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAW,EACX,IAAc,EACd,GAAW,EACX,OAAgB;
|
|
1
|
+
{"version":3,"file":"base.js","sourceRoot":"","sources":["../../src/runners/base.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAe/B,MAAM,UAAU,iBAAiB,CAC/B,UAAkB,EAClB,MAAc;IAEd,OAAO;QACL,WAAW,EAAE,UAAU;QACvB,SAAS,EAAE,KAAK;QAChB,SAAS,EAAE,CAAC,CAAC;QACb,MAAM,EAAE,CAAC;QACT,MAAM,EAAE,CAAC;QACT,OAAO,EAAE,CAAC;QACV,QAAQ,EAAE,EAAE;QACZ,WAAW,EAAE,CAAC;QACd,MAAM,EAAE,EAAE;QACV,MAAM,EAAE,MAAM;KACf,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CAAC,OAAe;IACtD,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,OAAO,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;QAClE,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;QACpE,OAAO,MAAM,CAAC,SAAS,KAAK,CAAC,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,GAAW,EACX,IAAc,EACd,GAAW,EACX,OAAgB,EAChB,QAAiC;IAEjC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEzB,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACnE,MAAM,KAAK,GAAG,KAAK,CAAC,GAAG,EAAE,UAAU,EAAE;YACnC,GAAG;YACH,KAAK,EAAE,IAAI;YACX,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;YAC/B,GAAG,EAAE,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE;SAC7C,CAAC,CAAC;QAEH,MAAM,YAAY,GAAa,EAAE,CAAC;QAClC,MAAM,YAAY,GAAa,EAAE,CAAC;QAElC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QACrE,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC,MAAM,EAAE,CAAC,KAAa,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAErE,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YACzB,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YACvC,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAC7D,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;YAE7D,IAAI,OAAO,EAAE,CAAC;gBACZ,SAAS,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;gBACjD,aAAa,CACX,OAAO,EACP,KAAK,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI;oBAC5B,cAAc,IAAI,IAAI,CAAC,IAAI;oBAC3B,gBAAgB,WAAW,MAAM;oBACjC,mBAAmB,MAAM,IAAI;oBAC7B,mBAAmB,MAAM,IAAI,CAChC,CAAC;YACJ,CAAC;YAED,OAAO,CAAC;gBACN,SAAS,EAAE,IAAI,IAAI,CAAC;gBACpB,MAAM;gBACN,MAAM;gBACN,WAAW;aACZ,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,KAAK,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YACrB,MAAM,WAAW,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;YACvC,OAAO,CAAC;gBACN,SAAS,EAAE,GAAG;gBACd,MAAM,EAAE,EAAE;gBACV,MAAM,EAAE,oBAAoB,GAAG,EAAE;gBACjC,WAAW;aACZ,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma Spec Generator — pulls design tokens (colors, typography, spacing)
|
|
3
|
+
* from a Figma file's styles and writes them to `docs/specs/` and
|
|
4
|
+
* `docs/ui_rules.md`. Extends the existing Figma fetcher with token-level
|
|
5
|
+
* extraction via the Figma REST API v1 `files/:key` endpoint.
|
|
6
|
+
*/
|
|
7
|
+
export interface ColorToken {
|
|
8
|
+
name: string;
|
|
9
|
+
hex: string;
|
|
10
|
+
opacity: number;
|
|
11
|
+
r: number;
|
|
12
|
+
g: number;
|
|
13
|
+
b: number;
|
|
14
|
+
}
|
|
15
|
+
export interface TypographyToken {
|
|
16
|
+
name: string;
|
|
17
|
+
family: string;
|
|
18
|
+
weight: number;
|
|
19
|
+
size: number;
|
|
20
|
+
line_height?: number;
|
|
21
|
+
letter_spacing?: number;
|
|
22
|
+
}
|
|
23
|
+
export interface SpacingToken {
|
|
24
|
+
name: string;
|
|
25
|
+
value: number;
|
|
26
|
+
}
|
|
27
|
+
export interface DesignTokens {
|
|
28
|
+
colors: ColorToken[];
|
|
29
|
+
typography: TypographyToken[];
|
|
30
|
+
spacing: SpacingToken[];
|
|
31
|
+
extracted_at: string;
|
|
32
|
+
figma_file_key: string;
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Extract design tokens from a Figma file using the REST API.
|
|
36
|
+
*/
|
|
37
|
+
export declare function extractTokensFromFigma(fileKey: string, token: string): Promise<{
|
|
38
|
+
tokens: DesignTokens;
|
|
39
|
+
errors: string[];
|
|
40
|
+
}>;
|
|
41
|
+
export declare function writeTokenFiles(cwd: string, tokens: DesignTokens): {
|
|
42
|
+
specPath: string;
|
|
43
|
+
rulesPath: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Full pipeline: load Figma config -> extract tokens -> write docs.
|
|
47
|
+
*/
|
|
48
|
+
export declare function generateSpec(cwd: string): Promise<{
|
|
49
|
+
tokens: DesignTokens;
|
|
50
|
+
errors: string[];
|
|
51
|
+
specPath?: string;
|
|
52
|
+
rulesPath?: string;
|
|
53
|
+
}>;
|
|
54
|
+
//# sourceMappingURL=figma_spec_generator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"figma_spec_generator.d.ts","sourceRoot":"","sources":["../../src/runners/figma_spec_generator.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAUH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,MAAM,CAAC;IAChB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,UAAU,EAAE,CAAC;IACrB,UAAU,EAAE,eAAe,EAAE,CAAC;IAC9B,OAAO,EAAE,YAAY,EAAE,CAAC;IACxB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB;AAUD;;GAEG;AACH,wBAAsB,sBAAsB,CAC1C,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAA;CAAE,CAAC,CAkFrD;AA4ED,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,YAAY,GACnB;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,CAWzC;AA8CD;;GAEG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,GACV,OAAO,CAAC;IAAE,MAAM,EAAE,YAAY,CAAC;IAAC,MAAM,EAAE,MAAM,EAAE,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC,CA4B5F"}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Figma Spec Generator — pulls design tokens (colors, typography, spacing)
|
|
3
|
+
* from a Figma file's styles and writes them to `docs/specs/` and
|
|
4
|
+
* `docs/ui_rules.md`. Extends the existing Figma fetcher with token-level
|
|
5
|
+
* extraction via the Figma REST API v1 `files/:key` endpoint.
|
|
6
|
+
*/
|
|
7
|
+
import { mkdirSync, writeFileSync } from "fs";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { loadFigmaConfig, resolveFigmaToken, parseFigmaFileKey, } from "./figma_fetcher.js";
|
|
10
|
+
function rgbaToHex(r, g, b) {
|
|
11
|
+
const toHex = (v) => Math.round(v * 255)
|
|
12
|
+
.toString(16)
|
|
13
|
+
.padStart(2, "0");
|
|
14
|
+
return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Extract design tokens from a Figma file using the REST API.
|
|
18
|
+
*/
|
|
19
|
+
export async function extractTokensFromFigma(fileKey, token) {
|
|
20
|
+
const errors = [];
|
|
21
|
+
const colors = [];
|
|
22
|
+
const typography = [];
|
|
23
|
+
const spacing = [];
|
|
24
|
+
let fileData;
|
|
25
|
+
try {
|
|
26
|
+
const resp = await fetch(`https://api.figma.com/v1/files/${fileKey}/styles`, {
|
|
27
|
+
headers: { "X-Figma-Token": token },
|
|
28
|
+
});
|
|
29
|
+
if (!resp.ok) {
|
|
30
|
+
errors.push(`Figma styles API returned ${resp.status}`);
|
|
31
|
+
return {
|
|
32
|
+
tokens: { colors, typography, spacing, extracted_at: new Date().toISOString(), figma_file_key: fileKey },
|
|
33
|
+
errors,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
fileData = (await resp.json());
|
|
37
|
+
}
|
|
38
|
+
catch (err) {
|
|
39
|
+
errors.push(`Network error: ${err.message}`);
|
|
40
|
+
return {
|
|
41
|
+
tokens: { colors, typography, spacing, extracted_at: new Date().toISOString(), figma_file_key: fileKey },
|
|
42
|
+
errors,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const meta = fileData.meta;
|
|
46
|
+
const styles = meta?.styles ?? [];
|
|
47
|
+
const colorStyleKeys = [];
|
|
48
|
+
const textStyleKeys = [];
|
|
49
|
+
for (const style of styles) {
|
|
50
|
+
if (style.style_type === "FILL") {
|
|
51
|
+
colorStyleKeys.push(style.key);
|
|
52
|
+
colors.push({
|
|
53
|
+
name: style.name,
|
|
54
|
+
hex: "#000000",
|
|
55
|
+
opacity: 1,
|
|
56
|
+
r: 0,
|
|
57
|
+
g: 0,
|
|
58
|
+
b: 0,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
else if (style.style_type === "TEXT") {
|
|
62
|
+
textStyleKeys.push(style.key);
|
|
63
|
+
typography.push({
|
|
64
|
+
name: style.name,
|
|
65
|
+
family: "Unknown",
|
|
66
|
+
weight: 400,
|
|
67
|
+
size: 16,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
try {
|
|
72
|
+
// depth=4 keeps the document payload bounded while still surfacing
|
|
73
|
+
// tokens that live inside frames/components/component-sets a few levels
|
|
74
|
+
// deep. We then traverse the returned subtree recursively up to a hard
|
|
75
|
+
// depth cap to avoid runaway traversal on huge files.
|
|
76
|
+
const fileResp = await fetch(`https://api.figma.com/v1/files/${fileKey}?depth=4`, {
|
|
77
|
+
headers: { "X-Figma-Token": token },
|
|
78
|
+
});
|
|
79
|
+
if (fileResp.ok) {
|
|
80
|
+
const file = (await fileResp.json());
|
|
81
|
+
const doc = file.document;
|
|
82
|
+
if (doc?.children) {
|
|
83
|
+
extractTokensFromNodes(doc.children, colors, typography, spacing);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
catch { /* swallow — we already have style names */ }
|
|
88
|
+
return {
|
|
89
|
+
tokens: {
|
|
90
|
+
colors,
|
|
91
|
+
typography,
|
|
92
|
+
spacing,
|
|
93
|
+
extracted_at: new Date().toISOString(),
|
|
94
|
+
figma_file_key: fileKey,
|
|
95
|
+
},
|
|
96
|
+
errors,
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Hard cap on tree traversal. depth=4 fetches roughly the visual hierarchy
|
|
101
|
+
* we care about (page → frame → component → variant → leaf), and the cap
|
|
102
|
+
* here protects us if Figma ever returns deeper inline trees (e.g. nested
|
|
103
|
+
* component sets). Anything deeper than 8 is almost always implementation
|
|
104
|
+
* detail unrelated to design tokens.
|
|
105
|
+
*/
|
|
106
|
+
const MAX_TRAVERSAL_DEPTH = 8;
|
|
107
|
+
function extractTokensFromNodes(nodes, colors, typography, spacing, depth = 0) {
|
|
108
|
+
if (depth > MAX_TRAVERSAL_DEPTH)
|
|
109
|
+
return;
|
|
110
|
+
for (const node of nodes) {
|
|
111
|
+
if (node.fills && Array.isArray(node.fills)) {
|
|
112
|
+
for (const fill of node.fills) {
|
|
113
|
+
if (fill.type === "SOLID" && fill.color) {
|
|
114
|
+
const existing = colors.find((c) => c.name === node.name);
|
|
115
|
+
if (existing) {
|
|
116
|
+
existing.r = fill.color.r;
|
|
117
|
+
existing.g = fill.color.g;
|
|
118
|
+
existing.b = fill.color.b;
|
|
119
|
+
existing.opacity = fill.color.a ?? 1;
|
|
120
|
+
existing.hex = rgbaToHex(fill.color.r, fill.color.g, fill.color.b);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
if (node.style && typeof node.style === "object") {
|
|
126
|
+
const s = node.style;
|
|
127
|
+
if (s.fontFamily) {
|
|
128
|
+
const existing = typography.find((t) => t.name === node.name);
|
|
129
|
+
if (existing) {
|
|
130
|
+
existing.family = String(s.fontFamily);
|
|
131
|
+
existing.weight = Number(s.fontWeight) || 400;
|
|
132
|
+
existing.size = Number(s.fontSize) || 16;
|
|
133
|
+
if (s.lineHeightPx)
|
|
134
|
+
existing.line_height = Number(s.lineHeightPx);
|
|
135
|
+
if (s.letterSpacing)
|
|
136
|
+
existing.letter_spacing = Number(s.letterSpacing);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (node.itemSpacing && node.name) {
|
|
141
|
+
spacing.push({ name: `${node.name}/gap`, value: node.itemSpacing });
|
|
142
|
+
}
|
|
143
|
+
if (node.paddingLeft && node.name) {
|
|
144
|
+
spacing.push({ name: `${node.name}/padding`, value: node.paddingLeft });
|
|
145
|
+
}
|
|
146
|
+
if (node.children) {
|
|
147
|
+
extractTokensFromNodes(node.children, colors, typography, spacing, depth + 1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export function writeTokenFiles(cwd, tokens) {
|
|
152
|
+
const specsDir = join(cwd, "docs", "specs");
|
|
153
|
+
mkdirSync(specsDir, { recursive: true });
|
|
154
|
+
const specPath = join(specsDir, "design_tokens.json");
|
|
155
|
+
writeFileSync(specPath, JSON.stringify(tokens, null, 2));
|
|
156
|
+
const rulesPath = join(cwd, "docs", "ui_rules.md");
|
|
157
|
+
const md = generateUiRulesMarkdown(tokens);
|
|
158
|
+
writeFileSync(rulesPath, md);
|
|
159
|
+
return { specPath, rulesPath };
|
|
160
|
+
}
|
|
161
|
+
function generateUiRulesMarkdown(tokens) {
|
|
162
|
+
const lines = [
|
|
163
|
+
"# UI Design Rules",
|
|
164
|
+
"",
|
|
165
|
+
`> Auto-generated from Figma (file: \`${tokens.figma_file_key}\`) on ${tokens.extracted_at}`,
|
|
166
|
+
"",
|
|
167
|
+
];
|
|
168
|
+
if (tokens.colors.length > 0) {
|
|
169
|
+
lines.push("## Colors", "");
|
|
170
|
+
lines.push("| Name | Hex | Opacity |");
|
|
171
|
+
lines.push("|------|-----|---------|");
|
|
172
|
+
for (const c of tokens.colors) {
|
|
173
|
+
lines.push(`| ${c.name} | \`${c.hex}\` | ${c.opacity} |`);
|
|
174
|
+
}
|
|
175
|
+
lines.push("");
|
|
176
|
+
}
|
|
177
|
+
if (tokens.typography.length > 0) {
|
|
178
|
+
lines.push("## Typography", "");
|
|
179
|
+
lines.push("| Name | Family | Weight | Size | Line Height |");
|
|
180
|
+
lines.push("|------|--------|--------|------|-------------|");
|
|
181
|
+
for (const t of tokens.typography) {
|
|
182
|
+
lines.push(`| ${t.name} | ${t.family} | ${t.weight} | ${t.size}px | ${t.line_height ? `${t.line_height}px` : "auto"} |`);
|
|
183
|
+
}
|
|
184
|
+
lines.push("");
|
|
185
|
+
}
|
|
186
|
+
if (tokens.spacing.length > 0) {
|
|
187
|
+
lines.push("## Spacing", "");
|
|
188
|
+
lines.push("| Name | Value |");
|
|
189
|
+
lines.push("|------|-------|");
|
|
190
|
+
for (const s of tokens.spacing) {
|
|
191
|
+
lines.push(`| ${s.name} | ${s.value}px |`);
|
|
192
|
+
}
|
|
193
|
+
lines.push("");
|
|
194
|
+
}
|
|
195
|
+
lines.push("---", "", "These rules are enforced by CodeLoop's `codeloop_design_compare` tool during verification.");
|
|
196
|
+
return lines.join("\n");
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Full pipeline: load Figma config -> extract tokens -> write docs.
|
|
200
|
+
*/
|
|
201
|
+
export async function generateSpec(cwd) {
|
|
202
|
+
const config = loadFigmaConfig(cwd);
|
|
203
|
+
if (!config) {
|
|
204
|
+
return {
|
|
205
|
+
tokens: { colors: [], typography: [], spacing: [], extracted_at: new Date().toISOString(), figma_file_key: "" },
|
|
206
|
+
errors: ["No .codeloop/figma.json found."],
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
const token = resolveFigmaToken(config);
|
|
210
|
+
if (!token) {
|
|
211
|
+
return {
|
|
212
|
+
tokens: { colors: [], typography: [], spacing: [], extracted_at: new Date().toISOString(), figma_file_key: "" },
|
|
213
|
+
errors: ["No Figma API token configured."],
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
const fileKey = config.file_key || (config.file_url ? parseFigmaFileKey(config.file_url) : null);
|
|
217
|
+
if (!fileKey) {
|
|
218
|
+
return {
|
|
219
|
+
tokens: { colors: [], typography: [], spacing: [], extracted_at: new Date().toISOString(), figma_file_key: "" },
|
|
220
|
+
errors: ["No Figma file key found."],
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
const { tokens, errors } = await extractTokensFromFigma(fileKey, token);
|
|
224
|
+
const { specPath, rulesPath } = writeTokenFiles(cwd, tokens);
|
|
225
|
+
return { tokens, errors, specPath, rulesPath };
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=figma_spec_generator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"figma_spec_generator.js","sourceRoot":"","sources":["../../src/runners/figma_spec_generator.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
1
|
import type { RunnerResult } from "./base.js";
|
|
2
|
-
export
|
|
2
|
+
export interface PlaywrightEnvOverrides {
|
|
3
|
+
viewportWidth?: number;
|
|
4
|
+
viewportHeight?: number;
|
|
5
|
+
deviceScaleFactor?: number;
|
|
6
|
+
isMobile?: boolean;
|
|
7
|
+
locale?: string;
|
|
8
|
+
timezone?: string;
|
|
9
|
+
}
|
|
10
|
+
export declare function runPlaywrightTests(cwd: string, logPath: string, jsonReportPath?: string, envOverrides?: PlaywrightEnvOverrides): Promise<RunnerResult>;
|
|
3
11
|
//# sourceMappingURL=playwright.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../../src/runners/playwright.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAI9C,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"playwright.d.ts","sourceRoot":"","sources":["../../src/runners/playwright.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAI9C,MAAM,WAAW,sBAAsB;IACrC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,cAAc,CAAC,EAAE,MAAM,EACvB,YAAY,CAAC,EAAE,sBAAsB,GACpC,OAAO,CAAC,YAAY,CAAC,CAmDvB"}
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { runCommand, checkToolAvailable, makeSkippedResult } from "./base.js";
|
|
2
2
|
import { existsSync, readFileSync } from "fs";
|
|
3
3
|
import { join } from "path";
|
|
4
|
-
export async function runPlaywrightTests(cwd, logPath, jsonReportPath) {
|
|
5
|
-
// Check for playwright config
|
|
4
|
+
export async function runPlaywrightTests(cwd, logPath, jsonReportPath, envOverrides) {
|
|
6
5
|
const hasConfig = existsSync(join(cwd, "playwright.config.ts")) ||
|
|
7
6
|
existsSync(join(cwd, "playwright.config.js"));
|
|
8
7
|
if (!hasConfig) {
|
|
@@ -12,7 +11,23 @@ export async function runPlaywrightTests(cwd, logPath, jsonReportPath) {
|
|
|
12
11
|
return makeSkippedResult("playwright", "npx not found on PATH");
|
|
13
12
|
}
|
|
14
13
|
const reportPath = jsonReportPath || join(cwd, "playwright-report.json");
|
|
15
|
-
|
|
14
|
+
// CodeLoop normalisation env vars; users can read these in their
|
|
15
|
+
// playwright.config.ts via `process.env.CODELOOP_VIEWPORT_WIDTH` etc.
|
|
16
|
+
// to lock viewport, locale, timezone for reproducibility.
|
|
17
|
+
const extraEnv = {};
|
|
18
|
+
if (envOverrides?.viewportWidth)
|
|
19
|
+
extraEnv.CODELOOP_VIEWPORT_WIDTH = String(envOverrides.viewportWidth);
|
|
20
|
+
if (envOverrides?.viewportHeight)
|
|
21
|
+
extraEnv.CODELOOP_VIEWPORT_HEIGHT = String(envOverrides.viewportHeight);
|
|
22
|
+
if (envOverrides?.deviceScaleFactor)
|
|
23
|
+
extraEnv.CODELOOP_DEVICE_SCALE_FACTOR = String(envOverrides.deviceScaleFactor);
|
|
24
|
+
if (envOverrides?.isMobile !== undefined)
|
|
25
|
+
extraEnv.CODELOOP_IS_MOBILE = envOverrides.isMobile ? "1" : "0";
|
|
26
|
+
if (envOverrides?.locale)
|
|
27
|
+
extraEnv.CODELOOP_LOCALE = envOverrides.locale;
|
|
28
|
+
if (envOverrides?.timezone)
|
|
29
|
+
extraEnv.CODELOOP_TIMEZONE = envOverrides.timezone;
|
|
30
|
+
const result = await runCommand("npx", ["playwright", "test", "--reporter=json"], cwd, logPath, Object.keys(extraEnv).length > 0 ? extraEnv : undefined);
|
|
16
31
|
const { passed, failed, skipped } = parsePlaywrightOutput(result.stdout, reportPath);
|
|
17
32
|
return {
|
|
18
33
|
runner_name: "playwright",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright.js","sourceRoot":"","sources":["../../src/runners/playwright.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"playwright.js","sourceRoot":"","sources":["../../src/runners/playwright.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin SDK — defines the runner plugin interface and provides a registry
|
|
3
|
+
* for custom runners. Third-party frameworks (Django, Rails, etc.) implement
|
|
4
|
+
* the `CodeLoopRunner` interface and register via `.codeloop/plugins.json`.
|
|
5
|
+
*/
|
|
6
|
+
import type { RunnerResult } from "./base.js";
|
|
7
|
+
export interface CodeLoopRunner {
|
|
8
|
+
name: string;
|
|
9
|
+
detect: (cwd: string) => boolean;
|
|
10
|
+
run: (cwd: string, logPath: string) => Promise<RunnerResult>;
|
|
11
|
+
}
|
|
12
|
+
export interface PluginConfig {
|
|
13
|
+
name: string;
|
|
14
|
+
command: string;
|
|
15
|
+
args?: string[];
|
|
16
|
+
detect_file?: string;
|
|
17
|
+
parse_output?: "jest" | "pytest" | "rspec" | "generic" | "exit_code";
|
|
18
|
+
}
|
|
19
|
+
export interface PluginsManifest {
|
|
20
|
+
plugins: PluginConfig[];
|
|
21
|
+
}
|
|
22
|
+
export declare function loadPluginsManifest(cwd: string): PluginsManifest;
|
|
23
|
+
export declare function createRunnerFromPlugin(plugin: PluginConfig): CodeLoopRunner;
|
|
24
|
+
export declare function loadCustomRunners(cwd: string): CodeLoopRunner[];
|
|
25
|
+
//# sourceMappingURL=plugin_sdk.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin_sdk.d.ts","sourceRoot":"","sources":["../../src/runners/plugin_sdk.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AAG9C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACjC,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CAC9D;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,SAAS,GAAG,WAAW,CAAC;CACtE;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,YAAY,EAAE,CAAC;CACzB;AAID,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,CAShE;AA0BD,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,YAAY,GAAG,cAAc,CAmC3E;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,cAAc,EAAE,CAK/D"}
|