@jacques-ai/cli 0.0.7-alpha.1

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 (508) hide show
  1. package/dist/assets/mascot-ansi.d.ts +8 -0
  2. package/dist/assets/mascot-ansi.d.ts.map +1 -0
  3. package/dist/assets/mascot-ansi.js +15 -0
  4. package/dist/assets/mascot-ansi.js.map +1 -0
  5. package/dist/cli.d.ts +15 -0
  6. package/dist/cli.d.ts.map +1 -0
  7. package/dist/cli.js +53 -0
  8. package/dist/cli.js.map +1 -0
  9. package/dist/commands/client-helper.d.ts +14 -0
  10. package/dist/commands/client-helper.d.ts.map +1 -0
  11. package/dist/commands/client-helper.js +38 -0
  12. package/dist/commands/client-helper.js.map +1 -0
  13. package/dist/commands/dashboard.d.ts +8 -0
  14. package/dist/commands/dashboard.d.ts.map +1 -0
  15. package/dist/commands/dashboard.js +159 -0
  16. package/dist/commands/dashboard.js.map +1 -0
  17. package/dist/commands/index.d.ts +6 -0
  18. package/dist/commands/index.d.ts.map +1 -0
  19. package/dist/commands/index.js +6 -0
  20. package/dist/commands/index.js.map +1 -0
  21. package/dist/commands/list.d.ts +5 -0
  22. package/dist/commands/list.d.ts.map +1 -0
  23. package/dist/commands/list.js +13 -0
  24. package/dist/commands/list.js.map +1 -0
  25. package/dist/commands/search.d.ts +13 -0
  26. package/dist/commands/search.d.ts.map +1 -0
  27. package/dist/commands/search.js +79 -0
  28. package/dist/commands/search.js.map +1 -0
  29. package/dist/commands/setup.d.ts +8 -0
  30. package/dist/commands/setup.d.ts.map +1 -0
  31. package/dist/commands/setup.js +32 -0
  32. package/dist/commands/setup.js.map +1 -0
  33. package/dist/commands/status.d.ts +5 -0
  34. package/dist/commands/status.d.ts.map +1 -0
  35. package/dist/commands/status.js +27 -0
  36. package/dist/commands/status.js.map +1 -0
  37. package/dist/components/ActiveSessionsView.d.ts +18 -0
  38. package/dist/components/ActiveSessionsView.d.ts.map +1 -0
  39. package/dist/components/ActiveSessionsView.js +68 -0
  40. package/dist/components/ActiveSessionsView.js.map +1 -0
  41. package/dist/components/AddContextConfirmView.d.ts +21 -0
  42. package/dist/components/AddContextConfirmView.d.ts.map +1 -0
  43. package/dist/components/AddContextConfirmView.js +44 -0
  44. package/dist/components/AddContextConfirmView.js.map +1 -0
  45. package/dist/components/App.d.ts +11 -0
  46. package/dist/components/App.d.ts.map +1 -0
  47. package/dist/components/App.js +292 -0
  48. package/dist/components/App.js.map +1 -0
  49. package/dist/components/ArchiveBrowserView.d.ts +40 -0
  50. package/dist/components/ArchiveBrowserView.d.ts.map +1 -0
  51. package/dist/components/ArchiveBrowserView.js +161 -0
  52. package/dist/components/ArchiveBrowserView.js.map +1 -0
  53. package/dist/components/ArchiveInitProgressView.d.ts +16 -0
  54. package/dist/components/ArchiveInitProgressView.d.ts.map +1 -0
  55. package/dist/components/ArchiveInitProgressView.js +72 -0
  56. package/dist/components/ArchiveInitProgressView.js.map +1 -0
  57. package/dist/components/AutoCompactToggle.d.ts +16 -0
  58. package/dist/components/AutoCompactToggle.d.ts.map +1 -0
  59. package/dist/components/AutoCompactToggle.js +12 -0
  60. package/dist/components/AutoCompactToggle.js.map +1 -0
  61. package/dist/components/BottomControls.d.ts +12 -0
  62. package/dist/components/BottomControls.d.ts.map +1 -0
  63. package/dist/components/BottomControls.js +11 -0
  64. package/dist/components/BottomControls.js.map +1 -0
  65. package/dist/components/CompactHeader.d.ts +21 -0
  66. package/dist/components/CompactHeader.d.ts.map +1 -0
  67. package/dist/components/CompactHeader.js +63 -0
  68. package/dist/components/CompactHeader.js.map +1 -0
  69. package/dist/components/CompactPanel.d.ts +22 -0
  70. package/dist/components/CompactPanel.d.ts.map +1 -0
  71. package/dist/components/CompactPanel.js +31 -0
  72. package/dist/components/CompactPanel.js.map +1 -0
  73. package/dist/components/ContentBox.d.ts +14 -0
  74. package/dist/components/ContentBox.d.ts.map +1 -0
  75. package/dist/components/ContentBox.js +24 -0
  76. package/dist/components/ContentBox.js.map +1 -0
  77. package/dist/components/ContextProgress.d.ts +14 -0
  78. package/dist/components/ContextProgress.d.ts.map +1 -0
  79. package/dist/components/ContextProgress.js +36 -0
  80. package/dist/components/ContextProgress.js.map +1 -0
  81. package/dist/components/Dashboard.d.ts +46 -0
  82. package/dist/components/Dashboard.d.ts.map +1 -0
  83. package/dist/components/Dashboard.js +67 -0
  84. package/dist/components/Dashboard.js.map +1 -0
  85. package/dist/components/FilterSelectionView.d.ts +15 -0
  86. package/dist/components/FilterSelectionView.d.ts.map +1 -0
  87. package/dist/components/FilterSelectionView.js +27 -0
  88. package/dist/components/FilterSelectionView.js.map +1 -0
  89. package/dist/components/GoogleDocsBrowserView.d.ts +20 -0
  90. package/dist/components/GoogleDocsBrowserView.d.ts.map +1 -0
  91. package/dist/components/GoogleDocsBrowserView.js +72 -0
  92. package/dist/components/GoogleDocsBrowserView.js.map +1 -0
  93. package/dist/components/HandoffBrowserView.d.ts +20 -0
  94. package/dist/components/HandoffBrowserView.d.ts.map +1 -0
  95. package/dist/components/HandoffBrowserView.js +66 -0
  96. package/dist/components/HandoffBrowserView.js.map +1 -0
  97. package/dist/components/Header.d.ts +14 -0
  98. package/dist/components/Header.d.ts.map +1 -0
  99. package/dist/components/Header.js +16 -0
  100. package/dist/components/Header.js.map +1 -0
  101. package/dist/components/HorizontalMenu.d.ts +24 -0
  102. package/dist/components/HorizontalMenu.d.ts.map +1 -0
  103. package/dist/components/HorizontalMenu.js +20 -0
  104. package/dist/components/HorizontalMenu.js.map +1 -0
  105. package/dist/components/ImageMascot.d.ts +8 -0
  106. package/dist/components/ImageMascot.d.ts.map +1 -0
  107. package/dist/components/ImageMascot.js +7 -0
  108. package/dist/components/ImageMascot.js.map +1 -0
  109. package/dist/components/LLMWorkingView.d.ts +19 -0
  110. package/dist/components/LLMWorkingView.d.ts.map +1 -0
  111. package/dist/components/LLMWorkingView.js +72 -0
  112. package/dist/components/LLMWorkingView.js.map +1 -0
  113. package/dist/components/LoadContextView.d.ts +22 -0
  114. package/dist/components/LoadContextView.d.ts.map +1 -0
  115. package/dist/components/LoadContextView.js +83 -0
  116. package/dist/components/LoadContextView.js.map +1 -0
  117. package/dist/components/MainMenuView.d.ts +20 -0
  118. package/dist/components/MainMenuView.d.ts.map +1 -0
  119. package/dist/components/MainMenuView.js +49 -0
  120. package/dist/components/MainMenuView.js.map +1 -0
  121. package/dist/components/Mascot.d.ts +24 -0
  122. package/dist/components/Mascot.d.ts.map +1 -0
  123. package/dist/components/Mascot.js +32 -0
  124. package/dist/components/Mascot.js.map +1 -0
  125. package/dist/components/Menu.d.ts +22 -0
  126. package/dist/components/Menu.d.ts.map +1 -0
  127. package/dist/components/Menu.js +28 -0
  128. package/dist/components/Menu.js.map +1 -0
  129. package/dist/components/NotionBrowserView.d.ts +21 -0
  130. package/dist/components/NotionBrowserView.d.ts.map +1 -0
  131. package/dist/components/NotionBrowserView.js +75 -0
  132. package/dist/components/NotionBrowserView.js.map +1 -0
  133. package/dist/components/ObsidianBrowserView.d.ts +21 -0
  134. package/dist/components/ObsidianBrowserView.d.ts.map +1 -0
  135. package/dist/components/ObsidianBrowserView.js +74 -0
  136. package/dist/components/ObsidianBrowserView.js.map +1 -0
  137. package/dist/components/ObsidianConfigView.d.ts +19 -0
  138. package/dist/components/ObsidianConfigView.d.ts.map +1 -0
  139. package/dist/components/ObsidianConfigView.js +45 -0
  140. package/dist/components/ObsidianConfigView.js.map +1 -0
  141. package/dist/components/PlaceholderView.d.ts +15 -0
  142. package/dist/components/PlaceholderView.d.ts.map +1 -0
  143. package/dist/components/PlaceholderView.js +20 -0
  144. package/dist/components/PlaceholderView.js.map +1 -0
  145. package/dist/components/PlanViewerView.d.ts +26 -0
  146. package/dist/components/PlanViewerView.d.ts.map +1 -0
  147. package/dist/components/PlanViewerView.js +170 -0
  148. package/dist/components/PlanViewerView.js.map +1 -0
  149. package/dist/components/ProgressBar.d.ts +24 -0
  150. package/dist/components/ProgressBar.d.ts.map +1 -0
  151. package/dist/components/ProgressBar.js +23 -0
  152. package/dist/components/ProgressBar.js.map +1 -0
  153. package/dist/components/ProjectDashboardView.d.ts +38 -0
  154. package/dist/components/ProjectDashboardView.d.ts.map +1 -0
  155. package/dist/components/ProjectDashboardView.js +129 -0
  156. package/dist/components/ProjectDashboardView.js.map +1 -0
  157. package/dist/components/SaveContextView.d.ts +32 -0
  158. package/dist/components/SaveContextView.d.ts.map +1 -0
  159. package/dist/components/SaveContextView.js +41 -0
  160. package/dist/components/SaveContextView.js.map +1 -0
  161. package/dist/components/SessionDetails.d.ts +14 -0
  162. package/dist/components/SessionDetails.d.ts.map +1 -0
  163. package/dist/components/SessionDetails.js +87 -0
  164. package/dist/components/SessionDetails.js.map +1 -0
  165. package/dist/components/SessionsExperimentView.d.ts +35 -0
  166. package/dist/components/SessionsExperimentView.d.ts.map +1 -0
  167. package/dist/components/SessionsExperimentView.js +347 -0
  168. package/dist/components/SessionsExperimentView.js.map +1 -0
  169. package/dist/components/SessionsList.d.ts +20 -0
  170. package/dist/components/SessionsList.d.ts.map +1 -0
  171. package/dist/components/SessionsList.js +75 -0
  172. package/dist/components/SessionsList.js.map +1 -0
  173. package/dist/components/SettingsView.d.ts +48 -0
  174. package/dist/components/SettingsView.d.ts.map +1 -0
  175. package/dist/components/SettingsView.js +156 -0
  176. package/dist/components/SettingsView.js.map +1 -0
  177. package/dist/components/SourceSelectionView.d.ts +26 -0
  178. package/dist/components/SourceSelectionView.d.ts.map +1 -0
  179. package/dist/components/SourceSelectionView.js +109 -0
  180. package/dist/components/SourceSelectionView.js.map +1 -0
  181. package/dist/components/VerticalMenu.d.ts +18 -0
  182. package/dist/components/VerticalMenu.d.ts.map +1 -0
  183. package/dist/components/VerticalMenu.js +15 -0
  184. package/dist/components/VerticalMenu.js.map +1 -0
  185. package/dist/components/ascii-art/index.d.ts +6 -0
  186. package/dist/components/ascii-art/index.d.ts.map +1 -0
  187. package/dist/components/ascii-art/index.js +6 -0
  188. package/dist/components/ascii-art/index.js.map +1 -0
  189. package/dist/components/ascii-art/progress.d.ts +53 -0
  190. package/dist/components/ascii-art/progress.d.ts.map +1 -0
  191. package/dist/components/ascii-art/progress.js +102 -0
  192. package/dist/components/ascii-art/progress.js.map +1 -0
  193. package/dist/components/ascii-art/scene.d.ts +34 -0
  194. package/dist/components/ascii-art/scene.d.ts.map +1 -0
  195. package/dist/components/ascii-art/scene.js +72 -0
  196. package/dist/components/ascii-art/scene.js.map +1 -0
  197. package/dist/components/index.d.ts +27 -0
  198. package/dist/components/index.d.ts.map +1 -0
  199. package/dist/components/index.js +25 -0
  200. package/dist/components/index.js.map +1 -0
  201. package/dist/components/layout/HorizontalLayout.d.ts +19 -0
  202. package/dist/components/layout/HorizontalLayout.d.ts.map +1 -0
  203. package/dist/components/layout/HorizontalLayout.js +62 -0
  204. package/dist/components/layout/HorizontalLayout.js.map +1 -0
  205. package/dist/components/layout/VerticalLayout.d.ts +16 -0
  206. package/dist/components/layout/VerticalLayout.d.ts.map +1 -0
  207. package/dist/components/layout/VerticalLayout.js +32 -0
  208. package/dist/components/layout/VerticalLayout.js.map +1 -0
  209. package/dist/components/layout/index.d.ts +6 -0
  210. package/dist/components/layout/index.d.ts.map +1 -0
  211. package/dist/components/layout/index.js +4 -0
  212. package/dist/components/layout/index.js.map +1 -0
  213. package/dist/components/layout/theme.d.ts +20 -0
  214. package/dist/components/layout/theme.d.ts.map +1 -0
  215. package/dist/components/layout/theme.js +23 -0
  216. package/dist/components/layout/theme.js.map +1 -0
  217. package/dist/components/setup/DoneStep.d.ts +7 -0
  218. package/dist/components/setup/DoneStep.d.ts.map +1 -0
  219. package/dist/components/setup/DoneStep.js +31 -0
  220. package/dist/components/setup/DoneStep.js.map +1 -0
  221. package/dist/components/setup/InstallingStep.d.ts +11 -0
  222. package/dist/components/setup/InstallingStep.d.ts.map +1 -0
  223. package/dist/components/setup/InstallingStep.js +27 -0
  224. package/dist/components/setup/InstallingStep.js.map +1 -0
  225. package/dist/components/setup/OptionsStep.d.ts +9 -0
  226. package/dist/components/setup/OptionsStep.d.ts.map +1 -0
  227. package/dist/components/setup/OptionsStep.js +39 -0
  228. package/dist/components/setup/OptionsStep.js.map +1 -0
  229. package/dist/components/setup/PrerequisitesStep.d.ts +7 -0
  230. package/dist/components/setup/PrerequisitesStep.d.ts.map +1 -0
  231. package/dist/components/setup/PrerequisitesStep.js +31 -0
  232. package/dist/components/setup/PrerequisitesStep.js.map +1 -0
  233. package/dist/components/setup/SetupFrame.d.ts +17 -0
  234. package/dist/components/setup/SetupFrame.d.ts.map +1 -0
  235. package/dist/components/setup/SetupFrame.js +33 -0
  236. package/dist/components/setup/SetupFrame.js.map +1 -0
  237. package/dist/components/setup/SetupWizard.d.ts +9 -0
  238. package/dist/components/setup/SetupWizard.d.ts.map +1 -0
  239. package/dist/components/setup/SetupWizard.js +149 -0
  240. package/dist/components/setup/SetupWizard.js.map +1 -0
  241. package/dist/components/setup/Spinner.d.ts +10 -0
  242. package/dist/components/setup/Spinner.d.ts.map +1 -0
  243. package/dist/components/setup/Spinner.js +20 -0
  244. package/dist/components/setup/Spinner.js.map +1 -0
  245. package/dist/components/setup/SyncStep.d.ts +11 -0
  246. package/dist/components/setup/SyncStep.d.ts.map +1 -0
  247. package/dist/components/setup/SyncStep.js +47 -0
  248. package/dist/components/setup/SyncStep.js.map +1 -0
  249. package/dist/components/setup/VerificationStep.d.ts +7 -0
  250. package/dist/components/setup/VerificationStep.d.ts.map +1 -0
  251. package/dist/components/setup/VerificationStep.js +27 -0
  252. package/dist/components/setup/VerificationStep.js.map +1 -0
  253. package/dist/components/setup/WelcomeStep.d.ts +6 -0
  254. package/dist/components/setup/WelcomeStep.d.ts.map +1 -0
  255. package/dist/components/setup/WelcomeStep.js +17 -0
  256. package/dist/components/setup/WelcomeStep.js.map +1 -0
  257. package/dist/components/shared/ProgressLine.d.ts +10 -0
  258. package/dist/components/shared/ProgressLine.d.ts.map +1 -0
  259. package/dist/components/shared/ProgressLine.js +18 -0
  260. package/dist/components/shared/ProgressLine.js.map +1 -0
  261. package/dist/components/shared/ProjectLine.d.ts +10 -0
  262. package/dist/components/shared/ProjectLine.d.ts.map +1 -0
  263. package/dist/components/shared/ProjectLine.js +19 -0
  264. package/dist/components/shared/ProjectLine.js.map +1 -0
  265. package/dist/components/shared/ScanningIndicator.d.ts +9 -0
  266. package/dist/components/shared/ScanningIndicator.d.ts.map +1 -0
  267. package/dist/components/shared/ScanningIndicator.js +22 -0
  268. package/dist/components/shared/ScanningIndicator.js.map +1 -0
  269. package/dist/components/shared/StatusLine.d.ts +13 -0
  270. package/dist/components/shared/StatusLine.d.ts.map +1 -0
  271. package/dist/components/shared/StatusLine.js +24 -0
  272. package/dist/components/shared/StatusLine.js.map +1 -0
  273. package/dist/components/shared/StatusLine.test.d.ts +8 -0
  274. package/dist/components/shared/StatusLine.test.d.ts.map +1 -0
  275. package/dist/components/shared/StatusLine.test.js +50 -0
  276. package/dist/components/shared/StatusLine.test.js.map +1 -0
  277. package/dist/components/shared/index.d.ts +4 -0
  278. package/dist/components/shared/index.d.ts.map +1 -0
  279. package/dist/components/shared/index.js +4 -0
  280. package/dist/components/shared/index.js.map +1 -0
  281. package/dist/handoff/catalog.test.d.ts +5 -0
  282. package/dist/handoff/catalog.test.d.ts.map +1 -0
  283. package/dist/handoff/catalog.test.js +172 -0
  284. package/dist/handoff/catalog.test.js.map +1 -0
  285. package/dist/handoff/subagents.test.d.ts +8 -0
  286. package/dist/handoff/subagents.test.d.ts.map +1 -0
  287. package/dist/handoff/subagents.test.js +139 -0
  288. package/dist/handoff/subagents.test.js.map +1 -0
  289. package/dist/hooks/useAddContextFlow.d.ts +29 -0
  290. package/dist/hooks/useAddContextFlow.d.ts.map +1 -0
  291. package/dist/hooks/useAddContextFlow.js +83 -0
  292. package/dist/hooks/useAddContextFlow.js.map +1 -0
  293. package/dist/hooks/useArchiveBrowser.d.ts +37 -0
  294. package/dist/hooks/useArchiveBrowser.d.ts.map +1 -0
  295. package/dist/hooks/useArchiveBrowser.js +175 -0
  296. package/dist/hooks/useArchiveBrowser.js.map +1 -0
  297. package/dist/hooks/useClaudeToken.d.ts +29 -0
  298. package/dist/hooks/useClaudeToken.d.ts.map +1 -0
  299. package/dist/hooks/useClaudeToken.js +166 -0
  300. package/dist/hooks/useClaudeToken.js.map +1 -0
  301. package/dist/hooks/useGoogleDocsBrowser.d.ts +29 -0
  302. package/dist/hooks/useGoogleDocsBrowser.d.ts.map +1 -0
  303. package/dist/hooks/useGoogleDocsBrowser.js +146 -0
  304. package/dist/hooks/useGoogleDocsBrowser.js.map +1 -0
  305. package/dist/hooks/useHandoffBrowser.d.ts +27 -0
  306. package/dist/hooks/useHandoffBrowser.d.ts.map +1 -0
  307. package/dist/hooks/useHandoffBrowser.js +92 -0
  308. package/dist/hooks/useHandoffBrowser.js.map +1 -0
  309. package/dist/hooks/useJacquesClient.d.ts +69 -0
  310. package/dist/hooks/useJacquesClient.d.ts.map +1 -0
  311. package/dist/hooks/useJacquesClient.js +248 -0
  312. package/dist/hooks/useJacquesClient.js.map +1 -0
  313. package/dist/hooks/useLlmWorking.d.ts +30 -0
  314. package/dist/hooks/useLlmWorking.d.ts.map +1 -0
  315. package/dist/hooks/useLlmWorking.js +127 -0
  316. package/dist/hooks/useLlmWorking.js.map +1 -0
  317. package/dist/hooks/useLoadContext.d.ts +27 -0
  318. package/dist/hooks/useLoadContext.d.ts.map +1 -0
  319. package/dist/hooks/useLoadContext.js +102 -0
  320. package/dist/hooks/useLoadContext.js.map +1 -0
  321. package/dist/hooks/useNotification.d.ts +36 -0
  322. package/dist/hooks/useNotification.d.ts.map +1 -0
  323. package/dist/hooks/useNotification.js +89 -0
  324. package/dist/hooks/useNotification.js.map +1 -0
  325. package/dist/hooks/useNotification.test.d.ts +9 -0
  326. package/dist/hooks/useNotification.test.d.ts.map +1 -0
  327. package/dist/hooks/useNotification.test.js +144 -0
  328. package/dist/hooks/useNotification.test.js.map +1 -0
  329. package/dist/hooks/useNotionBrowser.d.ts +30 -0
  330. package/dist/hooks/useNotionBrowser.d.ts.map +1 -0
  331. package/dist/hooks/useNotionBrowser.js +154 -0
  332. package/dist/hooks/useNotionBrowser.js.map +1 -0
  333. package/dist/hooks/useObsidianBrowser.d.ts +42 -0
  334. package/dist/hooks/useObsidianBrowser.d.ts.map +1 -0
  335. package/dist/hooks/useObsidianBrowser.js +63 -0
  336. package/dist/hooks/useObsidianBrowser.js.map +1 -0
  337. package/dist/hooks/useObsidianConfig.d.ts +28 -0
  338. package/dist/hooks/useObsidianConfig.d.ts.map +1 -0
  339. package/dist/hooks/useObsidianConfig.js +119 -0
  340. package/dist/hooks/useObsidianConfig.js.map +1 -0
  341. package/dist/hooks/useObsidianFileBrowser.d.ts +29 -0
  342. package/dist/hooks/useObsidianFileBrowser.d.ts.map +1 -0
  343. package/dist/hooks/useObsidianFileBrowser.js +114 -0
  344. package/dist/hooks/useObsidianFileBrowser.js.map +1 -0
  345. package/dist/hooks/useProjectDashboard.d.ts +38 -0
  346. package/dist/hooks/useProjectDashboard.d.ts.map +1 -0
  347. package/dist/hooks/useProjectDashboard.js +224 -0
  348. package/dist/hooks/useProjectDashboard.js.map +1 -0
  349. package/dist/hooks/useProjectSelector.d.ts +17 -0
  350. package/dist/hooks/useProjectSelector.d.ts.map +1 -0
  351. package/dist/hooks/useProjectSelector.js +43 -0
  352. package/dist/hooks/useProjectSelector.js.map +1 -0
  353. package/dist/hooks/useProjectSelector.test.d.ts +7 -0
  354. package/dist/hooks/useProjectSelector.test.d.ts.map +1 -0
  355. package/dist/hooks/useProjectSelector.test.js +117 -0
  356. package/dist/hooks/useProjectSelector.test.js.map +1 -0
  357. package/dist/hooks/useSaveFlow.d.ts +30 -0
  358. package/dist/hooks/useSaveFlow.d.ts.map +1 -0
  359. package/dist/hooks/useSaveFlow.js +195 -0
  360. package/dist/hooks/useSaveFlow.js.map +1 -0
  361. package/dist/hooks/useSessions.test.d.ts +7 -0
  362. package/dist/hooks/useSessions.test.d.ts.map +1 -0
  363. package/dist/hooks/useSessions.test.js +153 -0
  364. package/dist/hooks/useSessions.test.js.map +1 -0
  365. package/dist/hooks/useSessionsExperiment.d.ts +97 -0
  366. package/dist/hooks/useSessionsExperiment.d.ts.map +1 -0
  367. package/dist/hooks/useSessionsExperiment.js +321 -0
  368. package/dist/hooks/useSessionsExperiment.js.map +1 -0
  369. package/dist/hooks/useSettings.d.ts +47 -0
  370. package/dist/hooks/useSettings.d.ts.map +1 -0
  371. package/dist/hooks/useSettings.js +245 -0
  372. package/dist/hooks/useSettings.js.map +1 -0
  373. package/dist/hooks/useSetupWizard.d.ts +35 -0
  374. package/dist/hooks/useSetupWizard.d.ts.map +1 -0
  375. package/dist/hooks/useSetupWizard.js +438 -0
  376. package/dist/hooks/useSetupWizard.js.map +1 -0
  377. package/dist/hooks/useUsageLimits.d.ts +24 -0
  378. package/dist/hooks/useUsageLimits.d.ts.map +1 -0
  379. package/dist/hooks/useUsageLimits.js +58 -0
  380. package/dist/hooks/useUsageLimits.js.map +1 -0
  381. package/dist/hooks/useUsageLimits.test.d.ts +7 -0
  382. package/dist/hooks/useUsageLimits.test.d.ts.map +1 -0
  383. package/dist/hooks/useUsageLimits.test.js +136 -0
  384. package/dist/hooks/useUsageLimits.test.js.map +1 -0
  385. package/dist/hooks/useWorktrees.d.ts +28 -0
  386. package/dist/hooks/useWorktrees.d.ts.map +1 -0
  387. package/dist/hooks/useWorktrees.js +57 -0
  388. package/dist/hooks/useWorktrees.js.map +1 -0
  389. package/dist/hooks/useWorktrees.test.d.ts +7 -0
  390. package/dist/hooks/useWorktrees.test.d.ts.map +1 -0
  391. package/dist/hooks/useWorktrees.test.js +119 -0
  392. package/dist/hooks/useWorktrees.test.js.map +1 -0
  393. package/dist/templates/compact-prompt.d.ts +16 -0
  394. package/dist/templates/compact-prompt.d.ts.map +1 -0
  395. package/dist/templates/compact-prompt.js +47 -0
  396. package/dist/templates/compact-prompt.js.map +1 -0
  397. package/dist/templates/context-skill.d.ts +8 -0
  398. package/dist/templates/context-skill.d.ts.map +1 -0
  399. package/dist/templates/context-skill.js +42 -0
  400. package/dist/templates/context-skill.js.map +1 -0
  401. package/dist/types.d.ts +127 -0
  402. package/dist/types.d.ts.map +1 -0
  403. package/dist/types.js +8 -0
  404. package/dist/types.js.map +1 -0
  405. package/dist/utils/activity.d.ts +17 -0
  406. package/dist/utils/activity.d.ts.map +1 -0
  407. package/dist/utils/activity.js +78 -0
  408. package/dist/utils/activity.js.map +1 -0
  409. package/dist/utils/bottom-controls.d.ts +24 -0
  410. package/dist/utils/bottom-controls.d.ts.map +1 -0
  411. package/dist/utils/bottom-controls.js +29 -0
  412. package/dist/utils/bottom-controls.js.map +1 -0
  413. package/dist/utils/clipboard.d.ts +2 -0
  414. package/dist/utils/clipboard.d.ts.map +1 -0
  415. package/dist/utils/clipboard.js +20 -0
  416. package/dist/utils/clipboard.js.map +1 -0
  417. package/dist/utils/constants.d.ts +6 -0
  418. package/dist/utils/constants.d.ts.map +1 -0
  419. package/dist/utils/constants.js +6 -0
  420. package/dist/utils/constants.js.map +1 -0
  421. package/dist/utils/format.d.ts +17 -0
  422. package/dist/utils/format.d.ts.map +1 -0
  423. package/dist/utils/format.js +31 -0
  424. package/dist/utils/format.js.map +1 -0
  425. package/dist/utils/session-mode.d.ts +14 -0
  426. package/dist/utils/session-mode.d.ts.map +1 -0
  427. package/dist/utils/session-mode.js +29 -0
  428. package/dist/utils/session-mode.js.map +1 -0
  429. package/dist/utils/session-mode.test.d.ts +7 -0
  430. package/dist/utils/session-mode.test.d.ts.map +1 -0
  431. package/dist/utils/session-mode.test.js +102 -0
  432. package/dist/utils/session-mode.test.js.map +1 -0
  433. package/dist/utils/sessions-items-builder.d.ts +29 -0
  434. package/dist/utils/sessions-items-builder.d.ts.map +1 -0
  435. package/dist/utils/sessions-items-builder.js +394 -0
  436. package/dist/utils/sessions-items-builder.js.map +1 -0
  437. package/dist/utils/settings.d.ts +100 -0
  438. package/dist/utils/settings.d.ts.map +1 -0
  439. package/dist/utils/settings.js +206 -0
  440. package/dist/utils/settings.js.map +1 -0
  441. package/dist/websocket-client.d.ts +70 -0
  442. package/dist/websocket-client.d.ts.map +1 -0
  443. package/dist/websocket-client.js +188 -0
  444. package/dist/websocket-client.js.map +1 -0
  445. package/hooks/adapters/__init__.py +17 -0
  446. package/hooks/adapters/__pycache__/__init__.cpython-311.pyc +0 -0
  447. package/hooks/adapters/__pycache__/__init__.cpython-313.pyc +0 -0
  448. package/hooks/adapters/__pycache__/__init__.cpython-314.pyc +0 -0
  449. package/hooks/adapters/__pycache__/base.cpython-311.pyc +0 -0
  450. package/hooks/adapters/__pycache__/base.cpython-313.pyc +0 -0
  451. package/hooks/adapters/__pycache__/base.cpython-314.pyc +0 -0
  452. package/hooks/adapters/__pycache__/calibration.cpython-311.pyc +0 -0
  453. package/hooks/adapters/__pycache__/calibration.cpython-313.pyc +0 -0
  454. package/hooks/adapters/__pycache__/calibration.cpython-314.pyc +0 -0
  455. package/hooks/adapters/__pycache__/claude_code.cpython-311.pyc +0 -0
  456. package/hooks/adapters/__pycache__/claude_code.cpython-313.pyc +0 -0
  457. package/hooks/adapters/__pycache__/claude_code.cpython-314.pyc +0 -0
  458. package/hooks/adapters/__pycache__/cursor.cpython-311.pyc +0 -0
  459. package/hooks/adapters/__pycache__/cursor.cpython-313.pyc +0 -0
  460. package/hooks/adapters/__pycache__/cursor.cpython-314.pyc +0 -0
  461. package/hooks/adapters/__pycache__/skills.cpython-311.pyc +0 -0
  462. package/hooks/adapters/__pycache__/skills.cpython-313.pyc +0 -0
  463. package/hooks/adapters/__pycache__/skills.cpython-314.pyc +0 -0
  464. package/hooks/adapters/__pycache__/test_adapters.cpython-311-pytest-9.0.2.pyc +0 -0
  465. package/hooks/adapters/__pycache__/test_adapters.cpython-313.pyc +0 -0
  466. package/hooks/adapters/__pycache__/test_calibration.cpython-311-pytest-9.0.2.pyc +0 -0
  467. package/hooks/adapters/__pycache__/test_skills.cpython-311-pytest-9.0.2.pyc +0 -0
  468. package/hooks/adapters/__pycache__/test_tokenizer.cpython-311-pytest-9.0.2.pyc +0 -0
  469. package/hooks/adapters/__pycache__/tokenizer.cpython-311.pyc +0 -0
  470. package/hooks/adapters/__pycache__/tokenizer.cpython-313.pyc +0 -0
  471. package/hooks/adapters/__pycache__/tokenizer.cpython-314.pyc +0 -0
  472. package/hooks/adapters/base.py +459 -0
  473. package/hooks/adapters/calibration.py +236 -0
  474. package/hooks/adapters/claude_code.py +316 -0
  475. package/hooks/adapters/context_parser.py +288 -0
  476. package/hooks/adapters/cursor.py +258 -0
  477. package/hooks/adapters/skills.py +160 -0
  478. package/hooks/adapters/template.py +281 -0
  479. package/hooks/adapters/test_adapters.py +516 -0
  480. package/hooks/adapters/test_calibration.py +235 -0
  481. package/hooks/adapters/test_skills.py +162 -0
  482. package/hooks/adapters/test_tokenizer.py +158 -0
  483. package/hooks/adapters/tokenizer.py +192 -0
  484. package/hooks/claude-code/pre-tool-use.py +36 -0
  485. package/hooks/claude-code/register-session.py +35 -0
  486. package/hooks/claude-code/report-activity.py +32 -0
  487. package/hooks/claude-code/session-idle.py +32 -0
  488. package/hooks/claude-code/statusline.sh +91 -0
  489. package/hooks/claude-code/unregister-session.py +32 -0
  490. package/hooks/cursor/README.md +61 -0
  491. package/hooks/cursor/after-agent-response.py +97 -0
  492. package/hooks/cursor/hooks.json.template +30 -0
  493. package/hooks/cursor/post-tool-use.py +32 -0
  494. package/hooks/cursor/pre-compact.py +61 -0
  495. package/hooks/cursor/session-end.py +32 -0
  496. package/hooks/cursor/session-start.py +66 -0
  497. package/hooks/git-detect.sh +47 -0
  498. package/hooks/install.py +441 -0
  499. package/hooks/jacques-register-session.py +286 -0
  500. package/hooks/jacques-report-activity.py +145 -0
  501. package/hooks/jacques-session-idle.py +57 -0
  502. package/hooks/jacques-unregister-session.py +57 -0
  503. package/hooks/requirements.txt +9 -0
  504. package/hooks/statusline.py +457 -0
  505. package/hooks/statusline.sh +212 -0
  506. package/package.json +63 -0
  507. package/skills/jacques-continue/SKILL.md +87 -0
  508. package/skills/jacques-handoff/SKILL.md +147 -0
@@ -0,0 +1,459 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ base.py - Base Adapter for Jacques Source Integration
4
+
5
+ Provides shared functionality for all source adapters:
6
+ - Unix socket communication with Jacques server
7
+ - JSON input parsing with error handling
8
+ - Common payload building
9
+ - Session title extraction from transcripts
10
+ - Fallback file writing when server unavailable
11
+
12
+ All source-specific adapters (ClaudeCodeAdapter, CursorAdapter, etc.)
13
+ should extend this base class.
14
+ """
15
+ import json
16
+ import sys
17
+ import os
18
+ import socket
19
+ import time
20
+ import platform
21
+ import tempfile
22
+ from pathlib import Path
23
+ from typing import Optional, Any
24
+ from abc import ABC, abstractmethod
25
+
26
+
27
+ class BaseAdapter(ABC):
28
+ """
29
+ Abstract base class for Jacques source adapters.
30
+
31
+ Provides common functionality for:
32
+ - Sending events to the Jacques server via Unix socket
33
+ - Parsing JSON input from stdin
34
+ - Building normalized event payloads
35
+ - Extracting session titles from transcripts
36
+
37
+ Subclasses must implement:
38
+ - source: str property identifying the source (e.g., 'claude_code', 'cursor')
39
+ - get_session_id(input_data): Extract session ID from tool-specific input
40
+ - get_project_path(input_data): Extract project path from tool-specific input
41
+ """
42
+
43
+ DEFAULT_SOCKET_PATH = (
44
+ r'\\.\pipe\jacques' if platform.system() == 'Windows'
45
+ else os.environ.get('JACQUES_SOCKET_PATH', '/tmp/jacques.sock')
46
+ )
47
+ DEFAULT_TIMEOUT = 1.0
48
+ FALLBACK_PATH = Path.home() / '.jacques' / 'pending-events.jsonl'
49
+
50
+ @property
51
+ @abstractmethod
52
+ def source(self) -> str:
53
+ """Unique identifier for this source (e.g., 'claude_code', 'cursor')."""
54
+ pass
55
+
56
+ @abstractmethod
57
+ def get_session_id(self, input_data: dict) -> Optional[str]:
58
+ """Extract session ID from tool-specific input format."""
59
+ pass
60
+
61
+ @abstractmethod
62
+ def get_project_path(self, input_data: dict) -> Optional[str]:
63
+ """Extract project path from tool-specific input format."""
64
+ pass
65
+
66
+ # =========================================================================
67
+ # Input Parsing
68
+ # =========================================================================
69
+
70
+ def parse_input(self) -> Optional[dict]:
71
+ """
72
+ Parse JSON input from stdin with error handling.
73
+
74
+ Returns:
75
+ Parsed dict if successful, None if parsing fails.
76
+ """
77
+ try:
78
+ return json.load(sys.stdin)
79
+ except json.JSONDecodeError as e:
80
+ self._log_error(f"Invalid JSON input: {e}")
81
+ return None
82
+ except Exception as e:
83
+ self._log_error(f"Error reading input: {e}")
84
+ return None
85
+
86
+ def validate_session_id(self, input_data: dict) -> Optional[str]:
87
+ """
88
+ Extract and validate session ID from input data.
89
+
90
+ Returns:
91
+ Session ID string if valid, None otherwise.
92
+ """
93
+ session_id = self.get_session_id(input_data)
94
+ if not session_id:
95
+ self._log_error("No session_id in input")
96
+ return None
97
+ return session_id
98
+
99
+ # =========================================================================
100
+ # Server Communication
101
+ # =========================================================================
102
+
103
+ def send_to_server(
104
+ self,
105
+ payload: dict,
106
+ socket_path: str = None,
107
+ timeout: float = None
108
+ ) -> bool:
109
+ """
110
+ Send payload to Jacques server via IPC.
111
+
112
+ Uses Unix socket on macOS/Linux, Named Pipe on Windows.
113
+
114
+ Args:
115
+ payload: Dict to send as JSON
116
+ socket_path: Path to IPC socket (default: platform-specific)
117
+ timeout: Socket timeout in seconds (default: 1.0)
118
+
119
+ Returns:
120
+ True if sent successfully, False otherwise.
121
+ """
122
+ socket_path = socket_path or self.DEFAULT_SOCKET_PATH
123
+ timeout = timeout or self.DEFAULT_TIMEOUT
124
+ data = json.dumps(payload).encode() + b'\n'
125
+
126
+ try:
127
+ if platform.system() == 'Windows':
128
+ # Windows: write directly to Named Pipe as a file
129
+ with open(socket_path, 'wb') as pipe:
130
+ pipe.write(data)
131
+ else:
132
+ # Unix: use AF_UNIX socket
133
+ sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
134
+ sock.settimeout(timeout)
135
+ sock.connect(socket_path)
136
+ sock.sendall(data)
137
+ sock.close()
138
+ return True
139
+ except Exception as e:
140
+ self._log_error(f"Failed to send to server: {e}")
141
+ return False
142
+
143
+ def write_fallback(self, payload: dict) -> bool:
144
+ """
145
+ Write payload to fallback file when server is unavailable.
146
+
147
+ The fallback file stores events as newline-delimited JSON (JSONL).
148
+ These can be replayed when the server comes back online.
149
+
150
+ Args:
151
+ payload: Dict to write as JSON
152
+
153
+ Returns:
154
+ True if written successfully, False otherwise.
155
+ """
156
+ try:
157
+ self.FALLBACK_PATH.parent.mkdir(parents=True, exist_ok=True)
158
+ with open(self.FALLBACK_PATH, 'a') as f:
159
+ f.write(json.dumps(payload) + '\n')
160
+ return True
161
+ except Exception as e:
162
+ self._log_error(f"Failed to write fallback: {e}")
163
+ return False
164
+
165
+ def send_event(self, payload: dict, use_fallback: bool = True) -> bool:
166
+ """
167
+ Send event to server with optional fallback.
168
+
169
+ Args:
170
+ payload: Event payload to send
171
+ use_fallback: If True, write to fallback file on failure
172
+
173
+ Returns:
174
+ True if sent or written to fallback successfully.
175
+ """
176
+ if self.send_to_server(payload):
177
+ return True
178
+ if use_fallback:
179
+ return self.write_fallback(payload)
180
+ return False
181
+
182
+ # =========================================================================
183
+ # Payload Building
184
+ # =========================================================================
185
+
186
+ def build_base_payload(
187
+ self,
188
+ event: str,
189
+ session_id: str,
190
+ **extra_fields
191
+ ) -> dict:
192
+ """
193
+ Build base payload structure for any event type.
194
+
195
+ Args:
196
+ event: Event type (session_start, activity, idle, session_end, context_update)
197
+ session_id: Session identifier
198
+ **extra_fields: Additional fields to include
199
+
200
+ Returns:
201
+ Dict with base event structure plus extra fields.
202
+ """
203
+ payload = {
204
+ "event": event,
205
+ "timestamp": int(time.time() * 1000), # Convert to milliseconds
206
+ "session_id": session_id,
207
+ "source": self.source,
208
+ }
209
+ payload.update(extra_fields)
210
+ return payload
211
+
212
+ # =========================================================================
213
+ # Project Info Extraction
214
+ # =========================================================================
215
+
216
+ def extract_project_info(self, input_data: dict) -> dict:
217
+ """
218
+ Extract project name and path from input data.
219
+
220
+ Returns dict with:
221
+ - project: Project name (basename of path)
222
+ - project_path: Full path to project
223
+ - cwd: Current working directory
224
+ """
225
+ project_path = self.get_project_path(input_data) or ''
226
+ cwd = input_data.get('cwd', os.getcwd())
227
+
228
+ if project_path:
229
+ project_name = os.path.basename(project_path)
230
+ elif cwd:
231
+ project_name = os.path.basename(cwd)
232
+ else:
233
+ project_name = 'Unknown'
234
+
235
+ return {
236
+ 'project': project_name,
237
+ 'project_path': project_path,
238
+ 'cwd': cwd,
239
+ }
240
+
241
+ # =========================================================================
242
+ # Session Title Extraction
243
+ # =========================================================================
244
+
245
+ def extract_session_title(self, transcript_path: Optional[str]) -> Optional[str]:
246
+ """
247
+ Extract session title from Claude Code transcript file.
248
+
249
+ Checks for (in priority order):
250
+ 1. Explicit 'title' field
251
+ 2. 'summary' type entries
252
+ 3. First user message (truncated to 80 chars)
253
+
254
+ Args:
255
+ transcript_path: Path to the transcript JSONL file
256
+
257
+ Returns:
258
+ Extracted title string, or None if not found.
259
+ """
260
+ if not transcript_path:
261
+ return None
262
+
263
+ path = Path(transcript_path)
264
+ if not path.exists():
265
+ return None
266
+
267
+ title = None
268
+ first_user_message = None
269
+ summary_text = None
270
+
271
+ try:
272
+ with open(path, 'r') as f:
273
+ lines = f.readlines()
274
+
275
+ # Check recent lines first for updated title/summary
276
+ recent_lines = lines[-100:] if len(lines) > 100 else lines
277
+
278
+ for line in recent_lines:
279
+ try:
280
+ entry = json.loads(line.strip())
281
+
282
+ # Check for explicit title
283
+ if 'title' in entry:
284
+ title = entry['title']
285
+
286
+ # Check for summary type
287
+ if entry.get('type') == 'summary':
288
+ summary_content = entry.get('summary', '')
289
+ if summary_content:
290
+ summary_text = summary_content.split('.')[0][:200]
291
+
292
+ except json.JSONDecodeError:
293
+ continue
294
+
295
+ # Check first user message if no title found
296
+ if not title and not summary_text:
297
+ for line in lines[:20]:
298
+ try:
299
+ entry = json.loads(line.strip())
300
+ if entry.get('type') == 'human':
301
+ msg = entry.get('message', {})
302
+ content = msg.get('content', '') if isinstance(msg, dict) else ''
303
+ if isinstance(content, str) and content:
304
+ first_user_message = content.strip()[:200]
305
+ if len(content) > 200:
306
+ first_user_message += '...'
307
+ break
308
+ except:
309
+ continue
310
+
311
+ except Exception as e:
312
+ self._log_error(f"Error reading transcript: {e}")
313
+
314
+ return title or summary_text or first_user_message
315
+
316
+ def generate_fallback_title(self, project_name: str) -> str:
317
+ """Generate a fallback title when transcript is empty/unavailable."""
318
+ return f"Session in {project_name}"
319
+
320
+ # =========================================================================
321
+ # Terminal Identity
322
+ # =========================================================================
323
+
324
+ def get_terminal_identity(self) -> dict:
325
+ """
326
+ Get terminal-specific identifiers from environment and system.
327
+
328
+ Returns dict with terminal identification info for session tracking.
329
+ Cross-platform: TTY detection skipped on Windows (no TTY concept).
330
+ """
331
+ tty = None
332
+
333
+ # Try to get TTY (Unix only — Windows has no TTY concept)
334
+ if platform.system() != 'Windows':
335
+ try:
336
+ if sys.stdin.isatty():
337
+ tty = os.ttyname(sys.stdin.fileno())
338
+ except:
339
+ pass
340
+
341
+ if not tty:
342
+ try:
343
+ result = os.popen("tty 2>/dev/null").read().strip()
344
+ if result and result != "not a tty":
345
+ tty = result
346
+ except:
347
+ pass
348
+
349
+ return {
350
+ "tty": tty,
351
+ "terminal_pid": os.getppid(),
352
+ "term_program": os.environ.get("TERM_PROGRAM"),
353
+ "iterm_session_id": os.environ.get("ITERM_SESSION_ID"),
354
+ "term_session_id": os.environ.get("TERM_SESSION_ID"),
355
+ "kitty_window_id": os.environ.get("KITTY_WINDOW_ID"),
356
+ "wezterm_pane": os.environ.get("WEZTERM_PANE"),
357
+ "wt_session": os.environ.get("WT_SESSION"),
358
+ "vscode_injection": os.environ.get("VSCODE_INJECTION"),
359
+ "windowid": os.environ.get("WINDOWID"),
360
+ "term": os.environ.get("TERM"),
361
+ }
362
+
363
+ def build_terminal_key(self, terminal: dict) -> str:
364
+ """
365
+ Build a unique key for this terminal instance.
366
+
367
+ Priority: iTerm > Kitty > WezTerm > Windows Terminal > TTY > PID
368
+ """
369
+ if terminal.get("iterm_session_id"):
370
+ return f"ITERM:{terminal['iterm_session_id']}"
371
+ if terminal.get("kitty_window_id"):
372
+ return f"KITTY:{terminal['kitty_window_id']}"
373
+ if terminal.get("wezterm_pane"):
374
+ return f"WEZTERM:{terminal['wezterm_pane']}"
375
+ if terminal.get("wt_session"):
376
+ return f"WT:{terminal['wt_session']}"
377
+ if terminal.get("tty"):
378
+ return f"TTY:{terminal['tty']}"
379
+ if terminal.get("terminal_pid"):
380
+ return f"PID:{terminal['terminal_pid']}"
381
+ return f"UNKNOWN:{time.time()}"
382
+
383
+ # =========================================================================
384
+ # Git Detection
385
+ # =========================================================================
386
+
387
+ def detect_git_info(self, project_path: str) -> dict:
388
+ """
389
+ Detect git branch, worktree, and repo root from project directory.
390
+
391
+ Uses hooks/git-detect.sh as single source of truth.
392
+ Falls back to inline detection if script not found.
393
+
394
+ Returns dict with:
395
+ - git_branch: Current branch name (empty string if not a git repo)
396
+ - git_worktree: Worktree name if in a worktree (empty string otherwise)
397
+ - git_repo_root: Main worktree root path (empty string if not a git repo)
398
+ """
399
+ result = {'git_branch': '', 'git_worktree': '', 'git_repo_root': ''}
400
+ if not project_path or not os.path.isdir(project_path):
401
+ return result
402
+ try:
403
+ import subprocess
404
+ # Try git-detect.sh first (single source of truth)
405
+ script_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
406
+ script_path = os.path.join(script_dir, 'git-detect.sh')
407
+ if os.path.isfile(script_path) and os.access(script_path, os.X_OK):
408
+ proc = subprocess.run(
409
+ [script_path, project_path],
410
+ capture_output=True, text=True, timeout=5
411
+ )
412
+ if proc.returncode == 0:
413
+ lines = proc.stdout.split('\n')
414
+ result['git_branch'] = lines[0] if len(lines) > 0 else ''
415
+ result['git_worktree'] = lines[1] if len(lines) > 1 else ''
416
+ result['git_repo_root'] = lines[2] if len(lines) > 2 else ''
417
+ return result
418
+
419
+ # Fallback: inline detection (same algorithm as git-detect.sh)
420
+ proc = subprocess.run(
421
+ ['git', '-C', project_path, 'rev-parse', '--abbrev-ref', 'HEAD', '--git-common-dir'],
422
+ capture_output=True, text=True, timeout=5
423
+ )
424
+ if proc.returncode == 0:
425
+ lines = proc.stdout.strip().split('\n')
426
+ if len(lines) >= 2:
427
+ result['git_branch'] = lines[0]
428
+ common = lines[1]
429
+ if common == '.git':
430
+ result['git_repo_root'] = os.path.realpath(project_path)
431
+ elif common:
432
+ result['git_repo_root'] = os.path.dirname(common)
433
+ result['git_worktree'] = os.path.basename(project_path)
434
+ except Exception:
435
+ pass
436
+ return result
437
+
438
+ # =========================================================================
439
+ # Debug Logging
440
+ # =========================================================================
441
+
442
+ def log_debug(self, input_data: dict, hook_name: str) -> None:
443
+ """
444
+ Log input data for debugging purposes.
445
+
446
+ Writes to {tempdir}/jacques-hook-debug.log
447
+ """
448
+ try:
449
+ log_path = os.path.join(tempfile.gettempdir(), 'jacques-hook-debug.log')
450
+ with open(log_path, 'a') as f:
451
+ f.write(f"\n=== {hook_name} [{self.source}] {time.strftime('%Y-%m-%d %H:%M:%S')} ===\n")
452
+ f.write(json.dumps(input_data, indent=2))
453
+ f.write("\n")
454
+ except:
455
+ pass
456
+
457
+ def _log_error(self, message: str) -> None:
458
+ """Log error message to stderr."""
459
+ print(f"[jacques:{self.source}] {message}", file=sys.stderr)
@@ -0,0 +1,236 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ calibration.py - Token Estimation Calibration for Jacques
4
+
5
+ Stores and retrieves calibration factors to improve token estimation accuracy.
6
+ When preCompact provides actual token counts, we calculate a correction factor
7
+ that can be applied to future estimates for that session.
8
+
9
+ Calibration data is stored in ~/.jacques/calibration.json
10
+
11
+ Example calibration flow:
12
+ 1. afterAgentResponse estimates 50,000 tokens
13
+ 2. User runs /summarize, preCompact reports actual 55,000 tokens
14
+ 3. Correction factor = 55000 / 50000 = 1.1
15
+ 4. Future estimates are multiplied by 1.1 for better accuracy
16
+ """
17
+ import json
18
+ import sys
19
+ import time
20
+ from pathlib import Path
21
+ from typing import Optional
22
+
23
+ # Calibration data file path
24
+ CALIBRATION_PATH = Path.home() / '.jacques' / 'calibration.json'
25
+
26
+ # In-memory cache for current session
27
+ _calibration_cache: dict = None
28
+ _cache_loaded = False
29
+
30
+
31
+ def _load_calibration() -> dict:
32
+ """Load calibration data from file."""
33
+ global _calibration_cache, _cache_loaded
34
+
35
+ if _cache_loaded and _calibration_cache is not None:
36
+ return _calibration_cache
37
+
38
+ _calibration_cache = {
39
+ "sessions": {}, # session_id -> {factor, last_estimate, last_actual, updated_at}
40
+ "global_factor": 1.0, # Weighted average across all sessions
41
+ }
42
+
43
+ try:
44
+ if CALIBRATION_PATH.exists():
45
+ with open(CALIBRATION_PATH, 'r') as f:
46
+ data = json.load(f)
47
+ _calibration_cache.update(data)
48
+ except Exception as e:
49
+ print(f"[jacques:calibration] Error loading calibration: {e}", file=sys.stderr)
50
+
51
+ _cache_loaded = True
52
+ return _calibration_cache
53
+
54
+
55
+ def _save_calibration() -> bool:
56
+ """Save calibration data to file."""
57
+ try:
58
+ CALIBRATION_PATH.parent.mkdir(parents=True, exist_ok=True)
59
+ with open(CALIBRATION_PATH, 'w') as f:
60
+ json.dump(_calibration_cache, f, indent=2)
61
+ return True
62
+ except Exception as e:
63
+ print(f"[jacques:calibration] Error saving calibration: {e}", file=sys.stderr)
64
+ return False
65
+
66
+
67
+ def get_factor(session_id: str) -> float:
68
+ """
69
+ Get correction factor for a session.
70
+
71
+ Only returns session-specific factors, NOT global factors.
72
+ Global factors from old sessions with different estimation logic
73
+ can cause wildly inaccurate results.
74
+
75
+ Args:
76
+ session_id: Session identifier
77
+
78
+ Returns:
79
+ Correction factor (default 1.0 if no calibration for this session).
80
+ """
81
+ data = _load_calibration()
82
+
83
+ # Only use session-specific factor
84
+ # Don't use global_factor - it may be from old sessions with wrong estimates
85
+ session_data = data.get("sessions", {}).get(session_id)
86
+ if session_data and "factor" in session_data:
87
+ return session_data["factor"]
88
+
89
+ # Default to 1.0 for new sessions - no adjustment until calibrated
90
+ return 1.0
91
+
92
+
93
+ def set_factor(session_id: str, factor: float) -> None:
94
+ """
95
+ Set correction factor for a session.
96
+
97
+ Also updates the global factor as a weighted average.
98
+
99
+ Args:
100
+ session_id: Session identifier
101
+ factor: Correction factor (actual / estimated)
102
+ """
103
+ data = _load_calibration()
104
+
105
+ if "sessions" not in data:
106
+ data["sessions"] = {}
107
+
108
+ # Clamp factor to reasonable range (0.5 to 2.0)
109
+ factor = max(0.5, min(2.0, factor))
110
+
111
+ data["sessions"][session_id] = {
112
+ "factor": factor,
113
+ "updated_at": time.time(),
114
+ }
115
+
116
+ # Update global factor as average of recent sessions
117
+ recent_factors = [
118
+ s.get("factor", 1.0)
119
+ for s in data["sessions"].values()
120
+ if s.get("updated_at", 0) > time.time() - 86400 # Last 24 hours
121
+ ]
122
+
123
+ if recent_factors:
124
+ data["global_factor"] = sum(recent_factors) / len(recent_factors)
125
+
126
+ _save_calibration()
127
+
128
+
129
+ def get_last_estimate(session_id: str) -> Optional[int]:
130
+ """
131
+ Get the last token estimate for a session.
132
+
133
+ Used when preCompact fires to calculate correction factor.
134
+
135
+ Args:
136
+ session_id: Session identifier
137
+
138
+ Returns:
139
+ Last estimated token count, or None if not available.
140
+ """
141
+ data = _load_calibration()
142
+ session_data = data.get("sessions", {}).get(session_id)
143
+
144
+ if session_data:
145
+ return session_data.get("last_estimate")
146
+
147
+ return None
148
+
149
+
150
+ def set_last_estimate(session_id: str, tokens: int) -> None:
151
+ """
152
+ Store the latest token estimate for a session.
153
+
154
+ Called after each afterAgentResponse to track estimates.
155
+
156
+ Args:
157
+ session_id: Session identifier
158
+ tokens: Estimated token count
159
+ """
160
+ data = _load_calibration()
161
+
162
+ if "sessions" not in data:
163
+ data["sessions"] = {}
164
+
165
+ if session_id not in data["sessions"]:
166
+ data["sessions"][session_id] = {}
167
+
168
+ data["sessions"][session_id]["last_estimate"] = tokens
169
+ data["sessions"][session_id]["estimate_time"] = time.time()
170
+
171
+ _save_calibration()
172
+
173
+
174
+ def calibrate_from_actual(session_id: str, actual_tokens: int) -> Optional[float]:
175
+ """
176
+ Calculate and store correction factor from actual token count.
177
+
178
+ Called when preCompact provides actual metrics.
179
+
180
+ Args:
181
+ session_id: Session identifier
182
+ actual_tokens: Actual token count from preCompact
183
+
184
+ Returns:
185
+ Calculated correction factor, or None if no estimate available.
186
+ """
187
+ estimated = get_last_estimate(session_id)
188
+
189
+ if not estimated or estimated <= 0:
190
+ return None
191
+
192
+ factor = actual_tokens / estimated
193
+ set_factor(session_id, factor)
194
+
195
+ # Store actual for reference
196
+ data = _load_calibration()
197
+ if session_id in data.get("sessions", {}):
198
+ data["sessions"][session_id]["last_actual"] = actual_tokens
199
+ data["sessions"][session_id]["calibrated_at"] = time.time()
200
+ _save_calibration()
201
+
202
+ return factor
203
+
204
+
205
+ def clear_session(session_id: str) -> None:
206
+ """
207
+ Clear calibration data for a session.
208
+
209
+ Called when session ends.
210
+
211
+ Args:
212
+ session_id: Session identifier
213
+ """
214
+ data = _load_calibration()
215
+
216
+ if session_id in data.get("sessions", {}):
217
+ del data["sessions"][session_id]
218
+ _save_calibration()
219
+
220
+
221
+ def get_calibration_stats() -> dict:
222
+ """
223
+ Get calibration statistics for debugging.
224
+
225
+ Returns:
226
+ Dict with session count, global factor, etc.
227
+ """
228
+ data = _load_calibration()
229
+
230
+ sessions = data.get("sessions", {})
231
+
232
+ return {
233
+ "session_count": len(sessions),
234
+ "global_factor": data.get("global_factor", 1.0),
235
+ "calibrated_sessions": sum(1 for s in sessions.values() if "factor" in s),
236
+ }