agent-decoder 0.3.2__tar.gz

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 (327) hide show
  1. agent_decoder-0.3.2/.editorconfig +30 -0
  2. agent_decoder-0.3.2/.github/CODEOWNERS +7 -0
  3. agent_decoder-0.3.2/.github/ISSUE_TEMPLATE/bug_report.yml +20 -0
  4. agent_decoder-0.3.2/.github/ISSUE_TEMPLATE/config.yml +5 -0
  5. agent_decoder-0.3.2/.github/ISSUE_TEMPLATE/feature_request.yml +13 -0
  6. agent_decoder-0.3.2/.github/PULL_REQUEST_TEMPLATE.md +44 -0
  7. agent_decoder-0.3.2/.github/dependabot.yml +31 -0
  8. agent_decoder-0.3.2/.github/workflows/ci.yml +88 -0
  9. agent_decoder-0.3.2/.github/workflows/publish.yml +67 -0
  10. agent_decoder-0.3.2/.gitignore +53 -0
  11. agent_decoder-0.3.2/.pre-commit-config.yaml +27 -0
  12. agent_decoder-0.3.2/ARCHITECTURE.md +166 -0
  13. agent_decoder-0.3.2/CHANGELOG.md +247 -0
  14. agent_decoder-0.3.2/CODE_OF_CONDUCT.md +83 -0
  15. agent_decoder-0.3.2/CONTRIBUTING.md +80 -0
  16. agent_decoder-0.3.2/LICENSE +111 -0
  17. agent_decoder-0.3.2/Makefile +32 -0
  18. agent_decoder-0.3.2/PKG-INFO +387 -0
  19. agent_decoder-0.3.2/README.md +352 -0
  20. agent_decoder-0.3.2/SECURITY.md +49 -0
  21. agent_decoder-0.3.2/clau_decode/__init__.py +22 -0
  22. agent_decoder-0.3.2/docs/demo-poster.jpg +0 -0
  23. agent_decoder-0.3.2/docs/native-input-required-plan.md +135 -0
  24. agent_decoder-0.3.2/frontend/index.html +20 -0
  25. agent_decoder-0.3.2/frontend/package-lock.json +4263 -0
  26. agent_decoder-0.3.2/frontend/package.json +42 -0
  27. agent_decoder-0.3.2/frontend/playwright.config.ts +10 -0
  28. agent_decoder-0.3.2/frontend/public/android-chrome-192x192.png +0 -0
  29. agent_decoder-0.3.2/frontend/public/android-chrome-512x512.png +0 -0
  30. agent_decoder-0.3.2/frontend/public/app-icon.png +0 -0
  31. agent_decoder-0.3.2/frontend/public/apple-touch-icon.png +0 -0
  32. agent_decoder-0.3.2/frontend/public/favicon-16x16.png +0 -0
  33. agent_decoder-0.3.2/frontend/public/favicon-32x32.png +0 -0
  34. agent_decoder-0.3.2/frontend/public/favicon.ico +0 -0
  35. agent_decoder-0.3.2/frontend/public/site.webmanifest +1 -0
  36. agent_decoder-0.3.2/frontend/src/App.tsx +356 -0
  37. agent_decoder-0.3.2/frontend/src/api/__tests__/client.test.ts +265 -0
  38. agent_decoder-0.3.2/frontend/src/api/client.ts +628 -0
  39. agent_decoder-0.3.2/frontend/src/api/sessionCache.ts +50 -0
  40. agent_decoder-0.3.2/frontend/src/api/types.ts +593 -0
  41. agent_decoder-0.3.2/frontend/src/assets/fonts/LICENSES.md +128 -0
  42. agent_decoder-0.3.2/frontend/src/assets/fonts/fira-code.woff2 +0 -0
  43. agent_decoder-0.3.2/frontend/src/assets/fonts/ioskeley-mono.woff2 +0 -0
  44. agent_decoder-0.3.2/frontend/src/assets/fonts/jetbrains-mono.woff2 +0 -0
  45. agent_decoder-0.3.2/frontend/src/assets/fonts/julia-mono.woff2 +0 -0
  46. agent_decoder-0.3.2/frontend/src/assets/fonts/libertinus-mono.woff2 +0 -0
  47. agent_decoder-0.3.2/frontend/src/assets/fonts/monaspace-argon.woff2 +0 -0
  48. agent_decoder-0.3.2/frontend/src/assets/fonts/source-code-pro.woff2 +0 -0
  49. agent_decoder-0.3.2/frontend/src/assets/fonts/spline-sans-mono.woff2 +0 -0
  50. agent_decoder-0.3.2/frontend/src/assets/fonts/xanh-mono.woff2 +0 -0
  51. agent_decoder-0.3.2/frontend/src/components/ChatView/ChatInput.tsx +497 -0
  52. agent_decoder-0.3.2/frontend/src/components/ChatView/ChatInputBanners.tsx +176 -0
  53. agent_decoder-0.3.2/frontend/src/components/ChatView/ChatInputBar.tsx +91 -0
  54. agent_decoder-0.3.2/frontend/src/components/ChatView/ChatView.tsx +819 -0
  55. agent_decoder-0.3.2/frontend/src/components/ChatView/ConversationHeader.tsx +419 -0
  56. agent_decoder-0.3.2/frontend/src/components/ChatView/EmptyState.tsx +98 -0
  57. agent_decoder-0.3.2/frontend/src/components/ChatView/MessageListContainerContext.ts +5 -0
  58. agent_decoder-0.3.2/frontend/src/components/ChatView/MessageListLoader.tsx +9 -0
  59. agent_decoder-0.3.2/frontend/src/components/ChatView/ModelPicker.tsx +358 -0
  60. agent_decoder-0.3.2/frontend/src/components/ChatView/NativeActionBanner.tsx +111 -0
  61. agent_decoder-0.3.2/frontend/src/components/ChatView/NativeTerminalView.tsx +421 -0
  62. agent_decoder-0.3.2/frontend/src/components/ChatView/OwnershipBanner.tsx +132 -0
  63. agent_decoder-0.3.2/frontend/src/components/ChatView/ProviderThemeContext.tsx +70 -0
  64. agent_decoder-0.3.2/frontend/src/components/ChatView/__tests__/ChatInput.test.tsx +311 -0
  65. agent_decoder-0.3.2/frontend/src/components/ChatView/__tests__/ChatViewLifecycle.test.tsx +579 -0
  66. agent_decoder-0.3.2/frontend/src/components/ChatView/__tests__/ConversationHeader.test.tsx +122 -0
  67. agent_decoder-0.3.2/frontend/src/components/ChatView/__tests__/EmptyState.test.tsx +48 -0
  68. agent_decoder-0.3.2/frontend/src/components/ChatView/__tests__/NativeActionBanner.test.tsx +94 -0
  69. agent_decoder-0.3.2/frontend/src/components/ChatView/__tests__/NativeTerminalView.test.tsx +412 -0
  70. agent_decoder-0.3.2/frontend/src/components/ChatView/chatCard.ts +42 -0
  71. agent_decoder-0.3.2/frontend/src/components/ChatView/hooks/__tests__/useRecaps.test.tsx +88 -0
  72. agent_decoder-0.3.2/frontend/src/components/ChatView/hooks/useExpandPreserveAnchor.ts +48 -0
  73. agent_decoder-0.3.2/frontend/src/components/ChatView/hooks/useMessageHistory.ts +78 -0
  74. agent_decoder-0.3.2/frontend/src/components/ChatView/hooks/useNativePty.ts +158 -0
  75. agent_decoder-0.3.2/frontend/src/components/ChatView/hooks/useRecaps.ts +176 -0
  76. agent_decoder-0.3.2/frontend/src/components/ChatView/hooks/useSessionOwnership.ts +64 -0
  77. agent_decoder-0.3.2/frontend/src/components/ChatView/nativeTerminal/xtermTerminal.ts +49 -0
  78. agent_decoder-0.3.2/frontend/src/components/Dashboard/ActivityStrip.tsx +80 -0
  79. agent_decoder-0.3.2/frontend/src/components/Dashboard/Dashboard.tsx +165 -0
  80. agent_decoder-0.3.2/frontend/src/components/Dashboard/DashboardEmpty.tsx +57 -0
  81. agent_decoder-0.3.2/frontend/src/components/Dashboard/DashboardSearch.tsx +288 -0
  82. agent_decoder-0.3.2/frontend/src/components/Dashboard/FeaturedSession.tsx +168 -0
  83. agent_decoder-0.3.2/frontend/src/components/Dashboard/HeadlineInsight.tsx +102 -0
  84. agent_decoder-0.3.2/frontend/src/components/Dashboard/Hero.tsx +34 -0
  85. agent_decoder-0.3.2/frontend/src/components/Dashboard/ModelRates.tsx +45 -0
  86. agent_decoder-0.3.2/frontend/src/components/Dashboard/ModelUsageStrip.tsx +47 -0
  87. agent_decoder-0.3.2/frontend/src/components/Dashboard/ProjectStrip.tsx +89 -0
  88. agent_decoder-0.3.2/frontend/src/components/Dashboard/SectionHeader.tsx +19 -0
  89. agent_decoder-0.3.2/frontend/src/components/Dashboard/Sparkline.tsx +40 -0
  90. agent_decoder-0.3.2/frontend/src/components/Dashboard/StatStrip.tsx +64 -0
  91. agent_decoder-0.3.2/frontend/src/components/Dashboard/TipCard.tsx +17 -0
  92. agent_decoder-0.3.2/frontend/src/components/Dashboard/TouchedFilesList.tsx +75 -0
  93. agent_decoder-0.3.2/frontend/src/components/Dashboard/fmt.ts +46 -0
  94. agent_decoder-0.3.2/frontend/src/components/ErrorBoundary.tsx +43 -0
  95. agent_decoder-0.3.2/frontend/src/components/FileViewer/FileViewer.tsx +687 -0
  96. agent_decoder-0.3.2/frontend/src/components/FileViewer/index.ts +1 -0
  97. agent_decoder-0.3.2/frontend/src/components/Messages/AssistantMessage.tsx +459 -0
  98. agent_decoder-0.3.2/frontend/src/components/Messages/ConfirmDialog.tsx +59 -0
  99. agent_decoder-0.3.2/frontend/src/components/Messages/EphemeralMessage.tsx +144 -0
  100. agent_decoder-0.3.2/frontend/src/components/Messages/MessageList.tsx +626 -0
  101. agent_decoder-0.3.2/frontend/src/components/Messages/SidechainBranch.tsx +47 -0
  102. agent_decoder-0.3.2/frontend/src/components/Messages/StreamingIndicator.tsx +134 -0
  103. agent_decoder-0.3.2/frontend/src/components/Messages/TextBlock.tsx +244 -0
  104. agent_decoder-0.3.2/frontend/src/components/Messages/ThinkingBlock.tsx +60 -0
  105. agent_decoder-0.3.2/frontend/src/components/Messages/ThoughtChain.tsx +790 -0
  106. agent_decoder-0.3.2/frontend/src/components/Messages/ToolUseBlock.tsx +289 -0
  107. agent_decoder-0.3.2/frontend/src/components/Messages/UserMessage.tsx +484 -0
  108. agent_decoder-0.3.2/frontend/src/components/Messages/__tests__/Ephemeral.test.tsx +337 -0
  109. agent_decoder-0.3.2/frontend/src/components/Messages/__tests__/TextBlock.test.tsx +133 -0
  110. agent_decoder-0.3.2/frontend/src/components/Messages/__tests__/ToolUseBlock.test.tsx +93 -0
  111. agent_decoder-0.3.2/frontend/src/components/Messages/__tests__/UserMessage.test.tsx +206 -0
  112. agent_decoder-0.3.2/frontend/src/components/Messages/blockRenderers.tsx +43 -0
  113. agent_decoder-0.3.2/frontend/src/components/Messages/groupMessages.ts +116 -0
  114. agent_decoder-0.3.2/frontend/src/components/Messages/hooks/__tests__/useScrollController.test.tsx +306 -0
  115. agent_decoder-0.3.2/frontend/src/components/Messages/hooks/__tests__/useSessionDetail.test.ts +245 -0
  116. agent_decoder-0.3.2/frontend/src/components/Messages/hooks/useCycle.ts +10 -0
  117. agent_decoder-0.3.2/frontend/src/components/Messages/hooks/useScrollController.ts +309 -0
  118. agent_decoder-0.3.2/frontend/src/components/Messages/hooks/useSessionDetail.ts +144 -0
  119. agent_decoder-0.3.2/frontend/src/components/Messages/pairToolBlocks.ts +51 -0
  120. agent_decoder-0.3.2/frontend/src/components/ScrollContainer.tsx +79 -0
  121. agent_decoder-0.3.2/frontend/src/components/Settings/NativePtyFontPicker.tsx +107 -0
  122. agent_decoder-0.3.2/frontend/src/components/Settings/PathEditor.tsx +179 -0
  123. agent_decoder-0.3.2/frontend/src/components/Settings/PermissionModeSection.tsx +111 -0
  124. agent_decoder-0.3.2/frontend/src/components/Settings/ProfileSection.tsx +515 -0
  125. agent_decoder-0.3.2/frontend/src/components/Settings/RecapSettings.tsx +62 -0
  126. agent_decoder-0.3.2/frontend/src/components/Settings/RescanButton.tsx +39 -0
  127. agent_decoder-0.3.2/frontend/src/components/Settings/SettingsModal.tsx +527 -0
  128. agent_decoder-0.3.2/frontend/src/components/Settings/SortOrderSection.tsx +47 -0
  129. agent_decoder-0.3.2/frontend/src/components/Settings/__tests__/SettingsModal.test.tsx +120 -0
  130. agent_decoder-0.3.2/frontend/src/components/Settings/shared.tsx +119 -0
  131. agent_decoder-0.3.2/frontend/src/components/Sidebar/FileExplorer.tsx +368 -0
  132. agent_decoder-0.3.2/frontend/src/components/Sidebar/HelpPopup.tsx +95 -0
  133. agent_decoder-0.3.2/frontend/src/components/Sidebar/NewTaskButton.tsx +165 -0
  134. agent_decoder-0.3.2/frontend/src/components/Sidebar/ProjectGroup.tsx +191 -0
  135. agent_decoder-0.3.2/frontend/src/components/Sidebar/SearchOverlay.tsx +398 -0
  136. agent_decoder-0.3.2/frontend/src/components/Sidebar/SessionItem.tsx +596 -0
  137. agent_decoder-0.3.2/frontend/src/components/Sidebar/ShortcutsPopup.tsx +80 -0
  138. agent_decoder-0.3.2/frontend/src/components/Sidebar/Sidebar.tsx +1086 -0
  139. agent_decoder-0.3.2/frontend/src/components/Sidebar/SidebarHeader.tsx +150 -0
  140. agent_decoder-0.3.2/frontend/src/components/Sidebar/__tests__/NewTaskButton.test.tsx +92 -0
  141. agent_decoder-0.3.2/frontend/src/components/Sidebar/__tests__/SearchOverlay.test.tsx +39 -0
  142. agent_decoder-0.3.2/frontend/src/components/Sidebar/__tests__/SessionItem.test.tsx +197 -0
  143. agent_decoder-0.3.2/frontend/src/components/Sidebar/__tests__/SessionItemRename.test.tsx +149 -0
  144. agent_decoder-0.3.2/frontend/src/components/Sidebar/hooks/__tests__/useRunnerStatuses.test.tsx +117 -0
  145. agent_decoder-0.3.2/frontend/src/components/Sidebar/hooks/useRunnerStatuses.ts +127 -0
  146. agent_decoder-0.3.2/frontend/src/components/Toast.tsx +77 -0
  147. agent_decoder-0.3.2/frontend/src/components/ui/ContextMenu.tsx +309 -0
  148. agent_decoder-0.3.2/frontend/src/components/ui/DelayedSkeleton.tsx +36 -0
  149. agent_decoder-0.3.2/frontend/src/components/ui/LoadingAnimation.tsx +198 -0
  150. agent_decoder-0.3.2/frontend/src/components/ui/RenameInput.tsx +55 -0
  151. agent_decoder-0.3.2/frontend/src/components/ui/icons.tsx +75 -0
  152. agent_decoder-0.3.2/frontend/src/config/ui.ts +45 -0
  153. agent_decoder-0.3.2/frontend/src/constants/nativePtyFonts.ts +101 -0
  154. agent_decoder-0.3.2/frontend/src/main.tsx +51 -0
  155. agent_decoder-0.3.2/frontend/src/router.ts +33 -0
  156. agent_decoder-0.3.2/frontend/src/store/__tests__/toggleExplorer.test.ts +40 -0
  157. agent_decoder-0.3.2/frontend/src/store/blocksState.ts +21 -0
  158. agent_decoder-0.3.2/frontend/src/store/index.ts +183 -0
  159. agent_decoder-0.3.2/frontend/src/styles/nativePtyFonts.css +73 -0
  160. agent_decoder-0.3.2/frontend/src/styles/theme.css +733 -0
  161. agent_decoder-0.3.2/frontend/src/test-setup.ts +1 -0
  162. agent_decoder-0.3.2/frontend/src/utils/__tests__/lazyWithRetry.test.tsx +96 -0
  163. agent_decoder-0.3.2/frontend/src/utils/__tests__/sessionMeta.test.tsx +152 -0
  164. agent_decoder-0.3.2/frontend/src/utils/events.ts +60 -0
  165. agent_decoder-0.3.2/frontend/src/utils/fileType.ts +40 -0
  166. agent_decoder-0.3.2/frontend/src/utils/formatRelative.ts +38 -0
  167. agent_decoder-0.3.2/frontend/src/utils/hljs.ts +88 -0
  168. agent_decoder-0.3.2/frontend/src/utils/lazyWithRetry.tsx +87 -0
  169. agent_decoder-0.3.2/frontend/src/utils/localStorage.ts +93 -0
  170. agent_decoder-0.3.2/frontend/src/utils/nativePtyFocus.ts +15 -0
  171. agent_decoder-0.3.2/frontend/src/utils/renderSnippet.tsx +12 -0
  172. agent_decoder-0.3.2/frontend/src/utils/sessionMeta.ts +281 -0
  173. agent_decoder-0.3.2/frontend/src/utils/timestamps.ts +5 -0
  174. agent_decoder-0.3.2/frontend/src/utils/useDebounce.ts +10 -0
  175. agent_decoder-0.3.2/frontend/tests/ghostty-web.pw.ts +70 -0
  176. agent_decoder-0.3.2/frontend/tsconfig.json +25 -0
  177. agent_decoder-0.3.2/frontend/tsconfig.node.json +10 -0
  178. agent_decoder-0.3.2/frontend/vite.config.ts +34 -0
  179. agent_decoder-0.3.2/pyproject.toml +65 -0
  180. agent_decoder-0.3.2/scripts/capture-move-bundle.sh +17 -0
  181. agent_decoder-0.3.2/src/clau_decode/__init__.py +8 -0
  182. agent_decoder-0.3.2/src/clau_decode/__main__.py +11 -0
  183. agent_decoder-0.3.2/src/clau_decode/_auth_env.py +118 -0
  184. agent_decoder-0.3.2/src/clau_decode/_pty_preexec.py +42 -0
  185. agent_decoder-0.3.2/src/clau_decode/analytics/__init__.py +0 -0
  186. agent_decoder-0.3.2/src/clau_decode/analytics/aggregator.py +55 -0
  187. agent_decoder-0.3.2/src/clau_decode/analytics/cost.py +76 -0
  188. agent_decoder-0.3.2/src/clau_decode/analytics/extractor.py +32 -0
  189. agent_decoder-0.3.2/src/clau_decode/analytics/fast.py +289 -0
  190. agent_decoder-0.3.2/src/clau_decode/analytics/models.py +49 -0
  191. agent_decoder-0.3.2/src/clau_decode/analytics/pricing.py +162 -0
  192. agent_decoder-0.3.2/src/clau_decode/analytics/prompt.py +34 -0
  193. agent_decoder-0.3.2/src/clau_decode/analytics/service.py +23 -0
  194. agent_decoder-0.3.2/src/clau_decode/analytics/stats.py +117 -0
  195. agent_decoder-0.3.2/src/clau_decode/analytics/tips.py +176 -0
  196. agent_decoder-0.3.2/src/clau_decode/btw_capture.py +290 -0
  197. agent_decoder-0.3.2/src/clau_decode/cli.py +504 -0
  198. agent_decoder-0.3.2/src/clau_decode/config.py +144 -0
  199. agent_decoder-0.3.2/src/clau_decode/db.py +2054 -0
  200. agent_decoder-0.3.2/src/clau_decode/driver_manager.py +387 -0
  201. agent_decoder-0.3.2/src/clau_decode/drivers/__init__.py +27 -0
  202. agent_decoder-0.3.2/src/clau_decode/drivers/base.py +170 -0
  203. agent_decoder-0.3.2/src/clau_decode/drivers/registry.py +113 -0
  204. agent_decoder-0.3.2/src/clau_decode/drivers/tmux_driver.py +488 -0
  205. agent_decoder-0.3.2/src/clau_decode/editor.py +182 -0
  206. agent_decoder-0.3.2/src/clau_decode/events_bus.py +69 -0
  207. agent_decoder-0.3.2/src/clau_decode/locks.py +388 -0
  208. agent_decoder-0.3.2/src/clau_decode/migrate.py +1313 -0
  209. agent_decoder-0.3.2/src/clau_decode/models.py +285 -0
  210. agent_decoder-0.3.2/src/clau_decode/parser.py +466 -0
  211. agent_decoder-0.3.2/src/clau_decode/providers/__init__.py +30 -0
  212. agent_decoder-0.3.2/src/clau_decode/providers/base.py +132 -0
  213. agent_decoder-0.3.2/src/clau_decode/providers/claude.py +81 -0
  214. agent_decoder-0.3.2/src/clau_decode/providers/codex.py +441 -0
  215. agent_decoder-0.3.2/src/clau_decode/providers/registry.py +89 -0
  216. agent_decoder-0.3.2/src/clau_decode/pty_native.py +39 -0
  217. agent_decoder-0.3.2/src/clau_decode/pty_runner.py +2406 -0
  218. agent_decoder-0.3.2/src/clau_decode/pty_screen_state.py +65 -0
  219. agent_decoder-0.3.2/src/clau_decode/recap_runner.py +230 -0
  220. agent_decoder-0.3.2/src/clau_decode/reporter.py +305 -0
  221. agent_decoder-0.3.2/src/clau_decode/scanner.py +125 -0
  222. agent_decoder-0.3.2/src/clau_decode/server.py +2841 -0
  223. agent_decoder-0.3.2/src/clau_decode/static/android-chrome-192x192.png +0 -0
  224. agent_decoder-0.3.2/src/clau_decode/static/android-chrome-512x512.png +0 -0
  225. agent_decoder-0.3.2/src/clau_decode/static/app-icon.png +0 -0
  226. agent_decoder-0.3.2/src/clau_decode/static/apple-touch-icon.png +0 -0
  227. agent_decoder-0.3.2/src/clau_decode/static/assets/ChatView-BBVXuITs.js +79 -0
  228. agent_decoder-0.3.2/src/clau_decode/static/assets/ChatView-BrP-ENHg.css +1 -0
  229. agent_decoder-0.3.2/src/clau_decode/static/assets/Dashboard-3CYO4HQw.js +1 -0
  230. agent_decoder-0.3.2/src/clau_decode/static/assets/FileViewer-BwbDc3ky.js +1 -0
  231. agent_decoder-0.3.2/src/clau_decode/static/assets/HelpPopup-CPtECjYw.js +1 -0
  232. agent_decoder-0.3.2/src/clau_decode/static/assets/ScrollContainer-BT_Dzxah.js +1 -0
  233. agent_decoder-0.3.2/src/clau_decode/static/assets/SearchOverlay-DqI7aZaC.js +1 -0
  234. agent_decoder-0.3.2/src/clau_decode/static/assets/SessionItem-DlyKg80u.js +1 -0
  235. agent_decoder-0.3.2/src/clau_decode/static/assets/SettingsModal-CZBfMqx7.js +1 -0
  236. agent_decoder-0.3.2/src/clau_decode/static/assets/ShortcutsPopup-W7CPdCzW.js +1 -0
  237. agent_decoder-0.3.2/src/clau_decode/static/assets/Sidebar-BBswhEua.js +1 -0
  238. agent_decoder-0.3.2/src/clau_decode/static/assets/client-DTSXvAb8.js +1 -0
  239. agent_decoder-0.3.2/src/clau_decode/static/assets/fira-code-DGosTW8U.woff2 +0 -0
  240. agent_decoder-0.3.2/src/clau_decode/static/assets/formatRelative-j86webkr.js +1 -0
  241. agent_decoder-0.3.2/src/clau_decode/static/assets/hljs-B3O74O7Z.js +35 -0
  242. agent_decoder-0.3.2/src/clau_decode/static/assets/index-BHdCOIkq.css +10 -0
  243. agent_decoder-0.3.2/src/clau_decode/static/assets/index-DfKltZ6z.js +5 -0
  244. agent_decoder-0.3.2/src/clau_decode/static/assets/ioskeley-mono-C7VR2-sL.woff2 +0 -0
  245. agent_decoder-0.3.2/src/clau_decode/static/assets/jetbrains-mono-V6pRDFza.woff2 +0 -0
  246. agent_decoder-0.3.2/src/clau_decode/static/assets/julia-mono-Cbvba6sW.woff2 +0 -0
  247. agent_decoder-0.3.2/src/clau_decode/static/assets/libertinus-mono-Coz17E-O.woff2 +0 -0
  248. agent_decoder-0.3.2/src/clau_decode/static/assets/monaspace-argon-RS-2TGNg.woff2 +0 -0
  249. agent_decoder-0.3.2/src/clau_decode/static/assets/nativePtyFonts-BaXKtUsn.js +1 -0
  250. agent_decoder-0.3.2/src/clau_decode/static/assets/react-Bi_azaFt.js +9 -0
  251. agent_decoder-0.3.2/src/clau_decode/static/assets/renderSnippet-CqpQQugH.js +1 -0
  252. agent_decoder-0.3.2/src/clau_decode/static/assets/rolldown-runtime-S-ySWqyJ.js +1 -0
  253. agent_decoder-0.3.2/src/clau_decode/static/assets/shared-DdVGprvF.js +1 -0
  254. agent_decoder-0.3.2/src/clau_decode/static/assets/source-code-pro-DEXWLoHG.woff2 +0 -0
  255. agent_decoder-0.3.2/src/clau_decode/static/assets/spline-sans-mono-mUpA6Mve.woff2 +0 -0
  256. agent_decoder-0.3.2/src/clau_decode/static/assets/ui-BcGPjpYn.js +1 -0
  257. agent_decoder-0.3.2/src/clau_decode/static/assets/xanh-mono-Dsg19vWS.woff2 +0 -0
  258. agent_decoder-0.3.2/src/clau_decode/static/favicon-16x16.png +0 -0
  259. agent_decoder-0.3.2/src/clau_decode/static/favicon-32x32.png +0 -0
  260. agent_decoder-0.3.2/src/clau_decode/static/favicon.ico +0 -0
  261. agent_decoder-0.3.2/src/clau_decode/static/index.html +24 -0
  262. agent_decoder-0.3.2/src/clau_decode/static/site.webmanifest +1 -0
  263. agent_decoder-0.3.2/src/clau_decode/update_check.py +51 -0
  264. agent_decoder-0.3.2/src/clau_decode/watcher.py +63 -0
  265. agent_decoder-0.3.2/tests/__init__.py +0 -0
  266. agent_decoder-0.3.2/tests/analytics/__init__.py +0 -0
  267. agent_decoder-0.3.2/tests/analytics/test_aggregator.py +161 -0
  268. agent_decoder-0.3.2/tests/analytics/test_cost.py +112 -0
  269. agent_decoder-0.3.2/tests/analytics/test_extractor.py +128 -0
  270. agent_decoder-0.3.2/tests/analytics/test_fast_parity.py +182 -0
  271. agent_decoder-0.3.2/tests/analytics/test_integration.py +113 -0
  272. agent_decoder-0.3.2/tests/analytics/test_pricing.py +206 -0
  273. agent_decoder-0.3.2/tests/analytics/test_prompt.py +78 -0
  274. agent_decoder-0.3.2/tests/analytics/test_service.py +83 -0
  275. agent_decoder-0.3.2/tests/analytics/test_stats.py +193 -0
  276. agent_decoder-0.3.2/tests/analytics/test_tips.py +250 -0
  277. agent_decoder-0.3.2/tests/conftest.py +9 -0
  278. agent_decoder-0.3.2/tests/drivers/__init__.py +0 -0
  279. agent_decoder-0.3.2/tests/drivers/fake_cli.py +67 -0
  280. agent_decoder-0.3.2/tests/drivers/test_driver_manager.py +412 -0
  281. agent_decoder-0.3.2/tests/drivers/test_tmux_driver.py +376 -0
  282. agent_decoder-0.3.2/tests/fixtures/aaaaaaaa-0000-0000-0000-000000000001.jsonl +8 -0
  283. agent_decoder-0.3.2/tests/fixtures/bad-name.jsonl +1 -0
  284. agent_decoder-0.3.2/tests/fixtures/bbbbbbbb-0000-0000-0000-000000000002.jsonl +8 -0
  285. agent_decoder-0.3.2/tests/fixtures/btw_capture/live_zai_no_open_marker.bin +287 -0
  286. agent_decoder-0.3.2/tests/fixtures/btw_capture/multiline.bin +918 -0
  287. agent_decoder-0.3.2/tests/fixtures/codex/sample_rollout.jsonl +15 -0
  288. agent_decoder-0.3.2/tests/fixtures/fake_claude_tui.py +394 -0
  289. agent_decoder-0.3.2/tests/fixtures/session_with_usage.jsonl +5 -0
  290. agent_decoder-0.3.2/tests/fixtures/sidechain_session.jsonl +8 -0
  291. agent_decoder-0.3.2/tests/fixtures/simple_session.jsonl +8 -0
  292. agent_decoder-0.3.2/tests/providers/__init__.py +0 -0
  293. agent_decoder-0.3.2/tests/providers/test_claude_adapter.py +179 -0
  294. agent_decoder-0.3.2/tests/providers/test_codex_adapter.py +413 -0
  295. agent_decoder-0.3.2/tests/providers/test_registry.py +158 -0
  296. agent_decoder-0.3.2/tests/test_active_data_sources.py +50 -0
  297. agent_decoder-0.3.2/tests/test_analytics_ignores_ephemerals.py +276 -0
  298. agent_decoder-0.3.2/tests/test_auth_env.py +39 -0
  299. agent_decoder-0.3.2/tests/test_btw_capture.py +405 -0
  300. agent_decoder-0.3.2/tests/test_cli.py +239 -0
  301. agent_decoder-0.3.2/tests/test_codex_e2e.py +180 -0
  302. agent_decoder-0.3.2/tests/test_db.py +883 -0
  303. agent_decoder-0.3.2/tests/test_db_session_meta.py +117 -0
  304. agent_decoder-0.3.2/tests/test_delete_sessions.py +268 -0
  305. agent_decoder-0.3.2/tests/test_dev_import_path.py +32 -0
  306. agent_decoder-0.3.2/tests/test_e2e.py +864 -0
  307. agent_decoder-0.3.2/tests/test_editor.py +278 -0
  308. agent_decoder-0.3.2/tests/test_ephemeral_messages.py +402 -0
  309. agent_decoder-0.3.2/tests/test_events_bus.py +89 -0
  310. agent_decoder-0.3.2/tests/test_export_ephemeral.py +375 -0
  311. agent_decoder-0.3.2/tests/test_locks.py +278 -0
  312. agent_decoder-0.3.2/tests/test_migrate.py +531 -0
  313. agent_decoder-0.3.2/tests/test_parser.py +264 -0
  314. agent_decoder-0.3.2/tests/test_pty_native.py +19 -0
  315. agent_decoder-0.3.2/tests/test_pty_runner.py +1707 -0
  316. agent_decoder-0.3.2/tests/test_pty_runner_btw.py +907 -0
  317. agent_decoder-0.3.2/tests/test_pty_screen_state.py +62 -0
  318. agent_decoder-0.3.2/tests/test_recaps.py +342 -0
  319. agent_decoder-0.3.2/tests/test_reporter.py +414 -0
  320. agent_decoder-0.3.2/tests/test_scanner.py +132 -0
  321. agent_decoder-0.3.2/tests/test_search_ephemeral.py +274 -0
  322. agent_decoder-0.3.2/tests/test_server.py +819 -0
  323. agent_decoder-0.3.2/tests/test_server_capabilities.py +267 -0
  324. agent_decoder-0.3.2/tests/test_server_open_terminal.py +232 -0
  325. agent_decoder-0.3.2/tests/test_server_session_title.py +181 -0
  326. agent_decoder-0.3.2/tests/test_update_check.py +165 -0
  327. agent_decoder-0.3.2/uv.lock +971 -0
@@ -0,0 +1,30 @@
1
+ # EditorConfig — https://editorconfig.org
2
+ # Keeps indentation and line endings consistent across editors.
3
+ # Soft style hint: aim for 80–100 char lines in prose and code where reasonable;
4
+ # no hard max-line-length is enforced here (let ruff/prettier handle that).
5
+
6
+ root = true
7
+
8
+ [*]
9
+ charset = utf-8
10
+ end_of_line = lf
11
+ insert_final_newline = true
12
+ trim_trailing_whitespace = true
13
+ indent_style = space
14
+ indent_size = 2
15
+
16
+ # Python — 4-space indent per PEP 8
17
+ [*.py]
18
+ indent_size = 4
19
+
20
+ # TypeScript / React / styles / JSON / YAML / Markdown — 2-space indent
21
+ [*.{ts,tsx,js,jsx,css,scss,json,yml,yaml,md}]
22
+ indent_size = 2
23
+
24
+ # Markdown often relies on trailing whitespace for hard line breaks
25
+ [*.md]
26
+ trim_trailing_whitespace = false
27
+
28
+ # Makefiles require literal tabs for recipe lines
29
+ [Makefile]
30
+ indent_style = tab
@@ -0,0 +1,7 @@
1
+ # CODEOWNERS for Clau-Decode
2
+ #
3
+ # GitHub uses this file to auto-request reviews on pull requests.
4
+ # The last matching pattern for a given file wins, so order matters.
5
+ # Docs: https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-security/customizing-your-repository/about-code-owners
6
+
7
+ * @Comradery64
@@ -0,0 +1,20 @@
1
+ name: Bug report
2
+ description: Report something that's broken or behaving unexpectedly.
3
+ title: "bug: "
4
+ labels: ["bug"]
5
+ body:
6
+ - type: textarea
7
+ id: description
8
+ attributes:
9
+ label: What happened?
10
+ description: Describe the bug. Steps to reproduce, expected behavior, and logs are helpful but optional.
11
+ placeholder: When I click X, the app does Y instead of Z.
12
+ validations:
13
+ required: true
14
+
15
+ - type: textarea
16
+ id: logs
17
+ attributes:
18
+ label: Relevant logs / output
19
+ description: Paste any tracebacks, console output, or errors (optional).
20
+ render: shell
@@ -0,0 +1,5 @@
1
+ blank_issues_enabled: true
2
+ contact_links:
3
+ - name: Questions and general discussion
4
+ url: https://github.com/Comradery64/Clau-Decode/discussions
5
+ about: For usage questions, ideas in flux, or anything that isn't a clear bug or feature request, please use Discussions.
@@ -0,0 +1,13 @@
1
+ name: Feature request
2
+ description: Suggest a new feature, enhancement, or improvement.
3
+ title: "feat: "
4
+ labels: ["enhancement"]
5
+ body:
6
+ - type: textarea
7
+ id: description
8
+ attributes:
9
+ label: What do you want?
10
+ description: Describe the problem or feature. Don't worry about format — just explain what you're looking for.
11
+ placeholder: "It would be great if I could…"
12
+ validations:
13
+ required: true
@@ -0,0 +1,44 @@
1
+ <!--
2
+ Thanks for the PR! Fill out the sections below. Delete any that don't apply.
3
+ Title should follow Conventional Commits (feat:, fix:, chore:, docs:, refactor:, test:, ci:).
4
+ -->
5
+
6
+ ## Summary
7
+
8
+ <!-- One or two sentences: what does this PR do? -->
9
+
10
+ ## Motivation
11
+
12
+ <!-- Why are we making this change? Link the issue this PR closes:
13
+ Closes #123
14
+ -->
15
+
16
+ ## Changes
17
+
18
+ <!-- Bullet list of the meaningful changes. Avoid restating the diff line-by-line. -->
19
+
20
+ -
21
+
22
+ ## Screenshots
23
+
24
+ <!-- For UI changes, drop before/after screenshots or short clips here.
25
+ Delete this section if not applicable. -->
26
+
27
+ ## Testing
28
+
29
+ <!-- What did you run, and what was the result?
30
+
31
+ ```
32
+ make test
33
+ cd frontend && npm test
34
+ ```
35
+ -->
36
+
37
+ ## Checklist
38
+
39
+ - [ ] Tests pass locally (`make test` and `cd frontend && npm test`)
40
+ - [ ] `ruff` is clean (`uv run ruff check .` and `uv run ruff format --check .`)
41
+ - [ ] TypeScript builds (`cd frontend && npm run build`)
42
+ - [ ] `CHANGELOG.md` updated under `[Unreleased]` if user-visible
43
+ - [ ] Docs updated if behavior, CLI flags, or config changed
44
+ - [ ] No telemetry, no remote network calls — data stays on the user's machine
@@ -0,0 +1,31 @@
1
+ version: 2
2
+ updates:
3
+ # Python dependencies (pyproject.toml at the repo root)
4
+ - package-ecosystem: "pip"
5
+ directory: "/"
6
+ schedule:
7
+ interval: "weekly"
8
+ day: "monday"
9
+ open-pull-requests-limit: 5
10
+ commit-message:
11
+ prefix: "chore"
12
+
13
+ # Frontend dependencies (frontend/package.json)
14
+ - package-ecosystem: "npm"
15
+ directory: "/frontend"
16
+ schedule:
17
+ interval: "weekly"
18
+ day: "monday"
19
+ open-pull-requests-limit: 5
20
+ commit-message:
21
+ prefix: "chore"
22
+
23
+ # GitHub Actions workflow pins
24
+ - package-ecosystem: "github-actions"
25
+ directory: "/"
26
+ schedule:
27
+ interval: "weekly"
28
+ day: "monday"
29
+ open-pull-requests-limit: 5
30
+ commit-message:
31
+ prefix: "chore"
@@ -0,0 +1,88 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+ branches: [main]
8
+
9
+ concurrency:
10
+ group: ci-${{ github.ref }}
11
+ cancel-in-progress: true
12
+
13
+ permissions:
14
+ contents: read
15
+
16
+ jobs:
17
+ python:
18
+ name: Python (${{ matrix.python-version }})
19
+ runs-on: ubuntu-latest
20
+ strategy:
21
+ fail-fast: false
22
+ matrix:
23
+ python-version: ["3.10", "3.11", "3.12"]
24
+ steps:
25
+ - name: Checkout
26
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
27
+
28
+ - name: Set up uv
29
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
30
+ with:
31
+ python-version: ${{ matrix.python-version }}
32
+ enable-cache: true
33
+
34
+ - name: Install dependencies
35
+ run: uv sync --extra dev
36
+
37
+ - name: Install ruff
38
+ # Pinned (and kept in lockstep with .pre-commit-config.yaml's ruff rev) so
39
+ # CI lint/format results can't drift when a new ruff release changes default
40
+ # rules or formatting. Bump both together intentionally.
41
+ run: uv pip install ruff==0.15.12
42
+
43
+ - name: Lint (ruff check)
44
+ run: uv run ruff check .
45
+
46
+ - name: Format check (ruff format)
47
+ run: uv run ruff format --check .
48
+
49
+ - name: Tests (pytest with coverage)
50
+ # pyproject's [tool.pytest.ini_options] addopts already enables
51
+ # --cov=clau_decode; append an XML report for Codecov upload.
52
+ run: uv run pytest --cov-report=xml
53
+
54
+ - name: Upload coverage to Codecov
55
+ if: matrix.python-version == '3.12'
56
+ uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0
57
+ with:
58
+ files: ./coverage.xml
59
+ fail_ci_if_error: false
60
+
61
+ frontend:
62
+ name: Frontend
63
+ runs-on: ubuntu-latest
64
+ defaults:
65
+ run:
66
+ working-directory: frontend
67
+ steps:
68
+ - name: Checkout
69
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
70
+
71
+ - name: Set up Node.js
72
+ uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
73
+ with:
74
+ node-version: "20"
75
+ cache: npm
76
+ cache-dependency-path: frontend/package-lock.json
77
+
78
+ - name: Install dependencies
79
+ run: npm ci
80
+
81
+ - name: Type-check (tsc --noEmit)
82
+ run: npx tsc --noEmit
83
+
84
+ - name: Build (vite)
85
+ run: npm run build
86
+
87
+ - name: Tests (vitest)
88
+ run: npm test
@@ -0,0 +1,67 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ push:
5
+ tags:
6
+ - "v*"
7
+
8
+ concurrency:
9
+ group: publish-${{ github.ref }}
10
+ cancel-in-progress: false
11
+
12
+ permissions:
13
+ contents: read
14
+
15
+ jobs:
16
+ build:
17
+ name: Build distribution
18
+ runs-on: ubuntu-latest
19
+ steps:
20
+ - name: Checkout
21
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
22
+
23
+ - name: Set up uv
24
+ uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0
25
+ with:
26
+ python-version: "3.12"
27
+
28
+ - name: Verify tag matches package version
29
+ # __init__.py is the single source of truth (see its own header
30
+ # comment). A mismatched tag would publish the wrong version to
31
+ # PyPI, where releases are immutable and can only be yanked, not
32
+ # deleted or overwritten.
33
+ run: |
34
+ TAG_VERSION="${GITHUB_REF_NAME#v}"
35
+ PKG_VERSION="$(grep -oP '(?<=^__version__ = ")[^"]+' src/clau_decode/__init__.py)"
36
+ if [ "$TAG_VERSION" != "$PKG_VERSION" ]; then
37
+ echo "::error::Tag v$TAG_VERSION does not match __version__ ($PKG_VERSION)"
38
+ exit 1
39
+ fi
40
+
41
+ - name: Build sdist and wheel
42
+ run: uv build
43
+
44
+ - name: Upload build artifacts
45
+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
46
+ with:
47
+ name: dist
48
+ path: dist/
49
+
50
+ publish:
51
+ name: Publish to PyPI
52
+ needs: build
53
+ runs-on: ubuntu-latest
54
+ environment:
55
+ name: pypi
56
+ url: https://pypi.org/project/agent-decoder/
57
+ permissions:
58
+ id-token: write # required for PyPI trusted publishing (OIDC) — no stored API token
59
+ steps:
60
+ - name: Download build artifacts
61
+ uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
62
+ with:
63
+ name: dist
64
+ path: dist/
65
+
66
+ - name: Publish to PyPI
67
+ uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
@@ -0,0 +1,53 @@
1
+ # Python
2
+ __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.egg-info/
6
+ dist/
7
+ build/
8
+ .venv/
9
+ .venv*/
10
+ venv/
11
+ .pytest_cache/
12
+ .coverage
13
+ htmlcov/
14
+
15
+ # Databases / scratch
16
+ *.db
17
+ *.tmp
18
+
19
+ # Node
20
+ node_modules/
21
+ frontend/node_modules/
22
+ frontend/dist/
23
+
24
+ # Worktrees
25
+ .worktrees/
26
+
27
+ # OS / editor cruft
28
+ .DS_Store
29
+ Thumbs.db
30
+ *.swp
31
+ *.swo
32
+ .idea/
33
+ .vscode/
34
+
35
+ # Local secrets — never commit
36
+ .env
37
+ .env.*
38
+ !.env.example
39
+ *.pem
40
+ *.key
41
+ secrets.*
42
+
43
+ # Session-edit safety backups (created by --enable-edit before each write)
44
+ *.bak.*.jsonl
45
+
46
+ # Demo fixtures, --demo runtime state, and the VHS recording pipeline are
47
+ # local-only — useful for producing screen recordings but not part of the
48
+ # published source. Keep them on disk, just out of git.
49
+ demo-data/
50
+ demo-reel/
51
+
52
+ # Keep the built static assets — they ship with the package
53
+ !src/clau_decode/static/
@@ -0,0 +1,27 @@
1
+ # Pre-commit hooks for clau-decode.
2
+ # Install once with `pre-commit install`; run on demand with `pre-commit run --all-files`.
3
+ # pre-commit autoupdate will refresh these revs.
4
+
5
+ repos:
6
+ - repo: https://github.com/pre-commit/pre-commit-hooks
7
+ rev: v6.0.0
8
+ hooks:
9
+ - id: trailing-whitespace
10
+ - id: end-of-file-fixer
11
+ - id: check-yaml
12
+ - id: check-toml
13
+ - id: check-merge-conflict
14
+ - id: check-added-large-files
15
+
16
+ - repo: https://github.com/astral-sh/ruff-pre-commit
17
+ rev: v0.15.12
18
+ hooks:
19
+ - id: ruff
20
+ args: [--fix]
21
+ - id: ruff-format
22
+
23
+ - repo: https://github.com/pre-commit/mirrors-prettier
24
+ rev: v4.0.0-alpha.8
25
+ hooks:
26
+ - id: prettier
27
+ files: ^frontend/.*\.(ts|tsx|css|md|json)$
@@ -0,0 +1,166 @@
1
+ # Architecture
2
+
3
+ This document describes the internal architecture of `clau-decode`. It
4
+ complements [`README.md`](README.md) (user-facing) with the technical detail a
5
+ contributor or reviewer needs to navigate the code.
6
+
7
+ ## Overview
8
+
9
+ Clau-Decode is a single-process, local-first application. A FastAPI server
10
+ (uvicorn, bound to `127.0.0.1:4242` by default) scans your AI coding
11
+ assistant's JSONL session files into a SQLite index, exposes them through a
12
+ JSON HTTP API plus a Server-Sent Events stream, and serves a pre-built React +
13
+ TypeScript SPA from the same origin. A `watchfiles`-based watcher detects
14
+ session-file changes in real time, and a PTY manager drives the local Claude
15
+ CLI in interactive TUI mode through a hidden pseudo-terminal so users can send
16
+ messages from the web UI with subscription-backed interactive behavior.
17
+ Nothing leaves the machine — there is no remote
18
+ backend, no telemetry, and no authentication layer.
19
+
20
+ ## Component diagram
21
+
22
+ ```mermaid
23
+ flowchart LR
24
+ subgraph Browser
25
+ SPA[React + Vite SPA]
26
+ end
27
+
28
+ subgraph LocalMachine[Local machine]
29
+ Server[FastAPI / uvicorn<br/>127.0.0.1:4242]
30
+ DB[(SQLite index<br/>FTS5)]
31
+ Scanner[Scanner + Parser]
32
+ Watcher[watchfiles watcher]
33
+ PtyManager[PTY manager<br/>hidden TUI per chat]
34
+ Recap[Recap runner<br/>PTY fork-session]
35
+ end
36
+
37
+ subgraph Filesystem[Filesystem]
38
+ JSONL[~/.claude/projects/**/*.jsonl]
39
+ Cfg[~/.config/clau-decode/config.json]
40
+ Cache[~/.local/share/clau-decode/index.db]
41
+ end
42
+
43
+ SPA -->|fetch + EventSource| Server
44
+ Server --> DB
45
+ Server -->|scan + parse on startup| Scanner
46
+ Scanner --> JSONL
47
+ Scanner --> DB
48
+ Watcher -->|fsevents / inotify| JSONL
49
+ Watcher -->|asyncio.Queue| Server
50
+ Server -->|SSE /api/events| SPA
51
+ Server -->|focus/submit| PtyManager
52
+ PtyManager -->|hidden PTY| ClaudeTUI[claude TUI]
53
+ ClaudeTUI -->|appends turns| JSONL
54
+ Server -->|recap on demand| Recap
55
+ Recap -->|spawns claude --fork-session in hidden PTY| JSONL
56
+ Server --> Cfg
57
+ DB --- Cache
58
+ ```
59
+
60
+ ## Data flow: opening a session
61
+
62
+ 1. User clicks a session in the sidebar.
63
+ 2. The frontend router (`frontend/src/router.ts`) updates the URL hash to
64
+ `#/chat/<session_id>` and `App.tsx` reacts to the route change.
65
+ 3. `frontend/src/api/client.ts` issues `GET /api/sessions/<session_id>`.
66
+ 4. The FastAPI route (`src/clau_decode/server.py`) calls `Database.get_session_detail`
67
+ (`src/clau_decode/db.py`), which reads the session row and its messages
68
+ from SQLite.
69
+ 5. If the JSONL file's mtime has changed since the last index update, the
70
+ server re-parses the file via `parse_session` (`src/clau_decode/parser.py`)
71
+ and upserts the new messages back into SQLite before returning.
72
+ 6. The route returns a `SessionDetail` JSON document; the React `ChatView`
73
+ component renders markdown, tool-use, thinking, and sidechain blocks.
74
+ 7. While the session is open, `/api/events` (SSE) pushes `{"type": "refresh"}`
75
+ payloads whenever the watcher sees the underlying JSONL change. The
76
+ frontend re-fetches and updates in place.
77
+
78
+ ## Key directories
79
+
80
+ ### Backend — `src/clau_decode/`
81
+
82
+ | Path | Role |
83
+ |------|------|
84
+ | `cli.py` | Argument parsing, subcommands (`dashboard`, `scan`, `today`, `stats`, `tips`), uvicorn launch |
85
+ | `server.py` | FastAPI app: HTTP routes, SSE stream, static SPA mount |
86
+ | `config.py` | XDG-aware config + DB path resolution, atomic config writes |
87
+ | `models.py` | Pydantic models (`AppConfig`, `Project`, `Session`, `Message`, `ContentBlock`, ...) |
88
+ | `scanner.py` | Walks `<root>/projects/*/` for JSONL session files |
89
+ | `parser.py` | Parses JSONL into `Session` + `list[Message]`; builds message trees |
90
+ | `db.py` | Async SQLite layer (`aiosqlite`) with FTS5 for search |
91
+ | `watcher.py` | `watchfiles.awatch` wrapper that emits `WatchEvent`s |
92
+ | `pty_runner.py` | PTY-attached interactive `claude` driver — canonical send path |
93
+ | `recap_runner.py` | Fire-and-forget PTY-on-fork-session driver for inline recaps |
94
+ | `editor.py` | Sandboxed file editing (used by `--enable-edit`) |
95
+ | `reporter.py` | JSON / Markdown export of conversations |
96
+ | `analytics/` | Token + cost engine, daily/weekly aggregators, tool/file/model stats, tip rules |
97
+ | `static/` | Pre-built SPA assets (committed in the wheel; rebuilt by `make frontend`) |
98
+
99
+ ### Frontend — `frontend/src/`
100
+
101
+ | Path | Role |
102
+ |------|------|
103
+ | `main.tsx` | React entry point |
104
+ | `App.tsx` | Top-level layout, route-driven view selection, SSE wiring |
105
+ | `router.ts` | Hash-based routing (`/`, `/analytics`, `/chat/<id>`) |
106
+ | `api/` | Typed fetch client; `EventSource` helpers |
107
+ | `store/` | Zustand state stores (selection, UI flags, sidebar) |
108
+ | `components/Sidebar/` | Sidebar, search overlay, help, shortcuts |
109
+ | `components/ChatView/` | Conversation viewer, message blocks, runner panel |
110
+ | `components/Dashboard/` | Home dashboard (hero, heatmap, recap, projects) |
111
+ | `components/Analytics/` | Analytics panel and charts (ECharts) |
112
+ | `components/FileViewer/` | Split-pane file viewer + editor |
113
+ | `components/Settings/` | Settings modal |
114
+
115
+ ## Storage
116
+
117
+ Clau-Decode follows the [XDG Base Directory
118
+ spec](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html):
119
+
120
+ | Purpose | Path (default) | Notes |
121
+ |---------|----------------|-------|
122
+ | User config | `~/.config/clau-decode/config.json` (or `$XDG_CONFIG_HOME/clau-decode/config.json`) | Atomic writes (`*.tmp` → rename) |
123
+ | Session index | `~/.local/share/clau-decode/index.db` (or `$XDG_DATA_HOME/clau-decode/index.db`) | SQLite + FTS5; durable (holds stars/archives/titles). Use `--force-refresh` to rescan rather than deleting |
124
+ | Scanned sessions | `~/.claude/projects/**/*.jsonl` (configurable) | Read-only by default; written only via the runner or `--enable-edit` |
125
+
126
+ No remote storage. No outbound network calls except optional pricing-table
127
+ refresh in the analytics module.
128
+
129
+ ## Security model
130
+
131
+ - **Bind address.** Defaults to `127.0.0.1`. `--expose` (or `--host 0.0.0.0`)
132
+ binds to all interfaces and prints a warning; intended for trusted networks
133
+ only. There is no authentication.
134
+ - **File viewer.** Reads and (with `--enable-edit`) writes files. Access is
135
+ sandboxed to session-related directories; path traversal and symlink escapes
136
+ are explicitly defended against in `editor.py`. Binary and oversized writes
137
+ are refused. A backup is written next to the original before any edit.
138
+ - **JSONL parsing.** Treated as untrusted input. Malformed lines are skipped
139
+ with a warning; the parser never `eval`s and does not follow URLs.
140
+ - **PTY runner.** Spawns the local CLI binary on the user's behalf, attached
141
+ to a hidden pseudo-terminal. The binary name is derived from the session's
142
+ filesystem layout, not from user-supplied input; `shell=True` is never used.
143
+ `ANTHROPIC_API_KEY` / `ANTHROPIC_AUTH_TOKEN` are stripped from the spawned
144
+ child's env (unless the active profile is API-key authenticated) so a user
145
+ with one of those exported in their shell doesn't accidentally bill against
146
+ the static API key.
147
+ - **No telemetry.** No analytics, no error reporting, no remote logging. The
148
+ CI test suite asserts there are no outbound calls to remote services.
149
+
150
+ See [`SECURITY.md`](SECURITY.md) for the disclosure process and full scope.
151
+
152
+ ## Performance notes
153
+
154
+ - **Incremental scan.** Each session row stores the source JSONL's mtime;
155
+ files whose mtime is unchanged are skipped on rescan.
156
+ - **In-memory session-detail cache.** Hot sessions stay in a small LRU on the
157
+ server so repeated fetches don't re-query SQLite.
158
+ - **FTS5 search.** Full-text search uses SQLite's FTS5 virtual table, which
159
+ gives sub-millisecond query times across tens of thousands of messages.
160
+ - **Streaming SSE.** Live updates use a single long-lived `EventSource`
161
+ connection per browser tab; payloads are tiny (`{"type": "refresh"}`).
162
+ - **Lazy frontend chunks.** Heavy components (ChatView, Analytics, Dashboard,
163
+ FileViewer, SearchOverlay, SettingsModal) are `React.lazy`-loaded and
164
+ pre-warmed in the background after first paint.
165
+ - **GZip middleware.** FastAPI compresses JSON responses over a few hundred
166
+ bytes, which keeps session-detail payloads small on large conversations.