@codeyam/codeyam-cli 0.1.0-staging.323686 → 0.1.0-staging.483fdc2

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.
Files changed (171) hide show
  1. package/analyzer-template/.build-info.json +7 -7
  2. package/analyzer-template/log.txt +3 -3
  3. package/analyzer-template/package.json +2 -2
  4. package/analyzer-template/packages/ai/index.ts +6 -1
  5. package/analyzer-template/packages/ai/src/lib/analyzeScope.ts +39 -17
  6. package/analyzer-template/packages/ai/src/lib/astScopes/astScopeAnalyzer.ts +67 -9
  7. package/analyzer-template/packages/ai/src/lib/astScopes/processExpression.ts +308 -50
  8. package/analyzer-template/packages/ai/src/lib/astScopes/types.ts +15 -6
  9. package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +664 -242
  10. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.ts +16 -3
  11. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.ts +6 -4
  12. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.ts +20 -1
  13. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.ts +35 -13
  14. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.ts +160 -0
  15. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.ts +40 -30
  16. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.ts +289 -83
  17. package/analyzer-template/packages/ai/src/lib/generateEntityScenarioData.ts +269 -1
  18. package/analyzer-template/packages/ai/src/lib/generateEntityScenarios.ts +9 -5
  19. package/analyzer-template/packages/ai/src/lib/generateExecutionFlows.ts +11 -3
  20. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionalEffects.ts +1 -1
  21. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromConditionals.ts +297 -7
  22. package/analyzer-template/packages/ai/src/lib/generateExecutionFlowsFromJsxUsages.ts +1 -1
  23. package/analyzer-template/packages/ai/src/lib/mergeStatements.ts +90 -96
  24. package/analyzer-template/packages/ai/src/lib/promptGenerators/gatherAttributesMap.ts +10 -7
  25. package/analyzer-template/packages/ai/src/lib/resolvePathToControllable.ts +25 -13
  26. package/analyzer-template/packages/ai/src/lib/worker/SerializableDataStructure.ts +4 -3
  27. package/analyzer-template/packages/analyze/src/lib/FileAnalyzer.ts +65 -59
  28. package/analyzer-template/packages/analyze/src/lib/ProjectAnalyzer.ts +113 -26
  29. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.ts +19 -0
  30. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.ts +19 -0
  31. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getAllExports.ts +11 -0
  32. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.ts +8 -0
  33. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.ts +49 -1
  34. package/analyzer-template/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.ts +2 -1
  35. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.ts +20 -6
  36. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +14 -4
  37. package/analyzer-template/packages/analyze/src/lib/files/analyze/gatherEntityMap.ts +4 -2
  38. package/analyzer-template/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.ts +0 -3
  39. package/analyzer-template/packages/analyze/src/lib/files/analyzeRemixRoute.ts +4 -5
  40. package/analyzer-template/packages/analyze/src/lib/files/getImportedExports.ts +14 -12
  41. package/analyzer-template/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.ts +57 -13
  42. package/analyzer-template/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.ts +29 -0
  43. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +35 -4
  44. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.ts +117 -9
  45. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +199 -17
  46. package/analyzer-template/packages/analyze/src/lib/files/scenarios/propagateArrayItemSchemas.ts +474 -0
  47. package/analyzer-template/packages/analyze/src/lib/files/setImportedExports.ts +2 -1
  48. package/analyzer-template/packages/analyze/src/lib/utils/getFileByPath.ts +19 -0
  49. package/analyzer-template/packages/aws/package.json +1 -1
  50. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts +5 -5
  51. package/analyzer-template/packages/github/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  52. package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
  53. package/analyzer-template/packages/github/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
  54. package/analyzer-template/packages/github/package.json +1 -1
  55. package/analyzer-template/packages/types/src/types/ScenariosDataStructure.ts +6 -5
  56. package/analyzer-template/packages/types/src/types/ScopeAnalysis.ts +6 -1
  57. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts +5 -5
  58. package/analyzer-template/packages/utils/dist/types/src/types/ScenariosDataStructure.d.ts.map +1 -1
  59. package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts +6 -1
  60. package/analyzer-template/packages/utils/dist/types/src/types/ScopeAnalysis.d.ts.map +1 -1
  61. package/analyzer-template/project/constructMockCode.ts +54 -9
  62. package/analyzer-template/project/writeMockDataTsx.ts +73 -2
  63. package/background/src/lib/virtualized/project/constructMockCode.js +45 -3
  64. package/background/src/lib/virtualized/project/constructMockCode.js.map +1 -1
  65. package/background/src/lib/virtualized/project/writeMockDataTsx.js +71 -2
  66. package/background/src/lib/virtualized/project/writeMockDataTsx.js.map +1 -1
  67. package/codeyam-cli/scripts/apply-setup.js +146 -0
  68. package/codeyam-cli/scripts/apply-setup.js.map +1 -1
  69. package/codeyam-cli/src/commands/debug.js +7 -5
  70. package/codeyam-cli/src/commands/debug.js.map +1 -1
  71. package/codeyam-cli/src/utils/install-skills.js +22 -0
  72. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  73. package/codeyam-cli/src/utils/reviewedRules.js +92 -0
  74. package/codeyam-cli/src/utils/reviewedRules.js.map +1 -0
  75. package/codeyam-cli/src/webserver/build/client/assets/globals-CX9f-5xM.css +1 -0
  76. package/codeyam-cli/src/webserver/build/client/assets/{manifest-7522edd4.js → manifest-bba56ec1.js} +1 -1
  77. package/codeyam-cli/src/webserver/build/client/assets/memory-DuTFSyJ2.js +92 -0
  78. package/codeyam-cli/src/webserver/build/client/assets/{root-eVAaavTS.js → root-DTfSQARG.js} +6 -6
  79. package/codeyam-cli/src/webserver/build/server/assets/{index-DVzYx8PN.js → index-TD1f-DHV.js} +1 -1
  80. package/codeyam-cli/src/webserver/build/server/assets/server-build-BQ-1XyEa.js +258 -0
  81. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  82. package/codeyam-cli/src/webserver/build-info.json +5 -5
  83. package/codeyam-cli/templates/codeyam:memory.md +174 -233
  84. package/codeyam-cli/templates/codeyam:new-rule.md +41 -2
  85. package/codeyam-cli/templates/rule-reflection-hook.py +161 -0
  86. package/codeyam-cli/templates/rules-instructions.md +126 -0
  87. package/package.json +1 -1
  88. package/packages/ai/index.js +2 -1
  89. package/packages/ai/index.js.map +1 -1
  90. package/packages/ai/src/lib/analyzeScope.js +29 -12
  91. package/packages/ai/src/lib/analyzeScope.js.map +1 -1
  92. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js +54 -8
  93. package/packages/ai/src/lib/astScopes/astScopeAnalyzer.js.map +1 -1
  94. package/packages/ai/src/lib/astScopes/processExpression.js +239 -43
  95. package/packages/ai/src/lib/astScopes/processExpression.js.map +1 -1
  96. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +503 -165
  97. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  98. package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js +13 -3
  99. package/packages/ai/src/lib/dataStructure/helpers/BatchSchemaProcessor.js.map +1 -1
  100. package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js +6 -4
  101. package/packages/ai/src/lib/dataStructure/helpers/ScopeTreeManager.js.map +1 -1
  102. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +22 -1
  103. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
  104. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js +34 -9
  105. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js.map +1 -1
  106. package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js +159 -0
  107. package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js.map +1 -0
  108. package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js +37 -20
  109. package/packages/ai/src/lib/dataStructure/helpers/deduplicateFunctionSchemas.js.map +1 -1
  110. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js +237 -73
  111. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js.map +1 -1
  112. package/packages/ai/src/lib/generateEntityScenarioData.js +195 -1
  113. package/packages/ai/src/lib/generateEntityScenarioData.js.map +1 -1
  114. package/packages/ai/src/lib/generateEntityScenarios.js +7 -1
  115. package/packages/ai/src/lib/generateEntityScenarios.js.map +1 -1
  116. package/packages/ai/src/lib/generateExecutionFlows.js +10 -2
  117. package/packages/ai/src/lib/generateExecutionFlows.js.map +1 -1
  118. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js +209 -3
  119. package/packages/ai/src/lib/generateExecutionFlowsFromConditionals.js.map +1 -1
  120. package/packages/ai/src/lib/mergeStatements.js +70 -51
  121. package/packages/ai/src/lib/mergeStatements.js.map +1 -1
  122. package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js +10 -4
  123. package/packages/ai/src/lib/promptGenerators/gatherAttributesMap.js.map +1 -1
  124. package/packages/ai/src/lib/resolvePathToControllable.js +24 -14
  125. package/packages/ai/src/lib/resolvePathToControllable.js.map +1 -1
  126. package/packages/ai/src/lib/worker/SerializableDataStructure.js.map +1 -1
  127. package/packages/analyze/src/lib/FileAnalyzer.js +60 -36
  128. package/packages/analyze/src/lib/FileAnalyzer.js.map +1 -1
  129. package/packages/analyze/src/lib/ProjectAnalyzer.js +96 -26
  130. package/packages/analyze/src/lib/ProjectAnalyzer.js.map +1 -1
  131. package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js +14 -0
  132. package/packages/analyze/src/lib/asts/sourceFiles/getAllDeclaredEntityNodes.js.map +1 -1
  133. package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js +14 -0
  134. package/packages/analyze/src/lib/asts/sourceFiles/getAllEntityNodes.js.map +1 -1
  135. package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js +6 -0
  136. package/packages/analyze/src/lib/asts/sourceFiles/getAllExports.js.map +1 -1
  137. package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js +6 -0
  138. package/packages/analyze/src/lib/asts/sourceFiles/getImportsAnalysis.js.map +1 -1
  139. package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js +39 -1
  140. package/packages/analyze/src/lib/asts/sourceFiles/getResolvedModule.js.map +1 -1
  141. package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js +2 -1
  142. package/packages/analyze/src/lib/asts/sourceFiles/getSourceFilesForAllImports.js.map +1 -1
  143. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +13 -5
  144. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
  145. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +14 -4
  146. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
  147. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js +2 -1
  148. package/packages/analyze/src/lib/files/analyze/gatherEntityMap.js.map +1 -1
  149. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js +0 -3
  150. package/packages/analyze/src/lib/files/analyze/validateDependencyAnalyses.js.map +1 -1
  151. package/packages/analyze/src/lib/files/analyzeRemixRoute.js +3 -2
  152. package/packages/analyze/src/lib/files/analyzeRemixRoute.js.map +1 -1
  153. package/packages/analyze/src/lib/files/getImportedExports.js +11 -7
  154. package/packages/analyze/src/lib/files/getImportedExports.js.map +1 -1
  155. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js +52 -10
  156. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js.map +1 -1
  157. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js +25 -8
  158. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js.map +1 -1
  159. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +34 -4
  160. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  161. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js +56 -8
  162. package/packages/analyze/src/lib/files/scenarios/generateExecutionFlows.js.map +1 -1
  163. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +168 -9
  164. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
  165. package/packages/analyze/src/lib/files/setImportedExports.js +2 -1
  166. package/packages/analyze/src/lib/files/setImportedExports.js.map +1 -1
  167. package/packages/analyze/src/lib/utils/getFileByPath.js +12 -0
  168. package/packages/analyze/src/lib/utils/getFileByPath.js.map +1 -0
  169. package/codeyam-cli/src/webserver/build/client/assets/globals-D3yhhV8x.css +0 -1
  170. package/codeyam-cli/src/webserver/build/client/assets/memory-yxFcrxBX.js +0 -92
  171. package/codeyam-cli/src/webserver/build/server/assets/server-build-4Cr0uToj.js +0 -257
@@ -18,6 +18,7 @@ import type {
18
18
  CompoundConditional,
19
19
  DerivedVariableOperation,
20
20
  DerivedVariableInfo,
21
+ JsxRenderingUsage,
21
22
  } from './astScopes/types';
22
23
  import type { EnrichedConditionalUsage } from './worker/SerializableDataStructure';
23
24
  import resolvePathToControllable from './resolvePathToControllable';
@@ -32,7 +33,7 @@ export interface ChildComponentConditionalData {
32
33
  /** Child's conditional usages keyed by variable name (may include sourceDataPath from enrichment) */
33
34
  conditionalUsages: Record<string, ExtendedConditionalUsage[]>;
34
35
  /** Child's equivalent signature variables (maps internal paths to prop paths) */
35
- equivalentSignatureVariables: Record<string, string>;
36
+ equivalentSignatureVariables: Record<string, string | string[]>;
36
37
  /** Child's compound conditionals */
37
38
  compoundConditionals: CompoundConditional[];
38
39
  /**
@@ -41,6 +42,11 @@ export interface ChildComponentConditionalData {
41
42
  * then `hasAnalysis` is a gating condition for all of ChildComponent's flows.
42
43
  */
43
44
  gatingConditions?: ConditionalUsage[];
45
+ /**
46
+ * Child's JSX rendering usages (arrays rendered via .map(), text interpolation).
47
+ * These generate "variation flows" for different array lengths.
48
+ */
49
+ jsxRenderingUsages?: JsxRenderingUsage[];
44
50
  }
45
51
 
46
52
  export interface GenerateFlowsFromConditionalsArgs {
@@ -51,7 +57,7 @@ export interface GenerateFlowsFromConditionalsArgs {
51
57
  /** Map of controllable paths to their types */
52
58
  attributesMap: Record<string, string>;
53
59
  /** Map from local variable names to data sources */
54
- equivalentSignatureVariables: Record<string, string>;
60
+ equivalentSignatureVariables: Record<string, string | string[]>;
55
61
  /** Map from full paths to short paths */
56
62
  fullToShortPathMap: Record<string, string>;
57
63
  /**
@@ -73,6 +79,21 @@ export interface GenerateFlowsFromConditionalsArgs {
73
79
  * to `currentRun.entityShas` when `isInCurrentRun` isn't in conditionalUsages.
74
80
  */
75
81
  derivedVariables?: Record<string, DerivedVariableInfo>;
82
+ /**
83
+ * Optional map of child prop paths to their actual data sources.
84
+ * Used when child props flow through useState but ultimately come from
85
+ * mockable data sources (e.g., API calls, fetchers).
86
+ *
87
+ * Example:
88
+ * - "WorkoutsView().signature[0].workouts" → "createClient()...functionCallReturnValue.data"
89
+ *
90
+ * When a child path translates to a useState value, we check this map
91
+ * to find the real data source that can be mocked.
92
+ */
93
+ sourceEquivalencies?: Record<
94
+ string,
95
+ Array<{ scopeNodeName: string; schemaPath: string }>
96
+ >;
76
97
  }
77
98
 
78
99
  /**
@@ -117,7 +138,7 @@ function expandDerivedVariableToSources(
117
138
  path: string,
118
139
  conditionalUsages: Record<string, ConditionalUsage[]>,
119
140
  attributesMap: Record<string, string>,
120
- equivalentSignatureVariables: Record<string, string>,
141
+ equivalentSignatureVariables: Record<string, string | string[]>,
121
142
  fullToShortPathMap: Record<string, string>,
122
143
  visited: Set<string> = new Set(),
123
144
  derivedVariables?: Record<string, DerivedVariableInfo>,
@@ -387,6 +408,20 @@ function findInAttributesMapForPath(
387
408
  return null;
388
409
  }
389
410
 
411
+ /**
412
+ * Generate a slug from a path for use in flow IDs and exclusive groups.
413
+ */
414
+ function pathToSlug(path: string): string {
415
+ return path
416
+ .replace(/\[\d+\]/g, '')
417
+ .replace(/\[\]/g, '')
418
+ .replace(/\(\)/g, '')
419
+ .replace(/\.functionCallReturnValue/g, '')
420
+ .replace(/[<>]/g, '')
421
+ .replace(/\./g, '-')
422
+ .toLowerCase();
423
+ }
424
+
390
425
  /**
391
426
  * Generate a human-readable name from a path.
392
427
  * Extracts the last meaningful part of the path.
@@ -732,8 +767,8 @@ function normalizePathForDeduplication(
732
767
  */
733
768
  function translateChildPathToParent(
734
769
  childPath: string,
735
- childEquivalentSignatureVariables: Record<string, string>,
736
- parentEquivalentSignatureVariables: Record<string, string>,
770
+ childEquivalentSignatureVariables: Record<string, string | string[]>,
771
+ parentEquivalentSignatureVariables: Record<string, string | string[]>,
737
772
  childName: string,
738
773
  ): string | null {
739
774
  // Extract the root variable from the child path
@@ -760,7 +795,11 @@ function translateChildPathToParent(
760
795
 
761
796
  // Look up the child's equivalence for this root variable
762
797
  // e.g., childEquiv[selectedScenario] = "signature[0].selectedScenario"
763
- const childPropPath = childEquivalentSignatureVariables[rootVar];
798
+ // Handle array case (OR expressions) - use first element if array
799
+ const rawChildPropPath = childEquivalentSignatureVariables[rootVar];
800
+ const childPropPath = Array.isArray(rawChildPropPath)
801
+ ? rawChildPropPath[0]
802
+ : rawChildPropPath;
764
803
 
765
804
  if (!childPropPath) {
766
805
  // No mapping found - this might be internal state, not a prop
@@ -773,7 +812,11 @@ function translateChildPathToParent(
773
812
 
774
813
  // Look up parent's equivalence to find what value was passed to this prop
775
814
  // e.g., parentEquiv["ChildName().signature[0].selectedScenario"] = "selectedScenario"
776
- const parentValue = parentEquivalentSignatureVariables[fullChildPropPath];
815
+ // Handle array case (OR expressions) - use first element if array
816
+ const rawParentValue = parentEquivalentSignatureVariables[fullChildPropPath];
817
+ const parentValue = Array.isArray(rawParentValue)
818
+ ? rawParentValue[0]
819
+ : rawParentValue;
777
820
 
778
821
  if (!parentValue) {
779
822
  // No parent mapping found - log ALL parent keys that contain the childName
@@ -800,6 +843,7 @@ export default function generateExecutionFlowsFromConditionals(
800
843
  fullToShortPathMap,
801
844
  childComponentData,
802
845
  derivedVariables,
846
+ sourceEquivalencies,
803
847
  } = args;
804
848
 
805
849
  const flows: ExecutionFlow[] = [];
@@ -1771,6 +1815,69 @@ export default function generateExecutionFlowsFromConditionals(
1771
1815
  }
1772
1816
  }
1773
1817
 
1818
+ // Fallback 2: Try sourceEquivalencies to find the actual data source
1819
+ // This handles the case where props flow through useState but originate
1820
+ // from a mockable data source (e.g., API call, fetcher).
1821
+ //
1822
+ // Example: WorkoutsView receives `workouts` prop which in parent is stored
1823
+ // in useState, but ultimately comes from a Supabase query.
1824
+ // sourceEquivalencies tells us: "WorkoutsView().signature[0].workouts" → "createClient()...data"
1825
+ if (!resolvedPath && sourceEquivalencies) {
1826
+ // Build the child prop path to look up in sourceEquivalencies
1827
+ // Format: "ChildName().signature[0].propName"
1828
+ // First, find what prop this child path maps to
1829
+ let childPropName: string | null = null;
1830
+ for (const [varName, varPath] of Object.entries(
1831
+ childData.equivalentSignatureVariables,
1832
+ )) {
1833
+ // Check if childPath starts with this variable name
1834
+ // e.g., childPath = "workouts.length", varName = "workouts", varPath = "signature[0].workouts"
1835
+ if (
1836
+ childPath === varName ||
1837
+ childPath.startsWith(`${varName}.`)
1838
+ ) {
1839
+ childPropName = varName;
1840
+ break;
1841
+ }
1842
+ }
1843
+
1844
+ if (childPropName) {
1845
+ // Build the full sourceEquivalencies key
1846
+ const sourceEquivKey = `${childName}().signature[0].${childPropName}`;
1847
+
1848
+ const sourceEquivEntry = sourceEquivalencies[sourceEquivKey];
1849
+ if (sourceEquivEntry && sourceEquivEntry.length > 0) {
1850
+ const dataSourcePath = sourceEquivEntry[0].schemaPath;
1851
+
1852
+ // Check if this data source path is controllable
1853
+ const dataSourceResolution = resolvePathToControllable(
1854
+ dataSourcePath,
1855
+ attributesMap,
1856
+ equivalentSignatureVariables,
1857
+ fullToShortPathMap,
1858
+ );
1859
+
1860
+ if (
1861
+ dataSourceResolution.isControllable &&
1862
+ dataSourceResolution.resolvedPath
1863
+ ) {
1864
+ // Preserve any suffix from the child path
1865
+ // e.g., childPath = "workouts.length" → suffix = ".length"
1866
+ const suffix = childPath.startsWith(`${childPropName}.`)
1867
+ ? childPath.slice(childPropName.length)
1868
+ : '';
1869
+ resolvedPath = dataSourceResolution.resolvedPath + suffix;
1870
+
1871
+ if (shouldDebugChild) {
1872
+ console.log(
1873
+ `[DEBUG CHILD ${childName}] sourceEquivalencies fallback SUCCESS: using data source ${resolvedPath}`,
1874
+ );
1875
+ }
1876
+ }
1877
+ }
1878
+ }
1879
+ }
1880
+
1774
1881
  // If still not resolved after fallback, skip
1775
1882
  if (!resolvedPath) {
1776
1883
  if (shouldDebugChild) {
@@ -1970,6 +2077,189 @@ export default function generateExecutionFlowsFromConditionals(
1970
2077
  }
1971
2078
  }
1972
2079
  }
2080
+
2081
+ // Process child's jsxRenderingUsages (array.map flows)
2082
+ // This generates array variation flows (empty, few, many) for arrays rendered in child
2083
+ if (childData.jsxRenderingUsages) {
2084
+ for (const jsxUsage of childData.jsxRenderingUsages) {
2085
+ // Translate the child path to a parent path
2086
+ const translatedPath = translateChildPathToParent(
2087
+ jsxUsage.path,
2088
+ childData.equivalentSignatureVariables,
2089
+ equivalentSignatureVariables,
2090
+ childName,
2091
+ );
2092
+
2093
+ if (!translatedPath) {
2094
+ continue;
2095
+ }
2096
+
2097
+ // Resolve to controllable path in parent context
2098
+ const resolution = resolvePathToControllable(
2099
+ translatedPath,
2100
+ attributesMap,
2101
+ equivalentSignatureVariables,
2102
+ fullToShortPathMap,
2103
+ );
2104
+
2105
+ let resolvedPath = resolution.resolvedPath;
2106
+
2107
+ // Try sourceEquivalencies fallback if not controllable
2108
+ if (!resolution.isControllable || !resolvedPath) {
2109
+ if (sourceEquivalencies) {
2110
+ // Build the sourceEquivalencies key
2111
+ // The child path (e.g., "workouts") maps to a prop path (e.g., "signature[0].workouts")
2112
+ let childPropName: string | null = null;
2113
+ for (const [varName, varPath] of Object.entries(
2114
+ childData.equivalentSignatureVariables,
2115
+ )) {
2116
+ if (
2117
+ jsxUsage.path === varName ||
2118
+ jsxUsage.path.startsWith(`${varName}.`)
2119
+ ) {
2120
+ childPropName = varName;
2121
+ break;
2122
+ }
2123
+ }
2124
+
2125
+ if (childPropName) {
2126
+ const sourceEquivKey = `${childName}().signature[0].${childPropName}`;
2127
+ const sourceEquivEntry = sourceEquivalencies[sourceEquivKey];
2128
+
2129
+ if (sourceEquivEntry && sourceEquivEntry.length > 0) {
2130
+ const dataSourcePath = sourceEquivEntry[0].schemaPath;
2131
+
2132
+ const dataSourceResolution = resolvePathToControllable(
2133
+ dataSourcePath,
2134
+ attributesMap,
2135
+ equivalentSignatureVariables,
2136
+ fullToShortPathMap,
2137
+ );
2138
+
2139
+ if (
2140
+ dataSourceResolution.isControllable &&
2141
+ dataSourceResolution.resolvedPath
2142
+ ) {
2143
+ resolvedPath = dataSourceResolution.resolvedPath;
2144
+ }
2145
+ }
2146
+ }
2147
+ }
2148
+ }
2149
+
2150
+ if (!resolvedPath) {
2151
+ continue;
2152
+ }
2153
+
2154
+ // Check for duplicates
2155
+ const normalizedPath = normalizePathForDeduplication(
2156
+ resolvedPath,
2157
+ fullToShortPathMap,
2158
+ );
2159
+ const dedupeKey = `${normalizedPath}:${jsxUsage.renderingType}`;
2160
+ if (seenNormalizedPaths.has(dedupeKey)) {
2161
+ continue;
2162
+ }
2163
+ seenNormalizedPaths.add(dedupeKey);
2164
+
2165
+ // Generate array variation flows for array-map rendering
2166
+ if (jsxUsage.renderingType === 'array-map') {
2167
+ const baseName = generateNameFromPath(resolvedPath);
2168
+ const pathSlug = pathToSlug(resolvedPath);
2169
+ const exclusiveGroup = `array-length-${pathSlug}`;
2170
+
2171
+ // Empty array flow
2172
+ const emptyFlow: ExecutionFlow = {
2173
+ id: `${pathSlug}-empty-array`,
2174
+ name: `${baseName} Empty`,
2175
+ description: `When ${baseName.toLowerCase()} array is empty`,
2176
+ requiredValues: [
2177
+ {
2178
+ attributePath: resolvedPath,
2179
+ value: '0',
2180
+ comparison: 'length<',
2181
+ valueType: 'array',
2182
+ },
2183
+ ...gatingRequiredValues,
2184
+ ],
2185
+ impact: 'medium',
2186
+ exclusiveGroup,
2187
+ sourceLocation: jsxUsage.sourceLocation
2188
+ ? {
2189
+ lineNumber: jsxUsage.sourceLocation.lineNumber,
2190
+ column: jsxUsage.sourceLocation.column,
2191
+ }
2192
+ : undefined,
2193
+ codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
2194
+ };
2195
+
2196
+ if (!seenFlowIds.has(emptyFlow.id)) {
2197
+ seenFlowIds.add(emptyFlow.id);
2198
+ flows.push(emptyFlow);
2199
+ }
2200
+
2201
+ // Few items flow (1-3)
2202
+ const fewFlow: ExecutionFlow = {
2203
+ id: `${pathSlug}-few-items`,
2204
+ name: `${baseName} Few Items`,
2205
+ description: `When ${baseName.toLowerCase()} array has 1-3 items`,
2206
+ requiredValues: [
2207
+ {
2208
+ attributePath: resolvedPath,
2209
+ value: '3',
2210
+ comparison: 'length<',
2211
+ valueType: 'array',
2212
+ },
2213
+ ...gatingRequiredValues,
2214
+ ],
2215
+ impact: 'low',
2216
+ exclusiveGroup,
2217
+ sourceLocation: jsxUsage.sourceLocation
2218
+ ? {
2219
+ lineNumber: jsxUsage.sourceLocation.lineNumber,
2220
+ column: jsxUsage.sourceLocation.column,
2221
+ }
2222
+ : undefined,
2223
+ codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
2224
+ };
2225
+
2226
+ if (!seenFlowIds.has(fewFlow.id)) {
2227
+ seenFlowIds.add(fewFlow.id);
2228
+ flows.push(fewFlow);
2229
+ }
2230
+
2231
+ // Many items flow (10+)
2232
+ const manyFlow: ExecutionFlow = {
2233
+ id: `${pathSlug}-many-items`,
2234
+ name: `${baseName} Many Items`,
2235
+ description: `When ${baseName.toLowerCase()} array has many items`,
2236
+ requiredValues: [
2237
+ {
2238
+ attributePath: resolvedPath,
2239
+ value: '10',
2240
+ comparison: 'length>',
2241
+ valueType: 'array',
2242
+ },
2243
+ ...gatingRequiredValues,
2244
+ ],
2245
+ impact: 'low',
2246
+ exclusiveGroup,
2247
+ sourceLocation: jsxUsage.sourceLocation
2248
+ ? {
2249
+ lineNumber: jsxUsage.sourceLocation.lineNumber,
2250
+ column: jsxUsage.sourceLocation.column,
2251
+ }
2252
+ : undefined,
2253
+ codeSnippet: jsxUsage.sourceLocation?.codeSnippet,
2254
+ };
2255
+
2256
+ if (!seenFlowIds.has(manyFlow.id)) {
2257
+ seenFlowIds.add(manyFlow.id);
2258
+ flows.push(manyFlow);
2259
+ }
2260
+ }
2261
+ }
2262
+ }
1973
2263
  }
1974
2264
  }
1975
2265
 
@@ -19,7 +19,7 @@ export interface GenerateFlowsFromJsxUsagesArgs {
19
19
  /** Map of controllable paths to their types */
20
20
  attributesMap: Record<string, string>;
21
21
  /** Map from local variable names to data sources */
22
- equivalentSignatureVariables: Record<string, string>;
22
+ equivalentSignatureVariables: Record<string, string | string[]>;
23
23
  /** Map from full paths to short paths */
24
24
  fullToShortPathMap: Record<string, string>;
25
25
  /** Structure map for type lookup */
@@ -7,58 +7,12 @@ import {
7
7
 
8
8
  export interface Statement {
9
9
  structure: { [key: string]: string };
10
- equivalentVariables: { [key: string]: string };
10
+ // Supports multiple equivalencies per key for OR expressions (e.g., x = a || b)
11
+ equivalentVariables: { [key: string]: string | string[] };
11
12
  llmCall?: LlmCall;
12
13
  }
13
14
 
14
15
  export default function mergeStatements(statements: Statement[]) {
15
- // Debug: Log input statements to understand what we're merging
16
- const hasStateStructure = statements.some((s) =>
17
- Object.keys(s.structure || {}).some(
18
- (k) => k === 'state' || k.includes('state'),
19
- ),
20
- );
21
- const hasStateEquiv = statements.some((s) =>
22
- Object.keys(s.equivalentVariables || {}).some(
23
- (k) => k === 'state' || k.includes('state'),
24
- ),
25
- );
26
- if (hasStateStructure || hasStateEquiv) {
27
- console.log(
28
- `[UNION-TYPE] mergeStatements INPUT: ${statements.length} statements`,
29
- );
30
- statements.forEach((s, i) => {
31
- const stateInStructure = Object.entries(s.structure || {}).filter(
32
- ([k]) => k === 'state' || k.includes('state'),
33
- );
34
- const stateInEquiv = Object.entries(s.equivalentVariables || {}).filter(
35
- ([k]) => k === 'state' || k.includes('state'),
36
- );
37
- const sigInEquiv = Object.entries(s.equivalentVariables || {}).filter(
38
- ([k, v]) => k === 'state' || v === 'signature[0]',
39
- );
40
- if (
41
- stateInStructure.length > 0 ||
42
- stateInEquiv.length > 0 ||
43
- sigInEquiv.length > 0
44
- ) {
45
- console.log(`[UNION-TYPE] Statement ${i}:`);
46
- if (stateInStructure.length > 0)
47
- console.log(
48
- `[UNION-TYPE] structure: ${JSON.stringify(Object.fromEntries(stateInStructure))}`,
49
- );
50
- if (stateInEquiv.length > 0)
51
- console.log(
52
- `[UNION-TYPE] equivalentVariables (state): ${JSON.stringify(Object.fromEntries(stateInEquiv))}`,
53
- );
54
- if (sigInEquiv.length > 0)
55
- console.log(
56
- `[UNION-TYPE] equivalentVariables (sig): ${JSON.stringify(Object.fromEntries(sigInEquiv))}`,
57
- );
58
- }
59
- });
60
- }
61
-
62
16
  // This should be handled by the AST Analyzer but it currently has a bug
63
17
  const ensureFunctionCallReturnValue = (path: string, value: string) => {
64
18
  if (path.endsWith(')') && value !== 'function') {
@@ -172,12 +126,54 @@ export default function mergeStatements(statements: Statement[]) {
172
126
  // settingsData → fetcher.data.data uses fetcher::cyDuplicateKey1::.data.data
173
127
  const activeRemappings: Record<string, string> = {};
174
128
 
129
+ // Helper to normalize value to array and process
130
+ const normalizeValue = (value: string | string[]): string[] =>
131
+ Array.isArray(value) ? value : [value];
132
+
175
133
  const equivalentVariables = statements.reduce((acc: any, result) => {
176
- for (const [key, value] of Object.entries(
134
+ for (const [key, rawValue] of Object.entries(
177
135
  result.equivalentVariables ?? {},
178
136
  )) {
137
+ // Handle arrays from AST analyzer (OR expressions like x = a || b)
138
+ // These should be preserved as arrays to track all sources
139
+ if (Array.isArray(rawValue)) {
140
+ const keyOptions = deBinaryPath(key);
141
+ const validValues = rawValue.filter(
142
+ (v): v is string => typeof v === 'string' && v.length > 0,
143
+ );
144
+ if (validValues.length === 0) continue;
145
+
146
+ // Process each array value through deBinaryPath
147
+ const allValueOptions: string[] = [];
148
+ for (const value of validValues) {
149
+ const valueOptions = deBinaryPath(value);
150
+ allValueOptions.push(...valueOptions);
151
+ }
152
+
153
+ // Remove duplicates while preserving order
154
+ const uniqueValueOptions = [...new Set(allValueOptions)];
155
+ const finalValue =
156
+ uniqueValueOptions.length === 1
157
+ ? uniqueValueOptions[0]
158
+ : uniqueValueOptions;
159
+
160
+ delete result.equivalentVariables[key];
161
+ if (keyOptions.length >= 2) {
162
+ for (const keyOption of keyOptions) {
163
+ result.equivalentVariables[keyOption] = finalValue;
164
+ }
165
+ } else {
166
+ result.equivalentVariables[keyOptions[0]] = finalValue;
167
+ }
168
+ continue;
169
+ }
170
+
171
+ // Original logic for string values with potential binary expressions in the string
172
+ const firstValue = rawValue;
173
+ if (!firstValue) continue;
174
+
179
175
  const keyOptions = deBinaryPath(key);
180
- const valueOptions = deBinaryPath(value);
176
+ const valueOptions = deBinaryPath(firstValue);
181
177
  delete result.equivalentVariables[key];
182
178
  if (keyOptions.length === 2) {
183
179
  if (valueOptions.length === 2) {
@@ -199,27 +195,38 @@ export default function mergeStatements(statements: Statement[]) {
199
195
  }
200
196
  }
201
197
 
202
- for (const [key, value] of Object.entries(
198
+ for (const [key, rawValue] of Object.entries(
203
199
  result.equivalentVariables ?? {},
204
200
  )) {
201
+ // Normalize to array for consistent handling
202
+ const values = normalizeValue(rawValue);
203
+
205
204
  // Apply any active remappings to the VALUE, but ONLY for property paths
206
205
  // e.g., if fetcher was remapped to fetcher::cyDuplicateKey1::,
207
206
  // then "fetcher.data.data" should become "fetcher::cyDuplicateKey1::.data.data"
208
207
  // However, we should NOT remap standalone variable references like "description"
209
208
  // because that would lose type information from the original variable.
210
- let remappedValue = value;
211
- for (const [originalKey, remappedKey] of Object.entries(
212
- activeRemappings,
213
- )) {
214
- // Only remap if the value is a property path (contains a dot after the key)
215
- // This preserves type information for simple variable references
216
- const keyRegex = new RegExp(
217
- `^${originalKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?=\\.)`,
218
- );
219
- if (keyRegex.test(remappedValue)) {
220
- remappedValue = remappedValue.replace(keyRegex, remappedKey);
209
+ const remappedValues = values.map((value) => {
210
+ let remappedValue = value;
211
+ for (const [originalKey, remappedKey] of Object.entries(
212
+ activeRemappings,
213
+ )) {
214
+ // Only remap if the value is a property path (contains a dot after the key)
215
+ // This preserves type information for simple variable references
216
+ const keyRegex = new RegExp(
217
+ `^${originalKey.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?=\\.)`,
218
+ );
219
+ if (keyRegex.test(remappedValue)) {
220
+ remappedValue = remappedValue.replace(keyRegex, remappedKey);
221
+ }
221
222
  }
222
- }
223
+ return remappedValue;
224
+ });
225
+
226
+ // Use array if multiple values, string if single
227
+ const remappedValue =
228
+ remappedValues.length === 1 ? remappedValues[0] : remappedValues;
229
+ const firstRemappedValue = remappedValues[0];
223
230
 
224
231
  if (acc[key]) {
225
232
  if (acc[key] === remappedValue) continue;
@@ -247,45 +254,32 @@ export default function mergeStatements(statements: Statement[]) {
247
254
  // If we have equivalentVariables[A] = B, and structure[A] is more specific than structure[B],
248
255
  // then update structure[B] to match structure[A]
249
256
  // This handles cases like: "state" -> "signature[0]" where "state" has a union type
250
-
251
- // Debug: Log union types in merged structure
252
- const unionTypesInStructure = Object.entries(structure).filter(
253
- ([_, v]) => typeof v === 'string' && v.includes("'") && v.includes(' | '),
254
- );
255
- if (unionTypesInStructure.length > 0) {
256
- console.log(
257
- `[UNION-TYPE] mergeStatements: Found ${unionTypesInStructure.length} union types in merged structure`,
258
- );
259
- unionTypesInStructure.forEach(([k, v]) =>
260
- console.log(`[UNION-TYPE] structure["${k}"] = ${v}`),
261
- );
262
- console.log(
263
- `[UNION-TYPE] mergeStatements: equivalentVariables = ${JSON.stringify(equivalentVariables)}`,
264
- );
265
- }
266
-
267
- for (const [source, target] of Object.entries(equivalentVariables) as [
268
- string,
257
+ for (const [source, rawTarget] of Object.entries(equivalentVariables) as [
269
258
  string,
259
+ string | string[],
270
260
  ][]) {
261
+ // Normalize to array for consistent handling
262
+ const targets = Array.isArray(rawTarget) ? rawTarget : [rawTarget];
271
263
  const sourceType = structure[source];
272
- const targetType = structure[target];
273
264
 
274
- // Check if source has a more specific type (union type vs generic type)
275
- if (sourceType) {
276
- const sourceIsUnion =
277
- sourceType.includes("'") && sourceType.includes(' | ');
265
+ for (const target of targets) {
266
+ if (typeof target !== 'string') continue;
278
267
 
279
- if (sourceIsUnion) {
280
- // Target doesn't exist or is generic - propagate the union type
281
- const targetIsGeneric =
282
- !targetType || targetType === 'string' || targetType === 'unknown';
268
+ const targetType = structure[target];
283
269
 
284
- if (targetIsGeneric) {
285
- console.log(
286
- `[UNION-TYPE] mergeStatements: Propagating "${source}" (${sourceType}) -> "${target}"`,
287
- );
288
- structure[target] = sourceType;
270
+ // Check if source has a more specific type (union type vs generic type)
271
+ if (sourceType) {
272
+ const sourceIsUnion =
273
+ sourceType.includes("'") && sourceType.includes(' | ');
274
+
275
+ if (sourceIsUnion) {
276
+ // Target doesn't exist or is generic - propagate the union type
277
+ const targetIsGeneric =
278
+ !targetType || targetType === 'string' || targetType === 'unknown';
279
+
280
+ if (targetIsGeneric) {
281
+ structure[target] = sourceType;
282
+ }
289
283
  }
290
284
  }
291
285
  }
@@ -139,16 +139,19 @@ export function gatherAttributesMap(
139
139
  // Use merged type if available, otherwise fall back to isolated type
140
140
  const typeValue =
141
141
  mergedSignatureSchema[key] ?? isolatedSignatureSchema[key];
142
- const equivalentSignatureVariable =
143
- equivalentSignatureVariables[keyParts[0]];
144
- const equivalentSignatureVariableParts = splitOutsideParenthesesAndArrays(
145
- equivalentSignatureVariable,
146
- );
142
+ // Handle array case (OR expressions) - use first element if array
143
+ const rawEquivalent = equivalentSignatureVariables[keyParts[0]];
144
+ const equivalentSignatureVariable = Array.isArray(rawEquivalent)
145
+ ? rawEquivalent[0]
146
+ : rawEquivalent;
147
+ const equivalentSignatureVariableParts = equivalentSignatureVariable
148
+ ? splitOutsideParenthesesAndArrays(equivalentSignatureVariable)
149
+ : [];
147
150
  if (
148
151
  equivalentSignatureVariable &&
149
- !equivalentSignatureVariableParts[0].includes('(')
152
+ !equivalentSignatureVariableParts[0]?.includes('(')
150
153
  ) {
151
- const equivalentKey = equivalentSignatureVariables[keyParts[0]];
154
+ const equivalentKey = equivalentSignatureVariable;
152
155
  const equivalentPath = joinParenthesesAndArrays([
153
156
  equivalentKey,
154
157
  ...keyParts.slice(1),