@codeyam/codeyam-cli 0.1.0-staging.483fdc2 → 0.1.0-staging.62d4615

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 (213) 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 +3 -3
  4. package/analyzer-template/packages/ai/package.json +2 -2
  5. package/analyzer-template/packages/ai/src/lib/analyzeScope.ts +9 -1
  6. package/analyzer-template/packages/ai/src/lib/astScopes/patterns/forInStatementHandler.ts +10 -17
  7. package/analyzer-template/packages/ai/src/lib/dataStructure/ScopeDataStructure.ts +154 -1
  8. package/analyzer-template/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/JavascriptFrameworkManager.ts +5 -1
  9. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.ts +11 -2
  10. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.ts +2 -2
  11. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/coerceObjectsToPrimitivesBySchema.ts +70 -0
  12. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertDotNotation.ts +70 -1
  13. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.ts +20 -1
  14. package/analyzer-template/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.ts +84 -19
  15. package/analyzer-template/packages/ai/src/lib/generateEntityDataStructure.ts +58 -3
  16. package/analyzer-template/packages/ai/src/lib/generateEntityScenarioData.ts +14 -0
  17. package/analyzer-template/packages/ai/src/lib/isolateScopes.ts +51 -3
  18. package/analyzer-template/packages/analyze/index.ts +2 -0
  19. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.ts +51 -3
  20. package/analyzer-template/packages/analyze/src/lib/files/analyze/analyzeEntities.ts +5 -0
  21. package/analyzer-template/packages/analyze/src/lib/files/scenarios/TransformationTracer.ts +1315 -0
  22. package/analyzer-template/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.ts +4 -0
  23. package/analyzer-template/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.ts +9 -1
  24. package/analyzer-template/packages/analyze/src/lib/files/scenarios/generateDataStructure.ts +194 -15
  25. package/analyzer-template/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.ts +260 -22
  26. package/analyzer-template/packages/analyze/src/lib/index.ts +1 -0
  27. package/analyzer-template/packages/database/package.json +1 -1
  28. package/analyzer-template/packages/github/dist/types/src/types/ProjectMetadata.d.ts +3 -0
  29. package/analyzer-template/packages/github/dist/types/src/types/ProjectMetadata.d.ts.map +1 -1
  30. package/analyzer-template/packages/types/src/types/ProjectMetadata.ts +1 -0
  31. package/analyzer-template/packages/utils/dist/types/src/types/ProjectMetadata.d.ts +3 -0
  32. package/analyzer-template/packages/utils/dist/types/src/types/ProjectMetadata.d.ts.map +1 -1
  33. package/analyzer-template/project/constructMockCode.ts +36 -1
  34. package/analyzer-template/project/writeMockDataTsx.ts +111 -17
  35. package/analyzer-template/project/writeScenarioComponents.ts +60 -12
  36. package/analyzer-template/project/writeSimpleRoot.ts +21 -11
  37. package/background/src/lib/virtualized/project/constructMockCode.js +30 -1
  38. package/background/src/lib/virtualized/project/constructMockCode.js.map +1 -1
  39. package/background/src/lib/virtualized/project/writeMockDataTsx.js +95 -13
  40. package/background/src/lib/virtualized/project/writeMockDataTsx.js.map +1 -1
  41. package/background/src/lib/virtualized/project/writeScenarioComponents.js +60 -15
  42. package/background/src/lib/virtualized/project/writeScenarioComponents.js.map +1 -1
  43. package/background/src/lib/virtualized/project/writeSimpleRoot.js +21 -11
  44. package/background/src/lib/virtualized/project/writeSimpleRoot.js.map +1 -1
  45. package/codeyam-cli/scripts/apply-setup.js +43 -9
  46. package/codeyam-cli/scripts/apply-setup.js.map +1 -1
  47. package/codeyam-cli/src/commands/memory.js +12 -21
  48. package/codeyam-cli/src/commands/memory.js.map +1 -1
  49. package/codeyam-cli/src/utils/backgroundServer.js +4 -0
  50. package/codeyam-cli/src/utils/backgroundServer.js.map +1 -1
  51. package/codeyam-cli/src/utils/install-skills.js +23 -0
  52. package/codeyam-cli/src/utils/install-skills.js.map +1 -1
  53. package/codeyam-cli/src/utils/queue/job.js +4 -0
  54. package/codeyam-cli/src/utils/queue/job.js.map +1 -1
  55. package/codeyam-cli/src/utils/ruleReflection/__tests__/confusionDetector.test.js +82 -0
  56. package/codeyam-cli/src/utils/ruleReflection/__tests__/confusionDetector.test.js.map +1 -0
  57. package/codeyam-cli/src/utils/ruleReflection/__tests__/contextBuilder.test.js +128 -0
  58. package/codeyam-cli/src/utils/ruleReflection/__tests__/contextBuilder.test.js.map +1 -0
  59. package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/assertRules.js +67 -0
  60. package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/assertRules.js.map +1 -0
  61. package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/captureFixture.js +105 -0
  62. package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/captureFixture.js.map +1 -0
  63. package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/loadCapturedFixture.js +34 -0
  64. package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/loadCapturedFixture.js.map +1 -0
  65. package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/runClaude.js +162 -0
  66. package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/runClaude.js.map +1 -0
  67. package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/setupTempProject.js +75 -0
  68. package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/helpers/setupTempProject.js.map +1 -0
  69. package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/ruleReflectionE2E.test.js +285 -0
  70. package/codeyam-cli/src/utils/ruleReflection/__tests__/integration/ruleReflectionE2E.test.js.map +1 -0
  71. package/codeyam-cli/src/utils/ruleReflection/__tests__/promptBuilder.test.js +83 -0
  72. package/codeyam-cli/src/utils/ruleReflection/__tests__/promptBuilder.test.js.map +1 -0
  73. package/codeyam-cli/src/utils/ruleReflection/__tests__/transcriptParser.test.js +127 -0
  74. package/codeyam-cli/src/utils/ruleReflection/__tests__/transcriptParser.test.js.map +1 -0
  75. package/codeyam-cli/src/utils/ruleReflection/confusionDetector.js +50 -0
  76. package/codeyam-cli/src/utils/ruleReflection/confusionDetector.js.map +1 -0
  77. package/codeyam-cli/src/utils/ruleReflection/contextBuilder.js +96 -0
  78. package/codeyam-cli/src/utils/ruleReflection/contextBuilder.js.map +1 -0
  79. package/codeyam-cli/src/utils/ruleReflection/index.js +5 -0
  80. package/codeyam-cli/src/utils/ruleReflection/index.js.map +1 -0
  81. package/codeyam-cli/src/utils/ruleReflection/promptBuilder.js +33 -0
  82. package/codeyam-cli/src/utils/ruleReflection/promptBuilder.js.map +1 -0
  83. package/codeyam-cli/src/utils/ruleReflection/transcriptParser.js +85 -0
  84. package/codeyam-cli/src/utils/ruleReflection/transcriptParser.js.map +1 -0
  85. package/codeyam-cli/src/utils/ruleReflection/types.js +5 -0
  86. package/codeyam-cli/src/utils/ruleReflection/types.js.map +1 -0
  87. package/codeyam-cli/src/utils/rules/__tests__/ruleState.test.js +293 -0
  88. package/codeyam-cli/src/utils/rules/__tests__/ruleState.test.js.map +1 -0
  89. package/codeyam-cli/src/utils/rules/index.js +1 -0
  90. package/codeyam-cli/src/utils/rules/index.js.map +1 -1
  91. package/codeyam-cli/src/utils/rules/parser.js +0 -28
  92. package/codeyam-cli/src/utils/rules/parser.js.map +1 -1
  93. package/codeyam-cli/src/utils/rules/ruleState.js +150 -0
  94. package/codeyam-cli/src/utils/rules/ruleState.js.map +1 -0
  95. package/codeyam-cli/src/utils/rules/staleness.js +14 -9
  96. package/codeyam-cli/src/utils/rules/staleness.js.map +1 -1
  97. package/codeyam-cli/src/webserver/app/lib/database.js +1 -0
  98. package/codeyam-cli/src/webserver/app/lib/database.js.map +1 -1
  99. package/codeyam-cli/src/webserver/build/client/assets/CopyButton-CA3JxPb7.js +1 -0
  100. package/codeyam-cli/src/webserver/build/client/assets/{EntityItem-DsN1wKrm.js → EntityItem-B86KKU7e.js} +1 -1
  101. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeBadge-DLqD3qNt.js → EntityTypeBadge-B5ctlSYt.js} +1 -1
  102. package/codeyam-cli/src/webserver/build/client/assets/{EntityTypeIcon-Ba2JVPzP.js → EntityTypeIcon-BqY8gDAW.js} +1 -1
  103. package/codeyam-cli/src/webserver/build/client/assets/{InlineSpinner-C8lyxW9k.js → InlineSpinner-ClaLpuOo.js} +1 -1
  104. package/codeyam-cli/src/webserver/build/client/assets/{InteractivePreview-aht4aafF.js → InteractivePreview-BDhPilK7.js} +2 -2
  105. package/codeyam-cli/src/webserver/build/client/assets/{LibraryFunctionPreview-CVtiBnY5.js → LibraryFunctionPreview-VeqEBv9v.js} +1 -1
  106. package/codeyam-cli/src/webserver/build/client/assets/{LoadingDots-B0GLXMsr.js → LoadingDots-Bs7Nn1Jr.js} +1 -1
  107. package/codeyam-cli/src/webserver/build/client/assets/{LogViewer-xgeCVgSM.js → LogViewer-Bm3PmcCz.js} +1 -1
  108. package/codeyam-cli/src/webserver/build/client/assets/{ReportIssueModal-OApQuNyq.js → ReportIssueModal-C6PKeMYR.js} +3 -8
  109. package/codeyam-cli/src/webserver/build/client/assets/{SafeScreenshot-DuDvi0jm.js → SafeScreenshot-Gq3Ocjo6.js} +1 -1
  110. package/codeyam-cli/src/webserver/build/client/assets/{ScenarioViewer-DzccYyI8.js → ScenarioViewer-BNLaXBHR.js} +2 -2
  111. package/codeyam-cli/src/webserver/build/client/assets/{TruncatedFilePath-DyFZkK0l.js → TruncatedFilePath-CiwXDxLh.js} +1 -1
  112. package/codeyam-cli/src/webserver/build/client/assets/{_index-BwqWJOgH.js → _index-B3TDXxnk.js} +1 -1
  113. package/codeyam-cli/src/webserver/build/client/assets/{activity.(_tab)-BwavGCpm.js → activity.(_tab)-DD1r_QU0.js} +6 -11
  114. package/codeyam-cli/src/webserver/build/client/assets/agent-transcripts-DfKzxuoe.js +11 -0
  115. package/codeyam-cli/src/webserver/build/client/assets/api.agent-transcripts-l0sNRNKZ.js +1 -0
  116. package/codeyam-cli/src/webserver/build/client/assets/api.save-fixture-l0sNRNKZ.js +1 -0
  117. package/codeyam-cli/src/webserver/build/client/assets/book-open-PttOB2SF.js +6 -0
  118. package/codeyam-cli/src/webserver/build/client/assets/{chevron-down-Cx24_aWc.js → chevron-down-TJp6ofnp.js} +1 -1
  119. package/codeyam-cli/src/webserver/build/client/assets/{chunk-EPOLDU6W-CXRTFQ3F.js → chunk-JZWAC4HX-JE9ZIoBl.js} +12 -12
  120. package/codeyam-cli/src/webserver/build/client/assets/{circle-check-BOARzkeR.js → circle-check-CXhHQYrI.js} +1 -1
  121. package/codeyam-cli/src/webserver/build/client/assets/copy-6y9ALfGT.js +11 -0
  122. package/codeyam-cli/src/webserver/build/client/assets/{createLucideIcon-BdhJEx6B.js → createLucideIcon-Ca9fAY46.js} +1 -1
  123. package/codeyam-cli/src/webserver/build/client/assets/{dev.empty-BBnGWYga.js → dev.empty-C5lqplTC.js} +1 -1
  124. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha._-BJUiQqZF.js → entity._sha._-n38keI1k.js} +10 -10
  125. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha.scenarios._scenarioId.fullscreen-DavjRmOY.js → entity._sha.scenarios._scenarioId.fullscreen-CBoafmVs.js} +1 -1
  126. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.create-scenario-D1T4TGjf.js → entity._sha_.create-scenario-DGgZjdFg.js} +1 -1
  127. package/codeyam-cli/src/webserver/build/client/assets/{entity._sha_.edit._scenarioId-CTBG2mmz.js → entity._sha_.edit._scenarioId-38yPijoD.js} +1 -1
  128. package/codeyam-cli/src/webserver/build/client/assets/{entry.client-CS2cb_eZ.js → entry.client-BSHEfydn.js} +1 -1
  129. package/codeyam-cli/src/webserver/build/client/assets/{fileTableUtils-DMJ7zii9.js → fileTableUtils-DCPhhSMo.js} +1 -1
  130. package/codeyam-cli/src/webserver/build/client/assets/{files-CJ6lTdTA.js → files-Dk8wkAS7.js} +1 -1
  131. package/codeyam-cli/src/webserver/build/client/assets/{git-CPTZZ-JZ.js → git-DXnyr8uP.js} +1 -1
  132. package/codeyam-cli/src/webserver/build/client/assets/globals-Bh6jH0cL.css +1 -0
  133. package/codeyam-cli/src/webserver/build/client/assets/{index-lzqtyFU8.js → index-CcsFv748.js} +1 -1
  134. package/codeyam-cli/src/webserver/build/client/assets/{index-B1h680n5.js → index-ChN9-fAY.js} +1 -1
  135. package/codeyam-cli/src/webserver/build/client/assets/labs-BUvfJMNR.js +1 -0
  136. package/codeyam-cli/src/webserver/build/client/assets/{loader-circle-B7B9V-bu.js → loader-circle-CTqLEAGU.js} +1 -1
  137. package/codeyam-cli/src/webserver/build/client/assets/manifest-d4e77269.js +1 -0
  138. package/codeyam-cli/src/webserver/build/client/assets/memory-DCHBwHou.js +76 -0
  139. package/codeyam-cli/src/webserver/build/client/assets/pause-D6vreykR.js +11 -0
  140. package/codeyam-cli/src/webserver/build/client/assets/root-D6oziHts.js +62 -0
  141. package/codeyam-cli/src/webserver/build/client/assets/{search-CxXUmBSd.js → search-B8VUL8nl.js} +1 -1
  142. package/codeyam-cli/src/webserver/build/client/assets/{settings-CS5f3WzT.js → settings-B2X7lJgQ.js} +1 -1
  143. package/codeyam-cli/src/webserver/build/client/assets/{simulations-DwFIBT09.js → simulations-CPoAg7Zo.js} +1 -1
  144. package/codeyam-cli/src/webserver/build/client/assets/terminal-BrCP7uQo.js +11 -0
  145. package/codeyam-cli/src/webserver/build/client/assets/{triangle-alert-B6LgvRJg.js → triangle-alert-BZz2NjYa.js} +1 -1
  146. package/codeyam-cli/src/webserver/build/client/assets/{useCustomSizes-C1v1PQzo.js → useCustomSizes-DNwUduNu.js} +1 -1
  147. package/codeyam-cli/src/webserver/build/client/assets/{useLastLogLine-aSv48UbS.js → useLastLogLine-COky1GVF.js} +1 -1
  148. package/codeyam-cli/src/webserver/build/client/assets/{useReportContext-DYxHZQuP.js → useReportContext-CpZgwliL.js} +1 -1
  149. package/codeyam-cli/src/webserver/build/client/assets/{useToast-mBRpZPiu.js → useToast-Bv9JFvUO.js} +1 -1
  150. package/codeyam-cli/src/webserver/build/server/assets/{index-TD1f-DHV.js → index-C0KrUQp-.js} +1 -1
  151. package/codeyam-cli/src/webserver/build/server/assets/server-build-C2h1v1XD.js +260 -0
  152. package/codeyam-cli/src/webserver/build/server/index.js +1 -1
  153. package/codeyam-cli/src/webserver/build-info.json +5 -5
  154. package/codeyam-cli/templates/codeyam-memory-hook.sh +9 -10
  155. package/codeyam-cli/templates/codeyam:memory.md +10 -9
  156. package/codeyam-cli/templates/codeyam:new-rule.md +0 -39
  157. package/codeyam-cli/templates/rule-notification-hook.py +54 -0
  158. package/codeyam-cli/templates/rule-reflection-hook.py +356 -89
  159. package/codeyam-cli/templates/rules-instructions.md +19 -22
  160. package/package.json +2 -2
  161. package/packages/ai/src/lib/analyzeScope.js +7 -1
  162. package/packages/ai/src/lib/analyzeScope.js.map +1 -1
  163. package/packages/ai/src/lib/astScopes/patterns/forInStatementHandler.js +10 -14
  164. package/packages/ai/src/lib/astScopes/patterns/forInStatementHandler.js.map +1 -1
  165. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js +134 -1
  166. package/packages/ai/src/lib/dataStructure/ScopeDataStructure.js.map +1 -1
  167. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/JavascriptFrameworkManager.js +5 -1
  168. package/packages/ai/src/lib/dataStructure/equivalencyManagers/frameworks/JavascriptFrameworkManager.js.map +1 -1
  169. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js +11 -2
  170. package/packages/ai/src/lib/dataStructure/helpers/cleanKnownObjectFunctions.js.map +1 -1
  171. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js +2 -2
  172. package/packages/ai/src/lib/dataStructure/helpers/cleanNonObjectFunctions.js.map +1 -1
  173. package/packages/ai/src/lib/dataStructure/helpers/coerceObjectsToPrimitivesBySchema.js +63 -0
  174. package/packages/ai/src/lib/dataStructure/helpers/coerceObjectsToPrimitivesBySchema.js.map +1 -0
  175. package/packages/ai/src/lib/dataStructure/helpers/convertDotNotation.js +63 -1
  176. package/packages/ai/src/lib/dataStructure/helpers/convertDotNotation.js.map +1 -1
  177. package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js +15 -1
  178. package/packages/ai/src/lib/dataStructure/helpers/convertTypeAnnotationsToValues.js.map +1 -1
  179. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js +78 -17
  180. package/packages/ai/src/lib/dataStructure/helpers/fillInSchemaGapsAndUnknowns.js.map +1 -1
  181. package/packages/ai/src/lib/generateEntityDataStructure.js +46 -2
  182. package/packages/ai/src/lib/generateEntityDataStructure.js.map +1 -1
  183. package/packages/ai/src/lib/generateEntityScenarioData.js +10 -0
  184. package/packages/ai/src/lib/generateEntityScenarioData.js.map +1 -1
  185. package/packages/ai/src/lib/isolateScopes.js +39 -3
  186. package/packages/ai/src/lib/isolateScopes.js.map +1 -1
  187. package/packages/analyze/index.js +1 -0
  188. package/packages/analyze/index.js.map +1 -1
  189. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js +41 -1
  190. package/packages/analyze/src/lib/files/analyze/analyzeEntities/prepareDataStructures.js.map +1 -1
  191. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js +3 -0
  192. package/packages/analyze/src/lib/files/analyze/analyzeEntities.js.map +1 -1
  193. package/packages/analyze/src/lib/files/scenarios/TransformationTracer.js +880 -0
  194. package/packages/analyze/src/lib/files/scenarios/TransformationTracer.js.map +1 -0
  195. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js +5 -1
  196. package/packages/analyze/src/lib/files/scenarios/enrichArrayTypesFromChildSignatures.js.map +1 -1
  197. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js +9 -1
  198. package/packages/analyze/src/lib/files/scenarios/gatherDataForMocks.js.map +1 -1
  199. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js +116 -13
  200. package/packages/analyze/src/lib/files/scenarios/generateDataStructure.js.map +1 -1
  201. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js +231 -22
  202. package/packages/analyze/src/lib/files/scenarios/mergeInDependentDataStructure.js.map +1 -1
  203. package/packages/analyze/src/lib/index.js +1 -0
  204. package/packages/analyze/src/lib/index.js.map +1 -1
  205. package/codeyam-cli/src/utils/reviewedRules.js +0 -92
  206. package/codeyam-cli/src/utils/reviewedRules.js.map +0 -1
  207. package/codeyam-cli/src/webserver/build/client/assets/copy-Bb-80kDT.js +0 -6
  208. package/codeyam-cli/src/webserver/build/client/assets/file-code-Dhef1kWN.js +0 -6
  209. package/codeyam-cli/src/webserver/build/client/assets/globals-CX9f-5xM.css +0 -1
  210. package/codeyam-cli/src/webserver/build/client/assets/manifest-bba56ec1.js +0 -1
  211. package/codeyam-cli/src/webserver/build/client/assets/memory-DuTFSyJ2.js +0 -92
  212. package/codeyam-cli/src/webserver/build/client/assets/root-DTfSQARG.js +0 -62
  213. package/codeyam-cli/src/webserver/build/server/assets/server-build-BQ-1XyEa.js +0 -258
@@ -192,6 +192,36 @@ const safeString = (s: string, level: number) => {
192
192
  return s?.replace ? s.replace(/"/g, "'").replace(/\n/g, '\\n') : s;
193
193
  };
194
194
 
195
+ /**
196
+ * Clean up malformed LLM-generated function values.
197
+ * The LLM sometimes generates function values with an invalid "(function)" prefix like:
198
+ * - "(function)() => {}"
199
+ * - "(function)(event) => {}"
200
+ * - "(function)(id) => {}"
201
+ *
202
+ * This strips the prefix and adds TypeScript `any` type annotations to parameters.
203
+ * Returns null if the value doesn't match the malformed pattern.
204
+ */
205
+ const cleanMalformedFunctionValue = (value: string): string | null => {
206
+ // Match "(function)" followed by optional params in parens, then "=>"
207
+ const match = value.match(/^\(function\)(\([^)]*\))\s*=>\s*(.*)$/);
208
+ if (!match) return null;
209
+
210
+ const [, paramsWithParens, body] = match;
211
+
212
+ // Extract parameter names and add `: any` type annotation
213
+ // e.g., "(event)" -> "(event: any)", "()" -> "()"
214
+ const params = paramsWithParens.slice(1, -1).trim(); // Remove outer parens
215
+ const typedParams = params
216
+ ? params
217
+ .split(',')
218
+ .map((p) => `${p.trim()}: any`)
219
+ .join(', ')
220
+ : '';
221
+
222
+ return `(${typedParams}) => ${body}`;
223
+ };
224
+
195
225
  /**
196
226
  * Check if a mock key's schema indicates it should be instantiated as a Map.
197
227
  * A Map is indicated by:
@@ -310,6 +340,38 @@ function extractAllKeys(obj: JsonTypeDefinition | undefined): string[] {
310
340
  */
311
341
  const BARE_TYPE_MARKERS = ['function', 'async-function'];
312
342
 
343
+ /**
344
+ * Check if a string is a TypeScript arrow function type annotation rather than
345
+ * a valid JavaScript arrow function expression.
346
+ *
347
+ * TypeScript arrow types look like function expressions (they contain '=>') but
348
+ * their "body" is a type name, not a JS expression:
349
+ * - "() => void"
350
+ * - "(event: MouseEvent) => void"
351
+ * - "() => string"
352
+ * - "() => Promise<void>"
353
+ * - "(id: string, name: string) => boolean"
354
+ *
355
+ * Valid JS arrow functions have actual expression bodies:
356
+ * - "() => {}"
357
+ * - "() => 'hello'"
358
+ * - "(x) => x + 1"
359
+ * - "() => { console.log('hi'); }"
360
+ */
361
+ const ARROW_TYPE_RETURN_TYPES =
362
+ /^(void|string|number|boolean|any|unknown|never|null|undefined|object|Date|Promise<\w+>)$/i;
363
+
364
+ const isArrowFunctionTypeAnnotation = (value: string): boolean => {
365
+ const arrowIndex = value.indexOf('=>');
366
+ if (arrowIndex === -1) return false;
367
+
368
+ const body = value.slice(arrowIndex + 2).trim();
369
+
370
+ // A real JS function would have {}, an expression, a string literal, etc.
371
+ // A type annotation has a bare type keyword as the "body".
372
+ return ARROW_TYPE_RETURN_TYPES.test(body);
373
+ };
374
+
313
375
  /**
314
376
  * Check if a value is a bare function type marker that should be filtered out.
315
377
  * These markers indicate the LLM detected a function but couldn't generate meaningful mock data.
@@ -811,22 +873,41 @@ const jsonToCode = (
811
873
  `CodeYam: Nested func transform (string->function): "${key}" -> "${funcName}": () => {}`,
812
874
  );
813
875
  } else if (looksLikeFunction) {
814
- // Handle newlines in function expressions:
815
- // - Newlines inside string literals must be escaped to avoid
816
- // "unterminated string literal" errors
817
- // - Structural newlines (between statements) should be kept as-is
818
- // to produce valid multi-line JavaScript
819
- //
820
- // We escape ONLY newlines that appear inside string literals (single,
821
- // double, or template quotes). Structural newlines are preserved.
822
- if (hasNewlines) {
823
- outputValue = escapeNewlinesInStringLiterals(stringValue);
876
+ // First, check for malformed LLM-generated function values like "(function)() => {}"
877
+ // These have an invalid "(function)" prefix that must be stripped
878
+ const cleanedFn = cleanMalformedFunctionValue(stringValue);
879
+ if (cleanedFn) {
880
+ outputValue = cleanedFn;
881
+ console.log(
882
+ `CodeYam: Nested func transform (malformed->clean): "${key}" -> "${funcName}": ${cleanedFn}`,
883
+ );
884
+ } else if (isArrowFunctionTypeAnnotation(stringValue)) {
885
+ // TypeScript arrow function type annotations like "() => void",
886
+ // "(event: MouseEvent) => void", "() => Promise<void>" contain '=>'
887
+ // so they pass the looksLikeFunction check, but they are TYPE syntax,
888
+ // not valid JavaScript expressions. Convert to a no-op function.
889
+ outputValue = '() => {}';
890
+ console.log(
891
+ `CodeYam: Nested func transform (type-annotation->function): "${key}" -> "${funcName}": () => {}`,
892
+ );
824
893
  } else {
825
- outputValue = stringValue;
894
+ // Handle newlines in function expressions:
895
+ // - Newlines inside string literals must be escaped to avoid
896
+ // "unterminated string literal" errors
897
+ // - Structural newlines (between statements) should be kept as-is
898
+ // to produce valid multi-line JavaScript
899
+ //
900
+ // We escape ONLY newlines that appear inside string literals (single,
901
+ // double, or template quotes). Structural newlines are preserved.
902
+ if (hasNewlines) {
903
+ outputValue = escapeNewlinesInStringLiterals(stringValue);
904
+ } else {
905
+ outputValue = stringValue;
906
+ }
907
+ console.log(
908
+ `CodeYam: Nested func transform (string): "${key}" -> "${funcName}": ${stringValue.substring(0, 30)}...`,
909
+ );
826
910
  }
827
- console.log(
828
- `CodeYam: Nested func transform (string): "${key}" -> "${funcName}": ${stringValue.substring(0, 30)}...`,
829
- );
830
911
  } else {
831
912
  // Plain string value - must be quoted
832
913
  outputValue = `"${safeString(stringValue, level)}"`;
@@ -854,9 +935,22 @@ const jsonToCode = (
854
935
  if (isBareTypeMarker(stringValue)) {
855
936
  continue;
856
937
  }
857
- keyStrings.push(
858
- `${levelSpacing}"${safeString(key, level)}": "${safeString(stringValue, level)}"`,
859
- );
938
+ // Check for malformed LLM-generated function values like "(function)(id) => {}"
939
+ // These can appear on plain keys (e.g., "closeToast") and need to be output as
940
+ // actual functions, not quoted strings
941
+ const cleanedPlainKeyFn = cleanMalformedFunctionValue(stringValue);
942
+ if (cleanedPlainKeyFn) {
943
+ keyStrings.push(
944
+ `${levelSpacing}"${safeString(key, level)}": ${cleanedPlainKeyFn}`,
945
+ );
946
+ console.log(
947
+ `CodeYam: Plain key func transform (malformed->clean): "${key}": ${cleanedPlainKeyFn}`,
948
+ );
949
+ } else {
950
+ keyStrings.push(
951
+ `${levelSpacing}"${safeString(key, level)}": "${safeString(stringValue, level)}"`,
952
+ );
953
+ }
860
954
  }
861
955
  } else {
862
956
  // Skip plain keys that have function-call versions (at the same nesting level where
@@ -987,10 +987,19 @@ function addMockToContent(
987
987
  /[.*+?^${}()|[\]\\]/g,
988
988
  '\\$&',
989
989
  );
990
- // Create regex that matches the call (with optional whitespace variations)
990
+ // Create regex that matches the call (with optional whitespace variations).
991
+ // TypeScript formatters commonly break type parameters across lines, e.g.:
992
+ // useLoaderData<
993
+ // typeof loader
994
+ // >()
995
+ // So we allow optional whitespace around < and > delimiters, not just
996
+ // where whitespace already exists in the call signature string.
991
997
  const callRegex = new RegExp(
992
- escapedCallSignature.replace(/\s+/g, '\\s*'),
993
- 'g',
998
+ escapedCallSignature
999
+ .replace(/\s+/g, '\\s*')
1000
+ .replace(/</g, '\\s*<\\s*')
1001
+ .replace(/>/g, '\\s*>\\s*'),
1002
+ 'gs',
994
1003
  );
995
1004
  fileContent = fileContent.replace(callRegex, `${mockFunctionName}()`);
996
1005
  }
@@ -1012,15 +1021,29 @@ function addMockToContent(
1012
1021
  rootAnalysis.metadata?.scenariosDataStructure?.dataForMocks;
1013
1022
 
1014
1023
  // Find matching call signature key in dataForMocks
1024
+ // IMPORTANT: First try exact match with type parameters (e.g., "useLoaderData<LoaderData>()")
1025
+ // to avoid picking the wrong variant (e.g., "useLoaderData<typeof loader>()" which may
1026
+ // have different properties). Fall back to base name matching only if exact match fails.
1015
1027
  const hookBaseName = importedExport.name.split(/[<(]/)[0];
1016
- const callSignatureKey = dataForMocks
1017
- ? Object.keys(dataForMocks).find((key) => {
1028
+ const expectedKey = importedExport.calls?.[0];
1029
+ let callSignatureKey: string | undefined;
1030
+
1031
+ if (dataForMocks) {
1032
+ const keys = Object.keys(dataForMocks);
1033
+
1034
+ // First try exact match with the expected call signature
1035
+ if (expectedKey && keys.includes(expectedKey)) {
1036
+ callSignatureKey = expectedKey;
1037
+ } else {
1038
+ // Fall back to base name matching
1039
+ callSignatureKey = keys.find((key) => {
1018
1040
  // Split on ., <, or ( to get the true base name
1019
1041
  // This handles both "useFlags()" -> "useFlags" and "trpc.useUtils()" -> "trpc"
1020
1042
  const keyBaseName = key.split(/[.<(]/)[0];
1021
1043
  return keyBaseName === hookBaseName;
1022
- })
1023
- : undefined;
1044
+ });
1045
+ }
1046
+ }
1024
1047
 
1025
1048
  // Use the call signature if found, otherwise construct it
1026
1049
  const dataKey =
@@ -1061,18 +1084,36 @@ function addMockToContent(
1061
1084
  // IMPORTANT: We inline scenarios().data() inside the function rather than
1062
1085
  // storing in a const - see comment in constructMockCode.ts for why.
1063
1086
  if (!mockCode) {
1064
- mockCode = `function ${importedExport.name}(...args) {
1065
- return scenarios().data()?.["${dataKey}"];
1087
+ mockCode = `// PATCHED: memoize to return stable reference (prevents infinite useEffect re-triggers)
1088
+ const _${importedExport.name}Ref = {
1089
+ current: null,
1090
+ };
1091
+ function ${importedExport.name}(...args) {
1092
+ if (!_${importedExport.name}Ref.current) {
1093
+ _${importedExport.name}Ref.current = scenarios().data()?.["${dataKey}"];
1094
+ }
1095
+ return _${importedExport.name}Ref.current;
1066
1096
  }`;
1067
1097
  }
1068
1098
  } else {
1069
1099
  // Helper to find matching call signature key from dataForMocks
1100
+ // IMPORTANT: First try exact match with type parameters (e.g., "useLoaderData<LoaderData>()")
1101
+ // to avoid picking the wrong variant. Fall back to base name matching only if needed.
1070
1102
  const hookBaseName = importedExport.name.split(/[<(]/)[0];
1103
+ const expectedKey = importedExport.calls?.[0];
1071
1104
  const findMatchingKey = (
1072
1105
  dataForMocks: Record<string, unknown> | undefined,
1073
1106
  ): string | undefined => {
1074
1107
  if (!dataForMocks) return undefined;
1075
- return Object.keys(dataForMocks).find((key) => {
1108
+ const keys = Object.keys(dataForMocks);
1109
+
1110
+ // First try exact match with the expected call signature
1111
+ if (expectedKey && keys.includes(expectedKey)) {
1112
+ return expectedKey;
1113
+ }
1114
+
1115
+ // Fall back to base name matching
1116
+ return keys.find((key) => {
1076
1117
  // Split on ., <, or ( to get the true base name
1077
1118
  // This handles both "useFlags()" -> "useFlags" and "trpc.useUtils()" -> "trpc"
1078
1119
  const keyBaseName = key.split(/[.<(]/)[0];
@@ -1131,8 +1172,15 @@ function addMockToContent(
1131
1172
  // IMPORTANT: We inline scenarios().data() inside the function rather than
1132
1173
  // storing in a const - see comment in constructMockCode.ts for why.
1133
1174
  if (!mockCode && dataKey) {
1134
- mockCode = `function ${importedExport.name}(...args) {
1135
- return scenarios().data()?.["${dataKey}"];
1175
+ mockCode = `// PATCHED: memoize to return stable reference (prevents infinite useEffect re-triggers)
1176
+ const _${importedExport.name}Ref = {
1177
+ current: null,
1178
+ };
1179
+ function ${importedExport.name}(...args) {
1180
+ if (!_${importedExport.name}Ref.current) {
1181
+ _${importedExport.name}Ref.current = scenarios().data()?.["${dataKey}"];
1182
+ }
1183
+ return _${importedExport.name}Ref.current;
1136
1184
  }`;
1137
1185
  }
1138
1186
  }
@@ -277,16 +277,17 @@ export default async function writeSimpleRoot(
277
277
  let mockKey: string | undefined;
278
278
 
279
279
  if (matchingKeys.length > 1) {
280
- // Parse root.tsx to find how this hook is used
281
- // Look for patterns like: const { prop1, prop2 } = useLoaderData()
282
- // or: const result = useLoaderData()
280
+ // Parse root.tsx to find how this hook is used, INCLUDING type parameters
281
+ // Look for patterns like: const { prop1, prop2 } = useLoaderData<LoaderData>()
282
+ // or: const result = useLoaderData<typeof loader>()
283
283
  const hookUsageRegex = new RegExp(
284
- `(?:const|let|var)\\s+(?:\\{([^}]+)\\}|([a-zA-Z_][a-zA-Z0-9_]*))\\s*=\\s*${entityName}(?:<[^>]*>)?\\s*\\(`,
284
+ `(?:const|let|var)\\s+(?:\\{([^}]+)\\}|([a-zA-Z_][a-zA-Z0-9_]*))\\s*=\\s*${entityName}(<[^>]+>)?\\s*\\(`,
285
285
  'g',
286
286
  );
287
287
 
288
288
  const matches = [...newContent.matchAll(hookUsageRegex)];
289
289
  const rootPropertyNames: string[] = [];
290
+ const typeParamsUsed: Set<string> = new Set();
290
291
 
291
292
  for (const match of matches) {
292
293
  if (match[1]) {
@@ -300,17 +301,26 @@ export default async function writeSimpleRoot(
300
301
  // Simple assignment: const result = ...
301
302
  rootPropertyNames.push(match[2]);
302
303
  }
304
+ // Capture type parameters used (e.g., "<LoaderData>", "<typeof loader>")
305
+ if (match[3]) {
306
+ typeParamsUsed.add(match[3]);
307
+ }
303
308
  }
304
309
 
305
- // Find a key that matches by call signature
306
- // For call signature format (e.g., "useLoaderData()"), just use the first match
307
- mockKey = matchingKeys[0];
310
+ // First, try to find a key that matches the exact type parameters used in the source
311
+ // e.g., if source has useLoaderData<LoaderData>(), prefer "useLoaderData<LoaderData>()" key
312
+ if (typeParamsUsed.size > 0) {
313
+ for (const typeParam of typeParamsUsed) {
314
+ const exactKey = `${entityName}${typeParam}()`;
315
+ if (matchingKeys.includes(exactKey)) {
316
+ mockKey = exactKey;
317
+ break;
318
+ }
319
+ }
320
+ }
308
321
 
309
- // Fallback: if no match found but there are multiple keys,
310
- // exclude the route's key (which we can identify from mockedImports variable names)
322
+ // Fallback: if no exact type parameter match, use the first match
311
323
  if (!mockKey) {
312
- // The route's variable names would be in its own analysis
313
- // For simplicity, just use the first key that's different from the most common pattern
314
324
  mockKey = matchingKeys[0];
315
325
  }
316
326
  } else if (matchingKeys.length === 1) {
@@ -1170,6 +1170,16 @@ options) {
1170
1170
  returnValueContents = `${arrayBasePath}?.map((__item__, __idx__) => (\n${indent(trimmedContent)}\n))`;
1171
1171
  }
1172
1172
  else {
1173
+ // When generating object-wrapped .map(), ensure original item data is preserved.
1174
+ // If no data spread was included (e.g., because this is a plain array property,
1175
+ // not a function return), add ...__item__ to spread the original item properties.
1176
+ // Without this, the .map() would create new objects with only nested function
1177
+ // properties, losing data like filePath, frontmatter, body, etc.
1178
+ const hasDataSpread = mappedContents.includes('...scenarios()') ||
1179
+ mappedContents.includes('...__item__');
1180
+ if (!hasDataSpread) {
1181
+ mappedContents = `...__item__,\n${mappedContents}`;
1182
+ }
1173
1183
  returnValueContents = `${arrayBasePath}?.map((__item__, __idx__) => ({\n${indent(mappedContents)}\n}))`;
1174
1184
  }
1175
1185
  } // Close the empty content check else block
@@ -1231,6 +1241,13 @@ options) {
1231
1241
  mappedContents = filterOutArrowFunctions(mappedContents);
1232
1242
  mappedContents = filterOutBareObjects(mappedContents, true);
1233
1243
  mappedContents = cleanupContent(mappedContents);
1244
+ // Same as needsWrapper branch: ensure item data is preserved in .map()
1245
+ const hasDataSpreadInner = mappedContents.includes('...scenarios()') ||
1246
+ mappedContents.includes('...__item__');
1247
+ if (!hasDataSpreadInner && mappedContents.trim().length > 0) {
1248
+ // Insert ...__item__ after the opening brace
1249
+ mappedContents = mappedContents.replace(/^\s*\{/, '{\n...__item__,');
1250
+ }
1234
1251
  returnValueContents = `${arrayBasePath}?.map((__item__, __idx__) => (\n${indent(mappedContents)}\n))`;
1235
1252
  }
1236
1253
  }
@@ -2047,7 +2064,19 @@ options) {
2047
2064
  const safeReturnValue = isSimpleDataPath
2048
2065
  ? `${returnValue} ?? {}`
2049
2066
  : returnValue;
2050
- return `${isRootAsyncFunction ? 'async ' : ''}function ${safeFunctionName}(...args) {\n${indent(`return ${safeReturnValue};`)}\n}`;
2067
+ const refName = `_${safeFunctionName}Ref`;
2068
+ const assignment = `${refName}.current = ${safeReturnValue};`;
2069
+ const ifBlock = `if (!${refName}.current) {\n${indent(assignment)}\n}`;
2070
+ const body = `${ifBlock}\nreturn ${refName}.current;`;
2071
+ return [
2072
+ `// PATCHED: memoize to return stable reference (prevents infinite useEffect re-triggers)`,
2073
+ `const ${refName} = {`,
2074
+ ` current: null,`,
2075
+ `};`,
2076
+ `${isRootAsyncFunction ? 'async ' : ''}function ${safeFunctionName}(...args) {`,
2077
+ indent(body),
2078
+ `}`,
2079
+ ].join('\n');
2051
2080
  }
2052
2081
  else {
2053
2082
  // Generate safe const name: