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,327 @@
|
|
|
1
|
+
"""ExitPlanMode tool — lets the LLM submit a plan for user approval."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import logging
|
|
7
|
+
from collections.abc import Awaitable, Callable
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import override
|
|
10
|
+
from uuid import uuid4
|
|
11
|
+
|
|
12
|
+
from pydantic import BaseModel, Field, field_validator
|
|
13
|
+
from pythinker_core.tooling import BriefDisplayBlock, CallableTool2, ToolError, ToolReturnValue
|
|
14
|
+
|
|
15
|
+
from pythinker_code.soul import get_wire_or_none, wire_send
|
|
16
|
+
from pythinker_code.soul.toolset import get_current_tool_call_or_none
|
|
17
|
+
from pythinker_code.tools.utils import ToolRejectedError, load_desc
|
|
18
|
+
from pythinker_code.wire.types import (
|
|
19
|
+
PlanDisplay,
|
|
20
|
+
QuestionItem,
|
|
21
|
+
QuestionNotSupported,
|
|
22
|
+
QuestionOption,
|
|
23
|
+
QuestionRequest,
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
NAME = "ExitPlanMode"
|
|
29
|
+
|
|
30
|
+
_RESERVED_LABELS = {"reject", "revise", "approve", "reject and exit"}
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class PlanOption(BaseModel):
|
|
34
|
+
"""A selectable approach/option within the plan."""
|
|
35
|
+
|
|
36
|
+
label: str = Field(
|
|
37
|
+
description=(
|
|
38
|
+
"Short name for this option (1-8 words). "
|
|
39
|
+
"Append '(Recommended)' if you recommend this option."
|
|
40
|
+
),
|
|
41
|
+
)
|
|
42
|
+
description: str = Field(
|
|
43
|
+
default="",
|
|
44
|
+
description="Brief summary of this approach and its trade-offs.",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
@field_validator("label")
|
|
48
|
+
@classmethod
|
|
49
|
+
def label_not_reserved(cls, v: str) -> str:
|
|
50
|
+
if v.strip().lower() in _RESERVED_LABELS:
|
|
51
|
+
reserved = ", ".join(f"'{w.title()}'" for w in sorted(_RESERVED_LABELS))
|
|
52
|
+
raise ValueError(
|
|
53
|
+
f"Option label {v!r} is reserved. Do not use {reserved} as option labels."
|
|
54
|
+
)
|
|
55
|
+
return v
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class Params(BaseModel):
|
|
59
|
+
options: list[PlanOption] | None = Field(
|
|
60
|
+
default=None,
|
|
61
|
+
max_length=3,
|
|
62
|
+
description=(
|
|
63
|
+
"When the plan contains multiple alternative approaches, list them here "
|
|
64
|
+
"so the user can choose which one to execute. 2-3 options. "
|
|
65
|
+
"Each option represents a distinct approach from the plan. "
|
|
66
|
+
"Do not use 'Reject', 'Revise', 'Approve', or 'Reject and Exit' as labels."
|
|
67
|
+
),
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
@field_validator("options")
|
|
71
|
+
@classmethod
|
|
72
|
+
def options_labels_unique(cls, v: list[PlanOption] | None) -> list[PlanOption] | None:
|
|
73
|
+
if v is None:
|
|
74
|
+
return v
|
|
75
|
+
labels = [opt.label for opt in v]
|
|
76
|
+
if len(labels) != len(set(labels)):
|
|
77
|
+
raise ValueError("Option labels must be unique. Found duplicate label(s).")
|
|
78
|
+
return v
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class ExitPlanMode(CallableTool2[Params]):
|
|
82
|
+
name: str = NAME
|
|
83
|
+
description: str = load_desc(Path(__file__).parent / "description.md")
|
|
84
|
+
params: type[Params] = Params
|
|
85
|
+
|
|
86
|
+
def __init__(self) -> None:
|
|
87
|
+
super().__init__()
|
|
88
|
+
self._toggle_callback: Callable[[], Awaitable[bool]] | None = None
|
|
89
|
+
self._plan_file_path_getter: Callable[[], Path | None] | None = None
|
|
90
|
+
self._plan_mode_checker: Callable[[], bool] | None = None
|
|
91
|
+
self._should_auto_approve_exit: Callable[[], bool] | None = None
|
|
92
|
+
|
|
93
|
+
def bind(
|
|
94
|
+
self,
|
|
95
|
+
toggle_callback: Callable[[], Awaitable[bool]],
|
|
96
|
+
plan_file_path_getter: Callable[[], Path | None],
|
|
97
|
+
plan_mode_checker: Callable[[], bool],
|
|
98
|
+
should_auto_approve_exit: Callable[[], bool] | None = None,
|
|
99
|
+
) -> None:
|
|
100
|
+
"""Late-bind soul callbacks after PythinkerSoul is constructed."""
|
|
101
|
+
self._toggle_callback = toggle_callback
|
|
102
|
+
self._plan_file_path_getter = plan_file_path_getter
|
|
103
|
+
self._plan_mode_checker = plan_mode_checker
|
|
104
|
+
self._should_auto_approve_exit = should_auto_approve_exit
|
|
105
|
+
|
|
106
|
+
@override
|
|
107
|
+
async def __call__(self, params: Params) -> ToolReturnValue:
|
|
108
|
+
# Guard: only works in plan mode
|
|
109
|
+
if not self._plan_mode_checker or not self._plan_mode_checker():
|
|
110
|
+
return ToolError(
|
|
111
|
+
message="Not in plan mode. ExitPlanMode is only available during plan mode.",
|
|
112
|
+
brief="Not in plan mode",
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
if not self._toggle_callback or not self._plan_file_path_getter:
|
|
116
|
+
return ToolError(
|
|
117
|
+
message="ExitPlanMode is not properly initialized.",
|
|
118
|
+
brief="Not initialized",
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
# Read the plan file
|
|
122
|
+
plan_path = self._plan_file_path_getter()
|
|
123
|
+
plan_content: str | None = None
|
|
124
|
+
if plan_path and await asyncio.to_thread(plan_path.exists):
|
|
125
|
+
plan_content = await asyncio.to_thread(
|
|
126
|
+
plan_path.read_text, encoding="utf-8", errors="replace"
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
if not plan_content:
|
|
130
|
+
return ToolError(
|
|
131
|
+
message=f"No plan file found. Write your plan to {plan_path} first, "
|
|
132
|
+
"then call ExitPlanMode.",
|
|
133
|
+
brief="No plan file",
|
|
134
|
+
)
|
|
135
|
+
|
|
136
|
+
# Auto-approve plan approval only when no user is present (auto mode).
|
|
137
|
+
if self._should_auto_approve_exit and self._should_auto_approve_exit():
|
|
138
|
+
await self._toggle_callback()
|
|
139
|
+
return ToolReturnValue(
|
|
140
|
+
is_error=False,
|
|
141
|
+
output=(
|
|
142
|
+
f"Plan approved (auto-approved). "
|
|
143
|
+
f"Plan mode deactivated. All tools are now available.\n"
|
|
144
|
+
f"Plan saved to: {plan_path}\n\n"
|
|
145
|
+
f"## Approved Plan:\n{plan_content}"
|
|
146
|
+
),
|
|
147
|
+
message="Plan approved (auto)",
|
|
148
|
+
display=[BriefDisplayBlock(text="Plan approved (auto)")],
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
# Present plan to user via QuestionRequest
|
|
152
|
+
wire = get_wire_or_none()
|
|
153
|
+
if wire is None:
|
|
154
|
+
return ToolError(
|
|
155
|
+
message="Cannot present plan: Wire is not available.",
|
|
156
|
+
brief="Wire unavailable",
|
|
157
|
+
)
|
|
158
|
+
|
|
159
|
+
tool_call = get_current_tool_call_or_none()
|
|
160
|
+
if tool_call is None:
|
|
161
|
+
return ToolError(
|
|
162
|
+
message="ExitPlanMode must be called from a tool call context.",
|
|
163
|
+
brief="Invalid context",
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
has_options = params.options is not None and len(params.options) >= 2
|
|
167
|
+
|
|
168
|
+
_reject_options = [
|
|
169
|
+
QuestionOption(
|
|
170
|
+
label="Reject",
|
|
171
|
+
description="Reject and stay in plan mode",
|
|
172
|
+
),
|
|
173
|
+
QuestionOption(
|
|
174
|
+
label="Reject and Exit",
|
|
175
|
+
description="Reject and exit plan mode",
|
|
176
|
+
),
|
|
177
|
+
]
|
|
178
|
+
|
|
179
|
+
if has_options:
|
|
180
|
+
assert params.options is not None
|
|
181
|
+
question_options = [
|
|
182
|
+
QuestionOption(label=opt.label, description=opt.description)
|
|
183
|
+
for opt in params.options
|
|
184
|
+
]
|
|
185
|
+
question_options.extend(_reject_options)
|
|
186
|
+
else:
|
|
187
|
+
question_options = [
|
|
188
|
+
QuestionOption(
|
|
189
|
+
label="Approve",
|
|
190
|
+
description="Exit plan mode and start execution",
|
|
191
|
+
),
|
|
192
|
+
*_reject_options,
|
|
193
|
+
]
|
|
194
|
+
|
|
195
|
+
# Display plan content inline in the chat
|
|
196
|
+
wire_send(PlanDisplay(content=plan_content, file_path=str(plan_path)))
|
|
197
|
+
|
|
198
|
+
request = QuestionRequest(
|
|
199
|
+
id=str(uuid4()),
|
|
200
|
+
tool_call_id=tool_call.id,
|
|
201
|
+
questions=[
|
|
202
|
+
QuestionItem(
|
|
203
|
+
question="Approve this plan",
|
|
204
|
+
header="Plan",
|
|
205
|
+
options=question_options,
|
|
206
|
+
other_label="Revise",
|
|
207
|
+
other_description="Stay in plan mode and provide feedback",
|
|
208
|
+
)
|
|
209
|
+
],
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
wire_send(request)
|
|
213
|
+
|
|
214
|
+
try:
|
|
215
|
+
answers = await request.wait()
|
|
216
|
+
except QuestionNotSupported:
|
|
217
|
+
return ToolError(
|
|
218
|
+
message="The connected client does not support plan mode. "
|
|
219
|
+
"Do NOT call this tool again.",
|
|
220
|
+
brief="Client unsupported",
|
|
221
|
+
)
|
|
222
|
+
except Exception:
|
|
223
|
+
logger.exception("Failed to get user response for ExitPlanMode")
|
|
224
|
+
return ToolError(
|
|
225
|
+
message="Failed to get user response.",
|
|
226
|
+
brief="Question failed",
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
if not answers:
|
|
230
|
+
return ToolReturnValue(
|
|
231
|
+
is_error=False,
|
|
232
|
+
output="User dismissed without choosing. Plan mode remains active. "
|
|
233
|
+
"Continue working on your plan or call ExitPlanMode again when ready.",
|
|
234
|
+
message="Dismissed",
|
|
235
|
+
display=[BriefDisplayBlock(text="Dismissed")],
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
# Parse user choice — exact match on option label
|
|
239
|
+
chose_reject_and_exit = any(v == "Reject and Exit" for v in answers.values())
|
|
240
|
+
|
|
241
|
+
if chose_reject_and_exit:
|
|
242
|
+
await self._toggle_callback()
|
|
243
|
+
return ToolRejectedError(
|
|
244
|
+
message=(
|
|
245
|
+
"Plan rejected by user. Plan mode deactivated. "
|
|
246
|
+
"All tools are now available. "
|
|
247
|
+
"Wait for the user's next message."
|
|
248
|
+
),
|
|
249
|
+
brief="Plan rejected, exited plan mode",
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
chose_reject = any(v == "Reject" for v in answers.values())
|
|
253
|
+
|
|
254
|
+
if chose_reject:
|
|
255
|
+
return ToolRejectedError(
|
|
256
|
+
message=(
|
|
257
|
+
"Plan rejected by user. Stay in plan mode. "
|
|
258
|
+
"The user will provide feedback via conversation. "
|
|
259
|
+
"Wait for the user's next message before revising."
|
|
260
|
+
),
|
|
261
|
+
brief="Plan rejected",
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# Approve — multi-approach (user selected a specific option)
|
|
265
|
+
if has_options:
|
|
266
|
+
assert params.options is not None
|
|
267
|
+
option_labels = {opt.label for opt in params.options}
|
|
268
|
+
chosen_option = None
|
|
269
|
+
for v in answers.values():
|
|
270
|
+
if v in option_labels:
|
|
271
|
+
chosen_option = v
|
|
272
|
+
break
|
|
273
|
+
|
|
274
|
+
if chosen_option:
|
|
275
|
+
await self._toggle_callback()
|
|
276
|
+
return ToolReturnValue(
|
|
277
|
+
is_error=False,
|
|
278
|
+
output=(
|
|
279
|
+
f'Plan approved by user. Selected approach: "{chosen_option}"\n'
|
|
280
|
+
f"Plan mode deactivated. All tools are now available.\n"
|
|
281
|
+
f"Plan saved to: {plan_path}\n\n"
|
|
282
|
+
f'IMPORTANT: Execute ONLY the selected approach "{chosen_option}". '
|
|
283
|
+
f"Ignore other approaches in the plan.\n\n"
|
|
284
|
+
f"## Approved Plan:\n{plan_content}"
|
|
285
|
+
),
|
|
286
|
+
message=f"Plan approved: {chosen_option}",
|
|
287
|
+
display=[BriefDisplayBlock(text=f"Plan approved: {chosen_option}")],
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
# Approve — single-approach only (has_options uses option labels, not "Approve")
|
|
291
|
+
chose_approve = not has_options and any(v == "Approve" for v in answers.values())
|
|
292
|
+
if chose_approve:
|
|
293
|
+
await self._toggle_callback()
|
|
294
|
+
return ToolReturnValue(
|
|
295
|
+
is_error=False,
|
|
296
|
+
output=(
|
|
297
|
+
f"Plan approved by user. Plan mode deactivated. "
|
|
298
|
+
f"All tools are now available.\n"
|
|
299
|
+
f"Plan saved to: {plan_path}\n\n"
|
|
300
|
+
f"## Approved Plan:\n{plan_content}"
|
|
301
|
+
),
|
|
302
|
+
message="Plan approved",
|
|
303
|
+
display=[BriefDisplayBlock(text="Plan approved")],
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
# Revise — user selected the free-text "Revise" option (fallback)
|
|
307
|
+
feedback = ""
|
|
308
|
+
for v in answers.values():
|
|
309
|
+
if v not in ("Approve", "Reject", "Reject and Exit"):
|
|
310
|
+
feedback = v
|
|
311
|
+
if feedback:
|
|
312
|
+
msg = (
|
|
313
|
+
"User wants to revise the plan. Stay in plan mode. "
|
|
314
|
+
"Revise based on the feedback below.\n\n"
|
|
315
|
+
f"User feedback: {feedback}"
|
|
316
|
+
)
|
|
317
|
+
else:
|
|
318
|
+
msg = (
|
|
319
|
+
"User wants to revise the plan. Stay in plan mode. "
|
|
320
|
+
"Wait for the user's next message with feedback before revising."
|
|
321
|
+
)
|
|
322
|
+
return ToolReturnValue(
|
|
323
|
+
is_error=False,
|
|
324
|
+
output=msg,
|
|
325
|
+
message="Plan revision requested",
|
|
326
|
+
display=[BriefDisplayBlock(text="Plan revision requested")],
|
|
327
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
Use this tool when you are in plan mode and have finished writing your plan to the plan file and are ready for user approval.
|
|
2
|
+
|
|
3
|
+
## How This Tool Works
|
|
4
|
+
- You should have already written your plan to the plan file specified in the plan mode reminder.
|
|
5
|
+
- This tool does NOT take the plan content as a parameter — it reads the plan from the file you wrote.
|
|
6
|
+
- The user will see the contents of your plan file when they review it.
|
|
7
|
+
|
|
8
|
+
## When to Use
|
|
9
|
+
Only use this tool for tasks that require planning implementation steps. For research tasks (searching files, reading code, understanding the codebase), do NOT use this tool.
|
|
10
|
+
|
|
11
|
+
## Multiple Approaches
|
|
12
|
+
If your plan contains multiple alternative approaches:
|
|
13
|
+
- Pass them via the `options` parameter so the user can choose which approach to execute.
|
|
14
|
+
- Each option should have a concise label and a brief description of trade-offs.
|
|
15
|
+
- If you recommend one option, append "(Recommended)" to its label.
|
|
16
|
+
- The user will see all options alongside Reject and Revise choices.
|
|
17
|
+
- Provide 2-3 options at most (the system appends a "Reject" option automatically, so the total shown to the user is 3-4).
|
|
18
|
+
- Do NOT use "Reject", "Revise", or "Approve" as option labels — these are reserved by the system.
|
|
19
|
+
|
|
20
|
+
## Before Using
|
|
21
|
+
- Yolo mode does not auto-approve this tool. In yolo mode, this tool still presents
|
|
22
|
+
the plan to the user for approval.
|
|
23
|
+
- If auto mode is active, do NOT use AskUserQuestion; make the best decision from available context.
|
|
24
|
+
- If auto mode is active, this tool is auto-approved because no user is present.
|
|
25
|
+
- If auto mode is not active and you have unresolved questions, use AskUserQuestion first.
|
|
26
|
+
- If auto mode is not active and you have multiple approaches and haven't narrowed down yet, consider using AskUserQuestion first to let the user choose, then write a plan for the chosen approach only.
|
|
27
|
+
- Once your plan is finalized, use THIS tool to request approval.
|
|
28
|
+
- Do NOT use AskUserQuestion to ask "Is this plan OK?" or "Should I proceed?" — that is exactly what ExitPlanMode does.
|
|
29
|
+
- If rejected, revise based on feedback and call ExitPlanMode again.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""EnterPlanMode tool — lets the LLM request to enter plan mode."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
from collections.abc import Awaitable, Callable
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import override
|
|
9
|
+
from uuid import uuid4
|
|
10
|
+
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
from pythinker_core.tooling import BriefDisplayBlock, CallableTool2, ToolError, ToolReturnValue
|
|
13
|
+
|
|
14
|
+
from pythinker_code.soul import get_wire_or_none, wire_send
|
|
15
|
+
from pythinker_code.soul.toolset import get_current_tool_call_or_none
|
|
16
|
+
from pythinker_code.tools.utils import load_desc
|
|
17
|
+
from pythinker_code.wire.types import (
|
|
18
|
+
QuestionItem,
|
|
19
|
+
QuestionNotSupported,
|
|
20
|
+
QuestionOption,
|
|
21
|
+
QuestionRequest,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
logger = logging.getLogger(__name__)
|
|
25
|
+
|
|
26
|
+
NAME = "EnterPlanMode"
|
|
27
|
+
|
|
28
|
+
_DESCRIPTION = load_desc(Path(__file__).parent / "enter_description.md")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class Params(BaseModel):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class EnterPlanMode(CallableTool2[Params]):
|
|
36
|
+
name: str = NAME
|
|
37
|
+
description: str = _DESCRIPTION
|
|
38
|
+
params: type[Params] = Params
|
|
39
|
+
|
|
40
|
+
def __init__(self) -> None:
|
|
41
|
+
super().__init__()
|
|
42
|
+
self._toggle_callback: Callable[[], Awaitable[bool]] | None = None
|
|
43
|
+
self._plan_file_path_getter: Callable[[], Path | None] | None = None
|
|
44
|
+
self._plan_mode_checker: Callable[[], bool] | None = None
|
|
45
|
+
self._is_auto_approve: Callable[[], bool] | None = None
|
|
46
|
+
|
|
47
|
+
def bind(
|
|
48
|
+
self,
|
|
49
|
+
toggle_callback: Callable[[], Awaitable[bool]],
|
|
50
|
+
plan_file_path_getter: Callable[[], Path | None],
|
|
51
|
+
plan_mode_checker: Callable[[], bool],
|
|
52
|
+
is_auto_approve: Callable[[], bool] | None = None,
|
|
53
|
+
*,
|
|
54
|
+
is_yolo: Callable[[], bool] | None = None,
|
|
55
|
+
) -> None:
|
|
56
|
+
"""Late-bind soul callbacks after PythinkerSoul is constructed."""
|
|
57
|
+
self._toggle_callback = toggle_callback
|
|
58
|
+
self._plan_file_path_getter = plan_file_path_getter
|
|
59
|
+
self._plan_mode_checker = plan_mode_checker
|
|
60
|
+
self._is_auto_approve = is_auto_approve or is_yolo
|
|
61
|
+
|
|
62
|
+
@override
|
|
63
|
+
async def __call__(self, params: Params) -> ToolReturnValue:
|
|
64
|
+
# Guard: already in plan mode
|
|
65
|
+
if self._plan_mode_checker and self._plan_mode_checker():
|
|
66
|
+
return ToolError(
|
|
67
|
+
message="Already in plan mode. Use ExitPlanMode when your plan is ready.",
|
|
68
|
+
brief="Already in plan mode",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
if not self._toggle_callback or not self._plan_file_path_getter:
|
|
72
|
+
return ToolError(
|
|
73
|
+
message="EnterPlanMode is not properly initialized.",
|
|
74
|
+
brief="Not initialized",
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
# Auto-approve entering plan mode when approvals are bypassed.
|
|
78
|
+
if self._is_auto_approve and self._is_auto_approve():
|
|
79
|
+
await self._toggle_callback()
|
|
80
|
+
plan_path = self._plan_file_path_getter()
|
|
81
|
+
return ToolReturnValue(
|
|
82
|
+
is_error=False,
|
|
83
|
+
output=(
|
|
84
|
+
f"Plan mode activated (auto-approved).\n"
|
|
85
|
+
f"Plan file: {plan_path}\n"
|
|
86
|
+
f"Workflow: identify key questions about the codebase → "
|
|
87
|
+
f"use Agent(subagent_type='explore') to investigate if needed → "
|
|
88
|
+
f"design approach → "
|
|
89
|
+
f"modify the plan file with WriteFile or StrReplaceFile "
|
|
90
|
+
f"(create it with WriteFile first if it does not exist) → "
|
|
91
|
+
f"call ExitPlanMode.\n"
|
|
92
|
+
),
|
|
93
|
+
message="Plan mode on (auto)",
|
|
94
|
+
display=[BriefDisplayBlock(text="Plan mode on (auto)")],
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Present confirmation dialog to user via QuestionRequest
|
|
98
|
+
wire = get_wire_or_none()
|
|
99
|
+
if wire is None:
|
|
100
|
+
return ToolError(
|
|
101
|
+
message="Cannot request user confirmation: Wire is not available.",
|
|
102
|
+
brief="Wire unavailable",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
tool_call = get_current_tool_call_or_none()
|
|
106
|
+
if tool_call is None:
|
|
107
|
+
return ToolError(
|
|
108
|
+
message="EnterPlanMode must be called from a tool call context.",
|
|
109
|
+
brief="Invalid context",
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
request = QuestionRequest(
|
|
113
|
+
id=str(uuid4()),
|
|
114
|
+
tool_call_id=tool_call.id,
|
|
115
|
+
questions=[
|
|
116
|
+
QuestionItem(
|
|
117
|
+
question="Enter plan mode?",
|
|
118
|
+
header="Plan Mode",
|
|
119
|
+
options=[
|
|
120
|
+
QuestionOption(
|
|
121
|
+
label="Yes",
|
|
122
|
+
description="Enter plan mode to explore and design an approach",
|
|
123
|
+
),
|
|
124
|
+
QuestionOption(
|
|
125
|
+
label="No",
|
|
126
|
+
description="Skip planning, start implementing now",
|
|
127
|
+
),
|
|
128
|
+
],
|
|
129
|
+
)
|
|
130
|
+
],
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
wire_send(request)
|
|
134
|
+
|
|
135
|
+
try:
|
|
136
|
+
answers = await request.wait()
|
|
137
|
+
except QuestionNotSupported:
|
|
138
|
+
return ToolError(
|
|
139
|
+
message="The connected client does not support plan mode. "
|
|
140
|
+
"Do NOT call this tool again.",
|
|
141
|
+
brief="Client unsupported",
|
|
142
|
+
)
|
|
143
|
+
except Exception:
|
|
144
|
+
logger.exception("Failed to get user response for EnterPlanMode")
|
|
145
|
+
return ToolError(
|
|
146
|
+
message="Failed to get user response.",
|
|
147
|
+
brief="Question failed",
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
if not answers:
|
|
151
|
+
return ToolReturnValue(
|
|
152
|
+
is_error=False,
|
|
153
|
+
output="User dismissed without choosing. Proceed with implementation directly.",
|
|
154
|
+
message="Dismissed",
|
|
155
|
+
display=[BriefDisplayBlock(text="Dismissed")],
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
# Parse user choice — exact match on option label
|
|
159
|
+
chose_yes = any(v == "Yes" for v in answers.values())
|
|
160
|
+
if chose_yes:
|
|
161
|
+
await self._toggle_callback()
|
|
162
|
+
plan_path = self._plan_file_path_getter()
|
|
163
|
+
return ToolReturnValue(
|
|
164
|
+
is_error=False,
|
|
165
|
+
output=(
|
|
166
|
+
f"Plan mode activated. You MUST NOT edit code files — only read and plan.\n"
|
|
167
|
+
f"Plan file: {plan_path}\n"
|
|
168
|
+
f"Workflow: identify key questions about the codebase → "
|
|
169
|
+
f"use Agent(subagent_type='explore') to investigate if needed → "
|
|
170
|
+
f"design approach → "
|
|
171
|
+
f"modify the plan file with WriteFile or StrReplaceFile "
|
|
172
|
+
f"(create it with WriteFile first if it does not exist) → "
|
|
173
|
+
f"call ExitPlanMode.\n"
|
|
174
|
+
f"Use AskUserQuestion only to clarify missing requirements or choose "
|
|
175
|
+
f"between approaches.\n"
|
|
176
|
+
f"Do NOT use AskUserQuestion to ask about plan approval."
|
|
177
|
+
),
|
|
178
|
+
message="Plan mode on",
|
|
179
|
+
display=[BriefDisplayBlock(text="Plan mode on")],
|
|
180
|
+
)
|
|
181
|
+
else:
|
|
182
|
+
return ToolReturnValue(
|
|
183
|
+
is_error=False,
|
|
184
|
+
output=(
|
|
185
|
+
"User declined to enter plan mode. Please check with user whether "
|
|
186
|
+
"to proceed with implementation directly."
|
|
187
|
+
),
|
|
188
|
+
message="Declined",
|
|
189
|
+
display=[BriefDisplayBlock(text="Declined")],
|
|
190
|
+
)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
Use this tool proactively when you're about to start a non-trivial implementation task.
|
|
2
|
+
Getting user sign-off on your approach before writing code prevents wasted effort.
|
|
3
|
+
|
|
4
|
+
Use it when ANY of these conditions apply:
|
|
5
|
+
|
|
6
|
+
1. New Feature Implementation — e.g. "Add a caching layer to the API"
|
|
7
|
+
2. Multiple Valid Approaches — e.g. "Optimize database queries" (indexing vs rewrite vs caching)
|
|
8
|
+
3. Code Modifications — e.g. "Refactor auth module to support OAuth"
|
|
9
|
+
4. Architectural Decisions — e.g. "Add WebSocket support"
|
|
10
|
+
5. Multi-File Changes — involves more than 2-3 files
|
|
11
|
+
6. Unclear Requirements — need exploration to understand scope
|
|
12
|
+
7. User Preferences Matter — if user input would materially change the implementation approach, use EnterPlanMode to structure the decision
|
|
13
|
+
|
|
14
|
+
Auto-approve mode notes:
|
|
15
|
+
- Yolo mode only bypasses permission approval. It does not make the session non-interactive.
|
|
16
|
+
- In yolo mode, EnterPlanMode is approved automatically, but ExitPlanMode still presents
|
|
17
|
+
the plan to the user for approval.
|
|
18
|
+
- Auto mode bypasses permission approval and is non-interactive. In auto mode, do not use
|
|
19
|
+
AskUserQuestion; make the best decision from available context.
|
|
20
|
+
- In auto mode, EnterPlanMode / ExitPlanMode are approved automatically because no user
|
|
21
|
+
is present.
|
|
22
|
+
- Use EnterPlanMode only when planning itself adds value.
|
|
23
|
+
|
|
24
|
+
When NOT to use:
|
|
25
|
+
- Single-line or few-line fixes (typos, obvious bugs, small tweaks)
|
|
26
|
+
- User gave very specific, detailed instructions
|
|
27
|
+
- Pure research/exploration tasks
|
|
28
|
+
|
|
29
|
+
## What Happens in Plan Mode
|
|
30
|
+
In plan mode, you will:
|
|
31
|
+
1. Identify 2-3 key questions about the codebase that are critical to your plan. If you are not confident about the codebase structure or relevant code paths, use `Agent(subagent_type="explore")` to investigate these questions first — this is strongly recommended for non-trivial tasks.
|
|
32
|
+
2. Explore the codebase using Glob, Grep, ReadFile (read-only) for any remaining quick lookups
|
|
33
|
+
3. Design an implementation approach based on your findings
|
|
34
|
+
4. Write your plan to a plan file
|
|
35
|
+
5. Present your plan to the user via ExitPlanMode for approval
|