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
pythinker_code/app.py ADDED
@@ -0,0 +1,847 @@
1
+ from __future__ import annotations
2
+
3
+ import asyncio
4
+ import contextlib
5
+ import dataclasses
6
+ import sys
7
+ import time
8
+ import warnings
9
+ from collections.abc import AsyncGenerator, Callable
10
+ from pathlib import Path
11
+ from typing import TYPE_CHECKING, Any
12
+
13
+ import pythinker_host
14
+ from pydantic import SecretStr
15
+ from pythinker_host.path import HostPath
16
+
17
+ from pythinker_code.agentspec import DEFAULT_AGENT_FILE
18
+ from pythinker_code.auth.oauth import OAuthManager, get_device_id
19
+ from pythinker_code.background.models import is_terminal_status
20
+ from pythinker_code.cli import InputFormat, OutputFormat
21
+ from pythinker_code.config import Config, LLMModel, LLMProvider, load_config
22
+ from pythinker_code.constant import VERSION
23
+ from pythinker_code.llm import augment_provider_with_env_vars, create_llm, model_display_name
24
+ from pythinker_code.session import Session
25
+ from pythinker_code.share import get_share_dir
26
+ from pythinker_code.soul import RunCancelled, run_soul
27
+ from pythinker_code.soul.agent import Runtime, load_agent
28
+ from pythinker_code.soul.context import Context
29
+ from pythinker_code.soul.pythinkersoul import PythinkerSoul
30
+ from pythinker_code.utils.aioqueue import QueueShutDown
31
+ from pythinker_code.utils.envvar import get_env_bool
32
+ from pythinker_code.utils.logging import logger, open_original_stderr, redirect_stderr_to_logger
33
+ from pythinker_code.utils.path import shorten_home
34
+ from pythinker_code.wire import Wire, WireUISide
35
+ from pythinker_code.wire.types import ApprovalRequest, ApprovalResponse, ContentPart, WireMessage
36
+
37
+ if TYPE_CHECKING:
38
+ from fastmcp.mcp_config import MCPConfig
39
+
40
+
41
+ _CWD_LOCK = asyncio.Lock()
42
+
43
+
44
+ def _patch_session_id(record: dict[str, Any]) -> None:
45
+ """Inject the current session ID (from ContextVar) into log records."""
46
+ try:
47
+ from pythinker_code.soul.toolset import get_session_id
48
+
49
+ sid = get_session_id()
50
+ record["extra"]["sid"] = sid if sid else ""
51
+ except Exception:
52
+ record["extra"].setdefault("sid", "")
53
+
54
+
55
+ def enable_logging(debug: bool = False, *, redirect_stderr: bool = True) -> None:
56
+ # NOTE: stderr redirection is implemented by swapping the process-level fd=2 (dup2).
57
+ # That can hide Click/Typer error output during CLI startup, so some entrypoints delay
58
+ # installing it until after critical initialization succeeds.
59
+ logger.remove() # Remove default stderr handler
60
+ logger.enable("pythinker_code")
61
+ if debug:
62
+ logger.enable("pythinker_core")
63
+ logger.add(
64
+ get_share_dir() / "logs" / "pythinker.log",
65
+ # FIXME: configure level for different modules
66
+ level="TRACE" if debug else "INFO",
67
+ format=(
68
+ "{time:YYYY-MM-DD HH:mm:ss.SSS} | {level: <8} | "
69
+ "{name}:{function}:{line} | {extra[sid]} - {message}"
70
+ ),
71
+ rotation="06:00",
72
+ retention="10 days",
73
+ )
74
+ logger.configure(extra={"sid": ""}, patcher=_patch_session_id)
75
+ if redirect_stderr:
76
+ redirect_stderr_to_logger()
77
+
78
+
79
+ def _write_original_stderr(text: str) -> None:
80
+ """Write a user-facing notice to the terminal even if ``fd=2`` has been
81
+ redirected into the logger by ``redirect_stderr_to_logger``.
82
+
83
+ Falls back to ``sys.stderr`` when no redirector is installed (tests,
84
+ early-startup code paths), matching the semantics of ``_emit_fatal_error``
85
+ in ``cli/__init__.py``.
86
+ """
87
+ with open_original_stderr() as stream:
88
+ if stream is not None:
89
+ stream.write(text.encode("utf-8", errors="replace"))
90
+ stream.flush()
91
+ return
92
+ sys.stderr.write(text)
93
+
94
+
95
+ async def _refresh_managed_models_silent(config: Config) -> None:
96
+ from pythinker_code.auth.platforms import refresh_managed_models
97
+
98
+ try:
99
+ await refresh_managed_models(config)
100
+ except Exception as exc:
101
+ logger.warning("Background managed-model refresh failed: {error}", error=exc)
102
+
103
+
104
+ def _cleanup_stale_foreground_subagents(runtime: Runtime) -> None:
105
+ subagent_store = getattr(runtime, "subagent_store", None)
106
+ if subagent_store is None:
107
+ return
108
+
109
+ stale_agent_ids = [
110
+ record.agent_id
111
+ for record in subagent_store.list_instances()
112
+ if record.status == "running_foreground"
113
+ ]
114
+ for agent_id in stale_agent_ids:
115
+ logger.warning(
116
+ "Marking stale foreground subagent instance as failed during startup: {agent_id}",
117
+ agent_id=agent_id,
118
+ )
119
+ subagent_store.update_instance(agent_id, status="failed")
120
+
121
+
122
+ class PythinkerCLI:
123
+ @staticmethod
124
+ async def create(
125
+ session: Session,
126
+ *,
127
+ # Basic configuration
128
+ config: Config | Path | None = None,
129
+ model_name: str | None = None,
130
+ thinking: bool | None = None,
131
+ # Run mode
132
+ yolo: bool = False,
133
+ auto: bool = False,
134
+ runtime_auto: bool = False,
135
+ plan_mode: bool = False,
136
+ resumed: bool = False,
137
+ ui_mode: str = "shell",
138
+ # Extensions
139
+ agent_file: Path | None = None,
140
+ mcp_configs: list[MCPConfig] | list[dict[str, Any]] | None = None,
141
+ skills_dirs: list[HostPath] | None = None,
142
+ # Loop control
143
+ max_steps_per_turn: int | None = None,
144
+ max_retries_per_step: int | None = None,
145
+ max_ralph_iterations: int | None = None,
146
+ startup_progress: Callable[[str], None] | None = None,
147
+ defer_mcp_loading: bool = False,
148
+ ) -> PythinkerCLI:
149
+ """
150
+ Create a PythinkerCLI instance.
151
+
152
+ Args:
153
+ session (Session): A session created by `Session.create` or `Session.continue_`.
154
+ config (Config | Path | None, optional): Configuration to use, or path to config file.
155
+ Defaults to None.
156
+ model_name (str | None, optional): Name of the model to use. Defaults to None.
157
+ thinking (bool | None, optional): Whether to enable thinking mode. Defaults to None.
158
+ yolo (bool, optional): Dangerously skip permission approvals. The user is still
159
+ reachable via ``AskUserQuestion``. Defaults to False.
160
+ auto (bool, optional): Invocation-level auto mode (no user is present to answer
161
+ questions or approve actions). Implies auto-approve. Defaults to False.
162
+ runtime_auto (bool, optional): Internal invocation-only auto-mode overlay, used by
163
+ print mode so it stays non-interactive without changing persisted session auto
164
+ state. Defaults to False.
165
+ agent_file (Path | None, optional): Path to the agent file. Defaults to None.
166
+ mcp_configs (list[MCPConfig | dict[str, Any]] | None, optional): MCP configs to load
167
+ MCP tools from. Defaults to None.
168
+ skills_dirs (list[HostPath] | None, optional): Custom skills directories that
169
+ override default user/project discovery. Defaults to None.
170
+ max_steps_per_turn (int | None, optional): Maximum number of steps in one turn.
171
+ Defaults to None.
172
+ max_retries_per_step (int | None, optional): Maximum number of retries in one step.
173
+ Defaults to None.
174
+ max_ralph_iterations (int | None, optional): Extra iterations after the first turn in
175
+ Ralph mode. Defaults to None.
176
+ startup_progress (Callable[[str], None] | None, optional): Progress callback used by
177
+ interactive startup UI. Defaults to None.
178
+ defer_mcp_loading (bool, optional): Defer MCP startup until the interactive shell is
179
+ ready. Defaults to False.
180
+
181
+ Raises:
182
+ FileNotFoundError: When the agent file is not found.
183
+ ConfigError(PythinkerCLIException, ValueError): When the configuration is invalid.
184
+ AgentSpecError(PythinkerCLIException, ValueError): When the agent specification is
185
+ invalid.
186
+ SystemPromptTemplateError(PythinkerCLIException, ValueError): When the system prompt
187
+ template is invalid.
188
+ InvalidToolError(PythinkerCLIException, ValueError): When any tool cannot be loaded.
189
+ MCPConfigError(PythinkerCLIException, ValueError): When any MCP configuration is
190
+ invalid.
191
+ MCPRuntimeError(PythinkerCLIException, RuntimeError): When any MCP server cannot be
192
+ connected.
193
+ """
194
+ _create_t0 = time.monotonic()
195
+ _phase_timings_ms: dict[str, int] = {}
196
+
197
+ if startup_progress is not None:
198
+ startup_progress("Loading configuration...")
199
+
200
+ _phase_t = time.monotonic()
201
+ config = config if isinstance(config, Config) else load_config(config)
202
+ _phase_timings_ms["config_ms"] = int((time.monotonic() - _phase_t) * 1000)
203
+ if max_steps_per_turn is not None:
204
+ config.loop_control.max_steps_per_turn = max_steps_per_turn
205
+ if max_retries_per_step is not None:
206
+ config.loop_control.max_retries_per_step = max_retries_per_step
207
+ if max_ralph_iterations is not None:
208
+ config.loop_control.max_ralph_iterations = max_ralph_iterations
209
+ logger.info("Loaded config: {config}", config=config)
210
+
211
+ _phase_t = time.monotonic()
212
+ oauth = OAuthManager(config)
213
+
214
+ bg_refresh_task = asyncio.create_task(_refresh_managed_models_silent(config))
215
+
216
+ model: LLMModel | None = None
217
+ provider: LLMProvider | None = None
218
+
219
+ # try to use config file
220
+ if not model_name and config.default_model:
221
+ # no --model specified && default model is set in config
222
+ model = config.models[config.default_model]
223
+ provider = config.providers[model.provider]
224
+ if model_name and model_name in config.models:
225
+ # --model specified && model is set in config
226
+ model = config.models[model_name]
227
+ provider = config.providers[model.provider]
228
+
229
+ if not model:
230
+ model = LLMModel(provider="", model="", max_context_size=100_000)
231
+ provider = LLMProvider(type="pythinker", base_url="", api_key=SecretStr(""))
232
+
233
+ # try overwrite with environment variables
234
+ assert provider is not None
235
+ assert model is not None
236
+ env_overrides = augment_provider_with_env_vars(provider, model, provider_key=model.provider)
237
+
238
+ # determine thinking mode
239
+ thinking = config.default_thinking if thinking is None else thinking
240
+
241
+ # determine yolo mode
242
+ yolo = yolo if yolo else config.default_yolo
243
+
244
+ # determine plan mode (only for new sessions, not restored)
245
+ if not resumed:
246
+ plan_mode = plan_mode if plan_mode else config.default_plan_mode
247
+
248
+ llm = create_llm(
249
+ provider,
250
+ model,
251
+ thinking=thinking,
252
+ session_id=session.id,
253
+ oauth=oauth,
254
+ )
255
+ if llm is not None:
256
+ logger.info("Using LLM provider: {provider}", provider=provider)
257
+ logger.info("Using LLM model: {model}", model=model)
258
+ logger.info("Thinking mode: {thinking}", thinking=thinking)
259
+
260
+ if startup_progress is not None:
261
+ startup_progress("Scanning workspace...")
262
+
263
+ runtime = await Runtime.create(
264
+ config,
265
+ oauth,
266
+ llm,
267
+ session,
268
+ yolo,
269
+ auto=auto,
270
+ runtime_auto=runtime_auto,
271
+ skills_dirs=skills_dirs,
272
+ )
273
+ runtime.ui_mode = ui_mode
274
+ runtime.resumed = resumed
275
+ runtime.notifications.recover()
276
+ runtime.background_tasks.reconcile()
277
+ _cleanup_stale_foreground_subagents(runtime)
278
+ _phase_timings_ms["init_ms"] = int((time.monotonic() - _phase_t) * 1000)
279
+
280
+ # Refresh plugin configs with fresh credentials (e.g. OAuth tokens)
281
+ try:
282
+ from pythinker_code.plugin.manager import (
283
+ collect_host_values,
284
+ get_plugins_dir,
285
+ refresh_plugin_configs,
286
+ )
287
+
288
+ host_values = collect_host_values(config, oauth)
289
+ if host_values.get("api_key"):
290
+ refresh_plugin_configs(get_plugins_dir(), host_values)
291
+ except Exception:
292
+ logger.debug("Failed to refresh plugin configs, skipping")
293
+
294
+ if agent_file is None:
295
+ agent_file = DEFAULT_AGENT_FILE
296
+ if startup_progress is not None:
297
+ startup_progress("Loading agent...")
298
+
299
+ _phase_t = time.monotonic()
300
+ agent = await load_agent(
301
+ agent_file,
302
+ runtime,
303
+ mcp_configs=mcp_configs or [],
304
+ start_mcp_loading=not defer_mcp_loading,
305
+ )
306
+ _phase_timings_ms["mcp_ms"] = int((time.monotonic() - _phase_t) * 1000)
307
+
308
+ if startup_progress is not None:
309
+ startup_progress("Restoring conversation...")
310
+ context = Context(session.context_file)
311
+ await context.restore()
312
+
313
+ if context.system_prompt is not None:
314
+ agent = dataclasses.replace(agent, system_prompt=context.system_prompt)
315
+ else:
316
+ await context.write_system_prompt(agent.system_prompt)
317
+
318
+ soul = PythinkerSoul(agent, context=context)
319
+
320
+ # Activate plan mode if requested (for new sessions or --plan flag)
321
+ if plan_mode and not soul.plan_mode:
322
+ await soul.set_plan_mode_from_manual(True)
323
+ elif plan_mode and soul.plan_mode:
324
+ # Already in plan mode from restored session, trigger activation reminder
325
+ soul.schedule_plan_activation_reminder()
326
+
327
+ # Create and inject hook engine
328
+ from pythinker_code.hooks.engine import HookEngine
329
+
330
+ hook_engine = HookEngine(config.hooks, cwd=str(session.work_dir))
331
+ soul.set_hook_engine(hook_engine)
332
+ runtime.hook_engine = hook_engine
333
+
334
+ # --- Initialize telemetry ---
335
+ from pythinker_code.telemetry import attach_sink, set_context
336
+ from pythinker_code.telemetry import disable as disable_telemetry
337
+
338
+ telemetry_disabled = not config.telemetry or get_env_bool("PYTHINKER_DISABLE_TELEMETRY")
339
+ if telemetry_disabled:
340
+ disable_telemetry()
341
+ else:
342
+ device_id = get_device_id()
343
+ set_context(device_id=device_id, session_id=session.id)
344
+
345
+ # Initialize Sentry/Bugsink (errors) and OTel SDK (traces + logs)
346
+ # before the in-process EventSink picks up its first event.
347
+ from pythinker_code.telemetry import otel as _otel
348
+ from pythinker_code.telemetry import sentry as _sentry
349
+
350
+ _sentry.init(
351
+ version=VERSION,
352
+ device_id=device_id,
353
+ extra_tags={
354
+ "ui_mode": ui_mode,
355
+ "model": model.model if model else "",
356
+ },
357
+ )
358
+ _otel.init(version=VERSION, ui_mode=ui_mode, device_id=device_id)
359
+
360
+ from pythinker_code.telemetry.sink import EventSink
361
+
362
+ sink = EventSink(
363
+ version=VERSION,
364
+ model=model.model if model else "",
365
+ ui_mode=ui_mode,
366
+ )
367
+ attach_sink(sink)
368
+
369
+ from pythinker_code.telemetry import track, track_session_started_once
370
+ from pythinker_code.telemetry.crash import install_asyncio_handler, set_phase
371
+
372
+ # App init finished — enter runtime phase and hook asyncio crashes.
373
+ install_asyncio_handler()
374
+ set_phase("runtime")
375
+
376
+ if ui_mode != "wire":
377
+ track_session_started_once(ui_mode=ui_mode, resumed=resumed)
378
+ track(
379
+ "started",
380
+ resumed=resumed,
381
+ yolo=runtime.approval.is_yolo(),
382
+ auto=runtime.approval.is_auto(),
383
+ )
384
+ track(
385
+ "startup_perf",
386
+ duration_ms=int((time.monotonic() - _create_t0) * 1000),
387
+ config_ms=_phase_timings_ms.get("config_ms", 0),
388
+ init_ms=_phase_timings_ms.get("init_ms", 0),
389
+ mcp_ms=_phase_timings_ms.get("mcp_ms", 0),
390
+ )
391
+
392
+ return PythinkerCLI(soul, runtime, env_overrides, bg_refresh_task)
393
+
394
+ def __init__(
395
+ self,
396
+ _soul: PythinkerSoul,
397
+ _runtime: Runtime,
398
+ _env_overrides: dict[str, str],
399
+ _bg_refresh_task: asyncio.Task[None] | None = None,
400
+ ) -> None:
401
+ self._soul = _soul
402
+ self._runtime = _runtime
403
+ self._env_overrides = _env_overrides
404
+ self._bg_refresh_task = _bg_refresh_task
405
+
406
+ @property
407
+ def soul(self) -> PythinkerSoul:
408
+ """Get the PythinkerSoul instance."""
409
+ return self._soul
410
+
411
+ @property
412
+ def session(self) -> Session:
413
+ """Get the Session instance."""
414
+ return self._runtime.session
415
+
416
+ async def shutdown_background_tasks(self) -> None:
417
+ """Kill active background tasks on exit, unless keep_alive_on_exit is configured.
418
+
419
+ Prints a stderr notice naming each task so the user knows what is being
420
+ terminated, waits out the configured kill grace period so SIGTERM can
421
+ take effect, then reconciles and reports any workers that ignored the
422
+ signal.
423
+
424
+ This runs on the CLI's hard-shutdown path, so every failure mode must
425
+ be contained: disk IO errors from ``list_tasks`` / ``reconcile`` or
426
+ store corruption must not propagate and replace the real exit code
427
+ with a traceback.
428
+ """
429
+ # Cancel the startup managed-model refresh task if it is still running
430
+ # so it does not outlive the CLI process.
431
+ if self._bg_refresh_task is not None and not self._bg_refresh_task.done():
432
+ self._bg_refresh_task.cancel()
433
+
434
+ # Cleanup MCP connections held by the toolset
435
+ from pythinker_code.soul.toolset import PythinkerToolset
436
+
437
+ toolset = self._soul.agent.toolset
438
+ if isinstance(toolset, PythinkerToolset):
439
+ with contextlib.suppress(Exception):
440
+ await toolset.cleanup()
441
+
442
+ bg_config = self._runtime.config.background
443
+ if bg_config.keep_alive_on_exit:
444
+ return
445
+
446
+ try:
447
+ manager = self._runtime.background_tasks
448
+ active_views = [
449
+ v
450
+ for v in manager.list_tasks(status=None, limit=None)
451
+ if not is_terminal_status(v.runtime.status)
452
+ ]
453
+ if not active_views:
454
+ return
455
+
456
+ # Split by whether the task has already been kill-requested (e.g.
457
+ # by the ``--print`` timeout path which ran immediately before
458
+ # this shutdown). For those:
459
+ # - don't re-announce on stderr (user saw the timeout notice)
460
+ # - don't re-kill with a generic reason, which would overwrite
461
+ # the more specific ``kill_reason`` on disk
462
+ # We still reconcile + grace-wait for them so they reach terminal
463
+ # status before the process exits.
464
+ fresh_targets = [v for v in active_views if v.control.kill_requested_at is None]
465
+
466
+ if fresh_targets:
467
+ # Build and emit the kill notice via ``open_original_stderr``
468
+ # — ``sys.stderr.write`` alone would silently land in
469
+ # ``pythinker.log`` because ``redirect_stderr_to_logger`` has
470
+ # replaced fd=2 with a pipe into the logger by this point.
471
+ lines = [f"\u26a0 Killing {len(fresh_targets)} background tasks:\n"]
472
+ for view in fresh_targets:
473
+ description = view.spec.description or ""
474
+ if len(description) > 60:
475
+ description = description[:57] + "..."
476
+ lines.append(f" {view.spec.id} {description}\n")
477
+ _write_original_stderr("".join(lines))
478
+
479
+ killed: list[str] = []
480
+ for view in fresh_targets:
481
+ try:
482
+ manager.kill(view.spec.id, reason="CLI session ended")
483
+ killed.append(view.spec.id)
484
+ except Exception:
485
+ logger.exception(
486
+ "Failed to kill task {task_id} during shutdown",
487
+ task_id=view.spec.id,
488
+ )
489
+ if killed:
490
+ logger.info(
491
+ "Stopped {n} background task(s) on exit: {ids}",
492
+ n=len(killed),
493
+ ids=killed,
494
+ )
495
+
496
+ await asyncio.sleep(bg_config.kill_grace_period_ms / 1000)
497
+ manager.reconcile()
498
+ survivors = [
499
+ v
500
+ for v in manager.list_tasks(status=None, limit=None)
501
+ if not is_terminal_status(v.runtime.status)
502
+ ]
503
+ if survivors:
504
+ # Distinguish "worker is mid-shutdown" (kill request on record,
505
+ # SIGTERM delivered, worker just hasn't written terminal state
506
+ # yet) from a genuine leak (never got kill-requested, i.e.
507
+ # ``manager.kill`` raised). Without this split, users saw
508
+ # ``killed N`` from the --print timeout path immediately
509
+ # followed by ``(N tasks still alive)`` here — a direct
510
+ # semantic contradiction.
511
+ terminating = [s for s in survivors if s.control.kill_requested_at is not None]
512
+ leaking = [s for s in survivors if s.control.kill_requested_at is None]
513
+ # Report leaks first — ``stop request failed`` is strictly
514
+ # more severe than ``still terminating`` (the latter will
515
+ # resolve on its own once the worker writes terminal state).
516
+ if leaking:
517
+ _write_original_stderr(
518
+ f" ({len(leaking)} tasks still running; stop request failed)\n"
519
+ )
520
+ if terminating:
521
+ _write_original_stderr(f" ({len(terminating)} tasks still terminating)\n")
522
+ except Exception:
523
+ logger.warning("Error during background task shutdown; continuing exit", exc_info=True)
524
+
525
+ async def await_bg_tasks_shutdown(self, timeout: float = 2.0) -> None:
526
+ """Await completion of the model-refresh background task after cancellation."""
527
+ task = self._bg_refresh_task
528
+ if task is None or task.done():
529
+ return
530
+ # Best-effort cleanup — errors inside the task are already logged.
531
+ with contextlib.suppress(TimeoutError, asyncio.CancelledError, Exception):
532
+ await asyncio.wait_for(asyncio.shield(task), timeout=timeout)
533
+
534
+ @contextlib.asynccontextmanager
535
+ async def _env(self) -> AsyncGenerator[None]:
536
+ async with _CWD_LOCK:
537
+ original_cwd = HostPath.cwd()
538
+ await pythinker_host.chdir(self._runtime.session.work_dir)
539
+ try:
540
+ # to ignore possible warnings from dateparser
541
+ warnings.filterwarnings("ignore", category=DeprecationWarning)
542
+ async with self._runtime.oauth.refreshing(self._runtime):
543
+ yield
544
+ finally:
545
+ await pythinker_host.chdir(original_cwd)
546
+
547
+ async def run(
548
+ self,
549
+ user_input: str | list[ContentPart],
550
+ cancel_event: asyncio.Event,
551
+ merge_wire_messages: bool = False,
552
+ ) -> AsyncGenerator[WireMessage]:
553
+ """
554
+ Run the Pythinker CLI instance without any UI and yield Wire messages directly.
555
+
556
+ Args:
557
+ user_input (str | list[ContentPart]): The user input to the agent.
558
+ cancel_event (asyncio.Event): An event to cancel the run.
559
+ merge_wire_messages (bool): Whether to merge Wire messages as much as possible.
560
+
561
+ Yields:
562
+ WireMessage: The Wire messages from the `PythinkerSoul`.
563
+
564
+ Raises:
565
+ LLMNotSet: When the LLM is not set.
566
+ LLMNotSupported: When the LLM does not have required capabilities.
567
+ ChatProviderError: When the LLM provider returns an error.
568
+ MaxStepsReached: When the maximum number of steps is reached.
569
+ RunCancelled: When the run is cancelled by the cancel event.
570
+ """
571
+ async with self._env():
572
+ wire_future = asyncio.Future[WireUISide]()
573
+ stop_ui_loop = asyncio.Event()
574
+ approval_bridge_tasks: dict[str, asyncio.Task[None]] = {}
575
+ forwarded_approval_requests: dict[str, ApprovalRequest] = {}
576
+
577
+ async def _bridge_approval_request(request: ApprovalRequest) -> None:
578
+ try:
579
+ response = await request.wait()
580
+ assert self._runtime.approval_runtime is not None
581
+ self._runtime.approval_runtime.resolve(
582
+ request.id, response, feedback=request.feedback
583
+ )
584
+ finally:
585
+ approval_bridge_tasks.pop(request.id, None)
586
+ forwarded_approval_requests.pop(request.id, None)
587
+
588
+ def _forward_approval_request(wire: Wire, request: ApprovalRequest) -> None:
589
+ if request.id in forwarded_approval_requests:
590
+ return
591
+ forwarded_approval_requests[request.id] = request
592
+ if request.id not in approval_bridge_tasks:
593
+ approval_bridge_tasks[request.id] = asyncio.create_task(
594
+ _bridge_approval_request(request)
595
+ )
596
+ wire.soul_side.send(request)
597
+
598
+ async def _ui_loop_fn(wire: Wire) -> None:
599
+ wire_future.set_result(wire.ui_side(merge=merge_wire_messages))
600
+ assert self._runtime.root_wire_hub is not None
601
+ assert self._runtime.approval_runtime is not None
602
+ root_hub_queue = self._runtime.root_wire_hub.subscribe()
603
+ stop_task = asyncio.create_task(stop_ui_loop.wait())
604
+ queue_task = asyncio.create_task(root_hub_queue.get())
605
+ try:
606
+ for pending in self._runtime.approval_runtime.list_pending():
607
+ _forward_approval_request(
608
+ wire,
609
+ ApprovalRequest(
610
+ id=pending.id,
611
+ tool_call_id=pending.tool_call_id,
612
+ sender=pending.sender,
613
+ action=pending.action,
614
+ description=pending.description,
615
+ display=pending.display,
616
+ source_kind=pending.source.kind,
617
+ source_id=pending.source.id,
618
+ agent_id=pending.source.agent_id,
619
+ subagent_type=pending.source.subagent_type,
620
+ ),
621
+ )
622
+ while True:
623
+ done, _ = await asyncio.wait(
624
+ [stop_task, queue_task],
625
+ return_when=asyncio.FIRST_COMPLETED,
626
+ )
627
+ if stop_task in done:
628
+ break
629
+ try:
630
+ msg = queue_task.result()
631
+ except QueueShutDown:
632
+ break
633
+ match msg:
634
+ case ApprovalRequest() as request:
635
+ _forward_approval_request(wire, request)
636
+ queue_task = asyncio.create_task(root_hub_queue.get())
637
+ continue
638
+ case ApprovalResponse() as response:
639
+ if (
640
+ request := forwarded_approval_requests.get(response.request_id)
641
+ ) and not request.resolved:
642
+ request.resolve(response.response, response.feedback)
643
+ case _:
644
+ pass
645
+ wire.soul_side.send(msg)
646
+ queue_task = asyncio.create_task(root_hub_queue.get())
647
+ finally:
648
+ stop_task.cancel()
649
+ queue_task.cancel()
650
+ with contextlib.suppress(asyncio.CancelledError):
651
+ await stop_task
652
+ with contextlib.suppress(asyncio.CancelledError):
653
+ await queue_task
654
+ for task in list(approval_bridge_tasks.values()):
655
+ task.cancel()
656
+ for task in list(approval_bridge_tasks.values()):
657
+ with contextlib.suppress(asyncio.CancelledError):
658
+ await task
659
+ approval_bridge_tasks.clear()
660
+ forwarded_approval_requests.clear()
661
+ assert self._runtime.root_wire_hub is not None
662
+ self._runtime.root_wire_hub.unsubscribe(root_hub_queue)
663
+
664
+ run_cancel_event = asyncio.Event()
665
+
666
+ async def _mirror_external_cancel() -> None:
667
+ await cancel_event.wait()
668
+ run_cancel_event.set()
669
+
670
+ external_cancel_task = asyncio.create_task(
671
+ _mirror_external_cancel(),
672
+ name="cancel-event-mirror",
673
+ )
674
+ soul_task = asyncio.create_task(
675
+ run_soul(
676
+ self.soul,
677
+ user_input,
678
+ _ui_loop_fn,
679
+ run_cancel_event,
680
+ runtime=self._runtime,
681
+ )
682
+ )
683
+
684
+ wire_shut_down = False
685
+ try:
686
+ wire_ui = await wire_future
687
+ while True:
688
+ msg = await wire_ui.receive()
689
+ yield msg
690
+ except QueueShutDown:
691
+ wire_shut_down = True
692
+ pass
693
+ finally:
694
+ # stop consuming Wire messages
695
+ stop_ui_loop.set()
696
+ cleanup_cancelled_run = False
697
+ if not wire_shut_down and not soul_task.done() and not cancel_event.is_set():
698
+ cleanup_cancelled_run = True
699
+ run_cancel_event.set()
700
+ # wait for the soul task to finish, or raise
701
+ try:
702
+ await soul_task
703
+ except RunCancelled:
704
+ if not cleanup_cancelled_run:
705
+ raise
706
+ finally:
707
+ external_cancel_task.cancel()
708
+ with contextlib.suppress(asyncio.CancelledError):
709
+ await external_cancel_task
710
+
711
+ async def run_shell(
712
+ self, command: str | None = None, *, prefill_text: str | None = None
713
+ ) -> bool:
714
+ """Run the Pythinker CLI instance with shell UI."""
715
+ from pythinker_code.ui.shell import Shell, WelcomeInfoItem
716
+
717
+ if command is None:
718
+ from pythinker_code.ui.shell.update import (
719
+ print_update_banner,
720
+ schedule_auto_update_check,
721
+ )
722
+
723
+ print_update_banner()
724
+ schedule_auto_update_check()
725
+
726
+ welcome_info = [
727
+ WelcomeInfoItem(
728
+ name="Directory", value=str(shorten_home(self._runtime.session.work_dir))
729
+ ),
730
+ WelcomeInfoItem(name="Session", value=self._runtime.session.id),
731
+ ]
732
+ if base_url := self._env_overrides.get("PYTHINKER_BASE_URL"):
733
+ welcome_info.append(
734
+ WelcomeInfoItem(
735
+ name="API URL",
736
+ value=f"{base_url} (from PYTHINKER_BASE_URL)",
737
+ level=WelcomeInfoItem.Level.WARN,
738
+ )
739
+ )
740
+ if self._env_overrides.get("PYTHINKER_API_KEY"):
741
+ welcome_info.append(
742
+ WelcomeInfoItem(
743
+ name="API Key",
744
+ value="****** (from PYTHINKER_API_KEY)",
745
+ level=WelcomeInfoItem.Level.WARN,
746
+ )
747
+ )
748
+ if not self._runtime.llm:
749
+ welcome_info.append(
750
+ WelcomeInfoItem(
751
+ name="Model",
752
+ value="not set, send /login to login",
753
+ level=WelcomeInfoItem.Level.WARN,
754
+ )
755
+ )
756
+ elif "PYTHINKER_MODEL_NAME" in self._env_overrides:
757
+ welcome_info.append(
758
+ WelcomeInfoItem(
759
+ name="Model",
760
+ value=f"{self._soul.model_name} (from PYTHINKER_MODEL_NAME)",
761
+ level=WelcomeInfoItem.Level.WARN,
762
+ )
763
+ )
764
+ else:
765
+ welcome_info.append(
766
+ WelcomeInfoItem(
767
+ name="Model",
768
+ value=model_display_name(
769
+ self._soul.model_name,
770
+ self._runtime.llm.model_config if self._runtime.llm else None,
771
+ ),
772
+ level=WelcomeInfoItem.Level.INFO,
773
+ )
774
+ )
775
+ model_name = self._soul.model_name
776
+ if model_name not in (
777
+ "pythinker-for-coding",
778
+ "pythinker-code",
779
+ ) and not model_name.startswith("pythinker-ai"):
780
+ welcome_info.append(
781
+ WelcomeInfoItem(
782
+ name="Tip",
783
+ value="send /login to use Pythinker for Coding",
784
+ level=WelcomeInfoItem.Level.WARN,
785
+ )
786
+ )
787
+ welcome_info.append(
788
+ WelcomeInfoItem(
789
+ name="Tip",
790
+ value=(
791
+ 'Pythinker reviews before it writes. Try "review this diff",'
792
+ ' "scan for vulnerabilities", or "find the root cause"'
793
+ " — code edits come after the analysis."
794
+ ),
795
+ level=WelcomeInfoItem.Level.INFO,
796
+ )
797
+ )
798
+ welcome_info.append(
799
+ WelcomeInfoItem(
800
+ name="Tip",
801
+ value=(
802
+ "Spot a bug or have feedback? Type /feedback right in this session"
803
+ " — every report makes Pythinker better."
804
+ ),
805
+ level=WelcomeInfoItem.Level.INFO,
806
+ )
807
+ )
808
+ async with self._env():
809
+ shell = Shell(self._soul, welcome_info=welcome_info, prefill_text=prefill_text)
810
+ return await shell.run(command)
811
+
812
+ async def run_print(
813
+ self,
814
+ input_format: InputFormat,
815
+ output_format: OutputFormat,
816
+ command: str | None = None,
817
+ *,
818
+ final_only: bool = False,
819
+ ) -> int:
820
+ """Run the Pythinker CLI instance with print UI."""
821
+ from pythinker_code.ui.print import Print
822
+
823
+ async with self._env():
824
+ print_ = Print(
825
+ self._soul,
826
+ input_format,
827
+ output_format,
828
+ self._runtime.session.context_file,
829
+ final_only=final_only,
830
+ )
831
+ return await print_.run(command)
832
+
833
+ async def run_acp(self) -> None:
834
+ """Run the Pythinker CLI instance as ACP server."""
835
+ from pythinker_code.ui.acp import ACP
836
+
837
+ async with self._env():
838
+ acp = ACP(self._soul)
839
+ await acp.run()
840
+
841
+ async def run_wire_stdio(self) -> None:
842
+ """Run the Pythinker CLI instance as Wire server over stdio."""
843
+ from pythinker_code.wire.server import WireServer
844
+
845
+ async with self._env():
846
+ server = WireServer(self._soul)
847
+ await server.serve()