canicode 0.10.2 → 0.10.4

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
2
  import { GetFileResponse, Node } from '@figma/rest-api-spec';
3
3
 
4
- var version = "0.10.2";
4
+ var version = "0.10.4";
5
5
 
6
6
  declare const SeveritySchema: z.ZodEnum<{
7
7
  blocking: "blocking";
@@ -652,8 +652,163 @@ declare const GotchaSurveyQuestionSchema: z.ZodObject<{
652
652
  suggestedName: z.ZodOptional<z.ZodString>;
653
653
  isInstanceChild: z.ZodBoolean;
654
654
  sourceChildId: z.ZodOptional<z.ZodString>;
655
+ replicas: z.ZodOptional<z.ZodNumber>;
656
+ replicaNodeIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
655
657
  }, z.core.$strip>;
656
658
  type GotchaSurveyQuestion = z.infer<typeof GotchaSurveyQuestionSchema>;
659
+ /**
660
+ * Pre-grouped+batched view of `questions` (#369, #370, #381). Decoupled from
661
+ * `questions` so consumers that just want the flat list can ignore it; SKILLs
662
+ * (`canicode-gotchas`, `canicode-roundtrip`) iterate over `groupedQuestions`
663
+ * to keep sort / partition / batchable-rule decisions out of the LLM's
664
+ * prose interpretation, per ADR-016.
665
+ *
666
+ * Generated by `groupAndBatchSurveyQuestions` in `core/gotcha/`. Sort key is
667
+ * `(sourceComponentId ?? "_no-source", ruleId, nodeName)`. Source-component
668
+ * groups are emitted first; the trailing group with `instanceContext: null`
669
+ * carries every non-instance question.
670
+ */
671
+ declare const SurveyQuestionBatchSchema: z.ZodObject<{
672
+ ruleId: z.ZodString;
673
+ batchable: z.ZodBoolean;
674
+ questions: z.ZodArray<z.ZodObject<{
675
+ nodeId: z.ZodString;
676
+ nodeName: z.ZodString;
677
+ ruleId: z.ZodString;
678
+ severity: z.ZodEnum<{
679
+ blocking: "blocking";
680
+ risk: "risk";
681
+ "missing-info": "missing-info";
682
+ suggestion: "suggestion";
683
+ }>;
684
+ question: z.ZodString;
685
+ hint: z.ZodString;
686
+ example: z.ZodString;
687
+ instanceContext: z.ZodOptional<z.ZodObject<{
688
+ parentInstanceNodeId: z.ZodString;
689
+ sourceNodeId: z.ZodString;
690
+ sourceComponentId: z.ZodOptional<z.ZodString>;
691
+ sourceComponentName: z.ZodOptional<z.ZodString>;
692
+ }, z.core.$strip>>;
693
+ applyStrategy: z.ZodEnum<{
694
+ "property-mod": "property-mod";
695
+ "structural-mod": "structural-mod";
696
+ annotation: "annotation";
697
+ "auto-fix": "auto-fix";
698
+ }>;
699
+ targetProperty: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
700
+ annotationProperties: z.ZodOptional<z.ZodArray<z.ZodObject<{
701
+ type: z.ZodString;
702
+ }, z.core.$strip>>>;
703
+ suggestedName: z.ZodOptional<z.ZodString>;
704
+ isInstanceChild: z.ZodBoolean;
705
+ sourceChildId: z.ZodOptional<z.ZodString>;
706
+ replicas: z.ZodOptional<z.ZodNumber>;
707
+ replicaNodeIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
708
+ }, z.core.$strip>>;
709
+ totalScenes: z.ZodNumber;
710
+ }, z.core.$strip>;
711
+ type SurveyQuestionBatch = z.infer<typeof SurveyQuestionBatchSchema>;
712
+ declare const SurveyQuestionGroupSchema: z.ZodObject<{
713
+ instanceContext: z.ZodNullable<z.ZodObject<{
714
+ parentInstanceNodeId: z.ZodString;
715
+ sourceNodeId: z.ZodString;
716
+ sourceComponentId: z.ZodOptional<z.ZodString>;
717
+ sourceComponentName: z.ZodOptional<z.ZodString>;
718
+ }, z.core.$strip>>;
719
+ batches: z.ZodArray<z.ZodObject<{
720
+ ruleId: z.ZodString;
721
+ batchable: z.ZodBoolean;
722
+ questions: z.ZodArray<z.ZodObject<{
723
+ nodeId: z.ZodString;
724
+ nodeName: z.ZodString;
725
+ ruleId: z.ZodString;
726
+ severity: z.ZodEnum<{
727
+ blocking: "blocking";
728
+ risk: "risk";
729
+ "missing-info": "missing-info";
730
+ suggestion: "suggestion";
731
+ }>;
732
+ question: z.ZodString;
733
+ hint: z.ZodString;
734
+ example: z.ZodString;
735
+ instanceContext: z.ZodOptional<z.ZodObject<{
736
+ parentInstanceNodeId: z.ZodString;
737
+ sourceNodeId: z.ZodString;
738
+ sourceComponentId: z.ZodOptional<z.ZodString>;
739
+ sourceComponentName: z.ZodOptional<z.ZodString>;
740
+ }, z.core.$strip>>;
741
+ applyStrategy: z.ZodEnum<{
742
+ "property-mod": "property-mod";
743
+ "structural-mod": "structural-mod";
744
+ annotation: "annotation";
745
+ "auto-fix": "auto-fix";
746
+ }>;
747
+ targetProperty: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
748
+ annotationProperties: z.ZodOptional<z.ZodArray<z.ZodObject<{
749
+ type: z.ZodString;
750
+ }, z.core.$strip>>>;
751
+ suggestedName: z.ZodOptional<z.ZodString>;
752
+ isInstanceChild: z.ZodBoolean;
753
+ sourceChildId: z.ZodOptional<z.ZodString>;
754
+ replicas: z.ZodOptional<z.ZodNumber>;
755
+ replicaNodeIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
756
+ }, z.core.$strip>>;
757
+ totalScenes: z.ZodNumber;
758
+ }, z.core.$strip>>;
759
+ }, z.core.$strip>;
760
+ type SurveyQuestionGroup = z.infer<typeof SurveyQuestionGroupSchema>;
761
+ declare const GroupedSurveySchema: z.ZodObject<{
762
+ groups: z.ZodArray<z.ZodObject<{
763
+ instanceContext: z.ZodNullable<z.ZodObject<{
764
+ parentInstanceNodeId: z.ZodString;
765
+ sourceNodeId: z.ZodString;
766
+ sourceComponentId: z.ZodOptional<z.ZodString>;
767
+ sourceComponentName: z.ZodOptional<z.ZodString>;
768
+ }, z.core.$strip>>;
769
+ batches: z.ZodArray<z.ZodObject<{
770
+ ruleId: z.ZodString;
771
+ batchable: z.ZodBoolean;
772
+ questions: z.ZodArray<z.ZodObject<{
773
+ nodeId: z.ZodString;
774
+ nodeName: z.ZodString;
775
+ ruleId: z.ZodString;
776
+ severity: z.ZodEnum<{
777
+ blocking: "blocking";
778
+ risk: "risk";
779
+ "missing-info": "missing-info";
780
+ suggestion: "suggestion";
781
+ }>;
782
+ question: z.ZodString;
783
+ hint: z.ZodString;
784
+ example: z.ZodString;
785
+ instanceContext: z.ZodOptional<z.ZodObject<{
786
+ parentInstanceNodeId: z.ZodString;
787
+ sourceNodeId: z.ZodString;
788
+ sourceComponentId: z.ZodOptional<z.ZodString>;
789
+ sourceComponentName: z.ZodOptional<z.ZodString>;
790
+ }, z.core.$strip>>;
791
+ applyStrategy: z.ZodEnum<{
792
+ "property-mod": "property-mod";
793
+ "structural-mod": "structural-mod";
794
+ annotation: "annotation";
795
+ "auto-fix": "auto-fix";
796
+ }>;
797
+ targetProperty: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
798
+ annotationProperties: z.ZodOptional<z.ZodArray<z.ZodObject<{
799
+ type: z.ZodString;
800
+ }, z.core.$strip>>>;
801
+ suggestedName: z.ZodOptional<z.ZodString>;
802
+ isInstanceChild: z.ZodBoolean;
803
+ sourceChildId: z.ZodOptional<z.ZodString>;
804
+ replicas: z.ZodOptional<z.ZodNumber>;
805
+ replicaNodeIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
806
+ }, z.core.$strip>>;
807
+ totalScenes: z.ZodNumber;
808
+ }, z.core.$strip>>;
809
+ }, z.core.$strip>>;
810
+ }, z.core.$strip>;
811
+ type GroupedSurvey = z.infer<typeof GroupedSurveySchema>;
657
812
  declare const GotchaSurveySchema: z.ZodObject<{
658
813
  designGrade: z.ZodEnum<{
659
814
  S: "S";
@@ -699,12 +854,95 @@ declare const GotchaSurveySchema: z.ZodObject<{
699
854
  suggestedName: z.ZodOptional<z.ZodString>;
700
855
  isInstanceChild: z.ZodBoolean;
701
856
  sourceChildId: z.ZodOptional<z.ZodString>;
857
+ replicas: z.ZodOptional<z.ZodNumber>;
858
+ replicaNodeIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
702
859
  }, z.core.$strip>>;
860
+ groupedQuestions: z.ZodObject<{
861
+ groups: z.ZodArray<z.ZodObject<{
862
+ instanceContext: z.ZodNullable<z.ZodObject<{
863
+ parentInstanceNodeId: z.ZodString;
864
+ sourceNodeId: z.ZodString;
865
+ sourceComponentId: z.ZodOptional<z.ZodString>;
866
+ sourceComponentName: z.ZodOptional<z.ZodString>;
867
+ }, z.core.$strip>>;
868
+ batches: z.ZodArray<z.ZodObject<{
869
+ ruleId: z.ZodString;
870
+ batchable: z.ZodBoolean;
871
+ questions: z.ZodArray<z.ZodObject<{
872
+ nodeId: z.ZodString;
873
+ nodeName: z.ZodString;
874
+ ruleId: z.ZodString;
875
+ severity: z.ZodEnum<{
876
+ blocking: "blocking";
877
+ risk: "risk";
878
+ "missing-info": "missing-info";
879
+ suggestion: "suggestion";
880
+ }>;
881
+ question: z.ZodString;
882
+ hint: z.ZodString;
883
+ example: z.ZodString;
884
+ instanceContext: z.ZodOptional<z.ZodObject<{
885
+ parentInstanceNodeId: z.ZodString;
886
+ sourceNodeId: z.ZodString;
887
+ sourceComponentId: z.ZodOptional<z.ZodString>;
888
+ sourceComponentName: z.ZodOptional<z.ZodString>;
889
+ }, z.core.$strip>>;
890
+ applyStrategy: z.ZodEnum<{
891
+ "property-mod": "property-mod";
892
+ "structural-mod": "structural-mod";
893
+ annotation: "annotation";
894
+ "auto-fix": "auto-fix";
895
+ }>;
896
+ targetProperty: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodArray<z.ZodString>]>>;
897
+ annotationProperties: z.ZodOptional<z.ZodArray<z.ZodObject<{
898
+ type: z.ZodString;
899
+ }, z.core.$strip>>>;
900
+ suggestedName: z.ZodOptional<z.ZodString>;
901
+ isInstanceChild: z.ZodBoolean;
902
+ sourceChildId: z.ZodOptional<z.ZodString>;
903
+ replicas: z.ZodOptional<z.ZodNumber>;
904
+ replicaNodeIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
905
+ }, z.core.$strip>>;
906
+ totalScenes: z.ZodNumber;
907
+ }, z.core.$strip>>;
908
+ }, z.core.$strip>>;
909
+ }, z.core.$strip>;
910
+ designKey: z.ZodString;
703
911
  }, z.core.$strip>;
704
912
  type GotchaSurvey = z.infer<typeof GotchaSurveySchema>;
705
913
 
706
914
  /**
707
- * Analysis issue with calculated score and metadata
915
+ * Acknowledgment marker surfaced from a Figma Dev Mode annotation that
916
+ * canicode itself wrote during a roundtrip. When the analysis pipeline
917
+ * receives a list of acknowledgments, matching `(nodeId, ruleId)` issues are
918
+ * flagged `acknowledged: true` and contribute half their normal weight to
919
+ * the density score (#371).
920
+ *
921
+ * This contract is consumed by:
922
+ * - The MCP `analyze` tool (`acknowledgments?: Acknowledgment[]` input)
923
+ * - The CLI `analyze --acknowledgments <path>` flag
924
+ * - `RuleEngineOptions.acknowledgments`
925
+ *
926
+ * It is produced by the Plugin-API helper
927
+ * `extractAcknowledgmentsFromNode` / `readCanicodeAcknowledgments`
928
+ * (see `src/core/roundtrip/read-acknowledgments.ts`).
929
+ */
930
+ declare const AcknowledgmentSchema: z.ZodObject<{
931
+ nodeId: z.ZodString;
932
+ ruleId: z.ZodString;
933
+ }, z.core.$strip>;
934
+ type Acknowledgment = z.infer<typeof AcknowledgmentSchema>;
935
+
936
+ /**
937
+ * Analysis issue with calculated score and metadata.
938
+ *
939
+ * `acknowledged` (#371) — set when the analysis engine receives an
940
+ * `acknowledgments` list (typically read from canicode-authored Figma
941
+ * annotations) whose `(nodeId, ruleId)` matches this issue. Acknowledged
942
+ * issues still surface in the result (so code-gen retains the context) but
943
+ * contribute half their normal weight to the density score, so the grade
944
+ * reflects "the designer has a plan for this" instead of treating the rule
945
+ * as fully unaddressed.
708
946
  */
709
947
  interface AnalysisIssue {
710
948
  violation: RuleViolation;
@@ -713,6 +951,7 @@ interface AnalysisIssue {
713
951
  depth: number;
714
952
  maxDepth: number;
715
953
  calculatedScore: number;
954
+ acknowledged?: boolean;
716
955
  }
717
956
  /**
718
957
  * Information about a rule that threw during analysis
@@ -744,6 +983,14 @@ interface RuleEngineOptions {
744
983
  targetNodeId?: string;
745
984
  excludeNodeNames?: string[];
746
985
  excludeNodeTypes?: string[];
986
+ /**
987
+ * `(nodeId, ruleId)` pairs sourced from canicode-authored Figma annotations
988
+ * (#371). Issues whose violation matches an entry are flagged
989
+ * `acknowledged: true` and contribute half their normal weight to the
990
+ * density score in `calculateScores`. nodeId may be passed in URL form
991
+ * (`123-456`) or Plugin-API form (`123:456`) — both normalize to `:`.
992
+ */
993
+ acknowledgments?: Acknowledgment[];
747
994
  }
748
995
  /**
749
996
  * Rule engine for analyzing Figma files
@@ -755,6 +1002,7 @@ declare class RuleEngine {
755
1002
  private targetNodeId;
756
1003
  private excludeNamePattern;
757
1004
  private excludeNodeTypes;
1005
+ private acknowledgments;
758
1006
  constructor(options?: RuleEngineOptions);
759
1007
  /**
760
1008
  * Analyze a Figma file and return issues
@@ -811,6 +1059,15 @@ interface ScoreReport {
811
1059
  missingInfo: number;
812
1060
  suggestion: number;
813
1061
  nodeCount: number;
1062
+ /**
1063
+ * Number of issues marked `acknowledged` by an upstream
1064
+ * acknowledgments list (#371). Acknowledged issues are still counted in
1065
+ * `totalIssues` and the per-severity tallies — the count here is purely
1066
+ * additive context so consumers can render
1067
+ * `N issues — A acknowledged / N-A unaddressed`. They contribute half
1068
+ * weight to the density score upstream.
1069
+ */
1070
+ acknowledgedCount: number;
814
1071
  };
815
1072
  }
816
1073
  /**
@@ -858,6 +1115,7 @@ declare function getSeverityLabel(severity: Severity): string;
858
1115
  */
859
1116
  declare function buildResultJson(fileName: string, result: AnalysisResult, scores: ScoreReport, options?: {
860
1117
  fileKey?: string;
1118
+ designKey?: string;
861
1119
  }): Record<string, unknown>;
862
1120
 
863
1121
  /**
@@ -1849,4 +2107,4 @@ declare class ActivityLogger {
1849
2107
  getLogPath(): string;
1850
2108
  }
1851
2109
 
1852
- export { ALL_STRIP_TYPES, ActivityLogger, type AnalysisAgentInput, type AnalysisAgentOutput, type AnalysisFile, AnalysisFileSchema, type AnalysisIssue, type AnalysisNode, AnalysisNodeSchema, type AnalysisNodeType, AnalysisNodeTypeSchema, type AnalysisResult, AnnotationPropertySchema, CATEGORIES, CATEGORY_LABELS, type CalibrationConfig, type CalibrationConfigInput, CalibrationConfigSchema, type CalibrationRun, type CalibrationStatus, CalibrationStatusSchema, type Category, CategorySchema, type CategoryScore, type CategoryScoreResult, CategoryScoreSchema, type Confidence, ConfidenceSchema, type ConversionRecord, ConversionRecordSchema, DEPTH_WEIGHT_CATEGORIES, DESIGN_TREE_INFO_TYPES, type DesignTreeInfoType, type DesignTreeOptions, type DesignTreeResult, type DesignTreeStripType, type Difficulty, DifficultySchema, type EvaluationAgentInput, type EvaluationAgentOutput, FigmaClient, FigmaClientError, type FigmaClientOptions, FigmaFileLoadError, type FigmaUrlInfo, FigmaUrlInfoSchema, FigmaUrlParseError, type GapAnalyzerOutput, GapAnalyzerOutputSchema, type GapEntry, GapEntrySchema, type GetFileNodesResponse, type GotchaApplyResolution, type GotchaApplyStrategy, type GotchaSurvey, type GotchaSurveyQuestion, GotchaSurveyQuestionSchema, GotchaSurveySchema, type Grade, type GridChildAlign, GridChildAlignSchema, type InstanceChildIdParts, type InstanceContext, InstanceContextSchema, type Issue, IssueSchema, type LayoutAlign, LayoutAlignSchema, type LayoutConstraint, LayoutConstraintSchema, type LayoutMode, LayoutModeSchema, type LayoutPositioning, LayoutPositioningSchema, type LayoutWrap, LayoutWrapSchema, type McpAnalyzeResponse, McpAnalyzeResponseSchema, type MismatchCase, MismatchCaseSchema, type MismatchType, MismatchTypeSchema, type NewRuleProposal, NewRuleProposalSchema, type NodeIssueDetail, type NodeIssueSummary, NodeIssueSummarySchema, type OverflowDirection, OverflowDirectionSchema, type Preset, RULE_ANNOTATION_PROPERTIES, RULE_CONFIGS, RULE_ID_CATEGORY, type Report, type ReportMetadata, ReportMetadataSchema, ReportSchema, type Rule, type RuleApplyStrategy, RuleApplyStrategySchema, type RuleCheckFn, type RuleConfig, RuleConfigSchema, type RuleContext, type RuleDefinition, RuleDefinitionSchema, RuleEngine, type RuleEngineOptions, type RuleFailure, type RuleId, RuleImpactAssessmentSchema, type RuleRelatedStruggle, RuleRelatedStruggleSchema, type RuleViolation, SEVERITY_LABELS, SEVERITY_WEIGHT, type SamplingStrategy, SamplingStrategySchema, type ScoreAdjustment, ScoreAdjustmentSchema, type ScoreReport, type Severity, SeveritySchema, type StripDeltaForEval, type StripDeltaResult, StripDeltaResultSchema, StripDeltasArraySchema, StripTypeEnum, type TuningAgentInput, type TuningAgentOutput, type UncoveredStruggle, UncoveredStruggleSchema, UncoveredStrugglesInputSchema, version as VERSION, type VisualCompareCliOptions, VisualCompareCliOptionsSchema, absolutePositionInAutoLayout, analyzeFile, buildFigmaDeepLink, buildResultJson, calculateScores, collectComponentIds, collectInteractionDestinationIds, createRuleEngine, deepNesting, defineRule, detachedInstance, extractRuleScores, fixedSizeInAutoLayout, formatScoreSummary, generateCalibrationReport, generateDesignTree, generateDesignTreeWithStats, getAnalysisState, getAnnotationProperties, getCategoryLabel, getConfigsWithPreset, getRuleOption, getSeverityLabel, gradeToClassName, inconsistentNamingConvention, irregularSpacing, isInstanceChildNodeId, isReadyForCodeGen, loadFigmaFileFromJson, missingComponent, missingInteractionState, missingPrototype, missingSizeConstraint, noAutoLayout, nonLayoutContainer, nonSemanticName, nonStandardNaming, parseFigmaJson, parseFigmaUrl, parseInstanceChildNodeId, rawValue, resolveComponentDefinitions, resolveGotchaApplyTarget, resolveInteractionDestinations, ruleRegistry, runAnalysisAgent, runCalibrationAnalyze, runCalibrationEvaluate, runEvaluationAgent, runTuningAgent, stripDeltaToDifficulty, stripDesignTree, supportsDepthWeight, toCommentableNodeId, tokenDeltaToDifficulty, transformComponentMasterNodes, transformFigmaResponse, transformFileNodesResponse, variantStructureMismatch };
2110
+ export { ALL_STRIP_TYPES, ActivityLogger, type AnalysisAgentInput, type AnalysisAgentOutput, type AnalysisFile, AnalysisFileSchema, type AnalysisIssue, type AnalysisNode, AnalysisNodeSchema, type AnalysisNodeType, AnalysisNodeTypeSchema, type AnalysisResult, AnnotationPropertySchema, CATEGORIES, CATEGORY_LABELS, type CalibrationConfig, type CalibrationConfigInput, CalibrationConfigSchema, type CalibrationRun, type CalibrationStatus, CalibrationStatusSchema, type Category, CategorySchema, type CategoryScore, type CategoryScoreResult, CategoryScoreSchema, type Confidence, ConfidenceSchema, type ConversionRecord, ConversionRecordSchema, DEPTH_WEIGHT_CATEGORIES, DESIGN_TREE_INFO_TYPES, type DesignTreeInfoType, type DesignTreeOptions, type DesignTreeResult, type DesignTreeStripType, type Difficulty, DifficultySchema, type EvaluationAgentInput, type EvaluationAgentOutput, FigmaClient, FigmaClientError, type FigmaClientOptions, FigmaFileLoadError, type FigmaUrlInfo, FigmaUrlInfoSchema, FigmaUrlParseError, type GapAnalyzerOutput, GapAnalyzerOutputSchema, type GapEntry, GapEntrySchema, type GetFileNodesResponse, type GotchaApplyResolution, type GotchaApplyStrategy, type GotchaSurvey, type GotchaSurveyQuestion, GotchaSurveyQuestionSchema, GotchaSurveySchema, type Grade, type GridChildAlign, GridChildAlignSchema, type GroupedSurvey, GroupedSurveySchema, type InstanceChildIdParts, type InstanceContext, InstanceContextSchema, type Issue, IssueSchema, type LayoutAlign, LayoutAlignSchema, type LayoutConstraint, LayoutConstraintSchema, type LayoutMode, LayoutModeSchema, type LayoutPositioning, LayoutPositioningSchema, type LayoutWrap, LayoutWrapSchema, type McpAnalyzeResponse, McpAnalyzeResponseSchema, type MismatchCase, MismatchCaseSchema, type MismatchType, MismatchTypeSchema, type NewRuleProposal, NewRuleProposalSchema, type NodeIssueDetail, type NodeIssueSummary, NodeIssueSummarySchema, type OverflowDirection, OverflowDirectionSchema, type Preset, RULE_ANNOTATION_PROPERTIES, RULE_CONFIGS, RULE_ID_CATEGORY, type Report, type ReportMetadata, ReportMetadataSchema, ReportSchema, type Rule, type RuleApplyStrategy, RuleApplyStrategySchema, type RuleCheckFn, type RuleConfig, RuleConfigSchema, type RuleContext, type RuleDefinition, RuleDefinitionSchema, RuleEngine, type RuleEngineOptions, type RuleFailure, type RuleId, RuleImpactAssessmentSchema, type RuleRelatedStruggle, RuleRelatedStruggleSchema, type RuleViolation, SEVERITY_LABELS, SEVERITY_WEIGHT, type SamplingStrategy, SamplingStrategySchema, type ScoreAdjustment, ScoreAdjustmentSchema, type ScoreReport, type Severity, SeveritySchema, type StripDeltaForEval, type StripDeltaResult, StripDeltaResultSchema, StripDeltasArraySchema, StripTypeEnum, type SurveyQuestionBatch, SurveyQuestionBatchSchema, type SurveyQuestionGroup, SurveyQuestionGroupSchema, type TuningAgentInput, type TuningAgentOutput, type UncoveredStruggle, UncoveredStruggleSchema, UncoveredStrugglesInputSchema, version as VERSION, type VisualCompareCliOptions, VisualCompareCliOptionsSchema, absolutePositionInAutoLayout, analyzeFile, buildFigmaDeepLink, buildResultJson, calculateScores, collectComponentIds, collectInteractionDestinationIds, createRuleEngine, deepNesting, defineRule, detachedInstance, extractRuleScores, fixedSizeInAutoLayout, formatScoreSummary, generateCalibrationReport, generateDesignTree, generateDesignTreeWithStats, getAnalysisState, getAnnotationProperties, getCategoryLabel, getConfigsWithPreset, getRuleOption, getSeverityLabel, gradeToClassName, inconsistentNamingConvention, irregularSpacing, isInstanceChildNodeId, isReadyForCodeGen, loadFigmaFileFromJson, missingComponent, missingInteractionState, missingPrototype, missingSizeConstraint, noAutoLayout, nonLayoutContainer, nonSemanticName, nonStandardNaming, parseFigmaJson, parseFigmaUrl, parseInstanceChildNodeId, rawValue, resolveComponentDefinitions, resolveGotchaApplyTarget, resolveInteractionDestinations, ruleRegistry, runAnalysisAgent, runCalibrationAnalyze, runCalibrationEvaluate, runEvaluationAgent, runTuningAgent, stripDeltaToDifficulty, stripDesignTree, supportsDepthWeight, toCommentableNodeId, tokenDeltaToDifficulty, transformComponentMasterNodes, transformFigmaResponse, transformFileNodesResponse, variantStructureMismatch };
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import 'crypto';
6
6
  import { homedir } from 'os';
7
7
 
8
8
  // package.json
9
- var version = "0.10.2";
9
+ var version = "0.10.4";
10
10
  var SeveritySchema = z.enum([
11
11
  "blocking",
12
12
  "risk",
@@ -353,12 +353,60 @@ var GotchaSurveyQuestionSchema = z.object({
353
353
  annotationProperties: z.array(AnnotationPropertySchema).optional(),
354
354
  suggestedName: z.string().optional(),
355
355
  isInstanceChild: z.boolean(),
356
- sourceChildId: z.string().optional()
356
+ sourceChildId: z.string().optional(),
357
+ // #356: when this question collapses N instance-child issues that share the
358
+ // same `(sourceComponentId, sourceNodeId, ruleId)` tuple, `replicas` is the
359
+ // total instance count (>=2) and `replicaNodeIds` lists every instance scene
360
+ // node id OTHER than the kept `nodeId`. Apply step iterates
361
+ // `[nodeId, ...replicaNodeIds]` so the same answer lands on every replica.
362
+ // Single-instance questions omit both fields.
363
+ replicas: z.number().int().min(2).optional(),
364
+ replicaNodeIds: z.array(z.string()).optional()
365
+ });
366
+ var SurveyQuestionBatchSchema = z.object({
367
+ ruleId: z.string(),
368
+ /**
369
+ * `true` when every member shares an answer-shape uniformly applicable to
370
+ * all of them (e.g. one `min-width` value covers all FILL children).
371
+ * The SKILL renders one shared prompt for `batchable: true` batches with
372
+ * `questions.length >= 2`; everything else falls through to the
373
+ * single-question template.
374
+ */
375
+ batchable: z.boolean(),
376
+ questions: z.array(GotchaSurveyQuestionSchema),
377
+ /**
378
+ * Sum of `max(question.replicas, 1)` across `questions`. Counts the
379
+ * actual Figma scene fan-out so the SKILL can render `N instances`
380
+ * accurately even when one batch member already collapses multiple
381
+ * replicas via the #356 source-component dedupe.
382
+ */
383
+ totalScenes: z.number().int().min(1)
384
+ });
385
+ var SurveyQuestionGroupSchema = z.object({
386
+ /**
387
+ * Shared `instanceContext` for the group, or `null` for the trailing
388
+ * non-instance group. The SKILL emits the verbose "Instance note" header
389
+ * once per non-null group instead of once per question (#370).
390
+ */
391
+ instanceContext: InstanceContextSchema.nullable(),
392
+ batches: z.array(SurveyQuestionBatchSchema)
393
+ });
394
+ var GroupedSurveySchema = z.object({
395
+ groups: z.array(SurveyQuestionGroupSchema)
357
396
  });
358
397
  var GotchaSurveySchema = z.object({
359
398
  designGrade: GradeSchema2,
360
399
  isReadyForCodeGen: z.boolean(),
361
- questions: z.array(GotchaSurveyQuestionSchema)
400
+ questions: z.array(GotchaSurveyQuestionSchema),
401
+ groupedQuestions: GroupedSurveySchema,
402
+ /**
403
+ * #384 — canonical identifier for this design across canicode runs.
404
+ * Computed by `computeDesignKey(input)` (`<fileKey>#<nodeId>` for Figma
405
+ * URLs, absolute path for fixtures). The `canicode-gotchas` SKILL reads
406
+ * this directly when upserting the per-design section, so the SKILL.md
407
+ * prose no longer parses URLs (per ADR-016).
408
+ */
409
+ designKey: z.string()
362
410
  });
363
411
 
364
412
  // src/core/rules/rule-config.ts
@@ -642,6 +690,14 @@ function defineRule(rule) {
642
690
  ruleRegistry.register(rule);
643
691
  return rule;
644
692
  }
693
+ var AcknowledgmentSchema = z.object({
694
+ nodeId: z.string(),
695
+ ruleId: z.string()
696
+ });
697
+ z.array(AcknowledgmentSchema);
698
+ function normalizeNodeId(id) {
699
+ return id.replace(/-/g, ":");
700
+ }
645
701
 
646
702
  // src/core/engine/rule-engine.ts
647
703
  function calculateMaxDepth(node, currentDepth = 0) {
@@ -692,6 +748,7 @@ var RuleEngine = class {
692
748
  targetNodeId;
693
749
  excludeNamePattern;
694
750
  excludeNodeTypes;
751
+ acknowledgments;
695
752
  constructor(options = {}) {
696
753
  this.configs = options.configs ?? RULE_CONFIGS;
697
754
  this.enabledRuleIds = options.enabledRules ? new Set(options.enabledRules) : null;
@@ -699,6 +756,11 @@ var RuleEngine = class {
699
756
  this.targetNodeId = options.targetNodeId;
700
757
  this.excludeNamePattern = options.excludeNodeNames && options.excludeNodeNames.length > 0 ? new RegExp(`\\b(${options.excludeNodeNames.join("|")})\\b`, "i") : null;
701
758
  this.excludeNodeTypes = options.excludeNodeTypes && options.excludeNodeTypes.length > 0 ? new Set(options.excludeNodeTypes) : null;
759
+ this.acknowledgments = new Set(
760
+ (options.acknowledgments ?? []).map(
761
+ (a) => `${normalizeNodeId(a.nodeId)}::${a.ruleId}`
762
+ )
763
+ );
702
764
  }
703
765
  /**
704
766
  * Analyze a Figma file and return issues
@@ -733,6 +795,14 @@ var RuleEngine = class {
733
795
  void 0,
734
796
  void 0
735
797
  );
798
+ if (this.acknowledgments.size > 0) {
799
+ for (const issue of issues) {
800
+ const key = `${normalizeNodeId(issue.violation.nodeId)}::${issue.violation.ruleId}`;
801
+ if (this.acknowledgments.has(key)) {
802
+ issue.acknowledged = true;
803
+ }
804
+ }
805
+ }
736
806
  return {
737
807
  file,
738
808
  issues,
@@ -883,8 +953,6 @@ function resolveTargetProperty(ruleId, subType) {
883
953
  if (subType === "horizontal") return "layoutSizingHorizontal";
884
954
  return ["layoutSizingHorizontal", "layoutSizingVertical"];
885
955
  case "missing-size-constraint":
886
- if (subType === "wrap") return "minWidth";
887
- if (subType === "max-width") return "maxWidth";
888
956
  return ["minWidth", "maxWidth"];
889
957
  case "irregular-spacing":
890
958
  if (subType === "gap") return "itemSpacing";
@@ -984,7 +1052,8 @@ function calculateScores(result, configs) {
984
1052
  uniqueRulesPerCategory.get(category).add(ruleId);
985
1053
  ruleScorePerCategory.get(category).set(ruleId, Math.abs(issue.config.score));
986
1054
  const ruleCountMap = ruleIssueCountPerCategory.get(category);
987
- ruleCountMap.set(ruleId, (ruleCountMap.get(ruleId) ?? 0) + 1);
1055
+ const weight = issue.acknowledged === true ? 0.5 : 1;
1056
+ ruleCountMap.set(ruleId, (ruleCountMap.get(ruleId) ?? 0) + weight);
988
1057
  }
989
1058
  for (const category of CATEGORIES) {
990
1059
  const ruleCountMap = ruleIssueCountPerCategory.get(category);
@@ -1031,7 +1100,8 @@ function calculateScores(result, configs) {
1031
1100
  risk: 0,
1032
1101
  missingInfo: 0,
1033
1102
  suggestion: 0,
1034
- nodeCount
1103
+ nodeCount,
1104
+ acknowledgedCount: 0
1035
1105
  };
1036
1106
  for (const issue of result.issues) {
1037
1107
  switch (issue.config.severity) {
@@ -1048,6 +1118,7 @@ function calculateScores(result, configs) {
1048
1118
  summary.suggestion++;
1049
1119
  break;
1050
1120
  }
1121
+ if (issue.acknowledged === true) summary.acknowledgedCount++;
1051
1122
  }
1052
1123
  return {
1053
1124
  overall: {
@@ -1098,7 +1169,14 @@ function formatScoreSummary(report) {
1098
1169
  lines.push(` Risk: ${report.summary.risk}`);
1099
1170
  lines.push(` Missing Info: ${report.summary.missingInfo}`);
1100
1171
  lines.push(` Suggestion: ${report.summary.suggestion}`);
1101
- lines.push(` Total: ${report.summary.totalIssues}`);
1172
+ if (report.summary.acknowledgedCount > 0) {
1173
+ const unaddressed = report.summary.totalIssues - report.summary.acknowledgedCount;
1174
+ lines.push(
1175
+ ` Total: ${report.summary.totalIssues} (${report.summary.acknowledgedCount} acknowledged via canicode annotations / ${unaddressed} unaddressed)`
1176
+ );
1177
+ } else {
1178
+ lines.push(` Total: ${report.summary.totalIssues}`);
1179
+ }
1102
1180
  return lines.join("\n");
1103
1181
  }
1104
1182
  function getCategoryLabel(category) {
@@ -1134,17 +1212,20 @@ function buildResultJson(fileName, result, scores, options) {
1134
1212
  ...applyContext.annotationProperties !== void 0 ? { annotationProperties: applyContext.annotationProperties } : {},
1135
1213
  ...suggestedName !== void 0 ? { suggestedName } : {},
1136
1214
  isInstanceChild: applyContext.isInstanceChild,
1137
- ...applyContext.sourceChildId !== void 0 ? { sourceChildId: applyContext.sourceChildId } : {}
1215
+ ...applyContext.sourceChildId !== void 0 ? { sourceChildId: applyContext.sourceChildId } : {},
1216
+ ...issue.acknowledged === true ? { acknowledged: true } : {}
1138
1217
  };
1139
1218
  });
1140
1219
  const json = {
1141
1220
  version,
1142
1221
  analyzedAt: result.analyzedAt,
1143
1222
  ...options?.fileKey && { fileKey: options.fileKey },
1223
+ ...options?.designKey && { designKey: options.designKey },
1144
1224
  fileName,
1145
1225
  nodeCount: result.nodeCount,
1146
1226
  maxDepth: result.maxDepth,
1147
1227
  issueCount: result.issues.length,
1228
+ acknowledgedCount: scores.summary.acknowledgedCount,
1148
1229
  isReadyForCodeGen: isReadyForCodeGen(scores.overall.grade),
1149
1230
  blockingIssueCount: scores.summary.blocking,
1150
1231
  scores: {
@@ -3061,6 +3142,7 @@ var inconsistentNamingConventionCheck = (node, context) => {
3061
3142
  if (nodeConvention && nodeConvention !== dominantConvention && maxCount >= 2) {
3062
3143
  if (isCompatible(nodeConvention, dominantConvention, node.name)) return null;
3063
3144
  const suggested = convertName(node.name, dominantConvention);
3145
+ if (suggested === node.name) return null;
3064
3146
  return {
3065
3147
  ruleId: inconsistentNamingConventionDef.id,
3066
3148
  nodeId: node.id,
@@ -3156,6 +3238,15 @@ function hasStateInComponentMaster(node, context, statePattern) {
3156
3238
  if (!master) return false;
3157
3239
  return hasStateInVariantProps(master, statePattern);
3158
3240
  }
3241
+ function canDetermineVariants(node, context) {
3242
+ if (node.type === "COMPONENT") return true;
3243
+ if (node.componentPropertyDefinitions !== void 0) return true;
3244
+ if (node.componentId !== void 0) {
3245
+ const defs = context.file.componentDefinitions;
3246
+ if (defs && defs[node.componentId] !== void 0) return true;
3247
+ }
3248
+ return false;
3249
+ }
3159
3250
  var missingInteractionStateDef = {
3160
3251
  id: "missing-interaction-state",
3161
3252
  name: "Missing Interaction State",
@@ -3170,6 +3261,7 @@ var missingInteractionStateCheck = (node, context) => {
3170
3261
  if (!interactiveType) return null;
3171
3262
  const expectedStates = EXPECTED_STATES[interactiveType];
3172
3263
  if (!expectedStates) return null;
3264
+ if (!canDetermineVariants(node, context)) return null;
3173
3265
  const seen = getSeen(context);
3174
3266
  const nodePath = context.path.join(" > ");
3175
3267
  for (const state of expectedStates) {
@@ -5046,6 +5138,6 @@ var ActivityLogger = class {
5046
5138
  }
5047
5139
  };
5048
5140
 
5049
- export { ALL_STRIP_TYPES, ActivityLogger, AnalysisFileSchema, AnalysisNodeSchema, AnalysisNodeTypeSchema, AnnotationPropertySchema, CATEGORIES, CATEGORY_LABELS, CalibrationConfigSchema, CalibrationStatusSchema, CategorySchema, CategoryScoreSchema, ConfidenceSchema, ConversionRecordSchema, DEPTH_WEIGHT_CATEGORIES, DESIGN_TREE_INFO_TYPES, DifficultySchema, FigmaClient, FigmaClientError, FigmaFileLoadError, FigmaUrlInfoSchema, FigmaUrlParseError, GapAnalyzerOutputSchema, GapEntrySchema, GotchaSurveyQuestionSchema, GotchaSurveySchema, GridChildAlignSchema, InstanceContextSchema, IssueSchema, LayoutAlignSchema, LayoutConstraintSchema, LayoutModeSchema, LayoutPositioningSchema, LayoutWrapSchema, McpAnalyzeResponseSchema, MismatchCaseSchema, MismatchTypeSchema, NewRuleProposalSchema, NodeIssueSummarySchema, OverflowDirectionSchema, RULE_ANNOTATION_PROPERTIES, RULE_CONFIGS, RULE_ID_CATEGORY, ReportMetadataSchema, ReportSchema, RuleApplyStrategySchema, RuleConfigSchema, RuleDefinitionSchema, RuleEngine, RuleImpactAssessmentSchema, RuleRelatedStruggleSchema, SEVERITY_LABELS, SEVERITY_WEIGHT, SamplingStrategySchema, ScoreAdjustmentSchema, SeveritySchema, StripDeltaResultSchema, StripDeltasArraySchema, StripTypeEnum, UncoveredStruggleSchema, UncoveredStrugglesInputSchema, version as VERSION, VisualCompareCliOptionsSchema, absolutePositionInAutoLayout, analyzeFile, buildFigmaDeepLink, buildResultJson, calculateScores, collectComponentIds, collectInteractionDestinationIds, createRuleEngine, deepNesting, defineRule, detachedInstance, extractRuleScores, fixedSizeInAutoLayout, formatScoreSummary, generateCalibrationReport, generateDesignTree, generateDesignTreeWithStats, getAnalysisState, getAnnotationProperties, getCategoryLabel, getConfigsWithPreset, getRuleOption, getSeverityLabel, gradeToClassName, inconsistentNamingConvention, irregularSpacing, isInstanceChildNodeId, isReadyForCodeGen, loadFigmaFileFromJson, missingComponent, missingInteractionState, missingPrototype, missingSizeConstraint, noAutoLayout, nonLayoutContainer, nonSemanticName, nonStandardNaming, parseFigmaJson, parseFigmaUrl, parseInstanceChildNodeId, rawValue, resolveComponentDefinitions, resolveGotchaApplyTarget, resolveInteractionDestinations, ruleRegistry, runAnalysisAgent, runCalibrationAnalyze, runCalibrationEvaluate, runEvaluationAgent, runTuningAgent, stripDeltaToDifficulty, stripDesignTree, supportsDepthWeight, toCommentableNodeId, tokenDeltaToDifficulty, transformComponentMasterNodes, transformFigmaResponse, transformFileNodesResponse, variantStructureMismatch };
5141
+ export { ALL_STRIP_TYPES, ActivityLogger, AnalysisFileSchema, AnalysisNodeSchema, AnalysisNodeTypeSchema, AnnotationPropertySchema, CATEGORIES, CATEGORY_LABELS, CalibrationConfigSchema, CalibrationStatusSchema, CategorySchema, CategoryScoreSchema, ConfidenceSchema, ConversionRecordSchema, DEPTH_WEIGHT_CATEGORIES, DESIGN_TREE_INFO_TYPES, DifficultySchema, FigmaClient, FigmaClientError, FigmaFileLoadError, FigmaUrlInfoSchema, FigmaUrlParseError, GapAnalyzerOutputSchema, GapEntrySchema, GotchaSurveyQuestionSchema, GotchaSurveySchema, GridChildAlignSchema, GroupedSurveySchema, InstanceContextSchema, IssueSchema, LayoutAlignSchema, LayoutConstraintSchema, LayoutModeSchema, LayoutPositioningSchema, LayoutWrapSchema, McpAnalyzeResponseSchema, MismatchCaseSchema, MismatchTypeSchema, NewRuleProposalSchema, NodeIssueSummarySchema, OverflowDirectionSchema, RULE_ANNOTATION_PROPERTIES, RULE_CONFIGS, RULE_ID_CATEGORY, ReportMetadataSchema, ReportSchema, RuleApplyStrategySchema, RuleConfigSchema, RuleDefinitionSchema, RuleEngine, RuleImpactAssessmentSchema, RuleRelatedStruggleSchema, SEVERITY_LABELS, SEVERITY_WEIGHT, SamplingStrategySchema, ScoreAdjustmentSchema, SeveritySchema, StripDeltaResultSchema, StripDeltasArraySchema, StripTypeEnum, SurveyQuestionBatchSchema, SurveyQuestionGroupSchema, UncoveredStruggleSchema, UncoveredStrugglesInputSchema, version as VERSION, VisualCompareCliOptionsSchema, absolutePositionInAutoLayout, analyzeFile, buildFigmaDeepLink, buildResultJson, calculateScores, collectComponentIds, collectInteractionDestinationIds, createRuleEngine, deepNesting, defineRule, detachedInstance, extractRuleScores, fixedSizeInAutoLayout, formatScoreSummary, generateCalibrationReport, generateDesignTree, generateDesignTreeWithStats, getAnalysisState, getAnnotationProperties, getCategoryLabel, getConfigsWithPreset, getRuleOption, getSeverityLabel, gradeToClassName, inconsistentNamingConvention, irregularSpacing, isInstanceChildNodeId, isReadyForCodeGen, loadFigmaFileFromJson, missingComponent, missingInteractionState, missingPrototype, missingSizeConstraint, noAutoLayout, nonLayoutContainer, nonSemanticName, nonStandardNaming, parseFigmaJson, parseFigmaUrl, parseInstanceChildNodeId, rawValue, resolveComponentDefinitions, resolveGotchaApplyTarget, resolveInteractionDestinations, ruleRegistry, runAnalysisAgent, runCalibrationAnalyze, runCalibrationEvaluate, runEvaluationAgent, runTuningAgent, stripDeltaToDifficulty, stripDesignTree, supportsDepthWeight, toCommentableNodeId, tokenDeltaToDifficulty, transformComponentMasterNodes, transformFigmaResponse, transformFileNodesResponse, variantStructureMismatch };
5050
5142
  //# sourceMappingURL=index.js.map
5051
5143
  //# sourceMappingURL=index.js.map