canicode 0.10.3 → 0.10.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
2
  import { GetFileResponse, Node } from '@figma/rest-api-spec';
3
3
 
4
- var version = "0.10.3";
4
+ var version = "0.10.5";
5
5
 
6
6
  declare const SeveritySchema: z.ZodEnum<{
7
7
  blocking: "blocking";
@@ -656,6 +656,159 @@ declare const GotchaSurveyQuestionSchema: z.ZodObject<{
656
656
  replicaNodeIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
657
657
  }, z.core.$strip>;
658
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>;
659
812
  declare const GotchaSurveySchema: z.ZodObject<{
660
813
  designGrade: z.ZodEnum<{
661
814
  S: "S";
@@ -704,11 +857,92 @@ declare const GotchaSurveySchema: z.ZodObject<{
704
857
  replicas: z.ZodOptional<z.ZodNumber>;
705
858
  replicaNodeIds: z.ZodOptional<z.ZodArray<z.ZodString>>;
706
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;
707
911
  }, z.core.$strip>;
708
912
  type GotchaSurvey = z.infer<typeof GotchaSurveySchema>;
709
913
 
710
914
  /**
711
- * 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.
712
946
  */
713
947
  interface AnalysisIssue {
714
948
  violation: RuleViolation;
@@ -717,6 +951,7 @@ interface AnalysisIssue {
717
951
  depth: number;
718
952
  maxDepth: number;
719
953
  calculatedScore: number;
954
+ acknowledged?: boolean;
720
955
  }
721
956
  /**
722
957
  * Information about a rule that threw during analysis
@@ -748,6 +983,14 @@ interface RuleEngineOptions {
748
983
  targetNodeId?: string;
749
984
  excludeNodeNames?: string[];
750
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[];
751
994
  }
752
995
  /**
753
996
  * Rule engine for analyzing Figma files
@@ -759,6 +1002,7 @@ declare class RuleEngine {
759
1002
  private targetNodeId;
760
1003
  private excludeNamePattern;
761
1004
  private excludeNodeTypes;
1005
+ private acknowledgments;
762
1006
  constructor(options?: RuleEngineOptions);
763
1007
  /**
764
1008
  * Analyze a Figma file and return issues
@@ -815,6 +1059,15 @@ interface ScoreReport {
815
1059
  missingInfo: number;
816
1060
  suggestion: number;
817
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;
818
1071
  };
819
1072
  }
820
1073
  /**
@@ -862,6 +1115,7 @@ declare function getSeverityLabel(severity: Severity): string;
862
1115
  */
863
1116
  declare function buildResultJson(fileName: string, result: AnalysisResult, scores: ScoreReport, options?: {
864
1117
  fileKey?: string;
1118
+ designKey?: string;
865
1119
  }): Record<string, unknown>;
866
1120
 
867
1121
  /**
@@ -1853,4 +2107,4 @@ declare class ActivityLogger {
1853
2107
  getLogPath(): string;
1854
2108
  }
1855
2109
 
1856
- 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.3";
9
+ var version = "0.10.5";
10
10
  var SeveritySchema = z.enum([
11
11
  "blocking",
12
12
  "risk",
@@ -363,10 +363,50 @@ var GotchaSurveyQuestionSchema = z.object({
363
363
  replicas: z.number().int().min(2).optional(),
364
364
  replicaNodeIds: z.array(z.string()).optional()
365
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)
396
+ });
366
397
  var GotchaSurveySchema = z.object({
367
398
  designGrade: GradeSchema2,
368
399
  isReadyForCodeGen: z.boolean(),
369
- 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()
370
410
  });
371
411
 
372
412
  // src/core/rules/rule-config.ts
@@ -650,6 +690,14 @@ function defineRule(rule) {
650
690
  ruleRegistry.register(rule);
651
691
  return rule;
652
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
+ }
653
701
 
654
702
  // src/core/engine/rule-engine.ts
655
703
  function calculateMaxDepth(node, currentDepth = 0) {
@@ -700,6 +748,7 @@ var RuleEngine = class {
700
748
  targetNodeId;
701
749
  excludeNamePattern;
702
750
  excludeNodeTypes;
751
+ acknowledgments;
703
752
  constructor(options = {}) {
704
753
  this.configs = options.configs ?? RULE_CONFIGS;
705
754
  this.enabledRuleIds = options.enabledRules ? new Set(options.enabledRules) : null;
@@ -707,6 +756,11 @@ var RuleEngine = class {
707
756
  this.targetNodeId = options.targetNodeId;
708
757
  this.excludeNamePattern = options.excludeNodeNames && options.excludeNodeNames.length > 0 ? new RegExp(`\\b(${options.excludeNodeNames.join("|")})\\b`, "i") : null;
709
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
+ );
710
764
  }
711
765
  /**
712
766
  * Analyze a Figma file and return issues
@@ -741,6 +795,14 @@ var RuleEngine = class {
741
795
  void 0,
742
796
  void 0
743
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
+ }
744
806
  return {
745
807
  file,
746
808
  issues,
@@ -891,8 +953,6 @@ function resolveTargetProperty(ruleId, subType) {
891
953
  if (subType === "horizontal") return "layoutSizingHorizontal";
892
954
  return ["layoutSizingHorizontal", "layoutSizingVertical"];
893
955
  case "missing-size-constraint":
894
- if (subType === "wrap") return "minWidth";
895
- if (subType === "max-width") return "maxWidth";
896
956
  return ["minWidth", "maxWidth"];
897
957
  case "irregular-spacing":
898
958
  if (subType === "gap") return "itemSpacing";
@@ -992,7 +1052,8 @@ function calculateScores(result, configs) {
992
1052
  uniqueRulesPerCategory.get(category).add(ruleId);
993
1053
  ruleScorePerCategory.get(category).set(ruleId, Math.abs(issue.config.score));
994
1054
  const ruleCountMap = ruleIssueCountPerCategory.get(category);
995
- 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);
996
1057
  }
997
1058
  for (const category of CATEGORIES) {
998
1059
  const ruleCountMap = ruleIssueCountPerCategory.get(category);
@@ -1039,7 +1100,8 @@ function calculateScores(result, configs) {
1039
1100
  risk: 0,
1040
1101
  missingInfo: 0,
1041
1102
  suggestion: 0,
1042
- nodeCount
1103
+ nodeCount,
1104
+ acknowledgedCount: 0
1043
1105
  };
1044
1106
  for (const issue of result.issues) {
1045
1107
  switch (issue.config.severity) {
@@ -1056,6 +1118,7 @@ function calculateScores(result, configs) {
1056
1118
  summary.suggestion++;
1057
1119
  break;
1058
1120
  }
1121
+ if (issue.acknowledged === true) summary.acknowledgedCount++;
1059
1122
  }
1060
1123
  return {
1061
1124
  overall: {
@@ -1106,7 +1169,14 @@ function formatScoreSummary(report) {
1106
1169
  lines.push(` Risk: ${report.summary.risk}`);
1107
1170
  lines.push(` Missing Info: ${report.summary.missingInfo}`);
1108
1171
  lines.push(` Suggestion: ${report.summary.suggestion}`);
1109
- 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
+ }
1110
1180
  return lines.join("\n");
1111
1181
  }
1112
1182
  function getCategoryLabel(category) {
@@ -1142,17 +1212,20 @@ function buildResultJson(fileName, result, scores, options) {
1142
1212
  ...applyContext.annotationProperties !== void 0 ? { annotationProperties: applyContext.annotationProperties } : {},
1143
1213
  ...suggestedName !== void 0 ? { suggestedName } : {},
1144
1214
  isInstanceChild: applyContext.isInstanceChild,
1145
- ...applyContext.sourceChildId !== void 0 ? { sourceChildId: applyContext.sourceChildId } : {}
1215
+ ...applyContext.sourceChildId !== void 0 ? { sourceChildId: applyContext.sourceChildId } : {},
1216
+ ...issue.acknowledged === true ? { acknowledged: true } : {}
1146
1217
  };
1147
1218
  });
1148
1219
  const json = {
1149
1220
  version,
1150
1221
  analyzedAt: result.analyzedAt,
1151
1222
  ...options?.fileKey && { fileKey: options.fileKey },
1223
+ ...options?.designKey && { designKey: options.designKey },
1152
1224
  fileName,
1153
1225
  nodeCount: result.nodeCount,
1154
1226
  maxDepth: result.maxDepth,
1155
1227
  issueCount: result.issues.length,
1228
+ acknowledgedCount: scores.summary.acknowledgedCount,
1156
1229
  isReadyForCodeGen: isReadyForCodeGen(scores.overall.grade),
1157
1230
  blockingIssueCount: scores.summary.blocking,
1158
1231
  scores: {
@@ -3069,6 +3142,7 @@ var inconsistentNamingConventionCheck = (node, context) => {
3069
3142
  if (nodeConvention && nodeConvention !== dominantConvention && maxCount >= 2) {
3070
3143
  if (isCompatible(nodeConvention, dominantConvention, node.name)) return null;
3071
3144
  const suggested = convertName(node.name, dominantConvention);
3145
+ if (suggested === node.name) return null;
3072
3146
  return {
3073
3147
  ruleId: inconsistentNamingConventionDef.id,
3074
3148
  nodeId: node.id,
@@ -3164,12 +3238,22 @@ function hasStateInComponentMaster(node, context, statePattern) {
3164
3238
  if (!master) return false;
3165
3239
  return hasStateInVariantProps(master, statePattern);
3166
3240
  }
3241
+ var VARIANT_POSITION_NAME_RE = /^[\w ]+=[^,]+(,\s*[\w ]+=[^,]+)*$/;
3242
+ function hasUsablePropDefs(propDefs) {
3243
+ return propDefs != null && typeof propDefs === "object";
3244
+ }
3167
3245
  function canDetermineVariants(node, context) {
3168
- if (node.type === "COMPONENT") return true;
3169
- if (node.componentPropertyDefinitions !== void 0) return true;
3246
+ if (hasUsablePropDefs(node.componentPropertyDefinitions)) return true;
3247
+ if (node.type === "COMPONENT") {
3248
+ return !VARIANT_POSITION_NAME_RE.test(node.name);
3249
+ }
3170
3250
  if (node.componentId !== void 0) {
3171
3251
  const defs = context.file.componentDefinitions;
3172
- if (defs && defs[node.componentId] !== void 0) return true;
3252
+ const master = defs?.[node.componentId];
3253
+ if (master) {
3254
+ if (hasUsablePropDefs(master.componentPropertyDefinitions)) return true;
3255
+ return !VARIANT_POSITION_NAME_RE.test(master.name);
3256
+ }
3173
3257
  }
3174
3258
  return false;
3175
3259
  }
@@ -5064,6 +5148,6 @@ var ActivityLogger = class {
5064
5148
  }
5065
5149
  };
5066
5150
 
5067
- 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 };
5151
+ 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 };
5068
5152
  //# sourceMappingURL=index.js.map
5069
5153
  //# sourceMappingURL=index.js.map