painapple-code 1.0.0rc1__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 (483) hide show
  1. painapple_code-1.0.0rc1/.devcontainer/devcontainer.json +44 -0
  2. painapple_code-1.0.0rc1/.dockerignore +19 -0
  3. painapple_code-1.0.0rc1/.github/workflows/docker-publish.yml +187 -0
  4. painapple_code-1.0.0rc1/.github/workflows/pypi-publish.yml +94 -0
  5. painapple_code-1.0.0rc1/.github/workflows/release-features.yml +38 -0
  6. painapple_code-1.0.0rc1/.github/workflows/test-features.yml +33 -0
  7. painapple_code-1.0.0rc1/.gitignore +80 -0
  8. painapple_code-1.0.0rc1/Dockerfile +155 -0
  9. painapple_code-1.0.0rc1/LICENSE +661 -0
  10. painapple_code-1.0.0rc1/PKG-INFO +382 -0
  11. painapple_code-1.0.0rc1/README.dockerhub.md +164 -0
  12. painapple_code-1.0.0rc1/README.md +344 -0
  13. painapple_code-1.0.0rc1/docker-compose.yml +50 -0
  14. painapple_code-1.0.0rc1/docker-entrypoint.sh +53 -0
  15. painapple_code-1.0.0rc1/examples/devcontainer/README.md +76 -0
  16. painapple_code-1.0.0rc1/examples/devcontainer/devcontainer.json +30 -0
  17. painapple_code-1.0.0rc1/examples/personalize/README.md +84 -0
  18. painapple_code-1.0.0rc1/examples/personalize/devcontainer.json +30 -0
  19. painapple_code-1.0.0rc1/examples/personalize-dockerfile/Dockerfile +38 -0
  20. painapple_code-1.0.0rc1/examples/personalize-dockerfile/README.md +86 -0
  21. painapple_code-1.0.0rc1/features/src/painapple-code/README.md +179 -0
  22. painapple_code-1.0.0rc1/features/src/painapple-code/devcontainer-feature.json +53 -0
  23. painapple_code-1.0.0rc1/features/src/painapple-code/install.sh +391 -0
  24. painapple_code-1.0.0rc1/features/test/painapple-code/test.sh +65 -0
  25. painapple_code-1.0.0rc1/painapple-docker.sh +2584 -0
  26. painapple_code-1.0.0rc1/pyproject.toml +69 -0
  27. painapple_code-1.0.0rc1/requirements.txt +11 -0
  28. painapple_code-1.0.0rc1/setup.cfg +4 -0
  29. painapple_code-1.0.0rc1/src/painapple_code/__init__.py +30 -0
  30. painapple_code-1.0.0rc1/src/painapple_code/__main__.py +13 -0
  31. painapple_code-1.0.0rc1/src/painapple_code/_version.py +24 -0
  32. painapple_code-1.0.0rc1/src/painapple_code/auth_middleware.py +507 -0
  33. painapple_code-1.0.0rc1/src/painapple_code/bridge_paths.py +1028 -0
  34. painapple_code-1.0.0rc1/src/painapple_code/cli/__init__.py +32 -0
  35. painapple_code-1.0.0rc1/src/painapple_code/cli/docker/__init__.py +116 -0
  36. painapple_code-1.0.0rc1/src/painapple_code/cli/docker/commands.py +757 -0
  37. painapple_code-1.0.0rc1/src/painapple_code/cli/docker/config.py +364 -0
  38. painapple_code-1.0.0rc1/src/painapple_code/cli/docker/runtime.py +88 -0
  39. painapple_code-1.0.0rc1/src/painapple_code/cli/docker/wizard.py +415 -0
  40. painapple_code-1.0.0rc1/src/painapple_code/cli/ui.py +404 -0
  41. painapple_code-1.0.0rc1/src/painapple_code/cost_analytics.py +884 -0
  42. painapple_code-1.0.0rc1/src/painapple_code/data/models.defaults.yaml +15 -0
  43. painapple_code-1.0.0rc1/src/painapple_code/data/models.yaml +15 -0
  44. painapple_code-1.0.0rc1/src/painapple_code/data/presets.defaults.json +32 -0
  45. painapple_code-1.0.0rc1/src/painapple_code/data/strings.yaml +1573 -0
  46. painapple_code-1.0.0rc1/src/painapple_code/helpers.py +140 -0
  47. painapple_code-1.0.0rc1/src/painapple_code/prompt_explorer.py +984 -0
  48. painapple_code-1.0.0rc1/src/painapple_code/providers/__init__.py +215 -0
  49. painapple_code-1.0.0rc1/src/painapple_code/providers/base.py +515 -0
  50. painapple_code-1.0.0rc1/src/painapple_code/providers/claude/__init__.py +69 -0
  51. painapple_code-1.0.0rc1/src/painapple_code/providers/claude/capabilities.py +167 -0
  52. painapple_code-1.0.0rc1/src/painapple_code/providers/claude/errors.py +66 -0
  53. painapple_code-1.0.0rc1/src/painapple_code/providers/claude/launch.py +47 -0
  54. painapple_code-1.0.0rc1/src/painapple_code/providers/claude/summary.py +83 -0
  55. painapple_code-1.0.0rc1/src/painapple_code/providers/claude/translate.py +61 -0
  56. painapple_code-1.0.0rc1/src/painapple_code/routes/__init__.py +0 -0
  57. painapple_code-1.0.0rc1/src/painapple_code/routes/api_agents.py +440 -0
  58. painapple_code-1.0.0rc1/src/painapple_code/routes/api_bridge.py +132 -0
  59. painapple_code-1.0.0rc1/src/painapple_code/routes/api_bridge_commit_sections.py +127 -0
  60. painapple_code-1.0.0rc1/src/painapple_code/routes/api_bridge_config.py +506 -0
  61. painapple_code-1.0.0rc1/src/painapple_code/routes/api_bridge_session_prefs.py +212 -0
  62. painapple_code-1.0.0rc1/src/painapple_code/routes/api_browser.py +521 -0
  63. painapple_code-1.0.0rc1/src/painapple_code/routes/api_commands.py +368 -0
  64. painapple_code-1.0.0rc1/src/painapple_code/routes/api_costs.py +128 -0
  65. painapple_code-1.0.0rc1/src/painapple_code/routes/api_exec.py +50 -0
  66. painapple_code-1.0.0rc1/src/painapple_code/routes/api_files.py +493 -0
  67. painapple_code-1.0.0rc1/src/painapple_code/routes/api_git.py +651 -0
  68. painapple_code-1.0.0rc1/src/painapple_code/routes/api_logs.py +532 -0
  69. painapple_code-1.0.0rc1/src/painapple_code/routes/api_plugins.py +273 -0
  70. painapple_code-1.0.0rc1/src/painapple_code/routes/api_project_config.py +294 -0
  71. painapple_code-1.0.0rc1/src/painapple_code/routes/api_prompts.py +231 -0
  72. painapple_code-1.0.0rc1/src/painapple_code/routes/api_session_stash.py +205 -0
  73. painapple_code-1.0.0rc1/src/painapple_code/routes/api_session_welcome.py +138 -0
  74. painapple_code-1.0.0rc1/src/painapple_code/routes/api_sessions.py +540 -0
  75. painapple_code-1.0.0rc1/src/painapple_code/routes/api_shadow.py +323 -0
  76. painapple_code-1.0.0rc1/src/painapple_code/routes/api_shadow_db.py +473 -0
  77. painapple_code-1.0.0rc1/src/painapple_code/routes/api_shadow_files.py +327 -0
  78. painapple_code-1.0.0rc1/src/painapple_code/routes/api_shadow_search.py +476 -0
  79. painapple_code-1.0.0rc1/src/painapple_code/routes/api_skills.py +609 -0
  80. painapple_code-1.0.0rc1/src/painapple_code/routes/api_tasks.py +138 -0
  81. painapple_code-1.0.0rc1/src/painapple_code/routes/api_terminal.py +589 -0
  82. painapple_code-1.0.0rc1/src/painapple_code/routes/api_upload.py +213 -0
  83. painapple_code-1.0.0rc1/src/painapple_code/routes/api_viewer.py +188 -0
  84. painapple_code-1.0.0rc1/src/painapple_code/routes/dependencies.py +17 -0
  85. painapple_code-1.0.0rc1/src/painapple_code/routes/ws_chat.py +679 -0
  86. painapple_code-1.0.0rc1/src/painapple_code/server.py +1349 -0
  87. painapple_code-1.0.0rc1/src/painapple_code/server_logging.py +242 -0
  88. painapple_code-1.0.0rc1/src/painapple_code/services/__init__.py +0 -0
  89. painapple_code-1.0.0rc1/src/painapple_code/services/agent_session.py +2236 -0
  90. painapple_code-1.0.0rc1/src/painapple_code/session_store.py +254 -0
  91. painapple_code-1.0.0rc1/src/painapple_code/session_store_core.py +820 -0
  92. painapple_code-1.0.0rc1/src/painapple_code/session_store_migration.py +144 -0
  93. painapple_code-1.0.0rc1/src/painapple_code/shadow_db.py +760 -0
  94. painapple_code-1.0.0rc1/src/painapple_code/shadow_db_plans.py +369 -0
  95. painapple_code-1.0.0rc1/src/painapple_code/shadow_db_queries.py +595 -0
  96. painapple_code-1.0.0rc1/src/painapple_code/shadow_db_schema.py +676 -0
  97. painapple_code-1.0.0rc1/src/painapple_code/shadow_git.py +1133 -0
  98. painapple_code-1.0.0rc1/src/painapple_code/shadow_git_frontmatter.py +314 -0
  99. painapple_code-1.0.0rc1/src/painapple_code/shadow_git_sections.py +520 -0
  100. painapple_code-1.0.0rc1/src/painapple_code/shadow_git_summary.py +457 -0
  101. painapple_code-1.0.0rc1/src/painapple_code/shadow_parser.py +560 -0
  102. painapple_code-1.0.0rc1/src/painapple_code/static/css/00-variables.css +177 -0
  103. painapple_code-1.0.0rc1/src/painapple_code/static/css/01-base.css +150 -0
  104. painapple_code-1.0.0rc1/src/painapple_code/static/css/02-tooltips.css +36 -0
  105. painapple_code-1.0.0rc1/src/painapple_code/static/css/09-left-rail.css +311 -0
  106. painapple_code-1.0.0rc1/src/painapple_code/static/css/10-header.css +691 -0
  107. painapple_code-1.0.0rc1/src/painapple_code/static/css/11-connection-bar.css +120 -0
  108. painapple_code-1.0.0rc1/src/painapple_code/static/css/20-messages.css +739 -0
  109. painapple_code-1.0.0rc1/src/painapple_code/static/css/21-tool-blocks.css +2326 -0
  110. painapple_code-1.0.0rc1/src/painapple_code/static/css/22-edit-diff.css +574 -0
  111. painapple_code-1.0.0rc1/src/painapple_code/static/css/23-thinking.css +1570 -0
  112. painapple_code-1.0.0rc1/src/painapple_code/static/css/24-background-tasks.css +355 -0
  113. painapple_code-1.0.0rc1/src/painapple_code/static/css/25-context-block.css +252 -0
  114. painapple_code-1.0.0rc1/src/painapple_code/static/css/30-markdown.css +531 -0
  115. painapple_code-1.0.0rc1/src/painapple_code/static/css/40-input.css +1955 -0
  116. painapple_code-1.0.0rc1/src/painapple_code/static/css/41-autocomplete.css +127 -0
  117. painapple_code-1.0.0rc1/src/painapple_code/static/css/42-activity-strip.css +88 -0
  118. painapple_code-1.0.0rc1/src/painapple_code/static/css/43-file-autocomplete.css +136 -0
  119. painapple_code-1.0.0rc1/src/painapple_code/static/css/44-snippets-autocomplete.css +237 -0
  120. painapple_code-1.0.0rc1/src/painapple_code/static/css/45-selection-bar.css +362 -0
  121. painapple_code-1.0.0rc1/src/painapple_code/static/css/46-keyboard-bar.css +119 -0
  122. painapple_code-1.0.0rc1/src/painapple_code/static/css/47-keyboard-longpress.css +116 -0
  123. painapple_code-1.0.0rc1/src/painapple_code/static/css/47-token-profile.css +105 -0
  124. painapple_code-1.0.0rc1/src/painapple_code/static/css/50-welcome.css +2969 -0
  125. painapple_code-1.0.0rc1/src/painapple_code/static/css/51-modals.css +294 -0
  126. painapple_code-1.0.0rc1/src/painapple_code/static/css/52-utilities.css +77 -0
  127. painapple_code-1.0.0rc1/src/painapple_code/static/css/53-image-upload.css +415 -0
  128. painapple_code-1.0.0rc1/src/painapple_code/static/css/54-todo-list.css +102 -0
  129. painapple_code-1.0.0rc1/src/painapple_code/static/css/55-file-upload.css +113 -0
  130. painapple_code-1.0.0rc1/src/painapple_code/static/css/56-chat-navigator.css +218 -0
  131. painapple_code-1.0.0rc1/src/painapple_code/static/css/56-plan-approval.css +204 -0
  132. painapple_code-1.0.0rc1/src/painapple_code/static/css/56-question-form.css +496 -0
  133. painapple_code-1.0.0rc1/src/painapple_code/static/css/57-swipe-indicator.css +95 -0
  134. painapple_code-1.0.0rc1/src/painapple_code/static/css/58-session-families.css +626 -0
  135. painapple_code-1.0.0rc1/src/painapple_code/static/css/61-log-explorer.css +683 -0
  136. painapple_code-1.0.0rc1/src/painapple_code/static/css/62-json-tree.css +411 -0
  137. painapple_code-1.0.0rc1/src/painapple_code/static/css/62-skills-widget.css +648 -0
  138. painapple_code-1.0.0rc1/src/painapple_code/static/css/63-commands-widget.css +30 -0
  139. painapple_code-1.0.0rc1/src/painapple_code/static/css/63-plugins-widget.css +174 -0
  140. painapple_code-1.0.0rc1/src/painapple_code/static/css/63-terminal.css +514 -0
  141. painapple_code-1.0.0rc1/src/painapple_code/static/css/65-snippets-widget.css +425 -0
  142. painapple_code-1.0.0rc1/src/painapple_code/static/css/66-active-sessions.css +979 -0
  143. painapple_code-1.0.0rc1/src/painapple_code/static/css/66-agents-widget.css +265 -0
  144. painapple_code-1.0.0rc1/src/painapple_code/static/css/66-file-explorer-widget.css +854 -0
  145. painapple_code-1.0.0rc1/src/painapple_code/static/css/66-git-panel.css +795 -0
  146. painapple_code-1.0.0rc1/src/painapple_code/static/css/67-diff-viewer.css +576 -0
  147. painapple_code-1.0.0rc1/src/painapple_code/static/css/67-editor-view.css +675 -0
  148. painapple_code-1.0.0rc1/src/painapple_code/static/css/67-file-preview-widget.css +1317 -0
  149. painapple_code-1.0.0rc1/src/painapple_code/static/css/68-browser-widget.css +254 -0
  150. painapple_code-1.0.0rc1/src/painapple_code/static/css/68-compare-wizard.css +355 -0
  151. painapple_code-1.0.0rc1/src/painapple_code/static/css/68-csv-preview.css +106 -0
  152. painapple_code-1.0.0rc1/src/painapple_code/static/css/68-helpers-install-widget.css +437 -0
  153. painapple_code-1.0.0rc1/src/painapple_code/static/css/68-terminal-view.css +98 -0
  154. painapple_code-1.0.0rc1/src/painapple_code/static/css/68-uploads-widget.css +163 -0
  155. painapple_code-1.0.0rc1/src/painapple_code/static/css/69-cost-analytics.css +537 -0
  156. painapple_code-1.0.0rc1/src/painapple_code/static/css/70-config-panel.css +3227 -0
  157. painapple_code-1.0.0rc1/src/painapple_code/static/css/70-excalidraw.css +136 -0
  158. painapple_code-1.0.0rc1/src/painapple_code/static/css/70-prompt-explorer.css +782 -0
  159. painapple_code-1.0.0rc1/src/painapple_code/static/css/71-chart.css +111 -0
  160. painapple_code-1.0.0rc1/src/painapple_code/static/css/72-discussion-sidebar.css +685 -0
  161. painapple_code-1.0.0rc1/src/painapple_code/static/css/74-debug-logs.css +656 -0
  162. painapple_code-1.0.0rc1/src/painapple_code/static/css/76-history-explorer.css +680 -0
  163. painapple_code-1.0.0rc1/src/painapple_code/static/css/80-open-dialog.css +146 -0
  164. painapple_code-1.0.0rc1/src/painapple_code/static/css/80-quick-switcher.css +260 -0
  165. painapple_code-1.0.0rc1/src/painapple_code/static/css/80-widget-system.css +1733 -0
  166. painapple_code-1.0.0rc1/src/painapple_code/static/css/81-quick-actions.css +1011 -0
  167. painapple_code-1.0.0rc1/src/painapple_code/static/css/82-context-popover.css +1180 -0
  168. painapple_code-1.0.0rc1/src/painapple_code/static/css/83-context-menu.css +300 -0
  169. painapple_code-1.0.0rc1/src/painapple_code/static/css/85-lazy-loading.css +102 -0
  170. painapple_code-1.0.0rc1/src/painapple_code/static/css/86-zen-mode.css +708 -0
  171. painapple_code-1.0.0rc1/src/painapple_code/static/css/87-grid-switcher.css +341 -0
  172. painapple_code-1.0.0rc1/src/painapple_code/static/css/login.css +198 -0
  173. painapple_code-1.0.0rc1/src/painapple_code/static/feature-triage.html +1208 -0
  174. painapple_code-1.0.0rc1/src/painapple_code/static/icons/apple-touch-icon.png +0 -0
  175. painapple_code-1.0.0rc1/src/painapple_code/static/icons/favicon-32.png +0 -0
  176. painapple_code-1.0.0rc1/src/painapple_code/static/icons/favicon.ico +0 -0
  177. painapple_code-1.0.0rc1/src/painapple_code/static/icons/icon-128.png +0 -0
  178. painapple_code-1.0.0rc1/src/painapple_code/static/icons/icon-144.png +0 -0
  179. painapple_code-1.0.0rc1/src/painapple_code/static/icons/icon-152.png +0 -0
  180. painapple_code-1.0.0rc1/src/painapple_code/static/icons/icon-180.png +0 -0
  181. painapple_code-1.0.0rc1/src/painapple_code/static/icons/icon-192.png +0 -0
  182. painapple_code-1.0.0rc1/src/painapple_code/static/icons/icon-384.png +0 -0
  183. painapple_code-1.0.0rc1/src/painapple_code/static/icons/icon-512.png +0 -0
  184. painapple_code-1.0.0rc1/src/painapple_code/static/icons/icon-72.png +0 -0
  185. painapple_code-1.0.0rc1/src/painapple_code/static/icons/icon-96.png +0 -0
  186. painapple_code-1.0.0rc1/src/painapple_code/static/js/activity-strip.js +181 -0
  187. painapple_code-1.0.0rc1/src/painapple_code/static/js/app-context.js +157 -0
  188. painapple_code-1.0.0rc1/src/painapple_code/static/js/app.js +5875 -0
  189. painapple_code-1.0.0rc1/src/painapple_code/static/js/auth-fetch.js +46 -0
  190. painapple_code-1.0.0rc1/src/painapple_code/static/js/background-tasks.js +274 -0
  191. painapple_code-1.0.0rc1/src/painapple_code/static/js/caret-position.js +171 -0
  192. painapple_code-1.0.0rc1/src/painapple_code/static/js/chat-navigator.js +683 -0
  193. painapple_code-1.0.0rc1/src/painapple_code/static/js/chat-search.js +323 -0
  194. painapple_code-1.0.0rc1/src/painapple_code/static/js/command-executor.js +172 -0
  195. painapple_code-1.0.0rc1/src/painapple_code/static/js/command-store.js +497 -0
  196. painapple_code-1.0.0rc1/src/painapple_code/static/js/components.js +982 -0
  197. painapple_code-1.0.0rc1/src/painapple_code/static/js/config.js +158 -0
  198. painapple_code-1.0.0rc1/src/painapple_code/static/js/context-menu.js +570 -0
  199. painapple_code-1.0.0rc1/src/painapple_code/static/js/controllers/chat-controller.js +3667 -0
  200. painapple_code-1.0.0rc1/src/painapple_code/static/js/controllers/dialog-controller.js +185 -0
  201. painapple_code-1.0.0rc1/src/painapple_code/static/js/controllers/tab-controller.js +1505 -0
  202. painapple_code-1.0.0rc1/src/painapple_code/static/js/controllers/thinking-controller.js +905 -0
  203. painapple_code-1.0.0rc1/src/painapple_code/static/js/diff-utils.js +484 -0
  204. painapple_code-1.0.0rc1/src/painapple_code/static/js/editor-view.js +597 -0
  205. painapple_code-1.0.0rc1/src/painapple_code/static/js/effort-settings.js +369 -0
  206. painapple_code-1.0.0rc1/src/painapple_code/static/js/file-autocomplete.js +515 -0
  207. painapple_code-1.0.0rc1/src/painapple_code/static/js/file-tabs.js +120 -0
  208. painapple_code-1.0.0rc1/src/painapple_code/static/js/gestures.js +448 -0
  209. painapple_code-1.0.0rc1/src/painapple_code/static/js/grid-switcher.js +332 -0
  210. painapple_code-1.0.0rc1/src/painapple_code/static/js/input-handler.js +922 -0
  211. painapple_code-1.0.0rc1/src/painapple_code/static/js/keyboard-bar.js +495 -0
  212. painapple_code-1.0.0rc1/src/painapple_code/static/js/linkify-utils.js +200 -0
  213. painapple_code-1.0.0rc1/src/painapple_code/static/js/open-dialog.js +870 -0
  214. painapple_code-1.0.0rc1/src/painapple_code/static/js/orphan-terminals.js +366 -0
  215. painapple_code-1.0.0rc1/src/painapple_code/static/js/perf-marks.js +102 -0
  216. painapple_code-1.0.0rc1/src/painapple_code/static/js/permission-settings.js +347 -0
  217. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview/json-tree.js +213 -0
  218. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview/preview-edit.js +366 -0
  219. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview/preview-events.js +239 -0
  220. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview/preview-history.js +533 -0
  221. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview/preview-inline-edit.js +785 -0
  222. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview/preview-poll.js +102 -0
  223. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview/preview-render.js +445 -0
  224. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview/preview-search.js +391 -0
  225. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview/preview-state.js +175 -0
  226. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview/preview-utils.js +244 -0
  227. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview-plugins/chart-plugin.js +22 -0
  228. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview-plugins/csv-plugin.js +188 -0
  229. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview-plugins/excalidraw-plugin.js +25 -0
  230. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview-plugins/html-plugin.js +104 -0
  231. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview-plugins/image-plugin.js +20 -0
  232. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview-plugins/index.js +32 -0
  233. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview-plugins/json-plugin.js +131 -0
  234. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview-plugins/jsonl-plugin.js +170 -0
  235. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview-plugins/markdown-plugin.js +76 -0
  236. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview-plugins/panzoom-plugin.js +62 -0
  237. painapple_code-1.0.0rc1/src/painapple_code/static/js/preview-plugins/plugin-helpers.js +264 -0
  238. painapple_code-1.0.0rc1/src/painapple_code/static/js/prompt-favorites.js +187 -0
  239. painapple_code-1.0.0rc1/src/painapple_code/static/js/quick-actions-menu.js +1708 -0
  240. painapple_code-1.0.0rc1/src/painapple_code/static/js/quick-actions-registry.js +1147 -0
  241. painapple_code-1.0.0rc1/src/painapple_code/static/js/quick-switcher/controller.js +217 -0
  242. painapple_code-1.0.0rc1/src/painapple_code/static/js/quick-switcher/fuzzy-scorer.js +123 -0
  243. painapple_code-1.0.0rc1/src/painapple_code/static/js/quick-switcher/index.js +28 -0
  244. painapple_code-1.0.0rc1/src/painapple_code/static/js/quick-switcher/providers/base-provider.js +51 -0
  245. painapple_code-1.0.0rc1/src/painapple_code/static/js/quick-switcher/providers/command-provider.js +56 -0
  246. painapple_code-1.0.0rc1/src/painapple_code/static/js/quick-switcher/providers/file-provider.js +296 -0
  247. painapple_code-1.0.0rc1/src/painapple_code/static/js/quick-switcher/providers/panel-provider.js +130 -0
  248. painapple_code-1.0.0rc1/src/painapple_code/static/js/quick-switcher/providers/project-provider.js +254 -0
  249. painapple_code-1.0.0rc1/src/painapple_code/static/js/quick-switcher/providers/skills-provider.js +108 -0
  250. painapple_code-1.0.0rc1/src/painapple_code/static/js/quick-switcher/registry.js +69 -0
  251. painapple_code-1.0.0rc1/src/painapple_code/static/js/quick-switcher/ui/item.js +54 -0
  252. painapple_code-1.0.0rc1/src/painapple_code/static/js/quick-switcher/ui/picker.js +350 -0
  253. painapple_code-1.0.0rc1/src/painapple_code/static/js/recent-opens.js +40 -0
  254. painapple_code-1.0.0rc1/src/painapple_code/static/js/scroll-manager.js +488 -0
  255. painapple_code-1.0.0rc1/src/painapple_code/static/js/scroll-state-machine.js +359 -0
  256. painapple_code-1.0.0rc1/src/painapple_code/static/js/selection/action-bar.js +749 -0
  257. painapple_code-1.0.0rc1/src/painapple_code/static/js/selection/index.js +15 -0
  258. painapple_code-1.0.0rc1/src/painapple_code/static/js/selection/selection-handler.js +779 -0
  259. painapple_code-1.0.0rc1/src/painapple_code/static/js/selection/state.js +107 -0
  260. painapple_code-1.0.0rc1/src/painapple_code/static/js/session/agent-progress.js +202 -0
  261. painapple_code-1.0.0rc1/src/painapple_code/static/js/session/handle-agent-message.js +448 -0
  262. painapple_code-1.0.0rc1/src/painapple_code/static/js/session/handle-message.js +435 -0
  263. painapple_code-1.0.0rc1/src/painapple_code/static/js/session/interactive.js +305 -0
  264. painapple_code-1.0.0rc1/src/painapple_code/static/js/session/message-store.js +204 -0
  265. painapple_code-1.0.0rc1/src/painapple_code/static/js/session/messages.js +244 -0
  266. painapple_code-1.0.0rc1/src/painapple_code/static/js/session/persistence.js +111 -0
  267. painapple_code-1.0.0rc1/src/painapple_code/static/js/session/restore.js +123 -0
  268. painapple_code-1.0.0rc1/src/painapple_code/static/js/session/sync.js +227 -0
  269. painapple_code-1.0.0rc1/src/painapple_code/static/js/session-container-pool.js +340 -0
  270. painapple_code-1.0.0rc1/src/painapple_code/static/js/session.js +912 -0
  271. painapple_code-1.0.0rc1/src/painapple_code/static/js/shortcut-hints.js +240 -0
  272. painapple_code-1.0.0rc1/src/painapple_code/static/js/shortcuts.js +929 -0
  273. painapple_code-1.0.0rc1/src/painapple_code/static/js/skills-autocomplete.js +262 -0
  274. painapple_code-1.0.0rc1/src/painapple_code/static/js/snippets-autocomplete.js +738 -0
  275. painapple_code-1.0.0rc1/src/painapple_code/static/js/stash-ui.js +458 -0
  276. painapple_code-1.0.0rc1/src/painapple_code/static/js/stash.js +555 -0
  277. painapple_code-1.0.0rc1/src/painapple_code/static/js/status-bar.js +737 -0
  278. painapple_code-1.0.0rc1/src/painapple_code/static/js/token-profile.js +250 -0
  279. painapple_code-1.0.0rc1/src/painapple_code/static/js/tool-renderer-blocks.js +1576 -0
  280. painapple_code-1.0.0rc1/src/painapple_code/static/js/tool-renderer-thinking.js +819 -0
  281. painapple_code-1.0.0rc1/src/painapple_code/static/js/tool-renderer.js +1473 -0
  282. painapple_code-1.0.0rc1/src/painapple_code/static/js/tooltips.js +193 -0
  283. painapple_code-1.0.0rc1/src/painapple_code/static/js/upload-manager.js +688 -0
  284. painapple_code-1.0.0rc1/src/painapple_code/static/js/utils.js +453 -0
  285. painapple_code-1.0.0rc1/src/painapple_code/static/js/welcome/api.js +356 -0
  286. painapple_code-1.0.0rc1/src/painapple_code/static/js/welcome/cards.js +547 -0
  287. painapple_code-1.0.0rc1/src/painapple_code/static/js/welcome/context-menu.js +563 -0
  288. painapple_code-1.0.0rc1/src/painapple_code/static/js/welcome/families.js +763 -0
  289. painapple_code-1.0.0rc1/src/painapple_code/static/js/welcome/preview.js +234 -0
  290. painapple_code-1.0.0rc1/src/painapple_code/static/js/welcome/state.js +128 -0
  291. painapple_code-1.0.0rc1/src/painapple_code/static/js/welcome.js +1777 -0
  292. painapple_code-1.0.0rc1/src/painapple_code/static/js/widget-system/base-widget.js +708 -0
  293. painapple_code-1.0.0rc1/src/painapple_code/static/js/widget-system/device-manager.js +178 -0
  294. painapple_code-1.0.0rc1/src/painapple_code/static/js/widget-system/event-bus.js +152 -0
  295. painapple_code-1.0.0rc1/src/painapple_code/static/js/widget-system/icons.js +114 -0
  296. painapple_code-1.0.0rc1/src/painapple_code/static/js/widget-system/index.js +49 -0
  297. painapple_code-1.0.0rc1/src/painapple_code/static/js/widget-system/init.js +54 -0
  298. painapple_code-1.0.0rc1/src/painapple_code/static/js/widget-system/types/bottom-sheet.js +307 -0
  299. painapple_code-1.0.0rc1/src/painapple_code/static/js/widget-system/types/floating.js +813 -0
  300. painapple_code-1.0.0rc1/src/painapple_code/static/js/widget-system/types/index.js +40 -0
  301. painapple_code-1.0.0rc1/src/painapple_code/static/js/widget-system/types/modal.js +147 -0
  302. painapple_code-1.0.0rc1/src/painapple_code/static/js/widget-system/types/sidebar.js +158 -0
  303. painapple_code-1.0.0rc1/src/painapple_code/static/js/widget-system/types/tab.js +146 -0
  304. painapple_code-1.0.0rc1/src/painapple_code/static/js/widget-system/types/top-sheet.js +203 -0
  305. painapple_code-1.0.0rc1/src/painapple_code/static/js/widget-system/widget-manager.js +1170 -0
  306. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/active-sessions-widget.js +748 -0
  307. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/agents-widget.js +897 -0
  308. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/browser-widget.js +409 -0
  309. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/commands-widget.js +694 -0
  310. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/config/commit-sections.js +449 -0
  311. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/config/dir-autocomplete.js +223 -0
  312. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/config/gestures.js +161 -0
  313. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/config/models-tab.js +296 -0
  314. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/config/quick-actions-tab.js +641 -0
  315. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/config/shortcut-editor.js +587 -0
  316. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/config/state.js +483 -0
  317. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/config/system-controls.js +304 -0
  318. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/config-widget.js +1384 -0
  319. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/cost-analytics-widget.js +629 -0
  320. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/debug-widget.js +693 -0
  321. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/diff-viewer-widget.js +1606 -0
  322. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/discussion-widget.js +1074 -0
  323. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/file-explorer-widget.js +2147 -0
  324. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/file-preview-widget.js +754 -0
  325. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/git-widget.js +793 -0
  326. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/helpers-install-widget.js +569 -0
  327. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/history-explorer-widget.js +2699 -0
  328. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/image-preview-widget.js +433 -0
  329. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/index.js +162 -0
  330. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/log-explorer-widget.js +1002 -0
  331. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/plugins-widget.js +305 -0
  332. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/prompt-explorer-widget.js +833 -0
  333. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/skills-widget.js +843 -0
  334. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/snippets-widget.js +479 -0
  335. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/sub-agents-widget.js +335 -0
  336. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/tasks-widget.js +381 -0
  337. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/terminal/connection.js +283 -0
  338. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/terminal/gestures.js +330 -0
  339. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/terminal/init.js +518 -0
  340. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/terminal/link-providers.js +440 -0
  341. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/terminal/render.js +159 -0
  342. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/terminal/size.js +54 -0
  343. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/terminal/state.js +210 -0
  344. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/terminal-widget.js +791 -0
  345. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/uploads-widget.js +309 -0
  346. painapple_code-1.0.0rc1/src/painapple_code/static/js/widgets/zen-widget.js +999 -0
  347. painapple_code-1.0.0rc1/src/painapple_code/static/login.html +156 -0
  348. painapple_code-1.0.0rc1/src/painapple_code/static/manifest.json +73 -0
  349. painapple_code-1.0.0rc1/src/painapple_code/static/sw.js +277 -0
  350. painapple_code-1.0.0rc1/src/painapple_code/static/vendor/README.md +49 -0
  351. painapple_code-1.0.0rc1/src/painapple_code/static/vendor/codemirror.js +32 -0
  352. painapple_code-1.0.0rc1/src/painapple_code/static/vendor/github-markdown-dark.min.css +1124 -0
  353. painapple_code-1.0.0rc1/src/painapple_code/static/vendor/highlight-github-dark.min.css +10 -0
  354. painapple_code-1.0.0rc1/src/painapple_code/static/vendor/highlight-lang-dockerfile.min.js +8 -0
  355. painapple_code-1.0.0rc1/src/painapple_code/static/vendor/highlight-lang-nginx.min.js +21 -0
  356. painapple_code-1.0.0rc1/src/painapple_code/static/vendor/highlight-lang-properties.min.js +10 -0
  357. painapple_code-1.0.0rc1/src/painapple_code/static/vendor/highlight-lang-scala.min.js +28 -0
  358. painapple_code-1.0.0rc1/src/painapple_code/static/vendor/highlight.min.js +1213 -0
  359. painapple_code-1.0.0rc1/src/painapple_code/static/vendor/marked.min.js +6 -0
  360. painapple_code-1.0.0rc1/src/painapple_code/static/vendor/xterm-addon-fit.js +2 -0
  361. painapple_code-1.0.0rc1/src/painapple_code/static/vendor/xterm.css +209 -0
  362. painapple_code-1.0.0rc1/src/painapple_code/static/vendor/xterm.js +2 -0
  363. painapple_code-1.0.0rc1/src/painapple_code/static/web-client.html +660 -0
  364. painapple_code-1.0.0rc1/src/painapple_code/subprocess_registry.py +535 -0
  365. painapple_code-1.0.0rc1/src/painapple_code/tls_cert.py +86 -0
  366. painapple_code-1.0.0rc1/src/painapple_code/tools/agents/shadow-git-helper.md +399 -0
  367. painapple_code-1.0.0rc1/src/painapple_code/tools/excalidraw-to-svg.js +143 -0
  368. painapple_code-1.0.0rc1/src/painapple_code/tools/install-helpers.sh +148 -0
  369. painapple_code-1.0.0rc1/src/painapple_code/tools/shadow-git +262 -0
  370. painapple_code-1.0.0rc1/src/painapple_code/tools/shadow-query +108 -0
  371. painapple_code-1.0.0rc1/src/painapple_code/tools/vegalite-to-svg.js +79 -0
  372. painapple_code-1.0.0rc1/src/painapple_code/turn_query.py +766 -0
  373. painapple_code-1.0.0rc1/src/painapple_code/turn_tracker.py +194 -0
  374. painapple_code-1.0.0rc1/src/painapple_code/utils/__init__.py +0 -0
  375. painapple_code-1.0.0rc1/src/painapple_code/utils/agent_cli.py +297 -0
  376. painapple_code-1.0.0rc1/src/painapple_code/utils/chart.py +103 -0
  377. painapple_code-1.0.0rc1/src/painapple_code/utils/excalidraw.py +107 -0
  378. painapple_code-1.0.0rc1/src/painapple_code/utils/file_paths.py +340 -0
  379. painapple_code-1.0.0rc1/src/painapple_code/utils/generate_icons.py +154 -0
  380. painapple_code-1.0.0rc1/src/painapple_code/utils/token_profiles.py +83 -0
  381. painapple_code-1.0.0rc1/src/painapple_code/viewer_templates.py +647 -0
  382. painapple_code-1.0.0rc1/src/painapple_code/welcome_search.py +829 -0
  383. painapple_code-1.0.0rc1/src/painapple_code.egg-info/PKG-INFO +382 -0
  384. painapple_code-1.0.0rc1/src/painapple_code.egg-info/SOURCES.txt +481 -0
  385. painapple_code-1.0.0rc1/src/painapple_code.egg-info/dependency_links.txt +1 -0
  386. painapple_code-1.0.0rc1/src/painapple_code.egg-info/entry_points.txt +2 -0
  387. painapple_code-1.0.0rc1/src/painapple_code.egg-info/requires.txt +11 -0
  388. painapple_code-1.0.0rc1/src/painapple_code.egg-info/scm_file_list.json +476 -0
  389. painapple_code-1.0.0rc1/src/painapple_code.egg-info/scm_version.json +8 -0
  390. painapple_code-1.0.0rc1/src/painapple_code.egg-info/top_level.txt +1 -0
  391. painapple_code-1.0.0rc1/start.sh +132 -0
  392. painapple_code-1.0.0rc1/tauri-app/.gitignore +7 -0
  393. painapple_code-1.0.0rc1/tauri-app/APP.md +75 -0
  394. painapple_code-1.0.0rc1/tauri-app/CAPACITOR-VS-TAURI.md +125 -0
  395. painapple_code-1.0.0rc1/tauri-app/README.md +86 -0
  396. painapple_code-1.0.0rc1/tauri-app/WINDOWS.md +82 -0
  397. painapple_code-1.0.0rc1/tauri-app/package-lock.json +245 -0
  398. painapple_code-1.0.0rc1/tauri-app/package.json +24 -0
  399. painapple_code-1.0.0rc1/tauri-app/scripts/ios-install.sh +60 -0
  400. painapple_code-1.0.0rc1/tauri-app/scripts/ios-postinit.sh +225 -0
  401. painapple_code-1.0.0rc1/tauri-app/scripts/macos-install.sh +65 -0
  402. painapple_code-1.0.0rc1/tauri-app/scripts/tao-fix.patch +127 -0
  403. painapple_code-1.0.0rc1/tauri-app/scripts/tao-setup.sh +68 -0
  404. painapple_code-1.0.0rc1/tauri-app/src/icon.png +0 -0
  405. painapple_code-1.0.0rc1/tauri-app/src/index.html +345 -0
  406. painapple_code-1.0.0rc1/tauri-app/src/launcher.js +651 -0
  407. painapple_code-1.0.0rc1/tauri-app/src-tauri/Cargo.lock +5578 -0
  408. painapple_code-1.0.0rc1/tauri-app/src-tauri/Cargo.toml +87 -0
  409. painapple_code-1.0.0rc1/tauri-app/src-tauri/build.rs +3 -0
  410. painapple_code-1.0.0rc1/tauri-app/src-tauri/capabilities/default.json +11 -0
  411. painapple_code-1.0.0rc1/tauri-app/src-tauri/capabilities/server-pages.json +15 -0
  412. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/128x128.png +0 -0
  413. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/128x128@2x.png +0 -0
  414. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/32x32.png +0 -0
  415. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/64x64.png +0 -0
  416. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/Square107x107Logo.png +0 -0
  417. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/Square142x142Logo.png +0 -0
  418. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/Square150x150Logo.png +0 -0
  419. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/Square284x284Logo.png +0 -0
  420. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/Square30x30Logo.png +0 -0
  421. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/Square310x310Logo.png +0 -0
  422. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/Square44x44Logo.png +0 -0
  423. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/Square71x71Logo.png +0 -0
  424. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/Square89x89Logo.png +0 -0
  425. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/StoreLogo.png +0 -0
  426. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml +5 -0
  427. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png +0 -0
  428. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png +0 -0
  429. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png +0 -0
  430. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png +0 -0
  431. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png +0 -0
  432. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png +0 -0
  433. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png +0 -0
  434. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png +0 -0
  435. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png +0 -0
  436. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png +0 -0
  437. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png +0 -0
  438. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png +0 -0
  439. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png +0 -0
  440. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png +0 -0
  441. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png +0 -0
  442. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/android/values/ic_launcher_background.xml +4 -0
  443. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/icon.icns +0 -0
  444. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/icon.ico +0 -0
  445. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/icon.png +0 -0
  446. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-20x20@1x.png +0 -0
  447. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-20x20@2x-1.png +0 -0
  448. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-20x20@2x.png +0 -0
  449. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-20x20@3x.png +0 -0
  450. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-29x29@1x.png +0 -0
  451. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-29x29@2x-1.png +0 -0
  452. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-29x29@2x.png +0 -0
  453. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-29x29@3x.png +0 -0
  454. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-40x40@1x.png +0 -0
  455. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-40x40@2x-1.png +0 -0
  456. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-40x40@2x.png +0 -0
  457. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-40x40@3x.png +0 -0
  458. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-512@2x.png +0 -0
  459. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-60x60@2x.png +0 -0
  460. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-60x60@3x.png +0 -0
  461. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-76x76@1x.png +0 -0
  462. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-76x76@2x.png +0 -0
  463. painapple_code-1.0.0rc1/tauri-app/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png +0 -0
  464. painapple_code-1.0.0rc1/tauri-app/src-tauri/native-ios/KeyCommandShim.mm +244 -0
  465. painapple_code-1.0.0rc1/tauri-app/src-tauri/permissions/app-commands.toml +30 -0
  466. painapple_code-1.0.0rc1/tauri-app/src-tauri/src/dock_menu.rs +208 -0
  467. painapple_code-1.0.0rc1/tauri-app/src-tauri/src/ios_quick_actions.rs +190 -0
  468. painapple_code-1.0.0rc1/tauri-app/src-tauri/src/lib.rs +996 -0
  469. painapple_code-1.0.0rc1/tauri-app/src-tauri/src/main.rs +5 -0
  470. painapple_code-1.0.0rc1/tauri-app/src-tauri/src/proxy.rs +444 -0
  471. painapple_code-1.0.0rc1/tauri-app/src-tauri/tauri.conf.json +39 -0
  472. painapple_code-1.0.0rc1/tools/backfill_cumulative_cost.py +449 -0
  473. painapple_code-1.0.0rc1/tools/backfill_from_shadow_git.py +414 -0
  474. painapple_code-1.0.0rc1/tools/benchmark-fork.fish +444 -0
  475. painapple_code-1.0.0rc1/tools/bundle-feature-source.sh +99 -0
  476. painapple_code-1.0.0rc1/tools/codemirror-entry.js +72 -0
  477. painapple_code-1.0.0rc1/tools/obsidian-to-excalidraw.js +76 -0
  478. painapple_code-1.0.0rc1/tools/package-lock.json +3098 -0
  479. painapple_code-1.0.0rc1/tools/package.json +50 -0
  480. painapple_code-1.0.0rc1/tools/personalize-devcontainer.js +169 -0
  481. painapple_code-1.0.0rc1/tools/prototype-builder.py +1596 -0
  482. painapple_code-1.0.0rc1/tools/rebuild_shadow_db.py +861 -0
  483. painapple_code-1.0.0rc1/tools/shortcut_dna_gen.py +606 -0
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "pAInapple Code",
3
+ "image": "mcr.microsoft.com/devcontainers/base:ubuntu",
4
+
5
+ // pAInapple Code itself isn't auto-installed — open a terminal and run
6
+ // curl -fsSL https://raw.githubusercontent.com/wrotek/painapple-code/main/features/src/painapple-code/install.sh | sudo bash
7
+ // (see features/src/painapple-code/README.md). The forwarded port and
8
+ // the VS Code settings below are everything Codespaces needs prepared
9
+ // up front; the rest is on-demand.
10
+ //
11
+ // Python + Node come from the official Features so npm / node-gyp /
12
+ // native-module builds work out of the box. (The base:ubuntu image is
13
+ // bare — common-utils only.)
14
+ "features": {
15
+ "ghcr.io/devcontainers/features/python:1": {},
16
+ "ghcr.io/devcontainers/features/node:1": {}
17
+ },
18
+
19
+ "forwardPorts": [8765],
20
+ "portsAttributes": {
21
+ "8765": {
22
+ "label": "pAInapple Code",
23
+ "onAutoForward": "notify",
24
+ "visibility": "private"
25
+ }
26
+ },
27
+
28
+ "customizations": {
29
+ "vscode": {
30
+ "extensions": [
31
+ "ms-python.python",
32
+ "redhat.vscode-yaml"
33
+ ],
34
+ // Default-hide the secondary side bar (Copilot Chat lives there).
35
+ // Users who want it can toggle with Ctrl+Alt+B at any time — this
36
+ // just keeps it out of the way on first open so the terminal has
37
+ // room to show the painapple-code login URL without wrapping.
38
+ // Setting added in VS Code 1.94 (Sept 2024).
39
+ "settings": {
40
+ "workbench.secondarySideBar.defaultVisibility": "hidden"
41
+ }
42
+ }
43
+ }
44
+ }
@@ -0,0 +1,19 @@
1
+ # Deny-by-default. Only the explicit allowlist below ships into the image.
2
+ # Why deny-first: the repo has ~1.4GB of side projects (tauri-app/, tao/),
3
+ # build-time deps (top-level tools/node_modules/), local-only docs, and
4
+ # multiple worktrees that have no business in a runtime image.
5
+ *
6
+
7
+ # --- Runtime essentials ---
8
+ !src/
9
+ !requirements.txt
10
+ !pyproject.toml
11
+ !docker-entrypoint.sh
12
+ !README.md
13
+ !LICENSE
14
+
15
+ # --- Re-exclusions inside the allowlist (cruft that sneaks in via src/) ---
16
+ **/__pycache__/
17
+ **/*.pyc
18
+ **/.pytest_cache/
19
+ **/.DS_Store
@@ -0,0 +1,187 @@
1
+ name: Publish Docker Image
2
+
3
+ # Builds and publishes the painapple-code image to Docker Hub.
4
+ #
5
+ # Versioning scheme: SemVer — git tags shaped like vMAJOR.MINOR.PATCH
6
+ # (e.g. v0.1.0, v1.2.3, v1.2.3-rc1). The git tag is the single source of
7
+ # truth — both the Docker image tag and the pip package version
8
+ # (via setuptools-scm) derive from it.
9
+ #
10
+ # Triggers:
11
+ # - push of a tag matching v*.*.* → publishes :<tag> and :latest
12
+ # (pre-release tags like v0.1.0-rc1
13
+ # publish :<tag> and skip :latest)
14
+ # - workflow_dispatch → publishes :edge (does not move :latest)
15
+ #
16
+ # Required repository secrets:
17
+ # - DOCKERHUB_USERNAME (wrotek)
18
+ # - DOCKERHUB_TOKEN (Hub access token, NOT account password)
19
+ #
20
+ # Multi-arch: builds linux/amd64 + linux/arm64 via buildx + QEMU.
21
+
22
+ on:
23
+ push:
24
+ tags:
25
+ - "v*.*.*"
26
+ workflow_dispatch:
27
+
28
+ env:
29
+ IMAGE: docker.io/wrotek/painapple-code
30
+
31
+ jobs:
32
+ build-and-push:
33
+ runs-on: ubuntu-latest
34
+ permissions:
35
+ contents: read
36
+ steps:
37
+ # fetch-depth: 0 so setuptools-scm can `git describe` against
38
+ # all tags. Default shallow clone hides everything but HEAD.
39
+ - uses: actions/checkout@v4
40
+ with:
41
+ fetch-depth: 0
42
+
43
+ - name: Set up Python
44
+ uses: actions/setup-python@v5
45
+ with:
46
+ python-version: "3.12"
47
+
48
+ # Single source-of-truth version. Same value gets baked into
49
+ # the wheel (via SETUPTOOLS_SCM_PRETEND_VERSION_FOR_… inside
50
+ # the image) and surfaces as `painapple-code --version`.
51
+ - name: Resolve package version from git
52
+ id: scm
53
+ run: |
54
+ pip install --quiet 'setuptools-scm>=8'
55
+ version=$(python -m setuptools_scm)
56
+ echo "version=$version" >> "$GITHUB_OUTPUT"
57
+ echo "→ setuptools-scm resolved: $version"
58
+
59
+ - name: Set up QEMU
60
+ uses: docker/setup-qemu-action@v3
61
+
62
+ - name: Set up Buildx
63
+ uses: docker/setup-buildx-action@v3
64
+
65
+ - name: Log in to Docker Hub
66
+ uses: docker/login-action@v3
67
+ with:
68
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
69
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
70
+
71
+ # Tag policy:
72
+ # tag push v0.1.0 → :v0.1.0, :latest
73
+ # tag push v0.1.0-rc1 → :v0.1.0-rc1 (skip :latest)
74
+ # workflow_dispatch → :edge (skip :latest)
75
+ #
76
+ # type=ref,event=tag takes the tag name verbatim (keeps the
77
+ # leading `v`). We deliberately don't use type=semver — it
78
+ # would auto-emit :0.1 and :0 alias tags, which is library
79
+ # ergonomics we don't want for an end-user app.
80
+ - name: Derive image tags
81
+ id: meta
82
+ uses: docker/metadata-action@v5
83
+ with:
84
+ images: ${{ env.IMAGE }}
85
+ tags: |
86
+ type=ref,event=tag
87
+ type=raw,value=latest,enable=${{ startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '-') }}
88
+ type=raw,value=edge,enable=${{ github.event_name == 'workflow_dispatch' }}
89
+ labels: |
90
+ org.opencontainers.image.version=${{ steps.scm.outputs.version }}
91
+ org.opencontainers.image.revision=${{ github.sha }}
92
+ org.opencontainers.image.created=${{ github.event.repository.updated_at }}
93
+
94
+ - name: Build and push
95
+ id: build
96
+ uses: docker/build-push-action@v6
97
+ with:
98
+ context: .
99
+ platforms: linux/amd64,linux/arm64
100
+ push: true
101
+ tags: ${{ steps.meta.outputs.tags }}
102
+ labels: ${{ steps.meta.outputs.labels }}
103
+ build-args: |
104
+ SCM_VERSION=${{ steps.scm.outputs.version }}
105
+ cache-from: type=gha
106
+ cache-to: type=gha,mode=max
107
+
108
+ smoke-test:
109
+ # Pulls the freshly-pushed image on a clean runner and verifies the
110
+ # bridge boots: /health must return HTTP 200 within 30 s. Runs only
111
+ # for amd64 — buildx publishes the same code for both arches, so
112
+ # one platform is sufficient to catch packaging regressions.
113
+ needs: build-and-push
114
+ runs-on: ubuntu-latest
115
+ steps:
116
+ - name: Pick the tag we just published
117
+ id: tag
118
+ run: |
119
+ if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then
120
+ echo "ref=edge" >> "$GITHUB_OUTPUT"
121
+ else
122
+ # strip refs/tags/ prefix → vMAJOR.MINOR.PATCH[-prerelease]
123
+ echo "ref=${GITHUB_REF#refs/tags/}" >> "$GITHUB_OUTPUT"
124
+ fi
125
+
126
+ - name: Boot container and curl /health
127
+ run: |
128
+ set -euo pipefail
129
+ IMG="${{ env.IMAGE }}:${{ steps.tag.outputs.ref }}"
130
+ echo "→ Pulling $IMG"
131
+ docker pull "$IMG"
132
+
133
+ # Workspace mount: the entrypoint refuses to start unless
134
+ # /workspace is non-empty. Use a one-file dir as a stand-in.
135
+ mkdir -p /tmp/ws && echo "smoke" > /tmp/ws/README
136
+
137
+ echo "→ Starting smoke container"
138
+ docker run -d --name papple-smoke \
139
+ -p 127.0.0.1:8765:8765 \
140
+ -v /tmp/ws:/workspace \
141
+ "$IMG"
142
+
143
+ echo "→ Waiting for /health"
144
+ for i in $(seq 1 30); do
145
+ if curl -fsS http://127.0.0.1:8765/health > /dev/null 2>&1; then
146
+ echo "✓ /health responded after ${i}s"
147
+ # Also confirm the version we shipped is the one we baked in.
148
+ docker exec papple-smoke python -c \
149
+ "import painapple_code; print('version:', painapple_code.__version__)"
150
+ exit 0
151
+ fi
152
+ sleep 1
153
+ done
154
+
155
+ echo "✗ /health never responded"
156
+ docker logs papple-smoke || true
157
+ exit 1
158
+
159
+ - name: Cleanup
160
+ if: always()
161
+ run: docker rm -f papple-smoke 2>/dev/null || true
162
+
163
+ sync-dockerhub-readme:
164
+ # Pushes README.dockerhub.md to the Docker Hub repo's Overview tab
165
+ # and updates the short description. Runs only on real SemVer tag
166
+ # pushes — pre-releases and :edge builds don't move the public docs.
167
+ #
168
+ # If you later want the Hub docs to track main between releases,
169
+ # split this into its own workflow on `push: branches: [main]` with
170
+ # `paths: [README.dockerhub.md]`.
171
+ needs: build-and-push
172
+ if: startsWith(github.ref, 'refs/tags/v') && !contains(github.ref, '-')
173
+ runs-on: ubuntu-latest
174
+ steps:
175
+ - uses: actions/checkout@v4
176
+
177
+ - name: Sync Docker Hub description
178
+ uses: peter-evans/dockerhub-description@v5
179
+ with:
180
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
181
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
182
+ repository: wrotek/painapple-code
183
+ short-description: "WebSocket bridge that serves a vanilla-JS PWA for Claude Code sessions"
184
+ readme-filepath: ./README.dockerhub.md
185
+ # Rewrites relative links (e.g. [LICENSE](LICENSE)) into
186
+ # absolute github.com URLs so they resolve on the Hub page.
187
+ enable-url-completion: true
@@ -0,0 +1,94 @@
1
+ name: Publish to PyPI
2
+
3
+ # Builds the sdist + wheel and publishes them to PyPI, then creates a
4
+ # GitHub Release for the tag.
5
+ #
6
+ # Versioning: same scheme as docker-publish.yml — the git tag (vX.Y.Z)
7
+ # is the single source of truth; setuptools-scm derives the package
8
+ # version from it at build time.
9
+ #
10
+ # Auth: PyPI Trusted Publishing (OIDC) — no API token secrets. One-time
11
+ # setup on pypi.org: add a publisher for project `painapple-code` with
12
+ # owner: wrotek, repo: painapple-code,
13
+ # workflow: pypi-publish.yml, environment: pypi
14
+ # Pre-release tags (v1.1.0-rc1) publish to PyPI as 1.1.0rc1 (pip only
15
+ # installs those with --pre) and are marked pre-release on GitHub.
16
+
17
+ on:
18
+ push:
19
+ tags:
20
+ - "v*.*.*"
21
+
22
+ jobs:
23
+ build:
24
+ runs-on: ubuntu-latest
25
+ steps:
26
+ # fetch-depth: 0 so setuptools-scm can `git describe` against
27
+ # all tags. Default shallow clone hides everything but HEAD.
28
+ - uses: actions/checkout@v4
29
+ with:
30
+ fetch-depth: 0
31
+
32
+ - name: Set up Python
33
+ uses: actions/setup-python@v5
34
+ with:
35
+ python-version: "3.12"
36
+
37
+ - name: Build sdist and wheel
38
+ run: |
39
+ pip install --quiet build
40
+ python -m build
41
+ ls -l dist/
42
+
43
+ # Sanity check: the version baked into the wheel must match the
44
+ # tag that triggered us (v1.2.3 → 1.2.3, v1.2.3-rc1 → 1.2.3rc1).
45
+ - name: Verify version matches tag
46
+ run: |
47
+ tag="${GITHUB_REF#refs/tags/v}"
48
+ expected=$(python -c "from packaging.version import Version; import sys; print(Version(sys.argv[1]))" "$tag")
49
+ built=$(ls dist/*.whl | sed -E 's|dist/painapple_code-([^-]+)-.*|\1|')
50
+ echo "tag=$expected built=$built"
51
+ test "$expected" = "$built"
52
+
53
+ - uses: actions/upload-artifact@v4
54
+ with:
55
+ name: dist
56
+ path: dist/
57
+
58
+ publish-pypi:
59
+ needs: build
60
+ runs-on: ubuntu-latest
61
+ environment:
62
+ name: pypi
63
+ url: https://pypi.org/p/painapple-code
64
+ permissions:
65
+ id-token: write # OIDC token for Trusted Publishing
66
+ steps:
67
+ - uses: actions/download-artifact@v4
68
+ with:
69
+ name: dist
70
+ path: dist/
71
+
72
+ - name: Publish to PyPI
73
+ uses: pypa/gh-action-pypi-publish@release/v1
74
+
75
+ github-release:
76
+ needs: publish-pypi
77
+ runs-on: ubuntu-latest
78
+ permissions:
79
+ contents: write
80
+ steps:
81
+ - uses: actions/download-artifact@v4
82
+ with:
83
+ name: dist
84
+ path: dist/
85
+
86
+ - name: Create GitHub Release
87
+ env:
88
+ GH_TOKEN: ${{ github.token }}
89
+ run: |
90
+ tag="${GITHUB_REF#refs/tags/}"
91
+ args=(--generate-notes --title "$tag")
92
+ [[ "$tag" == *-* ]] && args+=(--prerelease)
93
+ gh release create "$tag" dist/* \
94
+ --repo "${{ github.repository }}" "${args[@]}"
@@ -0,0 +1,38 @@
1
+ name: Release Dev Container Features
2
+
3
+ # Publishes the Dev Container Features under features/src/ to GHCR.
4
+ # Consumers reference them via ghcr.io/<owner>/<repo>/<feature-id>:<major>.
5
+ #
6
+ # Triggers:
7
+ # - push to main that touches features/**
8
+ # - manual dispatch (for re-publishing after a metadata-only edit)
9
+
10
+ on:
11
+ push:
12
+ branches:
13
+ - main
14
+ paths:
15
+ - "features/src/**"
16
+ - ".github/workflows/release-features.yml"
17
+ workflow_dispatch:
18
+
19
+ jobs:
20
+ publish:
21
+ runs-on: ubuntu-latest
22
+ permissions:
23
+ contents: read
24
+ packages: write
25
+ steps:
26
+ - uses: actions/checkout@v4
27
+
28
+ - name: Bundle bridge source into Feature
29
+ run: tools/bundle-feature-source.sh --feature-only
30
+
31
+ - name: Publish Features to GHCR
32
+ uses: devcontainers/action@v1
33
+ with:
34
+ publish-features: "true"
35
+ base-path-to-features: "./features/src"
36
+ generate-docs: "false"
37
+ env:
38
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -0,0 +1,33 @@
1
+ name: Test Dev Container Features
2
+
3
+ # Runs the `devcontainer features test` harness on every PR that touches
4
+ # features/. Catches install.sh regressions before they ship to GHCR.
5
+
6
+ on:
7
+ pull_request:
8
+ paths:
9
+ - "features/**"
10
+ - ".github/workflows/test-features.yml"
11
+ workflow_dispatch:
12
+
13
+ jobs:
14
+ test:
15
+ runs-on: ubuntu-latest
16
+ strategy:
17
+ fail-fast: false
18
+ matrix:
19
+ feature: [painapple-code]
20
+ baseImage:
21
+ - mcr.microsoft.com/devcontainers/base:ubuntu
22
+ - debian:bookworm-slim
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+ - name: Install devcontainers CLI
26
+ run: npm install -g @devcontainers/cli
27
+ - name: Test ${{ matrix.feature }} on ${{ matrix.baseImage }}
28
+ run: |
29
+ devcontainer features test \
30
+ --features "${{ matrix.feature }}" \
31
+ --base-image "${{ matrix.baseImage }}" \
32
+ --project-folder . \
33
+ ./features
@@ -0,0 +1,80 @@
1
+ # Python virtual environment
2
+ venv/
3
+ __pycache__/
4
+ *.pyc
5
+
6
+ # Python build artifacts (from `pip install -e .` or `python -m build`)
7
+ *.egg-info/
8
+ build/
9
+ dist/
10
+
11
+ # IDE
12
+ .idea/
13
+ .vscode/
14
+ *.swp
15
+
16
+ # Logs
17
+ *.log
18
+
19
+ # OS
20
+ .DS_Store
21
+ sessions/
22
+
23
+ # Runtime worktrees
24
+ .painapple-code-worktrees/
25
+
26
+ # Review metadata (per-project, not committed)
27
+ .code-meta/
28
+
29
+ # Test results
30
+ test-results/
31
+
32
+ # Node.js (root package.json is for claude CLI only, not committed)
33
+ node_modules/
34
+ package.json
35
+ package-lock.json
36
+
37
+ # But tools/ has its own tracked package.json & lockfile
38
+ !tools/package.json
39
+ !tools/package-lock.json
40
+
41
+ # And tauri-app/ has its own tracked package.json & lockfile (native app shell)
42
+ !tauri-app/package.json
43
+ !tauri-app/package-lock.json
44
+
45
+ # Secrets / local data
46
+ .env*
47
+ *.duckdb
48
+
49
+ # Stray screenshot dumps
50
+ screenshots/
51
+
52
+ # Internal agent / personal infra (not for public)
53
+ .claude/
54
+ CLAUDE.md
55
+ deploy.fish
56
+ start-server.sh
57
+ TODO.md
58
+ TODO-PROD.md
59
+ bugs/
60
+ claude-stats.fish
61
+ docs/
62
+ docs-ai/
63
+ ideas/
64
+ tests/
65
+
66
+ # Devcontainer CLI build artifact
67
+ .devcontainer/devcontainer-lock.json
68
+
69
+ # Generated by setuptools-scm at build time — derived from git tag.
70
+ src/painapple_code/_version.py
71
+
72
+ # Local tao fork carrying the iOS scene-delegate UAF patch
73
+ # (see tauri-app/src-tauri/Cargo.toml [patch.crates-io]).
74
+ tao/
75
+
76
+ # Generated by tools/bundle-feature-source.sh — mirror of features/src/painapple-code/
77
+ # (the canonical Feature) plus the bundled bridge source. Regenerated by the
78
+ # initializeCommand in .devcontainer/devcontainer.json on every build.
79
+ .devcontainer/painapple-code/
80
+ features/src/painapple-code/bridge-source/
@@ -0,0 +1,155 @@
1
+ # syntax=docker/dockerfile:1.7
2
+ # Fully-qualified registry path — podman has no default registry and otherwise
3
+ # prompts to pick one when ~/.config/containers/registries.conf lists multiple
4
+ # unqualified-search-registries (quay.io / ghcr.io / docker.io). Docker ignores
5
+ # the prefix, so the same Dockerfile works on both runtimes.
6
+ FROM docker.io/library/python:3.13-slim-bookworm
7
+
8
+ # OCI image metadata — surfaces on Docker Hub / GHCR as the image's title,
9
+ # description, license, and "Source" link. The CI workflow overrides
10
+ # `revision` / `created` / `version` per build via --label.
11
+ LABEL org.opencontainers.image.title="painapple-code" \
12
+ org.opencontainers.image.description="WebSocket bridge that serves a vanilla-JS PWA for Claude Code sessions" \
13
+ org.opencontainers.image.source="https://github.com/wrotek/painapple-code" \
14
+ org.opencontainers.image.licenses="AGPL-3.0-or-later" \
15
+ org.opencontainers.image.vendor="wrotek"
16
+
17
+ # Disable apt's _apt sandbox user — required when building under rootless
18
+ # Docker with fuse-overlayfs (the unprivileged _apt UID can't write /tmp).
19
+ RUN echo 'APT::Sandbox::User "root";' > /etc/apt/apt.conf.d/00no-sandbox
20
+
21
+ # System deps:
22
+ # git — used by api_git.py and shadow_git.py
23
+ # curl — NodeSource bootstrap
24
+ # tini — clean PID 1 / signal forwarding so SIGTERM kills child claude procs
25
+ # fd-find — fast file enumeration for /api/files/list (.gitignore-aware).
26
+ # Debian ships the binary as `fdfind`; symlink to `fd` so
27
+ # routes/api_files.py finds it without a fallback shim.
28
+ RUN apt-get update && apt-get install -y --no-install-recommends \
29
+ ca-certificates curl git tini fd-find \
30
+ && ln -s /usr/bin/fdfind /usr/local/bin/fd \
31
+ && rm -rf /var/lib/apt/lists/*
32
+
33
+ # Developer tools for the in-container terminal. The bridge exposes a PTY
34
+ # the user runs commands in, so a base slim image with no editor / pager /
35
+ # search / multiplexer is painful. Adds ~80 MB. Anything bigger (compilers,
36
+ # language toolchains, language servers) belongs in a user-built derived
37
+ # image, not the base.
38
+ #
39
+ # editors — vim, nano
40
+ # pagers/text — less, jq
41
+ # search — ripgrep (`rg`); `fd` is already installed above
42
+ # process/system — htop, lsof, procps (ps/top), psmisc (killall/pstree)
43
+ # files/inspection — tree, ncdu, file
44
+ # network — iproute2 (ip), iputils-ping, dnsutils (dig/nslookup),
45
+ # netcat-openbsd, wget, rsync, openssh-client
46
+ # compression — zip, unzip, xz-utils
47
+ # shell — zsh (default user shell), tmux, bash-completion
48
+ # build — make
49
+ # misc — git-lfs, gettext-base (envsubst)
50
+ RUN apt-get update && apt-get install -y --no-install-recommends \
51
+ vim nano \
52
+ less jq ripgrep \
53
+ htop lsof procps psmisc tree ncdu file \
54
+ iproute2 iputils-ping dnsutils netcat-openbsd wget rsync openssh-client \
55
+ zip unzip xz-utils \
56
+ zsh tmux bash-completion \
57
+ make \
58
+ git-lfs gettext-base \
59
+ && rm -rf /var/lib/apt/lists/*
60
+
61
+ # Node.js 20 LTS + Claude Code CLI (the bridge spawns `claude` as a subprocess).
62
+ # Pinned to the 2.x line — each image build resolves the newest 2.x.y at
63
+ # build time, but the SemVer-2 ceiling protects us from a 3.0 with breaking
64
+ # arg/output changes. DISABLE_AUTOUPDATER stops the bundled CLI from trying
65
+ # to overwrite itself inside the immutable image layer at runtime, and
66
+ # CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC suppresses the update-check
67
+ # / telemetry calls that go with it.
68
+ ENV DISABLE_AUTOUPDATER=1 \
69
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC=1
70
+ RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
71
+ && apt-get install -y --no-install-recommends nodejs \
72
+ && rm -rf /var/lib/apt/lists/* \
73
+ && npm install -g @anthropic-ai/claude-code@2 \
74
+ && npm cache clean --force
75
+
76
+ # Non-root user — override UID/GID at build time to match your host so bind mounts
77
+ # (notably ~/.claude) keep correct ownership: docker compose build --build-arg USER_UID=$(id -u)
78
+ ARG USER_UID=1000
79
+ ARG USER_GID=1000
80
+ RUN groupadd --gid ${USER_GID} app \
81
+ && useradd --uid ${USER_UID} --gid ${USER_GID} --create-home --shell /usr/bin/zsh app
82
+
83
+ WORKDIR /app
84
+
85
+ COPY requirements.txt ./
86
+ RUN pip install --no-cache-dir -r requirements.txt
87
+
88
+ # --chown collapses what used to be COPY + a separate `RUN chown -R app:app`
89
+ # layer that duplicated every file (~1GB rewritten just to flip metadata).
90
+ COPY --chown=app:app . /app
91
+
92
+ # Package version — setuptools-scm normally derives this from `git describe`,
93
+ # but .git is excluded from the build context (see .dockerignore), so we
94
+ # pass it in as a build arg and let setuptools-scm read it from the env.
95
+ # CI populates it from the git tag; local builds without --build-arg fall
96
+ # back to "0.0.0+docker".
97
+ ARG SCM_VERSION=0.0.0+docker
98
+ ENV SETUPTOOLS_SCM_PRETEND_VERSION_FOR_PAINAPPLE_CODE=${SCM_VERSION}
99
+
100
+ # Editable install registers the painapple_code package against /app/src so
101
+ # `python -m painapple_code` resolves correctly without setting PYTHONPATH.
102
+ RUN pip install --no-cache-dir -e .
103
+
104
+ # Mountpoints — created up-front so volume mounts inherit the right ownership.
105
+ # /workspace gets a sentinel file that the entrypoint script checks for: any
106
+ # real bind mount overlays it, so its presence means the user forgot to mount.
107
+ RUN mkdir -p /data /workspace /home/app/.claude /home/app/.config/painapple-code \
108
+ && touch /workspace/.painapple-not-mounted \
109
+ && chown -R app:app /data /workspace /home/app/.claude /home/app/.config
110
+
111
+ # Entrypoint guard — refuses to start if /workspace isn't bind-mounted.
112
+ COPY --chmod=755 docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh
113
+
114
+ USER app
115
+
116
+ # Install user-scoped helpers — same script a host install would run.
117
+ # Drops tools/shadow-git and tools/shadow-query into ~/.local/bin and the
118
+ # shadow-git-helper agent template into ~/.claude/agents, so users get
119
+ # `shadow-git` / `shadow-query` in the in-container terminal and the agent shows
120
+ # up inside Claude Code without a separate setup step.
121
+ RUN /app/src/painapple_code/tools/install-helpers.sh
122
+
123
+ # zsh config — the bridge spawns its PTY via $SHELL (see
124
+ # routes/api_terminal.py), so an interactive terminal opened in the web UI
125
+ # lands in zsh. Bake a couple of conveniences in.
126
+ RUN printf '%s\n' \
127
+ 'alias ll="ls -lahtr"' \
128
+ 'autoload -Uz promptinit && promptinit && prompt adam1 2>/dev/null || PROMPT="%n@%m %~ %# "' \
129
+ 'setopt HIST_IGNORE_DUPS SHARE_HISTORY' \
130
+ 'HISTFILE=~/.zsh_history HISTSIZE=10000 SAVEHIST=10000' \
131
+ > /home/app/.zshrc
132
+
133
+ # Mirror the alias for bash too, in case a script or `docker exec` lands
134
+ # the user there explicitly.
135
+ RUN echo "alias ll='ls -lahtr'" >> /home/app/.bashrc
136
+
137
+ # PAINAPPLE_CODE_HOME relocates all bridge state (logs, shadow DB, sessions,
138
+ # uploads, presets) to /data so a single named volume covers everything.
139
+ # SHELL is read by the bridge's PTY spawn (routes/api_terminal.py) — set it
140
+ # explicitly so the web terminal opens zsh even though the bridge process
141
+ # itself wasn't started via login(1).
142
+ # PAINAPPLE_IN_CONTAINER marks "running containerized" — a build-time constant
143
+ # (the image only ever runs in a container). It's a last-resort fallback in
144
+ # bridge_paths.detect_environment(); the docker-vs-podman decision is made at
145
+ # runtime from /.dockerenv vs /run/.containerenv, NOT from this marker — a baked
146
+ # ENV can't tell the two apart since the same image runs on both engines.
147
+ ENV PAINAPPLE_CODE_HOME=/data \
148
+ PYTHONUNBUFFERED=1 \
149
+ SHELL=/usr/bin/zsh \
150
+ PAINAPPLE_IN_CONTAINER=1
151
+
152
+ EXPOSE 8765
153
+
154
+ ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/docker-entrypoint.sh"]
155
+ CMD ["python", "-m", "painapple_code", "--host", "0.0.0.0", "--port", "8765", "--workspace", "/workspace"]