pythinker-code 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (731) hide show
  1. pythinker_code/CHANGELOG.md +16 -0
  2. pythinker_code/__init__.py +0 -0
  3. pythinker_code/__main__.py +92 -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 +298 -0
  8. pythinker_code/acp/mcp.py +46 -0
  9. pythinker_code/acp/server.py +497 -0
  10. pythinker_code/acp/session.py +496 -0
  11. pythinker_code/acp/tools.py +167 -0
  12. pythinker_code/acp/types.py +13 -0
  13. pythinker_code/acp/version.py +45 -0
  14. pythinker_code/agents/default/agent.yaml +36 -0
  15. pythinker_code/agents/default/coder.yaml +25 -0
  16. pythinker_code/agents/default/explore.yaml +46 -0
  17. pythinker_code/agents/default/plan.yaml +30 -0
  18. pythinker_code/agents/default/system.md +164 -0
  19. pythinker_code/agents/okabe/agent.yaml +22 -0
  20. pythinker_code/agentspec.py +163 -0
  21. pythinker_code/app.py +820 -0
  22. pythinker_code/approval_runtime/__init__.py +29 -0
  23. pythinker_code/approval_runtime/models.py +42 -0
  24. pythinker_code/approval_runtime/runtime.py +235 -0
  25. pythinker_code/auth/__init__.py +25 -0
  26. pythinker_code/auth/anthropic_direct.py +207 -0
  27. pythinker_code/auth/deepseek.py +192 -0
  28. pythinker_code/auth/lm_studio.py +418 -0
  29. pythinker_code/auth/minimax.py +203 -0
  30. pythinker_code/auth/oauth.py +1122 -0
  31. pythinker_code/auth/ollama.py +293 -0
  32. pythinker_code/auth/openai.py +771 -0
  33. pythinker_code/auth/opencode_go.py +203 -0
  34. pythinker_code/auth/openrouter.py +225 -0
  35. pythinker_code/auth/platforms.py +466 -0
  36. pythinker_code/background/__init__.py +36 -0
  37. pythinker_code/background/agent_runner.py +231 -0
  38. pythinker_code/background/ids.py +19 -0
  39. pythinker_code/background/manager.py +650 -0
  40. pythinker_code/background/models.py +105 -0
  41. pythinker_code/background/store.py +237 -0
  42. pythinker_code/background/summary.py +66 -0
  43. pythinker_code/background/worker.py +209 -0
  44. pythinker_code/cli/__init__.py +1326 -0
  45. pythinker_code/cli/__main__.py +19 -0
  46. pythinker_code/cli/_lazy_group.py +238 -0
  47. pythinker_code/cli/export.py +322 -0
  48. pythinker_code/cli/info.py +62 -0
  49. pythinker_code/cli/mcp.py +349 -0
  50. pythinker_code/cli/plugin.py +351 -0
  51. pythinker_code/cli/toad.py +74 -0
  52. pythinker_code/cli/vis.py +38 -0
  53. pythinker_code/cli/web.py +80 -0
  54. pythinker_code/config.py +453 -0
  55. pythinker_code/constant.py +33 -0
  56. pythinker_code/exception.py +43 -0
  57. pythinker_code/hooks/__init__.py +4 -0
  58. pythinker_code/hooks/config.py +34 -0
  59. pythinker_code/hooks/engine.py +371 -0
  60. pythinker_code/hooks/events.py +190 -0
  61. pythinker_code/hooks/runner.py +89 -0
  62. pythinker_code/llm.py +412 -0
  63. pythinker_code/metadata.py +79 -0
  64. pythinker_code/notifications/__init__.py +33 -0
  65. pythinker_code/notifications/llm.py +77 -0
  66. pythinker_code/notifications/manager.py +145 -0
  67. pythinker_code/notifications/models.py +50 -0
  68. pythinker_code/notifications/notifier.py +41 -0
  69. pythinker_code/notifications/store.py +118 -0
  70. pythinker_code/notifications/wire.py +21 -0
  71. pythinker_code/plugin/__init__.py +124 -0
  72. pythinker_code/plugin/manager.py +153 -0
  73. pythinker_code/plugin/tool.py +173 -0
  74. pythinker_code/prompts/__init__.py +6 -0
  75. pythinker_code/prompts/compact.md +73 -0
  76. pythinker_code/prompts/init.md +21 -0
  77. pythinker_code/py.typed +0 -0
  78. pythinker_code/session.py +319 -0
  79. pythinker_code/session_fork.py +325 -0
  80. pythinker_code/session_state.py +132 -0
  81. pythinker_code/share.py +14 -0
  82. pythinker_code/skill/__init__.py +727 -0
  83. pythinker_code/skill/flow/__init__.py +99 -0
  84. pythinker_code/skill/flow/d2.py +482 -0
  85. pythinker_code/skill/flow/mermaid.py +266 -0
  86. pythinker_code/skills/pythinker-code-help/SKILL.md +54 -0
  87. pythinker_code/skills/skill-creator/SKILL.md +367 -0
  88. pythinker_code/soul/__init__.py +304 -0
  89. pythinker_code/soul/agent.py +520 -0
  90. pythinker_code/soul/approval.py +267 -0
  91. pythinker_code/soul/btw.py +214 -0
  92. pythinker_code/soul/compaction.py +189 -0
  93. pythinker_code/soul/context.py +339 -0
  94. pythinker_code/soul/denwarenji.py +39 -0
  95. pythinker_code/soul/dynamic_injection.py +84 -0
  96. pythinker_code/soul/dynamic_injections/__init__.py +0 -0
  97. pythinker_code/soul/dynamic_injections/auto_mode.py +72 -0
  98. pythinker_code/soul/dynamic_injections/plan_mode.py +239 -0
  99. pythinker_code/soul/message.py +92 -0
  100. pythinker_code/soul/pythinkersoul.py +1613 -0
  101. pythinker_code/soul/slash.py +340 -0
  102. pythinker_code/soul/toolset.py +788 -0
  103. pythinker_code/subagents/__init__.py +21 -0
  104. pythinker_code/subagents/builder.py +42 -0
  105. pythinker_code/subagents/core.py +86 -0
  106. pythinker_code/subagents/git_context.py +172 -0
  107. pythinker_code/subagents/models.py +54 -0
  108. pythinker_code/subagents/output.py +71 -0
  109. pythinker_code/subagents/registry.py +28 -0
  110. pythinker_code/subagents/runner.py +428 -0
  111. pythinker_code/subagents/store.py +196 -0
  112. pythinker_code/telemetry/__init__.py +211 -0
  113. pythinker_code/telemetry/config.py +54 -0
  114. pythinker_code/telemetry/crash.py +157 -0
  115. pythinker_code/telemetry/metrics.py +208 -0
  116. pythinker_code/telemetry/otel.py +240 -0
  117. pythinker_code/telemetry/sentry.py +167 -0
  118. pythinker_code/telemetry/sink.py +189 -0
  119. pythinker_code/tools/AGENTS.md +6 -0
  120. pythinker_code/tools/__init__.py +105 -0
  121. pythinker_code/tools/agent/__init__.py +277 -0
  122. pythinker_code/tools/agent/description.md +41 -0
  123. pythinker_code/tools/ask_user/__init__.py +159 -0
  124. pythinker_code/tools/ask_user/description.md +19 -0
  125. pythinker_code/tools/background/__init__.py +318 -0
  126. pythinker_code/tools/background/list.md +10 -0
  127. pythinker_code/tools/background/output.md +11 -0
  128. pythinker_code/tools/background/stop.md +8 -0
  129. pythinker_code/tools/display.py +46 -0
  130. pythinker_code/tools/dmail/__init__.py +38 -0
  131. pythinker_code/tools/dmail/dmail.md +17 -0
  132. pythinker_code/tools/file/__init__.py +30 -0
  133. pythinker_code/tools/file/glob.md +17 -0
  134. pythinker_code/tools/file/glob.py +160 -0
  135. pythinker_code/tools/file/grep.md +6 -0
  136. pythinker_code/tools/file/grep_local.py +589 -0
  137. pythinker_code/tools/file/plan_mode.py +45 -0
  138. pythinker_code/tools/file/read.md +16 -0
  139. pythinker_code/tools/file/read.py +300 -0
  140. pythinker_code/tools/file/read_media.md +24 -0
  141. pythinker_code/tools/file/read_media.py +217 -0
  142. pythinker_code/tools/file/replace.md +7 -0
  143. pythinker_code/tools/file/replace.py +195 -0
  144. pythinker_code/tools/file/utils.py +257 -0
  145. pythinker_code/tools/file/write.md +5 -0
  146. pythinker_code/tools/file/write.py +177 -0
  147. pythinker_code/tools/plan/__init__.py +327 -0
  148. pythinker_code/tools/plan/description.md +29 -0
  149. pythinker_code/tools/plan/enter.py +190 -0
  150. pythinker_code/tools/plan/enter_description.md +35 -0
  151. pythinker_code/tools/plan/heroes.py +277 -0
  152. pythinker_code/tools/shell/__init__.py +253 -0
  153. pythinker_code/tools/shell/bash.md +35 -0
  154. pythinker_code/tools/shell/powershell.md +30 -0
  155. pythinker_code/tools/test.py +55 -0
  156. pythinker_code/tools/think/__init__.py +21 -0
  157. pythinker_code/tools/think/think.md +1 -0
  158. pythinker_code/tools/todo/__init__.py +168 -0
  159. pythinker_code/tools/todo/set_todo_list.md +23 -0
  160. pythinker_code/tools/utils.py +199 -0
  161. pythinker_code/tools/web/__init__.py +4 -0
  162. pythinker_code/tools/web/fetch.md +1 -0
  163. pythinker_code/tools/web/fetch.py +189 -0
  164. pythinker_code/tools/web/search.md +1 -0
  165. pythinker_code/tools/web/search.py +163 -0
  166. pythinker_code/ui/__init__.py +0 -0
  167. pythinker_code/ui/acp/__init__.py +99 -0
  168. pythinker_code/ui/print/__init__.py +474 -0
  169. pythinker_code/ui/print/visualize.py +185 -0
  170. pythinker_code/ui/shell/__init__.py +1696 -0
  171. pythinker_code/ui/shell/console.py +109 -0
  172. pythinker_code/ui/shell/debug.py +190 -0
  173. pythinker_code/ui/shell/echo.py +17 -0
  174. pythinker_code/ui/shell/export_import.py +117 -0
  175. pythinker_code/ui/shell/keyboard.py +300 -0
  176. pythinker_code/ui/shell/mcp_status.py +113 -0
  177. pythinker_code/ui/shell/model_picker.py +318 -0
  178. pythinker_code/ui/shell/oauth.py +272 -0
  179. pythinker_code/ui/shell/placeholders.py +531 -0
  180. pythinker_code/ui/shell/prompt.py +2278 -0
  181. pythinker_code/ui/shell/replay.py +215 -0
  182. pythinker_code/ui/shell/session_picker.py +227 -0
  183. pythinker_code/ui/shell/setup.py +212 -0
  184. pythinker_code/ui/shell/slash.py +898 -0
  185. pythinker_code/ui/shell/startup.py +32 -0
  186. pythinker_code/ui/shell/task_browser.py +486 -0
  187. pythinker_code/ui/shell/update.py +350 -0
  188. pythinker_code/ui/shell/usage.py +291 -0
  189. pythinker_code/ui/shell/usage_adapters/__init__.py +50 -0
  190. pythinker_code/ui/shell/usage_adapters/anthropic_admin.py +233 -0
  191. pythinker_code/ui/shell/usage_adapters/base.py +72 -0
  192. pythinker_code/ui/shell/usage_adapters/deepseek.py +137 -0
  193. pythinker_code/ui/shell/usage_adapters/minimax.py +236 -0
  194. pythinker_code/ui/shell/usage_adapters/openai_admin.py +225 -0
  195. pythinker_code/ui/shell/usage_adapters/openai_chatgpt.py +241 -0
  196. pythinker_code/ui/shell/usage_adapters/opencode_go.py +232 -0
  197. pythinker_code/ui/shell/usage_adapters/openrouter.py +105 -0
  198. pythinker_code/ui/shell/usage_adapters/pythinker.py +189 -0
  199. pythinker_code/ui/shell/usage_adapters/pythinker_ai.py +50 -0
  200. pythinker_code/ui/shell/usage_render.py +150 -0
  201. pythinker_code/ui/shell/visualize/__init__.py +165 -0
  202. pythinker_code/ui/shell/visualize/_approval_panel.py +505 -0
  203. pythinker_code/ui/shell/visualize/_blocks.py +629 -0
  204. pythinker_code/ui/shell/visualize/_btw_panel.py +224 -0
  205. pythinker_code/ui/shell/visualize/_input_router.py +48 -0
  206. pythinker_code/ui/shell/visualize/_interactive.py +523 -0
  207. pythinker_code/ui/shell/visualize/_live_view.py +826 -0
  208. pythinker_code/ui/shell/visualize/_question_panel.py +586 -0
  209. pythinker_code/ui/theme.py +241 -0
  210. pythinker_code/usage_ratelimit_cache.py +175 -0
  211. pythinker_code/utils/__init__.py +0 -0
  212. pythinker_code/utils/aiohttp.py +24 -0
  213. pythinker_code/utils/aioqueue.py +72 -0
  214. pythinker_code/utils/broadcast.py +37 -0
  215. pythinker_code/utils/changelog.py +108 -0
  216. pythinker_code/utils/clipboard.py +246 -0
  217. pythinker_code/utils/datetime.py +64 -0
  218. pythinker_code/utils/diff.py +135 -0
  219. pythinker_code/utils/editor.py +91 -0
  220. pythinker_code/utils/environment.py +73 -0
  221. pythinker_code/utils/envvar.py +22 -0
  222. pythinker_code/utils/export.py +696 -0
  223. pythinker_code/utils/file_filter.py +375 -0
  224. pythinker_code/utils/frontmatter.py +70 -0
  225. pythinker_code/utils/io.py +27 -0
  226. pythinker_code/utils/logging.py +146 -0
  227. pythinker_code/utils/media_tags.py +29 -0
  228. pythinker_code/utils/message.py +24 -0
  229. pythinker_code/utils/path.py +199 -0
  230. pythinker_code/utils/proctitle.py +33 -0
  231. pythinker_code/utils/proxy.py +31 -0
  232. pythinker_code/utils/pyinstaller.py +45 -0
  233. pythinker_code/utils/rich/__init__.py +33 -0
  234. pythinker_code/utils/rich/columns.py +99 -0
  235. pythinker_code/utils/rich/diff_render.py +481 -0
  236. pythinker_code/utils/rich/markdown.py +900 -0
  237. pythinker_code/utils/rich/markdown_sample.md +108 -0
  238. pythinker_code/utils/rich/markdown_sample_short.md +2 -0
  239. pythinker_code/utils/rich/syntax.py +114 -0
  240. pythinker_code/utils/sensitive.py +54 -0
  241. pythinker_code/utils/server.py +121 -0
  242. pythinker_code/utils/signals.py +43 -0
  243. pythinker_code/utils/slashcmd.py +124 -0
  244. pythinker_code/utils/string.py +41 -0
  245. pythinker_code/utils/subprocess_env.py +73 -0
  246. pythinker_code/utils/term.py +168 -0
  247. pythinker_code/utils/typing.py +20 -0
  248. pythinker_code/vis/__init__.py +0 -0
  249. pythinker_code/vis/api/__init__.py +5 -0
  250. pythinker_code/vis/api/sessions.py +687 -0
  251. pythinker_code/vis/api/statistics.py +209 -0
  252. pythinker_code/vis/api/system.py +19 -0
  253. pythinker_code/vis/app.py +175 -0
  254. pythinker_code/vis/static/assets/highlighted-body-B3W2YXNL-D2MTYyJz.js +1 -0
  255. pythinker_code/vis/static/assets/index-CezafTt_.js +185 -0
  256. pythinker_code/vis/static/assets/index-DSRInNnm.css +1 -0
  257. pythinker_code/vis/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  258. pythinker_code/vis/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  259. pythinker_code/vis/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  260. pythinker_code/vis/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  261. pythinker_code/vis/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  262. pythinker_code/vis/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  263. pythinker_code/vis/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  264. pythinker_code/vis/static/index.html +17 -0
  265. pythinker_code/web/__init__.py +5 -0
  266. pythinker_code/web/api/__init__.py +15 -0
  267. pythinker_code/web/api/config.py +208 -0
  268. pythinker_code/web/api/open_in.py +199 -0
  269. pythinker_code/web/api/sessions.py +1225 -0
  270. pythinker_code/web/app.py +451 -0
  271. pythinker_code/web/auth.py +191 -0
  272. pythinker_code/web/models.py +98 -0
  273. pythinker_code/web/runner/__init__.py +5 -0
  274. pythinker_code/web/runner/messages.py +57 -0
  275. pythinker_code/web/runner/process.py +754 -0
  276. pythinker_code/web/runner/worker.py +97 -0
  277. pythinker_code/web/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
  278. pythinker_code/web/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
  279. pythinker_code/web/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
  280. pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
  281. pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
  282. pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
  283. pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
  284. pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
  285. pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
  286. pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
  287. pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
  288. pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
  289. pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
  290. pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
  291. pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
  292. pythinker_code/web/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
  293. pythinker_code/web/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
  294. pythinker_code/web/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
  295. pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
  296. pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
  297. pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
  298. pythinker_code/web/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
  299. pythinker_code/web/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
  300. pythinker_code/web/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
  301. pythinker_code/web/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
  302. pythinker_code/web/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
  303. pythinker_code/web/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
  304. pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
  305. pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
  306. pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
  307. pythinker_code/web/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
  308. pythinker_code/web/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
  309. pythinker_code/web/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
  310. pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
  311. pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
  312. pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
  313. pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
  314. pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
  315. pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
  316. pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
  317. pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
  318. pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
  319. pythinker_code/web/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
  320. pythinker_code/web/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
  321. pythinker_code/web/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
  322. pythinker_code/web/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
  323. pythinker_code/web/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
  324. pythinker_code/web/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
  325. pythinker_code/web/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
  326. pythinker_code/web/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
  327. pythinker_code/web/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
  328. pythinker_code/web/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
  329. pythinker_code/web/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
  330. pythinker_code/web/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
  331. pythinker_code/web/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
  332. pythinker_code/web/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
  333. pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
  334. pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
  335. pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
  336. pythinker_code/web/static/assets/_baseUniq--dyU3g5v.js +1 -0
  337. pythinker_code/web/static/assets/abap-BdImnpbu.js +1 -0
  338. pythinker_code/web/static/assets/actionscript-3-CfeIJUat.js +1 -0
  339. pythinker_code/web/static/assets/ada-bCR0ucgS.js +1 -0
  340. pythinker_code/web/static/assets/andromeeda-C-Jbm3Hp.js +1 -0
  341. pythinker_code/web/static/assets/angular-html-CU67Zn6k.js +1 -0
  342. pythinker_code/web/static/assets/angular-ts-BwZT4LLn.js +1 -0
  343. pythinker_code/web/static/assets/apache-Pmp26Uib.js +1 -0
  344. pythinker_code/web/static/assets/apex-D8_7TLub.js +1 -0
  345. pythinker_code/web/static/assets/apl-dKokRX4l.js +1 -0
  346. pythinker_code/web/static/assets/applescript-Co6uUVPk.js +1 -0
  347. pythinker_code/web/static/assets/ara-BRHolxvo.js +1 -0
  348. pythinker_code/web/static/assets/arc-DkMjLpYa.js +1 -0
  349. pythinker_code/web/static/assets/architectureDiagram-VXUJARFQ-CHWVaGo9.js +36 -0
  350. pythinker_code/web/static/assets/asciidoc-Dv7Oe6Be.js +1 -0
  351. pythinker_code/web/static/assets/asm-D_Q5rh1f.js +1 -0
  352. pythinker_code/web/static/assets/astro-CbQHKStN.js +1 -0
  353. pythinker_code/web/static/assets/aurora-x-D-2ljcwZ.js +1 -0
  354. pythinker_code/web/static/assets/awk-DMzUqQB5.js +1 -0
  355. pythinker_code/web/static/assets/ayu-dark-CmMr59Fi.js +1 -0
  356. pythinker_code/web/static/assets/ballerina-BFfxhgS-.js +1 -0
  357. pythinker_code/web/static/assets/bat-BkioyH1T.js +1 -0
  358. pythinker_code/web/static/assets/beancount-k_qm7-4y.js +1 -0
  359. pythinker_code/web/static/assets/berry-uYugtg8r.js +1 -0
  360. pythinker_code/web/static/assets/bibtex-CHM0blh-.js +1 -0
  361. pythinker_code/web/static/assets/bicep-Bmn6On1c.js +1 -0
  362. pythinker_code/web/static/assets/blade-D4QpJJKB.js +1 -0
  363. pythinker_code/web/static/assets/blockDiagram-VD42YOAC-DzdKe497.js +122 -0
  364. pythinker_code/web/static/assets/bsl-BO_Y6i37.js +1 -0
  365. pythinker_code/web/static/assets/c-BIGW1oBm.js +1 -0
  366. pythinker_code/web/static/assets/c3-VCDPK7BO.js +1 -0
  367. pythinker_code/web/static/assets/c4Diagram-YG6GDRKO-D84Blotg.js +10 -0
  368. pythinker_code/web/static/assets/cadence-Bv_4Rxtq.js +1 -0
  369. pythinker_code/web/static/assets/cairo-KRGpt6FW.js +1 -0
  370. pythinker_code/web/static/assets/catppuccin-frappe-DFWUc33u.js +1 -0
  371. pythinker_code/web/static/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
  372. pythinker_code/web/static/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
  373. pythinker_code/web/static/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
  374. pythinker_code/web/static/assets/channel-CllSjjdl.js +1 -0
  375. pythinker_code/web/static/assets/chunk-4BX2VUAB-C9w8wleE.js +1 -0
  376. pythinker_code/web/static/assets/chunk-55IACEB6-YlYJ8HnF.js +1 -0
  377. pythinker_code/web/static/assets/chunk-B4BG7PRW-Bwtz_AHU.js +165 -0
  378. pythinker_code/web/static/assets/chunk-DI55MBZ5-BbEHkl8h.js +220 -0
  379. pythinker_code/web/static/assets/chunk-FMBD7UC4-BKPbvjLC.js +15 -0
  380. pythinker_code/web/static/assets/chunk-QN33PNHL-D73dQvKf.js +1 -0
  381. pythinker_code/web/static/assets/chunk-QZHKN3VN-zGiLKes_.js +1 -0
  382. pythinker_code/web/static/assets/chunk-TZMSLE5B-LHJCi2fy.js +1 -0
  383. pythinker_code/web/static/assets/clarity-D53aC0YG.js +1 -0
  384. pythinker_code/web/static/assets/classDiagram-2ON5EDUG-vX27iZwa.js +1 -0
  385. pythinker_code/web/static/assets/classDiagram-v2-WZHVMYZB-vX27iZwa.js +1 -0
  386. pythinker_code/web/static/assets/clojure-P80f7IUj.js +1 -0
  387. pythinker_code/web/static/assets/clone-DYBkaPm2.js +1 -0
  388. pythinker_code/web/static/assets/cmake-D1j8_8rp.js +1 -0
  389. pythinker_code/web/static/assets/cobol-nwyudZeR.js +1 -0
  390. pythinker_code/web/static/assets/code-block-IT6T5CEO-NtKViZGl.js +2 -0
  391. pythinker_code/web/static/assets/codeowners-Bp6g37R7.js +1 -0
  392. pythinker_code/web/static/assets/codeql-DsOJ9woJ.js +1 -0
  393. pythinker_code/web/static/assets/coffee-Ch7k5sss.js +1 -0
  394. pythinker_code/web/static/assets/common-lisp-Cg-RD9OK.js +1 -0
  395. pythinker_code/web/static/assets/coq-DkFqJrB1.js +1 -0
  396. pythinker_code/web/static/assets/cose-bilkent-S5V4N54A-DialjZpd.js +1 -0
  397. pythinker_code/web/static/assets/cpp-CofmeUqb.js +1 -0
  398. pythinker_code/web/static/assets/crystal-tKQVLTB8.js +1 -0
  399. pythinker_code/web/static/assets/csharp-K5feNrxe.js +1 -0
  400. pythinker_code/web/static/assets/css-DPfMkruS.js +1 -0
  401. pythinker_code/web/static/assets/csv-fuZLfV_i.js +1 -0
  402. pythinker_code/web/static/assets/cue-D82EKSYY.js +1 -0
  403. pythinker_code/web/static/assets/cypher-COkxafJQ.js +1 -0
  404. pythinker_code/web/static/assets/cytoscape.esm-C_Fzpdck.js +321 -0
  405. pythinker_code/web/static/assets/d-85-TOEBH.js +1 -0
  406. pythinker_code/web/static/assets/dagre-6UL2VRFP-DfuvkZZ7.js +4 -0
  407. pythinker_code/web/static/assets/dark-plus-C3mMm8J8.js +1 -0
  408. pythinker_code/web/static/assets/dart-CF10PKvl.js +1 -0
  409. pythinker_code/web/static/assets/dax-CEL-wOlO.js +1 -0
  410. pythinker_code/web/static/assets/defaultLocale-DX6XiGOO.js +1 -0
  411. pythinker_code/web/static/assets/desktop-BmXAJ9_W.js +1 -0
  412. pythinker_code/web/static/assets/diagram-PSM6KHXK-DLGctX3v.js +24 -0
  413. pythinker_code/web/static/assets/diagram-QEK2KX5R-DnxN6S0F.js +43 -0
  414. pythinker_code/web/static/assets/diagram-S2PKOQOG-Caq_Set9.js +24 -0
  415. pythinker_code/web/static/assets/diff-D97Zzqfu.js +1 -0
  416. pythinker_code/web/static/assets/docker-BcOcwvcX.js +1 -0
  417. pythinker_code/web/static/assets/dotenv-Da5cRb03.js +1 -0
  418. pythinker_code/web/static/assets/dracula-BzJJZx-M.js +1 -0
  419. pythinker_code/web/static/assets/dracula-soft-BXkSAIEj.js +1 -0
  420. pythinker_code/web/static/assets/dream-maker-BtqSS_iP.js +1 -0
  421. pythinker_code/web/static/assets/edge-BkV0erSs.js +1 -0
  422. pythinker_code/web/static/assets/elixir-CDX3lj18.js +1 -0
  423. pythinker_code/web/static/assets/elm-DbKCFpqz.js +1 -0
  424. pythinker_code/web/static/assets/emacs-lisp-C9XAeP06.js +1 -0
  425. pythinker_code/web/static/assets/erDiagram-Q2GNP2WA-BgTfALoK.js +60 -0
  426. pythinker_code/web/static/assets/erb-BOJIQeun.js +1 -0
  427. pythinker_code/web/static/assets/erlang-DsQrWhSR.js +1 -0
  428. pythinker_code/web/static/assets/everforest-dark-BgDCqdQA.js +1 -0
  429. pythinker_code/web/static/assets/everforest-light-C8M2exoo.js +1 -0
  430. pythinker_code/web/static/assets/fennel-BYunw83y.js +1 -0
  431. pythinker_code/web/static/assets/fish-BvzEVeQv.js +1 -0
  432. pythinker_code/web/static/assets/flowDiagram-NV44I4VS-QjW_fnGi.js +162 -0
  433. pythinker_code/web/static/assets/fluent-C4IJs8-o.js +1 -0
  434. pythinker_code/web/static/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
  435. pythinker_code/web/static/assets/fortran-free-form-BxgE0vQu.js +1 -0
  436. pythinker_code/web/static/assets/fsharp-CXgrBDvD.js +1 -0
  437. pythinker_code/web/static/assets/ganttDiagram-JELNMOA3-fqi8JFof.js +267 -0
  438. pythinker_code/web/static/assets/gdresource-B7Tvp0Sc.js +1 -0
  439. pythinker_code/web/static/assets/gdscript-DTMYz4Jt.js +1 -0
  440. pythinker_code/web/static/assets/gdshader-DkwncUOv.js +1 -0
  441. pythinker_code/web/static/assets/genie-D0YGMca9.js +1 -0
  442. pythinker_code/web/static/assets/gherkin-DyxjwDmM.js +1 -0
  443. pythinker_code/web/static/assets/git-commit-F4YmCXRG.js +1 -0
  444. pythinker_code/web/static/assets/git-rebase-r7XF79zn.js +1 -0
  445. pythinker_code/web/static/assets/gitGraphDiagram-NY62KEGX-i7o6VQ8x.js +65 -0
  446. pythinker_code/web/static/assets/github-dark-DHJKELXO.js +1 -0
  447. pythinker_code/web/static/assets/github-dark-default-Cuk6v7N8.js +1 -0
  448. pythinker_code/web/static/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
  449. pythinker_code/web/static/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
  450. pythinker_code/web/static/assets/github-light-DAi9KRSo.js +1 -0
  451. pythinker_code/web/static/assets/github-light-default-D7oLnXFd.js +1 -0
  452. pythinker_code/web/static/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
  453. pythinker_code/web/static/assets/gleam-BspZqrRM.js +1 -0
  454. pythinker_code/web/static/assets/glimmer-js-Rg0-pVw9.js +1 -0
  455. pythinker_code/web/static/assets/glimmer-ts-U6CK756n.js +1 -0
  456. pythinker_code/web/static/assets/glsl-DplSGwfg.js +1 -0
  457. pythinker_code/web/static/assets/gn-n2N0HUVH.js +1 -0
  458. pythinker_code/web/static/assets/gnuplot-DdkO51Og.js +1 -0
  459. pythinker_code/web/static/assets/go-Dn2_MT6a.js +1 -0
  460. pythinker_code/web/static/assets/graph-C0vZK2pT.js +1 -0
  461. pythinker_code/web/static/assets/graphql-ChdNCCLP.js +1 -0
  462. pythinker_code/web/static/assets/groovy-gcz8RCvz.js +1 -0
  463. pythinker_code/web/static/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
  464. pythinker_code/web/static/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
  465. pythinker_code/web/static/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
  466. pythinker_code/web/static/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
  467. pythinker_code/web/static/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
  468. pythinker_code/web/static/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
  469. pythinker_code/web/static/assets/hack-CaT9iCJl.js +1 -0
  470. pythinker_code/web/static/assets/haml-B8DHNrY2.js +1 -0
  471. pythinker_code/web/static/assets/handlebars-BL8al0AC.js +1 -0
  472. pythinker_code/web/static/assets/haskell-Df6bDoY_.js +1 -0
  473. pythinker_code/web/static/assets/haxe-CzTSHFRz.js +1 -0
  474. pythinker_code/web/static/assets/hcl-BWvSN4gD.js +1 -0
  475. pythinker_code/web/static/assets/hjson-D5-asLiD.js +1 -0
  476. pythinker_code/web/static/assets/hlsl-D3lLCCz7.js +1 -0
  477. pythinker_code/web/static/assets/houston-DnULxvSX.js +1 -0
  478. pythinker_code/web/static/assets/html-GMplVEZG.js +1 -0
  479. pythinker_code/web/static/assets/html-derivative-BFtXZ54Q.js +1 -0
  480. pythinker_code/web/static/assets/http-jrhK8wxY.js +1 -0
  481. pythinker_code/web/static/assets/hurl-irOxFIW8.js +1 -0
  482. pythinker_code/web/static/assets/hxml-Bvhsp5Yf.js +1 -0
  483. pythinker_code/web/static/assets/hy-DFXneXwc.js +1 -0
  484. pythinker_code/web/static/assets/imba-DGztddWO.js +1 -0
  485. pythinker_code/web/static/assets/index-BYCCk6-K.js +153 -0
  486. pythinker_code/web/static/assets/index-BpoLgcEt.js +1 -0
  487. pythinker_code/web/static/assets/index-Cpy4G3uJ.js +2 -0
  488. pythinker_code/web/static/assets/index-CzV_vCfu.css +1 -0
  489. pythinker_code/web/static/assets/index-DI2oedCt.js +19 -0
  490. pythinker_code/web/static/assets/index-DdIkp80K.js +5 -0
  491. pythinker_code/web/static/assets/infoDiagram-WHAUD3N6-BMPpeZSM.js +2 -0
  492. pythinker_code/web/static/assets/ini-BEwlwnbL.js +1 -0
  493. pythinker_code/web/static/assets/init-Gi6I4Gst.js +1 -0
  494. pythinker_code/web/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
  495. pythinker_code/web/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
  496. pythinker_code/web/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
  497. pythinker_code/web/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
  498. pythinker_code/web/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  499. pythinker_code/web/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  500. pythinker_code/web/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
  501. pythinker_code/web/static/assets/java-CylS5w8V.js +1 -0
  502. pythinker_code/web/static/assets/javascript-wDzz0qaB.js +1 -0
  503. pythinker_code/web/static/assets/jinja-4LBKfQ-Z.js +1 -0
  504. pythinker_code/web/static/assets/jison-wvAkD_A8.js +1 -0
  505. pythinker_code/web/static/assets/journeyDiagram-XKPGCS4Q-DAM7gngo.js +139 -0
  506. pythinker_code/web/static/assets/json-Cp-IABpG.js +1 -0
  507. pythinker_code/web/static/assets/json5-C9tS-k6U.js +1 -0
  508. pythinker_code/web/static/assets/jsonc-Des-eS-w.js +1 -0
  509. pythinker_code/web/static/assets/jsonl-DcaNXYhu.js +1 -0
  510. pythinker_code/web/static/assets/jsonnet-DFQXde-d.js +1 -0
  511. pythinker_code/web/static/assets/jssm-C2t-YnRu.js +1 -0
  512. pythinker_code/web/static/assets/jsx-g9-lgVsj.js +1 -0
  513. pythinker_code/web/static/assets/julia-CxzCAyBv.js +1 -0
  514. pythinker_code/web/static/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
  515. pythinker_code/web/static/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
  516. pythinker_code/web/static/assets/kanagawa-wave-DWedfzmr.js +1 -0
  517. pythinker_code/web/static/assets/kanban-definition-3W4ZIXB7-ChpBpV0k.js +89 -0
  518. pythinker_code/web/static/assets/katex-D2lIc1rk.css +1 -0
  519. pythinker_code/web/static/assets/kdl-DV7GczEv.js +1 -0
  520. pythinker_code/web/static/assets/kotlin-BdnUsdx6.js +1 -0
  521. pythinker_code/web/static/assets/kusto-DZf3V79B.js +1 -0
  522. pythinker_code/web/static/assets/laserwave-DUszq2jm.js +1 -0
  523. pythinker_code/web/static/assets/latex-B4uzh10-.js +1 -0
  524. pythinker_code/web/static/assets/layout-C3Jp1gKO.js +1 -0
  525. pythinker_code/web/static/assets/lean-BZvkOJ9d.js +1 -0
  526. pythinker_code/web/static/assets/less-B1dDrJ26.js +1 -0
  527. pythinker_code/web/static/assets/light-plus-B7mTdjB0.js +1 -0
  528. pythinker_code/web/static/assets/linear-BGHtL1N4.js +1 -0
  529. pythinker_code/web/static/assets/liquid-DYVedYrR.js +1 -0
  530. pythinker_code/web/static/assets/llvm-BtvRca6l.js +1 -0
  531. pythinker_code/web/static/assets/log-2UxHyX5q.js +1 -0
  532. pythinker_code/web/static/assets/logo-BtOb2qkB.js +1 -0
  533. pythinker_code/web/static/assets/lua-BbnMAYS6.js +1 -0
  534. pythinker_code/web/static/assets/luau-C-HG3fhB.js +1 -0
  535. pythinker_code/web/static/assets/make-CHLpvVh8.js +1 -0
  536. pythinker_code/web/static/assets/markdown-Cvjx9yec.js +1 -0
  537. pythinker_code/web/static/assets/marko-DZsq8hO1.js +1 -0
  538. pythinker_code/web/static/assets/material-theme-D5KoaKCx.js +1 -0
  539. pythinker_code/web/static/assets/material-theme-darker-BfHTSMKl.js +1 -0
  540. pythinker_code/web/static/assets/material-theme-lighter-B0m2ddpp.js +1 -0
  541. pythinker_code/web/static/assets/material-theme-ocean-CyktbL80.js +1 -0
  542. pythinker_code/web/static/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
  543. pythinker_code/web/static/assets/matlab-D7o27uSR.js +1 -0
  544. pythinker_code/web/static/assets/mdc-DUICxH0z.js +1 -0
  545. pythinker_code/web/static/assets/mdx-Cmh6b_Ma.js +1 -0
  546. pythinker_code/web/static/assets/mermaid-VLURNSYL-B2P5VJ9v.css +1 -0
  547. pythinker_code/web/static/assets/mermaid-VLURNSYL-C_HW6koB.js +465 -0
  548. pythinker_code/web/static/assets/mermaid-mWjccvbQ.js +1 -0
  549. pythinker_code/web/static/assets/mermaid.core-CnT4VrPC.js +191 -0
  550. pythinker_code/web/static/assets/min-Dn5VRVmX.js +1 -0
  551. pythinker_code/web/static/assets/min-dark-CafNBF8u.js +1 -0
  552. pythinker_code/web/static/assets/min-light-CTRr51gU.js +1 -0
  553. pythinker_code/web/static/assets/mindmap-definition-VGOIOE7T-x8EwhfIt.js +68 -0
  554. pythinker_code/web/static/assets/mipsasm-CKIfxQSi.js +1 -0
  555. pythinker_code/web/static/assets/mojo-B93PlW-d.js +1 -0
  556. pythinker_code/web/static/assets/monokai-D4h5O-jR.js +1 -0
  557. pythinker_code/web/static/assets/moonbit-Ba13S78F.js +1 -0
  558. pythinker_code/web/static/assets/move-Bu9oaDYs.js +1 -0
  559. pythinker_code/web/static/assets/narrat-DRg8JJMk.js +1 -0
  560. pythinker_code/web/static/assets/nextflow-BrzmwbiE.js +1 -0
  561. pythinker_code/web/static/assets/nginx-DknmC5AR.js +1 -0
  562. pythinker_code/web/static/assets/night-owl-C39BiMTA.js +1 -0
  563. pythinker_code/web/static/assets/nim-CVrawwO9.js +1 -0
  564. pythinker_code/web/static/assets/nix-CwoSXNpI.js +1 -0
  565. pythinker_code/web/static/assets/nord-Ddv68eIx.js +1 -0
  566. pythinker_code/web/static/assets/nushell-C-sUppwS.js +1 -0
  567. pythinker_code/web/static/assets/objective-c-DXmwc3jG.js +1 -0
  568. pythinker_code/web/static/assets/objective-cpp-CLxacb5B.js +1 -0
  569. pythinker_code/web/static/assets/ocaml-C0hk2d4L.js +1 -0
  570. pythinker_code/web/static/assets/one-dark-pro-DVMEJ2y_.js +1 -0
  571. pythinker_code/web/static/assets/one-light-PoHY5YXO.js +1 -0
  572. pythinker_code/web/static/assets/openscad-C4EeE6gA.js +1 -0
  573. pythinker_code/web/static/assets/ordinal-Cboi1Yqb.js +1 -0
  574. pythinker_code/web/static/assets/pascal-D93ZcfNL.js +1 -0
  575. pythinker_code/web/static/assets/perl-C0TMdlhV.js +1 -0
  576. pythinker_code/web/static/assets/php-CDn_0X-4.js +1 -0
  577. pythinker_code/web/static/assets/pieDiagram-ADFJNKIX-DgxBKGz2.js +30 -0
  578. pythinker_code/web/static/assets/pkl-u5AG7uiY.js +1 -0
  579. pythinker_code/web/static/assets/plastic-3e1v2bzS.js +1 -0
  580. pythinker_code/web/static/assets/plsql-ChMvpjG-.js +1 -0
  581. pythinker_code/web/static/assets/po-BTJTHyun.js +1 -0
  582. pythinker_code/web/static/assets/poimandres-CS3Unz2-.js +1 -0
  583. pythinker_code/web/static/assets/polar-C0HS_06l.js +1 -0
  584. pythinker_code/web/static/assets/postcss-CXtECtnM.js +1 -0
  585. pythinker_code/web/static/assets/powerquery-CEu0bR-o.js +1 -0
  586. pythinker_code/web/static/assets/powershell-Dpen1YoG.js +1 -0
  587. pythinker_code/web/static/assets/prisma-Dd19v3D-.js +1 -0
  588. pythinker_code/web/static/assets/prolog-CbFg5uaA.js +1 -0
  589. pythinker_code/web/static/assets/proto-C7zT0LnQ.js +1 -0
  590. pythinker_code/web/static/assets/pug-CGlum2m_.js +1 -0
  591. pythinker_code/web/static/assets/puppet-BMWR74SV.js +1 -0
  592. pythinker_code/web/static/assets/purescript-CklMAg4u.js +1 -0
  593. pythinker_code/web/static/assets/python-B6aJPvgy.js +1 -0
  594. pythinker_code/web/static/assets/qml-3beO22l8.js +1 -0
  595. pythinker_code/web/static/assets/qmldir-C8lEn-DE.js +1 -0
  596. pythinker_code/web/static/assets/qss-IeuSbFQv.js +1 -0
  597. pythinker_code/web/static/assets/quadrantDiagram-AYHSOK5B-DSpe_dqk.js +7 -0
  598. pythinker_code/web/static/assets/r-Dspwwk_N.js +1 -0
  599. pythinker_code/web/static/assets/racket-BqYA7rlc.js +1 -0
  600. pythinker_code/web/static/assets/raku-DXvB9xmW.js +1 -0
  601. pythinker_code/web/static/assets/razor-C1TweQQi.js +1 -0
  602. pythinker_code/web/static/assets/red-bN70gL4F.js +1 -0
  603. pythinker_code/web/static/assets/reg-C-SQnVFl.js +1 -0
  604. pythinker_code/web/static/assets/regexp-CDVJQ6XC.js +1 -0
  605. pythinker_code/web/static/assets/rel-C3B-1QV4.js +1 -0
  606. pythinker_code/web/static/assets/requirementDiagram-UZGBJVZJ-8o9hozL-.js +64 -0
  607. pythinker_code/web/static/assets/riscv-BM1_JUlF.js +1 -0
  608. pythinker_code/web/static/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
  609. pythinker_code/web/static/assets/rose-pine-moon-D4_iv3hh.js +1 -0
  610. pythinker_code/web/static/assets/rose-pine-qdsjHGoJ.js +1 -0
  611. pythinker_code/web/static/assets/rosmsg-BJDFO7_C.js +1 -0
  612. pythinker_code/web/static/assets/rst-B0xPkSld.js +1 -0
  613. pythinker_code/web/static/assets/ruby-BvKwtOVI.js +1 -0
  614. pythinker_code/web/static/assets/rust-B1yitclQ.js +1 -0
  615. pythinker_code/web/static/assets/sankeyDiagram-TZEHDZUN-BLOSUixH.js +10 -0
  616. pythinker_code/web/static/assets/sas-cz2c8ADy.js +1 -0
  617. pythinker_code/web/static/assets/sass-Cj5Yp3dK.js +1 -0
  618. pythinker_code/web/static/assets/scala-C151Ov-r.js +1 -0
  619. pythinker_code/web/static/assets/scheme-C98Dy4si.js +1 -0
  620. pythinker_code/web/static/assets/scss-OYdSNvt2.js +1 -0
  621. pythinker_code/web/static/assets/sdbl-DVxCFoDh.js +1 -0
  622. pythinker_code/web/static/assets/sequenceDiagram-WL72ISMW-6F2G8JTU.js +145 -0
  623. pythinker_code/web/static/assets/shaderlab-Dg9Lc6iA.js +1 -0
  624. pythinker_code/web/static/assets/shellscript-Yzrsuije.js +1 -0
  625. pythinker_code/web/static/assets/shellsession-BADoaaVG.js +1 -0
  626. pythinker_code/web/static/assets/slack-dark-BthQWCQV.js +1 -0
  627. pythinker_code/web/static/assets/slack-ochin-DqwNpetd.js +1 -0
  628. pythinker_code/web/static/assets/smalltalk-BERRCDM3.js +1 -0
  629. pythinker_code/web/static/assets/snazzy-light-Bw305WKR.js +1 -0
  630. pythinker_code/web/static/assets/solarized-dark-DXbdFlpD.js +1 -0
  631. pythinker_code/web/static/assets/solarized-light-L9t79GZl.js +1 -0
  632. pythinker_code/web/static/assets/solidity-rGO070M0.js +1 -0
  633. pythinker_code/web/static/assets/soy-Brmx7dQM.js +1 -0
  634. pythinker_code/web/static/assets/sparql-rVzFXLq3.js +1 -0
  635. pythinker_code/web/static/assets/splunk-BtCnVYZw.js +1 -0
  636. pythinker_code/web/static/assets/sql-BLtJtn59.js +1 -0
  637. pythinker_code/web/static/assets/ssh-config-_ykCGR6B.js +1 -0
  638. pythinker_code/web/static/assets/stata-BH5u7GGu.js +1 -0
  639. pythinker_code/web/static/assets/stateDiagram-FKZM4ZOC-DP8xP0iJ.js +1 -0
  640. pythinker_code/web/static/assets/stateDiagram-v2-4FDKWEC3-1l6-EZNX.js +1 -0
  641. pythinker_code/web/static/assets/stylus-BEDo0Tqx.js +1 -0
  642. pythinker_code/web/static/assets/svelte-zxCyuUbr.js +1 -0
  643. pythinker_code/web/static/assets/swift-Dg5xB15N.js +1 -0
  644. pythinker_code/web/static/assets/synthwave-84-CbfX1IO0.js +1 -0
  645. pythinker_code/web/static/assets/system-verilog-CnnmHF94.js +1 -0
  646. pythinker_code/web/static/assets/systemd-4A_iFExJ.js +1 -0
  647. pythinker_code/web/static/assets/talonscript-CkByrt1z.js +1 -0
  648. pythinker_code/web/static/assets/tasl-QIJgUcNo.js +1 -0
  649. pythinker_code/web/static/assets/tcl-dwOrl1Do.js +1 -0
  650. pythinker_code/web/static/assets/templ-W15q3VgB.js +1 -0
  651. pythinker_code/web/static/assets/terraform-BETggiCN.js +1 -0
  652. pythinker_code/web/static/assets/tex-CvyZ59Mk.js +1 -0
  653. pythinker_code/web/static/assets/timeline-definition-IT6M3QCI-DMgruDfK.js +61 -0
  654. pythinker_code/web/static/assets/tokyo-night-hegEt444.js +1 -0
  655. pythinker_code/web/static/assets/toml-vGWfd6FD.js +1 -0
  656. pythinker_code/web/static/assets/treemap-KMMF4GRG-Bo9ZkrAK.js +128 -0
  657. pythinker_code/web/static/assets/ts-tags-zn1MmPIZ.js +1 -0
  658. pythinker_code/web/static/assets/tsv-B_m7g4N7.js +1 -0
  659. pythinker_code/web/static/assets/tsx-COt5Ahok.js +1 -0
  660. pythinker_code/web/static/assets/turtle-BsS91CYL.js +1 -0
  661. pythinker_code/web/static/assets/twig-CO9l9SDP.js +1 -0
  662. pythinker_code/web/static/assets/typescript-BPQ3VLAy.js +1 -0
  663. pythinker_code/web/static/assets/typespec-BGHnOYBU.js +1 -0
  664. pythinker_code/web/static/assets/typst-DHCkPAjA.js +1 -0
  665. pythinker_code/web/static/assets/v-BcVCzyr7.js +1 -0
  666. pythinker_code/web/static/assets/vala-CsfeWuGM.js +1 -0
  667. pythinker_code/web/static/assets/vb-D17OF-Vu.js +1 -0
  668. pythinker_code/web/static/assets/verilog-BQ8w6xss.js +1 -0
  669. pythinker_code/web/static/assets/vesper-DU1UobuO.js +1 -0
  670. pythinker_code/web/static/assets/vhdl-CeAyd5Ju.js +1 -0
  671. pythinker_code/web/static/assets/viml-CJc9bBzg.js +1 -0
  672. pythinker_code/web/static/assets/vitesse-black-Bkuqu6BP.js +1 -0
  673. pythinker_code/web/static/assets/vitesse-dark-D0r3Knsf.js +1 -0
  674. pythinker_code/web/static/assets/vitesse-light-CVO1_9PV.js +1 -0
  675. pythinker_code/web/static/assets/vue-DN_0RTcg.js +1 -0
  676. pythinker_code/web/static/assets/vue-html-AaS7Mt5G.js +1 -0
  677. pythinker_code/web/static/assets/vue-vine-CQOfvN7w.js +1 -0
  678. pythinker_code/web/static/assets/vyper-CDx5xZoG.js +1 -0
  679. pythinker_code/web/static/assets/wasm-CG6Dc4jp.js +1 -0
  680. pythinker_code/web/static/assets/wasm-MzD3tlZU.js +1 -0
  681. pythinker_code/web/static/assets/wenyan-BV7otONQ.js +1 -0
  682. pythinker_code/web/static/assets/wgsl-Dx-B1_4e.js +1 -0
  683. pythinker_code/web/static/assets/wikitext-BhOHFoWU.js +1 -0
  684. pythinker_code/web/static/assets/wit-5i3qLPDT.js +1 -0
  685. pythinker_code/web/static/assets/wolfram-lXgVvXCa.js +1 -0
  686. pythinker_code/web/static/assets/xml-sdJ4AIDG.js +1 -0
  687. pythinker_code/web/static/assets/xsl-CtQFsRM5.js +1 -0
  688. pythinker_code/web/static/assets/xychartDiagram-PRI3JC2R-GeF2johi.js +7 -0
  689. pythinker_code/web/static/assets/yaml-Buea-lGh.js +1 -0
  690. pythinker_code/web/static/assets/zenscript-DVFEvuxE.js +1 -0
  691. pythinker_code/web/static/assets/zig-VOosw3JB.js +1 -0
  692. pythinker_code/web/static/brand/apple-touch-icon.png +0 -0
  693. pythinker_code/web/static/brand/arctecture.webp +0 -0
  694. pythinker_code/web/static/brand/bimi-logo.svg +46 -0
  695. pythinker_code/web/static/brand/favicon.ico +0 -0
  696. pythinker_code/web/static/brand/fonts/dm-sans-latin-ext.woff2 +0 -0
  697. pythinker_code/web/static/brand/fonts/dm-sans-latin.woff2 +0 -0
  698. pythinker_code/web/static/brand/fonts/instrument-sans-latin-ext.woff2 +0 -0
  699. pythinker_code/web/static/brand/fonts/instrument-sans-latin.woff2 +0 -0
  700. pythinker_code/web/static/brand/fonts/instrument-serif-latin-ext.woff2 +0 -0
  701. pythinker_code/web/static/brand/fonts/instrument-serif-latin.woff2 +0 -0
  702. pythinker_code/web/static/brand/fonts/libre-baskerville-italic-latin-ext.woff2 +0 -0
  703. pythinker_code/web/static/brand/fonts/libre-baskerville-italic-latin.woff2 +0 -0
  704. pythinker_code/web/static/brand/fonts/libre-baskerville-latin-ext.woff2 +0 -0
  705. pythinker_code/web/static/brand/fonts/libre-baskerville-latin.woff2 +0 -0
  706. pythinker_code/web/static/brand/fonts/roboto-latin-ext.woff2 +0 -0
  707. pythinker_code/web/static/brand/fonts/roboto-latin.woff2 +0 -0
  708. pythinker_code/web/static/brand/icon-192.png +0 -0
  709. pythinker_code/web/static/brand/icon-512.png +0 -0
  710. pythinker_code/web/static/brand/icon.svg +1 -0
  711. pythinker_code/web/static/brand/logo.png +0 -0
  712. pythinker_code/web/static/brand/pythinker_animated.svg +79 -0
  713. pythinker_code/web/static/brand/robots.txt +4 -0
  714. pythinker_code/web/static/index.html +15 -0
  715. pythinker_code/web/static/logo.png +0 -0
  716. pythinker_code/web/store/__init__.py +1 -0
  717. pythinker_code/web/store/sessions.py +432 -0
  718. pythinker_code/wire/__init__.py +148 -0
  719. pythinker_code/wire/file.py +151 -0
  720. pythinker_code/wire/jsonrpc.py +263 -0
  721. pythinker_code/wire/protocol.py +2 -0
  722. pythinker_code/wire/root_hub.py +27 -0
  723. pythinker_code/wire/serde.py +26 -0
  724. pythinker_code/wire/server.py +1069 -0
  725. pythinker_code/wire/types.py +698 -0
  726. pythinker_code-2.0.0.dist-info/METADATA +660 -0
  727. pythinker_code-2.0.0.dist-info/RECORD +731 -0
  728. pythinker_code-2.0.0.dist-info/WHEEL +4 -0
  729. pythinker_code-2.0.0.dist-info/entry_points.txt +4 -0
  730. pythinker_code-2.0.0.dist-info/licenses/LICENSE +202 -0
  731. pythinker_code-2.0.0.dist-info/licenses/NOTICE +14 -0
@@ -0,0 +1,898 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ from collections.abc import Awaitable, Callable
5
+ from typing import TYPE_CHECKING, Any, cast
6
+
7
+ from prompt_toolkit.shortcuts.choice_input import ChoiceInput
8
+
9
+ from pythinker_code.auth.platforms import get_platform_name_for_provider, refresh_managed_models
10
+ from pythinker_code.cli import Reload, SwitchToVis, SwitchToWeb
11
+ from pythinker_code.config import load_config, save_config
12
+ from pythinker_code.exception import ConfigError
13
+ from pythinker_code.session import Session
14
+ from pythinker_code.soul.pythinkersoul import PythinkerSoul
15
+ from pythinker_code.ui.shell.console import console
16
+ from pythinker_code.ui.shell.mcp_status import render_mcp_console
17
+ from pythinker_code.ui.shell.task_browser import TaskBrowserApp
18
+ from pythinker_code.utils.changelog import CHANGELOG
19
+ from pythinker_code.utils.logging import logger
20
+ from pythinker_code.utils.slashcmd import SlashCommand, SlashCommandRegistry
21
+
22
+ if TYPE_CHECKING:
23
+ from pythinker_code.ui.shell import Shell
24
+
25
+ type ShellSlashCmdFunc = Callable[[Shell, str], None | Awaitable[None]]
26
+ """
27
+ A function that runs as a Shell-level slash command.
28
+
29
+ Raises:
30
+ Reload: When the configuration should be reloaded.
31
+ """
32
+
33
+
34
+ registry = SlashCommandRegistry[ShellSlashCmdFunc]()
35
+ shell_mode_registry = SlashCommandRegistry[ShellSlashCmdFunc]()
36
+
37
+
38
+ def ensure_pythinker_soul(app: Shell) -> PythinkerSoul | None:
39
+ if not isinstance(app.soul, PythinkerSoul):
40
+ console.print("[red]PythinkerSoul required[/red]")
41
+ return None
42
+ return app.soul
43
+
44
+
45
+ @registry.command(aliases=["quit"])
46
+ @shell_mode_registry.command(aliases=["quit"])
47
+ def exit(app: Shell, args: str):
48
+ """Exit the application"""
49
+ # should be handled by `Shell`
50
+ raise NotImplementedError
51
+
52
+
53
+ SKILL_COMMAND_PREFIX = "skill:"
54
+
55
+ _KEYBOARD_SHORTCUTS = [
56
+ ("Ctrl-X", "Toggle agent/shell mode"),
57
+ ("Shift-Tab", "Toggle plan mode (read-only research)"),
58
+ ("Ctrl-O", "Edit in external editor ($VISUAL/$EDITOR)"),
59
+ ("Ctrl-J / Alt-Enter", "Insert newline"),
60
+ ("Ctrl-V", "Paste (supports images)"),
61
+ ("Ctrl-D", "Exit"),
62
+ ("Ctrl-C", "Interrupt"),
63
+ ]
64
+
65
+
66
+ @registry.command(aliases=["h", "?"])
67
+ @shell_mode_registry.command(aliases=["h", "?"])
68
+ def help(app: Shell, args: str):
69
+ """Show help information"""
70
+ from rich.console import Group, RenderableType
71
+ from rich.text import Text
72
+
73
+ from pythinker_code.utils.rich.columns import BulletColumns
74
+
75
+ def section(title: str, items: list[tuple[str, str]], color: str) -> BulletColumns:
76
+ lines: list[RenderableType] = [Text.from_markup(f"[bold]{title}:[/bold]")]
77
+ for name, desc in items:
78
+ lines.append(
79
+ BulletColumns(
80
+ Text.from_markup(f"[{color}]{name}[/{color}]: [grey50]{desc}[/grey50]"),
81
+ bullet_style=color,
82
+ )
83
+ )
84
+ return BulletColumns(Group(*lines))
85
+
86
+ renderables: list[RenderableType] = []
87
+ renderables.append(
88
+ BulletColumns(
89
+ Group(
90
+ Text.from_markup("[grey50]Help! I need somebody. Help! Not just anybody.[/grey50]"),
91
+ Text.from_markup("[grey50]Help! You know I need someone. Help![/grey50]"),
92
+ Text.from_markup("[grey50]\u2015 The Beatles, [italic]Help![/italic][/grey50]"),
93
+ ),
94
+ bullet_style="grey50",
95
+ )
96
+ )
97
+ renderables.append(
98
+ BulletColumns(
99
+ Text(
100
+ "Sure, Pythinker is ready to help! "
101
+ "Just send me messages and I will help you get things done!"
102
+ ),
103
+ )
104
+ )
105
+
106
+ commands: list[SlashCommand[Any]] = []
107
+ skills: list[SlashCommand[Any]] = []
108
+ for cmd in app.available_slash_commands.values():
109
+ if cmd.name.startswith(SKILL_COMMAND_PREFIX):
110
+ skills.append(cmd)
111
+ else:
112
+ commands.append(cmd)
113
+
114
+ renderables.append(section("Keyboard shortcuts", _KEYBOARD_SHORTCUTS, "yellow"))
115
+ renderables.append(
116
+ section(
117
+ "Slash commands",
118
+ [(c.slash_name(), c.description) for c in sorted(commands, key=lambda c: c.name)],
119
+ "blue",
120
+ )
121
+ )
122
+ if skills:
123
+ renderables.append(
124
+ section(
125
+ "Skills",
126
+ [(c.slash_name(), c.description) for c in sorted(skills, key=lambda c: c.name)],
127
+ "cyan",
128
+ )
129
+ )
130
+
131
+ with console.pager(styles=True):
132
+ console.print(Group(*renderables))
133
+
134
+
135
+ @registry.command
136
+ @shell_mode_registry.command
137
+ def version(app: Shell, args: str):
138
+ """Show version information"""
139
+ from pythinker_code.constant import VERSION
140
+
141
+ console.print(f"pythinker, version {VERSION}")
142
+
143
+
144
+ @registry.command
145
+ async def model(app: Shell, args: str):
146
+ """Switch LLM model or thinking mode"""
147
+ from pythinker_code.llm import derive_model_capabilities
148
+
149
+ soul = ensure_pythinker_soul(app)
150
+ if soul is None:
151
+ return
152
+ config = soul.runtime.config
153
+
154
+ await refresh_managed_models(config)
155
+
156
+ if not config.models:
157
+ console.print('[yellow]No models configured, send "/login" to login.[/yellow]')
158
+ return
159
+
160
+ if not config.is_from_default_location:
161
+ console.print(
162
+ "[yellow]Model switching requires the default config file; "
163
+ "restart without --config/--config-file.[/yellow]"
164
+ )
165
+ return
166
+
167
+ # Find current model/thinking from runtime (may be overridden by --model/--thinking)
168
+ curr_model_cfg = soul.runtime.llm.model_config if soul.runtime.llm else None
169
+ curr_model_name: str | None = None
170
+ if curr_model_cfg is not None:
171
+ for name, model_cfg in config.models.items():
172
+ if model_cfg == curr_model_cfg:
173
+ curr_model_name = name
174
+ break
175
+ curr_thinking = soul.thinking
176
+
177
+ # Step 1: Pick a model — single grouped picker with type-to-filter.
178
+ from pythinker_code.ui.shell.model_picker import ModelPickerApp, build_provider_groups
179
+
180
+ groups = build_provider_groups(
181
+ config_models=config.models,
182
+ label_for=_provider_label,
183
+ )
184
+ selected_model_name = await ModelPickerApp(
185
+ groups=groups,
186
+ current_model_name=curr_model_name,
187
+ ).run()
188
+ if not selected_model_name:
189
+ return
190
+
191
+ selected_model_cfg = config.models[selected_model_name]
192
+ selected_provider = config.providers.get(selected_model_cfg.provider)
193
+ if selected_provider is None:
194
+ console.print(f"[red]Provider not found: {selected_model_cfg.provider}[/red]")
195
+ return
196
+
197
+ # Step 2: Determine thinking mode
198
+ capabilities = derive_model_capabilities(selected_model_cfg)
199
+ new_thinking: bool
200
+
201
+ if "always_thinking" in capabilities:
202
+ new_thinking = True
203
+ elif "thinking" in capabilities:
204
+ thinking_choices: list[tuple[str, str]] = [
205
+ ("off", "off" + (" (current)" if not curr_thinking else "")),
206
+ ("on", "on" + (" (current)" if curr_thinking else "")),
207
+ ]
208
+ try:
209
+ thinking_selection = await ChoiceInput(
210
+ message="Enable thinking mode? (↑↓ navigate, Enter select, Ctrl+C cancel):",
211
+ options=thinking_choices,
212
+ default="on" if curr_thinking else "off",
213
+ ).prompt_async()
214
+ except (EOFError, KeyboardInterrupt):
215
+ return
216
+
217
+ if not thinking_selection:
218
+ return
219
+
220
+ new_thinking = thinking_selection == "on"
221
+ else:
222
+ new_thinking = False
223
+
224
+ # Check if anything changed
225
+ model_changed = curr_model_name != selected_model_name
226
+ thinking_changed = curr_thinking != new_thinking
227
+ selected_display = selected_model_cfg.display_name or selected_model_cfg.model
228
+
229
+ if not model_changed and not thinking_changed:
230
+ console.print(
231
+ f"[yellow]Already using {selected_display} "
232
+ f"with thinking {'on' if new_thinking else 'off'}.[/yellow]"
233
+ )
234
+ return
235
+
236
+ # Save and reload
237
+ prev_model = config.default_model
238
+ prev_thinking = config.default_thinking
239
+ config.default_model = selected_model_name
240
+ config.default_thinking = new_thinking
241
+ try:
242
+ config_for_save = load_config()
243
+ config_for_save.default_model = selected_model_name
244
+ config_for_save.default_thinking = new_thinking
245
+ save_config(config_for_save)
246
+ except (ConfigError, OSError) as exc:
247
+ config.default_model = prev_model
248
+ config.default_thinking = prev_thinking
249
+ console.print(f"[red]Failed to save config: {exc}[/red]")
250
+ return
251
+
252
+ from pythinker_code.telemetry import track
253
+
254
+ if model_changed:
255
+ track("model_switch", model=selected_model_name)
256
+ if thinking_changed:
257
+ track("thinking_toggle", enabled=new_thinking)
258
+ console.print(
259
+ f"[green]Switched to {selected_display} "
260
+ f"with thinking {'on' if new_thinking else 'off'}. "
261
+ "Reloading...[/green]"
262
+ )
263
+
264
+ # Pre-load LM Studio models so the user doesn't hit a 10-60s wait on
265
+ # the first message. Fire-and-forget on error — Reload still proceeds.
266
+ if model_changed and selected_model_cfg.provider == "managed:lm-studio":
267
+ await _preload_lm_studio_model(selected_provider, selected_model_cfg.model)
268
+
269
+ raise Reload(session_id=soul.runtime.session.id)
270
+
271
+
272
+ _PROVIDER_LABEL_OVERRIDES = {
273
+ "managed:minimax-anthropic": "MiniMax",
274
+ "managed:opencode-go-openai": "OpenCode Go (OpenAI)",
275
+ "managed:opencode-go-anthropic": "OpenCode Go (Anthropic)",
276
+ "managed:deepseek": "DeepSeek",
277
+ "managed:anthropic": "Anthropic",
278
+ "managed:openrouter": "OpenRouter",
279
+ }
280
+
281
+
282
+ def _provider_label(provider_key: str) -> str:
283
+ if name := get_platform_name_for_provider(provider_key):
284
+ return name
285
+ if name := _PROVIDER_LABEL_OVERRIDES.get(provider_key):
286
+ return name
287
+ # Fall back: strip "managed:" prefix and humanize.
288
+ raw = provider_key.removeprefix("managed:")
289
+ return raw.replace("-", " ").title() if raw else provider_key
290
+
291
+
292
+ async def _preload_lm_studio_model(provider: Any, model_id: str) -> None:
293
+ """Best-effort: ask LM Studio to load the model now."""
294
+ from rich.status import Status
295
+
296
+ from pythinker_code.auth.lm_studio import request_lm_studio_load
297
+
298
+ base_url = provider.base_url
299
+ api_key = provider.api_key.get_secret_value()
300
+ status_msg = f"Pre-loading {model_id} in LM Studio (this may take a moment)..."
301
+ status = Status(status_msg, console=console)
302
+ status.start()
303
+ try:
304
+ result = await request_lm_studio_load(base_url=base_url, model_id=model_id, api_key=api_key)
305
+ status.stop()
306
+ console.print(
307
+ f"[dim]LM Studio loaded {model_id} in "
308
+ f"{result.load_time_seconds:.1f}s (status={result.status}).[/dim]"
309
+ )
310
+ except Exception as exc:
311
+ status.stop()
312
+ console.print(
313
+ f"[yellow]LM Studio pre-load failed for {model_id}: {exc}[/yellow]\n"
314
+ "[dim]The chat will still try the model on first message.[/dim]"
315
+ )
316
+
317
+
318
+ @registry.command
319
+ @shell_mode_registry.command
320
+ async def editor(app: Shell, args: str):
321
+ """Set default external editor for Ctrl-O"""
322
+ from pythinker_code.utils.editor import get_editor_command
323
+
324
+ soul = ensure_pythinker_soul(app)
325
+ if soul is None:
326
+ return
327
+ config = soul.runtime.config
328
+ config_file = config.source_file
329
+ if config_file is None:
330
+ console.print(
331
+ "[yellow]Editor switching is unavailable with inline --config; "
332
+ "use --config-file to persist this setting.[/yellow]"
333
+ )
334
+ return
335
+
336
+ current_editor = config.default_editor
337
+
338
+ # If args provided directly, use as editor command
339
+ if args.strip():
340
+ new_editor = args.strip()
341
+ else:
342
+ options: list[tuple[str, str]] = [
343
+ ("code --wait", "VS Code (code --wait)"),
344
+ ("vim", "Vim"),
345
+ ("nano", "Nano"),
346
+ ("", "Auto-detect (use $VISUAL/$EDITOR)"),
347
+ ]
348
+ # Mark current selection
349
+ options = [
350
+ (val, label + (" ← current" if val == current_editor else "")) for val, label in options
351
+ ]
352
+
353
+ try:
354
+ choice = cast(
355
+ str | None,
356
+ await ChoiceInput(
357
+ message="Select an editor (↑↓ navigate, Enter select, Ctrl+C cancel):",
358
+ options=options,
359
+ default=(
360
+ current_editor
361
+ if current_editor in {v for v, _ in options}
362
+ else "code --wait"
363
+ ),
364
+ ).prompt_async(),
365
+ )
366
+ except (EOFError, KeyboardInterrupt):
367
+ return
368
+
369
+ if choice is None:
370
+ return
371
+ new_editor = choice
372
+
373
+ # Validate the editor binary is available
374
+ if new_editor:
375
+ import shlex
376
+ import shutil
377
+
378
+ try:
379
+ parts = shlex.split(new_editor)
380
+ except ValueError:
381
+ console.print(f"[red]Invalid editor command: {new_editor}[/red]")
382
+ return
383
+
384
+ binary = parts[0]
385
+ if not shutil.which(binary):
386
+ console.print(
387
+ f"[yellow]Warning: '{binary}' not found in PATH. "
388
+ f"Saving anyway — make sure it's installed before using Ctrl-O.[/yellow]"
389
+ )
390
+
391
+ if new_editor == current_editor:
392
+ console.print(f"[yellow]Editor is already set to: {new_editor or 'auto-detect'}[/yellow]")
393
+ return
394
+
395
+ # Save to disk
396
+ try:
397
+ config_for_save = load_config(config_file)
398
+ config_for_save.default_editor = new_editor
399
+ save_config(config_for_save, config_file)
400
+ except (ConfigError, OSError) as exc:
401
+ console.print(f"[red]Failed to save config: {exc}[/red]")
402
+ return
403
+
404
+ # Sync in-memory config so Ctrl-O picks it up immediately
405
+ config.default_editor = new_editor
406
+
407
+ if new_editor:
408
+ console.print(f"[green]Editor set to: {new_editor}[/green]")
409
+ else:
410
+ resolved = get_editor_command()
411
+ label = " ".join(resolved) if resolved else "none"
412
+ console.print(f"[green]Editor set to auto-detect (resolved: {label})[/green]")
413
+
414
+
415
+ @registry.command(aliases=["release-notes"])
416
+ @shell_mode_registry.command(aliases=["release-notes"])
417
+ def changelog(app: Shell, args: str):
418
+ """Show release notes"""
419
+ from rich.console import Group, RenderableType
420
+ from rich.text import Text
421
+
422
+ from pythinker_code.utils.rich.columns import BulletColumns
423
+
424
+ renderables: list[RenderableType] = []
425
+ for ver, entry in CHANGELOG.items():
426
+ title = f"[bold]{ver}[/bold]"
427
+ if entry.description:
428
+ title += f": {entry.description}"
429
+
430
+ lines: list[RenderableType] = [Text.from_markup(title)]
431
+ for item in entry.entries:
432
+ if item.lower().startswith("lib:"):
433
+ continue
434
+ lines.append(
435
+ BulletColumns(
436
+ Text.from_markup(f"[grey50]{item}[/grey50]"),
437
+ bullet_style="grey50",
438
+ ),
439
+ )
440
+ renderables.append(BulletColumns(Group(*lines)))
441
+
442
+ with console.pager(styles=True):
443
+ console.print(Group(*renderables))
444
+
445
+
446
+ @registry.command
447
+ @shell_mode_registry.command
448
+ async def feedback(app: Shell, args: str):
449
+ """Submit feedback to make Pythinker CLI better"""
450
+ import platform
451
+ import webbrowser
452
+
453
+ import aiohttp
454
+
455
+ from pythinker_code.auth import PYTHINKER_CODE_PLATFORM_ID
456
+ from pythinker_code.auth.platforms import get_platform_by_id, managed_provider_key
457
+ from pythinker_code.constant import VERSION
458
+ from pythinker_code.ui.shell.oauth import current_model_key
459
+ from pythinker_code.utils.aiohttp import new_client_session
460
+
461
+ ISSUE_URL = "https://github.com/mohamed-elkholy95/Pythinker-Code/issues"
462
+
463
+ def _fallback_to_issues():
464
+ if not webbrowser.open(ISSUE_URL):
465
+ console.print(f"Please submit feedback at [underline]{ISSUE_URL}[/underline].")
466
+
467
+ soul = ensure_pythinker_soul(app)
468
+ if soul is None:
469
+ _fallback_to_issues()
470
+ return
471
+
472
+ pythinker_platform = get_platform_by_id(PYTHINKER_CODE_PLATFORM_ID)
473
+ if pythinker_platform is None:
474
+ _fallback_to_issues()
475
+ return
476
+
477
+ provider = soul.runtime.config.providers.get(managed_provider_key(PYTHINKER_CODE_PLATFORM_ID))
478
+ if provider is None or provider.oauth is None:
479
+ _fallback_to_issues()
480
+ return
481
+
482
+ from prompt_toolkit import PromptSession
483
+
484
+ prompt_session: PromptSession[str] = PromptSession()
485
+ try:
486
+ content = await prompt_session.prompt_async("Enter your feedback: ")
487
+ except (EOFError, KeyboardInterrupt):
488
+ console.print("[grey50]Feedback cancelled.[/grey50]")
489
+ return
490
+
491
+ content = content.strip()
492
+ if not content:
493
+ console.print("[yellow]Feedback cannot be empty.[/yellow]")
494
+ return
495
+
496
+ api_key = soul.runtime.oauth.resolve_api_key(provider.api_key, provider.oauth)
497
+ feedback_url = f"{pythinker_platform.base_url.rstrip('/')}/feedback"
498
+
499
+ payload = {
500
+ "session_id": soul.runtime.session.id,
501
+ "content": content,
502
+ "version": VERSION,
503
+ "os": f"{platform.system()} {platform.release()}",
504
+ "model": current_model_key(soul),
505
+ }
506
+
507
+ with console.status("[cyan]Submitting feedback...[/cyan]"):
508
+ try:
509
+ async with (
510
+ new_client_session() as session,
511
+ session.post(
512
+ feedback_url,
513
+ json=payload,
514
+ headers={
515
+ "Authorization": f"Bearer {api_key}",
516
+ **(provider.custom_headers or {}),
517
+ },
518
+ raise_for_status=True,
519
+ ),
520
+ ):
521
+ pass
522
+ session_id = soul.runtime.session.id
523
+ from pythinker_code.telemetry import track
524
+
525
+ track("feedback_submitted")
526
+ console.print(
527
+ f"[green]Feedback submitted, thank you! Your session ID is: {session_id}[/green]"
528
+ )
529
+ except TimeoutError:
530
+ console.print("[red]Feedback submission timed out.[/red]")
531
+ _fallback_to_issues()
532
+ except aiohttp.ClientError as e:
533
+ status = getattr(e, "status", None)
534
+ if status:
535
+ msg = f"Failed to submit feedback (HTTP {status})."
536
+ else:
537
+ msg = "Network error, failed to submit feedback."
538
+ console.print(f"[red]{msg}[/red]")
539
+ _fallback_to_issues()
540
+
541
+
542
+ @registry.command(aliases=["reset"])
543
+ async def clear(app: Shell, args: str):
544
+ """Clear the context"""
545
+ if ensure_pythinker_soul(app) is None:
546
+ return
547
+ from pythinker_code.telemetry import track
548
+
549
+ track("clear")
550
+ await app.run_soul_command("/clear")
551
+ raise Reload()
552
+
553
+
554
+ @registry.command
555
+ async def new(app: Shell, args: str):
556
+ """Start a new session"""
557
+ soul = ensure_pythinker_soul(app)
558
+ if soul is None:
559
+ return
560
+ current_session = soul.runtime.session
561
+ work_dir = current_session.work_dir
562
+ # Clean up the current session if it has no content, so that chaining
563
+ # /new commands (or switching away before the first message) does not
564
+ # leave orphan empty session directories on disk.
565
+ if current_session.is_empty():
566
+ await current_session.delete()
567
+ session = await Session.create(work_dir)
568
+ from pythinker_code.telemetry import track
569
+
570
+ track("session_new")
571
+ console.print("[green]New session created. Switching...[/green]")
572
+ raise Reload(session_id=session.id)
573
+
574
+
575
+ @registry.command(name="title", aliases=["rename"])
576
+ async def title(app: Shell, args: str):
577
+ """Set or show the session title"""
578
+ soul = ensure_pythinker_soul(app)
579
+ if soul is None:
580
+ return
581
+ session = soul.runtime.session
582
+ if not args.strip():
583
+ console.print(f"Session title: [bold]{session.title}[/bold]")
584
+ return
585
+
586
+ from pythinker_code.session_state import load_session_state, save_session_state
587
+
588
+ new_title = args.strip()[:200]
589
+ # Read-modify-write: load fresh state to avoid overwriting concurrent web changes
590
+ fresh = load_session_state(session.dir)
591
+ fresh.custom_title = new_title
592
+ fresh.title_generated = True
593
+ save_session_state(fresh, session.dir)
594
+ session.state.custom_title = new_title
595
+ session.state.title_generated = True
596
+ session.title = new_title
597
+ console.print(f"[green]Session title set to: {new_title}[/green]")
598
+
599
+
600
+ @registry.command(name="sessions", aliases=["resume"])
601
+ async def list_sessions(app: Shell, args: str):
602
+ """List sessions and resume optionally"""
603
+ import shlex
604
+
605
+ from pythinker_code.ui.shell.session_picker import SessionPickerApp
606
+
607
+ soul = ensure_pythinker_soul(app)
608
+ if soul is None:
609
+ return
610
+
611
+ current_session = soul.runtime.session
612
+ result = await SessionPickerApp(
613
+ work_dir=current_session.work_dir,
614
+ current_session=current_session,
615
+ ).run()
616
+
617
+ if result is None:
618
+ return
619
+
620
+ selection, selected_work_dir = result
621
+
622
+ if selection == current_session.id:
623
+ console.print("[yellow]You are already in this session.[/yellow]")
624
+ return
625
+
626
+ if selected_work_dir != current_session.work_dir:
627
+ cmd = f"pythinker --work-dir {shlex.quote(str(selected_work_dir))} --session {selection}"
628
+ console.print(f"[yellow]Session is in a different directory. Run:[/yellow]\n {cmd}")
629
+ return
630
+
631
+ from pythinker_code.telemetry import track
632
+
633
+ track("session_resume")
634
+ console.print(f"[green]Switching to session {selection}...[/green]")
635
+ raise Reload(session_id=selection)
636
+
637
+
638
+ @registry.command(name="task")
639
+ @shell_mode_registry.command(name="task")
640
+ async def task(app: Shell, args: str):
641
+ """Browse and manage background tasks"""
642
+ soul = ensure_pythinker_soul(app)
643
+ if soul is None:
644
+ return
645
+ if args.strip():
646
+ console.print('[yellow]Usage: "/task" opens the interactive task browser.[/yellow]')
647
+ return
648
+ if soul.runtime.role != "root":
649
+ console.print("[yellow]Background tasks are only available from the root agent.[/yellow]")
650
+ return
651
+
652
+ await TaskBrowserApp(soul).run()
653
+
654
+
655
+ @registry.command
656
+ @shell_mode_registry.command
657
+ def theme(app: Shell, args: str):
658
+ """Switch terminal color theme (dark/light)"""
659
+ from pythinker_code.ui.theme import get_active_theme
660
+
661
+ soul = ensure_pythinker_soul(app)
662
+ if soul is None:
663
+ return
664
+
665
+ current = get_active_theme()
666
+ arg = args.strip().lower()
667
+
668
+ if not arg:
669
+ console.print(f"Current theme: [bold]{current}[/bold]")
670
+ console.print("[grey50]Usage: /theme dark | /theme light[/grey50]")
671
+ return
672
+
673
+ if arg not in ("dark", "light"):
674
+ console.print(f"[red]Unknown theme: {arg}. Use 'dark' or 'light'.[/red]")
675
+ return
676
+
677
+ if arg == current:
678
+ console.print(f"[yellow]Already using {arg} theme.[/yellow]")
679
+ return
680
+
681
+ config_file = soul.runtime.config.source_file
682
+ if config_file is None:
683
+ console.print(
684
+ "[yellow]Theme switching requires a config file; "
685
+ "restart without --config to persist this setting.[/yellow]"
686
+ )
687
+ return
688
+
689
+ # Persist to disk first — only update in-memory state after success
690
+ try:
691
+ config_for_save = load_config(config_file)
692
+ config_for_save.theme = arg # type: ignore[assignment]
693
+ save_config(config_for_save, config_file)
694
+ except (ConfigError, OSError) as exc:
695
+ console.print(f"[red]Failed to save config: {exc}[/red]")
696
+ return
697
+
698
+ from pythinker_code.telemetry import track
699
+
700
+ track("theme_switch", theme=arg)
701
+ console.print(f"[green]Switched to {arg} theme. Reloading...[/green]")
702
+ raise Reload(session_id=soul.runtime.session.id)
703
+
704
+
705
+ @registry.command
706
+ def web(app: Shell, args: str):
707
+ """Open Pythinker Web UI in browser"""
708
+ from pythinker_code.telemetry import track
709
+
710
+ track("web_opened")
711
+ soul = ensure_pythinker_soul(app)
712
+ session_id = soul.runtime.session.id if soul else None
713
+ raise SwitchToWeb(session_id=session_id)
714
+
715
+
716
+ @registry.command
717
+ def vis(app: Shell, args: str):
718
+ """Open Pythinker Agent Tracing Visualizer in browser"""
719
+ from pythinker_code.telemetry import track
720
+
721
+ track("vis_opened")
722
+ soul = ensure_pythinker_soul(app)
723
+ session_id = soul.runtime.session.id if soul else None
724
+ raise SwitchToVis(session_id=session_id)
725
+
726
+
727
+ @registry.command
728
+ async def mcp(app: Shell, args: str):
729
+ """Show MCP servers and tools"""
730
+ from rich.live import Live
731
+
732
+ soul = ensure_pythinker_soul(app)
733
+ if soul is None:
734
+ return
735
+ await soul.start_background_mcp_loading()
736
+ snapshot = soul.status.mcp_status
737
+ if snapshot is None:
738
+ console.print("[yellow]No MCP servers configured.[/yellow]")
739
+ return
740
+
741
+ if not snapshot.loading:
742
+ console.print(render_mcp_console(snapshot))
743
+ return
744
+
745
+ with Live(
746
+ render_mcp_console(snapshot),
747
+ console=console,
748
+ refresh_per_second=8,
749
+ transient=False,
750
+ ) as live:
751
+ while True:
752
+ snapshot = soul.status.mcp_status
753
+ if snapshot is None:
754
+ break
755
+ live.update(render_mcp_console(snapshot), refresh=True)
756
+ if not snapshot.loading:
757
+ break
758
+ await asyncio.sleep(0.125)
759
+ try:
760
+ await soul.wait_for_background_mcp_loading()
761
+ except Exception as e:
762
+ logger.debug("MCP loading completed with error while rendering /mcp: {error}", error=e)
763
+ snapshot = soul.status.mcp_status
764
+ if snapshot is not None:
765
+ live.update(render_mcp_console(snapshot), refresh=True)
766
+
767
+
768
+ @registry.command
769
+ @shell_mode_registry.command
770
+ def hooks(app: Shell, args: str):
771
+ """List configured hooks"""
772
+ soul = ensure_pythinker_soul(app)
773
+ if soul is None:
774
+ return
775
+
776
+ engine = soul.hook_engine
777
+ if not engine.summary:
778
+ console.print(
779
+ "[yellow]No hooks configured. "
780
+ "Add [[hooks]] sections to your config.toml to set up hooks.[/yellow]"
781
+ )
782
+ return
783
+
784
+ console.print()
785
+ console.print("[bold]Configured Hooks:[/bold]")
786
+ console.print()
787
+
788
+ for event, entries in engine.details().items():
789
+ console.print(f" [cyan]{event}[/cyan]: {len(entries)} hook(s)")
790
+ for entry in entries:
791
+ source_tag = f" [dim]({entry['source']})[/dim]" if entry["source"] == "wire" else ""
792
+ console.print(f" [dim]{entry['matcher']}[/dim] {entry['command']}{source_tag}")
793
+
794
+ console.print()
795
+
796
+
797
+ @registry.command
798
+ async def undo(app: Shell, args: str):
799
+ """Undo: fork the session at a previous turn and retry"""
800
+ from pythinker_code.session_fork import enumerate_turns, fork_session
801
+ from pythinker_code.utils.string import shorten
802
+
803
+ soul = ensure_pythinker_soul(app)
804
+ if soul is None:
805
+ return
806
+
807
+ session = soul.runtime.session
808
+ wire_path = session.dir / "wire.jsonl"
809
+ turns = enumerate_turns(wire_path)
810
+
811
+ if not turns:
812
+ console.print("[yellow]No turns found in this session.[/yellow]")
813
+ return
814
+
815
+ # Build choices: each turn's first line, truncated
816
+ choices: list[tuple[str, str]] = []
817
+ for turn in turns:
818
+ first_line = turn.user_text.split("\n", 1)[0]
819
+ label = shorten(first_line, width=80, placeholder="...")
820
+ choices.append((str(turn.index), f"[{turn.index}] {label}"))
821
+
822
+ try:
823
+ selected = await ChoiceInput(
824
+ message="Select a turn to undo (↑↓ navigate, Enter select, Ctrl+C cancel):",
825
+ options=choices,
826
+ default=choices[-1][0],
827
+ ).prompt_async()
828
+ except (EOFError, KeyboardInterrupt):
829
+ return
830
+
831
+ turn_index = int(selected)
832
+
833
+ # The selected turn is the one we want to redo — fork includes turns *before* it
834
+ selected_turn = turns[turn_index]
835
+ user_text = selected_turn.user_text
836
+
837
+ if turn_index == 0:
838
+ # Fork with no history — just the user text
839
+ new_session = await Session.create(session.work_dir)
840
+ new_session_id = new_session.id
841
+ # Set title to match the convention used by fork_session
842
+ from pythinker_code.session_state import load_session_state, save_session_state
843
+
844
+ new_state = load_session_state(new_session.dir)
845
+ new_state.custom_title = f"Undo: {session.title}"
846
+ new_state.title_generated = True
847
+ save_session_state(new_state, new_session.dir)
848
+ else:
849
+ # Fork includes turns 0..turn_index-1
850
+ fork_turn_index = turn_index - 1
851
+ new_session_id = await fork_session(
852
+ source_session_dir=session.dir,
853
+ work_dir=session.work_dir,
854
+ turn_index=fork_turn_index,
855
+ title_prefix="Undo",
856
+ source_title=session.title,
857
+ )
858
+
859
+ from pythinker_code.telemetry import track
860
+
861
+ track("undo")
862
+ console.print(f"[green]Forked at turn {turn_index}. Switching to new session...[/green]")
863
+ raise Reload(session_id=new_session_id, prefill_text=user_text)
864
+
865
+
866
+ @registry.command
867
+ async def fork(app: Shell, args: str):
868
+ """Fork the current session (copy all history to a new session)"""
869
+ from pythinker_code.session_fork import fork_session
870
+
871
+ soul = ensure_pythinker_soul(app)
872
+ if soul is None:
873
+ return
874
+
875
+ session = soul.runtime.session
876
+ new_session_id = await fork_session(
877
+ source_session_dir=session.dir,
878
+ work_dir=session.work_dir,
879
+ turn_index=None,
880
+ title_prefix="Fork",
881
+ source_title=session.title,
882
+ )
883
+
884
+ from pythinker_code.telemetry import track
885
+
886
+ track("session_fork")
887
+ console.print("[green]Session forked. Switching to new session...[/green]")
888
+ raise Reload(session_id=new_session_id)
889
+
890
+
891
+ from . import ( # noqa: E402
892
+ debug, # noqa: F401 # type: ignore[reportUnusedImport]
893
+ export_import, # noqa: F401 # type: ignore[reportUnusedImport]
894
+ oauth, # noqa: F401 # type: ignore[reportUnusedImport]
895
+ setup, # noqa: F401 # type: ignore[reportUnusedImport]
896
+ update, # noqa: F401 # type: ignore[reportUnusedImport]
897
+ usage, # noqa: F401 # type: ignore[reportUnusedImport]
898
+ )