canicode 0.3.1 → 0.3.2
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 +27 -8
- package/dist/cli/index.js +80 -7
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +4 -0
- package/dist/index.js +12 -2
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +67 -3
- package/dist/mcp/server.js.map +1 -1
- package/docs/CUSTOMIZATION.md +273 -0
- package/package.json +3 -2
package/dist/mcp/server.js
CHANGED
|
@@ -464,11 +464,15 @@ var RuleEngine = class {
|
|
|
464
464
|
enabledRuleIds;
|
|
465
465
|
disabledRuleIds;
|
|
466
466
|
targetNodeId;
|
|
467
|
+
excludeNamePattern;
|
|
468
|
+
excludeNodeTypes;
|
|
467
469
|
constructor(options = {}) {
|
|
468
470
|
this.configs = options.configs ?? RULE_CONFIGS;
|
|
469
471
|
this.enabledRuleIds = options.enabledRules ? new Set(options.enabledRules) : null;
|
|
470
472
|
this.disabledRuleIds = new Set(options.disabledRules ?? []);
|
|
471
473
|
this.targetNodeId = options.targetNodeId;
|
|
474
|
+
this.excludeNamePattern = options.excludeNodeNames && options.excludeNodeNames.length > 0 ? new RegExp(`\\b(${options.excludeNodeNames.join("|")})\\b`, "i") : null;
|
|
475
|
+
this.excludeNodeTypes = options.excludeNodeTypes && options.excludeNodeTypes.length > 0 ? new Set(options.excludeNodeTypes) : null;
|
|
472
476
|
}
|
|
473
477
|
/**
|
|
474
478
|
* Analyze a Figma file and return issues
|
|
@@ -522,6 +526,12 @@ var RuleEngine = class {
|
|
|
522
526
|
*/
|
|
523
527
|
traverseAndCheck(node, file, rules, maxDepth, issues, depth, path, parent, siblings) {
|
|
524
528
|
const nodePath = [...path, node.name];
|
|
529
|
+
if (this.excludeNodeTypes && this.excludeNodeTypes.has(node.type)) {
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
if (this.excludeNamePattern && this.excludeNamePattern.test(node.name)) {
|
|
533
|
+
return;
|
|
534
|
+
}
|
|
525
535
|
const context = {
|
|
526
536
|
file,
|
|
527
537
|
parent,
|
|
@@ -1380,7 +1390,7 @@ function generateHtmlReport(file, result, scores, options) {
|
|
|
1380
1390
|
${CATEGORIES.map((cat) => {
|
|
1381
1391
|
const cs = scores.byCategory[cat];
|
|
1382
1392
|
const desc = CATEGORY_DESCRIPTIONS[cat];
|
|
1383
|
-
return ` <
|
|
1393
|
+
return ` <a href="#cat-${cat}" class="flex flex-col items-center group relative cursor-pointer no-underline text-foreground hover:opacity-80 transition-opacity">
|
|
1384
1394
|
${renderGaugeSvg(cs.percentage, 100, 7)}
|
|
1385
1395
|
<span class="text-xs font-medium mt-2.5 text-center leading-tight">${CATEGORY_LABELS[cat]}</span>
|
|
1386
1396
|
<span class="text-[11px] text-muted-foreground">${cs.issueCount} issues</span>
|
|
@@ -1388,7 +1398,7 @@ ${CATEGORIES.map((cat) => {
|
|
|
1388
1398
|
${esc(desc)}
|
|
1389
1399
|
<div class="absolute top-full left-1/2 -translate-x-1/2 border-4 border-transparent border-t-zinc-900"></div>
|
|
1390
1400
|
</div>
|
|
1391
|
-
</
|
|
1401
|
+
</a>`;
|
|
1392
1402
|
}).join("\n")}
|
|
1393
1403
|
</div>
|
|
1394
1404
|
</section>
|
|
@@ -1526,7 +1536,7 @@ function renderCategory(cat, scores, issues, fileKey, screenshotMap, figmaToken)
|
|
|
1526
1536
|
for (const sev of SEVERITY_ORDER) bySeverity.set(sev, []);
|
|
1527
1537
|
for (const issue of issues) bySeverity.get(issue.config.severity)?.push(issue);
|
|
1528
1538
|
return `
|
|
1529
|
-
<details class="bg-card border border-border rounded-lg shadow-sm overflow-hidden group"${hasProblems ? " open" : ""}>
|
|
1539
|
+
<details id="cat-${cat}" class="bg-card border border-border rounded-lg shadow-sm overflow-hidden group"${hasProblems ? " open" : ""}>
|
|
1530
1540
|
<summary class="px-5 py-3.5 flex items-center gap-3 cursor-pointer hover:bg-muted/50 transition-colors select-none">
|
|
1531
1541
|
<span class="inline-flex items-center justify-center w-10 h-6 rounded-md text-xs font-bold border ${scoreBadgeStyle(cs.percentage)}">${cs.percentage}</span>
|
|
1532
1542
|
<div class="flex-1 min-w-0">
|
|
@@ -3000,6 +3010,60 @@ server.tool(
|
|
|
3000
3010
|
};
|
|
3001
3011
|
}
|
|
3002
3012
|
);
|
|
3013
|
+
server.tool(
|
|
3014
|
+
"docs",
|
|
3015
|
+
`Get the customization guide for CanICode.
|
|
3016
|
+
|
|
3017
|
+
Returns documentation on:
|
|
3018
|
+
- Config overrides (excludeNodeNames, excludeNodeTypes, gridBase, colorTolerance, per-rule score/severity/enabled)
|
|
3019
|
+
- Custom rules (how to add project-specific checks)
|
|
3020
|
+
- All 39 rule IDs with default scores and severity
|
|
3021
|
+
- Example configs (strict, relaxed, mobile-first)
|
|
3022
|
+
|
|
3023
|
+
Use this when the user asks about customization, configuration, rule settings, or how to adjust scores.`,
|
|
3024
|
+
{
|
|
3025
|
+
topic: z.enum(["all", "config", "custom-rules", "rules"]).optional().describe("Specific topic: config (overrides), custom-rules (adding new rules), rules (all rule IDs). Default: all")
|
|
3026
|
+
},
|
|
3027
|
+
async ({ topic }) => {
|
|
3028
|
+
const { readFile: readFile4 } = await import('fs/promises');
|
|
3029
|
+
const { resolve: resolve4, dirname } = await import('path');
|
|
3030
|
+
const { fileURLToPath } = await import('url');
|
|
3031
|
+
try {
|
|
3032
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
3033
|
+
const docPath = resolve4(__dirname, "../../docs/CUSTOMIZATION.md");
|
|
3034
|
+
let content;
|
|
3035
|
+
try {
|
|
3036
|
+
content = await readFile4(docPath, "utf-8");
|
|
3037
|
+
} catch {
|
|
3038
|
+
const altPath = resolve4(__dirname, "../docs/CUSTOMIZATION.md");
|
|
3039
|
+
content = await readFile4(altPath, "utf-8");
|
|
3040
|
+
}
|
|
3041
|
+
if (topic && topic !== "all") {
|
|
3042
|
+
const sections = {
|
|
3043
|
+
"config": "## Config Overrides",
|
|
3044
|
+
"custom-rules": "## Custom Rules",
|
|
3045
|
+
"rules": "### All Rule IDs"
|
|
3046
|
+
};
|
|
3047
|
+
const header = sections[topic];
|
|
3048
|
+
if (header) {
|
|
3049
|
+
const startIdx = content.indexOf(header);
|
|
3050
|
+
if (startIdx !== -1) {
|
|
3051
|
+
const nextH2 = content.indexOf("\n## ", startIdx + header.length);
|
|
3052
|
+
content = nextH2 !== -1 ? content.slice(startIdx, nextH2) : content.slice(startIdx);
|
|
3053
|
+
}
|
|
3054
|
+
}
|
|
3055
|
+
}
|
|
3056
|
+
return {
|
|
3057
|
+
content: [{ type: "text", text: content }]
|
|
3058
|
+
};
|
|
3059
|
+
} catch {
|
|
3060
|
+
return {
|
|
3061
|
+
content: [{ type: "text", text: "Customization guide not found. See: https://github.com/let-sunny/canicode/blob/main/docs/CUSTOMIZATION.md" }],
|
|
3062
|
+
isError: true
|
|
3063
|
+
};
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
3066
|
+
);
|
|
3003
3067
|
async function main() {
|
|
3004
3068
|
const transport = new StdioServerTransport();
|
|
3005
3069
|
await server.connect(transport);
|