@camaradesuk/git-worktree-tools 1.8.0 → 1.10.0

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 (353) hide show
  1. package/README.md +48 -27
  2. package/dist/cli/cleanpr.js +74 -53
  3. package/dist/cli/cleanpr.js.map +1 -1
  4. package/dist/cli/cleanpr.test.js +2 -0
  5. package/dist/cli/cleanpr.test.js.map +1 -1
  6. package/dist/cli/lswt.js +32 -56
  7. package/dist/cli/lswt.js.map +1 -1
  8. package/dist/cli/lswt.test.js +17 -27
  9. package/dist/cli/lswt.test.js.map +1 -1
  10. package/dist/cli/newpr.d.ts +13 -1
  11. package/dist/cli/newpr.d.ts.map +1 -1
  12. package/dist/cli/newpr.js +350 -151
  13. package/dist/cli/newpr.js.map +1 -1
  14. package/dist/cli/newpr.test.js +314 -5
  15. package/dist/cli/newpr.test.js.map +1 -1
  16. package/dist/cli/prs.d.ts +3 -10
  17. package/dist/cli/prs.d.ts.map +1 -1
  18. package/dist/cli/prs.js +6 -168
  19. package/dist/cli/prs.js.map +1 -1
  20. package/dist/cli/prs.test.js +55 -0
  21. package/dist/cli/prs.test.js.map +1 -1
  22. package/dist/cli/wt/clean.d.ts +6 -2
  23. package/dist/cli/wt/clean.d.ts.map +1 -1
  24. package/dist/cli/wt/clean.js +401 -20
  25. package/dist/cli/wt/clean.js.map +1 -1
  26. package/dist/cli/wt/clean.test.d.ts +8 -0
  27. package/dist/cli/wt/clean.test.d.ts.map +1 -0
  28. package/dist/cli/wt/clean.test.js +624 -0
  29. package/dist/cli/wt/clean.test.js.map +1 -0
  30. package/dist/cli/wt/completion.d.ts +3 -0
  31. package/dist/cli/wt/completion.d.ts.map +1 -1
  32. package/dist/cli/wt/completion.js +80 -9
  33. package/dist/cli/wt/completion.js.map +1 -1
  34. package/dist/cli/wt/completion.test.js +102 -0
  35. package/dist/cli/wt/completion.test.js.map +1 -1
  36. package/dist/cli/wt/config.d.ts +3 -1
  37. package/dist/cli/wt/config.d.ts.map +1 -1
  38. package/dist/cli/wt/config.js +323 -32
  39. package/dist/cli/wt/config.js.map +1 -1
  40. package/dist/cli/wt/config.test.d.ts +2 -0
  41. package/dist/cli/wt/config.test.d.ts.map +1 -1
  42. package/dist/cli/wt/config.test.js +206 -26
  43. package/dist/cli/wt/config.test.js.map +1 -1
  44. package/dist/cli/wt/interactive-menu.d.ts +2 -0
  45. package/dist/cli/wt/interactive-menu.d.ts.map +1 -1
  46. package/dist/cli/wt/interactive-menu.js +346 -73
  47. package/dist/cli/wt/interactive-menu.js.map +1 -1
  48. package/dist/cli/wt/interactive-menu.test.d.ts +4 -2
  49. package/dist/cli/wt/interactive-menu.test.d.ts.map +1 -1
  50. package/dist/cli/wt/interactive-menu.test.js +383 -323
  51. package/dist/cli/wt/interactive-menu.test.js.map +1 -1
  52. package/dist/cli/wt/link.d.ts +3 -1
  53. package/dist/cli/wt/link.d.ts.map +1 -1
  54. package/dist/cli/wt/link.js +125 -38
  55. package/dist/cli/wt/link.js.map +1 -1
  56. package/dist/cli/wt/list.d.ts +4 -1
  57. package/dist/cli/wt/list.d.ts.map +1 -1
  58. package/dist/cli/wt/list.js +85 -16
  59. package/dist/cli/wt/list.js.map +1 -1
  60. package/dist/cli/wt/list.test.d.ts +10 -0
  61. package/dist/cli/wt/list.test.d.ts.map +1 -0
  62. package/dist/cli/wt/list.test.js +157 -0
  63. package/dist/cli/wt/list.test.js.map +1 -0
  64. package/dist/cli/wt/new.d.ts +8 -2
  65. package/dist/cli/wt/new.d.ts.map +1 -1
  66. package/dist/cli/wt/new.js +91 -46
  67. package/dist/cli/wt/new.js.map +1 -1
  68. package/dist/cli/wt/prs.d.ts +2 -1
  69. package/dist/cli/wt/prs.d.ts.map +1 -1
  70. package/dist/cli/wt/prs.js +3 -164
  71. package/dist/cli/wt/prs.js.map +1 -1
  72. package/dist/cli/wt/run-command.d.ts +4 -2
  73. package/dist/cli/wt/run-command.d.ts.map +1 -1
  74. package/dist/cli/wt/run-command.js +6 -4
  75. package/dist/cli/wt/run-command.js.map +1 -1
  76. package/dist/cli/wt/state.d.ts +3 -1
  77. package/dist/cli/wt/state.d.ts.map +1 -1
  78. package/dist/cli/wt/state.js +74 -10
  79. package/dist/cli/wt/state.js.map +1 -1
  80. package/dist/cli/wt/state.test.d.ts +9 -0
  81. package/dist/cli/wt/state.test.d.ts.map +1 -0
  82. package/dist/cli/wt/state.test.js +127 -0
  83. package/dist/cli/wt/state.test.js.map +1 -0
  84. package/dist/cli/wt/wt.test.d.ts +2 -2
  85. package/dist/cli/wt/wt.test.js +430 -212
  86. package/dist/cli/wt/wt.test.js.map +1 -1
  87. package/dist/cli/wt.d.ts.map +1 -1
  88. package/dist/cli/wt.js +50 -36
  89. package/dist/cli/wt.js.map +1 -1
  90. package/dist/cli/wt.unit.test.js +16 -38
  91. package/dist/cli/wt.unit.test.js.map +1 -1
  92. package/dist/cli/wtconfig.d.ts +1 -0
  93. package/dist/cli/wtconfig.d.ts.map +1 -1
  94. package/dist/cli/wtconfig.js +213 -21
  95. package/dist/cli/wtconfig.js.map +1 -1
  96. package/dist/cli/wtconfig.test.js +3 -0
  97. package/dist/cli/wtconfig.test.js.map +1 -1
  98. package/dist/cli/wtlink.js +116 -73
  99. package/dist/cli/wtlink.js.map +1 -1
  100. package/dist/cli/wtstate.js +21 -2
  101. package/dist/cli/wtstate.js.map +1 -1
  102. package/dist/e2e/wt/interactive-menu.e2e.test.js +17 -17
  103. package/dist/e2e/wt/interactive-menu.e2e.test.js.map +1 -1
  104. package/dist/lib/ai/types.d.ts +12 -0
  105. package/dist/lib/ai/types.d.ts.map +1 -1
  106. package/dist/lib/ai/types.js.map +1 -1
  107. package/dist/lib/cleanpr/args.d.ts.map +1 -1
  108. package/dist/lib/cleanpr/args.js +20 -0
  109. package/dist/lib/cleanpr/args.js.map +1 -1
  110. package/dist/lib/cleanpr/types.d.ts +6 -0
  111. package/dist/lib/cleanpr/types.d.ts.map +1 -1
  112. package/dist/lib/cleanpr/worktree-info.d.ts.map +1 -1
  113. package/dist/lib/cleanpr/worktree-info.js +1 -6
  114. package/dist/lib/cleanpr/worktree-info.js.map +1 -1
  115. package/dist/lib/cleanpr/worktree-info.test.js +10 -13
  116. package/dist/lib/cleanpr/worktree-info.test.js.map +1 -1
  117. package/dist/lib/colors.d.ts +5 -0
  118. package/dist/lib/colors.d.ts.map +1 -1
  119. package/dist/lib/colors.js +13 -6
  120. package/dist/lib/colors.js.map +1 -1
  121. package/dist/lib/config-editor.d.ts.map +1 -1
  122. package/dist/lib/config-editor.js.map +1 -1
  123. package/dist/lib/config-migration/detector.d.ts +25 -0
  124. package/dist/lib/config-migration/detector.d.ts.map +1 -0
  125. package/dist/lib/config-migration/detector.js +372 -0
  126. package/dist/lib/config-migration/detector.js.map +1 -0
  127. package/dist/lib/config-migration/detector.test.d.ts +5 -0
  128. package/dist/lib/config-migration/detector.test.d.ts.map +1 -0
  129. package/dist/lib/config-migration/detector.test.js +201 -0
  130. package/dist/lib/config-migration/detector.test.js.map +1 -0
  131. package/dist/lib/config-migration/index.d.ts +29 -0
  132. package/dist/lib/config-migration/index.d.ts.map +1 -0
  133. package/dist/lib/config-migration/index.js +33 -0
  134. package/dist/lib/config-migration/index.js.map +1 -0
  135. package/dist/lib/config-migration/reporter.d.ts +53 -0
  136. package/dist/lib/config-migration/reporter.d.ts.map +1 -0
  137. package/dist/lib/config-migration/reporter.js +257 -0
  138. package/dist/lib/config-migration/reporter.js.map +1 -0
  139. package/dist/lib/config-migration/reporter.test.d.ts +5 -0
  140. package/dist/lib/config-migration/reporter.test.d.ts.map +1 -0
  141. package/dist/lib/config-migration/reporter.test.js +305 -0
  142. package/dist/lib/config-migration/reporter.test.js.map +1 -0
  143. package/dist/lib/config-migration/runner.d.ts +46 -0
  144. package/dist/lib/config-migration/runner.d.ts.map +1 -0
  145. package/dist/lib/config-migration/runner.js +364 -0
  146. package/dist/lib/config-migration/runner.js.map +1 -0
  147. package/dist/lib/config-migration/runner.test.d.ts +5 -0
  148. package/dist/lib/config-migration/runner.test.d.ts.map +1 -0
  149. package/dist/lib/config-migration/runner.test.js +235 -0
  150. package/dist/lib/config-migration/runner.test.js.map +1 -0
  151. package/dist/lib/config-migration/types.d.ts +120 -0
  152. package/dist/lib/config-migration/types.d.ts.map +1 -0
  153. package/dist/lib/config-migration/types.js +70 -0
  154. package/dist/lib/config-migration/types.js.map +1 -0
  155. package/dist/lib/config-validation.d.ts.map +1 -1
  156. package/dist/lib/config-validation.js +6 -0
  157. package/dist/lib/config-validation.js.map +1 -1
  158. package/dist/lib/config-validation.test.js +25 -0
  159. package/dist/lib/config-validation.test.js.map +1 -1
  160. package/dist/lib/config.d.ts +31 -7
  161. package/dist/lib/config.d.ts.map +1 -1
  162. package/dist/lib/config.js +2 -0
  163. package/dist/lib/config.js.map +1 -1
  164. package/dist/lib/config.test.js +3 -15
  165. package/dist/lib/config.test.js.map +1 -1
  166. package/dist/lib/constants.d.ts +12 -4
  167. package/dist/lib/constants.d.ts.map +1 -1
  168. package/dist/lib/constants.js +24 -5
  169. package/dist/lib/constants.js.map +1 -1
  170. package/dist/lib/constants.test.js +88 -29
  171. package/dist/lib/constants.test.js.map +1 -1
  172. package/dist/lib/deprecation.d.ts +18 -0
  173. package/dist/lib/deprecation.d.ts.map +1 -0
  174. package/dist/lib/deprecation.js +28 -0
  175. package/dist/lib/deprecation.js.map +1 -0
  176. package/dist/lib/deprecation.test.d.ts +2 -0
  177. package/dist/lib/deprecation.test.d.ts.map +1 -0
  178. package/dist/lib/deprecation.test.js +71 -0
  179. package/dist/lib/deprecation.test.js.map +1 -0
  180. package/dist/lib/hooks/confirmation.d.ts +49 -0
  181. package/dist/lib/hooks/confirmation.d.ts.map +1 -0
  182. package/dist/lib/hooks/confirmation.js +147 -0
  183. package/dist/lib/hooks/confirmation.js.map +1 -0
  184. package/dist/lib/hooks/confirmation.test.d.ts +7 -0
  185. package/dist/lib/hooks/confirmation.test.d.ts.map +1 -0
  186. package/dist/lib/hooks/confirmation.test.js +300 -0
  187. package/dist/lib/hooks/confirmation.test.js.map +1 -0
  188. package/dist/lib/hooks/executor.d.ts +16 -1
  189. package/dist/lib/hooks/executor.d.ts.map +1 -1
  190. package/dist/lib/hooks/executor.js +53 -4
  191. package/dist/lib/hooks/executor.js.map +1 -1
  192. package/dist/lib/hooks/index.d.ts +4 -2
  193. package/dist/lib/hooks/index.d.ts.map +1 -1
  194. package/dist/lib/hooks/index.js +3 -2
  195. package/dist/lib/hooks/index.js.map +1 -1
  196. package/dist/lib/hooks/types.d.ts +16 -0
  197. package/dist/lib/hooks/types.d.ts.map +1 -1
  198. package/dist/lib/hooks/types.js +12 -0
  199. package/dist/lib/hooks/types.js.map +1 -1
  200. package/dist/lib/logger.d.ts +40 -155
  201. package/dist/lib/logger.d.ts.map +1 -1
  202. package/dist/lib/logger.js +349 -420
  203. package/dist/lib/logger.js.map +1 -1
  204. package/dist/lib/logger.test.d.ts +10 -1
  205. package/dist/lib/logger.test.d.ts.map +1 -1
  206. package/dist/lib/logger.test.js +658 -258
  207. package/dist/lib/logger.test.js.map +1 -1
  208. package/dist/lib/lswt/action-executors.d.ts +2 -0
  209. package/dist/lib/lswt/action-executors.d.ts.map +1 -1
  210. package/dist/lib/lswt/action-executors.js +4 -3
  211. package/dist/lib/lswt/action-executors.js.map +1 -1
  212. package/dist/lib/lswt/action-executors.test.js +7 -0
  213. package/dist/lib/lswt/action-executors.test.js.map +1 -1
  214. package/dist/lib/lswt/args.d.ts.map +1 -1
  215. package/dist/lib/lswt/args.js +15 -1
  216. package/dist/lib/lswt/args.js.map +1 -1
  217. package/dist/lib/lswt/environment.d.ts +21 -2
  218. package/dist/lib/lswt/environment.d.ts.map +1 -1
  219. package/dist/lib/lswt/environment.js +73 -32
  220. package/dist/lib/lswt/environment.js.map +1 -1
  221. package/dist/lib/lswt/environment.test.js +79 -1
  222. package/dist/lib/lswt/environment.test.js.map +1 -1
  223. package/dist/lib/lswt/index.d.ts +1 -0
  224. package/dist/lib/lswt/index.d.ts.map +1 -1
  225. package/dist/lib/lswt/index.js +2 -0
  226. package/dist/lib/lswt/index.js.map +1 -1
  227. package/dist/lib/lswt/table.d.ts +15 -0
  228. package/dist/lib/lswt/table.d.ts.map +1 -0
  229. package/dist/lib/lswt/table.js +61 -0
  230. package/dist/lib/lswt/table.js.map +1 -0
  231. package/dist/lib/lswt/table.test.d.ts +5 -0
  232. package/dist/lib/lswt/table.test.d.ts.map +1 -0
  233. package/dist/lib/lswt/table.test.js +262 -0
  234. package/dist/lib/lswt/table.test.js.map +1 -0
  235. package/dist/lib/lswt/types.d.ts +4 -0
  236. package/dist/lib/lswt/types.d.ts.map +1 -1
  237. package/dist/lib/lswt/worktree-info.d.ts.map +1 -1
  238. package/dist/lib/lswt/worktree-info.js +1 -6
  239. package/dist/lib/lswt/worktree-info.js.map +1 -1
  240. package/dist/lib/lswt/worktree-info.test.js +5 -17
  241. package/dist/lib/lswt/worktree-info.test.js.map +1 -1
  242. package/dist/lib/newpr/args.d.ts.map +1 -1
  243. package/dist/lib/newpr/args.js +36 -1
  244. package/dist/lib/newpr/args.js.map +1 -1
  245. package/dist/lib/newpr/hook-runner.d.ts +11 -0
  246. package/dist/lib/newpr/hook-runner.d.ts.map +1 -1
  247. package/dist/lib/newpr/hook-runner.js +49 -1
  248. package/dist/lib/newpr/hook-runner.js.map +1 -1
  249. package/dist/lib/newpr/hook-runner.test.js +121 -0
  250. package/dist/lib/newpr/hook-runner.test.js.map +1 -1
  251. package/dist/lib/newpr/plan-generator.d.ts +121 -0
  252. package/dist/lib/newpr/plan-generator.d.ts.map +1 -0
  253. package/dist/lib/newpr/plan-generator.js +185 -0
  254. package/dist/lib/newpr/plan-generator.js.map +1 -0
  255. package/dist/lib/newpr/plan-generator.test.d.ts +7 -0
  256. package/dist/lib/newpr/plan-generator.test.d.ts.map +1 -0
  257. package/dist/lib/newpr/plan-generator.test.js +387 -0
  258. package/dist/lib/newpr/plan-generator.test.js.map +1 -0
  259. package/dist/lib/newpr/types.d.ts +12 -0
  260. package/dist/lib/newpr/types.d.ts.map +1 -1
  261. package/dist/lib/prs/actions.d.ts +5 -1
  262. package/dist/lib/prs/actions.d.ts.map +1 -1
  263. package/dist/lib/prs/actions.js +12 -10
  264. package/dist/lib/prs/actions.js.map +1 -1
  265. package/dist/lib/prs/actions.test.js +48 -5
  266. package/dist/lib/prs/actions.test.js.map +1 -1
  267. package/dist/lib/prs/command.d.ts +21 -0
  268. package/dist/lib/prs/command.d.ts.map +1 -0
  269. package/dist/lib/prs/command.js +175 -0
  270. package/dist/lib/prs/command.js.map +1 -0
  271. package/dist/lib/prs/command.test.d.ts +11 -0
  272. package/dist/lib/prs/command.test.d.ts.map +1 -0
  273. package/dist/lib/prs/command.test.js +409 -0
  274. package/dist/lib/prs/command.test.js.map +1 -0
  275. package/dist/lib/prs/interactive.d.ts.map +1 -1
  276. package/dist/lib/prs/interactive.js +15 -2
  277. package/dist/lib/prs/interactive.js.map +1 -1
  278. package/dist/lib/prs/interactive.test.js +153 -0
  279. package/dist/lib/prs/interactive.test.js.map +1 -1
  280. package/dist/lib/prs/types.d.ts +15 -0
  281. package/dist/lib/prs/types.d.ts.map +1 -1
  282. package/dist/lib/ui/error.d.ts +31 -0
  283. package/dist/lib/ui/error.d.ts.map +1 -0
  284. package/dist/lib/ui/error.js +47 -0
  285. package/dist/lib/ui/error.js.map +1 -0
  286. package/dist/lib/ui/error.test.d.ts +2 -0
  287. package/dist/lib/ui/error.test.d.ts.map +1 -0
  288. package/dist/lib/ui/error.test.js +143 -0
  289. package/dist/lib/ui/error.test.js.map +1 -0
  290. package/dist/lib/ui/index.d.ts +15 -0
  291. package/dist/lib/ui/index.d.ts.map +1 -0
  292. package/dist/lib/ui/index.js +19 -0
  293. package/dist/lib/ui/index.js.map +1 -0
  294. package/dist/lib/ui/output.d.ts +18 -0
  295. package/dist/lib/ui/output.d.ts.map +1 -0
  296. package/dist/lib/ui/output.js +31 -0
  297. package/dist/lib/ui/output.js.map +1 -0
  298. package/dist/lib/ui/output.test.d.ts +2 -0
  299. package/dist/lib/ui/output.test.d.ts.map +1 -0
  300. package/dist/lib/ui/output.test.js +59 -0
  301. package/dist/lib/ui/output.test.js.map +1 -0
  302. package/dist/lib/ui/spinner.d.ts +10 -0
  303. package/dist/lib/ui/spinner.d.ts.map +1 -0
  304. package/dist/lib/ui/spinner.js +10 -0
  305. package/dist/lib/ui/spinner.js.map +1 -0
  306. package/dist/lib/ui/status.d.ts +65 -0
  307. package/dist/lib/ui/status.d.ts.map +1 -0
  308. package/dist/lib/ui/status.js +100 -0
  309. package/dist/lib/ui/status.js.map +1 -0
  310. package/dist/lib/ui/status.test.d.ts +2 -0
  311. package/dist/lib/ui/status.test.d.ts.map +1 -0
  312. package/dist/lib/ui/status.test.js +158 -0
  313. package/dist/lib/ui/status.test.js.map +1 -0
  314. package/dist/lib/ui/table.d.ts +39 -0
  315. package/dist/lib/ui/table.d.ts.map +1 -0
  316. package/dist/lib/ui/table.js +45 -0
  317. package/dist/lib/ui/table.js.map +1 -0
  318. package/dist/lib/ui/table.test.d.ts +2 -0
  319. package/dist/lib/ui/table.test.d.ts.map +1 -0
  320. package/dist/lib/ui/table.test.js +115 -0
  321. package/dist/lib/ui/table.test.js.map +1 -0
  322. package/dist/lib/ui/theme.d.ts +34 -0
  323. package/dist/lib/ui/theme.d.ts.map +1 -0
  324. package/dist/lib/ui/theme.js +37 -0
  325. package/dist/lib/ui/theme.js.map +1 -0
  326. package/dist/lib/ui/theme.test.d.ts +2 -0
  327. package/dist/lib/ui/theme.test.d.ts.map +1 -0
  328. package/dist/lib/ui/theme.test.js +76 -0
  329. package/dist/lib/ui/theme.test.js.map +1 -0
  330. package/dist/lib/wtconfig/environment.d.ts +18 -1
  331. package/dist/lib/wtconfig/environment.d.ts.map +1 -1
  332. package/dist/lib/wtconfig/environment.js +60 -24
  333. package/dist/lib/wtconfig/environment.js.map +1 -1
  334. package/dist/lib/wtconfig/environment.test.js +45 -1
  335. package/dist/lib/wtconfig/environment.test.js.map +1 -1
  336. package/dist/lib/wtlink/config-manifest.test.js +26 -0
  337. package/dist/lib/wtlink/config-manifest.test.js.map +1 -1
  338. package/dist/lib/wtlink/link-configs.js +7 -7
  339. package/dist/lib/wtlink/link-configs.js.map +1 -1
  340. package/dist/lib/wtlink/validate-manifest.d.ts.map +1 -1
  341. package/dist/lib/wtlink/validate-manifest.js +5 -5
  342. package/dist/lib/wtlink/validate-manifest.js.map +1 -1
  343. package/dist/lib/wtstate/args.d.ts.map +1 -1
  344. package/dist/lib/wtstate/args.js +2 -0
  345. package/dist/lib/wtstate/args.js.map +1 -1
  346. package/dist/mcp/server.d.ts +2 -1
  347. package/dist/mcp/server.d.ts.map +1 -1
  348. package/dist/mcp/server.js +264 -44
  349. package/dist/mcp/server.js.map +1 -1
  350. package/dist/mcp/server.test.js +111 -0
  351. package/dist/mcp/server.test.js.map +1 -1
  352. package/package.json +3 -1
  353. package/schemas/worktreerc.schema.json +23 -0
@@ -1,475 +1,404 @@
1
1
  /**
2
2
  * Logging system for git-worktree-tools
3
3
  *
4
- * Provides a singleton logger with:
5
- * - Multiple log levels (silent, error, warn, info, debug, trace)
6
- * - Console and file output support
7
- * - Log file rotation
8
- * - Timestamps and context
9
- * - Color support for console output
4
+ * Consola-based singleton logger with:
5
+ * - AuditFileReporter: persistent audit log with size-based rotation
6
+ * - ConditionalStderrReporter: verbose/quiet aware stderr output
7
+ * - Audit session tracking (command, cwd, branch, exit code, duration)
8
+ * - DEBUG=newpr deprecation handling
10
9
  *
11
10
  * Configuration sources (in order of priority):
12
- * 1. CLI flags (--verbose, --debug, --log-file)
13
- * 2. Environment variables (GWT_LOG_LEVEL, GWT_LOG_FILE)
14
- * 3. Config files (logLevel, logFile properties)
11
+ * 1. CLI flags (--verbose, --quiet, --no-color)
12
+ * 2. Environment variables (GWT_LOG_LEVEL, DEBUG)
13
+ * 3. Default (INFO)
15
14
  */
16
15
  import fs from 'fs';
17
16
  import path from 'path';
18
- import { LogLevel, DEFAULT_LOG_LEVEL, MAX_LOG_FILE_SIZE, MAX_LOG_FILES, getGlobalLogDir, } from './constants.js';
19
- import { red, yellow, cyan, gray, bold } from './colors.js';
17
+ import { execSync } from 'child_process';
18
+ import { createConsola } from 'consola';
19
+ import { LogLevel, MAX_LOG_FILE_SIZE, MAX_LOG_FILES, getGlobalDataDir } from './constants.js';
20
+ import { setColorEnabled } from './colors.js';
20
21
  export { LogLevel };
22
+ // ---------------------------------------------------------------------------
23
+ // Module-level state
24
+ // ---------------------------------------------------------------------------
25
+ /** Whether the DEBUG=newpr deprecation warning has been printed */
26
+ let deprecationWarned = false;
27
+ /** Whether JSON output mode is active */
28
+ let jsonMode = false;
29
+ /** Audit session context for the process exit handler */
30
+ let auditContext = {};
31
+ /** Track whether exit handler has been registered */
32
+ let exitHandlerRegistered = false;
33
+ /** Track whether audit file warning has been issued */
34
+ let auditFileWarned = false;
35
+ /** Reference to the AuditFileReporter for exit-time sync write */
36
+ let activeAuditReporter = null;
37
+ // ---------------------------------------------------------------------------
38
+ // AuditFileReporter
39
+ // ---------------------------------------------------------------------------
21
40
  /**
22
- * Parse log level from string
41
+ * Writes log entries to a persistent audit file with size-based rotation.
42
+ * Human-readable text lines by default; JSONL when json mode is active.
23
43
  */
24
- export function parseLogLevel(value) {
25
- const normalized = value.toLowerCase().trim();
26
- const mapping = {
27
- silent: LogLevel.SILENT,
28
- error: LogLevel.ERROR,
29
- warn: LogLevel.WARN,
30
- warning: LogLevel.WARN,
31
- info: LogLevel.INFO,
32
- debug: LogLevel.DEBUG,
33
- trace: LogLevel.TRACE,
34
- verbose: LogLevel.DEBUG,
35
- '0': LogLevel.SILENT,
36
- '1': LogLevel.ERROR,
37
- '2': LogLevel.WARN,
38
- '3': LogLevel.INFO,
39
- '4': LogLevel.DEBUG,
40
- '5': LogLevel.TRACE,
41
- };
42
- return mapping[normalized];
43
- }
44
- /**
45
- * Get log level name
46
- */
47
- function getLevelName(level) {
48
- const names = {
49
- [LogLevel.SILENT]: 'SILENT',
50
- [LogLevel.ERROR]: 'ERROR',
51
- [LogLevel.WARN]: 'WARN',
52
- [LogLevel.INFO]: 'INFO',
53
- [LogLevel.DEBUG]: 'DEBUG',
54
- [LogLevel.TRACE]: 'TRACE',
55
- };
56
- return names[level] || 'UNKNOWN';
57
- }
58
- /**
59
- * Format a log message for console output with colors
60
- */
61
- function formatConsoleMessage(level, message, timestamp, useColors) {
62
- const parts = [];
63
- if (timestamp) {
64
- parts.push(useColors ? gray(`[${timestamp}]`) : `[${timestamp}]`);
65
- }
66
- const levelStr = getLevelName(level).padEnd(5);
67
- let formattedLevel;
68
- let formattedMessage;
69
- if (useColors) {
70
- switch (level) {
71
- case LogLevel.ERROR:
72
- formattedLevel = red(bold(levelStr));
73
- formattedMessage = red(message);
74
- break;
75
- case LogLevel.WARN:
76
- formattedLevel = yellow(bold(levelStr));
77
- formattedMessage = yellow(message);
78
- break;
79
- case LogLevel.DEBUG:
80
- case LogLevel.TRACE:
81
- formattedLevel = gray(levelStr);
82
- formattedMessage = gray(message);
83
- break;
84
- default:
85
- formattedLevel = cyan(levelStr);
86
- formattedMessage = message;
87
- }
88
- }
89
- else {
90
- formattedLevel = levelStr;
91
- formattedMessage = message;
92
- }
93
- parts.push(formattedLevel);
94
- parts.push(formattedMessage);
95
- return parts.join(' ');
96
- }
97
- /**
98
- * Singleton Logger class
99
- */
100
- class Logger {
101
- static instance = null;
102
- level = DEFAULT_LOG_LEVEL;
103
- logFile = null;
104
- fileStream = null;
105
- timestamps = true;
106
- colors = true;
107
- consoleOutput = true;
108
- context = null;
109
- initialized = false;
110
- constructor() {
111
- // Private constructor for singleton
112
- }
113
- /**
114
- * Get the singleton logger instance
115
- */
116
- static getInstance() {
117
- if (!Logger.instance) {
118
- Logger.instance = new Logger();
119
- }
120
- return Logger.instance;
121
- }
122
- /**
123
- * Initialize the logger with configuration
124
- * Should be called early in CLI startup
125
- */
126
- initialize(config = {}) {
127
- // Determine log level from multiple sources (priority order)
128
- // 1. Explicit config (from CLI flags)
129
- // 2. Environment variable
130
- // 3. Default
131
- if (config.level !== undefined) {
132
- this.level = config.level;
133
- }
134
- else {
135
- const envLevel = process.env.GWT_LOG_LEVEL;
136
- if (envLevel) {
137
- const parsed = parseLogLevel(envLevel);
138
- if (parsed !== undefined) {
139
- this.level = parsed;
140
- }
141
- }
142
- }
143
- // Determine log file from multiple sources
144
- if (config.logFile !== undefined) {
145
- this.logFile = config.logFile;
146
- }
147
- else {
148
- const envLogFile = process.env.GWT_LOG_FILE;
149
- if (envLogFile) {
150
- this.logFile = envLogFile;
151
- }
152
- }
153
- if (config.timestamps !== undefined) {
154
- this.timestamps = config.timestamps;
155
- }
156
- if (config.colors !== undefined) {
157
- this.colors = config.colors;
158
- }
159
- if (config.consoleOutput !== undefined) {
160
- this.consoleOutput = config.consoleOutput;
161
- }
162
- // Set up file logging if configured
163
- if (this.logFile) {
164
- this.setupFileLogging(this.logFile);
165
- }
166
- this.initialized = true;
167
- }
168
- /**
169
- * Set up file logging with rotation
170
- */
171
- setupFileLogging(filePath) {
44
+ class AuditFileReporter {
45
+ stream = null;
46
+ filePath;
47
+ constructor(filePath) {
48
+ this.filePath = filePath;
172
49
  try {
173
- // Ensure directory exists
174
50
  const dir = path.dirname(filePath);
175
51
  fs.mkdirSync(dir, { recursive: true });
176
- // Check if rotation is needed
177
- if (fs.existsSync(filePath)) {
178
- const stats = fs.statSync(filePath);
179
- if (stats.size > MAX_LOG_FILE_SIZE) {
180
- this.rotateLogFiles(filePath);
52
+ rotateIfNeeded(filePath);
53
+ this.stream = fs.createWriteStream(filePath, { flags: 'a' });
54
+ this.stream.on('error', (err) => {
55
+ if (!auditFileWarned) {
56
+ auditFileWarned = true;
57
+ process.stderr.write(`[gwt] Audit log write error: ${err.message}\n`);
181
58
  }
182
- }
183
- // Open file stream in append mode
184
- this.fileStream = fs.createWriteStream(filePath, { flags: 'a' });
185
- this.fileStream.on('error', (err) => {
186
- console.error(`Logger: Failed to write to log file: ${err.message}`);
187
- this.fileStream = null;
59
+ this.stream = null;
188
60
  });
189
61
  }
190
62
  catch (err) {
191
- const message = err instanceof Error ? err.message : String(err);
192
- console.error(`Logger: Failed to set up file logging: ${message}`);
193
- }
194
- }
195
- /**
196
- * Rotate log files
197
- */
198
- rotateLogFiles(filePath) {
199
- try {
200
- // Shift existing rotated files
201
- for (let i = MAX_LOG_FILES - 1; i >= 1; i--) {
202
- const older = `${filePath}.${i}`;
203
- const newer = i === 1 ? filePath : `${filePath}.${i - 1}`;
204
- if (fs.existsSync(newer)) {
205
- if (i === MAX_LOG_FILES - 1 && fs.existsSync(older)) {
206
- fs.unlinkSync(older);
207
- }
208
- fs.renameSync(newer, older);
209
- }
63
+ if (!auditFileWarned) {
64
+ auditFileWarned = true;
65
+ const msg = err instanceof Error ? err.message : String(err);
66
+ process.stderr.write(`[gwt] Failed to open audit log: ${msg}\n`);
210
67
  }
211
68
  }
212
- catch (err) {
213
- const message = err instanceof Error ? err.message : String(err);
214
- console.error(`Logger: Failed to rotate log files: ${message}`);
215
- }
216
- }
217
- /**
218
- * Set the current context (e.g., command name)
219
- */
220
- setContext(context) {
221
- this.context = context;
222
69
  }
223
- /**
224
- * Set the log level
225
- */
226
- setLevel(level) {
227
- this.level = level;
228
- }
229
- /**
230
- * Get the current log level
231
- */
232
- getLevel() {
233
- return this.level;
234
- }
235
- /**
236
- * Check if a log level is enabled
237
- */
238
- isLevelEnabled(level) {
239
- return level <= this.level;
240
- }
241
- /**
242
- * Check if the logger is in debug mode
243
- */
244
- isDebug() {
245
- return this.level >= LogLevel.DEBUG;
246
- }
247
- /**
248
- * Check if the logger is in trace mode
249
- */
250
- isTrace() {
251
- return this.level >= LogLevel.TRACE;
252
- }
253
- /**
254
- * Internal log method
255
- */
256
- log(level, message, ...args) {
257
- this.logWithContext(level, this.context, message, ...args);
258
- }
259
- /**
260
- * Internal log method with explicit context (thread-safe for child loggers)
261
- */
262
- logWithContext(level, context, message, ...args) {
263
- if (level > this.level) {
70
+ log(logObj) {
71
+ if (!this.stream)
264
72
  return;
265
- }
266
- const timestamp = this.timestamps ? new Date().toISOString() : null;
267
- const formattedMessage = args.length > 0 ? this.formatMessage(message, args) : message;
268
- // Console output
269
- if (this.consoleOutput || !this.fileStream) {
270
- const consoleMsg = formatConsoleMessage(level, formattedMessage, timestamp, this.colors && process.stdout.isTTY === true);
271
- if (level === LogLevel.ERROR) {
272
- console.error(consoleMsg);
273
- }
274
- else if (level === LogLevel.WARN) {
275
- console.warn(consoleMsg);
73
+ try {
74
+ const timestamp = new Date().toISOString();
75
+ const levelName = levelToName(logObj.level);
76
+ const tag = logObj.tag ? ` [${logObj.tag}]` : '';
77
+ const message = formatLogArgs(logObj.args);
78
+ if (jsonMode) {
79
+ const entry = {
80
+ timestamp,
81
+ level: levelName,
82
+ ...(logObj.tag ? { tag: logObj.tag } : {}),
83
+ message,
84
+ };
85
+ this.stream.write(JSON.stringify(entry) + '\n');
276
86
  }
277
87
  else {
278
- console.log(consoleMsg);
88
+ this.stream.write(`[${timestamp}] ${levelName}${tag} ${message}\n`);
279
89
  }
280
90
  }
281
- // File output
282
- if (this.fileStream) {
283
- const entry = {
284
- timestamp: timestamp || new Date().toISOString(),
285
- level: getLevelName(level),
286
- message: formattedMessage,
287
- };
288
- if (context) {
289
- entry.context = context;
290
- }
291
- if (args.length > 0 && typeof args[0] === 'object') {
292
- entry.data = args[0];
293
- }
294
- this.fileStream.write(JSON.stringify(entry) + '\n');
91
+ catch {
92
+ // Swallow write errors silently after first warning
295
93
  }
296
94
  }
297
- /**
298
- * Format message with arguments (simple substitution)
299
- */
300
- formatMessage(message, args) {
301
- let result = message;
302
- for (const arg of args) {
303
- const replacement = typeof arg === 'object' ? JSON.stringify(arg) : String(arg);
304
- result = result.replace('%s', replacement);
305
- result = result.replace('%d', replacement);
306
- result = result.replace('%j', typeof arg === 'object' ? JSON.stringify(arg) : replacement);
307
- result = result.replace('%o', typeof arg === 'object' ? JSON.stringify(arg) : replacement);
308
- }
309
- // Append remaining args if placeholders exhausted
310
- const placeholders = (message.match(/%[sdjo]/g) || []).length;
311
- if (args.length > placeholders) {
312
- const extra = args
313
- .slice(placeholders)
314
- .map((a) => (typeof a === 'object' ? JSON.stringify(a) : String(a)));
315
- result += ' ' + extra.join(' ');
316
- }
317
- return result;
318
- }
319
- /**
320
- * Log an error message
321
- */
322
- error(message, ...args) {
323
- this.log(LogLevel.ERROR, message, ...args);
324
- }
325
- /**
326
- * Log a warning message
327
- */
328
- warn(message, ...args) {
329
- this.log(LogLevel.WARN, message, ...args);
330
- }
331
- /**
332
- * Log an info message
333
- */
334
- info(message, ...args) {
335
- this.log(LogLevel.INFO, message, ...args);
336
- }
337
- /**
338
- * Log a debug message
339
- */
340
- debug(message, ...args) {
341
- this.log(LogLevel.DEBUG, message, ...args);
342
- }
343
- /**
344
- * Log a trace message
345
- */
346
- trace(message, ...args) {
347
- this.log(LogLevel.TRACE, message, ...args);
348
- }
349
- /**
350
- * Log an error with stack trace
351
- */
352
- errorWithStack(err, message) {
353
- const msg = message ? `${message}: ${err.message}` : err.message;
354
- this.error(msg);
355
- if (this.level >= LogLevel.DEBUG && err.stack) {
356
- this.debug(err.stack);
357
- }
358
- }
359
- /**
360
- * Create a child logger with a specific context
361
- */
362
- child(context) {
363
- return new ChildLogger(this, context);
364
- }
365
- /**
366
- * Close the logger (flush file stream)
367
- */
368
95
  close() {
369
- return new Promise((resolve) => {
370
- if (this.fileStream) {
371
- this.fileStream.end(() => {
372
- this.fileStream = null;
373
- resolve();
374
- });
375
- }
376
- else {
377
- resolve();
96
+ if (this.stream) {
97
+ const stream = this.stream;
98
+ this.stream = null;
99
+ // Close the fd synchronously so callers can immediately delete the file.
100
+ // On Windows, stream.end()/destroy() close the fd asynchronously, leaving
101
+ // the file locked until the next tick.
102
+ const streamInternal = stream;
103
+ const fd = streamInternal.fd;
104
+ if (typeof fd === 'number') {
105
+ // Null out the fd BEFORE closing so destroy() won't try to close it
106
+ // again (which would risk closing a reused fd number).
107
+ streamInternal.fd = null;
108
+ try {
109
+ fs.closeSync(fd);
110
+ }
111
+ catch {
112
+ /* best effort */
113
+ }
378
114
  }
379
- });
380
- }
381
- /**
382
- * Get the default log file path
383
- */
384
- static getDefaultLogFilePath() {
385
- return path.join(getGlobalLogDir(), 'wt.log');
386
- }
387
- /**
388
- * Reset the singleton instance (for testing)
389
- */
390
- static reset() {
391
- if (Logger.instance) {
392
- Logger.instance.close();
393
- Logger.instance = null;
115
+ stream.removeAllListeners('error');
116
+ stream.on('error', () => { });
117
+ stream.destroy();
394
118
  }
395
119
  }
396
120
  }
121
+ // ---------------------------------------------------------------------------
122
+ // ConditionalStderrReporter
123
+ // ---------------------------------------------------------------------------
397
124
  /**
398
- * Child logger with a specific context
399
- * Uses logWithContext to avoid race conditions with concurrent logging
125
+ * Writes to stderr based on log level and verbose mode.
126
+ * WARN and ERROR always print; DEBUG/INFO/TRACE only print when verbose=true.
400
127
  */
401
- class ChildLogger {
402
- parent;
403
- context;
404
- constructor(parent, context) {
405
- this.parent = parent;
406
- this.context = context;
128
+ class ConditionalStderrReporter {
129
+ verbose;
130
+ useColors;
131
+ constructor(verbose, useColors) {
132
+ this.verbose = verbose;
133
+ this.useColors = useColors;
134
+ }
135
+ log(logObj) {
136
+ // Level < 2 means warn (1) or error/fatal (0) — always print
137
+ // Level >= 2 means info (3), debug (4), trace (5) — only if verbose
138
+ if (logObj.level >= 2 && !this.verbose) {
139
+ return;
140
+ }
141
+ const levelName = levelToName(logObj.level);
142
+ const tag = logObj.tag ? ` [${logObj.tag}]` : '';
143
+ const message = formatLogArgs(logObj.args);
144
+ let prefix;
145
+ if (this.useColors) {
146
+ prefix = colorizeLevel(levelName, logObj.level);
147
+ }
148
+ else {
149
+ prefix = `[${levelName}]`;
150
+ }
151
+ process.stderr.write(`${prefix}${tag} ${message}\n`);
407
152
  }
408
- error(message, ...args) {
409
- this.parent.logWithContext(LogLevel.ERROR, this.context, message, ...args);
153
+ }
154
+ // ---------------------------------------------------------------------------
155
+ // Rotation logic
156
+ // ---------------------------------------------------------------------------
157
+ function rotateIfNeeded(filePath) {
158
+ try {
159
+ if (!fs.existsSync(filePath))
160
+ return;
161
+ const stats = fs.statSync(filePath);
162
+ if (stats.size <= MAX_LOG_FILE_SIZE)
163
+ return;
164
+ // Shift: audit.log.2 deleted, audit.log.1 -> .2, audit.log -> .1
165
+ for (let i = MAX_LOG_FILES - 1; i >= 1; i--) {
166
+ const older = `${filePath}.${i}`;
167
+ const newer = i === 1 ? filePath : `${filePath}.${i - 1}`;
168
+ if (fs.existsSync(newer)) {
169
+ if (i === MAX_LOG_FILES - 1 && fs.existsSync(older)) {
170
+ fs.unlinkSync(older);
171
+ }
172
+ fs.renameSync(newer, older);
173
+ }
174
+ }
410
175
  }
411
- warn(message, ...args) {
412
- this.parent.logWithContext(LogLevel.WARN, this.context, message, ...args);
176
+ catch {
177
+ // Best-effort rotation — do not crash
413
178
  }
414
- info(message, ...args) {
415
- this.parent.logWithContext(LogLevel.INFO, this.context, message, ...args);
179
+ }
180
+ // ---------------------------------------------------------------------------
181
+ // Helpers
182
+ // ---------------------------------------------------------------------------
183
+ function levelToName(level) {
184
+ if (level <= 0) {
185
+ // 0 = error/fatal, negative = silent (shouldn't log)
186
+ return level === 0 ? 'ERROR' : 'SILENT';
187
+ }
188
+ switch (level) {
189
+ case 1:
190
+ return 'WARN';
191
+ case 2:
192
+ return 'LOG';
193
+ case 3:
194
+ return 'INFO';
195
+ case 4:
196
+ return 'DEBUG';
197
+ case 5:
198
+ return 'TRACE';
199
+ default:
200
+ return level > 5 ? 'TRACE' : 'ERROR';
416
201
  }
417
- debug(message, ...args) {
418
- this.parent.logWithContext(LogLevel.DEBUG, this.context, message, ...args);
202
+ }
203
+ function colorizeLevel(name, level) {
204
+ // ANSI color codes
205
+ const RED = '\x1b[31m';
206
+ const YELLOW = '\x1b[33m';
207
+ const CYAN = '\x1b[36m';
208
+ const GRAY = '\x1b[90m';
209
+ const RESET = '\x1b[0m';
210
+ switch (true) {
211
+ case level <= 0:
212
+ return `${RED}[${name}]${RESET}`;
213
+ case level === 1:
214
+ return `${YELLOW}[${name}]${RESET}`;
215
+ case level <= 3:
216
+ return `${CYAN}[${name}]${RESET}`;
217
+ default:
218
+ return `${GRAY}[${name}]${RESET}`;
419
219
  }
420
- trace(message, ...args) {
421
- this.parent.logWithContext(LogLevel.TRACE, this.context, message, ...args);
220
+ }
221
+ function formatLogArgs(args) {
222
+ return args
223
+ .map((a) => (typeof a === 'object' && a !== null ? JSON.stringify(a) : String(a)))
224
+ .join(' ');
225
+ }
226
+ function getCurrentGitBranch() {
227
+ try {
228
+ return execSync('git rev-parse --abbrev-ref HEAD', {
229
+ encoding: 'utf8',
230
+ stdio: ['pipe', 'pipe', 'pipe'],
231
+ }).trim();
232
+ }
233
+ catch {
234
+ return 'unknown';
422
235
  }
423
236
  }
237
+ // ---------------------------------------------------------------------------
238
+ // Logger singleton
239
+ // ---------------------------------------------------------------------------
424
240
  /**
425
- * The singleton logger instance
241
+ * The singleton consola logger instance.
242
+ * Starts with empty reporters — call initializeLogger() to configure.
426
243
  */
427
- export const logger = Logger.getInstance();
244
+ export const logger = createConsola({
245
+ level: 3, // INFO default
246
+ reporters: [],
247
+ });
428
248
  /**
429
- * Initialize the logger from CLI arguments and config
430
- * Call this early in CLI entry points
249
+ * Configure the logger with CLI flags, env vars, and reporters.
250
+ * Must be called early in CLI startup. Safe to call multiple times
251
+ * (replaces reporters each time).
431
252
  */
432
- export function initializeLogger(options) {
253
+ export function initializeLogger(options = {}) {
254
+ // ----------------------------------------------------------
255
+ // 1. Resolve level: CLI flag > env var > default
256
+ // ----------------------------------------------------------
433
257
  let level;
434
- // Priority: CLI flags > env vars > config
435
258
  if (options.quiet) {
436
- level = LogLevel.SILENT;
259
+ level = 0; // error only
437
260
  }
438
- else if (options.debug) {
439
- level = LogLevel.DEBUG;
440
- }
441
- else if (options.verbose !== undefined) {
442
- if (typeof options.verbose === 'number') {
443
- // -v = DEBUG, -vv = TRACE
444
- level = options.verbose >= 2 ? LogLevel.TRACE : LogLevel.DEBUG;
445
- }
446
- else if (options.verbose) {
447
- level = LogLevel.DEBUG;
448
- }
261
+ else if (options.verbose) {
262
+ level = 4; // debug
449
263
  }
450
264
  else if (process.env.GWT_LOG_LEVEL) {
451
- level = parseLogLevel(process.env.GWT_LOG_LEVEL);
452
- }
453
- else if (options.configLogLevel) {
454
- level =
455
- typeof options.configLogLevel === 'string'
456
- ? parseLogLevel(options.configLogLevel)
457
- : options.configLogLevel;
458
- }
459
- // Log file priority: CLI flag > env var > config
460
- let logFile;
461
- if (options.logFile) {
462
- logFile = options.logFile;
463
- }
464
- else if (process.env.GWT_LOG_FILE) {
465
- logFile = process.env.GWT_LOG_FILE;
265
+ const parsed = parseLogLevel(process.env.GWT_LOG_LEVEL);
266
+ level = parsed !== undefined ? parsed : 3;
267
+ }
268
+ else if (process.env.DEBUG === 'newpr' ||
269
+ process.env.DEBUG === '*' ||
270
+ process.env.DEBUG === '1') {
271
+ level = 4; // debug
272
+ if (!deprecationWarned) {
273
+ deprecationWarned = true;
274
+ process.stderr.write('WARNING: DEBUG=newpr is deprecated, use GWT_LOG_LEVEL=debug\n');
275
+ }
466
276
  }
467
- else if (options.configLogFile) {
468
- logFile = options.configLogFile;
277
+ else {
278
+ level = 3; // info (default)
279
+ }
280
+ logger.level = level;
281
+ // ----------------------------------------------------------
282
+ // 2. Handle color
283
+ // ----------------------------------------------------------
284
+ const useColors = !options.noColor && process.env.NO_COLOR === undefined;
285
+ if (options.noColor) {
286
+ setColorEnabled(false);
287
+ }
288
+ // ----------------------------------------------------------
289
+ // 3. JSON mode
290
+ // ----------------------------------------------------------
291
+ jsonMode = options.json ?? false;
292
+ // ----------------------------------------------------------
293
+ // 4. Build reporters
294
+ // ----------------------------------------------------------
295
+ const reporters = [];
296
+ // Audit file reporter (always on)
297
+ try {
298
+ const auditPath = path.join(getGlobalDataDir(), 'audit.log');
299
+ const auditReporter = new AuditFileReporter(auditPath);
300
+ reporters.push(auditReporter);
301
+ activeAuditReporter = auditReporter;
302
+ }
303
+ catch {
304
+ // Best effort — continue without audit file
305
+ }
306
+ // Stderr reporter
307
+ const verbose = options.verbose ?? false;
308
+ reporters.push(new ConditionalStderrReporter(verbose, useColors));
309
+ logger.setReporters(reporters);
310
+ // ----------------------------------------------------------
311
+ // 5. Audit session tracking
312
+ // ----------------------------------------------------------
313
+ if (options.commandName) {
314
+ auditContext.command = options.commandName;
315
+ auditContext.startTime = Date.now();
316
+ auditContext.cwd = process.cwd();
317
+ auditContext.gitBranch = getCurrentGitBranch();
318
+ }
319
+ if (!exitHandlerRegistered) {
320
+ exitHandlerRegistered = true;
321
+ process.on('exit', (code) => {
322
+ if (!auditContext.command || !activeAuditReporter)
323
+ return;
324
+ const duration = auditContext.startTime ? Date.now() - auditContext.startTime : 0;
325
+ const entry = {
326
+ type: 'session',
327
+ timestamp: new Date().toISOString(),
328
+ command: auditContext.command,
329
+ cwd: auditContext.cwd,
330
+ gitBranch: auditContext.gitBranch,
331
+ exitCode: code,
332
+ durationMs: duration,
333
+ };
334
+ if (auditContext.worktreePath)
335
+ entry.worktreePath = auditContext.worktreePath;
336
+ if (auditContext.prNumber !== undefined)
337
+ entry.prNumber = auditContext.prNumber;
338
+ // Must be synchronous — Node.js does not process async after 'exit'
339
+ try {
340
+ const logPath = activeAuditReporter.filePath;
341
+ const line = jsonMode
342
+ ? JSON.stringify(entry)
343
+ : `[${entry.timestamp}] SESSION command=${entry.command} cwd=${entry.cwd} branch=${entry.gitBranch} exit=${code} duration=${duration}ms`;
344
+ fs.appendFileSync(logPath, line + '\n');
345
+ }
346
+ catch {
347
+ // Cannot do anything on exit — swallow
348
+ }
349
+ });
469
350
  }
470
- logger.initialize({
471
- level,
472
- logFile,
473
- });
351
+ }
352
+ // ---------------------------------------------------------------------------
353
+ // setAuditContext
354
+ // ---------------------------------------------------------------------------
355
+ /**
356
+ * Merge additional metadata into the audit context.
357
+ * Called from CLI handlers to add worktreePath, prNumber, etc.
358
+ */
359
+ export function setAuditContext(ctx) {
360
+ Object.assign(auditContext, ctx);
361
+ }
362
+ // ---------------------------------------------------------------------------
363
+ // parseLogLevel
364
+ // ---------------------------------------------------------------------------
365
+ /**
366
+ * Parse a string log level name to its numeric consola equivalent.
367
+ * Returns undefined for unrecognized values.
368
+ */
369
+ export function parseLogLevel(value) {
370
+ const normalized = value.toLowerCase().trim();
371
+ const mapping = {
372
+ silent: -999,
373
+ error: 0,
374
+ warn: 1,
375
+ warning: 1,
376
+ info: 3,
377
+ debug: 4,
378
+ trace: 5,
379
+ verbose: 4,
380
+ };
381
+ return mapping[normalized];
382
+ }
383
+ // ---------------------------------------------------------------------------
384
+ // _resetForTesting
385
+ // ---------------------------------------------------------------------------
386
+ /**
387
+ * Reset all module-level state for test isolation.
388
+ * Prefixed with _ to signal internal-only use.
389
+ */
390
+ export function _resetForTesting() {
391
+ deprecationWarned = false;
392
+ jsonMode = false;
393
+ auditContext = {};
394
+ auditFileWarned = false;
395
+ if (activeAuditReporter) {
396
+ activeAuditReporter.close();
397
+ }
398
+ activeAuditReporter = null;
399
+ // Note: we don't reset exitHandlerRegistered because process.on('exit') handlers
400
+ // cannot be removed cleanly and we only register once per process.
401
+ logger.setReporters([]);
402
+ logger.level = 3; // INFO default
474
403
  }
475
404
  //# sourceMappingURL=logger.js.map