canicode 0.8.2 → 0.8.5

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/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { z } from 'zod';
2
- import { GetFileResponse } from '@figma/rest-api-spec';
2
+ import { GetFileResponse, Node } from '@figma/rest-api-spec';
3
3
 
4
- var version = "0.8.2";
4
+ var version = "0.8.5";
5
5
 
6
6
  declare const SeveritySchema: z.ZodEnum<{
7
7
  blocking: "blocking";
@@ -265,7 +265,7 @@ interface Rule {
265
265
  /**
266
266
  * Rule ID type for type safety
267
267
  */
268
- type RuleId = "no-auto-layout" | "absolute-position-in-auto-layout" | "fixed-width-in-responsive-context" | "missing-responsive-behavior" | "group-usage" | "fixed-size-in-auto-layout" | "missing-min-width" | "missing-max-width" | "deep-nesting" | "overflow-hidden-abuse" | "inconsistent-sibling-layout-direction" | "raw-color" | "raw-font" | "inconsistent-spacing" | "magic-number-spacing" | "raw-shadow" | "raw-opacity" | "multiple-fill-colors" | "missing-component" | "detached-instance" | "nested-instance-override" | "variant-not-used" | "component-property-unused" | "single-use-component" | "default-name" | "non-semantic-name" | "inconsistent-naming-convention" | "numeric-suffix-name" | "too-long-name" | "ambiguous-structure" | "z-index-dependent-layout" | "missing-layout-hint" | "invisible-layer" | "empty-frame" | "hardcode-risk" | "text-truncation-unhandled" | "image-no-placeholder" | "prototype-link-in-design" | "no-dev-status";
268
+ type RuleId = "no-auto-layout" | "absolute-position-in-auto-layout" | "fixed-width-in-responsive-context" | "missing-responsive-behavior" | "group-usage" | "fixed-size-in-auto-layout" | "missing-min-width" | "missing-max-width" | "deep-nesting" | "overflow-hidden-abuse" | "inconsistent-sibling-layout-direction" | "raw-color" | "raw-font" | "inconsistent-spacing" | "magic-number-spacing" | "raw-shadow" | "raw-opacity" | "multiple-fill-colors" | "missing-component" | "detached-instance" | "nested-instance-override" | "variant-not-used" | "component-property-unused" | "single-use-component" | "missing-component-description" | "default-name" | "non-semantic-name" | "inconsistent-naming-convention" | "numeric-suffix-name" | "too-long-name" | "ambiguous-structure" | "z-index-dependent-layout" | "missing-layout-hint" | "invisible-layer" | "empty-frame" | "hardcode-risk" | "text-truncation-unhandled" | "image-no-placeholder" | "prototype-link-in-design" | "no-dev-status";
269
269
  /**
270
270
  * Categories that support depthWeight
271
271
  */
@@ -590,6 +590,13 @@ declare const nestedInstanceOverride: Rule;
590
590
  declare const variantNotUsed: Rule;
591
591
  declare const componentPropertyUnused: Rule;
592
592
  declare const singleUseComponent: Rule;
593
+ declare const missingComponentDescription: Rule;
594
+ /**
595
+ * Reset deduplication state between analysis runs.
596
+ * Call this at the start of each analysis if the process is long-running
597
+ * (e.g. MCP server mode).
598
+ */
599
+ declare function resetMissingComponentDescriptionState(): void;
593
600
 
594
601
  declare const defaultName: Rule;
595
602
  declare const nonSemanticName: Rule;
@@ -621,6 +628,16 @@ declare class FigmaUrlParseError extends Error {
621
628
  }
622
629
  declare function buildFigmaDeepLink(fileKey: string, nodeId: string): string;
623
630
 
631
+ interface GetFileNodesResponse {
632
+ name: string;
633
+ lastModified: string;
634
+ version: string;
635
+ nodes: Record<string, {
636
+ document: Node;
637
+ components: GetFileResponse["components"];
638
+ styles: GetFileResponse["styles"];
639
+ }>;
640
+ }
624
641
  interface FigmaClientOptions {
625
642
  token: string;
626
643
  }
@@ -641,7 +658,7 @@ declare class FigmaClient {
641
658
  * Download an image URL and return as base64
642
659
  */
643
660
  fetchImageAsBase64(imageUrl: string): Promise<string>;
644
- getFileNodes(fileKey: string, nodeIds: string[]): Promise<GetFileResponse>;
661
+ getFileNodes(fileKey: string, nodeIds: string[]): Promise<GetFileNodesResponse>;
645
662
  }
646
663
  declare class FigmaClientError extends Error {
647
664
  statusCode?: number | undefined;
@@ -653,6 +670,11 @@ declare class FigmaClientError extends Error {
653
670
  * Transform Figma API response to analysis types
654
671
  */
655
672
  declare function transformFigmaResponse(fileKey: string, response: GetFileResponse): AnalysisFile;
673
+ /**
674
+ * Transform Figma /v1/files/{key}/nodes response to analysis types.
675
+ * Returns the first node's subtree as the document.
676
+ */
677
+ declare function transformFileNodesResponse(fileKey: string, response: GetFileNodesResponse): AnalysisFile;
656
678
 
657
679
  /**
658
680
  * Load Figma data from a JSON file
@@ -1133,4 +1155,4 @@ declare class ActivityLogger {
1133
1155
  getLogPath(): string;
1134
1156
  }
1135
1157
 
1136
- export { ActivityLogger, type AnalysisAgentInput, type AnalysisAgentOutput, type AnalysisFile, AnalysisFileSchema, type AnalysisIssue, type AnalysisNode, AnalysisNodeSchema, type AnalysisNodeType, AnalysisNodeTypeSchema, type AnalysisResult, CATEGORIES, CATEGORY_LABELS, type CalibrationConfig, CalibrationConfigSchema, type CalibrationRun, type CalibrationStatus, CalibrationStatusSchema, type Category, CategorySchema, type CategoryScore, type CategoryScoreResult, CategoryScoreSchema, type Confidence, ConfidenceSchema, type ConversionAgentInput, type ConversionAgentOutput, type ConversionExecutor, type ConversionExecutorResult, type ConversionRecord, ConversionRecordSchema, DEPTH_WEIGHT_CATEGORIES, type Difficulty, DifficultySchema, type EvaluationAgentInput, type EvaluationAgentOutput, FigmaClient, FigmaClientError, type FigmaClientOptions, FigmaFileLoadError, type FigmaUrlInfo, FigmaUrlInfoSchema, FigmaUrlParseError, type Grade, type Issue, IssueSchema, type LayoutAlign, LayoutAlignSchema, type LayoutMode, LayoutModeSchema, type LayoutPositioning, LayoutPositioningSchema, type MismatchCase, MismatchCaseSchema, type MismatchType, MismatchTypeSchema, type NewRuleProposal, NewRuleProposalSchema, type NodeIssueDetail, type NodeIssueSummary, NodeIssueSummarySchema, type Preset, RULE_CONFIGS, type Report, type ReportMetadata, ReportMetadataSchema, ReportSchema, type Rule, type RuleCheckFn, type RuleConfig, RuleConfigSchema, type RuleContext, type RuleDefinition, RuleDefinitionSchema, RuleEngine, type RuleEngineOptions, type RuleId, type RuleRelatedStruggle, RuleRelatedStruggleSchema, type RuleViolation, SEVERITY_LABELS, SEVERITY_WEIGHT, type SamplingStrategy, SamplingStrategySchema, type ScoreAdjustment, ScoreAdjustmentSchema, type ScoreReport, type Severity, SeveritySchema, type TuningAgentInput, type TuningAgentOutput, type UncoveredStruggle, UncoveredStruggleSchema, version as VERSION, absolutePositionInAutoLayout, ambiguousStructure, analyzeFile, buildConversionPrompt, buildFigmaDeepLink, buildResultJson, calculateScores, componentPropertyUnused, createRuleEngine, deepNesting, defaultName, defineRule, detachedInstance, emptyFrame, enrichWithDesignContext, extractRuleScores, fixedSizeInAutoLayout, fixedWidthInResponsiveContext, formatScoreSummary, generateCalibrationReport, getCategoryLabel, getConfigsWithPreset, getRuleOption, getSeverityLabel, gradeToClassName, groupUsage, hardcodeRisk, imageNoPlaceholder, inconsistentNamingConvention, inconsistentSiblingLayoutDirection, inconsistentSpacing, invisibleLayer, loadFigmaFileFromJson, magicNumberSpacing, missingComponent, missingLayoutHint, missingMaxWidth, missingMinWidth, missingResponsiveBehavior, multipleFillColors, nestedInstanceOverride, noAutoLayout, noDevStatus, nonSemanticName, numericSuffixName, overflowHiddenAbuse, parseFigmaJson, parseFigmaUrl, parseMcpMetadataXml, prototypeLinkInDesign, rawColor, rawFont, rawOpacity, rawShadow, ruleRegistry, runAnalysisAgent, runCalibration, runCalibrationAnalyze, runCalibrationEvaluate, runConversionAgent, runEvaluationAgent, runTuningAgent, singleUseComponent, supportsDepthWeight, textTruncationUnhandled, tooLongName, transformFigmaResponse, variantNotUsed, zIndexDependentLayout };
1158
+ export { ActivityLogger, type AnalysisAgentInput, type AnalysisAgentOutput, type AnalysisFile, AnalysisFileSchema, type AnalysisIssue, type AnalysisNode, AnalysisNodeSchema, type AnalysisNodeType, AnalysisNodeTypeSchema, type AnalysisResult, CATEGORIES, CATEGORY_LABELS, type CalibrationConfig, CalibrationConfigSchema, type CalibrationRun, type CalibrationStatus, CalibrationStatusSchema, type Category, CategorySchema, type CategoryScore, type CategoryScoreResult, CategoryScoreSchema, type Confidence, ConfidenceSchema, type ConversionAgentInput, type ConversionAgentOutput, type ConversionExecutor, type ConversionExecutorResult, type ConversionRecord, ConversionRecordSchema, DEPTH_WEIGHT_CATEGORIES, type Difficulty, DifficultySchema, type EvaluationAgentInput, type EvaluationAgentOutput, FigmaClient, FigmaClientError, type FigmaClientOptions, FigmaFileLoadError, type FigmaUrlInfo, FigmaUrlInfoSchema, FigmaUrlParseError, type GetFileNodesResponse, type Grade, type Issue, IssueSchema, type LayoutAlign, LayoutAlignSchema, type LayoutMode, LayoutModeSchema, type LayoutPositioning, LayoutPositioningSchema, type MismatchCase, MismatchCaseSchema, type MismatchType, MismatchTypeSchema, type NewRuleProposal, NewRuleProposalSchema, type NodeIssueDetail, type NodeIssueSummary, NodeIssueSummarySchema, type Preset, RULE_CONFIGS, type Report, type ReportMetadata, ReportMetadataSchema, ReportSchema, type Rule, type RuleCheckFn, type RuleConfig, RuleConfigSchema, type RuleContext, type RuleDefinition, RuleDefinitionSchema, RuleEngine, type RuleEngineOptions, type RuleId, type RuleRelatedStruggle, RuleRelatedStruggleSchema, type RuleViolation, SEVERITY_LABELS, SEVERITY_WEIGHT, type SamplingStrategy, SamplingStrategySchema, type ScoreAdjustment, ScoreAdjustmentSchema, type ScoreReport, type Severity, SeveritySchema, type TuningAgentInput, type TuningAgentOutput, type UncoveredStruggle, UncoveredStruggleSchema, version as VERSION, absolutePositionInAutoLayout, ambiguousStructure, analyzeFile, buildConversionPrompt, buildFigmaDeepLink, buildResultJson, calculateScores, componentPropertyUnused, createRuleEngine, deepNesting, defaultName, defineRule, detachedInstance, emptyFrame, enrichWithDesignContext, extractRuleScores, fixedSizeInAutoLayout, fixedWidthInResponsiveContext, formatScoreSummary, generateCalibrationReport, getCategoryLabel, getConfigsWithPreset, getRuleOption, getSeverityLabel, gradeToClassName, groupUsage, hardcodeRisk, imageNoPlaceholder, inconsistentNamingConvention, inconsistentSiblingLayoutDirection, inconsistentSpacing, invisibleLayer, loadFigmaFileFromJson, magicNumberSpacing, missingComponent, missingComponentDescription, missingLayoutHint, missingMaxWidth, missingMinWidth, missingResponsiveBehavior, multipleFillColors, nestedInstanceOverride, noAutoLayout, noDevStatus, nonSemanticName, numericSuffixName, overflowHiddenAbuse, parseFigmaJson, parseFigmaUrl, parseMcpMetadataXml, prototypeLinkInDesign, rawColor, rawFont, rawOpacity, rawShadow, resetMissingComponentDescriptionState, ruleRegistry, runAnalysisAgent, runCalibration, runCalibrationAnalyze, runCalibrationEvaluate, runConversionAgent, runEvaluationAgent, runTuningAgent, singleUseComponent, supportsDepthWeight, textTruncationUnhandled, tooLongName, transformFigmaResponse, transformFileNodesResponse, variantNotUsed, zIndexDependentLayout };
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import { basename, resolve, dirname } from 'path';
4
4
  import { existsSync, mkdirSync } from 'fs';
5
5
 
6
6
  // package.json
7
- var version = "0.8.2";
7
+ var version = "0.8.5";
8
8
  var SeveritySchema = z.enum([
9
9
  "blocking",
10
10
  "risk",
@@ -317,7 +317,7 @@ var RULE_CONFIGS = {
317
317
  }
318
318
  },
319
319
  // ============================================
320
- // Component (6 rules)
320
+ // Component (7 rules)
321
321
  // ============================================
322
322
  "missing-component": {
323
323
  severity: "risk",
@@ -352,6 +352,11 @@ var RULE_CONFIGS = {
352
352
  score: -1,
353
353
  enabled: true
354
354
  },
355
+ "missing-component-description": {
356
+ severity: "missing-info",
357
+ score: -2,
358
+ enabled: true
359
+ },
355
360
  // ============================================
356
361
  // Naming (5 rules)
357
362
  // ============================================
@@ -1606,6 +1611,38 @@ var singleUseComponent = defineRule({
1606
1611
  definition: singleUseComponentDef,
1607
1612
  check: singleUseComponentCheck
1608
1613
  });
1614
+ var seenMissingDescriptionComponentIds = /* @__PURE__ */ new Set();
1615
+ var missingComponentDescriptionDef = {
1616
+ id: "missing-component-description",
1617
+ name: "Missing Component Description",
1618
+ category: "component",
1619
+ why: "Component descriptions in Figma are the primary channel for communicating intent, usage guidelines, and prop expectations to developers. Without them, developers must reverse-engineer purpose from visual appearance alone.",
1620
+ impact: "Increases implementation ambiguity, especially for icon-only components, compound components with multiple variants, and components whose names are variant key strings that give no prose context.",
1621
+ fix: "Open the component in Figma, select it, and add a description in the right-hand panel under the component's properties. Include: what the component is, when to use it, any accessibility or interaction notes, and the owning team or design token set if applicable."
1622
+ };
1623
+ var missingComponentDescriptionCheck = (node, context) => {
1624
+ if (node.type !== "INSTANCE") return null;
1625
+ const componentId = node.componentId;
1626
+ if (!componentId) return null;
1627
+ const componentMeta = context.file.components[componentId];
1628
+ if (!componentMeta) return null;
1629
+ if (componentMeta.description.trim() !== "") return null;
1630
+ if (seenMissingDescriptionComponentIds.has(componentId)) return null;
1631
+ seenMissingDescriptionComponentIds.add(componentId);
1632
+ return {
1633
+ ruleId: missingComponentDescriptionDef.id,
1634
+ nodeId: node.id,
1635
+ nodePath: context.path.join(" > "),
1636
+ message: `Component "${componentMeta.name}" has no description. Descriptions help developers understand purpose and usage.`
1637
+ };
1638
+ };
1639
+ var missingComponentDescription = defineRule({
1640
+ definition: missingComponentDescriptionDef,
1641
+ check: missingComponentDescriptionCheck
1642
+ });
1643
+ function resetMissingComponentDescriptionState() {
1644
+ seenMissingDescriptionComponentIds.clear();
1645
+ }
1609
1646
 
1610
1647
  // src/core/rules/naming/index.ts
1611
1648
  var DEFAULT_NAME_PATTERNS = [
@@ -2353,6 +2390,20 @@ function transformNode(node) {
2353
2390
  }
2354
2391
  return base;
2355
2392
  }
2393
+ function transformFileNodesResponse(fileKey, response) {
2394
+ const entries = Object.values(response.nodes);
2395
+ const first = entries[0];
2396
+ if (!first) throw new Error("No nodes returned from Figma API");
2397
+ return {
2398
+ fileKey,
2399
+ name: response.name,
2400
+ lastModified: response.lastModified,
2401
+ version: response.version,
2402
+ document: transformNode(first.document),
2403
+ components: transformComponents(first.components),
2404
+ styles: transformStyles(first.styles)
2405
+ };
2406
+ }
2356
2407
  function transformComponents(components) {
2357
2408
  const result = {};
2358
2409
  for (const [id, component] of Object.entries(components)) {
@@ -3567,13 +3618,19 @@ function hasTextDescendant(node) {
3567
3618
  }
3568
3619
  var MIN_WIDTH = 200;
3569
3620
  var MIN_HEIGHT = 200;
3621
+ var FILTER_THRESHOLD = 500;
3570
3622
  var ELIGIBLE_NODE_TYPES = /* @__PURE__ */ new Set([
3571
3623
  "FRAME",
3572
3624
  "COMPONENT",
3573
3625
  "INSTANCE"
3574
3626
  ]);
3575
3627
  function filterConversionCandidates(summaries, documentRoot) {
3576
- return summaries.filter((summary) => {
3628
+ const visibleSummaries = summaries.filter((summary) => {
3629
+ const node = findNode(documentRoot, summary.nodeId);
3630
+ return node ? node.visible !== false : false;
3631
+ });
3632
+ if (visibleSummaries.length <= FILTER_THRESHOLD) return visibleSummaries;
3633
+ return visibleSummaries.filter((summary) => {
3577
3634
  const node = findNode(documentRoot, summary.nodeId);
3578
3635
  if (!node) return false;
3579
3636
  if (EXCLUDED_NODE_TYPES.has(node.type)) return false;
@@ -3809,6 +3866,6 @@ async function runCalibration(config, executor, options) {
3809
3866
  }
3810
3867
  }
3811
3868
 
3812
- export { ActivityLogger, AnalysisFileSchema, AnalysisNodeSchema, AnalysisNodeTypeSchema, CATEGORIES, CATEGORY_LABELS, CalibrationConfigSchema, CalibrationStatusSchema, CategorySchema, CategoryScoreSchema, ConfidenceSchema, ConversionRecordSchema, DEPTH_WEIGHT_CATEGORIES, DifficultySchema, FigmaClient, FigmaClientError, FigmaFileLoadError, FigmaUrlInfoSchema, FigmaUrlParseError, IssueSchema, LayoutAlignSchema, LayoutModeSchema, LayoutPositioningSchema, MismatchCaseSchema, MismatchTypeSchema, NewRuleProposalSchema, NodeIssueSummarySchema, RULE_CONFIGS, ReportMetadataSchema, ReportSchema, RuleConfigSchema, RuleDefinitionSchema, RuleEngine, RuleRelatedStruggleSchema, SEVERITY_LABELS, SEVERITY_WEIGHT, SamplingStrategySchema, ScoreAdjustmentSchema, SeveritySchema, UncoveredStruggleSchema, version as VERSION, absolutePositionInAutoLayout, ambiguousStructure, analyzeFile, buildConversionPrompt, buildFigmaDeepLink, buildResultJson, calculateScores, componentPropertyUnused, createRuleEngine, deepNesting, defaultName, defineRule, detachedInstance, emptyFrame, enrichWithDesignContext, extractRuleScores, fixedSizeInAutoLayout, fixedWidthInResponsiveContext, formatScoreSummary, generateCalibrationReport, getCategoryLabel, getConfigsWithPreset, getRuleOption, getSeverityLabel, gradeToClassName, groupUsage, hardcodeRisk, imageNoPlaceholder, inconsistentNamingConvention, inconsistentSiblingLayoutDirection, inconsistentSpacing, invisibleLayer, loadFigmaFileFromJson, magicNumberSpacing, missingComponent, missingLayoutHint, missingMaxWidth, missingMinWidth, missingResponsiveBehavior, multipleFillColors, nestedInstanceOverride, noAutoLayout, noDevStatus, nonSemanticName, numericSuffixName, overflowHiddenAbuse, parseFigmaJson, parseFigmaUrl, parseMcpMetadataXml, prototypeLinkInDesign, rawColor, rawFont, rawOpacity, rawShadow, ruleRegistry, runAnalysisAgent, runCalibration, runCalibrationAnalyze, runCalibrationEvaluate, runConversionAgent, runEvaluationAgent, runTuningAgent, singleUseComponent, supportsDepthWeight, textTruncationUnhandled, tooLongName, transformFigmaResponse, variantNotUsed, zIndexDependentLayout };
3869
+ export { ActivityLogger, AnalysisFileSchema, AnalysisNodeSchema, AnalysisNodeTypeSchema, CATEGORIES, CATEGORY_LABELS, CalibrationConfigSchema, CalibrationStatusSchema, CategorySchema, CategoryScoreSchema, ConfidenceSchema, ConversionRecordSchema, DEPTH_WEIGHT_CATEGORIES, DifficultySchema, FigmaClient, FigmaClientError, FigmaFileLoadError, FigmaUrlInfoSchema, FigmaUrlParseError, IssueSchema, LayoutAlignSchema, LayoutModeSchema, LayoutPositioningSchema, MismatchCaseSchema, MismatchTypeSchema, NewRuleProposalSchema, NodeIssueSummarySchema, RULE_CONFIGS, ReportMetadataSchema, ReportSchema, RuleConfigSchema, RuleDefinitionSchema, RuleEngine, RuleRelatedStruggleSchema, SEVERITY_LABELS, SEVERITY_WEIGHT, SamplingStrategySchema, ScoreAdjustmentSchema, SeveritySchema, UncoveredStruggleSchema, version as VERSION, absolutePositionInAutoLayout, ambiguousStructure, analyzeFile, buildConversionPrompt, buildFigmaDeepLink, buildResultJson, calculateScores, componentPropertyUnused, createRuleEngine, deepNesting, defaultName, defineRule, detachedInstance, emptyFrame, enrichWithDesignContext, extractRuleScores, fixedSizeInAutoLayout, fixedWidthInResponsiveContext, formatScoreSummary, generateCalibrationReport, getCategoryLabel, getConfigsWithPreset, getRuleOption, getSeverityLabel, gradeToClassName, groupUsage, hardcodeRisk, imageNoPlaceholder, inconsistentNamingConvention, inconsistentSiblingLayoutDirection, inconsistentSpacing, invisibleLayer, loadFigmaFileFromJson, magicNumberSpacing, missingComponent, missingComponentDescription, missingLayoutHint, missingMaxWidth, missingMinWidth, missingResponsiveBehavior, multipleFillColors, nestedInstanceOverride, noAutoLayout, noDevStatus, nonSemanticName, numericSuffixName, overflowHiddenAbuse, parseFigmaJson, parseFigmaUrl, parseMcpMetadataXml, prototypeLinkInDesign, rawColor, rawFont, rawOpacity, rawShadow, resetMissingComponentDescriptionState, ruleRegistry, runAnalysisAgent, runCalibration, runCalibrationAnalyze, runCalibrationEvaluate, runConversionAgent, runEvaluationAgent, runTuningAgent, singleUseComponent, supportsDepthWeight, textTruncationUnhandled, tooLongName, transformFigmaResponse, transformFileNodesResponse, variantNotUsed, zIndexDependentLayout };
3813
3870
  //# sourceMappingURL=index.js.map
3814
3871
  //# sourceMappingURL=index.js.map