canicode 0.5.2 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,10 +10,13 @@
10
10
  <a href="https://github.com/let-sunny/canicode/actions/workflows/release.yml"><img src="https://github.com/let-sunny/canicode/actions/workflows/release.yml/badge.svg" alt="Release"></a>
11
11
  <a href="https://let-sunny.github.io/canicode/"><img src="https://img.shields.io/badge/Try_it-GitHub_Pages-blue" alt="GitHub Pages"></a>
12
12
  <a href="https://www.figma.com/community/plugin/1617144221046795292/canicode"><img src="https://img.shields.io/badge/Figma_Plugin-under_review-orange" alt="Figma Plugin"></a>
13
+ <a href="https://github.com/let-sunny/canicode#mcp-server-claude-code--cursor--claude-desktop"><img src="https://img.shields.io/badge/MCP_Registry-published-green" alt="MCP Registry"></a>
13
14
  </p>
14
15
 
15
16
  <p align="center">Analyze Figma designs. Score how dev-friendly and AI-friendly they are. Get actionable issues before writing code.</p>
16
17
 
18
+ <p align="center"><strong><a href="https://github.com/let-sunny/canicode/discussions/new?category=share-your-figma">Share your Figma design</a></strong> to help improve scoring accuracy.</p>
19
+
17
20
  <p align="center"><strong><a href="https://let-sunny.github.io/canicode/">Try it in your browser</a></strong> — no install needed.</p>
18
21
 
19
22
  <p align="center">
@@ -102,7 +105,7 @@ canicode init --token figd_xxxxxxxxxxxxx
102
105
 
103
106
  **Claude Code:**
104
107
  ```bash
105
- claude mcp add canicode -e FIGMA_TOKEN=figd_xxxxxxxxxxxxx -- npx -y canicode canicode-mcp
108
+ claude mcp add canicode -e FIGMA_TOKEN=figd_xxxxxxxxxxxxx -- npx -y -p canicode canicode-mcp
106
109
  ```
107
110
 
108
111
  **Cursor** (`~/.cursor/mcp.json`):
@@ -146,9 +149,10 @@ Then ask: *"Analyze this Figma design: https://www.figma.com/design/..."*
146
149
 
147
150
  | Flag | Source | Token required |
148
151
  |------|--------|----------------|
149
- | (none) | Auto-detect: MCP first, then REST API | For API fallback |
150
- | `--mcp` | Figma MCP via Claude Code | None |
151
- | `--api` | Figma REST API | Yes |
152
+ | (none) | Figma REST API | Yes |
153
+ | `--api` | Figma REST API (explicit) | Yes |
154
+
155
+ For token-free analysis, use the **canicode MCP server** with the official Figma MCP, or the **`/canicode` skill** in Claude Code.
152
156
 
153
157
  Token priority:
154
158
  1. `--token` flag (one-time override)
@@ -299,7 +303,6 @@ Save Figma file data as JSON for offline analysis:
299
303
 
300
304
  ```bash
301
305
  canicode save-fixture https://www.figma.com/design/ABC123/MyDesign
302
- canicode save-fixture https://www.figma.com/design/ABC123/MyDesign --mcp
303
306
  ```
304
307
 
305
308
  </details>
package/dist/cli/index.js CHANGED
@@ -1060,137 +1060,6 @@ async function loadFigmaFileFromJson(filePath) {
1060
1060
  const fileKey = basename(filePath, ".json");
1061
1061
  return transformFigmaResponse(fileKey, data);
1062
1062
  }
1063
-
1064
- // src/adapters/figma-mcp-adapter.ts
1065
- var TAG_TYPE_MAP = {
1066
- canvas: "CANVAS",
1067
- frame: "FRAME",
1068
- group: "GROUP",
1069
- section: "SECTION",
1070
- component: "COMPONENT",
1071
- "component-set": "COMPONENT_SET",
1072
- instance: "INSTANCE",
1073
- rectangle: "RECTANGLE",
1074
- "rounded-rectangle": "RECTANGLE",
1075
- ellipse: "ELLIPSE",
1076
- vector: "VECTOR",
1077
- text: "TEXT",
1078
- line: "LINE",
1079
- "boolean-operation": "BOOLEAN_OPERATION",
1080
- star: "STAR",
1081
- "regular-polygon": "REGULAR_POLYGON",
1082
- slice: "SLICE",
1083
- sticky: "STICKY",
1084
- table: "TABLE",
1085
- "table-cell": "TABLE_CELL",
1086
- symbol: "COMPONENT",
1087
- slot: "FRAME"
1088
- };
1089
- function parseXml(xml) {
1090
- const nodes = [];
1091
- const stack = [];
1092
- const tagRegex = /<(\/?)([\w-]+)([^>]*?)(\/?)>/g;
1093
- let match;
1094
- while ((match = tagRegex.exec(xml)) !== null) {
1095
- const isClosing = match[1] === "/";
1096
- const tagName = match[2];
1097
- const attrString = match[3] ?? "";
1098
- const isSelfClosing = match[4] === "/";
1099
- if (isClosing) {
1100
- const finished = stack.pop();
1101
- if (finished) {
1102
- const parent = stack[stack.length - 1];
1103
- if (parent) {
1104
- parent.children.push(finished);
1105
- } else {
1106
- nodes.push(finished);
1107
- }
1108
- }
1109
- } else {
1110
- const attrs = parseAttributes(attrString);
1111
- const node = { tag: tagName, attrs, children: [] };
1112
- if (isSelfClosing) {
1113
- const parent = stack[stack.length - 1];
1114
- if (parent) {
1115
- parent.children.push(node);
1116
- } else {
1117
- nodes.push(node);
1118
- }
1119
- } else {
1120
- stack.push(node);
1121
- }
1122
- }
1123
- }
1124
- while (stack.length > 0) {
1125
- const finished = stack.pop();
1126
- const parent = stack[stack.length - 1];
1127
- if (parent) {
1128
- parent.children.push(finished);
1129
- } else {
1130
- nodes.push(finished);
1131
- }
1132
- }
1133
- return nodes;
1134
- }
1135
- function parseAttributes(attrString) {
1136
- const attrs = {};
1137
- const attrRegex = /([\w-]+)="([^"]*)"/g;
1138
- let match;
1139
- while ((match = attrRegex.exec(attrString)) !== null) {
1140
- const key = match[1];
1141
- const value = match[2];
1142
- if (key && value !== void 0) {
1143
- attrs[key] = value;
1144
- }
1145
- }
1146
- return attrs;
1147
- }
1148
- function toAnalysisNode(xmlNode) {
1149
- const type = TAG_TYPE_MAP[xmlNode.tag] ?? "FRAME";
1150
- const id = xmlNode.attrs["id"] ?? "0:0";
1151
- const name = xmlNode.attrs["name"] ?? xmlNode.tag;
1152
- const hidden = xmlNode.attrs["hidden"] === "true";
1153
- const x = parseFloat(xmlNode.attrs["x"] ?? "0");
1154
- const y = parseFloat(xmlNode.attrs["y"] ?? "0");
1155
- const width = parseFloat(xmlNode.attrs["width"] ?? "0");
1156
- const height = parseFloat(xmlNode.attrs["height"] ?? "0");
1157
- const node = {
1158
- id,
1159
- name,
1160
- type,
1161
- visible: !hidden,
1162
- absoluteBoundingBox: { x, y, width, height }
1163
- };
1164
- if (xmlNode.children.length > 0) {
1165
- node.children = xmlNode.children.map(toAnalysisNode);
1166
- }
1167
- return node;
1168
- }
1169
- function parseMcpMetadataXml(xml, fileKey, fileName) {
1170
- const parsed = parseXml(xml);
1171
- const children = parsed.map(toAnalysisNode);
1172
- let document;
1173
- if (children.length === 1 && children[0]) {
1174
- document = children[0];
1175
- } else {
1176
- document = {
1177
- id: "0:0",
1178
- name: "Document",
1179
- type: "DOCUMENT",
1180
- visible: true,
1181
- children
1182
- };
1183
- }
1184
- return {
1185
- fileKey,
1186
- name: fileName ?? fileKey,
1187
- lastModified: (/* @__PURE__ */ new Date()).toISOString(),
1188
- version: "mcp",
1189
- document,
1190
- components: {},
1191
- styles: {}
1192
- };
1193
- }
1194
1063
  var AIREADY_DIR = join(homedir(), ".canicode");
1195
1064
  var CONFIG_PATH = join(AIREADY_DIR, "config.json");
1196
1065
  var REPORTS_DIR = join(AIREADY_DIR, "reports");
@@ -1257,7 +1126,7 @@ function isFigmaUrl(input) {
1257
1126
  function isJsonFile(input) {
1258
1127
  return input.endsWith(".json");
1259
1128
  }
1260
- async function loadFile(input, token, mode = "auto") {
1129
+ async function loadFile(input, token) {
1261
1130
  if (isJsonFile(input)) {
1262
1131
  const filePath = resolve(input);
1263
1132
  if (!existsSync(filePath)) {
@@ -1267,31 +1136,13 @@ async function loadFile(input, token, mode = "auto") {
1267
1136
  return { file: await loadFigmaFileFromJson(filePath) };
1268
1137
  }
1269
1138
  if (isFigmaUrl(input)) {
1270
- const { fileKey, nodeId, fileName } = parseFigmaUrl(input);
1271
- if (mode === "mcp") {
1272
- return loadFromMcp(fileKey, nodeId, fileName);
1273
- }
1274
- if (mode === "api") {
1275
- return loadFromApi(fileKey, nodeId, token);
1276
- }
1277
- try {
1278
- console.log("Auto-detecting data source... trying MCP first.");
1279
- return await loadFromMcp(fileKey, nodeId, fileName);
1280
- } catch (mcpError) {
1281
- const mcpMsg = mcpError instanceof Error ? mcpError.message : String(mcpError);
1282
- console.log(`MCP unavailable (${mcpMsg}). Falling back to REST API.`);
1283
- return loadFromApi(fileKey, nodeId, token);
1284
- }
1139
+ const { fileKey, nodeId } = parseFigmaUrl(input);
1140
+ return loadFromApi(fileKey, nodeId, token);
1285
1141
  }
1286
1142
  throw new Error(
1287
1143
  `Invalid input: ${input}. Provide a Figma URL or JSON file path.`
1288
1144
  );
1289
1145
  }
1290
- async function loadFromMcp(fileKey, nodeId, fileName) {
1291
- console.log(`Loading via MCP: ${fileKey} (node: ${nodeId ?? "root"})`);
1292
- const file = await loadViaMcp(fileKey, nodeId ?? "0:1", fileName);
1293
- return { file, nodeId };
1294
- }
1295
1146
  async function loadFromApi(fileKey, nodeId, token) {
1296
1147
  console.log(`Fetching from Figma REST API: ${fileKey}`);
1297
1148
  if (nodeId) {
@@ -1310,20 +1161,6 @@ async function loadFromApi(fileKey, nodeId, token) {
1310
1161
  nodeId
1311
1162
  };
1312
1163
  }
1313
- async function loadViaMcp(fileKey, nodeId, fileName) {
1314
- const { execSync } = await import('child_process');
1315
- const result = execSync(
1316
- `claude --print "Use the mcp__figma__get_metadata tool with fileKey=\\"${fileKey}\\" and nodeId=\\"${nodeId.replace(/-/g, ":")}\\" \u2014 return ONLY the raw XML output, nothing else."`,
1317
- { encoding: "utf-8", timeout: 12e4 }
1318
- );
1319
- const xmlStart = result.indexOf("<");
1320
- const xmlEnd = result.lastIndexOf(">");
1321
- if (xmlStart === -1 || xmlEnd === -1) {
1322
- throw new Error("MCP did not return valid XML metadata");
1323
- }
1324
- const xml = result.slice(xmlStart, xmlEnd + 1);
1325
- return parseMcpMetadataXml(xml, fileKey, fileName);
1326
- }
1327
1164
 
1328
1165
  // src/core/scoring.ts
1329
1166
  var SEVERITY_DENSITY_WEIGHT = {
@@ -2923,7 +2760,7 @@ function printDocsSetup() {
2923
2760
  CANICODE SETUP GUIDE
2924
2761
 
2925
2762
  \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
2926
- 1. CLI
2763
+ 1. CLI (REST API)
2927
2764
  \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
2928
2765
 
2929
2766
  Install:
@@ -2937,11 +2774,6 @@ CANICODE SETUP GUIDE
2937
2774
  canicode analyze "https://www.figma.com/design/ABC123/MyDesign?node-id=1-234"
2938
2775
  (opens report in browser automatically, use --no-open to disable)
2939
2776
 
2940
- Data source flags:
2941
- --api REST API (uses saved token)
2942
- --mcp Figma MCP bridge (Claude Code only, no token needed)
2943
- (none) Auto: try MCP first, fallback to API
2944
-
2945
2777
  Options:
2946
2778
  --preset strict|relaxed|dev-friendly|ai-ready
2947
2779
  --config ./my-config.json
@@ -2952,54 +2784,25 @@ CANICODE SETUP GUIDE
2952
2784
  ~/.canicode/reports/report-YYYY-MM-DD-HH-mm-<filekey>.html
2953
2785
 
2954
2786
  \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
2955
- 2. MCP SERVER (Claude Code integration)
2787
+ 2. CLAUDE CODE SKILL (Figma MCP, no token needed)
2956
2788
  \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
2957
2789
 
2958
- Route A \u2014 Figma MCP relay (no token needed):
2959
-
2960
- Install (once):
2961
- claude mcp add figma -- npx -y @anthropic-ai/claude-code-mcp-figma
2962
- claude mcp add --transport stdio canicode npx canicode-mcp
2963
-
2964
- Flow:
2965
- Claude Code
2966
- -> Figma MCP get_metadata(fileKey, nodeId) -> XML node tree
2967
- -> canicode MCP analyze(designData: XML) -> analysis result
2790
+ Requires the official Figma MCP server at project level.
2968
2791
 
2969
- Route B \u2014 REST API direct (token needed):
2970
-
2971
- Install (once):
2972
- claude mcp add --transport stdio canicode npx canicode-mcp
2973
- canicode init --token figd_xxxxxxxxxxxxx
2974
-
2975
- Flow:
2976
- Claude Code
2977
- -> canicode MCP analyze(input: URL) -> internal REST API fetch -> result
2978
-
2979
- Use (both routes \u2014 just ask Claude Code):
2980
- "Analyze this Figma design: https://www.figma.com/design/..."
2981
-
2982
- Route A vs B:
2983
- A: No token, 2 MCP servers, Claude orchestrates 2 calls
2984
- B: Token needed, 1 MCP server, canicode fetches directly
2985
-
2986
- \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
2987
- 3. CLAUDE SKILLS (lightweight)
2988
- \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
2989
-
2990
- Install:
2991
- cp -r path/to/canicode/.claude/skills/canicode .claude/skills/
2992
-
2993
- Setup (for REST API):
2994
- npx canicode init --token figd_xxxxxxxxxxxxx
2792
+ Setup (once):
2793
+ claude mcp add -s project -t http figma https://mcp.figma.com/mcp
2995
2794
 
2996
2795
  Use (in Claude Code):
2997
- /canicode analyze "https://www.figma.com/design/..."
2796
+ /canicode https://www.figma.com/design/ABC123/MyDesign?node-id=1-234
2998
2797
 
2999
- Runs CLI under the hood \u2014 all flags work (--mcp, --api, --preset, etc.)
2798
+ Flow:
2799
+ Claude Code
2800
+ -> Figma MCP get_metadata(fileKey, nodeId) -> XML node tree
2801
+ -> Convert to fixture JSON
2802
+ -> canicode analyze fixture.json -> report
3000
2803
 
3001
2804
  \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
3002
- TOKEN PRIORITY (all methods)
2805
+ TOKEN PRIORITY (CLI mode)
3003
2806
  \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
3004
2807
 
3005
2808
  1. --token flag (one-time override)
@@ -3011,9 +2814,11 @@ CANICODE SETUP GUIDE
3011
2814
  \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
3012
2815
 
3013
2816
  CI/CD, automation -> CLI + FIGMA_TOKEN env var
3014
- Claude Code, interactive -> MCP Server (Route A)
3015
- No token, Claude Code -> MCP Server (Route A)
3016
- Quick trial -> Skills
2817
+ Claude Code (full) -> canicode MCP + Figma MCP (no token needed)
2818
+ Claude Code (light) -> /canicode skill + Figma MCP (no token needed)
2819
+ In Figma -> Figma Plugin
2820
+ Browser -> Web App (GitHub Pages)
2821
+ Quick trial, offline -> CLI + JSON fixtures
3017
2822
  `.trimStart());
3018
2823
  }
3019
2824
  function printDocsRules() {
@@ -3055,7 +2860,7 @@ EXAMPLE
3055
2860
  "maxHeight": 48,
3056
2861
  "nameContains": "icon"
3057
2862
  },
3058
- "message": ""{name}" is an icon but not a component",
2863
+ "message": "\\"{name}\\" is an icon but not a component",
3059
2864
  "why": "Icons should be reusable components.",
3060
2865
  "impact": "Developers hardcode icons.",
3061
2866
  "fix": "Convert to component and publish to library."
@@ -4414,16 +4219,13 @@ function countNodes2(node) {
4414
4219
  }
4415
4220
  return count;
4416
4221
  }
4417
- cli.command("analyze <input>", "Analyze a Figma file or JSON fixture").option("--preset <preset>", "Analysis preset (relaxed | dev-friendly | ai-ready | strict)").option("--output <path>", "HTML report output path").option("--token <token>", "Figma API token (or use FIGMA_TOKEN env var)").option("--mcp", "Load via Figma MCP (no FIGMA_TOKEN needed)").option("--api", "Load via Figma REST API (requires FIGMA_TOKEN)").option("--screenshot", "Include screenshot comparison in report (requires ANTHROPIC_API_KEY)").option("--custom-rules <path>", "Path to custom rules JSON file").option("--config <path>", "Path to config JSON file (override rule scores/settings)").option("--no-open", "Don't open report in browser after analysis").example(" canicode analyze https://www.figma.com/design/ABC123/MyDesign").example(" canicode analyze https://www.figma.com/design/ABC123/MyDesign --mcp").example(" canicode analyze https://www.figma.com/design/ABC123/MyDesign --api --token YOUR_TOKEN").example(" canicode analyze ./fixtures/design.json --output report.html").example(" canicode analyze ./fixtures/design.json --custom-rules ./my-rules.json").example(" canicode analyze ./fixtures/design.json --config ./my-config.json").action(async (input, options) => {
4222
+ cli.command("analyze <input>", "Analyze a Figma file or JSON fixture").option("--preset <preset>", "Analysis preset (relaxed | dev-friendly | ai-ready | strict)").option("--output <path>", "HTML report output path").option("--token <token>", "Figma API token (or use FIGMA_TOKEN env var)").option("--api", "Load via Figma REST API (requires FIGMA_TOKEN)").option("--screenshot", "Include screenshot comparison in report (requires ANTHROPIC_API_KEY)").option("--custom-rules <path>", "Path to custom rules JSON file").option("--config <path>", "Path to config JSON file (override rule scores/settings)").option("--no-open", "Don't open report in browser after analysis").example(" canicode analyze https://www.figma.com/design/ABC123/MyDesign").example(" canicode analyze https://www.figma.com/design/ABC123/MyDesign --api --token YOUR_TOKEN").example(" canicode analyze ./fixtures/design.json --output report.html").example(" canicode analyze ./fixtures/design.json --custom-rules ./my-rules.json").example(" canicode analyze ./fixtures/design.json --config ./my-config.json").action(async (input, options) => {
4418
4223
  const analysisStart = Date.now();
4419
4224
  trackEvent(EVENTS.ANALYSIS_STARTED, { source: isJsonFile(input) ? "fixture" : "figma" });
4420
4225
  try {
4421
- if (options.mcp && options.api) {
4422
- throw new Error("Cannot use --mcp and --api together. Choose one.");
4423
- }
4424
- if (!options.mcp && !options.token && !getFigmaToken() && !isJsonFile(input)) {
4226
+ if (!options.token && !getFigmaToken() && !isJsonFile(input)) {
4425
4227
  throw new Error(
4426
- "canicode is not configured. Run 'canicode init --token YOUR_TOKEN' first.\nOr use --mcp flag for Figma MCP mode (no token needed)."
4228
+ "canicode is not configured. Run 'canicode init --token YOUR_TOKEN' first."
4427
4229
  );
4428
4230
  }
4429
4231
  if (options.screenshot) {
@@ -4435,8 +4237,7 @@ cli.command("analyze <input>", "Analyze a Figma file or JSON fixture").option("-
4435
4237
  }
4436
4238
  console.log("Screenshot comparison mode enabled (coming soon).\n");
4437
4239
  }
4438
- const mode = options.mcp ? "mcp" : options.api ? "api" : "auto";
4439
- const { file, nodeId } = await loadFile(input, options.token, mode);
4240
+ const { file, nodeId } = await loadFile(input, options.token);
4440
4241
  const totalNodes = countNodes2(file.document);
4441
4242
  let effectiveNodeId = nodeId;
4442
4243
  if (!effectiveNodeId && totalNodes > MAX_NODES_WITHOUT_SCOPE) {
@@ -4718,17 +4519,13 @@ cli.command(
4718
4519
  cli.command(
4719
4520
  "save-fixture <input>",
4720
4521
  "Save Figma file data as a JSON fixture for offline analysis"
4721
- ).option("--output <path>", "Output JSON path (default: fixtures/<filekey>.json)").option("--mcp", "Load via Figma MCP (no FIGMA_TOKEN needed)").option("--api", "Load via Figma REST API (requires FIGMA_TOKEN)").option("--token <token>", "Figma API token (or use FIGMA_TOKEN env var)").example(" canicode save-fixture https://www.figma.com/design/ABC123/MyDesign --mcp").example(" canicode save-fixture https://www.figma.com/design/ABC123/MyDesign --api --token YOUR_TOKEN").action(async (input, options) => {
4522
+ ).option("--output <path>", "Output JSON path (default: fixtures/<filekey>.json)").option("--token <token>", "Figma API token (or use FIGMA_TOKEN env var)").example(" canicode save-fixture https://www.figma.com/design/ABC123/MyDesign").example(" canicode save-fixture https://www.figma.com/design/ABC123/MyDesign --token YOUR_TOKEN").action(async (input, options) => {
4722
4523
  try {
4723
- if (options.mcp && options.api) {
4724
- throw new Error("Cannot use --mcp and --api together. Choose one.");
4725
- }
4726
4524
  if (isFigmaUrl(input) && !parseFigmaUrl(input).nodeId) {
4727
4525
  console.warn("\nWarning: No node-id specified. Saving entire file as fixture.");
4728
4526
  console.warn("Tip: Add ?node-id=XXX to save a specific section.\n");
4729
4527
  }
4730
- const mode = options.mcp ? "mcp" : options.api ? "api" : "auto";
4731
- const { file } = await loadFile(input, options.token, mode);
4528
+ const { file } = await loadFile(input, options.token);
4732
4529
  const outputPath = resolve(
4733
4530
  options.output ?? `fixtures/${file.fileKey}.json`
4734
4531
  );
@@ -4759,19 +4556,17 @@ cli.command("init", "Set up canicode (Figma token or MCP)").option("--token <tok
4759
4556
  return;
4760
4557
  }
4761
4558
  if (options.mcp) {
4762
- console.log(`MCP SETUP
4559
+ console.log(`FIGMA MCP SETUP (for Claude Code)
4763
4560
  `);
4764
- console.log(`1. Install Figma MCP in Claude Code:`);
4765
- console.log(` claude mcp add figma -- npx -y @anthropic-ai/claude-code-mcp-figma
4561
+ console.log(`1. Register the official Figma MCP server at project level:`);
4562
+ console.log(` claude mcp add -s project -t http figma https://mcp.figma.com/mcp
4766
4563
  `);
4767
- console.log(`2. Add canicode MCP server:`);
4768
- console.log(` claude mcp add --transport stdio canicode npx canicode-mcp
4564
+ console.log(` This creates .mcp.json in your project root.
4769
4565
  `);
4770
- console.log(`3. Set Figma token (for MCP server's REST API fallback):`);
4771
- console.log(` canicode init --token YOUR_TOKEN
4566
+ console.log(`2. Use the /canicode skill in Claude Code:`);
4567
+ console.log(` /canicode https://www.figma.com/design/.../MyDesign?node-id=1-234
4772
4568
  `);
4773
- console.log(`4. Use in Claude Code:`);
4774
- console.log(` "Analyze this Figma design: https://www.figma.com/design/..."`);
4569
+ console.log(` The skill calls Figma MCP directly \u2014 no FIGMA_TOKEN needed.`);
4775
4570
  return;
4776
4571
  }
4777
4572
  console.log(`CANICODE SETUP
@@ -4784,7 +4579,7 @@ cli.command("init", "Set up canicode (Figma token or MCP)").option("--token <tok
4784
4579
  `);
4785
4580
  console.log(`Option 2: Figma MCP (recommended for Claude Code)`);
4786
4581
  console.log(` canicode init --mcp`);
4787
- console.log(` No token needed for CLI \u2014 uses Claude Code's Figma MCP bridge
4582
+ console.log(` Uses the /canicode skill in Claude Code with official Figma MCP
4788
4583
  `);
4789
4584
  console.log(`After setup:`);
4790
4585
  console.log(` canicode analyze "https://www.figma.com/design/..."`);
@@ -4895,9 +4690,8 @@ cli.help((sections) => {
4895
4690
  {
4896
4691
  title: "\nData source",
4897
4692
  body: [
4898
- ` --mcp Load via Figma MCP (no token needed)`,
4899
4693
  ` --api Load via Figma REST API (needs FIGMA_TOKEN)`,
4900
- ` (default) Auto-detect: try MCP first, then API`
4694
+ ` --token <token> Figma API token (or use FIGMA_TOKEN env var)`
4901
4695
  ].join("\n")
4902
4696
  },
4903
4697
  {
@@ -4910,7 +4704,6 @@ cli.help((sections) => {
4910
4704
  {
4911
4705
  title: "\nExamples",
4912
4706
  body: [
4913
- ` $ canicode analyze "https://www.figma.com/design/..." --mcp`,
4914
4707
  ` $ canicode analyze "https://www.figma.com/design/..." --api`,
4915
4708
  ` $ canicode analyze "https://www.figma.com/design/..." --preset strict`,
4916
4709
  ` $ canicode analyze "https://www.figma.com/design/..." --config ./my-config.json`
@@ -4920,7 +4713,7 @@ cli.help((sections) => {
4920
4713
  title: "\nInstallation",
4921
4714
  body: [
4922
4715
  ` CLI: npm install -g canicode`,
4923
- ` MCP: claude mcp add --transport stdio canicode npx canicode-mcp`,
4716
+ ` MCP: claude mcp add canicode -- npx -y -p canicode canicode-mcp`,
4924
4717
  ` Skills: github.com/let-sunny/canicode`
4925
4718
  ].join("\n")
4926
4719
  }