@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
@@ -2,64 +2,199 @@
2
2
  """
3
3
  Rule reflection hook for Claude Code.
4
4
 
5
- Reads the session transcript to find new conversation since last check,
6
- then prompts Claude to consider rule updates if there was substantive content.
5
+ Two responsibilities, now split into independent agents:
6
+ 1. Check for stale rules (files changed since rule was last reviewed) and prompt review
7
+ 2. Review conversation for confusion signals (mistakes, corrections, interruptions)
8
+
9
+ Each fires as a separate `claude -p` invocation so the LLM can focus on one task at a time.
10
+ Stays silent if there's nothing to review.
11
+
12
+ Prompt text lives in templates/prompts/*.txt (single source of truth shared with TypeScript tests).
7
13
  """
8
14
 
9
15
  import json
16
+ import os
17
+ import subprocess
10
18
  import sys
11
19
  from pathlib import Path
12
20
 
13
- MIN_USER_TURNS = 5 # Fire after N user turns
21
+ MIN_USER_TURNS = 3 # Minimum user turns before checking for confusion
14
22
 
15
- def main():
16
- # Read hook input from stdin
17
- try:
18
- hook_input = json.load(sys.stdin)
19
- except json.JSONDecodeError:
20
- # No valid input, exit silently
21
- return
23
+ # Signals that indicate confusion, mistakes, or user corrections
24
+ CONFUSION_SIGNALS = [
25
+ 'no,', 'no ', "that's not", "thats not", "that is not",
26
+ 'wrong', 'incorrect', 'actually,', 'actually ',
27
+ "i meant", "i mean", "not what i", "stop", "wait",
28
+ "don't do", "dont do", "shouldn't", "should not",
29
+ "try again", "let me clarify", "to clarify",
30
+ "that broke", "that failed", "error", "bug",
31
+ ]
22
32
 
23
- session_id = hook_input.get('session_id', '')
24
- transcript_path = hook_input.get('transcript_path', '')
25
- stop_hook_active = hook_input.get('stop_hook_active', False)
33
+ # Prompt templates directory — installed alongside this hook in .codeyam/bin/prompts/
34
+ PROMPTS_DIR = Path(__file__).parent / 'prompts'
26
35
 
27
- # Prevent infinite loops - if we're already in a stop hook continuation, exit silently
28
- if stop_hook_active:
29
- return
30
36
 
31
- if not session_id or not transcript_path:
32
- return
37
+ def load_prompt_template(name, **kwargs):
38
+ """
39
+ Load a prompt template from the prompts/ directory and substitute placeholders.
40
+ Placeholders use {{KEY}} syntax (e.g., {{CONTEXT_FILE}}, {{PROJECT_DIR}}).
41
+ """
42
+ template_path = PROMPTS_DIR / name
43
+ text = template_path.read_text()
44
+ for key, value in kwargs.items():
45
+ text = text.replace(f'{{{{{key}}}}}', str(value))
46
+ return text
33
47
 
34
- # Marker file tracks last checked line per session
35
- marker_dir = Path('/tmp/claude-rule-markers')
36
- marker_dir.mkdir(exist_ok=True)
37
- marker_file = marker_dir / f'{session_id}.marker'
38
48
 
39
- # Read last checked line
40
- last_line = 0
41
- if marker_file.exists():
49
+ def has_confusion_signals(conversation_snippets):
50
+ """
51
+ Check if the conversation contains signals of confusion or user corrections.
52
+ Returns True if confusion signals are detected.
53
+ """
54
+ for snippet in conversation_snippets:
55
+ if snippet['role'] != 'user':
56
+ continue
57
+ content_lower = snippet['content'].lower()
58
+ for signal in CONFUSION_SIGNALS:
59
+ if signal in content_lower:
60
+ return True
61
+ return False
62
+
63
+
64
+ def get_file_diff(file_path, max_lines=50):
65
+ """
66
+ Get git diff for a file. Tries staged diff first, then unstaged.
67
+ Returns truncated diff string or empty string.
68
+ """
69
+ for diff_cmd in [
70
+ ['git', 'diff', 'HEAD', '--', file_path],
71
+ ['git', 'diff', '--', file_path],
72
+ ]:
42
73
  try:
43
- last_line = int(marker_file.read_text().strip())
44
- except (ValueError, IOError):
45
- last_line = 0
74
+ result = subprocess.run(
75
+ diff_cmd, capture_output=True, text=True, timeout=10
76
+ )
77
+ if result.stdout.strip():
78
+ lines = result.stdout.strip().split('\n')
79
+ # Skip the diff header (--- a/, +++ b/, @@ lines)
80
+ content_lines = [l for l in lines if not l.startswith('diff ') and
81
+ not l.startswith('index ') and not l.startswith('--- ') and
82
+ not l.startswith('+++ ')]
83
+ if len(content_lines) > max_lines:
84
+ content_lines = content_lines[:max_lines] + [f'... ({len(lines) - max_lines} more lines)']
85
+ return '\n'.join(content_lines)
86
+ except (subprocess.TimeoutExpired, FileNotFoundError):
87
+ continue
88
+ return ''
89
+
90
+
91
+ def read_rule_content(rule_name):
92
+ """
93
+ Read a rule file and return its content after YAML frontmatter.
94
+ Searches in .claude/rules/ directory.
95
+ """
96
+ # Search for the rule file
97
+ rules_dir = Path('.claude/rules')
98
+ if not rules_dir.exists():
99
+ return ''
100
+
101
+ # Find the file - could be at any depth
102
+ matches = list(rules_dir.rglob(rule_name))
103
+ if not matches:
104
+ return ''
105
+
106
+ try:
107
+ text = matches[0].read_text()
108
+ except IOError:
109
+ return ''
110
+
111
+ # Strip YAML frontmatter
112
+ if text.startswith('---'):
113
+ end = text.find('---', 3)
114
+ if end != -1:
115
+ text = text[end + 3:].strip()
116
+
117
+ return text
118
+
119
+
120
+ def get_stale_rules():
121
+ """
122
+ Run `codeyam memory status` and parse the output to find stale rules.
123
+ Returns a list of dicts with rule info, or empty list if none stale.
124
+ """
125
+ try:
126
+ # Use the local codeyam CLI via node to avoid PATH issues
127
+ project_dir = os.environ.get('CLAUDE_PROJECT_DIR', os.getcwd())
128
+ codeyam_js = os.path.join(project_dir, 'codeyam-cli', 'dist', 'codeyam-cli', 'src', 'codeyam-cli.js')
129
+ if os.path.exists(codeyam_js):
130
+ cmd = ['node', '--no-warnings', codeyam_js, 'memory', 'status']
131
+ else:
132
+ cmd = ['codeyam', 'memory', 'status']
133
+ result = subprocess.run(
134
+ cmd,
135
+ capture_output=True,
136
+ text=True,
137
+ timeout=30
138
+ )
139
+ output = result.stdout
140
+
141
+ # Check if there are stale rules
142
+ if 'Found' not in output or 'stale rule' not in output:
143
+ return []
46
144
 
47
- # Read transcript and get new lines
145
+ # Parse the output to extract stale rule information
146
+ stale_rules = []
147
+ lines = output.split('\n')
148
+ i = 0
149
+ while i < len(lines):
150
+ line = lines[i].strip()
151
+ # Look for rule filenames (lines that end with .md and aren't indented much)
152
+ if line.endswith('.md') and not line.startswith('Rule') and not line.startswith('Newest'):
153
+ rule_info = {'name': line}
154
+ # Look for the next few lines for details
155
+ for j in range(i + 1, min(i + 4, len(lines))):
156
+ detail = lines[j].strip()
157
+ if detail.startswith('Rule timestamp:'):
158
+ rule_info['rule_timestamp'] = detail.replace('Rule timestamp:', '').strip()
159
+ elif detail.startswith('Newest file:'):
160
+ rule_info['newest_file'] = detail.replace('Newest file:', '').strip()
161
+ elif detail.startswith('File modified:'):
162
+ rule_info['file_modified'] = detail.replace('File modified:', '').strip()
163
+ # Get diff for the changed file
164
+ if rule_info.get('newest_file'):
165
+ rule_info['diff'] = get_file_diff(rule_info['newest_file'])
166
+
167
+ # Get inline rule content
168
+ rule_info['rule_content'] = read_rule_content(rule_info['name'])
169
+
170
+ stale_rules.append(rule_info)
171
+ i += 1
172
+
173
+ return stale_rules
174
+ except (subprocess.TimeoutExpired, FileNotFoundError, Exception):
175
+ return []
176
+
177
+
178
+ def get_conversation_context(transcript_path, last_line):
179
+ """
180
+ Extract conversation snippets and modified files from transcript for rule review.
181
+ Returns (user_turn_count, conversation_snippets, modified_files, current_line_count)
182
+ """
48
183
  try:
49
184
  with open(transcript_path, 'r') as f:
50
185
  all_lines = f.readlines()
51
186
  except IOError:
52
- return
187
+ return 0, [], set(), 0
53
188
 
54
189
  current_line_count = len(all_lines)
55
190
  new_lines = all_lines[last_line:]
56
191
 
57
192
  if not new_lines:
58
- return
193
+ return 0, [], set(), current_line_count
59
194
 
60
- # Count user turns and extract conversation content from new lines
61
195
  user_turn_count = 0
62
196
  conversation_snippets = []
197
+ modified_files = set()
63
198
 
64
199
  for line in new_lines:
65
200
  try:
@@ -75,87 +210,219 @@ def main():
75
210
 
76
211
  # Handle string content
77
212
  if isinstance(content, str):
78
- # Skip meta messages and very short content
79
213
  if obj.get('isMeta') or len(content) < 20:
80
214
  continue
81
- # Skip tool results (they show up as user messages)
82
215
  if content.startswith('[{') or '<tool_result' in content:
83
216
  continue
84
217
 
85
- # Count substantive user turns
86
218
  if is_external_user and not content.startswith('<'):
87
219
  user_turn_count += 1
88
220
 
89
221
  conversation_snippets.append({
90
222
  'role': message.get('role', msg_type),
91
- 'content': content[:500] # Truncate long messages
223
+ 'content': content
92
224
  })
93
225
 
94
- # Handle array content (tool calls, etc.)
95
226
  elif isinstance(content, list):
96
227
  for item in content:
97
- if isinstance(item, dict):
98
- if item.get('type') == 'text':
99
- text = item.get('text', '')
100
- if len(text) > 20:
101
- conversation_snippets.append({
102
- 'role': message.get('role', msg_type),
103
- 'content': text[:500]
104
- })
228
+ if not isinstance(item, dict):
229
+ continue
230
+
231
+ # Extract text snippets
232
+ if item.get('type') == 'text':
233
+ text = item.get('text', '')
234
+ if len(text) > 20:
235
+ conversation_snippets.append({
236
+ 'role': message.get('role', msg_type),
237
+ 'content': text
238
+ })
239
+
240
+ # Track file modifications from tool_use entries
241
+ if item.get('type') == 'tool_use':
242
+ tool_name = item.get('name', '')
243
+ tool_input = item.get('input', {})
244
+ if tool_name in ('Edit', 'Write') and tool_input.get('file_path'):
245
+ modified_files.add((tool_input['file_path'], tool_name))
246
+
105
247
  except (json.JSONDecodeError, KeyError):
106
248
  continue
107
249
 
108
- # Only fire after enough user turns
109
- if user_turn_count < MIN_USER_TURNS:
110
- return
250
+ return user_turn_count, conversation_snippets, modified_files, current_line_count
111
251
 
112
- # Update marker with current line count
113
- marker_file.write_text(str(current_line_count))
114
252
 
115
- # If not enough substantive conversation, exit silently
116
- if len(conversation_snippets) < 3:
117
- return
253
+ def build_stale_rules_context(stale_rules):
254
+ """Build context content for stale rules review."""
255
+ parts = []
256
+ parts.append("# Reflection Step\n")
257
+ parts.append("Please review the stale rules below to determine if any need updating based on recent code changes.\n")
258
+ parts.append("Please show your thinking regarding the stale rules.\n")
259
+ parts.append("## Stale Rules to Review\n")
260
+ parts.append("The following rules have files that changed since the rule was last reviewed.")
261
+ parts.append("For each rule, review the rule content and the diff of changes, then:")
262
+ parts.append("1. Determine if the rule content needs updating based on the code changes")
263
+ parts.append("2. Update the rule if needed")
264
+ parts.append("3. ALWAYS update the timestamp (run `codeyam memory touch`)\n")
118
265
 
119
- # Build a summary of the recent conversation for the rule check
266
+ for rule in stale_rules:
267
+ parts.append(f"### {rule['name']}")
268
+ if rule.get('rule_content'):
269
+ parts.append(f" Rule content:")
270
+ for line in rule['rule_content'].split('\n'):
271
+ parts.append(f" {line}")
272
+ if rule.get('newest_file'):
273
+ parts.append(f" Changed file: {rule['newest_file']}")
274
+ if rule.get('file_modified'):
275
+ parts.append(f" File modified: {rule['file_modified']}")
276
+ if rule.get('rule_timestamp'):
277
+ parts.append(f" Rule timestamp: {rule['rule_timestamp']}")
278
+ if rule.get('diff'):
279
+ parts.append(f" Changes:")
280
+ for line in rule['diff'].split('\n'):
281
+ parts.append(f" {line}")
282
+ parts.append("")
283
+
284
+ return '\n'.join(parts)
285
+
286
+
287
+ def build_conversation_context(conversation_snippets, modified_files):
288
+ """Build context content for conversation review."""
289
+ parts = []
290
+ parts.append("## Conversation Review\n")
291
+ parts.append(
292
+ "Review this session for rule-worthy learnings: architectural decisions, tribal knowledge, "
293
+ "confusion, mistakes, or corrections that future sessions would benefit from knowing.\n"
294
+ )
295
+
296
+ # Load guidance from shared template file
297
+ guidance = (PROMPTS_DIR / 'conversation-guidance.txt').read_text()
298
+ parts.append(guidance)
299
+
300
+ if modified_files:
301
+ parts.append("Files modified this session:")
302
+ for file_path, tool_name in sorted(modified_files):
303
+ parts.append(f"- {file_path} ({tool_name})")
304
+ parts.append("")
305
+
306
+ parts.append("### Session transcript\n")
120
307
  summary_lines = []
121
- for snippet in conversation_snippets[-15:]: # Last 15 messages
308
+ for snippet in conversation_snippets:
122
309
  role = snippet['role']
123
- content = snippet['content'].replace('\n', ' ')[:300]
310
+ content = snippet['content'].replace('\n', ' ')
124
311
  summary_lines.append(f"[{role}]: {content}")
312
+ parts.append('\n'.join(summary_lines))
125
313
 
126
- summary = '\n'.join(summary_lines)
127
-
128
- # Write detailed context to a file (keeps displayed output short)
129
- context_file = marker_dir / f'{session_id}.context'
130
- context = f"""Recent conversation to review for rule-worthy learnings:
131
-
132
- {summary}
133
-
134
- ---
135
- Guidelines:
136
- - Did Claude get confused during this session in any way?
137
- - How exactly did it get confused and what information would have resolved that confusion. No additional detail is needed especially if it is something that Claude likely already knows.
138
- - Only update rules for patterns of confusion that will likely recur in future sessions.
139
- - Document knowledge not easily evident from code
140
- - Locations of tests
141
- - Commands that can be run that are relevant to the task at hand
142
- - Architectural design, where to look for, fix, or add certain things
143
- - Pay particular attention to times when the user must interrupt or correct Claude.
144
- - Do not document bugs being fixed as they will likely be resolved.
145
- - Issues that are abandoned as known issues can be noted.
146
- - Keep rules concise (<50 lines), use bullets/tables where appropriate.
147
- - Clean up, summarize, and delete existing rules as needed.
148
- - If no changes are needed, just say "No rule updates needed."
149
- """
150
- context_file.write_text(context)
151
-
152
- # Output blocking decision - just trigger background agent
153
- # Keep reason minimal since main agent will spawn background worker
154
- output = {
155
- "decision": "block",
156
- "reason": f"RULE_CHECK:{context_file}"
157
- }
158
- print(json.dumps(output))
314
+ return '\n'.join(parts)
315
+
316
+
317
+ def spawn_claude_agent(prompt, log_file, project_dir):
318
+ """Spawn a detached claude -p agent as a background process."""
319
+ try:
320
+ log_fh = open(log_file, 'w')
321
+ env = os.environ.copy()
322
+ env['CODEYAM_RULE_AGENT'] = '1'
323
+ subprocess.Popen(
324
+ ['claude', '-p', prompt,
325
+ '--model', 'haiku',
326
+ '--output-format', 'stream-json', '--verbose',
327
+ '--allowedTools', 'Read,Edit,Write,Bash,Glob,Grep'],
328
+ cwd=project_dir,
329
+ stdout=log_fh,
330
+ stderr=log_fh,
331
+ env=env,
332
+ start_new_session=True,
333
+ )
334
+ except FileNotFoundError:
335
+ pass # claude CLI not available, skip silently
336
+
337
+
338
+ def main():
339
+ # Read hook input from stdin
340
+ try:
341
+ hook_input = json.load(sys.stdin)
342
+ except json.JSONDecodeError:
343
+ return
344
+
345
+ session_id = hook_input.get('session_id', '')
346
+ transcript_path = hook_input.get('transcript_path', '')
347
+ stop_hook_active = hook_input.get('stop_hook_active', False)
348
+
349
+ # Prevent infinite loops — stop_hook_active covers same-process recursion,
350
+ # env var covers spawned subagent sessions triggering the hook on exit
351
+ if stop_hook_active or os.environ.get('CODEYAM_RULE_AGENT'):
352
+ return
353
+
354
+ if not session_id or not transcript_path:
355
+ return
356
+
357
+ marker_dir = Path('/tmp/claude-rule-markers')
358
+ marker_dir.mkdir(exist_ok=True)
359
+ marker_file = marker_dir / f'{session_id}.marker'
360
+
361
+ # Read last checked line
362
+ last_line = 0
363
+ if marker_file.exists():
364
+ try:
365
+ last_line = int(marker_file.read_text().strip())
366
+ except (ValueError, IOError):
367
+ last_line = 0
368
+
369
+ # Check for stale rules
370
+ stale_rules = get_stale_rules()
371
+
372
+ # Get conversation context
373
+ user_turn_count, conversation_snippets, modified_files, current_line_count = get_conversation_context(
374
+ transcript_path, last_line
375
+ )
376
+
377
+ # Determine what to include in review
378
+ has_stale_rules = len(stale_rules) > 0
379
+ has_conversation = len(conversation_snippets) > 0
380
+
381
+ # Only fire if there's something to review
382
+ if not has_stale_rules and not has_conversation:
383
+ return
384
+
385
+ # Update marker
386
+ marker_file.write_text(str(current_line_count))
387
+
388
+ project_dir = os.environ.get('CLAUDE_PROJECT_DIR', os.getcwd())
389
+
390
+ # Unique suffix per invocation so each Stop produces a distinct transcript
391
+ from datetime import datetime
392
+ invocation_ts = datetime.now().strftime('%Y%m%d-%H%M%S')
393
+ invocation_id = f'{session_id}-{invocation_ts}'
394
+
395
+ # Spawn independent agent for stale rules review
396
+ if has_stale_rules:
397
+ stale_context = build_stale_rules_context(stale_rules)
398
+ stale_context_file = marker_dir / f'{invocation_id}-stale.context'
399
+ stale_context_file.write_text(stale_context)
400
+
401
+ stale_log_file = marker_dir / f'{invocation_id}-stale.log'
402
+ stale_notification_file = marker_dir / 'rule-notification-stale.md'
403
+ stale_prompt = load_prompt_template(
404
+ 'stale-rules-prompt.txt',
405
+ CONTEXT_FILE=str(stale_context_file),
406
+ NOTIFICATION_FILE=str(stale_notification_file),
407
+ PROJECT_DIR=project_dir,
408
+ )
409
+ spawn_claude_agent(stale_prompt, stale_log_file, project_dir)
410
+
411
+ # Spawn independent agent for conversation review
412
+ if has_conversation:
413
+ conv_context = build_conversation_context(conversation_snippets, modified_files)
414
+ conv_context_file = marker_dir / f'{invocation_id}-conversation.context'
415
+ conv_context_file.write_text(conv_context)
416
+
417
+ conv_log_file = marker_dir / f'{invocation_id}-conversation.log'
418
+ conv_notification_file = marker_dir / 'rule-notification-conversation.md'
419
+ conv_prompt = load_prompt_template(
420
+ 'conversation-prompt.txt',
421
+ CONTEXT_FILE=str(conv_context_file),
422
+ NOTIFICATION_FILE=str(conv_notification_file),
423
+ PROJECT_DIR=project_dir,
424
+ )
425
+ spawn_claude_agent(conv_prompt, conv_log_file, project_dir)
159
426
 
160
427
  if __name__ == '__main__':
161
428
  main()
@@ -19,34 +19,33 @@ Create a new rule when:
19
19
 
20
20
  ## Before Creating: The Confusion Test
21
21
 
22
- Before writing a rule, verify it passes these tests:
22
+ Only create rules that document genuinely confusing aspects—not things Claude can figure out by reading code and will likely come up again in future sessions.
23
23
 
24
- **1. Could Claude figure this out by reading the code?**
24
+ **Ask yourself:**
25
25
 
26
- - If YES don't create the rule (it documents the obvious)
27
- - If NO → the rule is valuable
26
+ 1. **Could Claude figure this out by reading the code?**
27
+ - If YESdon't create the rule
28
+ - If NO → proceed
28
29
 
29
- **2. Does this explain "why" not just "what"?**
30
+ 2. **Does this explain "why" not just "what"?**
31
+ - Good: Historical context, gotchas, non-obvious behavior, non-obvious architectural decisions
32
+ - Bad: Bug fixes, Limitations (unless the user requests the limitation be noted), What functions do (unless providing a high-level architectural overview)
30
33
 
31
- - Good: Explains historical context, gotchas, or non-obvious behavior
32
- - Bad: Describes what functions do or how files are structured
33
-
34
- **3. Would this have prevented a past mistake?**
35
-
36
- - If you can point to a commit, bug, or confusion this would have prevented → create it
37
- - If it's just "nice to know" information → skip it
34
+ 3. **Would this have prevented a past mistake?**
35
+ - If you can point to a bug or confusion this would have prevented → create it
36
+ - If it's just "nice to know" → skip it
38
37
 
39
38
  ### Examples
40
39
 
41
40
  **✅ Worth documenting:**
42
41
 
43
- - "Path prefix matching must check boundaries" (learned from a bug)
44
- - "These files must change together because of X invariant" (hidden dependency)
45
- - "This pattern exists because the obvious approach causes Y problem" (non-obvious design)
42
+ - Where to add new functionality
43
+ - An overview of all files, classes, functions, etc used in a particular part of the repo
44
+ - Debugging strategies for a particular area of the repo
45
+ - "Use `pnpm test` to run tests" (if there is more than one way to run tests and this way is preferred)
46
46
 
47
47
  **❌ Not worth documenting:**
48
48
 
49
- - "Use `pnpm jest` to run tests" (obvious from package.json)
50
49
  - "The auth module handles authentication" (obvious from reading code)
51
50
  - "This function takes X and returns Y" (Claude can read the signature)
52
51
 
@@ -80,16 +79,14 @@ Rules mirror the source code structure:
80
79
  paths:
81
80
  - 'specific/path/to/file.ts'
82
81
  - 'another/specific/path/*.ts'
83
- category: architecture | testing | faq
84
82
  timestamp: 2026-01-30T00:00:00Z
85
83
  ---
86
84
  ```
87
85
 
88
- | Field | Purpose |
89
- | ----------- | -------------------------------------------------------------------------------------------------- |
90
- | `paths` | Glob patterns - be specific to avoid loading rules unnecessarily |
91
- | `category` | `architecture` (design/data flow), `testing` (test commands/patterns), `faq` (gotchas/workarounds) |
92
- | `timestamp` | ISO 8601 - update when rule is reviewed |
86
+ | Field | Purpose |
87
+ | ----------- | ---------------------------------------------------------------- |
88
+ | `paths` | Glob patterns - be specific to avoid loading rules unnecessarily |
89
+ | `timestamp` | ISO 8601 - update when rule is reviewed |
93
90
 
94
91
  ## Content Guidelines
95
92
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@codeyam/codeyam-cli",
3
- "version": "0.1.0-staging.483fdc2",
3
+ "version": "0.1.0-staging.62d4615",
4
4
  "description": "Local development CLI for CodeYam analysis",
5
5
  "type": "module",
6
6
  "bin": {
@@ -52,7 +52,7 @@
52
52
  "pluralize": "^8.0.0",
53
53
  "prompts": "^2.4.2",
54
54
  "react": "^19.2.3",
55
- "react-diff-viewer-continued": "^4.0.6",
55
+ "react-diff-viewer-continued": "4.0.6",
56
56
  "react-dom": "^19.2.3",
57
57
  "react-markdown": "^10.1.0",
58
58
  "react-resizable": "^3.0.5",
@@ -94,9 +94,14 @@ function ensureWorkerPool() {
94
94
  }
95
95
  // Note: WORKER_PATH is null when __mocks__/analyzeScope.ts is active
96
96
  const { WORKER_PATH } = analyzeScopeWorkerPaths();
97
+ // Disable workers when tracing is enabled - we need access to ScopeDataStructure
98
+ // instance in the main thread to capture pre-serialization state
99
+ const tracingEnabled = process.env.CODEYAM_TRACE_TRANSFORMS === '1' ||
100
+ process.env.CODEYAM_TRACE_TRANSFORMS === 'true';
97
101
  if (process.env.USE_WORKER_THREADS &&
98
102
  WORKER_PATH &&
99
- fs.existsSync(WORKER_PATH)) {
103
+ fs.existsSync(WORKER_PATH) &&
104
+ !tracingEnabled) {
100
105
  console.log('CodeYam: Using worker threads for analyzeScope', {
101
106
  WORKER_PATH,
102
107
  });
@@ -124,6 +129,7 @@ function ensureWorkerPool() {
124
129
  WORKER_PATH,
125
130
  workerExists: WORKER_PATH ? fs.existsSync(WORKER_PATH) : false,
126
131
  USE_WORKER_THREADS: process.env.USE_WORKER_THREADS,
132
+ tracingEnabled,
127
133
  });
128
134
  workerPool = null; // Explicitly set to indicate "no workers"
129
135
  }