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.
- pythinker_code/CHANGELOG.md +16 -0
- pythinker_code/__init__.py +0 -0
- pythinker_code/__main__.py +92 -0
- pythinker_code/acp/AGENTS.md +93 -0
- pythinker_code/acp/__init__.py +13 -0
- pythinker_code/acp/convert.py +128 -0
- pythinker_code/acp/host.py +298 -0
- pythinker_code/acp/mcp.py +46 -0
- pythinker_code/acp/server.py +497 -0
- pythinker_code/acp/session.py +496 -0
- pythinker_code/acp/tools.py +167 -0
- pythinker_code/acp/types.py +13 -0
- pythinker_code/acp/version.py +45 -0
- pythinker_code/agents/default/agent.yaml +36 -0
- pythinker_code/agents/default/coder.yaml +25 -0
- pythinker_code/agents/default/explore.yaml +46 -0
- pythinker_code/agents/default/plan.yaml +30 -0
- pythinker_code/agents/default/system.md +164 -0
- pythinker_code/agents/okabe/agent.yaml +22 -0
- pythinker_code/agentspec.py +163 -0
- pythinker_code/app.py +820 -0
- pythinker_code/approval_runtime/__init__.py +29 -0
- pythinker_code/approval_runtime/models.py +42 -0
- pythinker_code/approval_runtime/runtime.py +235 -0
- pythinker_code/auth/__init__.py +25 -0
- pythinker_code/auth/anthropic_direct.py +207 -0
- pythinker_code/auth/deepseek.py +192 -0
- pythinker_code/auth/lm_studio.py +418 -0
- pythinker_code/auth/minimax.py +203 -0
- pythinker_code/auth/oauth.py +1122 -0
- pythinker_code/auth/ollama.py +293 -0
- pythinker_code/auth/openai.py +771 -0
- pythinker_code/auth/opencode_go.py +203 -0
- pythinker_code/auth/openrouter.py +225 -0
- pythinker_code/auth/platforms.py +466 -0
- pythinker_code/background/__init__.py +36 -0
- pythinker_code/background/agent_runner.py +231 -0
- pythinker_code/background/ids.py +19 -0
- pythinker_code/background/manager.py +650 -0
- pythinker_code/background/models.py +105 -0
- pythinker_code/background/store.py +237 -0
- pythinker_code/background/summary.py +66 -0
- pythinker_code/background/worker.py +209 -0
- pythinker_code/cli/__init__.py +1326 -0
- pythinker_code/cli/__main__.py +19 -0
- pythinker_code/cli/_lazy_group.py +238 -0
- pythinker_code/cli/export.py +322 -0
- pythinker_code/cli/info.py +62 -0
- pythinker_code/cli/mcp.py +349 -0
- pythinker_code/cli/plugin.py +351 -0
- pythinker_code/cli/toad.py +74 -0
- pythinker_code/cli/vis.py +38 -0
- pythinker_code/cli/web.py +80 -0
- pythinker_code/config.py +453 -0
- pythinker_code/constant.py +33 -0
- pythinker_code/exception.py +43 -0
- pythinker_code/hooks/__init__.py +4 -0
- pythinker_code/hooks/config.py +34 -0
- pythinker_code/hooks/engine.py +371 -0
- pythinker_code/hooks/events.py +190 -0
- pythinker_code/hooks/runner.py +89 -0
- pythinker_code/llm.py +412 -0
- pythinker_code/metadata.py +79 -0
- pythinker_code/notifications/__init__.py +33 -0
- pythinker_code/notifications/llm.py +77 -0
- pythinker_code/notifications/manager.py +145 -0
- pythinker_code/notifications/models.py +50 -0
- pythinker_code/notifications/notifier.py +41 -0
- pythinker_code/notifications/store.py +118 -0
- pythinker_code/notifications/wire.py +21 -0
- pythinker_code/plugin/__init__.py +124 -0
- pythinker_code/plugin/manager.py +153 -0
- pythinker_code/plugin/tool.py +173 -0
- pythinker_code/prompts/__init__.py +6 -0
- pythinker_code/prompts/compact.md +73 -0
- pythinker_code/prompts/init.md +21 -0
- pythinker_code/py.typed +0 -0
- pythinker_code/session.py +319 -0
- pythinker_code/session_fork.py +325 -0
- pythinker_code/session_state.py +132 -0
- pythinker_code/share.py +14 -0
- pythinker_code/skill/__init__.py +727 -0
- pythinker_code/skill/flow/__init__.py +99 -0
- pythinker_code/skill/flow/d2.py +482 -0
- pythinker_code/skill/flow/mermaid.py +266 -0
- pythinker_code/skills/pythinker-code-help/SKILL.md +54 -0
- pythinker_code/skills/skill-creator/SKILL.md +367 -0
- pythinker_code/soul/__init__.py +304 -0
- pythinker_code/soul/agent.py +520 -0
- pythinker_code/soul/approval.py +267 -0
- pythinker_code/soul/btw.py +214 -0
- pythinker_code/soul/compaction.py +189 -0
- pythinker_code/soul/context.py +339 -0
- pythinker_code/soul/denwarenji.py +39 -0
- pythinker_code/soul/dynamic_injection.py +84 -0
- pythinker_code/soul/dynamic_injections/__init__.py +0 -0
- pythinker_code/soul/dynamic_injections/auto_mode.py +72 -0
- pythinker_code/soul/dynamic_injections/plan_mode.py +239 -0
- pythinker_code/soul/message.py +92 -0
- pythinker_code/soul/pythinkersoul.py +1613 -0
- pythinker_code/soul/slash.py +340 -0
- pythinker_code/soul/toolset.py +788 -0
- pythinker_code/subagents/__init__.py +21 -0
- pythinker_code/subagents/builder.py +42 -0
- pythinker_code/subagents/core.py +86 -0
- pythinker_code/subagents/git_context.py +172 -0
- pythinker_code/subagents/models.py +54 -0
- pythinker_code/subagents/output.py +71 -0
- pythinker_code/subagents/registry.py +28 -0
- pythinker_code/subagents/runner.py +428 -0
- pythinker_code/subagents/store.py +196 -0
- pythinker_code/telemetry/__init__.py +211 -0
- pythinker_code/telemetry/config.py +54 -0
- pythinker_code/telemetry/crash.py +157 -0
- pythinker_code/telemetry/metrics.py +208 -0
- pythinker_code/telemetry/otel.py +240 -0
- pythinker_code/telemetry/sentry.py +167 -0
- pythinker_code/telemetry/sink.py +189 -0
- pythinker_code/tools/AGENTS.md +6 -0
- pythinker_code/tools/__init__.py +105 -0
- pythinker_code/tools/agent/__init__.py +277 -0
- pythinker_code/tools/agent/description.md +41 -0
- pythinker_code/tools/ask_user/__init__.py +159 -0
- pythinker_code/tools/ask_user/description.md +19 -0
- pythinker_code/tools/background/__init__.py +318 -0
- pythinker_code/tools/background/list.md +10 -0
- pythinker_code/tools/background/output.md +11 -0
- pythinker_code/tools/background/stop.md +8 -0
- pythinker_code/tools/display.py +46 -0
- pythinker_code/tools/dmail/__init__.py +38 -0
- pythinker_code/tools/dmail/dmail.md +17 -0
- pythinker_code/tools/file/__init__.py +30 -0
- pythinker_code/tools/file/glob.md +17 -0
- pythinker_code/tools/file/glob.py +160 -0
- pythinker_code/tools/file/grep.md +6 -0
- pythinker_code/tools/file/grep_local.py +589 -0
- pythinker_code/tools/file/plan_mode.py +45 -0
- pythinker_code/tools/file/read.md +16 -0
- pythinker_code/tools/file/read.py +300 -0
- pythinker_code/tools/file/read_media.md +24 -0
- pythinker_code/tools/file/read_media.py +217 -0
- pythinker_code/tools/file/replace.md +7 -0
- pythinker_code/tools/file/replace.py +195 -0
- pythinker_code/tools/file/utils.py +257 -0
- pythinker_code/tools/file/write.md +5 -0
- pythinker_code/tools/file/write.py +177 -0
- pythinker_code/tools/plan/__init__.py +327 -0
- pythinker_code/tools/plan/description.md +29 -0
- pythinker_code/tools/plan/enter.py +190 -0
- pythinker_code/tools/plan/enter_description.md +35 -0
- pythinker_code/tools/plan/heroes.py +277 -0
- pythinker_code/tools/shell/__init__.py +253 -0
- pythinker_code/tools/shell/bash.md +35 -0
- pythinker_code/tools/shell/powershell.md +30 -0
- pythinker_code/tools/test.py +55 -0
- pythinker_code/tools/think/__init__.py +21 -0
- pythinker_code/tools/think/think.md +1 -0
- pythinker_code/tools/todo/__init__.py +168 -0
- pythinker_code/tools/todo/set_todo_list.md +23 -0
- pythinker_code/tools/utils.py +199 -0
- pythinker_code/tools/web/__init__.py +4 -0
- pythinker_code/tools/web/fetch.md +1 -0
- pythinker_code/tools/web/fetch.py +189 -0
- pythinker_code/tools/web/search.md +1 -0
- pythinker_code/tools/web/search.py +163 -0
- pythinker_code/ui/__init__.py +0 -0
- pythinker_code/ui/acp/__init__.py +99 -0
- pythinker_code/ui/print/__init__.py +474 -0
- pythinker_code/ui/print/visualize.py +185 -0
- pythinker_code/ui/shell/__init__.py +1696 -0
- pythinker_code/ui/shell/console.py +109 -0
- pythinker_code/ui/shell/debug.py +190 -0
- pythinker_code/ui/shell/echo.py +17 -0
- pythinker_code/ui/shell/export_import.py +117 -0
- pythinker_code/ui/shell/keyboard.py +300 -0
- pythinker_code/ui/shell/mcp_status.py +113 -0
- pythinker_code/ui/shell/model_picker.py +318 -0
- pythinker_code/ui/shell/oauth.py +272 -0
- pythinker_code/ui/shell/placeholders.py +531 -0
- pythinker_code/ui/shell/prompt.py +2278 -0
- pythinker_code/ui/shell/replay.py +215 -0
- pythinker_code/ui/shell/session_picker.py +227 -0
- pythinker_code/ui/shell/setup.py +212 -0
- pythinker_code/ui/shell/slash.py +898 -0
- pythinker_code/ui/shell/startup.py +32 -0
- pythinker_code/ui/shell/task_browser.py +486 -0
- pythinker_code/ui/shell/update.py +350 -0
- pythinker_code/ui/shell/usage.py +291 -0
- pythinker_code/ui/shell/usage_adapters/__init__.py +50 -0
- pythinker_code/ui/shell/usage_adapters/anthropic_admin.py +233 -0
- pythinker_code/ui/shell/usage_adapters/base.py +72 -0
- pythinker_code/ui/shell/usage_adapters/deepseek.py +137 -0
- pythinker_code/ui/shell/usage_adapters/minimax.py +236 -0
- pythinker_code/ui/shell/usage_adapters/openai_admin.py +225 -0
- pythinker_code/ui/shell/usage_adapters/openai_chatgpt.py +241 -0
- pythinker_code/ui/shell/usage_adapters/opencode_go.py +232 -0
- pythinker_code/ui/shell/usage_adapters/openrouter.py +105 -0
- pythinker_code/ui/shell/usage_adapters/pythinker.py +189 -0
- pythinker_code/ui/shell/usage_adapters/pythinker_ai.py +50 -0
- pythinker_code/ui/shell/usage_render.py +150 -0
- pythinker_code/ui/shell/visualize/__init__.py +165 -0
- pythinker_code/ui/shell/visualize/_approval_panel.py +505 -0
- pythinker_code/ui/shell/visualize/_blocks.py +629 -0
- pythinker_code/ui/shell/visualize/_btw_panel.py +224 -0
- pythinker_code/ui/shell/visualize/_input_router.py +48 -0
- pythinker_code/ui/shell/visualize/_interactive.py +523 -0
- pythinker_code/ui/shell/visualize/_live_view.py +826 -0
- pythinker_code/ui/shell/visualize/_question_panel.py +586 -0
- pythinker_code/ui/theme.py +241 -0
- pythinker_code/usage_ratelimit_cache.py +175 -0
- pythinker_code/utils/__init__.py +0 -0
- pythinker_code/utils/aiohttp.py +24 -0
- pythinker_code/utils/aioqueue.py +72 -0
- pythinker_code/utils/broadcast.py +37 -0
- pythinker_code/utils/changelog.py +108 -0
- pythinker_code/utils/clipboard.py +246 -0
- pythinker_code/utils/datetime.py +64 -0
- pythinker_code/utils/diff.py +135 -0
- pythinker_code/utils/editor.py +91 -0
- pythinker_code/utils/environment.py +73 -0
- pythinker_code/utils/envvar.py +22 -0
- pythinker_code/utils/export.py +696 -0
- pythinker_code/utils/file_filter.py +375 -0
- pythinker_code/utils/frontmatter.py +70 -0
- pythinker_code/utils/io.py +27 -0
- pythinker_code/utils/logging.py +146 -0
- pythinker_code/utils/media_tags.py +29 -0
- pythinker_code/utils/message.py +24 -0
- pythinker_code/utils/path.py +199 -0
- pythinker_code/utils/proctitle.py +33 -0
- pythinker_code/utils/proxy.py +31 -0
- pythinker_code/utils/pyinstaller.py +45 -0
- pythinker_code/utils/rich/__init__.py +33 -0
- pythinker_code/utils/rich/columns.py +99 -0
- pythinker_code/utils/rich/diff_render.py +481 -0
- pythinker_code/utils/rich/markdown.py +900 -0
- pythinker_code/utils/rich/markdown_sample.md +108 -0
- pythinker_code/utils/rich/markdown_sample_short.md +2 -0
- pythinker_code/utils/rich/syntax.py +114 -0
- pythinker_code/utils/sensitive.py +54 -0
- pythinker_code/utils/server.py +121 -0
- pythinker_code/utils/signals.py +43 -0
- pythinker_code/utils/slashcmd.py +124 -0
- pythinker_code/utils/string.py +41 -0
- pythinker_code/utils/subprocess_env.py +73 -0
- pythinker_code/utils/term.py +168 -0
- pythinker_code/utils/typing.py +20 -0
- pythinker_code/vis/__init__.py +0 -0
- pythinker_code/vis/api/__init__.py +5 -0
- pythinker_code/vis/api/sessions.py +687 -0
- pythinker_code/vis/api/statistics.py +209 -0
- pythinker_code/vis/api/system.py +19 -0
- pythinker_code/vis/app.py +175 -0
- pythinker_code/vis/static/assets/highlighted-body-B3W2YXNL-D2MTYyJz.js +1 -0
- pythinker_code/vis/static/assets/index-CezafTt_.js +185 -0
- pythinker_code/vis/static/assets/index-DSRInNnm.css +1 -0
- pythinker_code/vis/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- pythinker_code/vis/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- pythinker_code/vis/static/index.html +17 -0
- pythinker_code/web/__init__.py +5 -0
- pythinker_code/web/api/__init__.py +15 -0
- pythinker_code/web/api/config.py +208 -0
- pythinker_code/web/api/open_in.py +199 -0
- pythinker_code/web/api/sessions.py +1225 -0
- pythinker_code/web/app.py +451 -0
- pythinker_code/web/auth.py +191 -0
- pythinker_code/web/models.py +98 -0
- pythinker_code/web/runner/__init__.py +5 -0
- pythinker_code/web/runner/messages.py +57 -0
- pythinker_code/web/runner/process.py +754 -0
- pythinker_code/web/runner/worker.py +97 -0
- pythinker_code/web/static/assets/KaTeX_AMS-Regular-BQhdFMY1.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_AMS-Regular-DMm9YOAa.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_AMS-Regular-DRggAlZN.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-ATXxdsX0.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-BEiXGLvX.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Bold-Dq_IR9rO.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-CTRA-rTL.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-Di6jR-x-.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Caligraphic-Regular-wX97UBjC.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-BdnERNNW.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-BsDP51OF.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Bold-CL6g_b3V.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-CB_wures.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-CTYiF6lA.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Fraktur-Regular-Dxdc4cR9.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Bold-Cx986IdX.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Bold-Jm3AIy58.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Bold-waoOVXN0.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-DxDJ3AOS.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-DzxPMmG6.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-BoldItalic-SpSLRI95.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Italic-3WenGoN9.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Italic-BMLOBm91.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Italic-NWA7e6Wa.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Regular-B22Nviop.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Regular-Dr94JaBh.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Main-Regular-ypZvNtVU.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-B3XSjfu4.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-CZnvNsCZ.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-BoldItalic-iY-2wyZ7.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-Italic-DA0__PXp.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-Italic-flOr_0UB.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Math-Italic-t53AETM-.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-CFMepnvq.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-D1sUS0GD.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Bold-DbIhKOiC.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-C3H0VqGB.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-DN2j7dab.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Italic-YYjJ1zSn.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-BNo7hRIc.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-CS6fqUqJ.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_SansSerif-Regular-DDBCnlJ7.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Script-Regular-C5JkGWo-.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Script-Regular-D3wIWfF6.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Script-Regular-D5yQViql.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size1-Regular-C195tn64.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size1-Regular-Dbsnue_I.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size1-Regular-mCD8mA8B.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Size2-Regular-B7gKUWhC.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size2-Regular-Dy4dx90m.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Size2-Regular-oD1tc_U0.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size3-Regular-CTq5MqoE.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size3-Regular-DgpXs0kz.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size4-Regular-BF-4gkZK.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Size4-Regular-DWFBv043.ttf +0 -0
- pythinker_code/web/static/assets/KaTeX_Size4-Regular-Dl5lxZxV.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-C0xS9mPB.woff +0 -0
- pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-CO6r4hn1.woff2 +0 -0
- pythinker_code/web/static/assets/KaTeX_Typewriter-Regular-D3Ib7_Hf.ttf +0 -0
- pythinker_code/web/static/assets/_baseUniq--dyU3g5v.js +1 -0
- pythinker_code/web/static/assets/abap-BdImnpbu.js +1 -0
- pythinker_code/web/static/assets/actionscript-3-CfeIJUat.js +1 -0
- pythinker_code/web/static/assets/ada-bCR0ucgS.js +1 -0
- pythinker_code/web/static/assets/andromeeda-C-Jbm3Hp.js +1 -0
- pythinker_code/web/static/assets/angular-html-CU67Zn6k.js +1 -0
- pythinker_code/web/static/assets/angular-ts-BwZT4LLn.js +1 -0
- pythinker_code/web/static/assets/apache-Pmp26Uib.js +1 -0
- pythinker_code/web/static/assets/apex-D8_7TLub.js +1 -0
- pythinker_code/web/static/assets/apl-dKokRX4l.js +1 -0
- pythinker_code/web/static/assets/applescript-Co6uUVPk.js +1 -0
- pythinker_code/web/static/assets/ara-BRHolxvo.js +1 -0
- pythinker_code/web/static/assets/arc-DkMjLpYa.js +1 -0
- pythinker_code/web/static/assets/architectureDiagram-VXUJARFQ-CHWVaGo9.js +36 -0
- pythinker_code/web/static/assets/asciidoc-Dv7Oe6Be.js +1 -0
- pythinker_code/web/static/assets/asm-D_Q5rh1f.js +1 -0
- pythinker_code/web/static/assets/astro-CbQHKStN.js +1 -0
- pythinker_code/web/static/assets/aurora-x-D-2ljcwZ.js +1 -0
- pythinker_code/web/static/assets/awk-DMzUqQB5.js +1 -0
- pythinker_code/web/static/assets/ayu-dark-CmMr59Fi.js +1 -0
- pythinker_code/web/static/assets/ballerina-BFfxhgS-.js +1 -0
- pythinker_code/web/static/assets/bat-BkioyH1T.js +1 -0
- pythinker_code/web/static/assets/beancount-k_qm7-4y.js +1 -0
- pythinker_code/web/static/assets/berry-uYugtg8r.js +1 -0
- pythinker_code/web/static/assets/bibtex-CHM0blh-.js +1 -0
- pythinker_code/web/static/assets/bicep-Bmn6On1c.js +1 -0
- pythinker_code/web/static/assets/blade-D4QpJJKB.js +1 -0
- pythinker_code/web/static/assets/blockDiagram-VD42YOAC-DzdKe497.js +122 -0
- pythinker_code/web/static/assets/bsl-BO_Y6i37.js +1 -0
- pythinker_code/web/static/assets/c-BIGW1oBm.js +1 -0
- pythinker_code/web/static/assets/c3-VCDPK7BO.js +1 -0
- pythinker_code/web/static/assets/c4Diagram-YG6GDRKO-D84Blotg.js +10 -0
- pythinker_code/web/static/assets/cadence-Bv_4Rxtq.js +1 -0
- pythinker_code/web/static/assets/cairo-KRGpt6FW.js +1 -0
- pythinker_code/web/static/assets/catppuccin-frappe-DFWUc33u.js +1 -0
- pythinker_code/web/static/assets/catppuccin-latte-C9dUb6Cb.js +1 -0
- pythinker_code/web/static/assets/catppuccin-macchiato-DQyhUUbL.js +1 -0
- pythinker_code/web/static/assets/catppuccin-mocha-D87Tk5Gz.js +1 -0
- pythinker_code/web/static/assets/channel-CllSjjdl.js +1 -0
- pythinker_code/web/static/assets/chunk-4BX2VUAB-C9w8wleE.js +1 -0
- pythinker_code/web/static/assets/chunk-55IACEB6-YlYJ8HnF.js +1 -0
- pythinker_code/web/static/assets/chunk-B4BG7PRW-Bwtz_AHU.js +165 -0
- pythinker_code/web/static/assets/chunk-DI55MBZ5-BbEHkl8h.js +220 -0
- pythinker_code/web/static/assets/chunk-FMBD7UC4-BKPbvjLC.js +15 -0
- pythinker_code/web/static/assets/chunk-QN33PNHL-D73dQvKf.js +1 -0
- pythinker_code/web/static/assets/chunk-QZHKN3VN-zGiLKes_.js +1 -0
- pythinker_code/web/static/assets/chunk-TZMSLE5B-LHJCi2fy.js +1 -0
- pythinker_code/web/static/assets/clarity-D53aC0YG.js +1 -0
- pythinker_code/web/static/assets/classDiagram-2ON5EDUG-vX27iZwa.js +1 -0
- pythinker_code/web/static/assets/classDiagram-v2-WZHVMYZB-vX27iZwa.js +1 -0
- pythinker_code/web/static/assets/clojure-P80f7IUj.js +1 -0
- pythinker_code/web/static/assets/clone-DYBkaPm2.js +1 -0
- pythinker_code/web/static/assets/cmake-D1j8_8rp.js +1 -0
- pythinker_code/web/static/assets/cobol-nwyudZeR.js +1 -0
- pythinker_code/web/static/assets/code-block-IT6T5CEO-NtKViZGl.js +2 -0
- pythinker_code/web/static/assets/codeowners-Bp6g37R7.js +1 -0
- pythinker_code/web/static/assets/codeql-DsOJ9woJ.js +1 -0
- pythinker_code/web/static/assets/coffee-Ch7k5sss.js +1 -0
- pythinker_code/web/static/assets/common-lisp-Cg-RD9OK.js +1 -0
- pythinker_code/web/static/assets/coq-DkFqJrB1.js +1 -0
- pythinker_code/web/static/assets/cose-bilkent-S5V4N54A-DialjZpd.js +1 -0
- pythinker_code/web/static/assets/cpp-CofmeUqb.js +1 -0
- pythinker_code/web/static/assets/crystal-tKQVLTB8.js +1 -0
- pythinker_code/web/static/assets/csharp-K5feNrxe.js +1 -0
- pythinker_code/web/static/assets/css-DPfMkruS.js +1 -0
- pythinker_code/web/static/assets/csv-fuZLfV_i.js +1 -0
- pythinker_code/web/static/assets/cue-D82EKSYY.js +1 -0
- pythinker_code/web/static/assets/cypher-COkxafJQ.js +1 -0
- pythinker_code/web/static/assets/cytoscape.esm-C_Fzpdck.js +321 -0
- pythinker_code/web/static/assets/d-85-TOEBH.js +1 -0
- pythinker_code/web/static/assets/dagre-6UL2VRFP-DfuvkZZ7.js +4 -0
- pythinker_code/web/static/assets/dark-plus-C3mMm8J8.js +1 -0
- pythinker_code/web/static/assets/dart-CF10PKvl.js +1 -0
- pythinker_code/web/static/assets/dax-CEL-wOlO.js +1 -0
- pythinker_code/web/static/assets/defaultLocale-DX6XiGOO.js +1 -0
- pythinker_code/web/static/assets/desktop-BmXAJ9_W.js +1 -0
- pythinker_code/web/static/assets/diagram-PSM6KHXK-DLGctX3v.js +24 -0
- pythinker_code/web/static/assets/diagram-QEK2KX5R-DnxN6S0F.js +43 -0
- pythinker_code/web/static/assets/diagram-S2PKOQOG-Caq_Set9.js +24 -0
- pythinker_code/web/static/assets/diff-D97Zzqfu.js +1 -0
- pythinker_code/web/static/assets/docker-BcOcwvcX.js +1 -0
- pythinker_code/web/static/assets/dotenv-Da5cRb03.js +1 -0
- pythinker_code/web/static/assets/dracula-BzJJZx-M.js +1 -0
- pythinker_code/web/static/assets/dracula-soft-BXkSAIEj.js +1 -0
- pythinker_code/web/static/assets/dream-maker-BtqSS_iP.js +1 -0
- pythinker_code/web/static/assets/edge-BkV0erSs.js +1 -0
- pythinker_code/web/static/assets/elixir-CDX3lj18.js +1 -0
- pythinker_code/web/static/assets/elm-DbKCFpqz.js +1 -0
- pythinker_code/web/static/assets/emacs-lisp-C9XAeP06.js +1 -0
- pythinker_code/web/static/assets/erDiagram-Q2GNP2WA-BgTfALoK.js +60 -0
- pythinker_code/web/static/assets/erb-BOJIQeun.js +1 -0
- pythinker_code/web/static/assets/erlang-DsQrWhSR.js +1 -0
- pythinker_code/web/static/assets/everforest-dark-BgDCqdQA.js +1 -0
- pythinker_code/web/static/assets/everforest-light-C8M2exoo.js +1 -0
- pythinker_code/web/static/assets/fennel-BYunw83y.js +1 -0
- pythinker_code/web/static/assets/fish-BvzEVeQv.js +1 -0
- pythinker_code/web/static/assets/flowDiagram-NV44I4VS-QjW_fnGi.js +162 -0
- pythinker_code/web/static/assets/fluent-C4IJs8-o.js +1 -0
- pythinker_code/web/static/assets/fortran-fixed-form-CkoXwp7k.js +1 -0
- pythinker_code/web/static/assets/fortran-free-form-BxgE0vQu.js +1 -0
- pythinker_code/web/static/assets/fsharp-CXgrBDvD.js +1 -0
- pythinker_code/web/static/assets/ganttDiagram-JELNMOA3-fqi8JFof.js +267 -0
- pythinker_code/web/static/assets/gdresource-B7Tvp0Sc.js +1 -0
- pythinker_code/web/static/assets/gdscript-DTMYz4Jt.js +1 -0
- pythinker_code/web/static/assets/gdshader-DkwncUOv.js +1 -0
- pythinker_code/web/static/assets/genie-D0YGMca9.js +1 -0
- pythinker_code/web/static/assets/gherkin-DyxjwDmM.js +1 -0
- pythinker_code/web/static/assets/git-commit-F4YmCXRG.js +1 -0
- pythinker_code/web/static/assets/git-rebase-r7XF79zn.js +1 -0
- pythinker_code/web/static/assets/gitGraphDiagram-NY62KEGX-i7o6VQ8x.js +65 -0
- pythinker_code/web/static/assets/github-dark-DHJKELXO.js +1 -0
- pythinker_code/web/static/assets/github-dark-default-Cuk6v7N8.js +1 -0
- pythinker_code/web/static/assets/github-dark-dimmed-DH5Ifo-i.js +1 -0
- pythinker_code/web/static/assets/github-dark-high-contrast-E3gJ1_iC.js +1 -0
- pythinker_code/web/static/assets/github-light-DAi9KRSo.js +1 -0
- pythinker_code/web/static/assets/github-light-default-D7oLnXFd.js +1 -0
- pythinker_code/web/static/assets/github-light-high-contrast-BfjtVDDH.js +1 -0
- pythinker_code/web/static/assets/gleam-BspZqrRM.js +1 -0
- pythinker_code/web/static/assets/glimmer-js-Rg0-pVw9.js +1 -0
- pythinker_code/web/static/assets/glimmer-ts-U6CK756n.js +1 -0
- pythinker_code/web/static/assets/glsl-DplSGwfg.js +1 -0
- pythinker_code/web/static/assets/gn-n2N0HUVH.js +1 -0
- pythinker_code/web/static/assets/gnuplot-DdkO51Og.js +1 -0
- pythinker_code/web/static/assets/go-Dn2_MT6a.js +1 -0
- pythinker_code/web/static/assets/graph-C0vZK2pT.js +1 -0
- pythinker_code/web/static/assets/graphql-ChdNCCLP.js +1 -0
- pythinker_code/web/static/assets/groovy-gcz8RCvz.js +1 -0
- pythinker_code/web/static/assets/gruvbox-dark-hard-CFHQjOhq.js +1 -0
- pythinker_code/web/static/assets/gruvbox-dark-medium-GsRaNv29.js +1 -0
- pythinker_code/web/static/assets/gruvbox-dark-soft-CVdnzihN.js +1 -0
- pythinker_code/web/static/assets/gruvbox-light-hard-CH1njM8p.js +1 -0
- pythinker_code/web/static/assets/gruvbox-light-medium-DRw_LuNl.js +1 -0
- pythinker_code/web/static/assets/gruvbox-light-soft-hJgmCMqR.js +1 -0
- pythinker_code/web/static/assets/hack-CaT9iCJl.js +1 -0
- pythinker_code/web/static/assets/haml-B8DHNrY2.js +1 -0
- pythinker_code/web/static/assets/handlebars-BL8al0AC.js +1 -0
- pythinker_code/web/static/assets/haskell-Df6bDoY_.js +1 -0
- pythinker_code/web/static/assets/haxe-CzTSHFRz.js +1 -0
- pythinker_code/web/static/assets/hcl-BWvSN4gD.js +1 -0
- pythinker_code/web/static/assets/hjson-D5-asLiD.js +1 -0
- pythinker_code/web/static/assets/hlsl-D3lLCCz7.js +1 -0
- pythinker_code/web/static/assets/houston-DnULxvSX.js +1 -0
- pythinker_code/web/static/assets/html-GMplVEZG.js +1 -0
- pythinker_code/web/static/assets/html-derivative-BFtXZ54Q.js +1 -0
- pythinker_code/web/static/assets/http-jrhK8wxY.js +1 -0
- pythinker_code/web/static/assets/hurl-irOxFIW8.js +1 -0
- pythinker_code/web/static/assets/hxml-Bvhsp5Yf.js +1 -0
- pythinker_code/web/static/assets/hy-DFXneXwc.js +1 -0
- pythinker_code/web/static/assets/imba-DGztddWO.js +1 -0
- pythinker_code/web/static/assets/index-BYCCk6-K.js +153 -0
- pythinker_code/web/static/assets/index-BpoLgcEt.js +1 -0
- pythinker_code/web/static/assets/index-Cpy4G3uJ.js +2 -0
- pythinker_code/web/static/assets/index-CzV_vCfu.css +1 -0
- pythinker_code/web/static/assets/index-DI2oedCt.js +19 -0
- pythinker_code/web/static/assets/index-DdIkp80K.js +5 -0
- pythinker_code/web/static/assets/infoDiagram-WHAUD3N6-BMPpeZSM.js +2 -0
- pythinker_code/web/static/assets/ini-BEwlwnbL.js +1 -0
- pythinker_code/web/static/assets/init-Gi6I4Gst.js +1 -0
- pythinker_code/web/static/assets/inter-cyrillic-ext-wght-normal-BOeWTOD4.woff2 +0 -0
- pythinker_code/web/static/assets/inter-cyrillic-wght-normal-DqGufNeO.woff2 +0 -0
- pythinker_code/web/static/assets/inter-greek-ext-wght-normal-DlzME5K_.woff2 +0 -0
- pythinker_code/web/static/assets/inter-greek-wght-normal-CkhJZR-_.woff2 +0 -0
- pythinker_code/web/static/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
- pythinker_code/web/static/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
- pythinker_code/web/static/assets/inter-vietnamese-wght-normal-CBcvBZtf.woff2 +0 -0
- pythinker_code/web/static/assets/java-CylS5w8V.js +1 -0
- pythinker_code/web/static/assets/javascript-wDzz0qaB.js +1 -0
- pythinker_code/web/static/assets/jinja-4LBKfQ-Z.js +1 -0
- pythinker_code/web/static/assets/jison-wvAkD_A8.js +1 -0
- pythinker_code/web/static/assets/journeyDiagram-XKPGCS4Q-DAM7gngo.js +139 -0
- pythinker_code/web/static/assets/json-Cp-IABpG.js +1 -0
- pythinker_code/web/static/assets/json5-C9tS-k6U.js +1 -0
- pythinker_code/web/static/assets/jsonc-Des-eS-w.js +1 -0
- pythinker_code/web/static/assets/jsonl-DcaNXYhu.js +1 -0
- pythinker_code/web/static/assets/jsonnet-DFQXde-d.js +1 -0
- pythinker_code/web/static/assets/jssm-C2t-YnRu.js +1 -0
- pythinker_code/web/static/assets/jsx-g9-lgVsj.js +1 -0
- pythinker_code/web/static/assets/julia-CxzCAyBv.js +1 -0
- pythinker_code/web/static/assets/kanagawa-dragon-CkXjmgJE.js +1 -0
- pythinker_code/web/static/assets/kanagawa-lotus-CfQXZHmo.js +1 -0
- pythinker_code/web/static/assets/kanagawa-wave-DWedfzmr.js +1 -0
- pythinker_code/web/static/assets/kanban-definition-3W4ZIXB7-ChpBpV0k.js +89 -0
- pythinker_code/web/static/assets/katex-D2lIc1rk.css +1 -0
- pythinker_code/web/static/assets/kdl-DV7GczEv.js +1 -0
- pythinker_code/web/static/assets/kotlin-BdnUsdx6.js +1 -0
- pythinker_code/web/static/assets/kusto-DZf3V79B.js +1 -0
- pythinker_code/web/static/assets/laserwave-DUszq2jm.js +1 -0
- pythinker_code/web/static/assets/latex-B4uzh10-.js +1 -0
- pythinker_code/web/static/assets/layout-C3Jp1gKO.js +1 -0
- pythinker_code/web/static/assets/lean-BZvkOJ9d.js +1 -0
- pythinker_code/web/static/assets/less-B1dDrJ26.js +1 -0
- pythinker_code/web/static/assets/light-plus-B7mTdjB0.js +1 -0
- pythinker_code/web/static/assets/linear-BGHtL1N4.js +1 -0
- pythinker_code/web/static/assets/liquid-DYVedYrR.js +1 -0
- pythinker_code/web/static/assets/llvm-BtvRca6l.js +1 -0
- pythinker_code/web/static/assets/log-2UxHyX5q.js +1 -0
- pythinker_code/web/static/assets/logo-BtOb2qkB.js +1 -0
- pythinker_code/web/static/assets/lua-BbnMAYS6.js +1 -0
- pythinker_code/web/static/assets/luau-C-HG3fhB.js +1 -0
- pythinker_code/web/static/assets/make-CHLpvVh8.js +1 -0
- pythinker_code/web/static/assets/markdown-Cvjx9yec.js +1 -0
- pythinker_code/web/static/assets/marko-DZsq8hO1.js +1 -0
- pythinker_code/web/static/assets/material-theme-D5KoaKCx.js +1 -0
- pythinker_code/web/static/assets/material-theme-darker-BfHTSMKl.js +1 -0
- pythinker_code/web/static/assets/material-theme-lighter-B0m2ddpp.js +1 -0
- pythinker_code/web/static/assets/material-theme-ocean-CyktbL80.js +1 -0
- pythinker_code/web/static/assets/material-theme-palenight-Csfq5Kiy.js +1 -0
- pythinker_code/web/static/assets/matlab-D7o27uSR.js +1 -0
- pythinker_code/web/static/assets/mdc-DUICxH0z.js +1 -0
- pythinker_code/web/static/assets/mdx-Cmh6b_Ma.js +1 -0
- pythinker_code/web/static/assets/mermaid-VLURNSYL-B2P5VJ9v.css +1 -0
- pythinker_code/web/static/assets/mermaid-VLURNSYL-C_HW6koB.js +465 -0
- pythinker_code/web/static/assets/mermaid-mWjccvbQ.js +1 -0
- pythinker_code/web/static/assets/mermaid.core-CnT4VrPC.js +191 -0
- pythinker_code/web/static/assets/min-Dn5VRVmX.js +1 -0
- pythinker_code/web/static/assets/min-dark-CafNBF8u.js +1 -0
- pythinker_code/web/static/assets/min-light-CTRr51gU.js +1 -0
- pythinker_code/web/static/assets/mindmap-definition-VGOIOE7T-x8EwhfIt.js +68 -0
- pythinker_code/web/static/assets/mipsasm-CKIfxQSi.js +1 -0
- pythinker_code/web/static/assets/mojo-B93PlW-d.js +1 -0
- pythinker_code/web/static/assets/monokai-D4h5O-jR.js +1 -0
- pythinker_code/web/static/assets/moonbit-Ba13S78F.js +1 -0
- pythinker_code/web/static/assets/move-Bu9oaDYs.js +1 -0
- pythinker_code/web/static/assets/narrat-DRg8JJMk.js +1 -0
- pythinker_code/web/static/assets/nextflow-BrzmwbiE.js +1 -0
- pythinker_code/web/static/assets/nginx-DknmC5AR.js +1 -0
- pythinker_code/web/static/assets/night-owl-C39BiMTA.js +1 -0
- pythinker_code/web/static/assets/nim-CVrawwO9.js +1 -0
- pythinker_code/web/static/assets/nix-CwoSXNpI.js +1 -0
- pythinker_code/web/static/assets/nord-Ddv68eIx.js +1 -0
- pythinker_code/web/static/assets/nushell-C-sUppwS.js +1 -0
- pythinker_code/web/static/assets/objective-c-DXmwc3jG.js +1 -0
- pythinker_code/web/static/assets/objective-cpp-CLxacb5B.js +1 -0
- pythinker_code/web/static/assets/ocaml-C0hk2d4L.js +1 -0
- pythinker_code/web/static/assets/one-dark-pro-DVMEJ2y_.js +1 -0
- pythinker_code/web/static/assets/one-light-PoHY5YXO.js +1 -0
- pythinker_code/web/static/assets/openscad-C4EeE6gA.js +1 -0
- pythinker_code/web/static/assets/ordinal-Cboi1Yqb.js +1 -0
- pythinker_code/web/static/assets/pascal-D93ZcfNL.js +1 -0
- pythinker_code/web/static/assets/perl-C0TMdlhV.js +1 -0
- pythinker_code/web/static/assets/php-CDn_0X-4.js +1 -0
- pythinker_code/web/static/assets/pieDiagram-ADFJNKIX-DgxBKGz2.js +30 -0
- pythinker_code/web/static/assets/pkl-u5AG7uiY.js +1 -0
- pythinker_code/web/static/assets/plastic-3e1v2bzS.js +1 -0
- pythinker_code/web/static/assets/plsql-ChMvpjG-.js +1 -0
- pythinker_code/web/static/assets/po-BTJTHyun.js +1 -0
- pythinker_code/web/static/assets/poimandres-CS3Unz2-.js +1 -0
- pythinker_code/web/static/assets/polar-C0HS_06l.js +1 -0
- pythinker_code/web/static/assets/postcss-CXtECtnM.js +1 -0
- pythinker_code/web/static/assets/powerquery-CEu0bR-o.js +1 -0
- pythinker_code/web/static/assets/powershell-Dpen1YoG.js +1 -0
- pythinker_code/web/static/assets/prisma-Dd19v3D-.js +1 -0
- pythinker_code/web/static/assets/prolog-CbFg5uaA.js +1 -0
- pythinker_code/web/static/assets/proto-C7zT0LnQ.js +1 -0
- pythinker_code/web/static/assets/pug-CGlum2m_.js +1 -0
- pythinker_code/web/static/assets/puppet-BMWR74SV.js +1 -0
- pythinker_code/web/static/assets/purescript-CklMAg4u.js +1 -0
- pythinker_code/web/static/assets/python-B6aJPvgy.js +1 -0
- pythinker_code/web/static/assets/qml-3beO22l8.js +1 -0
- pythinker_code/web/static/assets/qmldir-C8lEn-DE.js +1 -0
- pythinker_code/web/static/assets/qss-IeuSbFQv.js +1 -0
- pythinker_code/web/static/assets/quadrantDiagram-AYHSOK5B-DSpe_dqk.js +7 -0
- pythinker_code/web/static/assets/r-Dspwwk_N.js +1 -0
- pythinker_code/web/static/assets/racket-BqYA7rlc.js +1 -0
- pythinker_code/web/static/assets/raku-DXvB9xmW.js +1 -0
- pythinker_code/web/static/assets/razor-C1TweQQi.js +1 -0
- pythinker_code/web/static/assets/red-bN70gL4F.js +1 -0
- pythinker_code/web/static/assets/reg-C-SQnVFl.js +1 -0
- pythinker_code/web/static/assets/regexp-CDVJQ6XC.js +1 -0
- pythinker_code/web/static/assets/rel-C3B-1QV4.js +1 -0
- pythinker_code/web/static/assets/requirementDiagram-UZGBJVZJ-8o9hozL-.js +64 -0
- pythinker_code/web/static/assets/riscv-BM1_JUlF.js +1 -0
- pythinker_code/web/static/assets/rose-pine-dawn-DHQR4-dF.js +1 -0
- pythinker_code/web/static/assets/rose-pine-moon-D4_iv3hh.js +1 -0
- pythinker_code/web/static/assets/rose-pine-qdsjHGoJ.js +1 -0
- pythinker_code/web/static/assets/rosmsg-BJDFO7_C.js +1 -0
- pythinker_code/web/static/assets/rst-B0xPkSld.js +1 -0
- pythinker_code/web/static/assets/ruby-BvKwtOVI.js +1 -0
- pythinker_code/web/static/assets/rust-B1yitclQ.js +1 -0
- pythinker_code/web/static/assets/sankeyDiagram-TZEHDZUN-BLOSUixH.js +10 -0
- pythinker_code/web/static/assets/sas-cz2c8ADy.js +1 -0
- pythinker_code/web/static/assets/sass-Cj5Yp3dK.js +1 -0
- pythinker_code/web/static/assets/scala-C151Ov-r.js +1 -0
- pythinker_code/web/static/assets/scheme-C98Dy4si.js +1 -0
- pythinker_code/web/static/assets/scss-OYdSNvt2.js +1 -0
- pythinker_code/web/static/assets/sdbl-DVxCFoDh.js +1 -0
- pythinker_code/web/static/assets/sequenceDiagram-WL72ISMW-6F2G8JTU.js +145 -0
- pythinker_code/web/static/assets/shaderlab-Dg9Lc6iA.js +1 -0
- pythinker_code/web/static/assets/shellscript-Yzrsuije.js +1 -0
- pythinker_code/web/static/assets/shellsession-BADoaaVG.js +1 -0
- pythinker_code/web/static/assets/slack-dark-BthQWCQV.js +1 -0
- pythinker_code/web/static/assets/slack-ochin-DqwNpetd.js +1 -0
- pythinker_code/web/static/assets/smalltalk-BERRCDM3.js +1 -0
- pythinker_code/web/static/assets/snazzy-light-Bw305WKR.js +1 -0
- pythinker_code/web/static/assets/solarized-dark-DXbdFlpD.js +1 -0
- pythinker_code/web/static/assets/solarized-light-L9t79GZl.js +1 -0
- pythinker_code/web/static/assets/solidity-rGO070M0.js +1 -0
- pythinker_code/web/static/assets/soy-Brmx7dQM.js +1 -0
- pythinker_code/web/static/assets/sparql-rVzFXLq3.js +1 -0
- pythinker_code/web/static/assets/splunk-BtCnVYZw.js +1 -0
- pythinker_code/web/static/assets/sql-BLtJtn59.js +1 -0
- pythinker_code/web/static/assets/ssh-config-_ykCGR6B.js +1 -0
- pythinker_code/web/static/assets/stata-BH5u7GGu.js +1 -0
- pythinker_code/web/static/assets/stateDiagram-FKZM4ZOC-DP8xP0iJ.js +1 -0
- pythinker_code/web/static/assets/stateDiagram-v2-4FDKWEC3-1l6-EZNX.js +1 -0
- pythinker_code/web/static/assets/stylus-BEDo0Tqx.js +1 -0
- pythinker_code/web/static/assets/svelte-zxCyuUbr.js +1 -0
- pythinker_code/web/static/assets/swift-Dg5xB15N.js +1 -0
- pythinker_code/web/static/assets/synthwave-84-CbfX1IO0.js +1 -0
- pythinker_code/web/static/assets/system-verilog-CnnmHF94.js +1 -0
- pythinker_code/web/static/assets/systemd-4A_iFExJ.js +1 -0
- pythinker_code/web/static/assets/talonscript-CkByrt1z.js +1 -0
- pythinker_code/web/static/assets/tasl-QIJgUcNo.js +1 -0
- pythinker_code/web/static/assets/tcl-dwOrl1Do.js +1 -0
- pythinker_code/web/static/assets/templ-W15q3VgB.js +1 -0
- pythinker_code/web/static/assets/terraform-BETggiCN.js +1 -0
- pythinker_code/web/static/assets/tex-CvyZ59Mk.js +1 -0
- pythinker_code/web/static/assets/timeline-definition-IT6M3QCI-DMgruDfK.js +61 -0
- pythinker_code/web/static/assets/tokyo-night-hegEt444.js +1 -0
- pythinker_code/web/static/assets/toml-vGWfd6FD.js +1 -0
- pythinker_code/web/static/assets/treemap-KMMF4GRG-Bo9ZkrAK.js +128 -0
- pythinker_code/web/static/assets/ts-tags-zn1MmPIZ.js +1 -0
- pythinker_code/web/static/assets/tsv-B_m7g4N7.js +1 -0
- pythinker_code/web/static/assets/tsx-COt5Ahok.js +1 -0
- pythinker_code/web/static/assets/turtle-BsS91CYL.js +1 -0
- pythinker_code/web/static/assets/twig-CO9l9SDP.js +1 -0
- pythinker_code/web/static/assets/typescript-BPQ3VLAy.js +1 -0
- pythinker_code/web/static/assets/typespec-BGHnOYBU.js +1 -0
- pythinker_code/web/static/assets/typst-DHCkPAjA.js +1 -0
- pythinker_code/web/static/assets/v-BcVCzyr7.js +1 -0
- pythinker_code/web/static/assets/vala-CsfeWuGM.js +1 -0
- pythinker_code/web/static/assets/vb-D17OF-Vu.js +1 -0
- pythinker_code/web/static/assets/verilog-BQ8w6xss.js +1 -0
- pythinker_code/web/static/assets/vesper-DU1UobuO.js +1 -0
- pythinker_code/web/static/assets/vhdl-CeAyd5Ju.js +1 -0
- pythinker_code/web/static/assets/viml-CJc9bBzg.js +1 -0
- pythinker_code/web/static/assets/vitesse-black-Bkuqu6BP.js +1 -0
- pythinker_code/web/static/assets/vitesse-dark-D0r3Knsf.js +1 -0
- pythinker_code/web/static/assets/vitesse-light-CVO1_9PV.js +1 -0
- pythinker_code/web/static/assets/vue-DN_0RTcg.js +1 -0
- pythinker_code/web/static/assets/vue-html-AaS7Mt5G.js +1 -0
- pythinker_code/web/static/assets/vue-vine-CQOfvN7w.js +1 -0
- pythinker_code/web/static/assets/vyper-CDx5xZoG.js +1 -0
- pythinker_code/web/static/assets/wasm-CG6Dc4jp.js +1 -0
- pythinker_code/web/static/assets/wasm-MzD3tlZU.js +1 -0
- pythinker_code/web/static/assets/wenyan-BV7otONQ.js +1 -0
- pythinker_code/web/static/assets/wgsl-Dx-B1_4e.js +1 -0
- pythinker_code/web/static/assets/wikitext-BhOHFoWU.js +1 -0
- pythinker_code/web/static/assets/wit-5i3qLPDT.js +1 -0
- pythinker_code/web/static/assets/wolfram-lXgVvXCa.js +1 -0
- pythinker_code/web/static/assets/xml-sdJ4AIDG.js +1 -0
- pythinker_code/web/static/assets/xsl-CtQFsRM5.js +1 -0
- pythinker_code/web/static/assets/xychartDiagram-PRI3JC2R-GeF2johi.js +7 -0
- pythinker_code/web/static/assets/yaml-Buea-lGh.js +1 -0
- pythinker_code/web/static/assets/zenscript-DVFEvuxE.js +1 -0
- pythinker_code/web/static/assets/zig-VOosw3JB.js +1 -0
- pythinker_code/web/static/brand/apple-touch-icon.png +0 -0
- pythinker_code/web/static/brand/arctecture.webp +0 -0
- pythinker_code/web/static/brand/bimi-logo.svg +46 -0
- pythinker_code/web/static/brand/favicon.ico +0 -0
- pythinker_code/web/static/brand/fonts/dm-sans-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/dm-sans-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-sans-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-sans-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-serif-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/instrument-serif-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-italic-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-italic-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/libre-baskerville-latin.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/roboto-latin-ext.woff2 +0 -0
- pythinker_code/web/static/brand/fonts/roboto-latin.woff2 +0 -0
- pythinker_code/web/static/brand/icon-192.png +0 -0
- pythinker_code/web/static/brand/icon-512.png +0 -0
- pythinker_code/web/static/brand/icon.svg +1 -0
- pythinker_code/web/static/brand/logo.png +0 -0
- pythinker_code/web/static/brand/pythinker_animated.svg +79 -0
- pythinker_code/web/static/brand/robots.txt +4 -0
- pythinker_code/web/static/index.html +15 -0
- pythinker_code/web/static/logo.png +0 -0
- pythinker_code/web/store/__init__.py +1 -0
- pythinker_code/web/store/sessions.py +432 -0
- pythinker_code/wire/__init__.py +148 -0
- pythinker_code/wire/file.py +151 -0
- pythinker_code/wire/jsonrpc.py +263 -0
- pythinker_code/wire/protocol.py +2 -0
- pythinker_code/wire/root_hub.py +27 -0
- pythinker_code/wire/serde.py +26 -0
- pythinker_code/wire/server.py +1069 -0
- pythinker_code/wire/types.py +698 -0
- pythinker_code-2.0.0.dist-info/METADATA +660 -0
- pythinker_code-2.0.0.dist-info/RECORD +731 -0
- pythinker_code-2.0.0.dist-info/WHEEL +4 -0
- pythinker_code-2.0.0.dist-info/entry_points.txt +4 -0
- pythinker_code-2.0.0.dist-info/licenses/LICENSE +202 -0
- pythinker_code-2.0.0.dist-info/licenses/NOTICE +14 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0 (2026-05-06)
|
|
4
|
+
|
|
5
|
+
Initial release of Pythinker CLI.
|
|
6
|
+
|
|
7
|
+
- Interactive terminal AI coding agent with shell command mode (Ctrl-X)
|
|
8
|
+
- ACP server (`pythinker acp`) for IDE integrations such as Zed and JetBrains
|
|
9
|
+
- MCP tool support: `pythinker mcp` sub-commands and ad-hoc `--mcp-config-file`
|
|
10
|
+
- Multi-provider auth: OpenAI, OpenAI Codex, Anthropic, OpenRouter, DeepSeek, MiniMax, Opencode-Go
|
|
11
|
+
- Skills, plugins, and slash commands across project / user / built-in scopes
|
|
12
|
+
- Session persistence with subagents and approval workflows
|
|
13
|
+
- Print mode and wire mode for non-interactive and programmatic use
|
|
14
|
+
- Plan mode for read-only research and design before code changes
|
|
15
|
+
- YOLO mode (`--yolo` / `/yolo`) to dangerously skip permission approvals while keeping the user reachable via `AskUserQuestion`
|
|
16
|
+
- Auto mode (`--auto` / `/auto`) for unattended runs: auto-approves tool calls and auto-dismisses `AskUserQuestion` so the agent finishes end-to-end without a user
|
|
File without changes
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from collections.abc import Sequence
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
ROOT_HELP = """Usage: pythinker [OPTIONS] COMMAND [ARGS]...
|
|
8
|
+
|
|
9
|
+
Pythinker, your next CLI agent.
|
|
10
|
+
|
|
11
|
+
Options:
|
|
12
|
+
-h, --help Show this message and exit.
|
|
13
|
+
-V, --version Show version and exit.
|
|
14
|
+
--verbose Print verbose information.
|
|
15
|
+
--debug Log debug information.
|
|
16
|
+
-w, --work-dir DIRECTORY Working directory for the agent.
|
|
17
|
+
--add-dir DIRECTORY Add an additional workspace directory.
|
|
18
|
+
-S, -r, --session, --resume TEXT Resume a session.
|
|
19
|
+
-C, --continue Continue the previous session.
|
|
20
|
+
--config TEXT Config TOML/JSON string to load.
|
|
21
|
+
--config-file FILE Config TOML/JSON file to load.
|
|
22
|
+
-m, --model TEXT LLM model to use.
|
|
23
|
+
--thinking / --no-thinking Enable or disable thinking mode.
|
|
24
|
+
-y, --yolo, --yes, --auto-approve
|
|
25
|
+
Dangerously skip permission approvals.
|
|
26
|
+
--plan Start in plan mode.
|
|
27
|
+
--auto Run in auto mode (no user present).
|
|
28
|
+
-p, -c, --prompt, --command TEXT User prompt to the agent.
|
|
29
|
+
--print Run in print mode.
|
|
30
|
+
--acp Deprecated; use `pythinker acp`.
|
|
31
|
+
--wire Run as Wire server.
|
|
32
|
+
--quiet Print only the final assistant message.
|
|
33
|
+
--agent [default|okabe] Builtin agent specification to use.
|
|
34
|
+
--agent-file FILE Custom agent specification file.
|
|
35
|
+
--mcp-config-file FILE MCP config file to load; repeatable.
|
|
36
|
+
--mcp-config TEXT MCP config JSON to load; repeatable.
|
|
37
|
+
--skills-dir DIRECTORY Custom skills directory; repeatable.
|
|
38
|
+
--no-telemetry Disable anonymous telemetry & error reporting.
|
|
39
|
+
|
|
40
|
+
Commands:
|
|
41
|
+
acp Run Pythinker CLI ACP server.
|
|
42
|
+
term Run Toad TUI backed by Pythinker CLI ACP server.
|
|
43
|
+
login Login with a model provider.
|
|
44
|
+
logout Logout from a model provider.
|
|
45
|
+
info Show version and protocol information.
|
|
46
|
+
export Export session data.
|
|
47
|
+
mcp Manage MCP server configurations.
|
|
48
|
+
plugin Manage plugins.
|
|
49
|
+
vis Run Pythinker Agent Tracing Visualizer.
|
|
50
|
+
web Run Pythinker CLI web interface.
|
|
51
|
+
|
|
52
|
+
Documentation: https://mohamed-elkholy95.github.io/Pythinker-Code/
|
|
53
|
+
LLM friendly version: https://mohamed-elkholy95.github.io/Pythinker-Code/llms.txt
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _prog_name() -> str:
|
|
58
|
+
return Path(sys.argv[0]).name or "pythinker"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def main(argv: Sequence[str] | None = None) -> int | str | None:
|
|
62
|
+
args = list(sys.argv[1:] if argv is None else argv)
|
|
63
|
+
|
|
64
|
+
if len(args) == 1 and args[0] in {"--version", "-V"}:
|
|
65
|
+
from pythinker_code.constant import get_version
|
|
66
|
+
|
|
67
|
+
print(f"pythinker, version {get_version()}")
|
|
68
|
+
return 0
|
|
69
|
+
|
|
70
|
+
if len(args) == 1 and args[0] in {"--help", "-h"}:
|
|
71
|
+
print(ROOT_HELP, end="")
|
|
72
|
+
return 0
|
|
73
|
+
|
|
74
|
+
from pythinker_code.telemetry.crash import install_crash_handlers, set_phase
|
|
75
|
+
from pythinker_code.utils.proxy import normalize_proxy_env
|
|
76
|
+
|
|
77
|
+
# Install excepthook before anything else so startup-phase crashes are captured.
|
|
78
|
+
install_crash_handlers()
|
|
79
|
+
normalize_proxy_env()
|
|
80
|
+
|
|
81
|
+
from pythinker_code.cli import cli
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
return cli(args=args, prog_name=_prog_name())
|
|
85
|
+
except SystemExit as exc:
|
|
86
|
+
return exc.code
|
|
87
|
+
finally:
|
|
88
|
+
set_phase("shutdown")
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
if __name__ == "__main__":
|
|
92
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# ACP Integration Notes (pythinker-code)
|
|
2
|
+
|
|
3
|
+
## Protocol summary (ACP overview)
|
|
4
|
+
- ACP is JSON-RPC 2.0 with request/response methods plus one-way notifications.
|
|
5
|
+
- Typical flow: `initialize` -> optional `authenticate` -> `session/new` or `session/load`
|
|
6
|
+
-> `session/prompt`
|
|
7
|
+
with `session/update` notifications and optional `session/cancel`.
|
|
8
|
+
- Clients provide `session/request_permission` and optional terminal/filesystem methods.
|
|
9
|
+
- All ACP file paths must be absolute; line numbers are 1-based.
|
|
10
|
+
|
|
11
|
+
## Entry points and server modes
|
|
12
|
+
- **Deprecated single-session server**: `PythinkerCLI.run_acp()` uses `ACP` -> `ACPServerSingleSession`.
|
|
13
|
+
- Code: `src/pythinker_code/app.py`, `src/pythinker_code/ui/acp/__init__.py`.
|
|
14
|
+
- Used when running CLI with `--acp` UI mode; every ACP method now raises a deprecation error.
|
|
15
|
+
- **Multi-session server**: `acp_main()` runs `ACPServer` with `use_unstable_protocol=True`.
|
|
16
|
+
- Code: `src/pythinker_code/acp/__init__.py`, `src/pythinker_code/acp/server.py`.
|
|
17
|
+
- Exposed via the `pythinker acp` command in `src/pythinker_code/cli/__init__.py`.
|
|
18
|
+
|
|
19
|
+
## Capabilities advertised
|
|
20
|
+
- `prompt_capabilities`: `embedded_context=True`, `image=True`, `audio=False`.
|
|
21
|
+
- `mcp_capabilities`: `http=True`, `sse=False`.
|
|
22
|
+
- Multi-session: `load_session=True`, `session_capabilities.list` and `session_capabilities.resume` supported.
|
|
23
|
+
- `auth_methods` advertises terminal auth for `pythinker login`.
|
|
24
|
+
|
|
25
|
+
## Session lifecycle (implemented behavior)
|
|
26
|
+
- `session/new`
|
|
27
|
+
- Multi-session: creates a persisted `Session`, builds `PythinkerCLI`, stores `ACPSession`.
|
|
28
|
+
- Sends `AvailableCommandsUpdate` for slash commands on session creation.
|
|
29
|
+
- MCP servers passed by ACP are converted via `acp_mcp_servers_to_mcp_config`.
|
|
30
|
+
- `session/load`
|
|
31
|
+
- Multi-session only: loads by `Session.find`, then builds `PythinkerCLI` and `ACPSession`.
|
|
32
|
+
- No history replay yet (TODO).
|
|
33
|
+
- `session/list`
|
|
34
|
+
- Multi-session only: lists sessions via `Session.list`, no pagination.
|
|
35
|
+
- `session/resume`
|
|
36
|
+
- Multi-session only: loads the session if needed and returns current mode/model state.
|
|
37
|
+
- `session/set_mode`
|
|
38
|
+
- Multi-session only: accepts only `default`.
|
|
39
|
+
- `session/set_model`
|
|
40
|
+
- Multi-session only: switches the session LLM and persists default model/thinking settings.
|
|
41
|
+
- `session/prompt`
|
|
42
|
+
- Uses `ACPSession.prompt()` to stream updates and produce a `stop_reason`.
|
|
43
|
+
- Stop reasons: `end_turn`, `max_turn_requests`, `cancelled`.
|
|
44
|
+
- `session/cancel`
|
|
45
|
+
- Sets the per-turn cancel event to stop the prompt.
|
|
46
|
+
|
|
47
|
+
## Streaming updates and content mapping
|
|
48
|
+
- Text chunks -> `AgentMessageChunk`.
|
|
49
|
+
- Think chunks -> `AgentThoughtChunk`.
|
|
50
|
+
- Tool calls:
|
|
51
|
+
- Start -> `ToolCallStart` with JSON args as text content.
|
|
52
|
+
- Streaming args -> `ToolCallProgress` with updated title/args.
|
|
53
|
+
- Results -> `ToolCallProgress` with `completed` or `failed`.
|
|
54
|
+
- Tool call IDs are prefixed with turn ID to avoid collisions across turns.
|
|
55
|
+
- Plan updates:
|
|
56
|
+
- `TodoDisplayBlock` is converted into `AgentPlanUpdate`.
|
|
57
|
+
- Available commands:
|
|
58
|
+
- `AvailableCommandsUpdate` is sent right after session creation.
|
|
59
|
+
|
|
60
|
+
## Prompt/content conversion
|
|
61
|
+
- Incoming prompt blocks:
|
|
62
|
+
- Supported: `TextContentBlock`, `ImageContentBlock` (converted to data URL).
|
|
63
|
+
- Unsupported types are logged and ignored.
|
|
64
|
+
- Tool result display blocks:
|
|
65
|
+
- `DiffDisplayBlock` -> `FileEditToolCallContent`.
|
|
66
|
+
- `HideOutputDisplayBlock` suppresses tool output in ACP (used by terminal tool).
|
|
67
|
+
|
|
68
|
+
## Tool integration and permission flow
|
|
69
|
+
- ACP sessions use `ACPHost` to route filesystem reads/writes through ACP clients.
|
|
70
|
+
- If the client advertises `terminal` capability, the `Shell` tool is replaced by an
|
|
71
|
+
ACP-backed `Terminal` tool.
|
|
72
|
+
- Uses ACP `terminal/create`, waits for exit, streams `TerminalToolCallContent`,
|
|
73
|
+
then releases the terminal handle.
|
|
74
|
+
- Approval requests in the core tool system are bridged to ACP
|
|
75
|
+
`session/request_permission` with allow-once/allow-always/reject options.
|
|
76
|
+
|
|
77
|
+
## Current gaps / not implemented
|
|
78
|
+
- `fork_session` is not implemented.
|
|
79
|
+
- `ext_method` / `ext_notification` for custom ACP extensions are stubbed.
|
|
80
|
+
- Deprecated single-session `--acp` rejects all methods and exists only to report migration guidance.
|
|
81
|
+
- `session/load` still has no history replay.
|
|
82
|
+
|
|
83
|
+
## Filesystem (ACP client-backed)
|
|
84
|
+
- When the client advertises `fs.readTextFile` / `fs.writeTextFile`, `ACPHost` routes
|
|
85
|
+
reads and writes through ACP `fs/*` methods.
|
|
86
|
+
- `ReadFile` uses `HostPath.read_lines`, which `ACPHost` implements via ACP reads.
|
|
87
|
+
- `ReadMediaFile` uses `HostPath.read_bytes` to load image/video payloads through ACP reads.
|
|
88
|
+
- `WriteFile` uses `HostPath.read_text/write_text/append_text` and still generates diffs
|
|
89
|
+
and approvals in the tool layer.
|
|
90
|
+
|
|
91
|
+
## Zed-specific notes (as of current integration)
|
|
92
|
+
- Terminal auth advertises `pythinker login`; `authenticate` verifies that login completed.
|
|
93
|
+
- External agent clients should use `pythinker acp`, not deprecated `pythinker --acp`.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
def acp_main() -> None:
|
|
2
|
+
"""Entry point for the multi-session ACP server."""
|
|
3
|
+
import asyncio
|
|
4
|
+
|
|
5
|
+
import acp
|
|
6
|
+
|
|
7
|
+
from pythinker_code.acp.server import ACPServer
|
|
8
|
+
from pythinker_code.app import enable_logging
|
|
9
|
+
from pythinker_code.utils.logging import logger
|
|
10
|
+
|
|
11
|
+
enable_logging()
|
|
12
|
+
logger.info("Starting ACP server on stdio")
|
|
13
|
+
asyncio.run(acp.run_agent(ACPServer(), use_unstable_protocol=True))
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import acp
|
|
4
|
+
|
|
5
|
+
from pythinker_code.acp.types import ACPContentBlock
|
|
6
|
+
from pythinker_code.utils.logging import logger
|
|
7
|
+
from pythinker_code.wire.types import (
|
|
8
|
+
ContentPart,
|
|
9
|
+
DiffDisplayBlock,
|
|
10
|
+
DisplayBlock,
|
|
11
|
+
ImageURLPart,
|
|
12
|
+
TextPart,
|
|
13
|
+
ToolReturnValue,
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def acp_blocks_to_content_parts(prompt: list[ACPContentBlock]) -> list[ContentPart]:
|
|
18
|
+
content: list[ContentPart] = []
|
|
19
|
+
for block in prompt:
|
|
20
|
+
match block:
|
|
21
|
+
case acp.schema.TextContentBlock():
|
|
22
|
+
content.append(TextPart(text=block.text))
|
|
23
|
+
case acp.schema.ImageContentBlock():
|
|
24
|
+
content.append(
|
|
25
|
+
ImageURLPart(
|
|
26
|
+
image_url=ImageURLPart.ImageURL(
|
|
27
|
+
url=f"data:{block.mime_type};base64,{block.data}"
|
|
28
|
+
)
|
|
29
|
+
)
|
|
30
|
+
)
|
|
31
|
+
case acp.schema.EmbeddedResourceContentBlock():
|
|
32
|
+
resource = block.resource
|
|
33
|
+
if isinstance(resource, acp.schema.TextResourceContents):
|
|
34
|
+
uri = resource.uri
|
|
35
|
+
text = resource.text
|
|
36
|
+
content.append(TextPart(text=f"<resource uri={uri!r}>\n{text}\n</resource>"))
|
|
37
|
+
else:
|
|
38
|
+
logger.warning(
|
|
39
|
+
"Unsupported embedded resource type: {type}",
|
|
40
|
+
type=type(resource).__name__,
|
|
41
|
+
)
|
|
42
|
+
case acp.schema.ResourceContentBlock():
|
|
43
|
+
# ResourceContentBlock is a link reference without inline content;
|
|
44
|
+
# include the URI so the model is at least aware of the reference.
|
|
45
|
+
content.append(
|
|
46
|
+
TextPart(text=f"<resource_link uri={block.uri!r} name={block.name!r} />")
|
|
47
|
+
)
|
|
48
|
+
case _:
|
|
49
|
+
logger.warning("Unsupported prompt content block: {block}", block=block)
|
|
50
|
+
return content
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def display_block_to_acp_content(
|
|
54
|
+
block: DisplayBlock,
|
|
55
|
+
) -> acp.schema.FileEditToolCallContent | None:
|
|
56
|
+
if isinstance(block, DiffDisplayBlock):
|
|
57
|
+
return acp.schema.FileEditToolCallContent(
|
|
58
|
+
type="diff",
|
|
59
|
+
path=block.path,
|
|
60
|
+
old_text=block.old_text,
|
|
61
|
+
new_text=block.new_text,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
return None
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def tool_result_to_acp_content(
|
|
68
|
+
tool_ret: ToolReturnValue,
|
|
69
|
+
) -> list[
|
|
70
|
+
acp.schema.ContentToolCallContent
|
|
71
|
+
| acp.schema.FileEditToolCallContent
|
|
72
|
+
| acp.schema.TerminalToolCallContent
|
|
73
|
+
]:
|
|
74
|
+
from pythinker_code.acp.tools import HideOutputDisplayBlock
|
|
75
|
+
|
|
76
|
+
def _to_acp_content(
|
|
77
|
+
part: ContentPart,
|
|
78
|
+
) -> (
|
|
79
|
+
acp.schema.ContentToolCallContent
|
|
80
|
+
| acp.schema.FileEditToolCallContent
|
|
81
|
+
| acp.schema.TerminalToolCallContent
|
|
82
|
+
):
|
|
83
|
+
if isinstance(part, TextPart):
|
|
84
|
+
return acp.schema.ContentToolCallContent(
|
|
85
|
+
type="content", content=acp.schema.TextContentBlock(type="text", text=part.text)
|
|
86
|
+
)
|
|
87
|
+
logger.warning("Unsupported content part in tool result: {part}", part=part)
|
|
88
|
+
return acp.schema.ContentToolCallContent(
|
|
89
|
+
type="content",
|
|
90
|
+
content=acp.schema.TextContentBlock(type="text", text=f"[{part.__class__.__name__}]"),
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
def _to_text_block(text: str) -> acp.schema.ContentToolCallContent:
|
|
94
|
+
return acp.schema.ContentToolCallContent(
|
|
95
|
+
type="content", content=acp.schema.TextContentBlock(type="text", text=text)
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
contents: list[
|
|
99
|
+
acp.schema.ContentToolCallContent
|
|
100
|
+
| acp.schema.FileEditToolCallContent
|
|
101
|
+
| acp.schema.TerminalToolCallContent
|
|
102
|
+
] = []
|
|
103
|
+
|
|
104
|
+
for block in tool_ret.display:
|
|
105
|
+
if isinstance(block, HideOutputDisplayBlock):
|
|
106
|
+
# return early to indicate no output should be shown
|
|
107
|
+
return []
|
|
108
|
+
|
|
109
|
+
content = display_block_to_acp_content(block)
|
|
110
|
+
if content is not None:
|
|
111
|
+
contents.append(content)
|
|
112
|
+
# TODO: better concatenation of `display` blocks and `output`?
|
|
113
|
+
|
|
114
|
+
output = tool_ret.output
|
|
115
|
+
if isinstance(output, str):
|
|
116
|
+
if output:
|
|
117
|
+
contents.append(_to_text_block(output))
|
|
118
|
+
else:
|
|
119
|
+
# NOTE: At the moment, ToolReturnValue.output is either a string or a
|
|
120
|
+
# list of ContentPart. We avoid an unnecessary isinstance() check here
|
|
121
|
+
# to keep pyright happy while still handling list outputs.
|
|
122
|
+
contents.extend(_to_acp_content(part) for part in output)
|
|
123
|
+
|
|
124
|
+
if not contents and tool_ret.message:
|
|
125
|
+
# Fallback to the `message` for LLM if there's no other content
|
|
126
|
+
contents.append(_to_text_block(tool_ret.message))
|
|
127
|
+
|
|
128
|
+
return contents
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from collections.abc import AsyncGenerator, Iterable, Mapping
|
|
5
|
+
from contextlib import suppress
|
|
6
|
+
from typing import Literal
|
|
7
|
+
|
|
8
|
+
import acp
|
|
9
|
+
from pythinker_host import (
|
|
10
|
+
AsyncReadable,
|
|
11
|
+
AsyncWritable,
|
|
12
|
+
Host,
|
|
13
|
+
HostProcess,
|
|
14
|
+
StatResult,
|
|
15
|
+
StrOrHostPath,
|
|
16
|
+
)
|
|
17
|
+
from pythinker_host.local import local_host
|
|
18
|
+
from pythinker_host.path import HostPath
|
|
19
|
+
|
|
20
|
+
_DEFAULT_TERMINAL_OUTPUT_LIMIT = 50_000
|
|
21
|
+
_DEFAULT_POLL_INTERVAL = 0.2
|
|
22
|
+
_TRUNCATION_NOTICE = "[acp output truncated]\n"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _NullWritable:
|
|
26
|
+
def can_write_eof(self) -> bool:
|
|
27
|
+
return False
|
|
28
|
+
|
|
29
|
+
def close(self) -> None:
|
|
30
|
+
return None
|
|
31
|
+
|
|
32
|
+
async def drain(self) -> None:
|
|
33
|
+
return None
|
|
34
|
+
|
|
35
|
+
def is_closing(self) -> bool:
|
|
36
|
+
return False
|
|
37
|
+
|
|
38
|
+
async def wait_closed(self) -> None:
|
|
39
|
+
return None
|
|
40
|
+
|
|
41
|
+
def write(self, data: bytes) -> None:
|
|
42
|
+
return None
|
|
43
|
+
|
|
44
|
+
def writelines(self, data: Iterable[bytes], /) -> None:
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
def write_eof(self) -> None:
|
|
48
|
+
return None
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ACPProcess:
|
|
52
|
+
"""Host process adapter for ACP terminal execution."""
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
client: acp.Client,
|
|
57
|
+
session_id: str,
|
|
58
|
+
terminal_id: str,
|
|
59
|
+
*,
|
|
60
|
+
poll_interval: float = _DEFAULT_POLL_INTERVAL,
|
|
61
|
+
) -> None:
|
|
62
|
+
self._client = client
|
|
63
|
+
self._session_id = session_id
|
|
64
|
+
self._terminal_id = terminal_id
|
|
65
|
+
self._poll_interval = poll_interval
|
|
66
|
+
self._stdin = _NullWritable()
|
|
67
|
+
self._stdout = asyncio.StreamReader()
|
|
68
|
+
self._stderr = asyncio.StreamReader()
|
|
69
|
+
self.stdin: AsyncWritable = self._stdin
|
|
70
|
+
self.stdout: AsyncReadable = self._stdout
|
|
71
|
+
# ACP does not expose stderr separately; keep stderr empty.
|
|
72
|
+
self.stderr: AsyncReadable = self._stderr
|
|
73
|
+
self._returncode: int | None = None
|
|
74
|
+
self._last_output = ""
|
|
75
|
+
self._truncation_noted = False
|
|
76
|
+
self._exit_future: asyncio.Future[int] = asyncio.get_running_loop().create_future()
|
|
77
|
+
self._poll_task = asyncio.create_task(self._poll_output())
|
|
78
|
+
|
|
79
|
+
@property
|
|
80
|
+
def pid(self) -> int:
|
|
81
|
+
return -1
|
|
82
|
+
|
|
83
|
+
@property
|
|
84
|
+
def returncode(self) -> int | None:
|
|
85
|
+
return self._returncode
|
|
86
|
+
|
|
87
|
+
async def wait(self) -> int:
|
|
88
|
+
return await self._exit_future
|
|
89
|
+
|
|
90
|
+
async def kill(self) -> None:
|
|
91
|
+
await self._client.kill_terminal(
|
|
92
|
+
session_id=self._session_id,
|
|
93
|
+
terminal_id=self._terminal_id,
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
def _feed_output(self, output_response: acp.schema.TerminalOutputResponse) -> None:
|
|
97
|
+
output = output_response.output
|
|
98
|
+
reset = output_response.truncated or (
|
|
99
|
+
self._last_output and not output.startswith(self._last_output)
|
|
100
|
+
)
|
|
101
|
+
if reset and self._last_output and not self._truncation_noted:
|
|
102
|
+
self._stdout.feed_data(_TRUNCATION_NOTICE.encode("utf-8"))
|
|
103
|
+
self._truncation_noted = True
|
|
104
|
+
|
|
105
|
+
delta = output if reset else output[len(self._last_output) :]
|
|
106
|
+
if delta:
|
|
107
|
+
self._stdout.feed_data(delta.encode("utf-8", "replace"))
|
|
108
|
+
self._last_output = output
|
|
109
|
+
|
|
110
|
+
@staticmethod
|
|
111
|
+
def _normalize_exit_code(exit_code: int | None) -> int:
|
|
112
|
+
return 1 if exit_code is None else exit_code
|
|
113
|
+
|
|
114
|
+
async def _poll_output(self) -> None:
|
|
115
|
+
exit_task = asyncio.create_task(
|
|
116
|
+
self._client.wait_for_terminal_exit(
|
|
117
|
+
session_id=self._session_id,
|
|
118
|
+
terminal_id=self._terminal_id,
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
exit_code: int | None = None
|
|
122
|
+
try:
|
|
123
|
+
while True:
|
|
124
|
+
if exit_task.done():
|
|
125
|
+
exit_response = exit_task.result()
|
|
126
|
+
exit_code = exit_response.exit_code
|
|
127
|
+
break
|
|
128
|
+
|
|
129
|
+
output_response = await self._client.terminal_output(
|
|
130
|
+
session_id=self._session_id,
|
|
131
|
+
terminal_id=self._terminal_id,
|
|
132
|
+
)
|
|
133
|
+
self._feed_output(output_response)
|
|
134
|
+
if output_response.exit_status:
|
|
135
|
+
exit_code = output_response.exit_status.exit_code
|
|
136
|
+
try:
|
|
137
|
+
exit_response = await exit_task
|
|
138
|
+
exit_code = exit_response.exit_code or exit_code
|
|
139
|
+
except Exception:
|
|
140
|
+
pass
|
|
141
|
+
break
|
|
142
|
+
|
|
143
|
+
await asyncio.sleep(self._poll_interval)
|
|
144
|
+
|
|
145
|
+
final_output = await self._client.terminal_output(
|
|
146
|
+
session_id=self._session_id,
|
|
147
|
+
terminal_id=self._terminal_id,
|
|
148
|
+
)
|
|
149
|
+
self._feed_output(final_output)
|
|
150
|
+
except Exception as exc:
|
|
151
|
+
error_note = f"[acp terminal error] {exc}\n"
|
|
152
|
+
self._stdout.feed_data(error_note.encode("utf-8", "replace"))
|
|
153
|
+
if exit_code is None:
|
|
154
|
+
exit_code = 1
|
|
155
|
+
finally:
|
|
156
|
+
if not exit_task.done():
|
|
157
|
+
exit_task.cancel()
|
|
158
|
+
with suppress(Exception):
|
|
159
|
+
await exit_task
|
|
160
|
+
self._returncode = self._normalize_exit_code(exit_code)
|
|
161
|
+
self._stdout.feed_eof()
|
|
162
|
+
self._stderr.feed_eof()
|
|
163
|
+
if not self._exit_future.done():
|
|
164
|
+
self._exit_future.set_result(self._returncode)
|
|
165
|
+
with suppress(Exception):
|
|
166
|
+
await self._client.release_terminal(
|
|
167
|
+
session_id=self._session_id,
|
|
168
|
+
terminal_id=self._terminal_id,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class ACPHost:
|
|
173
|
+
"""Host backend that routes supported operations through ACP."""
|
|
174
|
+
|
|
175
|
+
name: str = "acp"
|
|
176
|
+
|
|
177
|
+
def __init__(
|
|
178
|
+
self,
|
|
179
|
+
client: acp.Client,
|
|
180
|
+
session_id: str,
|
|
181
|
+
client_capabilities: acp.schema.ClientCapabilities | None,
|
|
182
|
+
fallback: Host | None = None,
|
|
183
|
+
*,
|
|
184
|
+
output_byte_limit: int | None = _DEFAULT_TERMINAL_OUTPUT_LIMIT,
|
|
185
|
+
poll_interval: float = _DEFAULT_POLL_INTERVAL,
|
|
186
|
+
) -> None:
|
|
187
|
+
self._client = client
|
|
188
|
+
self._session_id = session_id
|
|
189
|
+
self._fallback = fallback or local_host
|
|
190
|
+
fs = client_capabilities.fs if client_capabilities else None
|
|
191
|
+
self._supports_read = bool(fs and fs.read_text_file)
|
|
192
|
+
self._supports_write = bool(fs and fs.write_text_file)
|
|
193
|
+
self._supports_terminal = bool(client_capabilities and client_capabilities.terminal)
|
|
194
|
+
self._output_byte_limit = output_byte_limit
|
|
195
|
+
self._poll_interval = poll_interval
|
|
196
|
+
|
|
197
|
+
def pathclass(self):
|
|
198
|
+
return self._fallback.pathclass()
|
|
199
|
+
|
|
200
|
+
def normpath(self, path: StrOrHostPath) -> HostPath:
|
|
201
|
+
return self._fallback.normpath(path)
|
|
202
|
+
|
|
203
|
+
def gethome(self) -> HostPath:
|
|
204
|
+
return self._fallback.gethome()
|
|
205
|
+
|
|
206
|
+
def getcwd(self) -> HostPath:
|
|
207
|
+
return self._fallback.getcwd()
|
|
208
|
+
|
|
209
|
+
async def chdir(self, path: StrOrHostPath) -> None:
|
|
210
|
+
await self._fallback.chdir(path)
|
|
211
|
+
|
|
212
|
+
async def stat(self, path: StrOrHostPath, *, follow_symlinks: bool = True) -> StatResult:
|
|
213
|
+
return await self._fallback.stat(path, follow_symlinks=follow_symlinks)
|
|
214
|
+
|
|
215
|
+
def iterdir(self, path: StrOrHostPath) -> AsyncGenerator[HostPath]:
|
|
216
|
+
return self._fallback.iterdir(path)
|
|
217
|
+
|
|
218
|
+
def glob(
|
|
219
|
+
self, path: StrOrHostPath, pattern: str, *, case_sensitive: bool = True
|
|
220
|
+
) -> AsyncGenerator[HostPath]:
|
|
221
|
+
return self._fallback.glob(path, pattern, case_sensitive=case_sensitive)
|
|
222
|
+
|
|
223
|
+
async def readbytes(self, path: StrOrHostPath, n: int | None = None) -> bytes:
|
|
224
|
+
return await self._fallback.readbytes(path, n=n)
|
|
225
|
+
|
|
226
|
+
async def readtext(
|
|
227
|
+
self,
|
|
228
|
+
path: StrOrHostPath,
|
|
229
|
+
*,
|
|
230
|
+
encoding: str = "utf-8",
|
|
231
|
+
errors: Literal["strict", "ignore", "replace"] = "strict",
|
|
232
|
+
) -> str:
|
|
233
|
+
abs_path = self._abs_path(path)
|
|
234
|
+
if not self._supports_read:
|
|
235
|
+
return await self._fallback.readtext(abs_path, encoding=encoding, errors=errors)
|
|
236
|
+
response = await self._client.read_text_file(path=abs_path, session_id=self._session_id)
|
|
237
|
+
return response.content
|
|
238
|
+
|
|
239
|
+
async def readlines(
|
|
240
|
+
self,
|
|
241
|
+
path: StrOrHostPath,
|
|
242
|
+
*,
|
|
243
|
+
encoding: str = "utf-8",
|
|
244
|
+
errors: Literal["strict", "ignore", "replace"] = "strict",
|
|
245
|
+
) -> AsyncGenerator[str]:
|
|
246
|
+
text = await self.readtext(path, encoding=encoding, errors=errors)
|
|
247
|
+
for line in text.splitlines(keepends=True):
|
|
248
|
+
yield line
|
|
249
|
+
|
|
250
|
+
async def writebytes(self, path: StrOrHostPath, data: bytes) -> int:
|
|
251
|
+
return await self._fallback.writebytes(path, data)
|
|
252
|
+
|
|
253
|
+
async def writetext(
|
|
254
|
+
self,
|
|
255
|
+
path: StrOrHostPath,
|
|
256
|
+
data: str,
|
|
257
|
+
*,
|
|
258
|
+
mode: Literal["w", "a"] = "w",
|
|
259
|
+
encoding: str = "utf-8",
|
|
260
|
+
errors: Literal["strict", "ignore", "replace"] = "strict",
|
|
261
|
+
) -> int:
|
|
262
|
+
abs_path = self._abs_path(path)
|
|
263
|
+
if mode == "a":
|
|
264
|
+
if self._supports_read and self._supports_write:
|
|
265
|
+
existing = await self.readtext(abs_path, encoding=encoding, errors=errors)
|
|
266
|
+
await self._client.write_text_file(
|
|
267
|
+
path=abs_path,
|
|
268
|
+
content=existing + data,
|
|
269
|
+
session_id=self._session_id,
|
|
270
|
+
)
|
|
271
|
+
return len(data)
|
|
272
|
+
return await self._fallback.writetext(
|
|
273
|
+
abs_path, data, mode="a", encoding=encoding, errors=errors
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
if not self._supports_write:
|
|
277
|
+
return await self._fallback.writetext(
|
|
278
|
+
abs_path, data, mode=mode, encoding=encoding, errors=errors
|
|
279
|
+
)
|
|
280
|
+
|
|
281
|
+
await self._client.write_text_file(
|
|
282
|
+
path=abs_path,
|
|
283
|
+
content=data,
|
|
284
|
+
session_id=self._session_id,
|
|
285
|
+
)
|
|
286
|
+
return len(data)
|
|
287
|
+
|
|
288
|
+
async def mkdir(
|
|
289
|
+
self, path: StrOrHostPath, parents: bool = False, exist_ok: bool = False
|
|
290
|
+
) -> None:
|
|
291
|
+
await self._fallback.mkdir(path, parents=parents, exist_ok=exist_ok)
|
|
292
|
+
|
|
293
|
+
async def exec(self, *args: str, env: Mapping[str, str] | None = None) -> HostProcess:
|
|
294
|
+
return await self._fallback.exec(*args, env=env)
|
|
295
|
+
|
|
296
|
+
def _abs_path(self, path: StrOrHostPath) -> str:
|
|
297
|
+
host_path = path if isinstance(path, HostPath) else HostPath(path)
|
|
298
|
+
return str(host_path.canonical())
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
import acp.schema
|
|
6
|
+
from fastmcp.mcp_config import MCPConfig
|
|
7
|
+
from pydantic import ValidationError
|
|
8
|
+
|
|
9
|
+
from pythinker_code.acp.types import MCPServer
|
|
10
|
+
from pythinker_code.exception import MCPConfigError
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def acp_mcp_servers_to_mcp_config(mcp_servers: list[MCPServer]) -> MCPConfig:
|
|
14
|
+
if not mcp_servers:
|
|
15
|
+
return MCPConfig()
|
|
16
|
+
|
|
17
|
+
try:
|
|
18
|
+
return MCPConfig.model_validate(
|
|
19
|
+
{"mcpServers": {server.name: _convert_acp_mcp_server(server) for server in mcp_servers}}
|
|
20
|
+
)
|
|
21
|
+
except ValidationError as exc:
|
|
22
|
+
raise MCPConfigError(f"Invalid MCP config from ACP client: {exc}") from exc
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _convert_acp_mcp_server(server: MCPServer) -> dict[str, Any]:
|
|
26
|
+
"""Convert an ACP MCP server to a dictionary representation."""
|
|
27
|
+
match server:
|
|
28
|
+
case acp.schema.HttpMcpServer():
|
|
29
|
+
return {
|
|
30
|
+
"url": server.url,
|
|
31
|
+
"transport": "http",
|
|
32
|
+
"headers": {header.name: header.value for header in server.headers},
|
|
33
|
+
}
|
|
34
|
+
case acp.schema.SseMcpServer():
|
|
35
|
+
return {
|
|
36
|
+
"url": server.url,
|
|
37
|
+
"transport": "sse",
|
|
38
|
+
"headers": {header.name: header.value for header in server.headers},
|
|
39
|
+
}
|
|
40
|
+
case acp.schema.McpServerStdio():
|
|
41
|
+
return {
|
|
42
|
+
"command": server.command,
|
|
43
|
+
"args": server.args,
|
|
44
|
+
"env": {item.name: item.value for item in server.env},
|
|
45
|
+
"transport": "stdio",
|
|
46
|
+
}
|