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,406 @@
1
+ """
2
+ Helpers - Track install state of bundled CLI/agent helpers.
3
+
4
+ Mirrors the FILES array in `tools/install-helpers.sh`. If you add a new
5
+ helper there, add it here too (and vice versa).
6
+
7
+ Surfaces install/freshness state via `helpers_status()`, used by the
8
+ GET /api/app/helpers/status endpoint to drive the install-prompt UI.
9
+ """
10
+
11
+ import hashlib
12
+ import logging
13
+ import os
14
+ import re
15
+ import shutil
16
+ import stat as stat_mod
17
+ import sys
18
+ from pathlib import Path
19
+
20
+ from painapple_code import PACKAGE_DIR
21
+
22
+ logger = logging.getLogger(__name__)
23
+
24
+ # Source-in-package (relative to PACKAGE_DIR) → install target (absolute, expanded $HOME).
25
+ # Must mirror tools/install-helpers.sh:FILES.
26
+ HELPER_FILES = [
27
+ ("tools/shadow-git", Path.home() / ".local" / "bin" / "shadow-git"),
28
+ ("tools/shadow-query", Path.home() / ".local" / "bin" / "shadow-query"),
29
+ ("tools/agents/shadow-git-helper.md",
30
+ Path.home() / ".claude" / "agents" / "shadow-git-helper.md"),
31
+ ]
32
+
33
+ # Legacy install paths that earlier versions wrote — install + uninstall
34
+ # both clean these up so users don't end up with both old and new agents
35
+ # in `#` autocomplete after a rename. Safe to keep forever.
36
+ LEGACY_TARGETS = [
37
+ Path.home() / ".claude" / "agents" / "shadow-git-researcher.md",
38
+ Path.home() / ".local" / "bin" / "dbq",
39
+ ]
40
+
41
+ # These two helpers are bash scripts, which used to mean "not on Windows".
42
+ # They do work there: Git for Windows ships bash and every coreutil they
43
+ # call (sha256sum, sed, cut, awk...), and anyone using shadow-git already
44
+ # has git. What they can't do is be *invoked* from the PowerShell the
45
+ # terminal tab runs, so on Windows each one also gets a .cmd shim.
46
+ SHELL_SCRIPT_SOURCES = {"tools/shadow-git", "tools/shadow-query"}
47
+
48
+ _NO_BASH_REASON = "needs Git for Windows (bash) — not found"
49
+
50
+
51
+ def find_bash() -> str | None:
52
+ """Absolute path to a bash that can run the shell helpers, or None.
53
+
54
+ On Windows, resolve Git for Windows' own bash from git.exe rather than
55
+ trusting PATH. `C:\\Windows\\System32\\bash.exe` is the WSL launcher: it
56
+ would run the helper inside a Linux filesystem namespace where
57
+ ~/.painapple-code is a DIFFERENT (empty) directory, so the helper would
58
+ succeed while querying nothing. Git ships bash at <root>/bin/bash.exe
59
+ and deliberately keeps it off PATH, so PATH alone finds the wrong one.
60
+ """
61
+ if sys.platform != "win32":
62
+ return shutil.which("bash")
63
+
64
+ git = shutil.which("git")
65
+ if git:
66
+ root = Path(git).parent.parent # ...\Git\cmd\git.exe -> ...\Git
67
+ for cand in (root / "bin" / "bash.exe", root / "usr" / "bin" / "bash.exe"):
68
+ if cand.is_file():
69
+ return str(cand)
70
+
71
+ found = shutil.which("bash")
72
+ if found and Path(found).parent.name.lower() != "system32":
73
+ return found
74
+ return None
75
+
76
+
77
+ def _shim_path(target: Path) -> Path:
78
+ """The .cmd next to `target` that makes it callable from PowerShell."""
79
+ return target.with_name(target.name + ".cmd")
80
+
81
+
82
+ def _shim_body(bash: str, target: Path) -> str:
83
+ """A batch wrapper that hands the script to bash and forwards argv.
84
+
85
+ The bash path is resolved at install time, not runtime: a missing Git
86
+ then fails with a readable message from cmd instead of a silent
87
+ fall-through to whatever else answers to `bash`.
88
+ """
89
+ return (
90
+ "@echo off\r\n"
91
+ "rem Generated by painapple-code helper install. Re-run install to refresh.\r\n"
92
+ f'"{bash}" "{target}" %*\r\n'
93
+ )
94
+
95
+
96
+ def _supported_here(src_rel: str) -> bool:
97
+ if sys.platform == "win32" and src_rel in SHELL_SCRIPT_SOURCES:
98
+ return find_bash() is not None
99
+ return True
100
+
101
+ # The shadow-git-helper agent's model is user-selectable (see
102
+ # paths.get_helper_agent_model). Its `model:` frontmatter line is kept
103
+ # orthogonal to the freshness check so a non-default choice doesn't read as
104
+ # "outdated", and is re-applied after every install (cp -f resets it).
105
+ AGENT_SRC_REL = "tools/agents/shadow-git-helper.md"
106
+ AGENT_TARGET = Path.home() / ".claude" / "agents" / "shadow-git-helper.md"
107
+
108
+ # Matches the leading YAML frontmatter block only (anchored at start, up to the
109
+ # first closing `---`), so example frontmatter later in the body is untouched.
110
+ _FRONTMATTER_RE = re.compile(r"\A(---[ \t]*\r?\n)(.*?\r?\n)(---[ \t]*\r?\n?)", re.DOTALL)
111
+ _MODEL_LINE_RE = re.compile(r"(?im)^[ \t]*model:.*\r?\n?")
112
+
113
+
114
+ def _strip_model_line(text: str) -> str:
115
+ """Remove the `model:` line from the leading frontmatter only."""
116
+ m = _FRONTMATTER_RE.match(text)
117
+ if not m:
118
+ return text
119
+ body = _MODEL_LINE_RE.sub("", m.group(2))
120
+ return m.group(1) + body + m.group(3) + text[m.end():]
121
+
122
+
123
+ def _set_model_line(text: str, model: str) -> str:
124
+ """Set `model: <model>` in the leading frontmatter, replacing any existing."""
125
+ m = _FRONTMATTER_RE.match(text)
126
+ if not m:
127
+ return text
128
+ body = _MODEL_LINE_RE.sub("", m.group(2))
129
+ if body and not body.endswith("\n"):
130
+ body += "\n"
131
+ body += f"model: {model}\n"
132
+ return m.group(1) + body + m.group(3) + text[m.end():]
133
+
134
+
135
+ def read_installed_agent_model() -> str | None:
136
+ """Return the `model:` value parsed from the installed agent file, or None."""
137
+ try:
138
+ text = AGENT_TARGET.read_text(encoding="utf-8")
139
+ except Exception:
140
+ return None
141
+ m = _FRONTMATTER_RE.match(text)
142
+ if not m:
143
+ return None
144
+ mm = re.search(r"(?im)^[ \t]*model:[ \t]*(\S+)", m.group(2))
145
+ return mm.group(1).strip().lower() if mm else None
146
+
147
+
148
+ def _sha256(path: Path) -> str | None:
149
+ """Return hex sha256 of a file's contents, or None if unreadable."""
150
+ try:
151
+ return hashlib.sha256(path.read_bytes()).hexdigest()
152
+ except Exception as e:
153
+ logger.debug(f"sha256 failed for {path}: {e}")
154
+ return None
155
+
156
+
157
+ def _normalized_hash(path: Path) -> str | None:
158
+ """sha256 of file text with the frontmatter `model:` line removed, so the
159
+ user's model choice does not read as a freshness mismatch."""
160
+ try:
161
+ text = path.read_text(encoding="utf-8")
162
+ except Exception as e:
163
+ logger.debug(f"read failed for {path}: {e}")
164
+ return None
165
+ return hashlib.sha256(_strip_model_line(text).encode("utf-8")).hexdigest()
166
+
167
+
168
+ def helpers_status() -> dict:
169
+ """
170
+ Per-file install + freshness state, plus aggregate flags.
171
+
172
+ Returns:
173
+ {
174
+ "files": [
175
+ {"name": "shadow-git", "src": "tools/shadow-git", "target": "/...",
176
+ "installed": bool, "up_to_date": bool},
177
+ ...
178
+ ],
179
+ "all_installed": bool, # every target exists
180
+ "all_current": bool, # every target exists AND matches source hash
181
+ "any_outdated": bool, # at least one installed-but-stale file
182
+ }
183
+ """
184
+ files = []
185
+ all_installed = True
186
+ all_current = True
187
+ any_outdated = False
188
+
189
+ for src_rel, target in HELPER_FILES:
190
+ src_abs = PACKAGE_DIR / src_rel
191
+ installed = target.exists()
192
+ up_to_date = False
193
+
194
+ if not _supported_here(src_rel):
195
+ # Don't let a helper we deliberately never install drag
196
+ # all_installed/all_current to False forever — that would
197
+ # show a permanent "helpers outdated" nag on Windows.
198
+ files.append({
199
+ "name": target.name,
200
+ "src": src_rel,
201
+ "target": str(target),
202
+ "installed": False,
203
+ "up_to_date": False,
204
+ "unsupported": True,
205
+ "unsupported_reason": _NO_BASH_REASON,
206
+ })
207
+ continue
208
+
209
+ if installed:
210
+ if src_rel == AGENT_SRC_REL:
211
+ # Model choice is orthogonal to freshness — compare content
212
+ # with the `model:` frontmatter line stripped from both sides.
213
+ src_hash = _normalized_hash(src_abs)
214
+ tgt_hash = _normalized_hash(target)
215
+ else:
216
+ src_hash = _sha256(src_abs)
217
+ tgt_hash = _sha256(target)
218
+ up_to_date = src_hash is not None and src_hash == tgt_hash
219
+ if (up_to_date and sys.platform == "win32"
220
+ and src_rel in SHELL_SCRIPT_SOURCES
221
+ and not _shim_path(target).exists()):
222
+ # Script content is current but the .cmd is missing — an
223
+ # install from before shims existed. It's unreachable from
224
+ # the terminal's PowerShell, so surface it as stale and let
225
+ # the normal Install button regenerate the wrapper.
226
+ up_to_date = False
227
+ if not up_to_date:
228
+ any_outdated = True
229
+ else:
230
+ all_installed = False
231
+
232
+ if not (installed and up_to_date):
233
+ all_current = False
234
+
235
+ files.append({
236
+ "name": target.name,
237
+ "src": src_rel,
238
+ "target": str(target),
239
+ "installed": installed,
240
+ "up_to_date": up_to_date,
241
+ })
242
+
243
+ from painapple_code import paths
244
+
245
+ return {
246
+ "files": files,
247
+ "all_installed": all_installed,
248
+ "all_current": all_current,
249
+ "any_outdated": any_outdated,
250
+ # How to actually invoke the helper in THIS platform's terminal.
251
+ # PowerShell resolves a bare name through PATHEXT and finds the
252
+ # .cmd, but `~/.local/bin/shadow-git` (no extension) silently does
253
+ # nothing there — it exits 0 having run no command at all, which is
254
+ # worse than an error. POSIX shells take the explicit path.
255
+ "usage_command": ("shadow-git log" if sys.platform == "win32"
256
+ else "~/.local/bin/shadow-git log"),
257
+ # Model the shadow-git-helper subagent runs on (full model ID or
258
+ # "inherit"); options mirror the main model selector + "inherit".
259
+ "agent_model": paths.get_helper_agent_model(),
260
+ "agent_model_options": paths.get_helper_agent_options(),
261
+ }
262
+
263
+
264
+ def install_helpers_script_path() -> Path:
265
+ """Return the path to tools/install-helpers.sh — the equivalent shell
266
+ script users can run by hand. install_helpers() below does the same
267
+ work in-process; this stays for docs and manual installs."""
268
+ return PACKAGE_DIR / "tools" / "install-helpers.sh"
269
+
270
+
271
+ def install_helpers(force: bool = True) -> dict:
272
+ """Copy the bundled helpers to their user-level targets.
273
+
274
+ A Python port of tools/install-helpers.sh --update (mkdir -p, cp -f,
275
+ chmod 755 when the source is executable). The endpoint used to shell
276
+ out to `bash`, which simply isn't there on a stock Windows box — and
277
+ uninstall was already pure Python, so the two halves now match.
278
+
279
+ Returns the same {ok, exit_code, stdout, stderr} shape the route
280
+ reports, so the frontend is unchanged.
281
+ """
282
+ lines = [f"painapple-code helpers — install (pkg: {PACKAGE_DIR})", ""]
283
+ errors = []
284
+
285
+ bash = find_bash() if sys.platform == "win32" else None
286
+
287
+ for src_rel, target in HELPER_FILES:
288
+ if not _supported_here(src_rel):
289
+ lines.append(f" skip {target} ({_NO_BASH_REASON})")
290
+ continue
291
+ src_abs = PACKAGE_DIR / src_rel
292
+ if not src_abs.is_file():
293
+ errors.append(f"source missing: {src_abs}")
294
+ continue
295
+ existed = target.exists()
296
+ if existed and not force:
297
+ lines.append(f" skip {target} (exists — use --update to overwrite)")
298
+ continue
299
+ try:
300
+ target.parent.mkdir(parents=True, exist_ok=True)
301
+ # copyfile, not copy2: we deliberately do not carry over source
302
+ # mtimes (the freshness check hashes content instead).
303
+ shutil.copyfile(src_abs, target)
304
+ if os.access(src_abs, os.X_OK):
305
+ target.chmod(0o755) # mirrors the script's `chmod 755`
306
+ lines.append(f" {'update' if existed else 'install'} {target}")
307
+ if bash and src_rel in SHELL_SCRIPT_SOURCES:
308
+ # PowerShell (what the terminal tab runs) can't execute an
309
+ # extensionless bash script; the .cmd is what makes
310
+ # `shadow-git log` work there the way it does on a POSIX box.
311
+ shim = _shim_path(target)
312
+ shim.write_text(_shim_body(bash, target), encoding="ascii")
313
+ lines.append(f" {'update' if existed else 'install'} {shim}")
314
+ except OSError as e:
315
+ logger.warning(f"install: failed to write {target}: {e}")
316
+ errors.append(f"{target}: {e}")
317
+
318
+ for legacy in LEGACY_TARGETS:
319
+ if legacy.exists():
320
+ try:
321
+ legacy.unlink()
322
+ lines.append(f" remove {legacy} (legacy)")
323
+ except OSError as e:
324
+ errors.append(f"{legacy}: {e}")
325
+
326
+ return {
327
+ "ok": not errors,
328
+ "exit_code": 0 if not errors else 1,
329
+ "stdout": "\n".join(lines) + "\n",
330
+ "stderr": "\n".join(errors),
331
+ }
332
+
333
+
334
+ def uninstall_helpers() -> dict:
335
+ """
336
+ Remove every installed helper target from disk, plus any legacy
337
+ targets from earlier installs (renamed/moved files). Idempotent —
338
+ missing targets are skipped, not errors.
339
+
340
+ Returns:
341
+ {
342
+ "ok": bool, # True if every target is gone after the call
343
+ "removed": [str], # paths actually deleted this call
344
+ "missing": [str], # paths that were already absent
345
+ "errors": [{"path": str, "error": str}],
346
+ }
347
+ """
348
+ removed: list[str] = []
349
+ missing: list[str] = []
350
+ errors: list[dict] = []
351
+
352
+ targets = [t for _, t in HELPER_FILES] + LEGACY_TARGETS
353
+ if sys.platform == "win32":
354
+ # Remove what install could have created — the PowerShell shims.
355
+ targets += [_shim_path(t) for src, t in HELPER_FILES
356
+ if src in SHELL_SCRIPT_SOURCES]
357
+ for target in targets:
358
+ if not target.exists():
359
+ missing.append(str(target))
360
+ continue
361
+ try:
362
+ target.unlink()
363
+ removed.append(str(target))
364
+ except Exception as e:
365
+ logger.warning(f"uninstall: failed to remove {target}: {e}")
366
+ errors.append({"path": str(target), "error": str(e)})
367
+
368
+ return {
369
+ "ok": not errors,
370
+ "removed": removed,
371
+ "missing": missing,
372
+ "errors": errors,
373
+ }
374
+
375
+
376
+ def apply_agent_model(model: str) -> bool:
377
+ """Write `model: <model>` into the installed shadow-git-helper frontmatter.
378
+
379
+ Returns True when the installed file exists and now carries that model
380
+ (whether it needed a write or was already correct), False otherwise.
381
+ """
382
+ if not AGENT_TARGET.exists():
383
+ return False
384
+ try:
385
+ text = AGENT_TARGET.read_text(encoding="utf-8")
386
+ except Exception as e:
387
+ logger.warning(f"apply_agent_model: read failed: {e}")
388
+ return False
389
+ new_text = _set_model_line(text, model)
390
+ if new_text != text:
391
+ try:
392
+ AGENT_TARGET.write_text(new_text, encoding="utf-8")
393
+ except Exception as e:
394
+ logger.warning(f"apply_agent_model: write failed: {e}")
395
+ return False
396
+ return True
397
+
398
+
399
+ def apply_agent_model_from_config() -> bool:
400
+ """Re-apply the persisted model choice to the installed agent file.
401
+
402
+ Called after install-helpers.sh, whose `cp -f` overwrites the installed
403
+ copy with the bundled default (model: sonnet).
404
+ """
405
+ from painapple_code import paths
406
+ return apply_agent_model(paths.get_helper_agent_model())