painapple-code 1.0.0__py3-none-any.whl

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 (436) hide show
  1. painapple_code/__init__.py +30 -0
  2. painapple_code/__main__.py +18 -0
  3. painapple_code/_version.py +24 -0
  4. painapple_code/auth_middleware.py +930 -0
  5. painapple_code/cli/__init__.py +204 -0
  6. painapple_code/cli/deploy/__init__.py +15 -0
  7. painapple_code/cli/deploy/claude_seed.py +41 -0
  8. painapple_code/cli/deploy/config.py +260 -0
  9. painapple_code/cli/deploy/container.py +715 -0
  10. painapple_code/cli/deploy/launch.py +163 -0
  11. painapple_code/cli/deploy/runtime.py +223 -0
  12. painapple_code/cli/lifecycle_cmd.py +443 -0
  13. painapple_code/cli/list_cmd.py +572 -0
  14. painapple_code/cli/manage_cmd.py +670 -0
  15. painapple_code/cli/netinfo.py +125 -0
  16. painapple_code/cli/profiles.py +390 -0
  17. painapple_code/cli/serve_args.py +163 -0
  18. painapple_code/cli/serve_config.py +256 -0
  19. painapple_code/cli/setup_cmd.py +664 -0
  20. painapple_code/cli/ui.py +438 -0
  21. painapple_code/cost_analytics.py +879 -0
  22. painapple_code/data/models.defaults.yaml +18 -0
  23. painapple_code/data/models.yaml +18 -0
  24. painapple_code/data/presets.defaults.json +32 -0
  25. painapple_code/data/strings.yaml +2269 -0
  26. painapple_code/helpers.py +406 -0
  27. painapple_code/paths.py +1514 -0
  28. painapple_code/prompt_explorer.py +984 -0
  29. painapple_code/providers/__init__.py +224 -0
  30. painapple_code/providers/base.py +710 -0
  31. painapple_code/providers/claude/__init__.py +74 -0
  32. painapple_code/providers/claude/capabilities.py +218 -0
  33. painapple_code/providers/claude/errors.py +79 -0
  34. painapple_code/providers/claude/launch.py +50 -0
  35. painapple_code/providers/claude/summary.py +89 -0
  36. painapple_code/providers/claude/translate.py +74 -0
  37. painapple_code/providers/claude_sdk/__init__.py +139 -0
  38. painapple_code/providers/claude_sdk/driver.py +412 -0
  39. painapple_code/providers/codex/__init__.py +87 -0
  40. painapple_code/providers/codex/capabilities.py +321 -0
  41. painapple_code/providers/codex/errors.py +70 -0
  42. painapple_code/providers/codex/launch.py +142 -0
  43. painapple_code/providers/codex/summary.py +196 -0
  44. painapple_code/providers/codex/translate.py +204 -0
  45. painapple_code/providers/codex_app_server/__init__.py +100 -0
  46. painapple_code/providers/codex_app_server/capabilities.py +41 -0
  47. painapple_code/providers/codex_app_server/launch.py +202 -0
  48. painapple_code/providers/codex_app_server/summary.py +291 -0
  49. painapple_code/providers/codex_app_server/translate.py +263 -0
  50. painapple_code/providers/codex_app_server/transport.py +238 -0
  51. painapple_code/routes/__init__.py +0 -0
  52. painapple_code/routes/api_agents.py +441 -0
  53. painapple_code/routes/api_app.py +368 -0
  54. painapple_code/routes/api_app_commit_sections.py +127 -0
  55. painapple_code/routes/api_app_config.py +997 -0
  56. painapple_code/routes/api_app_session_prefs.py +217 -0
  57. painapple_code/routes/api_browser.py +690 -0
  58. painapple_code/routes/api_commands.py +374 -0
  59. painapple_code/routes/api_costs.py +128 -0
  60. painapple_code/routes/api_drafts.py +139 -0
  61. painapple_code/routes/api_exec.py +149 -0
  62. painapple_code/routes/api_files.py +542 -0
  63. painapple_code/routes/api_git.py +657 -0
  64. painapple_code/routes/api_logs.py +649 -0
  65. painapple_code/routes/api_plugins.py +275 -0
  66. painapple_code/routes/api_project_config.py +418 -0
  67. painapple_code/routes/api_prompts.py +231 -0
  68. painapple_code/routes/api_providers.py +53 -0
  69. painapple_code/routes/api_search.py +411 -0
  70. painapple_code/routes/api_session_stash.py +234 -0
  71. painapple_code/routes/api_session_welcome.py +151 -0
  72. painapple_code/routes/api_sessions.py +742 -0
  73. painapple_code/routes/api_shadow.py +323 -0
  74. painapple_code/routes/api_shadow_db.py +512 -0
  75. painapple_code/routes/api_shadow_files.py +332 -0
  76. painapple_code/routes/api_shadow_search.py +476 -0
  77. painapple_code/routes/api_skills.py +610 -0
  78. painapple_code/routes/api_tasks.py +147 -0
  79. painapple_code/routes/api_terminal.py +540 -0
  80. painapple_code/routes/api_upload.py +293 -0
  81. painapple_code/routes/api_viewer.py +229 -0
  82. painapple_code/routes/dependencies.py +109 -0
  83. painapple_code/routes/ws_chat.py +837 -0
  84. painapple_code/server.py +2208 -0
  85. painapple_code/server_logging.py +263 -0
  86. painapple_code/services/__init__.py +0 -0
  87. painapple_code/services/agent_session.py +2738 -0
  88. painapple_code/session_store.py +253 -0
  89. painapple_code/session_store_core.py +879 -0
  90. painapple_code/shadow_db.py +830 -0
  91. painapple_code/shadow_db_plans.py +369 -0
  92. painapple_code/shadow_db_queries.py +595 -0
  93. painapple_code/shadow_db_schema.py +676 -0
  94. painapple_code/shadow_git.py +1296 -0
  95. painapple_code/shadow_git_frontmatter.py +314 -0
  96. painapple_code/shadow_git_sections.py +520 -0
  97. painapple_code/shadow_git_summary.py +462 -0
  98. painapple_code/shadow_parser.py +560 -0
  99. painapple_code/static/css/00-variables.css +276 -0
  100. painapple_code/static/css/01-base.css +245 -0
  101. painapple_code/static/css/02-tooltips.css +36 -0
  102. painapple_code/static/css/09-left-rail.css +448 -0
  103. painapple_code/static/css/10-header.css +777 -0
  104. painapple_code/static/css/11-connection-bar.css +120 -0
  105. painapple_code/static/css/20-messages.css +750 -0
  106. painapple_code/static/css/21-tool-blocks.css +2340 -0
  107. painapple_code/static/css/22-edit-diff.css +619 -0
  108. painapple_code/static/css/23-thinking.css +1584 -0
  109. painapple_code/static/css/24-background-tasks.css +359 -0
  110. painapple_code/static/css/25-context-block.css +252 -0
  111. painapple_code/static/css/26-session-setup.css +144 -0
  112. painapple_code/static/css/30-markdown.css +854 -0
  113. painapple_code/static/css/40-input.css +2418 -0
  114. painapple_code/static/css/41-autocomplete.css +127 -0
  115. painapple_code/static/css/42-activity-strip.css +97 -0
  116. painapple_code/static/css/43-file-autocomplete.css +121 -0
  117. painapple_code/static/css/44-snippets-autocomplete.css +224 -0
  118. painapple_code/static/css/45-selection-bar.css +433 -0
  119. painapple_code/static/css/46-keyboard-bar.css +119 -0
  120. painapple_code/static/css/47-keyboard-longpress.css +116 -0
  121. painapple_code/static/css/47-token-profile.css +106 -0
  122. painapple_code/static/css/50-welcome.css +3095 -0
  123. painapple_code/static/css/51-modals.css +301 -0
  124. painapple_code/static/css/52-utilities.css +77 -0
  125. painapple_code/static/css/53-image-upload.css +456 -0
  126. painapple_code/static/css/54-todo-list.css +102 -0
  127. painapple_code/static/css/55-file-upload.css +113 -0
  128. painapple_code/static/css/56-chat-navigator.css +218 -0
  129. painapple_code/static/css/56-permission-card.css +332 -0
  130. painapple_code/static/css/56-plan-approval.css +204 -0
  131. painapple_code/static/css/56-question-form.css +602 -0
  132. painapple_code/static/css/57-swipe-indicator.css +95 -0
  133. painapple_code/static/css/58-session-families.css +627 -0
  134. painapple_code/static/css/59-about.css +196 -0
  135. painapple_code/static/css/61-log-explorer.css +723 -0
  136. painapple_code/static/css/62-json-tree.css +489 -0
  137. painapple_code/static/css/62-skills-widget.css +650 -0
  138. painapple_code/static/css/63-commands-widget.css +30 -0
  139. painapple_code/static/css/63-plugins-widget.css +174 -0
  140. painapple_code/static/css/63-terminal.css +516 -0
  141. painapple_code/static/css/64-search-files-widget.css +448 -0
  142. painapple_code/static/css/65-snippets-widget.css +426 -0
  143. painapple_code/static/css/66-active-sessions.css +979 -0
  144. painapple_code/static/css/66-agents-widget.css +265 -0
  145. painapple_code/static/css/66-file-explorer-widget.css +855 -0
  146. painapple_code/static/css/66-git-panel.css +791 -0
  147. painapple_code/static/css/66-history-explorer.css +1496 -0
  148. painapple_code/static/css/67-diff-viewer.css +602 -0
  149. painapple_code/static/css/67-editor-view.css +748 -0
  150. painapple_code/static/css/67-file-preview-widget.css +1873 -0
  151. painapple_code/static/css/68-browser-widget.css +337 -0
  152. painapple_code/static/css/68-compare-wizard.css +355 -0
  153. painapple_code/static/css/68-csv-preview.css +106 -0
  154. painapple_code/static/css/68-diff-preview.css +103 -0
  155. painapple_code/static/css/68-helpers-install-widget.css +491 -0
  156. painapple_code/static/css/68-image-annotate.css +346 -0
  157. painapple_code/static/css/68-terminal-view.css +98 -0
  158. painapple_code/static/css/68-uploads-widget.css +229 -0
  159. painapple_code/static/css/69-cost-analytics.css +537 -0
  160. painapple_code/static/css/70-config-panel.css +3736 -0
  161. painapple_code/static/css/70-excalidraw.css +136 -0
  162. painapple_code/static/css/70-prompt-explorer.css +890 -0
  163. painapple_code/static/css/71-chart.css +111 -0
  164. painapple_code/static/css/72-discussion-sidebar.css +678 -0
  165. painapple_code/static/css/74-debug-logs.css +656 -0
  166. painapple_code/static/css/80-open-dialog.css +146 -0
  167. painapple_code/static/css/80-quick-switcher.css +383 -0
  168. painapple_code/static/css/80-widget-system.css +1763 -0
  169. painapple_code/static/css/81-quick-actions.css +1020 -0
  170. painapple_code/static/css/82-context-popover.css +1233 -0
  171. painapple_code/static/css/83-context-menu.css +339 -0
  172. painapple_code/static/css/85-lazy-loading.css +102 -0
  173. painapple_code/static/css/86-zen-mode.css +708 -0
  174. painapple_code/static/css/87-grid-switcher.css +346 -0
  175. painapple_code/static/css/login.css +198 -0
  176. painapple_code/static/dist/app.bundle.js +7177 -0
  177. painapple_code/static/feature-triage.html +446 -0
  178. painapple_code/static/icons/apple-touch-icon.png +0 -0
  179. painapple_code/static/icons/favicon-32.png +0 -0
  180. painapple_code/static/icons/favicon.ico +0 -0
  181. painapple_code/static/icons/icon-128.png +0 -0
  182. painapple_code/static/icons/icon-144.png +0 -0
  183. painapple_code/static/icons/icon-152.png +0 -0
  184. painapple_code/static/icons/icon-180.png +0 -0
  185. painapple_code/static/icons/icon-192.png +0 -0
  186. painapple_code/static/icons/icon-384.png +0 -0
  187. painapple_code/static/icons/icon-512.png +0 -0
  188. painapple_code/static/icons/icon-72.png +0 -0
  189. painapple_code/static/icons/icon-96.png +0 -0
  190. painapple_code/static/js/action-delegate.js +187 -0
  191. painapple_code/static/js/activity-strip.js +193 -0
  192. painapple_code/static/js/app/context-menus.js +619 -0
  193. painapple_code/static/js/app/file-preview.js +163 -0
  194. painapple_code/static/js/app/init-ui.js +574 -0
  195. painapple_code/static/js/app/input-events.js +561 -0
  196. painapple_code/static/js/app/input-resize.js +313 -0
  197. painapple_code/static/js/app/message-actions.js +419 -0
  198. painapple_code/static/js/app/panels.js +210 -0
  199. painapple_code/static/js/app/rendering-delegators.js +246 -0
  200. painapple_code/static/js/app/server-loading.js +365 -0
  201. painapple_code/static/js/app/session-ops.js +287 -0
  202. painapple_code/static/js/app/session-switch.js +443 -0
  203. painapple_code/static/js/app/settings-helpers.js +280 -0
  204. painapple_code/static/js/app/startup-welcome.js +290 -0
  205. painapple_code/static/js/app-context.js +157 -0
  206. painapple_code/static/js/app.js +2134 -0
  207. painapple_code/static/js/auth-fetch.js +46 -0
  208. painapple_code/static/js/background-tasks.js +274 -0
  209. painapple_code/static/js/boot.js +73 -0
  210. painapple_code/static/js/caret-position.js +171 -0
  211. painapple_code/static/js/chat-navigator.js +710 -0
  212. painapple_code/static/js/chat-search.js +323 -0
  213. painapple_code/static/js/command-executor.js +176 -0
  214. painapple_code/static/js/command-store.js +497 -0
  215. painapple_code/static/js/components.js +1414 -0
  216. painapple_code/static/js/config.js +218 -0
  217. painapple_code/static/js/context-menu.js +740 -0
  218. painapple_code/static/js/controllers/chat-controller.js +4252 -0
  219. painapple_code/static/js/controllers/dialog-controller.js +217 -0
  220. painapple_code/static/js/controllers/tab-controller.js +1828 -0
  221. painapple_code/static/js/controllers/thinking-controller.js +915 -0
  222. painapple_code/static/js/diff-utils.js +486 -0
  223. painapple_code/static/js/drafts-pill.js +95 -0
  224. painapple_code/static/js/editor-view.js +815 -0
  225. painapple_code/static/js/effort-settings.js +430 -0
  226. painapple_code/static/js/feature-triage.js +811 -0
  227. painapple_code/static/js/file-autocomplete.js +516 -0
  228. painapple_code/static/js/file-tabs.js +123 -0
  229. painapple_code/static/js/gestures.js +448 -0
  230. painapple_code/static/js/grid-switcher.js +334 -0
  231. painapple_code/static/js/input-handler.js +1472 -0
  232. painapple_code/static/js/keyboard-bar.js +495 -0
  233. painapple_code/static/js/layout-switcher.js +109 -0
  234. painapple_code/static/js/linkify-utils.js +200 -0
  235. painapple_code/static/js/login.js +141 -0
  236. painapple_code/static/js/open-dialog.js +879 -0
  237. painapple_code/static/js/orphan-terminals.js +373 -0
  238. painapple_code/static/js/path-utils.js +179 -0
  239. painapple_code/static/js/perf-marks.js +102 -0
  240. painapple_code/static/js/permission-settings.js +475 -0
  241. painapple_code/static/js/pointer-input.js +59 -0
  242. painapple_code/static/js/preview/json-tree-menu.js +226 -0
  243. painapple_code/static/js/preview/json-tree-toolbar.js +291 -0
  244. painapple_code/static/js/preview/json-tree.js +457 -0
  245. painapple_code/static/js/preview/preview-breadcrumb.js +387 -0
  246. painapple_code/static/js/preview/preview-edit.js +367 -0
  247. painapple_code/static/js/preview/preview-events.js +361 -0
  248. painapple_code/static/js/preview/preview-history.js +567 -0
  249. painapple_code/static/js/preview/preview-inline-edit.js +869 -0
  250. painapple_code/static/js/preview/preview-poll.js +108 -0
  251. painapple_code/static/js/preview/preview-render.js +572 -0
  252. painapple_code/static/js/preview/preview-search.js +421 -0
  253. painapple_code/static/js/preview/preview-state.js +256 -0
  254. painapple_code/static/js/preview/preview-utils.js +251 -0
  255. painapple_code/static/js/preview-plugins/chart-plugin.js +22 -0
  256. painapple_code/static/js/preview-plugins/csv-plugin.js +188 -0
  257. painapple_code/static/js/preview-plugins/diff-plugin.js +258 -0
  258. painapple_code/static/js/preview-plugins/excalidraw-plugin.js +25 -0
  259. painapple_code/static/js/preview-plugins/html-plugin.js +104 -0
  260. painapple_code/static/js/preview-plugins/image-plugin.js +20 -0
  261. painapple_code/static/js/preview-plugins/index.js +34 -0
  262. painapple_code/static/js/preview-plugins/json-plugin.js +99 -0
  263. painapple_code/static/js/preview-plugins/jsonl-plugin.js +152 -0
  264. painapple_code/static/js/preview-plugins/markdown-plugin.js +266 -0
  265. painapple_code/static/js/preview-plugins/panzoom-plugin.js +63 -0
  266. painapple_code/static/js/preview-plugins/plugin-helpers.js +282 -0
  267. painapple_code/static/js/project-color-picker.js +110 -0
  268. painapple_code/static/js/project-colors.js +163 -0
  269. painapple_code/static/js/prompt-favorites.js +187 -0
  270. painapple_code/static/js/quick-actions-menu.js +1736 -0
  271. painapple_code/static/js/quick-actions-registry.js +1239 -0
  272. painapple_code/static/js/quick-switcher/controller.js +294 -0
  273. painapple_code/static/js/quick-switcher/fuzzy-scorer.js +123 -0
  274. painapple_code/static/js/quick-switcher/index.js +30 -0
  275. painapple_code/static/js/quick-switcher/providers/base-provider.js +51 -0
  276. painapple_code/static/js/quick-switcher/providers/command-provider.js +56 -0
  277. painapple_code/static/js/quick-switcher/providers/file-provider.js +291 -0
  278. painapple_code/static/js/quick-switcher/providers/panel-provider.js +132 -0
  279. painapple_code/static/js/quick-switcher/providers/project-provider.js +361 -0
  280. painapple_code/static/js/quick-switcher/providers/read-files-provider.js +104 -0
  281. painapple_code/static/js/quick-switcher/providers/skills-provider.js +129 -0
  282. painapple_code/static/js/quick-switcher/registry.js +69 -0
  283. painapple_code/static/js/quick-switcher/ui/item.js +54 -0
  284. painapple_code/static/js/quick-switcher/ui/picker.js +464 -0
  285. painapple_code/static/js/recent-opens.js +40 -0
  286. painapple_code/static/js/scroll-manager.js +530 -0
  287. painapple_code/static/js/scroll-state-machine.js +359 -0
  288. painapple_code/static/js/selection/action-bar.js +793 -0
  289. painapple_code/static/js/selection/index.js +15 -0
  290. painapple_code/static/js/selection/selection-handler.js +877 -0
  291. painapple_code/static/js/selection/state.js +181 -0
  292. painapple_code/static/js/session/agent-progress.js +202 -0
  293. painapple_code/static/js/session/handle-agent-message.js +481 -0
  294. painapple_code/static/js/session/handle-message.js +550 -0
  295. painapple_code/static/js/session/interactive.js +587 -0
  296. painapple_code/static/js/session/message-store.js +271 -0
  297. painapple_code/static/js/session/messages.js +253 -0
  298. painapple_code/static/js/session/persistence.js +179 -0
  299. painapple_code/static/js/session/restore.js +136 -0
  300. painapple_code/static/js/session/sync.js +333 -0
  301. painapple_code/static/js/session-container-pool.js +371 -0
  302. painapple_code/static/js/session-setup-panel.js +274 -0
  303. painapple_code/static/js/session.js +1047 -0
  304. painapple_code/static/js/shortcut-hints.js +272 -0
  305. painapple_code/static/js/shortcuts.js +1146 -0
  306. painapple_code/static/js/skills-autocomplete.js +262 -0
  307. painapple_code/static/js/snippets-autocomplete.js +738 -0
  308. painapple_code/static/js/stash-refs-view.js +83 -0
  309. painapple_code/static/js/stash-ui.js +768 -0
  310. painapple_code/static/js/stash.js +785 -0
  311. painapple_code/static/js/status-bar.js +1184 -0
  312. painapple_code/static/js/token-profile.js +274 -0
  313. painapple_code/static/js/tool-renderer-blocks.js +1626 -0
  314. painapple_code/static/js/tool-renderer-thinking.js +813 -0
  315. painapple_code/static/js/tool-renderer.js +1500 -0
  316. painapple_code/static/js/tooltips.js +197 -0
  317. painapple_code/static/js/upload-manager.js +1019 -0
  318. painapple_code/static/js/utils.js +521 -0
  319. painapple_code/static/js/welcome/api.js +363 -0
  320. painapple_code/static/js/welcome/cards.js +626 -0
  321. painapple_code/static/js/welcome/context-menu.js +585 -0
  322. painapple_code/static/js/welcome/families.js +801 -0
  323. painapple_code/static/js/welcome/preview.js +234 -0
  324. painapple_code/static/js/welcome/state.js +129 -0
  325. painapple_code/static/js/welcome.js +1874 -0
  326. painapple_code/static/js/widget-system/base-widget.js +717 -0
  327. painapple_code/static/js/widget-system/device-manager.js +178 -0
  328. painapple_code/static/js/widget-system/event-bus.js +152 -0
  329. painapple_code/static/js/widget-system/icons.js +120 -0
  330. painapple_code/static/js/widget-system/index.js +49 -0
  331. painapple_code/static/js/widget-system/init.js +54 -0
  332. painapple_code/static/js/widget-system/types/bottom-sheet.js +307 -0
  333. painapple_code/static/js/widget-system/types/floating.js +915 -0
  334. painapple_code/static/js/widget-system/types/index.js +40 -0
  335. painapple_code/static/js/widget-system/types/modal.js +147 -0
  336. painapple_code/static/js/widget-system/types/sidebar.js +158 -0
  337. painapple_code/static/js/widget-system/types/tab.js +146 -0
  338. painapple_code/static/js/widget-system/types/top-sheet.js +203 -0
  339. painapple_code/static/js/widget-system/widget-manager.js +1187 -0
  340. painapple_code/static/js/widgets/about-widget.js +260 -0
  341. painapple_code/static/js/widgets/active-sessions-widget.js +760 -0
  342. painapple_code/static/js/widgets/agents-widget.js +897 -0
  343. painapple_code/static/js/widgets/browser-widget.js +579 -0
  344. painapple_code/static/js/widgets/commands-widget.js +694 -0
  345. painapple_code/static/js/widgets/config/commit-sections.js +449 -0
  346. painapple_code/static/js/widgets/config/dir-autocomplete.js +224 -0
  347. painapple_code/static/js/widgets/config/gestures.js +161 -0
  348. painapple_code/static/js/widgets/config/models-tab.js +988 -0
  349. painapple_code/static/js/widgets/config/quick-actions-tab.js +882 -0
  350. painapple_code/static/js/widgets/config/shortcut-editor.js +587 -0
  351. painapple_code/static/js/widgets/config/state.js +517 -0
  352. painapple_code/static/js/widgets/config/system-controls.js +90 -0
  353. painapple_code/static/js/widgets/config-widget.js +1535 -0
  354. painapple_code/static/js/widgets/cost-analytics-widget.js +621 -0
  355. painapple_code/static/js/widgets/debug-widget.js +693 -0
  356. painapple_code/static/js/widgets/diff-viewer-widget.js +1420 -0
  357. painapple_code/static/js/widgets/discussion-widget.js +1107 -0
  358. painapple_code/static/js/widgets/file-explorer-widget.js +2166 -0
  359. painapple_code/static/js/widgets/file-preview-widget.js +861 -0
  360. painapple_code/static/js/widgets/git-widget.js +794 -0
  361. painapple_code/static/js/widgets/helpers-install-widget.js +720 -0
  362. painapple_code/static/js/widgets/history-explorer-widget.js +1804 -0
  363. painapple_code/static/js/widgets/image-annotate-widget.js +951 -0
  364. painapple_code/static/js/widgets/image-preview-widget.js +692 -0
  365. painapple_code/static/js/widgets/index.js +176 -0
  366. painapple_code/static/js/widgets/log-explorer-widget.js +1057 -0
  367. painapple_code/static/js/widgets/plugins-widget.js +305 -0
  368. painapple_code/static/js/widgets/prompt-explorer-widget.js +1119 -0
  369. painapple_code/static/js/widgets/search-files-widget.js +529 -0
  370. painapple_code/static/js/widgets/skills-widget.js +843 -0
  371. painapple_code/static/js/widgets/snippets-widget.js +479 -0
  372. painapple_code/static/js/widgets/sub-agents-widget.js +333 -0
  373. painapple_code/static/js/widgets/tasks-widget.js +379 -0
  374. painapple_code/static/js/widgets/terminal/connection.js +283 -0
  375. painapple_code/static/js/widgets/terminal/gestures.js +357 -0
  376. painapple_code/static/js/widgets/terminal/init.js +642 -0
  377. painapple_code/static/js/widgets/terminal/link-providers.js +532 -0
  378. painapple_code/static/js/widgets/terminal/osc52.js +213 -0
  379. painapple_code/static/js/widgets/terminal/render.js +159 -0
  380. painapple_code/static/js/widgets/terminal/size.js +54 -0
  381. painapple_code/static/js/widgets/terminal/state.js +211 -0
  382. painapple_code/static/js/widgets/terminal/wrap-utils.js +101 -0
  383. painapple_code/static/js/widgets/terminal-widget.js +793 -0
  384. painapple_code/static/js/widgets/uploads-widget.js +461 -0
  385. painapple_code/static/js/widgets/zen-widget.js +1001 -0
  386. painapple_code/static/login.html +51 -0
  387. painapple_code/static/manifest.json +73 -0
  388. painapple_code/static/sw.js +293 -0
  389. painapple_code/static/vendor/LICENSES.txt +769 -0
  390. painapple_code/static/vendor/README.md +103 -0
  391. painapple_code/static/vendor/codemirror.js +35 -0
  392. painapple_code/static/vendor/codemirror.js.LEGAL.txt +304 -0
  393. painapple_code/static/vendor/github-markdown-dark.min.css +1124 -0
  394. painapple_code/static/vendor/highlight-github-dark.min.css +10 -0
  395. painapple_code/static/vendor/highlight-lang-dockerfile.min.js +8 -0
  396. painapple_code/static/vendor/highlight-lang-nginx.min.js +21 -0
  397. painapple_code/static/vendor/highlight-lang-properties.min.js +10 -0
  398. painapple_code/static/vendor/highlight-lang-scala.min.js +28 -0
  399. painapple_code/static/vendor/highlight.min.js +1213 -0
  400. painapple_code/static/vendor/marked.min.js +6 -0
  401. painapple_code/static/vendor/purify.min.js +3 -0
  402. painapple_code/static/vendor/xterm-addon-fit.js +2 -0
  403. painapple_code/static/vendor/xterm.css +209 -0
  404. painapple_code/static/vendor/xterm.js +2 -0
  405. painapple_code/static/web-client.html +635 -0
  406. painapple_code/subprocess_registry.py +535 -0
  407. painapple_code/tls_cert.py +79 -0
  408. painapple_code/tools/agents/shadow-git-helper.md +400 -0
  409. painapple_code/tools/excalidraw-to-svg.js +143 -0
  410. painapple_code/tools/install-helpers.sh +148 -0
  411. painapple_code/tools/shadow-git +320 -0
  412. painapple_code/tools/shadow-query +120 -0
  413. painapple_code/tools/vegalite-to-svg.js +79 -0
  414. painapple_code/turn_query.py +766 -0
  415. painapple_code/turn_tracker.py +221 -0
  416. painapple_code/utils/__init__.py +0 -0
  417. painapple_code/utils/agent_cli.py +306 -0
  418. painapple_code/utils/chart.py +103 -0
  419. painapple_code/utils/excalidraw.py +107 -0
  420. painapple_code/utils/file_lock.py +162 -0
  421. painapple_code/utils/file_paths.py +648 -0
  422. painapple_code/utils/generate_icons.py +251 -0
  423. painapple_code/utils/proc.py +205 -0
  424. painapple_code/utils/pty_backend.py +59 -0
  425. painapple_code/utils/pty_posix.py +160 -0
  426. painapple_code/utils/pty_windows.py +245 -0
  427. painapple_code/utils/token_profiles.py +91 -0
  428. painapple_code/viewer_templates.py +657 -0
  429. painapple_code/welcome_search.py +879 -0
  430. painapple_code-1.0.0.dist-info/METADATA +292 -0
  431. painapple_code-1.0.0.dist-info/RECORD +436 -0
  432. painapple_code-1.0.0.dist-info/WHEEL +5 -0
  433. painapple_code-1.0.0.dist-info/entry_points.txt +2 -0
  434. painapple_code-1.0.0.dist-info/licenses/LICENSE +661 -0
  435. painapple_code-1.0.0.dist-info/licenses/THIRD_PARTY_NOTICES.md +261 -0
  436. painapple_code-1.0.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,234 @@
1
+ """
2
+ Session Stash & Favorites API Routes
3
+
4
+ Endpoints for managing:
5
+ - Session stash (context references for next message)
6
+ - Favorites (bookmarked sessions)
7
+ """
8
+
9
+ import logging
10
+ from pathlib import Path
11
+ from typing import Optional
12
+
13
+ from fastapi import APIRouter, Depends, HTTPException, Request
14
+ from pydantic import BaseModel
15
+
16
+ from painapple_code.session_store import SessionStore
17
+ from painapple_code.routes.dependencies import get_session_store
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+ router = APIRouter(tags=["sessions"])
22
+
23
+
24
+ # ═══════════════════════════════════════════════════════════════════
25
+ # Stash API
26
+ # ═══════════════════════════════════════════════════════════════════
27
+
28
+ @router.get("/api/session/{session_id}/stash")
29
+ async def get_session_stash(session_id: str, store: SessionStore = Depends(get_session_store)):
30
+ """Get all stash items for a session."""
31
+ items = store.get_stash(session_id)
32
+ return {"items": items, "count": len(items)}
33
+
34
+
35
+ @router.post("/api/session/{session_id}/stash")
36
+ async def add_stash_item(session_id: str, request: Request, store: SessionStore = Depends(get_session_store)):
37
+ """Add an item to the session stash."""
38
+ item = await request.json()
39
+ success = store.add_stash_item(session_id, item)
40
+ if not success:
41
+ raise HTTPException(status_code=500, detail="Failed to save stash item")
42
+ return {"success": True, "item": item}
43
+
44
+
45
+ @router.delete("/api/session/{session_id}/stash")
46
+ async def clear_session_stash(session_id: str, scope: str = "all",
47
+ store: SessionStore = Depends(get_session_store)):
48
+ """Clear stash items by scope: 'pending' (default UI clear keeps sent
49
+ history), 'history' (keeps pending), or 'all' (legacy behavior)."""
50
+ if scope not in ("pending", "history", "all"):
51
+ raise HTTPException(status_code=400, detail=f"Unknown scope: {scope}")
52
+ success = store.clear_stash_scope(session_id, scope)
53
+ if not success:
54
+ raise HTTPException(status_code=500, detail="Failed to clear stash")
55
+ return {"success": True, "scope": scope}
56
+
57
+
58
+ class MarkSentRequest(BaseModel):
59
+ item_ids: list[str]
60
+ message_id: Optional[str] = None
61
+ sent_at: Optional[str] = None
62
+ sent_session_id: Optional[str] = None
63
+
64
+
65
+ @router.post("/api/session/{session_id}/stash/mark-sent")
66
+ async def mark_stash_sent(session_id: str, req: MarkSentRequest,
67
+ store: SessionStore = Depends(get_session_store)):
68
+ """Mark stash items as sent — they move to history instead of being
69
+ deleted. Trims sent history beyond the per-session cap."""
70
+ marked, saved = store.mark_stash_sent(
71
+ session_id,
72
+ req.item_ids,
73
+ message_id=req.message_id,
74
+ sent_at=req.sent_at,
75
+ sent_session_id=req.sent_session_id,
76
+ )
77
+ if marked and not saved:
78
+ raise HTTPException(status_code=500, detail="Failed to save stash")
79
+ return {"success": True, "marked": marked}
80
+
81
+
82
+ @router.delete("/api/session/{session_id}/stash/{item_id}")
83
+ async def remove_stash_item(session_id: str, item_id: str, store: SessionStore = Depends(get_session_store)):
84
+ """Remove a specific item from the session stash."""
85
+ success = store.remove_stash_item(session_id, item_id)
86
+ if not success:
87
+ raise HTTPException(status_code=500, detail="Failed to remove stash item")
88
+ return {"success": True}
89
+
90
+
91
+ @router.patch("/api/session/{session_id}/stash/{item_id}")
92
+ async def update_stash_item(session_id: str, item_id: str, request: Request, store: SessionStore = Depends(get_session_store)):
93
+ """Update a stash item (toggle enabled, edit note)."""
94
+ updates = await request.json()
95
+ found, saved = store.update_stash_item(session_id, item_id, updates)
96
+ if not found:
97
+ raise HTTPException(status_code=404, detail="Item not found")
98
+ if not saved:
99
+ raise HTTPException(status_code=500, detail="Failed to save stash update")
100
+
101
+ return {"success": True}
102
+
103
+
104
+ # ═══════════════════════════════════════════════════════════════════
105
+ # Favorites API
106
+ # ═══════════════════════════════════════════════════════════════════
107
+
108
+ class FavoriteRequest(BaseModel):
109
+ note: Optional[str] = None
110
+
111
+
112
+ @router.get("/api/favorites")
113
+ async def get_favorites():
114
+ """Get all favorited sessions with full session metadata."""
115
+ from painapple_code.paths import load_favorites
116
+ from painapple_code.welcome_search import get_welcome_searcher
117
+
118
+ fav_data = load_favorites()
119
+ favorites = fav_data.get("favorites", [])
120
+
121
+ if not favorites:
122
+ return {"favorites": []}
123
+
124
+ searcher = get_welcome_searcher()
125
+ all_sessions_result = await searcher.search("", limit=500)
126
+ all_sessions = {s["session_id"]: s for s in all_sessions_result.get("results", [])}
127
+
128
+ enriched = []
129
+ for fav in favorites:
130
+ session_id = fav["session_id"]
131
+ session = all_sessions.get(session_id)
132
+
133
+ if session:
134
+ enriched.append({
135
+ "session_id": session_id,
136
+ "note": fav.get("note"),
137
+ "added_at": fav.get("added_at"),
138
+ "updated_at": fav.get("updated_at"),
139
+ "project_hash": fav.get("project_hash") or session.get("project_hash"),
140
+ "session": {
141
+ "name": session.get("name") or session.get("project") or "Session",
142
+ "project": session.get("project"),
143
+ "project_path": session.get("project_path"),
144
+ "last_activity": session.get("last_activity"),
145
+ "created_at": session.get("created_at"),
146
+ "summary": session.get("summary"),
147
+ "tags": session.get("tags", []),
148
+ "total_cost": session.get("total_cost", 0),
149
+ "message_count": session.get("message_count", 0),
150
+ }
151
+ })
152
+ else:
153
+ session_data = SessionStore.load(session_id)
154
+ if session_data:
155
+ cwd = session_data.get("cwd", "")
156
+ enriched.append({
157
+ "session_id": session_id,
158
+ "note": fav.get("note"),
159
+ "added_at": fav.get("added_at"),
160
+ "updated_at": fav.get("updated_at"),
161
+ "project_hash": fav.get("project_hash") or session_data.get("project_hash"),
162
+ "session": {
163
+ "name": session_data.get("name") or (Path(cwd).name if cwd else "") or "Session",
164
+ "project": Path(cwd).name if cwd else None,
165
+ "project_path": cwd,
166
+ "last_activity": session_data.get("last_activity"),
167
+ "created_at": session_data.get("created_at"),
168
+ "summary": None,
169
+ "tags": [],
170
+ "total_cost": session_data.get("total_cost", 0),
171
+ "message_count": session_data.get("message_count", 0),
172
+ }
173
+ })
174
+ else:
175
+ enriched.append({
176
+ "session_id": session_id,
177
+ "note": fav.get("note"),
178
+ "added_at": fav.get("added_at"),
179
+ "updated_at": fav.get("updated_at"),
180
+ "project_hash": fav.get("project_hash"),
181
+ "session": None,
182
+ })
183
+
184
+ enriched.sort(key=lambda x: (x.get("session") or {}).get("last_activity") or x.get("added_at") or "", reverse=True)
185
+
186
+ return {"favorites": enriched}
187
+
188
+
189
+ @router.post("/api/favorites/{session_id}")
190
+ async def add_favorite(session_id: str, request: FavoriteRequest = None):
191
+ """Add a session to favorites."""
192
+ from painapple_code.paths import add_favorite as _add_favorite
193
+
194
+ session_data = SessionStore.load(session_id)
195
+ if not session_data:
196
+ raise HTTPException(status_code=404, detail="Session not found")
197
+
198
+ cwd = session_data.get("cwd")
199
+ project_hash = session_data.get("project_hash")
200
+ if not project_hash and cwd:
201
+ from painapple_code.paths import get_project_hash
202
+ project_hash = get_project_hash(cwd)
203
+
204
+ note = request.note if request else None
205
+ added = _add_favorite(session_id, project_hash, note)
206
+
207
+ if added:
208
+ return {"success": True, "session_id": session_id, "note": note}
209
+ else:
210
+ return {"success": False, "message": "Already favorited", "session_id": session_id}
211
+
212
+
213
+ @router.delete("/api/favorites/{session_id}")
214
+ async def remove_favorite_endpoint(session_id: str):
215
+ """Remove a session from favorites."""
216
+ from painapple_code.paths import remove_favorite
217
+
218
+ removed = remove_favorite(session_id)
219
+ if removed:
220
+ return {"success": True, "session_id": session_id}
221
+ else:
222
+ raise HTTPException(status_code=404, detail="Favorite not found")
223
+
224
+
225
+ @router.patch("/api/favorites/{session_id}")
226
+ async def update_favorite(session_id: str, request: FavoriteRequest):
227
+ """Update a favorite's note."""
228
+ from painapple_code.paths import update_favorite_note
229
+
230
+ updated = update_favorite_note(session_id, request.note or "")
231
+ if updated:
232
+ return {"success": True, "session_id": session_id, "note": request.note}
233
+ else:
234
+ raise HTTPException(status_code=404, detail="Favorite not found")
@@ -0,0 +1,151 @@
1
+ """
2
+ Welcome Screen API Routes
3
+
4
+ Endpoints for:
5
+ - Welcome screen data (recent sessions, search, project list)
6
+ """
7
+
8
+ import logging
9
+ from pathlib import Path
10
+
11
+ from fastapi import APIRouter, Request
12
+
13
+
14
+ logger = logging.getLogger(__name__)
15
+
16
+ router = APIRouter(tags=["sessions"])
17
+
18
+
19
+ # ═══════════════════════════════════════════════════════════════════
20
+ # Welcome Screen Endpoints
21
+ # ═══════════════════════════════════════════════════════════════════
22
+
23
+ @router.get("/api/welcome/sessions")
24
+ async def get_welcome_sessions(limit: int = 10):
25
+ """Get recent sessions with enriched shadow git data for the welcome screen."""
26
+ from painapple_code.welcome_search import get_recent_sessions
27
+
28
+ # Fetch extra to account for filtering out empty sessions
29
+ sessions = await get_recent_sessions(limit=limit + 20)
30
+
31
+ # Filter out empty sessions (opened but no messages sent)
32
+ sessions = [
33
+ s for s in sessions
34
+ if s.get("message_count", 0) > 0 or s.get("turn_count", 0) > 0
35
+ ]
36
+
37
+ sessions = sessions[:limit]
38
+
39
+ return {
40
+ "sessions": sessions,
41
+ "total": len(sessions),
42
+ }
43
+
44
+
45
+ @router.post("/api/welcome/search")
46
+ async def search_welcome(request: Request):
47
+ """Smart session search using shadow git archaeology."""
48
+ from painapple_code.welcome_search import get_welcome_searcher
49
+
50
+ body = await request.json()
51
+ query = body.get("query", "").strip()
52
+ limit = body.get("limit", 10)
53
+
54
+ if not query:
55
+ from painapple_code.welcome_search import get_recent_sessions
56
+ sessions = await get_recent_sessions(limit=limit)
57
+ return {
58
+ "query": "",
59
+ "parsed": {"keywords": [], "tags": [], "files": [], "intent": "browse"},
60
+ "results": sessions,
61
+ "total_found": len(sessions),
62
+ "suggestions": [
63
+ "Try: '#bugfix last week'",
64
+ "Try: 'files:server.py'",
65
+ "Try: 'auth bug fix'",
66
+ ],
67
+ }
68
+
69
+ searcher = get_welcome_searcher()
70
+ result = await searcher.search(query=query, limit=limit)
71
+
72
+ return result
73
+
74
+
75
+ @router.get("/api/welcome/projects")
76
+ async def get_welcome_projects(request: Request):
77
+ """Get projects for the quick-start picker, plus workspace siblings the
78
+ user hasn't opened a session in yet."""
79
+ from painapple_code.paths import list_projects, list_workspace_dirs
80
+
81
+ projects = list_projects()
82
+
83
+ enriched = []
84
+ for p in projects:
85
+ enriched.append({
86
+ "path": p["path"],
87
+ "hash": p["hash"],
88
+ "name": Path(p["path"]).name,
89
+ "session_count": p.get("session_count", 0),
90
+ "color": p.get("color"), # custom override, else None (→ hash color)
91
+ })
92
+
93
+ enriched.sort(key=lambda x: x["name"].lower())
94
+
95
+ workspace_root = getattr(request.app.state, "workspace_root", None)
96
+ workspace_dirs: list[dict] = []
97
+ if workspace_root:
98
+ exclude = [p["path"] for p in projects]
99
+ workspace_dirs = list_workspace_dirs(workspace_root, exclude_paths=exclude)
100
+
101
+ return {
102
+ "projects": enriched,
103
+ "workspace_root": workspace_root,
104
+ "workspace_dirs": workspace_dirs,
105
+ }
106
+
107
+
108
+ @router.get("/api/welcome/projects/sessions")
109
+ async def get_project_sessions(path: str, limit: int = 15):
110
+ """Recent sessions for a specific project — drives the quick-switcher drill-in.
111
+
112
+ limit=0 returns ALL non-empty sessions, enriched with shadow-git AI
113
+ summaries — the quick-switcher's search-over-everything path. Positive
114
+ limits stay summary-free so the instant drill-in preview keeps its
115
+ meta.json-only fast path.
116
+ """
117
+ from painapple_code.session_store import SessionStoreV2
118
+ from painapple_code.welcome_search import get_session_summaries
119
+
120
+ # Skip if the project path isn't mounted in this container. Avoids
121
+ # listing sessions the user can't actually resume.
122
+ if not Path(path).is_dir():
123
+ return {"sessions": []}
124
+
125
+ store = SessionStoreV2(path)
126
+ all_sessions = store.list_all()
127
+
128
+ # Drop empty/never-used sessions — they're noise here.
129
+ active = [s for s in all_sessions if s.get("message_count", 0) > 0]
130
+ summaries = {}
131
+ if limit > 0:
132
+ active = active[:limit]
133
+ else:
134
+ summaries = await get_session_summaries(path)
135
+
136
+ return {
137
+ "sessions": [
138
+ {
139
+ "id": s["id"],
140
+ "name": s.get("name", ""),
141
+ "description": s.get("description", ""),
142
+ "summary": summaries.get(s["id"], ""),
143
+ "message_count": s.get("message_count", 0),
144
+ "total_cost": s.get("total_cost", 0),
145
+ "last_activity": s.get("last_activity", ""),
146
+ "model": s.get("model"),
147
+ "provider": s.get("provider"), # None = default engine (claude-sdk)
148
+ }
149
+ for s in active
150
+ ],
151
+ }