pythinker-code 0.8.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 (790) hide show
  1. pythinker_code/CHANGELOG.md +60 -0
  2. pythinker_code/__init__.py +0 -0
  3. pythinker_code/__main__.py +97 -0
  4. pythinker_code/acp/AGENTS.md +93 -0
  5. pythinker_code/acp/__init__.py +13 -0
  6. pythinker_code/acp/convert.py +128 -0
  7. pythinker_code/acp/host.py +301 -0
  8. pythinker_code/acp/mcp.py +46 -0
  9. pythinker_code/acp/server.py +497 -0
  10. pythinker_code/acp/session.py +502 -0
  11. pythinker_code/acp/tools.py +174 -0
  12. pythinker_code/acp/types.py +13 -0
  13. pythinker_code/acp/version.py +45 -0
  14. pythinker_code/agents/default/agent.yaml +55 -0
  15. pythinker_code/agents/default/code_reviewer.yaml +47 -0
  16. pythinker_code/agents/default/coder.yaml +42 -0
  17. pythinker_code/agents/default/debugger.yaml +35 -0
  18. pythinker_code/agents/default/explore.yaml +59 -0
  19. pythinker_code/agents/default/implementer.yaml +46 -0
  20. pythinker_code/agents/default/plan.yaml +42 -0
  21. pythinker_code/agents/default/review.yaml +47 -0
  22. pythinker_code/agents/default/security_reviewer.yaml +37 -0
  23. pythinker_code/agents/default/system.md +192 -0
  24. pythinker_code/agents/default/verifier.yaml +46 -0
  25. pythinker_code/agents/okabe/agent.yaml +22 -0
  26. pythinker_code/agentspec.py +163 -0
  27. pythinker_code/app.py +847 -0
  28. pythinker_code/approval_runtime/__init__.py +29 -0
  29. pythinker_code/approval_runtime/models.py +42 -0
  30. pythinker_code/approval_runtime/runtime.py +235 -0
  31. pythinker_code/auth/__init__.py +25 -0
  32. pythinker_code/auth/anthropic_direct.py +207 -0
  33. pythinker_code/auth/deepseek.py +192 -0
  34. pythinker_code/auth/github_feedback.py +228 -0
  35. pythinker_code/auth/lm_studio.py +418 -0
  36. pythinker_code/auth/minimax.py +203 -0
  37. pythinker_code/auth/oauth.py +1145 -0
  38. pythinker_code/auth/ollama.py +293 -0
  39. pythinker_code/auth/openai.py +783 -0
  40. pythinker_code/auth/opencode_go.py +203 -0
  41. pythinker_code/auth/openrouter.py +225 -0
  42. pythinker_code/auth/platforms.py +475 -0
  43. pythinker_code/background/__init__.py +36 -0
  44. pythinker_code/background/agent_runner.py +231 -0
  45. pythinker_code/background/ids.py +19 -0
  46. pythinker_code/background/manager.py +668 -0
  47. pythinker_code/background/models.py +118 -0
  48. pythinker_code/background/store.py +243 -0
  49. pythinker_code/background/summary.py +66 -0
  50. pythinker_code/background/worker.py +209 -0
  51. pythinker_code/cli/__init__.py +1326 -0
  52. pythinker_code/cli/__main__.py +19 -0
  53. pythinker_code/cli/_lazy_group.py +268 -0
  54. pythinker_code/cli/debug.py +11 -0
  55. pythinker_code/cli/export.py +322 -0
  56. pythinker_code/cli/info.py +62 -0
  57. pythinker_code/cli/mcp.py +362 -0
  58. pythinker_code/cli/plugin.py +351 -0
  59. pythinker_code/cli/review.py +74 -0
  60. pythinker_code/cli/secscan.py +11 -0
  61. pythinker_code/cli/security_scan.py +35 -0
  62. pythinker_code/cli/toad.py +74 -0
  63. pythinker_code/cli/update.py +26 -0
  64. pythinker_code/cli/vis.py +38 -0
  65. pythinker_code/cli/web.py +80 -0
  66. pythinker_code/config.py +511 -0
  67. pythinker_code/constant.py +33 -0
  68. pythinker_code/events.py +106 -0
  69. pythinker_code/exception.py +43 -0
  70. pythinker_code/extensions.py +151 -0
  71. pythinker_code/hooks/__init__.py +4 -0
  72. pythinker_code/hooks/config.py +34 -0
  73. pythinker_code/hooks/engine.py +383 -0
  74. pythinker_code/hooks/events.py +190 -0
  75. pythinker_code/hooks/runner.py +92 -0
  76. pythinker_code/llm.py +441 -0
  77. pythinker_code/metadata.py +79 -0
  78. pythinker_code/notifications/__init__.py +33 -0
  79. pythinker_code/notifications/llm.py +77 -0
  80. pythinker_code/notifications/manager.py +145 -0
  81. pythinker_code/notifications/models.py +50 -0
  82. pythinker_code/notifications/notifier.py +41 -0
  83. pythinker_code/notifications/store.py +118 -0
  84. pythinker_code/notifications/wire.py +21 -0
  85. pythinker_code/plugin/__init__.py +124 -0
  86. pythinker_code/plugin/manager.py +166 -0
  87. pythinker_code/plugin/tool.py +173 -0
  88. pythinker_code/prompt_templates.py +181 -0
  89. pythinker_code/prompts/__init__.py +6 -0
  90. pythinker_code/prompts/compact.md +73 -0
  91. pythinker_code/prompts/init.md +21 -0
  92. pythinker_code/py.typed +0 -0
  93. pythinker_code/session.py +319 -0
  94. pythinker_code/session_fork.py +325 -0
  95. pythinker_code/session_state.py +132 -0
  96. pythinker_code/share.py +14 -0
  97. pythinker_code/skill/__init__.py +727 -0
  98. pythinker_code/skill/flow/__init__.py +99 -0
  99. pythinker_code/skill/flow/d2.py +482 -0
  100. pythinker_code/skill/flow/mermaid.py +266 -0
  101. pythinker_code/skills/pythinker-code-help/SKILL.md +54 -0
  102. pythinker_code/skills/skill-creator/SKILL.md +367 -0
  103. pythinker_code/soul/__init__.py +304 -0
  104. pythinker_code/soul/agent.py +552 -0
  105. pythinker_code/soul/approval.py +267 -0
  106. pythinker_code/soul/btw.py +220 -0
  107. pythinker_code/soul/compaction.py +189 -0
  108. pythinker_code/soul/context.py +339 -0
  109. pythinker_code/soul/denwarenji.py +39 -0
  110. pythinker_code/soul/dynamic_injection.py +84 -0
  111. pythinker_code/soul/dynamic_injections/__init__.py +0 -0
  112. pythinker_code/soul/dynamic_injections/auto_mode.py +72 -0
  113. pythinker_code/soul/dynamic_injections/plan_mode.py +239 -0
  114. pythinker_code/soul/message.py +92 -0
  115. pythinker_code/soul/permission.py +368 -0
  116. pythinker_code/soul/pythinkersoul.py +1763 -0
  117. pythinker_code/soul/slash.py +340 -0
  118. pythinker_code/soul/toolset.py +826 -0
  119. pythinker_code/subagents/__init__.py +21 -0
  120. pythinker_code/subagents/builder.py +43 -0
  121. pythinker_code/subagents/core.py +86 -0
  122. pythinker_code/subagents/discovery.py +234 -0
  123. pythinker_code/subagents/git_context.py +172 -0
  124. pythinker_code/subagents/models.py +56 -0
  125. pythinker_code/subagents/output.py +71 -0
  126. pythinker_code/subagents/registry.py +28 -0
  127. pythinker_code/subagents/runner.py +442 -0
  128. pythinker_code/subagents/store.py +200 -0
  129. pythinker_code/telemetry/__init__.py +217 -0
  130. pythinker_code/telemetry/config.py +113 -0
  131. pythinker_code/telemetry/crash.py +191 -0
  132. pythinker_code/telemetry/errors.py +113 -0
  133. pythinker_code/telemetry/metrics.py +208 -0
  134. pythinker_code/telemetry/otel.py +303 -0
  135. pythinker_code/telemetry/sentry.py +212 -0
  136. pythinker_code/telemetry/sink.py +189 -0
  137. pythinker_code/tools/AGENTS.md +6 -0
  138. pythinker_code/tools/__init__.py +105 -0
  139. pythinker_code/tools/agent/__init__.py +326 -0
  140. pythinker_code/tools/agent/description.md +65 -0
  141. pythinker_code/tools/ask_user/__init__.py +162 -0
  142. pythinker_code/tools/ask_user/description.md +19 -0
  143. pythinker_code/tools/background/__init__.py +318 -0
  144. pythinker_code/tools/background/list.md +10 -0
  145. pythinker_code/tools/background/output.md +11 -0
  146. pythinker_code/tools/background/stop.md +8 -0
  147. pythinker_code/tools/display.py +46 -0
  148. pythinker_code/tools/dmail/__init__.py +38 -0
  149. pythinker_code/tools/dmail/dmail.md +17 -0
  150. pythinker_code/tools/file/__init__.py +31 -0
  151. pythinker_code/tools/file/glob.md +17 -0
  152. pythinker_code/tools/file/glob.py +163 -0
  153. pythinker_code/tools/file/grep.md +6 -0
  154. pythinker_code/tools/file/grep_local.py +904 -0
  155. pythinker_code/tools/file/plan_mode.py +45 -0
  156. pythinker_code/tools/file/read.md +16 -0
  157. pythinker_code/tools/file/read.py +303 -0
  158. pythinker_code/tools/file/read_media.md +24 -0
  159. pythinker_code/tools/file/read_media.py +220 -0
  160. pythinker_code/tools/file/replace.md +7 -0
  161. pythinker_code/tools/file/replace.py +204 -0
  162. pythinker_code/tools/file/utils.py +257 -0
  163. pythinker_code/tools/file/write.md +5 -0
  164. pythinker_code/tools/file/write.py +186 -0
  165. pythinker_code/tools/plan/__init__.py +362 -0
  166. pythinker_code/tools/plan/description.md +29 -0
  167. pythinker_code/tools/plan/enter.py +193 -0
  168. pythinker_code/tools/plan/enter_description.md +35 -0
  169. pythinker_code/tools/plan/handoff.py +69 -0
  170. pythinker_code/tools/plan/heroes.py +277 -0
  171. pythinker_code/tools/shell/__init__.py +263 -0
  172. pythinker_code/tools/shell/bash.md +35 -0
  173. pythinker_code/tools/shell/powershell.md +30 -0
  174. pythinker_code/tools/test.py +55 -0
  175. pythinker_code/tools/think/__init__.py +21 -0
  176. pythinker_code/tools/think/think.md +1 -0
  177. pythinker_code/tools/todo/__init__.py +168 -0
  178. pythinker_code/tools/todo/set_todo_list.md +23 -0
  179. pythinker_code/tools/utils.py +200 -0
  180. pythinker_code/tools/web/__init__.py +4 -0
  181. pythinker_code/tools/web/fetch.md +1 -0
  182. pythinker_code/tools/web/fetch.py +261 -0
  183. pythinker_code/tools/web/search.md +1 -0
  184. pythinker_code/tools/web/search.py +163 -0
  185. pythinker_code/ui/__init__.py +0 -0
  186. pythinker_code/ui/acp/__init__.py +99 -0
  187. pythinker_code/ui/print/__init__.py +474 -0
  188. pythinker_code/ui/print/visualize.py +185 -0
  189. pythinker_code/ui/shell/__init__.py +1806 -0
  190. pythinker_code/ui/shell/components/__init__.py +110 -0
  191. pythinker_code/ui/shell/components/base.py +25 -0
  192. pythinker_code/ui/shell/components/bash_execution.py +249 -0
  193. pythinker_code/ui/shell/components/bordered_loader.py +62 -0
  194. pythinker_code/ui/shell/components/diff.py +308 -0
  195. pythinker_code/ui/shell/components/footer.py +231 -0
  196. pythinker_code/ui/shell/components/key_hints.py +27 -0
  197. pythinker_code/ui/shell/components/messages.py +152 -0
  198. pythinker_code/ui/shell/components/render_utils.py +198 -0
  199. pythinker_code/ui/shell/components/settings_list.py +369 -0
  200. pythinker_code/ui/shell/components/special_messages.py +125 -0
  201. pythinker_code/ui/shell/components/tool_execution.py +261 -0
  202. pythinker_code/ui/shell/console.py +109 -0
  203. pythinker_code/ui/shell/debug.py +190 -0
  204. pythinker_code/ui/shell/echo.py +30 -0
  205. pythinker_code/ui/shell/export_import.py +117 -0
  206. pythinker_code/ui/shell/keyboard.py +300 -0
  207. pythinker_code/ui/shell/keymap.py +84 -0
  208. pythinker_code/ui/shell/mcp_status.py +112 -0
  209. pythinker_code/ui/shell/model_picker.py +318 -0
  210. pythinker_code/ui/shell/oauth.py +273 -0
  211. pythinker_code/ui/shell/placeholders.py +578 -0
  212. pythinker_code/ui/shell/prompt.py +2888 -0
  213. pythinker_code/ui/shell/replay.py +215 -0
  214. pythinker_code/ui/shell/selector.py +364 -0
  215. pythinker_code/ui/shell/selectors/__init__.py +38 -0
  216. pythinker_code/ui/shell/selectors/extension.py +37 -0
  217. pythinker_code/ui/shell/selectors/oauth.py +63 -0
  218. pythinker_code/ui/shell/selectors/settings.py +349 -0
  219. pythinker_code/ui/shell/selectors/show_images.py +29 -0
  220. pythinker_code/ui/shell/selectors/theme.py +28 -0
  221. pythinker_code/ui/shell/selectors/thinking.py +42 -0
  222. pythinker_code/ui/shell/session_picker.py +227 -0
  223. pythinker_code/ui/shell/setup.py +212 -0
  224. pythinker_code/ui/shell/slash.py +1433 -0
  225. pythinker_code/ui/shell/spinner_words.py +222 -0
  226. pythinker_code/ui/shell/startup.py +32 -0
  227. pythinker_code/ui/shell/task_browser.py +486 -0
  228. pythinker_code/ui/shell/tool_renderers/__init__.py +197 -0
  229. pythinker_code/ui/shell/tool_renderers/_render_utils.py +168 -0
  230. pythinker_code/ui/shell/tool_renderers/agent.py +140 -0
  231. pythinker_code/ui/shell/tool_renderers/ask_user.py +93 -0
  232. pythinker_code/ui/shell/tool_renderers/background.py +144 -0
  233. pythinker_code/ui/shell/tool_renderers/bash.py +103 -0
  234. pythinker_code/ui/shell/tool_renderers/edit.py +163 -0
  235. pythinker_code/ui/shell/tool_renderers/find.py +81 -0
  236. pythinker_code/ui/shell/tool_renderers/generic.py +60 -0
  237. pythinker_code/ui/shell/tool_renderers/grep.py +98 -0
  238. pythinker_code/ui/shell/tool_renderers/plan.py +98 -0
  239. pythinker_code/ui/shell/tool_renderers/read.py +103 -0
  240. pythinker_code/ui/shell/tool_renderers/think.py +66 -0
  241. pythinker_code/ui/shell/tool_renderers/todo.py +164 -0
  242. pythinker_code/ui/shell/tool_renderers/web.py +128 -0
  243. pythinker_code/ui/shell/tool_renderers/write.py +102 -0
  244. pythinker_code/ui/shell/update.py +352 -0
  245. pythinker_code/ui/shell/usage.py +291 -0
  246. pythinker_code/ui/shell/usage_adapters/__init__.py +50 -0
  247. pythinker_code/ui/shell/usage_adapters/anthropic_admin.py +233 -0
  248. pythinker_code/ui/shell/usage_adapters/base.py +72 -0
  249. pythinker_code/ui/shell/usage_adapters/deepseek.py +137 -0
  250. pythinker_code/ui/shell/usage_adapters/minimax.py +236 -0
  251. pythinker_code/ui/shell/usage_adapters/openai_admin.py +225 -0
  252. pythinker_code/ui/shell/usage_adapters/openai_chatgpt.py +241 -0
  253. pythinker_code/ui/shell/usage_adapters/opencode_go.py +232 -0
  254. pythinker_code/ui/shell/usage_adapters/openrouter.py +105 -0
  255. pythinker_code/ui/shell/usage_adapters/pythinker.py +189 -0
  256. pythinker_code/ui/shell/usage_adapters/pythinker_ai.py +50 -0
  257. pythinker_code/ui/shell/usage_render.py +150 -0
  258. pythinker_code/ui/shell/visualize/__init__.py +165 -0
  259. pythinker_code/ui/shell/visualize/_approval_panel.py +539 -0
  260. pythinker_code/ui/shell/visualize/_blocks.py +802 -0
  261. pythinker_code/ui/shell/visualize/_btw_panel.py +227 -0
  262. pythinker_code/ui/shell/visualize/_input_router.py +48 -0
  263. pythinker_code/ui/shell/visualize/_interactive.py +531 -0
  264. pythinker_code/ui/shell/visualize/_live_view.py +891 -0
  265. pythinker_code/ui/shell/visualize/_question_panel.py +586 -0
  266. pythinker_code/ui/shell/visualize/_worklog.py +245 -0
  267. pythinker_code/ui/theme.py +395 -0
  268. pythinker_code/ui/tui_config.py +82 -0
  269. pythinker_code/usage_ratelimit_cache.py +175 -0
  270. pythinker_code/utils/__init__.py +0 -0
  271. pythinker_code/utils/aiohttp.py +24 -0
  272. pythinker_code/utils/aioqueue.py +72 -0
  273. pythinker_code/utils/broadcast.py +38 -0
  274. pythinker_code/utils/changelog.py +108 -0
  275. pythinker_code/utils/clipboard.py +246 -0
  276. pythinker_code/utils/datetime.py +64 -0
  277. pythinker_code/utils/diff.py +135 -0
  278. pythinker_code/utils/editor.py +91 -0
  279. pythinker_code/utils/environment.py +73 -0
  280. pythinker_code/utils/envvar.py +22 -0
  281. pythinker_code/utils/export.py +696 -0
  282. pythinker_code/utils/file_filter.py +375 -0
  283. pythinker_code/utils/frontmatter.py +70 -0
  284. pythinker_code/utils/io.py +27 -0
  285. pythinker_code/utils/logging.py +146 -0
  286. pythinker_code/utils/media_tags.py +29 -0
  287. pythinker_code/utils/message.py +24 -0
  288. pythinker_code/utils/path.py +199 -0
  289. pythinker_code/utils/proctitle.py +33 -0
  290. pythinker_code/utils/proxy.py +31 -0
  291. pythinker_code/utils/pyinstaller.py +45 -0
  292. pythinker_code/utils/rich/__init__.py +33 -0
  293. pythinker_code/utils/rich/columns.py +99 -0
  294. pythinker_code/utils/rich/diff_render.py +481 -0
  295. pythinker_code/utils/rich/markdown.py +935 -0
  296. pythinker_code/utils/rich/markdown_sample.md +108 -0
  297. pythinker_code/utils/rich/markdown_sample_short.md +2 -0
  298. pythinker_code/utils/rich/syntax.py +114 -0
  299. pythinker_code/utils/sensitive.py +54 -0
  300. pythinker_code/utils/server.py +121 -0
  301. pythinker_code/utils/signals.py +43 -0
  302. pythinker_code/utils/slashcmd.py +124 -0
  303. pythinker_code/utils/string.py +41 -0
  304. pythinker_code/utils/subprocess_env.py +83 -0
  305. pythinker_code/utils/term.py +168 -0
  306. pythinker_code/utils/typing.py +20 -0
  307. pythinker_code/vis/__init__.py +0 -0
  308. pythinker_code/vis/api/__init__.py +5 -0
  309. pythinker_code/vis/api/sessions.py +714 -0
  310. pythinker_code/vis/api/statistics.py +209 -0
  311. pythinker_code/vis/api/system.py +19 -0
  312. pythinker_code/vis/app.py +199 -0
  313. pythinker_code/vis/static/assets/highlighted-body-B3W2YXNL-CY1rtwrX.js +1 -0
  314. pythinker_code/vis/static/assets/index-DSRInNnm.css +1 -0
  315. pythinker_code/vis/static/assets/index-DgmTI2M_.js +185 -0
  316. pythinker_code/vis/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  317. pythinker_code/vis/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  318. pythinker_code/vis/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  319. pythinker_code/vis/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  320. pythinker_code/vis/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  321. pythinker_code/vis/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  322. pythinker_code/vis/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  323. pythinker_code/vis/static/index.html +17 -0
  324. pythinker_code/web/__init__.py +5 -0
  325. pythinker_code/web/api/__init__.py +15 -0
  326. pythinker_code/web/api/config.py +217 -0
  327. pythinker_code/web/api/open_in.py +233 -0
  328. pythinker_code/web/api/sessions.py +1256 -0
  329. pythinker_code/web/app.py +449 -0
  330. pythinker_code/web/auth.py +191 -0
  331. pythinker_code/web/models.py +98 -0
  332. pythinker_code/web/runner/__init__.py +5 -0
  333. pythinker_code/web/runner/messages.py +57 -0
  334. pythinker_code/web/runner/process.py +754 -0
  335. pythinker_code/web/runner/worker.py +97 -0
  336. pythinker_code/web/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  337. pythinker_code/web/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  338. pythinker_code/web/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  339. pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  340. pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  341. pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  342. pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  343. pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  344. pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  345. pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  346. pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  347. pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  348. pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  349. pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  350. pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  351. pythinker_code/web/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  352. pythinker_code/web/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  353. pythinker_code/web/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  354. pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  355. pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  356. pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  357. pythinker_code/web/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  358. pythinker_code/web/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  359. pythinker_code/web/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  360. pythinker_code/web/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  361. pythinker_code/web/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  362. pythinker_code/web/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  363. pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  364. pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  365. pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  366. pythinker_code/web/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  367. pythinker_code/web/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  368. pythinker_code/web/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  369. pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  370. pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  371. pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  372. pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  373. pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  374. pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  375. pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  376. pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  377. pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  378. pythinker_code/web/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  379. pythinker_code/web/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  380. pythinker_code/web/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  381. pythinker_code/web/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  382. pythinker_code/web/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  383. pythinker_code/web/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  384. pythinker_code/web/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  385. pythinker_code/web/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  386. pythinker_code/web/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  387. pythinker_code/web/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  388. pythinker_code/web/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  389. pythinker_code/web/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  390. pythinker_code/web/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  391. pythinker_code/web/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  392. pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  393. pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  394. pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  395. pythinker_code/web/static/assets/_baseUniq-DpSMr1jx.js +1 -0
  396. pythinker_code/web/static/assets/abap-BdImnpbu.js +1 -0
  397. pythinker_code/web/static/assets/actionscript-3-CfeIJUat.js +1 -0
  398. pythinker_code/web/static/assets/ada-bCR0ucgS.js +1 -0
  399. pythinker_code/web/static/assets/andromeeda-C-Jbm3Hp.js +1 -0
  400. pythinker_code/web/static/assets/angular-html-CU67Zn6k.js +1 -0
  401. pythinker_code/web/static/assets/angular-ts-BwZT4LLn.js +1 -0
  402. pythinker_code/web/static/assets/apache-Pmp26Uib.js +1 -0
  403. pythinker_code/web/static/assets/apex-D8_7TLub.js +1 -0
  404. pythinker_code/web/static/assets/apl-dKokRX4l.js +1 -0
  405. pythinker_code/web/static/assets/applescript-Co6uUVPk.js +1 -0
  406. pythinker_code/web/static/assets/ara-BRHolxvo.js +1 -0
  407. pythinker_code/web/static/assets/arc-DpsahJyV.js +1 -0
  408. pythinker_code/web/static/assets/architectureDiagram-VXUJARFQ-DqiRv9Eg.js +36 -0
  409. pythinker_code/web/static/assets/asciidoc-Dv7Oe6Be.js +1 -0
  410. pythinker_code/web/static/assets/asm-D_Q5rh1f.js +1 -0
  411. pythinker_code/web/static/assets/astro-CbQHKStN.js +1 -0
  412. pythinker_code/web/static/assets/aurora-x-D-2ljcwZ.js +1 -0
  413. pythinker_code/web/static/assets/awk-DMzUqQB5.js +1 -0
  414. pythinker_code/web/static/assets/ayu-dark-CmMr59Fi.js +1 -0
  415. pythinker_code/web/static/assets/ballerina-BFfxhgS-.js +1 -0
  416. pythinker_code/web/static/assets/bat-BkioyH1T.js +1 -0
  417. pythinker_code/web/static/assets/beancount-k_qm7-4y.js +1 -0
  418. pythinker_code/web/static/assets/berry-uYugtg8r.js +1 -0
  419. pythinker_code/web/static/assets/bibtex-CHM0blh-.js +1 -0
  420. pythinker_code/web/static/assets/bicep-Bmn6On1c.js +1 -0
  421. pythinker_code/web/static/assets/blade-D4QpJJKB.js +1 -0
  422. pythinker_code/web/static/assets/blockDiagram-VD42YOAC-WgtUvqbp.js +122 -0
  423. pythinker_code/web/static/assets/bsl-BO_Y6i37.js +1 -0
  424. pythinker_code/web/static/assets/c-BIGW1oBm.js +1 -0
  425. pythinker_code/web/static/assets/c3-VCDPK7BO.js +1 -0
  426. pythinker_code/web/static/assets/c4Diagram-YG6GDRKO-rK0RPuZd.js +10 -0
  427. pythinker_code/web/static/assets/cadence-Bv_4Rxtq.js +1 -0
  428. pythinker_code/web/static/assets/cairo-KRGpt6FW.js +1 -0
  429. pythinker_code/web/static/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  430. pythinker_code/web/static/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  431. pythinker_code/web/static/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  432. pythinker_code/web/static/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  433. pythinker_code/web/static/assets/channel-B0rlvkH-.js +1 -0
  434. pythinker_code/web/static/assets/chunk-4BX2VUAB-DIkMuLV-.js +1 -0
  435. pythinker_code/web/static/assets/chunk-55IACEB6-CORdm4k4.js +1 -0
  436. pythinker_code/web/static/assets/chunk-B4BG7PRW-D9xDhwHO.js +165 -0
  437. pythinker_code/web/static/assets/chunk-DI55MBZ5-BDmF9Bh-.js +220 -0
  438. pythinker_code/web/static/assets/chunk-FMBD7UC4-BCse_HmM.js +15 -0
  439. pythinker_code/web/static/assets/chunk-QN33PNHL-DCpBmTzA.js +1 -0
  440. pythinker_code/web/static/assets/chunk-QZHKN3VN-BqLuqobw.js +1 -0
  441. pythinker_code/web/static/assets/chunk-TZMSLE5B-8K2ogOKS.js +1 -0
  442. pythinker_code/web/static/assets/clarity-D53aC0YG.js +1 -0
  443. pythinker_code/web/static/assets/classDiagram-2ON5EDUG-D_ZHSii2.js +1 -0
  444. pythinker_code/web/static/assets/classDiagram-v2-WZHVMYZB-D_ZHSii2.js +1 -0
  445. pythinker_code/web/static/assets/clojure-P80f7IUj.js +1 -0
  446. pythinker_code/web/static/assets/clone-GSXejyY1.js +1 -0
  447. pythinker_code/web/static/assets/cmake-D1j8_8rp.js +1 -0
  448. pythinker_code/web/static/assets/cobol-nwyudZeR.js +1 -0
  449. pythinker_code/web/static/assets/code-block-IT6T5CEO-DWTFYA28.js +2 -0
  450. pythinker_code/web/static/assets/codeowners-Bp6g37R7.js +1 -0
  451. pythinker_code/web/static/assets/codeql-DsOJ9woJ.js +1 -0
  452. pythinker_code/web/static/assets/coffee-Ch7k5sss.js +1 -0
  453. pythinker_code/web/static/assets/common-lisp-Cg-RD9OK.js +1 -0
  454. pythinker_code/web/static/assets/coq-DkFqJrB1.js +1 -0
  455. pythinker_code/web/static/assets/cose-bilkent-S5V4N54A-BRI7ES-N.js +1 -0
  456. pythinker_code/web/static/assets/cpp-CofmeUqb.js +1 -0
  457. pythinker_code/web/static/assets/crystal-tKQVLTB8.js +1 -0
  458. pythinker_code/web/static/assets/csharp-K5feNrxe.js +1 -0
  459. pythinker_code/web/static/assets/css-DPfMkruS.js +1 -0
  460. pythinker_code/web/static/assets/csv-fuZLfV_i.js +1 -0
  461. pythinker_code/web/static/assets/cue-D82EKSYY.js +1 -0
  462. pythinker_code/web/static/assets/cypher-COkxafJQ.js +1 -0
  463. pythinker_code/web/static/assets/cytoscape.esm-B6BxUuKW.js +321 -0
  464. pythinker_code/web/static/assets/d-85-TOEBH.js +1 -0
  465. pythinker_code/web/static/assets/dagre-6UL2VRFP-Ci5GdWfi.js +4 -0
  466. pythinker_code/web/static/assets/dark-plus-C3mMm8J8.js +1 -0
  467. pythinker_code/web/static/assets/dart-CF10PKvl.js +1 -0
  468. pythinker_code/web/static/assets/dax-CEL-wOlO.js +1 -0
  469. pythinker_code/web/static/assets/defaultLocale-DX6XiGOO.js +1 -0
  470. pythinker_code/web/static/assets/desktop-BmXAJ9_W.js +1 -0
  471. pythinker_code/web/static/assets/diagram-PSM6KHXK-0hhAylV4.js +24 -0
  472. pythinker_code/web/static/assets/diagram-QEK2KX5R-8fxgaW6d.js +43 -0
  473. pythinker_code/web/static/assets/diagram-S2PKOQOG-FRr0_atE.js +24 -0
  474. pythinker_code/web/static/assets/diff-D97Zzqfu.js +1 -0
  475. pythinker_code/web/static/assets/docker-BcOcwvcX.js +1 -0
  476. pythinker_code/web/static/assets/dotenv-Da5cRb03.js +1 -0
  477. pythinker_code/web/static/assets/dracula-BzJJZx-M.js +1 -0
  478. pythinker_code/web/static/assets/dracula-soft-BXkSAIEj.js +1 -0
  479. pythinker_code/web/static/assets/dream-maker-BtqSS_iP.js +1 -0
  480. pythinker_code/web/static/assets/edge-BkV0erSs.js +1 -0
  481. pythinker_code/web/static/assets/elixir-CDX3lj18.js +1 -0
  482. pythinker_code/web/static/assets/elm-DbKCFpqz.js +1 -0
  483. pythinker_code/web/static/assets/emacs-lisp-C9XAeP06.js +1 -0
  484. pythinker_code/web/static/assets/erDiagram-Q2GNP2WA-B3T-hJUM.js +60 -0
  485. pythinker_code/web/static/assets/erb-BOJIQeun.js +1 -0
  486. pythinker_code/web/static/assets/erlang-DsQrWhSR.js +1 -0
  487. pythinker_code/web/static/assets/everforest-dark-BgDCqdQA.js +1 -0
  488. pythinker_code/web/static/assets/everforest-light-C8M2exoo.js +1 -0
  489. pythinker_code/web/static/assets/fennel-BYunw83y.js +1 -0
  490. pythinker_code/web/static/assets/fish-BvzEVeQv.js +1 -0
  491. pythinker_code/web/static/assets/flowDiagram-NV44I4VS-D0S3u7ot.js +162 -0
  492. pythinker_code/web/static/assets/fluent-C4IJs8-o.js +1 -0
  493. pythinker_code/web/static/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  494. pythinker_code/web/static/assets/fortran-free-form-BxgE0vQu.js +1 -0
  495. pythinker_code/web/static/assets/fsharp-CXgrBDvD.js +1 -0
  496. pythinker_code/web/static/assets/ganttDiagram-JELNMOA3-CHrN2a23.js +267 -0
  497. pythinker_code/web/static/assets/gdresource-B7Tvp0Sc.js +1 -0
  498. pythinker_code/web/static/assets/gdscript-DTMYz4Jt.js +1 -0
  499. pythinker_code/web/static/assets/gdshader-DkwncUOv.js +1 -0
  500. pythinker_code/web/static/assets/genie-D0YGMca9.js +1 -0
  501. pythinker_code/web/static/assets/gherkin-DyxjwDmM.js +1 -0
  502. pythinker_code/web/static/assets/git-commit-F4YmCXRG.js +1 -0
  503. pythinker_code/web/static/assets/git-rebase-r7XF79zn.js +1 -0
  504. pythinker_code/web/static/assets/gitGraphDiagram-NY62KEGX-CfcXZWg0.js +65 -0
  505. pythinker_code/web/static/assets/github-dark-DHJKELXO.js +1 -0
  506. pythinker_code/web/static/assets/github-dark-default-Cuk6v7N8.js +1 -0
  507. pythinker_code/web/static/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  508. pythinker_code/web/static/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  509. pythinker_code/web/static/assets/github-light-DAi9KRSo.js +1 -0
  510. pythinker_code/web/static/assets/github-light-default-D7oLnXFd.js +1 -0
  511. pythinker_code/web/static/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  512. pythinker_code/web/static/assets/gleam-BspZqrRM.js +1 -0
  513. pythinker_code/web/static/assets/glimmer-js-Rg0-pVw9.js +1 -0
  514. pythinker_code/web/static/assets/glimmer-ts-U6CK756n.js +1 -0
  515. pythinker_code/web/static/assets/glsl-DplSGwfg.js +1 -0
  516. pythinker_code/web/static/assets/gn-n2N0HUVH.js +1 -0
  517. pythinker_code/web/static/assets/gnuplot-DdkO51Og.js +1 -0
  518. pythinker_code/web/static/assets/go-Dn2_MT6a.js +1 -0
  519. pythinker_code/web/static/assets/graph-8jMJwCqE.js +1 -0
  520. pythinker_code/web/static/assets/graphql-ChdNCCLP.js +1 -0
  521. pythinker_code/web/static/assets/groovy-gcz8RCvz.js +1 -0
  522. pythinker_code/web/static/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  523. pythinker_code/web/static/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  524. pythinker_code/web/static/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  525. pythinker_code/web/static/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  526. pythinker_code/web/static/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  527. pythinker_code/web/static/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  528. pythinker_code/web/static/assets/hack-CaT9iCJl.js +1 -0
  529. pythinker_code/web/static/assets/haml-B8DHNrY2.js +1 -0
  530. pythinker_code/web/static/assets/handlebars-BL8al0AC.js +1 -0
  531. pythinker_code/web/static/assets/haskell-Df6bDoY_.js +1 -0
  532. pythinker_code/web/static/assets/haxe-CzTSHFRz.js +1 -0
  533. pythinker_code/web/static/assets/hcl-BWvSN4gD.js +1 -0
  534. pythinker_code/web/static/assets/hjson-D5-asLiD.js +1 -0
  535. pythinker_code/web/static/assets/hlsl-D3lLCCz7.js +1 -0
  536. pythinker_code/web/static/assets/houston-DnULxvSX.js +1 -0
  537. pythinker_code/web/static/assets/html-GMplVEZG.js +1 -0
  538. pythinker_code/web/static/assets/html-derivative-BFtXZ54Q.js +1 -0
  539. pythinker_code/web/static/assets/http-jrhK8wxY.js +1 -0
  540. pythinker_code/web/static/assets/hurl-irOxFIW8.js +1 -0
  541. pythinker_code/web/static/assets/hxml-Bvhsp5Yf.js +1 -0
  542. pythinker_code/web/static/assets/hy-DFXneXwc.js +1 -0
  543. pythinker_code/web/static/assets/imba-DGztddWO.js +1 -0
  544. pythinker_code/web/static/assets/index-BXrFnzMy.js +153 -0
  545. pythinker_code/web/static/assets/index-BpoLgcEt.js +1 -0
  546. pythinker_code/web/static/assets/index-BrfQJnRD.js +5 -0
  547. pythinker_code/web/static/assets/index-C4gFzubz.js +2 -0
  548. pythinker_code/web/static/assets/index-CzV_vCfu.css +1 -0
  549. pythinker_code/web/static/assets/index-DI2oedCt.js +19 -0
  550. pythinker_code/web/static/assets/infoDiagram-WHAUD3N6-DdxonBf3.js +2 -0
  551. pythinker_code/web/static/assets/ini-BEwlwnbL.js +1 -0
  552. pythinker_code/web/static/assets/init-Gi6I4Gst.js +1 -0
  553. pythinker_code/web/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  554. pythinker_code/web/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  555. pythinker_code/web/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  556. pythinker_code/web/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  557. pythinker_code/web/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  558. pythinker_code/web/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  559. pythinker_code/web/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  560. pythinker_code/web/static/assets/java-CylS5w8V.js +1 -0
  561. pythinker_code/web/static/assets/javascript-wDzz0qaB.js +1 -0
  562. pythinker_code/web/static/assets/jinja-4LBKfQ-Z.js +1 -0
  563. pythinker_code/web/static/assets/jison-wvAkD_A8.js +1 -0
  564. pythinker_code/web/static/assets/journeyDiagram-XKPGCS4Q-BXf4aQei.js +139 -0
  565. pythinker_code/web/static/assets/json-Cp-IABpG.js +1 -0
  566. pythinker_code/web/static/assets/json5-C9tS-k6U.js +1 -0
  567. pythinker_code/web/static/assets/jsonc-Des-eS-w.js +1 -0
  568. pythinker_code/web/static/assets/jsonl-DcaNXYhu.js +1 -0
  569. pythinker_code/web/static/assets/jsonnet-DFQXde-d.js +1 -0
  570. pythinker_code/web/static/assets/jssm-C2t-YnRu.js +1 -0
  571. pythinker_code/web/static/assets/jsx-g9-lgVsj.js +1 -0
  572. pythinker_code/web/static/assets/julia-CxzCAyBv.js +1 -0
  573. pythinker_code/web/static/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  574. pythinker_code/web/static/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  575. pythinker_code/web/static/assets/kanagawa-wave-DWedfzmr.js +1 -0
  576. pythinker_code/web/static/assets/kanban-definition-3W4ZIXB7-DLpPPOu8.js +89 -0
  577. pythinker_code/web/static/assets/katex-D2lIc1rk.css +1 -0
  578. pythinker_code/web/static/assets/kdl-DV7GczEv.js +1 -0
  579. pythinker_code/web/static/assets/kotlin-BdnUsdx6.js +1 -0
  580. pythinker_code/web/static/assets/kusto-DZf3V79B.js +1 -0
  581. pythinker_code/web/static/assets/laserwave-DUszq2jm.js +1 -0
  582. pythinker_code/web/static/assets/latex-B4uzh10-.js +1 -0
  583. pythinker_code/web/static/assets/layout-DH73UoAH.js +1 -0
  584. pythinker_code/web/static/assets/lean-BZvkOJ9d.js +1 -0
  585. pythinker_code/web/static/assets/less-B1dDrJ26.js +1 -0
  586. pythinker_code/web/static/assets/light-plus-B7mTdjB0.js +1 -0
  587. pythinker_code/web/static/assets/linear-bAer2-sK.js +1 -0
  588. pythinker_code/web/static/assets/liquid-DYVedYrR.js +1 -0
  589. pythinker_code/web/static/assets/llvm-BtvRca6l.js +1 -0
  590. pythinker_code/web/static/assets/log-2UxHyX5q.js +1 -0
  591. pythinker_code/web/static/assets/logo-BtOb2qkB.js +1 -0
  592. pythinker_code/web/static/assets/lua-BbnMAYS6.js +1 -0
  593. pythinker_code/web/static/assets/luau-C-HG3fhB.js +1 -0
  594. pythinker_code/web/static/assets/make-CHLpvVh8.js +1 -0
  595. pythinker_code/web/static/assets/markdown-Cvjx9yec.js +1 -0
  596. pythinker_code/web/static/assets/marko-DZsq8hO1.js +1 -0
  597. pythinker_code/web/static/assets/material-theme-D5KoaKCx.js +1 -0
  598. pythinker_code/web/static/assets/material-theme-darker-BfHTSMKl.js +1 -0
  599. pythinker_code/web/static/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  600. pythinker_code/web/static/assets/material-theme-ocean-CyktbL80.js +1 -0
  601. pythinker_code/web/static/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  602. pythinker_code/web/static/assets/matlab-D7o27uSR.js +1 -0
  603. pythinker_code/web/static/assets/mdc-DUICxH0z.js +1 -0
  604. pythinker_code/web/static/assets/mdx-Cmh6b_Ma.js +1 -0
  605. pythinker_code/web/static/assets/mermaid-VLURNSYL-B2P5VJ9v.css +1 -0
  606. pythinker_code/web/static/assets/mermaid-VLURNSYL-CuqbwKXv.js +465 -0
  607. pythinker_code/web/static/assets/mermaid-mWjccvbQ.js +1 -0
  608. pythinker_code/web/static/assets/mermaid.core-Nx-rTKiV.js +191 -0
  609. pythinker_code/web/static/assets/min-DbfD8Ywu.js +1 -0
  610. pythinker_code/web/static/assets/min-dark-CafNBF8u.js +1 -0
  611. pythinker_code/web/static/assets/min-light-CTRr51gU.js +1 -0
  612. pythinker_code/web/static/assets/mindmap-definition-VGOIOE7T-C6l761Ue.js +68 -0
  613. pythinker_code/web/static/assets/mipsasm-CKIfxQSi.js +1 -0
  614. pythinker_code/web/static/assets/mojo-B93PlW-d.js +1 -0
  615. pythinker_code/web/static/assets/monokai-D4h5O-jR.js +1 -0
  616. pythinker_code/web/static/assets/moonbit-Ba13S78F.js +1 -0
  617. pythinker_code/web/static/assets/move-Bu9oaDYs.js +1 -0
  618. pythinker_code/web/static/assets/narrat-DRg8JJMk.js +1 -0
  619. pythinker_code/web/static/assets/nextflow-BrzmwbiE.js +1 -0
  620. pythinker_code/web/static/assets/nginx-DknmC5AR.js +1 -0
  621. pythinker_code/web/static/assets/night-owl-C39BiMTA.js +1 -0
  622. pythinker_code/web/static/assets/nim-CVrawwO9.js +1 -0
  623. pythinker_code/web/static/assets/nix-CwoSXNpI.js +1 -0
  624. pythinker_code/web/static/assets/nord-Ddv68eIx.js +1 -0
  625. pythinker_code/web/static/assets/nushell-C-sUppwS.js +1 -0
  626. pythinker_code/web/static/assets/objective-c-DXmwc3jG.js +1 -0
  627. pythinker_code/web/static/assets/objective-cpp-CLxacb5B.js +1 -0
  628. pythinker_code/web/static/assets/ocaml-C0hk2d4L.js +1 -0
  629. pythinker_code/web/static/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  630. pythinker_code/web/static/assets/one-light-PoHY5YXO.js +1 -0
  631. pythinker_code/web/static/assets/openscad-C4EeE6gA.js +1 -0
  632. pythinker_code/web/static/assets/ordinal-Cboi1Yqb.js +1 -0
  633. pythinker_code/web/static/assets/pascal-D93ZcfNL.js +1 -0
  634. pythinker_code/web/static/assets/perl-C0TMdlhV.js +1 -0
  635. pythinker_code/web/static/assets/php-CDn_0X-4.js +1 -0
  636. pythinker_code/web/static/assets/pieDiagram-ADFJNKIX-fNg41mT9.js +30 -0
  637. pythinker_code/web/static/assets/pkl-u5AG7uiY.js +1 -0
  638. pythinker_code/web/static/assets/plastic-3e1v2bzS.js +1 -0
  639. pythinker_code/web/static/assets/plsql-ChMvpjG-.js +1 -0
  640. pythinker_code/web/static/assets/po-BTJTHyun.js +1 -0
  641. pythinker_code/web/static/assets/poimandres-CS3Unz2-.js +1 -0
  642. pythinker_code/web/static/assets/polar-C0HS_06l.js +1 -0
  643. pythinker_code/web/static/assets/postcss-CXtECtnM.js +1 -0
  644. pythinker_code/web/static/assets/powerquery-CEu0bR-o.js +1 -0
  645. pythinker_code/web/static/assets/powershell-Dpen1YoG.js +1 -0
  646. pythinker_code/web/static/assets/prisma-Dd19v3D-.js +1 -0
  647. pythinker_code/web/static/assets/prolog-CbFg5uaA.js +1 -0
  648. pythinker_code/web/static/assets/proto-C7zT0LnQ.js +1 -0
  649. pythinker_code/web/static/assets/pug-CGlum2m_.js +1 -0
  650. pythinker_code/web/static/assets/puppet-BMWR74SV.js +1 -0
  651. pythinker_code/web/static/assets/purescript-CklMAg4u.js +1 -0
  652. pythinker_code/web/static/assets/python-B6aJPvgy.js +1 -0
  653. pythinker_code/web/static/assets/qml-3beO22l8.js +1 -0
  654. pythinker_code/web/static/assets/qmldir-C8lEn-DE.js +1 -0
  655. pythinker_code/web/static/assets/qss-IeuSbFQv.js +1 -0
  656. pythinker_code/web/static/assets/quadrantDiagram-AYHSOK5B-DJz3Kx87.js +7 -0
  657. pythinker_code/web/static/assets/r-Dspwwk_N.js +1 -0
  658. pythinker_code/web/static/assets/racket-BqYA7rlc.js +1 -0
  659. pythinker_code/web/static/assets/raku-DXvB9xmW.js +1 -0
  660. pythinker_code/web/static/assets/razor-C1TweQQi.js +1 -0
  661. pythinker_code/web/static/assets/red-bN70gL4F.js +1 -0
  662. pythinker_code/web/static/assets/reg-C-SQnVFl.js +1 -0
  663. pythinker_code/web/static/assets/regexp-CDVJQ6XC.js +1 -0
  664. pythinker_code/web/static/assets/rel-C3B-1QV4.js +1 -0
  665. pythinker_code/web/static/assets/requirementDiagram-UZGBJVZJ-B4SbrfE9.js +64 -0
  666. pythinker_code/web/static/assets/riscv-BM1_JUlF.js +1 -0
  667. pythinker_code/web/static/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
  668. pythinker_code/web/static/assets/rose-pine-moon-D4_iv3hh.js +1 -0
  669. pythinker_code/web/static/assets/rose-pine-qdsjHGoJ.js +1 -0
  670. pythinker_code/web/static/assets/rosmsg-BJDFO7_C.js +1 -0
  671. pythinker_code/web/static/assets/rst-B0xPkSld.js +1 -0
  672. pythinker_code/web/static/assets/ruby-BvKwtOVI.js +1 -0
  673. pythinker_code/web/static/assets/rust-B1yitclQ.js +1 -0
  674. pythinker_code/web/static/assets/sankeyDiagram-TZEHDZUN-CoSUjLAG.js +10 -0
  675. pythinker_code/web/static/assets/sas-cz2c8ADy.js +1 -0
  676. pythinker_code/web/static/assets/sass-Cj5Yp3dK.js +1 -0
  677. pythinker_code/web/static/assets/scala-C151Ov-r.js +1 -0
  678. pythinker_code/web/static/assets/scheme-C98Dy4si.js +1 -0
  679. pythinker_code/web/static/assets/scss-OYdSNvt2.js +1 -0
  680. pythinker_code/web/static/assets/sdbl-DVxCFoDh.js +1 -0
  681. pythinker_code/web/static/assets/sequenceDiagram-WL72ISMW-PjhBNHi3.js +145 -0
  682. pythinker_code/web/static/assets/shaderlab-Dg9Lc6iA.js +1 -0
  683. pythinker_code/web/static/assets/shellscript-Yzrsuije.js +1 -0
  684. pythinker_code/web/static/assets/shellsession-BADoaaVG.js +1 -0
  685. pythinker_code/web/static/assets/slack-dark-BthQWCQV.js +1 -0
  686. pythinker_code/web/static/assets/slack-ochin-DqwNpetd.js +1 -0
  687. pythinker_code/web/static/assets/smalltalk-BERRCDM3.js +1 -0
  688. pythinker_code/web/static/assets/snazzy-light-Bw305WKR.js +1 -0
  689. pythinker_code/web/static/assets/solarized-dark-DXbdFlpD.js +1 -0
  690. pythinker_code/web/static/assets/solarized-light-L9t79GZl.js +1 -0
  691. pythinker_code/web/static/assets/solidity-rGO070M0.js +1 -0
  692. pythinker_code/web/static/assets/soy-Brmx7dQM.js +1 -0
  693. pythinker_code/web/static/assets/sparql-rVzFXLq3.js +1 -0
  694. pythinker_code/web/static/assets/splunk-BtCnVYZw.js +1 -0
  695. pythinker_code/web/static/assets/sql-BLtJtn59.js +1 -0
  696. pythinker_code/web/static/assets/ssh-config-_ykCGR6B.js +1 -0
  697. pythinker_code/web/static/assets/stata-BH5u7GGu.js +1 -0
  698. pythinker_code/web/static/assets/stateDiagram-FKZM4ZOC-DOwESt8-.js +1 -0
  699. pythinker_code/web/static/assets/stateDiagram-v2-4FDKWEC3-yl3OHWiP.js +1 -0
  700. pythinker_code/web/static/assets/stylus-BEDo0Tqx.js +1 -0
  701. pythinker_code/web/static/assets/svelte-zxCyuUbr.js +1 -0
  702. pythinker_code/web/static/assets/swift-Dg5xB15N.js +1 -0
  703. pythinker_code/web/static/assets/synthwave-84-CbfX1IO0.js +1 -0
  704. pythinker_code/web/static/assets/system-verilog-CnnmHF94.js +1 -0
  705. pythinker_code/web/static/assets/systemd-4A_iFExJ.js +1 -0
  706. pythinker_code/web/static/assets/talonscript-CkByrt1z.js +1 -0
  707. pythinker_code/web/static/assets/tasl-QIJgUcNo.js +1 -0
  708. pythinker_code/web/static/assets/tcl-dwOrl1Do.js +1 -0
  709. pythinker_code/web/static/assets/templ-W15q3VgB.js +1 -0
  710. pythinker_code/web/static/assets/terraform-BETggiCN.js +1 -0
  711. pythinker_code/web/static/assets/tex-CvyZ59Mk.js +1 -0
  712. pythinker_code/web/static/assets/timeline-definition-IT6M3QCI-CkCLnAgi.js +61 -0
  713. pythinker_code/web/static/assets/tokyo-night-hegEt444.js +1 -0
  714. pythinker_code/web/static/assets/toml-vGWfd6FD.js +1 -0
  715. pythinker_code/web/static/assets/treemap-KMMF4GRG-CZS5XwTf.js +128 -0
  716. pythinker_code/web/static/assets/ts-tags-zn1MmPIZ.js +1 -0
  717. pythinker_code/web/static/assets/tsv-B_m7g4N7.js +1 -0
  718. pythinker_code/web/static/assets/tsx-COt5Ahok.js +1 -0
  719. pythinker_code/web/static/assets/turtle-BsS91CYL.js +1 -0
  720. pythinker_code/web/static/assets/twig-CO9l9SDP.js +1 -0
  721. pythinker_code/web/static/assets/typescript-BPQ3VLAy.js +1 -0
  722. pythinker_code/web/static/assets/typespec-BGHnOYBU.js +1 -0
  723. pythinker_code/web/static/assets/typst-DHCkPAjA.js +1 -0
  724. pythinker_code/web/static/assets/v-BcVCzyr7.js +1 -0
  725. pythinker_code/web/static/assets/vala-CsfeWuGM.js +1 -0
  726. pythinker_code/web/static/assets/vb-D17OF-Vu.js +1 -0
  727. pythinker_code/web/static/assets/verilog-BQ8w6xss.js +1 -0
  728. pythinker_code/web/static/assets/vesper-DU1UobuO.js +1 -0
  729. pythinker_code/web/static/assets/vhdl-CeAyd5Ju.js +1 -0
  730. pythinker_code/web/static/assets/viml-CJc9bBzg.js +1 -0
  731. pythinker_code/web/static/assets/vitesse-black-Bkuqu6BP.js +1 -0
  732. pythinker_code/web/static/assets/vitesse-dark-D0r3Knsf.js +1 -0
  733. pythinker_code/web/static/assets/vitesse-light-CVO1_9PV.js +1 -0
  734. pythinker_code/web/static/assets/vue-DN_0RTcg.js +1 -0
  735. pythinker_code/web/static/assets/vue-html-AaS7Mt5G.js +1 -0
  736. pythinker_code/web/static/assets/vue-vine-CQOfvN7w.js +1 -0
  737. pythinker_code/web/static/assets/vyper-CDx5xZoG.js +1 -0
  738. pythinker_code/web/static/assets/wasm-CG6Dc4jp.js +1 -0
  739. pythinker_code/web/static/assets/wasm-MzD3tlZU.js +1 -0
  740. pythinker_code/web/static/assets/wenyan-BV7otONQ.js +1 -0
  741. pythinker_code/web/static/assets/wgsl-Dx-B1_4e.js +1 -0
  742. pythinker_code/web/static/assets/wikitext-BhOHFoWU.js +1 -0
  743. pythinker_code/web/static/assets/wit-5i3qLPDT.js +1 -0
  744. pythinker_code/web/static/assets/wolfram-lXgVvXCa.js +1 -0
  745. pythinker_code/web/static/assets/xml-sdJ4AIDG.js +1 -0
  746. pythinker_code/web/static/assets/xsl-CtQFsRM5.js +1 -0
  747. pythinker_code/web/static/assets/xychartDiagram-PRI3JC2R-DkqqHNLh.js +7 -0
  748. pythinker_code/web/static/assets/yaml-Buea-lGh.js +1 -0
  749. pythinker_code/web/static/assets/zenscript-DVFEvuxE.js +1 -0
  750. pythinker_code/web/static/assets/zig-VOosw3JB.js +1 -0
  751. pythinker_code/web/static/brand/apple-touch-icon.png +0 -0
  752. pythinker_code/web/static/brand/arctecture.webp +0 -0
  753. pythinker_code/web/static/brand/bimi-logo.svg +46 -0
  754. pythinker_code/web/static/brand/favicon.ico +0 -0
  755. pythinker_code/web/static/brand/fonts/dm-sans-latin-ext.woff2 +0 -0
  756. pythinker_code/web/static/brand/fonts/dm-sans-latin.woff2 +0 -0
  757. pythinker_code/web/static/brand/fonts/instrument-sans-latin-ext.woff2 +0 -0
  758. pythinker_code/web/static/brand/fonts/instrument-sans-latin.woff2 +0 -0
  759. pythinker_code/web/static/brand/fonts/instrument-serif-latin-ext.woff2 +0 -0
  760. pythinker_code/web/static/brand/fonts/instrument-serif-latin.woff2 +0 -0
  761. pythinker_code/web/static/brand/fonts/libre-baskerville-italic-latin-ext.woff2 +0 -0
  762. pythinker_code/web/static/brand/fonts/libre-baskerville-italic-latin.woff2 +0 -0
  763. pythinker_code/web/static/brand/fonts/libre-baskerville-latin-ext.woff2 +0 -0
  764. pythinker_code/web/static/brand/fonts/libre-baskerville-latin.woff2 +0 -0
  765. pythinker_code/web/static/brand/fonts/roboto-latin-ext.woff2 +0 -0
  766. pythinker_code/web/static/brand/fonts/roboto-latin.woff2 +0 -0
  767. pythinker_code/web/static/brand/icon-192.png +0 -0
  768. pythinker_code/web/static/brand/icon-512.png +0 -0
  769. pythinker_code/web/static/brand/icon.svg +1 -0
  770. pythinker_code/web/static/brand/logo.png +0 -0
  771. pythinker_code/web/static/brand/pythinker_animated.svg +79 -0
  772. pythinker_code/web/static/brand/robots.txt +4 -0
  773. pythinker_code/web/static/index.html +15 -0
  774. pythinker_code/web/static/logo.png +0 -0
  775. pythinker_code/web/store/__init__.py +1 -0
  776. pythinker_code/web/store/sessions.py +433 -0
  777. pythinker_code/wire/__init__.py +148 -0
  778. pythinker_code/wire/file.py +151 -0
  779. pythinker_code/wire/jsonrpc.py +263 -0
  780. pythinker_code/wire/protocol.py +2 -0
  781. pythinker_code/wire/root_hub.py +27 -0
  782. pythinker_code/wire/serde.py +26 -0
  783. pythinker_code/wire/server.py +1072 -0
  784. pythinker_code/wire/types.py +698 -0
  785. pythinker_code-0.8.0.dist-info/METADATA +706 -0
  786. pythinker_code-0.8.0.dist-info/RECORD +790 -0
  787. pythinker_code-0.8.0.dist-info/WHEEL +4 -0
  788. pythinker_code-0.8.0.dist-info/entry_points.txt +4 -0
  789. pythinker_code-0.8.0.dist-info/licenses/LICENSE +202 -0
  790. pythinker_code-0.8.0.dist-info/licenses/NOTICE +14 -0
@@ -0,0 +1,351 @@
1
+ """CLI commands for plugin management."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+ from typing import Annotated
7
+
8
+ import typer
9
+
10
+ from pythinker_code.plugin import PluginError
11
+
12
+ cli = typer.Typer(help="Manage plugins.")
13
+
14
+
15
+ def _parse_git_url(target: str) -> tuple[str, str | None, str | None]:
16
+ """Parse a git URL into (clone_url, subpath, branch).
17
+
18
+ Splits .git URLs at the .git boundary. For GitHub/GitLab short URLs,
19
+ treats the first two path segments as owner/repo and the rest as subpath.
20
+ Strips ``tree/{branch}/`` or ``-/tree/{branch}/`` prefixes from
21
+ browser-copied URLs and returns the branch name.
22
+ """
23
+ # Path 1: URL contains .git followed by / or end-of-string
24
+ idx = target.find(".git/")
25
+ if idx == -1 and target.endswith(".git"):
26
+ return target, None, None
27
+ if idx != -1:
28
+ clone_url = target[: idx + 4] # up to and including ".git"
29
+ rest = target[idx + 5 :] # after ".git/"
30
+ subpath = rest.strip("/") or None
31
+ return clone_url, subpath, None
32
+
33
+ # Path 2: GitHub/GitLab short URL (no .git)
34
+ from urllib.parse import urlparse
35
+
36
+ parsed = urlparse(target)
37
+ segments = [s for s in parsed.path.split("/") if s]
38
+ if len(segments) < 2:
39
+ return target, None, None
40
+
41
+ owner_repo = "/".join(segments[:2])
42
+ clone_url = f"{parsed.scheme}://{parsed.netloc}/{owner_repo}"
43
+ rest_segments = segments[2:]
44
+
45
+ # GitLab uses /-/tree/{branch}/, strip leading "-"
46
+ if rest_segments and rest_segments[0] == "-":
47
+ rest_segments = rest_segments[1:]
48
+
49
+ # Strip tree/{branch}/ prefix and extract branch
50
+ branch: str | None = None
51
+ if len(rest_segments) >= 2 and rest_segments[0] == "tree":
52
+ branch = rest_segments[1]
53
+ rest_segments = rest_segments[2:]
54
+
55
+ subpath = "/".join(rest_segments) or None
56
+ return clone_url, subpath, branch
57
+
58
+
59
+ def _extract_zip_to_plugin(zip_path: Path, tmp: Path) -> tuple[Path, Path]:
60
+ """Extract zip_path into tmp and locate the plugin directory.
61
+
62
+ Returns ``(plugin_dir, tmp)`` for cleanup by the caller. Rejects zip
63
+ members whose paths escape ``tmp``. Searches the extraction root and
64
+ one level deep for ``plugin.json``.
65
+ """
66
+ import shutil
67
+ import zipfile
68
+
69
+ try:
70
+ with zipfile.ZipFile(zip_path, "r") as zf:
71
+ for member in zf.namelist():
72
+ member_path = (tmp / member).resolve()
73
+ if not member_path.is_relative_to(tmp.resolve()):
74
+ shutil.rmtree(tmp, ignore_errors=True)
75
+ typer.echo(f"Error: zip contains unsafe path: {member}", err=True)
76
+ raise typer.Exit(1)
77
+ zf.extractall(tmp)
78
+ except zipfile.BadZipFile as exc:
79
+ shutil.rmtree(tmp, ignore_errors=True)
80
+ typer.echo(f"Error: invalid zip archive: {exc}", err=True)
81
+ raise typer.Exit(1) from exc
82
+
83
+ for candidate in [tmp] + sorted(tmp.iterdir()):
84
+ if candidate.is_dir() and (candidate / "plugin.json").exists():
85
+ return candidate, tmp
86
+ dirs = [d for d in tmp.iterdir() if d.is_dir() and not d.name.startswith("_")]
87
+ if len(dirs) == 1 and (dirs[0] / "plugin.json").exists():
88
+ return dirs[0], tmp
89
+
90
+ shutil.rmtree(tmp, ignore_errors=True)
91
+ typer.echo("Error: No plugin.json found in zip", err=True)
92
+ raise typer.Exit(1)
93
+
94
+
95
+ def _resolve_source(target: str) -> tuple[Path, Path | None]:
96
+ """Resolve plugin source to (local_dir, tmp_to_cleanup).
97
+
98
+ Returns the source directory and an optional temp directory that
99
+ the caller must clean up after use.
100
+ """
101
+ import shutil
102
+ import tempfile
103
+ from urllib.parse import urlparse
104
+
105
+ # HTTP(S) URL pointing to a .zip — download then extract.
106
+ # Checked before the git-URL branch so GitHub/GitLab archive links
107
+ # like .../archive/refs/heads/main.zip take this path.
108
+ parsed = urlparse(target)
109
+ if parsed.scheme in ("http", "https") and parsed.path.lower().endswith(".zip"):
110
+ import httpx
111
+
112
+ tmp = Path(tempfile.mkdtemp(prefix="pythinker-plugin-"))
113
+ zip_path = tmp / "_download.zip"
114
+ typer.echo(f"Downloading {target}...")
115
+ try:
116
+ with httpx.stream("GET", target, follow_redirects=True, timeout=60.0) as resp:
117
+ resp.raise_for_status()
118
+ with zip_path.open("wb") as f:
119
+ for chunk in resp.iter_bytes():
120
+ f.write(chunk)
121
+ except httpx.HTTPError as exc:
122
+ shutil.rmtree(tmp, ignore_errors=True)
123
+ typer.echo(f"Error: download failed: {exc}", err=True)
124
+ raise typer.Exit(1) from exc
125
+
126
+ return _extract_zip_to_plugin(zip_path, tmp)
127
+
128
+ # Git URL
129
+ if target.startswith(("https://", "git@", "http://")) and (
130
+ ".git/" in target
131
+ or target.endswith(".git")
132
+ or "github.com/" in target
133
+ or "gitlab.com/" in target
134
+ ):
135
+ import subprocess
136
+
137
+ clone_url, subpath, branch = _parse_git_url(target)
138
+
139
+ tmp = Path(tempfile.mkdtemp(prefix="pythinker-plugin-"))
140
+ typer.echo(f"Cloning {clone_url}...")
141
+ clone_cmd = ["git", "clone", "--depth", "1"]
142
+ if branch:
143
+ clone_cmd += ["--branch", branch]
144
+ clone_cmd += [clone_url, str(tmp / "repo")]
145
+ result = subprocess.run(
146
+ clone_cmd,
147
+ capture_output=True,
148
+ text=True,
149
+ encoding="utf-8",
150
+ errors="replace",
151
+ )
152
+ if result.returncode != 0:
153
+ shutil.rmtree(tmp, ignore_errors=True)
154
+ typer.echo(
155
+ f"Error: git clone failed: {result.stderr.strip()}",
156
+ err=True,
157
+ )
158
+ raise typer.Exit(1)
159
+
160
+ repo_root = tmp / "repo"
161
+
162
+ if subpath:
163
+ source = (repo_root / subpath).resolve()
164
+ if not source.is_relative_to(repo_root.resolve()):
165
+ shutil.rmtree(tmp, ignore_errors=True)
166
+ typer.echo(
167
+ f"Error: subpath escapes repository: {subpath}",
168
+ err=True,
169
+ )
170
+ raise typer.Exit(1)
171
+ if not source.is_dir():
172
+ shutil.rmtree(tmp, ignore_errors=True)
173
+ typer.echo(
174
+ f"Error: subpath '{subpath}' not found in repository",
175
+ err=True,
176
+ )
177
+ raise typer.Exit(1)
178
+ if not (source / "plugin.json").exists():
179
+ shutil.rmtree(tmp, ignore_errors=True)
180
+ typer.echo(
181
+ f"Error: no plugin.json in '{subpath}'",
182
+ err=True,
183
+ )
184
+ raise typer.Exit(1)
185
+ return source, tmp
186
+
187
+ # No subpath — check root first
188
+ if (repo_root / "plugin.json").exists():
189
+ return repo_root, tmp
190
+
191
+ # Scan one level for available plugins
192
+ available = sorted(
193
+ d.name for d in repo_root.iterdir() if d.is_dir() and (d / "plugin.json").exists()
194
+ )
195
+ if available:
196
+ names = "\n".join(f" - {n}" for n in available)
197
+ typer.echo(
198
+ f"Error: No plugin.json at repository root. "
199
+ f"Available plugins:\n{names}\n"
200
+ f"Use: pythinker plugin install <url>/<plugin-name>",
201
+ err=True,
202
+ )
203
+ else:
204
+ typer.echo(
205
+ "Error: No plugin.json found in repository",
206
+ err=True,
207
+ )
208
+ shutil.rmtree(tmp, ignore_errors=True)
209
+ raise typer.Exit(1)
210
+
211
+ p = Path(target).expanduser().resolve()
212
+
213
+ # Zip file
214
+ if p.is_file() and p.suffix == ".zip":
215
+ tmp = Path(tempfile.mkdtemp(prefix="pythinker-plugin-"))
216
+ typer.echo(f"Extracting {p.name}...")
217
+ return _extract_zip_to_plugin(p, tmp)
218
+
219
+ # Local directory
220
+ if p.is_dir():
221
+ return p, None
222
+
223
+ typer.echo(
224
+ f"Error: {target} is not a directory, zip file, zip URL, or git URL",
225
+ err=True,
226
+ )
227
+ raise typer.Exit(1)
228
+
229
+
230
+ @cli.command("install")
231
+ def install_cmd(
232
+ target: Annotated[
233
+ str,
234
+ typer.Argument(help="Plugin source: directory, .zip file, .zip URL, or git URL"),
235
+ ],
236
+ ) -> None:
237
+ """Install a plugin and inject host configuration."""
238
+ import shutil
239
+
240
+ from pythinker_code.config import load_config
241
+ from pythinker_code.constant import VERSION
242
+ from pythinker_code.plugin.manager import get_plugins_dir, install_plugin
243
+
244
+ source, tmp_dir = _resolve_source(target)
245
+
246
+ try:
247
+ config = load_config()
248
+
249
+ from pythinker_code.auth.oauth import OAuthManager
250
+ from pythinker_code.llm import augment_provider_with_env_vars
251
+ from pythinker_code.plugin.manager import collect_host_values
252
+
253
+ # Apply env var overrides (install runs outside normal startup)
254
+ if config.default_model and config.default_model in config.models:
255
+ model = config.models[config.default_model]
256
+ if model.provider in config.providers:
257
+ augment_provider_with_env_vars(
258
+ config.providers[model.provider], model, provider_key=model.provider
259
+ )
260
+
261
+ oauth = OAuthManager(config)
262
+ host_values = collect_host_values(config, oauth)
263
+
264
+ if not host_values.get("api_key"):
265
+ typer.echo(
266
+ "Warning: No LLM provider configured. "
267
+ "Plugins requiring API key injection will fail. "
268
+ "Run 'pythinker login' or configure a provider first.",
269
+ err=True,
270
+ )
271
+
272
+ spec = install_plugin(
273
+ source=source,
274
+ plugins_dir=get_plugins_dir(),
275
+ host_values=host_values,
276
+ host_name="pythinker-code",
277
+ host_version=VERSION,
278
+ )
279
+ except PluginError as exc:
280
+ typer.echo(f"Error: {exc}", err=True)
281
+ raise typer.Exit(1) from exc
282
+ finally:
283
+ # Clean up temp directory from zip/git extraction
284
+ if tmp_dir is not None:
285
+ shutil.rmtree(tmp_dir, ignore_errors=True)
286
+
287
+ typer.echo(f"Installed plugin '{spec.name}' v{spec.version}")
288
+ if spec.runtime:
289
+ typer.echo(f" runtime: host={spec.runtime.host}, version={spec.runtime.host_version}")
290
+
291
+
292
+ @cli.command("list")
293
+ def list_cmd() -> None:
294
+ """List installed plugins."""
295
+ from pythinker_code.plugin.manager import get_plugins_dir, list_plugins
296
+
297
+ plugins = list_plugins(get_plugins_dir())
298
+ if not plugins:
299
+ typer.echo("No plugins installed.")
300
+ return
301
+
302
+ for p in plugins:
303
+ status = "installed" if p.runtime else "not configured"
304
+ typer.echo(f" {p.name} v{p.version} ({status})")
305
+
306
+
307
+ @cli.command("remove")
308
+ def remove_cmd(
309
+ name: Annotated[str, typer.Argument(help="Plugin name to remove")],
310
+ ) -> None:
311
+ """Remove an installed plugin."""
312
+ from pythinker_code.plugin.manager import get_plugins_dir, remove_plugin
313
+
314
+ try:
315
+ remove_plugin(name, get_plugins_dir())
316
+ except PluginError as exc:
317
+ typer.echo(f"Error: {exc}", err=True)
318
+ raise typer.Exit(1) from exc
319
+
320
+ typer.echo(f"Removed plugin '{name}'")
321
+
322
+
323
+ @cli.command("info")
324
+ def info_cmd(
325
+ name: Annotated[str, typer.Argument(help="Plugin name")],
326
+ ) -> None:
327
+ """Show plugin details."""
328
+ from pythinker_code.plugin import parse_plugin_json
329
+ from pythinker_code.plugin.manager import get_plugins_dir
330
+
331
+ plugin_json = get_plugins_dir() / name / "plugin.json"
332
+ if not plugin_json.exists():
333
+ typer.echo(f"Error: Plugin '{name}' not found", err=True)
334
+ raise typer.Exit(1)
335
+
336
+ try:
337
+ spec = parse_plugin_json(plugin_json)
338
+ except PluginError as exc:
339
+ typer.echo(f"Error: {exc}", err=True)
340
+ raise typer.Exit(1) from exc
341
+
342
+ typer.echo(f"Name: {spec.name}")
343
+ typer.echo(f"Version: {spec.version}")
344
+ typer.echo(f"Description: {spec.description or '(none)'}")
345
+ typer.echo(f"Config file: {spec.config_file or '(none)'}")
346
+ if spec.inject:
347
+ typer.echo(f"Inject: {', '.join(f'{k} <- {v}' for k, v in spec.inject.items())}")
348
+ if spec.runtime:
349
+ typer.echo(f"Runtime: host={spec.runtime.host}, version={spec.runtime.host_version}")
350
+ else:
351
+ typer.echo("Runtime: (not installed via host)")
@@ -0,0 +1,74 @@
1
+ """`pythinker review` — delegates to pythinker-review with active-model wiring."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ from dataclasses import dataclass
7
+ from typing import Any, cast
8
+
9
+ from pydantic import SecretStr
10
+ from pythinker_review.cli import review as upstream_review
11
+ from pythinker_review.llm.protocol import ReviewLLM
12
+
13
+ cli = upstream_review.app
14
+
15
+
16
+ @dataclass(slots=True)
17
+ class PythinkerActiveLLM:
18
+ """Bridge pythinker-core's configured provider to the ReviewLLM protocol."""
19
+
20
+ chat_provider: Any
21
+ model_display_name: str
22
+
23
+ async def complete_json(self, *, system: str, user: str, timeout_s: float) -> str:
24
+ from pythinker_core import generate
25
+ from pythinker_core.message import Message
26
+ from pythinker_core.tooling.empty import EmptyToolset
27
+
28
+ result = await asyncio.wait_for(
29
+ generate(
30
+ self.chat_provider,
31
+ system,
32
+ EmptyToolset().tools,
33
+ [Message(role="user", content=user)],
34
+ ),
35
+ timeout=timeout_s,
36
+ )
37
+ return result.message.extract_text()
38
+
39
+
40
+ def build_active_llm(*, model_name: str | None = None) -> ReviewLLM | None:
41
+ """Build a ReviewLLM from the current Pythinker config, returning None when unset."""
42
+ from pythinker_code.auth.oauth import OAuthManager
43
+ from pythinker_code.config import LLMModel, LLMProvider, load_config
44
+ from pythinker_code.llm import augment_provider_with_env_vars, create_llm, model_display_name
45
+
46
+ config = load_config()
47
+ selected = model_name or config.default_model
48
+ if selected and selected in config.models:
49
+ model = config.models[selected].model_copy(deep=True)
50
+ provider = config.providers[model.provider].model_copy(deep=True)
51
+ else:
52
+ model = LLMModel(provider="", model="", max_context_size=100_000)
53
+ provider = LLMProvider(type="pythinker", base_url="", api_key=SecretStr(""))
54
+ augment_provider_with_env_vars(provider, model, provider_key=model.provider)
55
+ llm = create_llm(
56
+ provider,
57
+ model,
58
+ thinking=config.default_thinking,
59
+ session_id=None,
60
+ oauth=OAuthManager(config),
61
+ )
62
+ if llm is None:
63
+ return None
64
+ display = model_display_name(model.model, model)
65
+ return PythinkerActiveLLM(
66
+ chat_provider=cast(Any, llm.chat_provider), model_display_name=display
67
+ )
68
+
69
+
70
+ def _resolve_llm_with_active_model() -> ReviewLLM | None:
71
+ return build_active_llm()
72
+
73
+
74
+ upstream_review.set_llm_resolver(_resolve_llm_with_active_model)
@@ -0,0 +1,11 @@
1
+ """`pythinker secscan` — active-model wrapper for pythinker-secscan."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pythinker_review.cli import secscan as upstream_secscan
6
+
7
+ # Importing review installs the active-model resolver used by secscan.
8
+ from pythinker_code.cli import review as review_wrapper
9
+
10
+ _REVIEW_CLI = review_wrapper.cli
11
+ cli = upstream_secscan.app
@@ -0,0 +1,35 @@
1
+ """`pythinker security-scan` — active-model wrapper for Python-native Pythinker Security Scan."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+
7
+ import typer
8
+ from pythinker_review.cli import security_scan as upstream_security_scan
9
+ from pythinker_review.llm.fake import FakeReviewLLM
10
+ from pythinker_review.llm.protocol import ReviewLLM
11
+
12
+ # Importing review installs the active-model resolver shared by review/secscan.
13
+ from pythinker_code.cli import review as review_wrapper
14
+
15
+ _REVIEW_CLI = review_wrapper.cli
16
+ cli = upstream_security_scan.app
17
+
18
+
19
+ def _resolve_security_scan_llm() -> ReviewLLM:
20
+ adapter = review_wrapper.build_active_llm()
21
+ if adapter is not None:
22
+ return adapter
23
+ fake = os.environ.get("PYTHINKER_REVIEW_FAKE_LLM_RESPONSES")
24
+ if fake is not None:
25
+ return FakeReviewLLM(scripted=fake.split("\0") if fake else ["[]"])
26
+ typer.secho(
27
+ "No active model configured. Set PYTHINKER_REVIEW_FAKE_LLM_RESPONSES for tests, "
28
+ "or configure a Pythinker model before running `pythinker security-scan`.",
29
+ fg=typer.colors.RED,
30
+ err=True,
31
+ )
32
+ raise typer.Exit(code=3)
33
+
34
+
35
+ upstream_security_scan.__dict__["_resolve_llm"] = _resolve_security_scan_llm
@@ -0,0 +1,74 @@
1
+ import importlib.util
2
+ import shlex
3
+ import shutil
4
+ import subprocess
5
+ import sys
6
+ from pathlib import Path
7
+
8
+ import typer
9
+
10
+
11
+ def _default_acp_command() -> list[str]:
12
+ argv0 = sys.argv[0]
13
+ if argv0:
14
+ resolved = shutil.which(argv0)
15
+ resolved_path = Path(resolved).expanduser() if resolved else Path(argv0).expanduser()
16
+ if (
17
+ resolved_path.exists()
18
+ and resolved_path.suffix != ".py"
19
+ and not resolved_path.name.startswith(("python", "pypy"))
20
+ ):
21
+ return [str(resolved_path), "acp"]
22
+
23
+ return [sys.executable, "-m", "pythinker_code.cli", "acp"]
24
+
25
+
26
+ def _default_toad_command() -> list[str]:
27
+ if sys.version_info < (3, 14):
28
+ typer.echo("`pythinker term` requires Python 3.14+ because Toad requires it.", err=True)
29
+ raise typer.Exit(code=1)
30
+ if importlib.util.find_spec("toad") is None:
31
+ typer.echo(
32
+ "Toad dependency is missing. Install pythinker-code with Python 3.14+ "
33
+ "to use `pythinker term`.",
34
+ err=True,
35
+ )
36
+ raise typer.Exit(code=1)
37
+ return [sys.executable, "-m", "toad.cli"]
38
+
39
+
40
+ def _extract_project_dir(extra_args: list[str]) -> Path | None:
41
+ work_dir: str | None = None
42
+ idx = 0
43
+ while idx < len(extra_args):
44
+ arg = extra_args[idx]
45
+ if arg in ("--work-dir", "-w"):
46
+ if idx + 1 < len(extra_args):
47
+ work_dir = extra_args[idx + 1]
48
+ idx += 2
49
+ continue
50
+ elif arg.startswith("--work-dir=") or arg.startswith("-w="):
51
+ work_dir = arg.split("=", 1)[1]
52
+ elif arg.startswith("-w") and len(arg) > 2:
53
+ work_dir = arg[2:]
54
+ idx += 1
55
+
56
+ if not work_dir:
57
+ return None
58
+
59
+ return Path(work_dir).expanduser().resolve()
60
+
61
+
62
+ def run_term(ctx: typer.Context) -> None:
63
+ extra_args = list(ctx.args)
64
+ acp_args = _default_acp_command()
65
+ acp_command = shlex.join(acp_args)
66
+ toad_parts = _default_toad_command()
67
+ args = [*toad_parts, "acp", acp_command]
68
+ project_dir = _extract_project_dir(extra_args)
69
+ if project_dir is not None:
70
+ args.append(str(project_dir))
71
+
72
+ result = subprocess.run(args)
73
+ if result.returncode != 0:
74
+ raise typer.Exit(code=result.returncode)
@@ -0,0 +1,26 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from typing import Annotated
5
+
6
+ import typer
7
+
8
+ cli = typer.Typer(help="Check for and install Pythinker CLI updates.")
9
+
10
+
11
+ @cli.callback(invoke_without_command=True)
12
+ def update(
13
+ check_only: Annotated[
14
+ bool,
15
+ typer.Option(
16
+ "--check",
17
+ help="Only check whether an update is available; don't install.",
18
+ ),
19
+ ] = False,
20
+ ):
21
+ """Check for and install Pythinker CLI updates."""
22
+ from pythinker_code.ui.shell.update import UpdateResult, do_update
23
+
24
+ result = asyncio.run(do_update(print=True, check_only=check_only))
25
+ if result in (UpdateResult.FAILED, UpdateResult.UNSUPPORTED):
26
+ raise typer.Exit(1)
@@ -0,0 +1,38 @@
1
+ """Vis command for Pythinker Agent Tracing Visualizer."""
2
+
3
+ from typing import Annotated
4
+
5
+ import typer
6
+
7
+ cli = typer.Typer(help="Run Pythinker Agent Tracing Visualizer.")
8
+
9
+
10
+ @cli.callback(invoke_without_command=True)
11
+ def vis(
12
+ ctx: typer.Context,
13
+ host: Annotated[
14
+ str | None,
15
+ typer.Option("--host", "-h", help="Bind to specific IP address"),
16
+ ] = None,
17
+ network: Annotated[
18
+ bool,
19
+ typer.Option("--network", "-n", help="Enable network access (bind to 0.0.0.0)"),
20
+ ] = False,
21
+ port: Annotated[int, typer.Option("--port", "-p", help="Port to bind to")] = 5495,
22
+ open_browser: Annotated[
23
+ bool, typer.Option("--open/--no-open", help="Open browser automatically")
24
+ ] = True,
25
+ reload: Annotated[bool, typer.Option("--reload", help="Enable auto-reload")] = False,
26
+ ):
27
+ """Launch the agent tracing visualizer."""
28
+ from pythinker_code.vis.app import run_vis_server
29
+
30
+ # Determine bind address (same logic as pythinker web)
31
+ if host:
32
+ bind_host = host
33
+ elif network:
34
+ bind_host = "0.0.0.0"
35
+ else:
36
+ bind_host = "127.0.0.1"
37
+
38
+ run_vis_server(host=bind_host, port=port, open_browser=open_browser, reload=reload)
@@ -0,0 +1,80 @@
1
+ """Web UI command for Pythinker CLI."""
2
+
3
+ from typing import Annotated
4
+
5
+ import typer
6
+
7
+ cli = typer.Typer(help="Run Pythinker CLI web interface.")
8
+
9
+
10
+ @cli.callback(invoke_without_command=True)
11
+ def web(
12
+ ctx: typer.Context,
13
+ host: Annotated[
14
+ str | None,
15
+ typer.Option("--host", "-h", help="Bind to specific IP address"),
16
+ ] = None,
17
+ network: Annotated[
18
+ bool,
19
+ typer.Option("--network", "-n", help="Enable network access (bind to 0.0.0.0)"),
20
+ ] = False,
21
+ port: Annotated[int, typer.Option("--port", "-p", help="Port to bind to")] = 5494,
22
+ reload: Annotated[bool, typer.Option("--reload", help="Enable auto-reload")] = False,
23
+ open_browser: Annotated[
24
+ bool, typer.Option("--open/--no-open", help="Open browser automatically")
25
+ ] = True,
26
+ auth_token: Annotated[
27
+ str | None,
28
+ typer.Option("--auth-token", help="Bearer token for API authentication."),
29
+ ] = None,
30
+ allowed_origins: Annotated[
31
+ str | None,
32
+ typer.Option(
33
+ "--allowed-origins",
34
+ help="Comma-separated list of allowed Origin values.",
35
+ ),
36
+ ] = None,
37
+ dangerously_omit_auth: Annotated[
38
+ bool,
39
+ typer.Option(
40
+ "--dangerously-omit-auth",
41
+ help="Disable auth checks (dangerous in public networks).",
42
+ ),
43
+ ] = False,
44
+ restrict_sensitive_apis: Annotated[
45
+ bool | None,
46
+ typer.Option(
47
+ "--restrict-sensitive-apis/--no-restrict-sensitive-apis",
48
+ help="Disable sensitive APIs (config write, open-in, file access limits).",
49
+ ),
50
+ ] = None,
51
+ lan_only: Annotated[
52
+ bool,
53
+ typer.Option(
54
+ "--lan-only/--public",
55
+ help="Only allow access from local network (default) or allow public access.",
56
+ ),
57
+ ] = True,
58
+ ):
59
+ """Run Pythinker CLI web interface."""
60
+ from pythinker_code.web.app import run_web_server
61
+
62
+ # Determine bind address
63
+ if host:
64
+ bind_host = host
65
+ elif network:
66
+ bind_host = "0.0.0.0"
67
+ else:
68
+ bind_host = "127.0.0.1"
69
+
70
+ run_web_server(
71
+ host=bind_host,
72
+ port=port,
73
+ reload=reload,
74
+ open_browser=open_browser,
75
+ auth_token=auth_token,
76
+ allowed_origins=allowed_origins,
77
+ dangerously_omit_auth=dangerously_omit_auth,
78
+ restrict_sensitive_apis=restrict_sensitive_apis,
79
+ lan_only=lan_only,
80
+ )