canicode 0.11.4 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +13 -0
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +50 -16
- package/dist/cli/index.js +391 -96
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +21 -3
- package/dist/index.js +89 -22
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +102 -29
- package/dist/mcp/server.js.map +1 -1
- package/docs/CUSTOMIZATION.md +51 -9
- package/package.json +1 -1
- package/skills/canicode-roundtrip/SKILL.md +104 -4
- package/skills/canicode-roundtrip/helpers-bootstrap.js +1 -1
- package/skills/canicode-roundtrip/helpers-installer.js +1 -1
- package/skills/cursor/canicode-roundtrip/SKILL.md +104 -4
- package/skills/cursor/canicode-roundtrip/helpers-bootstrap.js +1 -1
- package/skills/cursor/canicode-roundtrip/helpers-installer.js +1 -1
package/dist/mcp/server.js
CHANGED
|
@@ -302,7 +302,14 @@ var SeveritySchema = z.enum([
|
|
|
302
302
|
"blocking",
|
|
303
303
|
"risk",
|
|
304
304
|
"missing-info",
|
|
305
|
-
"suggestion"
|
|
305
|
+
"suggestion",
|
|
306
|
+
/**
|
|
307
|
+
* `note` is the zero-impact tier (#519): findings render in the report but
|
|
308
|
+
* never move the grade. Used for annotation-primary rules whose value is the
|
|
309
|
+
* nudge, not the score (e.g. unmapped Code Connect components, info-collection
|
|
310
|
+
* rules whose answers belong in figma-implement-design context, not in linting).
|
|
311
|
+
*/
|
|
312
|
+
"note"
|
|
306
313
|
]);
|
|
307
314
|
|
|
308
315
|
// src/core/contracts/rule.ts
|
|
@@ -348,6 +355,7 @@ var RULE_ID_CATEGORY = {
|
|
|
348
355
|
"detached-instance": "code-quality",
|
|
349
356
|
"variant-structure-mismatch": "code-quality",
|
|
350
357
|
"deep-nesting": "code-quality",
|
|
358
|
+
"unmapped-component": "code-quality",
|
|
351
359
|
// Token Management
|
|
352
360
|
"raw-value": "token-management",
|
|
353
361
|
"irregular-spacing": "token-management",
|
|
@@ -378,6 +386,12 @@ var RULE_PURPOSE = {
|
|
|
378
386
|
"detached-instance": "violation",
|
|
379
387
|
"variant-structure-mismatch": "violation",
|
|
380
388
|
"deep-nesting": "violation",
|
|
389
|
+
// #520: unmapped-component is annotation-primary. Fires only when the
|
|
390
|
+
// user has Code Connect set up at all (figma.config.json present in cwd).
|
|
391
|
+
// The gotcha drives the user to /canicode-roundtrip for actual mapping
|
|
392
|
+
// registration via the Figma MCP tools — analyze itself does not parse
|
|
393
|
+
// mapping declarations (deferred to v1.5).
|
|
394
|
+
"unmapped-component": "info-collection",
|
|
381
395
|
// Token Management
|
|
382
396
|
"raw-value": "violation",
|
|
383
397
|
"irregular-spacing": "violation",
|
|
@@ -421,12 +435,12 @@ var RULE_CONFIGS = {
|
|
|
421
435
|
enabled: true
|
|
422
436
|
},
|
|
423
437
|
"missing-size-constraint": {
|
|
424
|
-
// #403:
|
|
425
|
-
//
|
|
426
|
-
//
|
|
427
|
-
//
|
|
428
|
-
severity: "
|
|
429
|
-
score:
|
|
438
|
+
// #403 → #519: info-collection rule. Score is 0 (severity `note`):
|
|
439
|
+
// its value is the gotcha annotation, not the grade impact. Survey-
|
|
440
|
+
// generator includes this rule via the `purpose === "info-collection"`
|
|
441
|
+
// branch so the gotcha keeps surfacing.
|
|
442
|
+
severity: "note",
|
|
443
|
+
score: 0,
|
|
430
444
|
enabled: true
|
|
431
445
|
},
|
|
432
446
|
// ── Code Quality ──
|
|
@@ -458,6 +472,16 @@ var RULE_CONFIGS = {
|
|
|
458
472
|
maxDepth: 5
|
|
459
473
|
}
|
|
460
474
|
},
|
|
475
|
+
"unmapped-component": {
|
|
476
|
+
// #520 / #519: zero-impact tier. Fires per main component when Code
|
|
477
|
+
// Connect is set up in the consuming repo (figma.config.json at cwd).
|
|
478
|
+
// Score is 0 because the rule's value is the gotcha + roundtrip handoff,
|
|
479
|
+
// not the grade signal — designers who deliberately do not map (e.g.
|
|
480
|
+
// marketing-only banners) are not punished.
|
|
481
|
+
severity: "note",
|
|
482
|
+
score: 0,
|
|
483
|
+
enabled: true
|
|
484
|
+
},
|
|
461
485
|
// ── Token Management ──
|
|
462
486
|
"raw-value": {
|
|
463
487
|
severity: "missing-info",
|
|
@@ -479,15 +503,15 @@ var RULE_CONFIGS = {
|
|
|
479
503
|
// is minimal. Score stays at -1 so re-enabling `missing-prototype` on
|
|
480
504
|
// fixtures that lack `interactionDestinations` (#139) cannot swing grades.
|
|
481
505
|
"missing-interaction-state": {
|
|
482
|
-
severity: "
|
|
483
|
-
|
|
484
|
-
|
|
506
|
+
severity: "note",
|
|
507
|
+
// #519: info-collection rule, zero-score tier
|
|
508
|
+
score: 0,
|
|
485
509
|
enabled: true
|
|
486
510
|
},
|
|
487
511
|
"missing-prototype": {
|
|
488
|
-
severity: "
|
|
489
|
-
|
|
490
|
-
|
|
512
|
+
severity: "note",
|
|
513
|
+
// #519: info-collection — annotation is primary output, no grade impact
|
|
514
|
+
score: 0,
|
|
491
515
|
enabled: true
|
|
492
516
|
},
|
|
493
517
|
// ── Semantic ──
|
|
@@ -1805,6 +1829,7 @@ var STRATEGY_BY_RULE = {
|
|
|
1805
1829
|
// Strategy C — annotation only
|
|
1806
1830
|
"absolute-position-in-auto-layout": "annotation",
|
|
1807
1831
|
"variant-structure-mismatch": "annotation",
|
|
1832
|
+
"unmapped-component": "annotation",
|
|
1808
1833
|
// Strategy D — auto-fix lower-severity issues from analyze output
|
|
1809
1834
|
"non-standard-naming": "auto-fix",
|
|
1810
1835
|
"inconsistent-naming-convention": "auto-fix",
|
|
@@ -1839,6 +1864,7 @@ function resolveTargetProperty(ruleId, subType) {
|
|
|
1839
1864
|
case "raw-value":
|
|
1840
1865
|
case "missing-interaction-state":
|
|
1841
1866
|
case "missing-prototype":
|
|
1867
|
+
case "unmapped-component":
|
|
1842
1868
|
return void 0;
|
|
1843
1869
|
}
|
|
1844
1870
|
}
|
|
@@ -1863,7 +1889,7 @@ function computeApplyContext(violation, instanceContext) {
|
|
|
1863
1889
|
}
|
|
1864
1890
|
|
|
1865
1891
|
// package.json
|
|
1866
|
-
var version = "0.
|
|
1892
|
+
var version = "0.12.0";
|
|
1867
1893
|
|
|
1868
1894
|
// src/core/engine/scoring.ts
|
|
1869
1895
|
var GRADE_ORDER = ["S", "A+", "A", "B+", "B", "C+", "C", "D", "F"];
|
|
@@ -1970,6 +1996,7 @@ function calculateScores(result, configs) {
|
|
|
1970
1996
|
risk: 0,
|
|
1971
1997
|
missingInfo: 0,
|
|
1972
1998
|
suggestion: 0,
|
|
1999
|
+
note: 0,
|
|
1973
2000
|
nodeCount,
|
|
1974
2001
|
acknowledgedCount: 0
|
|
1975
2002
|
};
|
|
@@ -1987,6 +2014,9 @@ function calculateScores(result, configs) {
|
|
|
1987
2014
|
case "suggestion":
|
|
1988
2015
|
summary.suggestion++;
|
|
1989
2016
|
break;
|
|
2017
|
+
case "note":
|
|
2018
|
+
summary.note++;
|
|
2019
|
+
break;
|
|
1990
2020
|
}
|
|
1991
2021
|
if (issue.acknowledged === true) summary.acknowledgedCount++;
|
|
1992
2022
|
}
|
|
@@ -2018,7 +2048,8 @@ function initializeCategoryScores() {
|
|
|
2018
2048
|
blocking: 0,
|
|
2019
2049
|
risk: 0,
|
|
2020
2050
|
"missing-info": 0,
|
|
2021
|
-
suggestion: 0
|
|
2051
|
+
suggestion: 0,
|
|
2052
|
+
note: 0
|
|
2022
2053
|
}
|
|
2023
2054
|
};
|
|
2024
2055
|
}
|
|
@@ -2039,6 +2070,7 @@ function formatScoreSummary(report) {
|
|
|
2039
2070
|
lines.push(` Risk: ${report.summary.risk}`);
|
|
2040
2071
|
lines.push(` Missing Info: ${report.summary.missingInfo}`);
|
|
2041
2072
|
lines.push(` Suggestion: ${report.summary.suggestion}`);
|
|
2073
|
+
lines.push(` Note: ${report.summary.note}`);
|
|
2042
2074
|
if (report.summary.acknowledgedCount > 0) {
|
|
2043
2075
|
const unaddressed = report.summary.totalIssues - report.summary.acknowledgedCount;
|
|
2044
2076
|
lines.push(
|
|
@@ -2165,6 +2197,12 @@ var GOTCHA_QUESTION_CONTENT = {
|
|
|
2165
2197
|
hint: "Describe which variant has the correct structure, or if they should all match",
|
|
2166
2198
|
example: "Default variant is canonical \u2014 other variants should toggle child visibility instead of adding/removing elements"
|
|
2167
2199
|
},
|
|
2200
|
+
"unmapped-component": {
|
|
2201
|
+
ruleId: "unmapped-component",
|
|
2202
|
+
question: '"{nodeName}" has no Code Connect mapping yet. Should we register one so figma-implement-design reuses your code?',
|
|
2203
|
+
hint: "Skip if this component is intentionally unmapped (e.g. marketing-only banner). Otherwise run /canicode-roundtrip on the component to walk through registration.",
|
|
2204
|
+
example: "Yes \u2014 map to src/components/Button.tsx so future screens reuse the existing implementation"
|
|
2205
|
+
},
|
|
2168
2206
|
"deep-nesting": {
|
|
2169
2207
|
ruleId: "deep-nesting",
|
|
2170
2208
|
question: '"{nodeName}" is deeply nested. Can some intermediate layers be flattened or extracted?',
|
|
@@ -2310,10 +2348,7 @@ function generateGotchaSurvey(result, scores, options = {}) {
|
|
|
2310
2348
|
const relevantIssues = result.issues.filter((issue) => {
|
|
2311
2349
|
const severity = issue.config.severity;
|
|
2312
2350
|
if (severity === "blocking" || severity === "risk") return true;
|
|
2313
|
-
|
|
2314
|
-
return getRulePurpose(issue.violation.ruleId) === "info-collection";
|
|
2315
|
-
}
|
|
2316
|
-
return false;
|
|
2351
|
+
return getRulePurpose(issue.violation.ruleId) === "info-collection";
|
|
2317
2352
|
});
|
|
2318
2353
|
const deduped = deduplicateSiblingIssues(relevantIssues);
|
|
2319
2354
|
const sorted = stableSortBySeverity(deduped);
|
|
@@ -2494,7 +2529,8 @@ function severityDot(sev) {
|
|
|
2494
2529
|
blocking: "sev-blocking",
|
|
2495
2530
|
risk: "sev-risk",
|
|
2496
2531
|
"missing-info": "sev-missing",
|
|
2497
|
-
suggestion: "sev-suggestion"
|
|
2532
|
+
suggestion: "sev-suggestion",
|
|
2533
|
+
note: "sev-note"
|
|
2498
2534
|
};
|
|
2499
2535
|
return map[sev];
|
|
2500
2536
|
}
|
|
@@ -2503,7 +2539,8 @@ function severityBadge(sev) {
|
|
|
2503
2539
|
blocking: "sev-blocking",
|
|
2504
2540
|
risk: "sev-risk",
|
|
2505
2541
|
"missing-info": "sev-missing",
|
|
2506
|
-
suggestion: "sev-suggestion"
|
|
2542
|
+
suggestion: "sev-suggestion",
|
|
2543
|
+
note: "sev-note"
|
|
2507
2544
|
};
|
|
2508
2545
|
return map[sev];
|
|
2509
2546
|
}
|
|
@@ -2561,6 +2598,7 @@ ${CATEGORIES.map((cat) => {
|
|
|
2561
2598
|
${renderSummaryDot("sev-risk", scores.summary.risk, "Risk")}
|
|
2562
2599
|
${renderSummaryDot("sev-missing", scores.summary.missingInfo, "Missing Info")}
|
|
2563
2600
|
${renderSummaryDot("sev-suggestion", scores.summary.suggestion, "Suggestion")}
|
|
2601
|
+
${renderSummaryDot("sev-note", scores.summary.note, "Note")}
|
|
2564
2602
|
<div class="rpt-summary-total">
|
|
2565
2603
|
<span class="rpt-summary-count">${scores.summary.totalIssues}</span>
|
|
2566
2604
|
<span class="rpt-summary-label">Total</span>
|
|
@@ -2775,7 +2813,7 @@ function groupIssuesByRule(issues) {
|
|
|
2775
2813
|
group.issues.push(issue);
|
|
2776
2814
|
group.totalScore += issue.calculatedScore;
|
|
2777
2815
|
}
|
|
2778
|
-
const SEVERITY_RANK = { blocking: 0, risk: 1, "missing-info": 2, suggestion: 3 };
|
|
2816
|
+
const SEVERITY_RANK = { blocking: 0, risk: 1, "missing-info": 2, suggestion: 3, note: 4 };
|
|
2779
2817
|
return [...byRule.values()].sort((a, b) => {
|
|
2780
2818
|
const sevDiff = (SEVERITY_RANK[a.severity] ?? 4) - (SEVERITY_RANK[b.severity] ?? 4);
|
|
2781
2819
|
return sevDiff !== 0 ? sevDiff : a.totalScore - b.totalScore;
|
|
@@ -2843,6 +2881,7 @@ body {
|
|
|
2843
2881
|
.sev-risk { background: var(--amber); }
|
|
2844
2882
|
.sev-missing { background: #a1a1aa; }
|
|
2845
2883
|
.sev-suggestion { background: var(--green); }
|
|
2884
|
+
.sev-note { background: #d4d4d8; }
|
|
2846
2885
|
|
|
2847
2886
|
/* ---- Print ---- */
|
|
2848
2887
|
@media print {
|
|
@@ -3239,6 +3278,7 @@ body {
|
|
|
3239
3278
|
.rpt-issue-score.sev-risk { background: var(--amber-bg); color: #d97706; border-color: rgba(245,158,11,0.2); }
|
|
3240
3279
|
.rpt-issue-score.sev-missing { background: #f4f4f5; color: #71717a; border-color: rgba(113,113,122,0.2); }
|
|
3241
3280
|
.rpt-issue-score.sev-suggestion { background: var(--green-bg); color: #16a34a; border-color: rgba(34,197,94,0.2); }
|
|
3281
|
+
.rpt-issue-score.sev-note { background: #f4f4f5; color: #71717a; border-color: rgba(113,113,122,0.2); }
|
|
3242
3282
|
|
|
3243
3283
|
.rpt-issue-body {
|
|
3244
3284
|
padding: 12px;
|
|
@@ -3644,6 +3684,8 @@ var EVENTS = {
|
|
|
3644
3684
|
// CLI
|
|
3645
3685
|
CLI_COMMAND: `${EVENT_PREFIX}cli_command`,
|
|
3646
3686
|
CLI_INIT: `${EVENT_PREFIX}cli_init`,
|
|
3687
|
+
CLI_CONFIG_SET_TOKEN: `${EVENT_PREFIX}cli_config_set_token`,
|
|
3688
|
+
CLI_DOCTOR: `${EVENT_PREFIX}cli_doctor`,
|
|
3647
3689
|
// Roundtrip (ADR-012)
|
|
3648
3690
|
// Wiring point for the roundtrip helper's `telemetry` callback. No Node-side
|
|
3649
3691
|
// orchestrator reads this yet — the helper ships in a sandbox-pure IIFE that
|
|
@@ -4061,6 +4103,10 @@ var missingComponentMsg = {
|
|
|
4061
4103
|
suggestion: `Create a new variant for this style combination`
|
|
4062
4104
|
})
|
|
4063
4105
|
};
|
|
4106
|
+
var unmappedComponentMsg = (componentName) => ({
|
|
4107
|
+
message: `"${componentName}" has no Code Connect mapping`,
|
|
4108
|
+
suggestion: `Run /canicode-roundtrip on this component to register a mapping so figma-implement-design reuses your code instead of regenerating markup. Skip if intentionally unmapped.`
|
|
4109
|
+
});
|
|
4064
4110
|
var detachedInstanceMsg = (name, componentName) => ({
|
|
4065
4111
|
message: `"${name}" may be a detached instance of component "${componentName}"`,
|
|
4066
4112
|
suggestion: `Restore as an instance of "${componentName}" or create a new variant`
|
|
@@ -4612,8 +4658,6 @@ defineRule({
|
|
|
4612
4658
|
definition: irregularSpacingDef,
|
|
4613
4659
|
check: irregularSpacingCheck
|
|
4614
4660
|
});
|
|
4615
|
-
|
|
4616
|
-
// src/core/rules/component/index.ts
|
|
4617
4661
|
var STYLE_COMPARE_KEYS = ["fills", "strokes", "effects", "cornerRadius", "strokeWeight", "individualStrokeWeights"];
|
|
4618
4662
|
function detectStyleOverrides(master, instance) {
|
|
4619
4663
|
const overrides = [];
|
|
@@ -4821,6 +4865,35 @@ defineRule({
|
|
|
4821
4865
|
definition: variantStructureMismatchDef,
|
|
4822
4866
|
check: variantStructureMismatchCheck
|
|
4823
4867
|
});
|
|
4868
|
+
var CODE_CONNECT_SETUP_KEY = "unmapped-component:setup-detected";
|
|
4869
|
+
function codeConnectIsSetUp(context) {
|
|
4870
|
+
return getAnalysisState(context, CODE_CONNECT_SETUP_KEY, () => {
|
|
4871
|
+
return existsSync(join(process.cwd(), "figma.config.json"));
|
|
4872
|
+
});
|
|
4873
|
+
}
|
|
4874
|
+
var unmappedComponentDef = {
|
|
4875
|
+
id: "unmapped-component",
|
|
4876
|
+
name: "Unmapped Component",
|
|
4877
|
+
category: "code-quality",
|
|
4878
|
+
why: "Without a Code Connect mapping, figma-implement-design regenerates the same markup every time this component appears in a screen \u2014 wasting tokens and risking drift.",
|
|
4879
|
+
impact: "Future roundtrips on screens containing this component cannot reuse your existing code; they regenerate markup that may not match the canonical implementation.",
|
|
4880
|
+
fix: "Run /canicode-roundtrip on this component to register a mapping. Figma's get_code_connect_map will skip if a mapping already exists."
|
|
4881
|
+
};
|
|
4882
|
+
var unmappedComponentCheck = (node, context) => {
|
|
4883
|
+
if (node.type !== "COMPONENT" && node.type !== "COMPONENT_SET") return null;
|
|
4884
|
+
if (isInsideInstance(context)) return null;
|
|
4885
|
+
if (!codeConnectIsSetUp(context)) return null;
|
|
4886
|
+
return {
|
|
4887
|
+
ruleId: unmappedComponentDef.id,
|
|
4888
|
+
nodeId: node.id,
|
|
4889
|
+
nodePath: context.path.join(" > "),
|
|
4890
|
+
...unmappedComponentMsg(node.name)
|
|
4891
|
+
};
|
|
4892
|
+
};
|
|
4893
|
+
defineRule({
|
|
4894
|
+
definition: unmappedComponentDef,
|
|
4895
|
+
check: unmappedComponentCheck
|
|
4896
|
+
});
|
|
4824
4897
|
|
|
4825
4898
|
// src/core/rules/naming/index.ts
|
|
4826
4899
|
function capitalize(s) {
|
|
@@ -5211,9 +5284,9 @@ Provide a Figma URL or fixture path via the input parameter. Requires FIGMA_TOKE
|
|
|
5211
5284
|
preset: z.enum(["relaxed", "dev-friendly", "ai-ready", "strict"]).optional().describe("Analysis preset"),
|
|
5212
5285
|
targetNodeId: z.string().optional().describe("Scope analysis to a specific node ID"),
|
|
5213
5286
|
configPath: z.string().optional().describe("Path to config JSON file for rule overrides"),
|
|
5214
|
-
openReport: z.boolean().optional().describe("Open the generated HTML report in the user's browser. Defaults to false \u2014 opt in only when a visible report is the explicit user request
|
|
5215
|
-
acknowledgments: z.array(AcknowledgmentSchema).optional().describe("
|
|
5216
|
-
scope: z.enum(["page", "component"]).optional().describe("
|
|
5287
|
+
openReport: z.boolean().optional().describe("Open the generated HTML report in the user's browser. Defaults to false \u2014 opt in only when a visible report is the explicit user request. The HTML file is always written to disk regardless."),
|
|
5288
|
+
acknowledgments: z.array(AcknowledgmentSchema).optional().describe("Pre-resolved acknowledgments from canicode-authored Figma annotations (e.g. via readCanicodeAcknowledgments in a use_figma batch). Each entry includes nodeId and ruleId; newer annotations may also carry intent, sceneWriteOutcome, and codegenDirective from a canicode-json fenced block. Matching issues are flagged acknowledged and contribute half weight to the density score."),
|
|
5289
|
+
scope: z.enum(["page", "component"]).optional().describe("Override analysis scope \u2014 `page` (screen/section where container bounds are required) or `component` (standalone reusable unit where root FILL is the design contract). Defaults to auto-detection from the root node type: `COMPONENT` / `COMPONENT_SET` / `INSTANCE` roots resolve to `component`, everything else to `page`."),
|
|
5217
5290
|
codegenReadyMinGrade: z.enum(["S", "A+", "A", "B+", "B", "C+", "C", "D", "F"]).optional().describe("Minimum grade for code-gen readiness. Overrides the codegenReadyMinGrade field in configPath. Default: A")
|
|
5218
5291
|
},
|
|
5219
5292
|
{
|
|
@@ -5307,7 +5380,7 @@ Provide a Figma URL or fixture path via the input parameter. Requires FIGMA_TOKE
|
|
|
5307
5380
|
preset: z.enum(["relaxed", "dev-friendly", "ai-ready", "strict"]).optional().describe("Analysis preset"),
|
|
5308
5381
|
targetNodeId: z.string().optional().describe("Scope analysis to a specific node ID"),
|
|
5309
5382
|
configPath: z.string().optional().describe("Path to config JSON file for rule overrides"),
|
|
5310
|
-
scope: z.enum(["page", "component"]).optional().describe("
|
|
5383
|
+
scope: z.enum(["page", "component"]).optional().describe("Override analysis scope \u2014 `page` or `component`. Defaults to auto-detection from the root node type."),
|
|
5311
5384
|
codegenReadyMinGrade: z.enum(["S", "A+", "A", "B+", "B", "C+", "C", "D", "F"]).optional().describe("Minimum grade for code-gen readiness. Overrides the codegenReadyMinGrade field in configPath. Default: A")
|
|
5312
5385
|
},
|
|
5313
5386
|
{
|
|
@@ -5480,7 +5553,7 @@ Get your token: Figma \u2192 Settings \u2192 Security \u2192 Personal access tok
|
|
|
5480
5553
|
claude mcp add canicode -- npx --yes --package=canicode canicode-mcp
|
|
5481
5554
|
\`\`\`
|
|
5482
5555
|
|
|
5483
|
-
Requires FIGMA_TOKEN for live Figma URL analysis. The MCP server reads it from \`~/.canicode/config.json\` (set via \`canicode init --token \u2026\`) or from the host's environment, so do **not** pass \`-e FIGMA_TOKEN=\u2026\` to \`claude mcp add\` \u2014 \`@anthropic-ai/claude-code\`'s current parser rejects short-form flags placed before \`--\`.
|
|
5556
|
+
Requires FIGMA_TOKEN for live Figma URL analysis. The MCP server reads it from \`~/.canicode/config.json\` (set via \`canicode init --token \u2026\`) or from the host's environment, so do **not** pass \`-e FIGMA_TOKEN=\u2026\` to \`claude mcp add\` \u2014 \`@anthropic-ai/claude-code\`'s current parser rejects short-form flags placed before \`--\`.
|
|
5484
5557
|
|
|
5485
5558
|
## CLI only (no MCP server)
|
|
5486
5559
|
|