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,74 @@
|
|
|
1
|
+
import importlib.util
|
|
2
|
+
import shlex
|
|
3
|
+
import shutil
|
|
4
|
+
import subprocess
|
|
5
|
+
import sys
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
|
|
8
|
+
import typer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def _default_acp_command() -> list[str]:
|
|
12
|
+
argv0 = sys.argv[0]
|
|
13
|
+
if argv0:
|
|
14
|
+
resolved = shutil.which(argv0)
|
|
15
|
+
resolved_path = Path(resolved).expanduser() if resolved else Path(argv0).expanduser()
|
|
16
|
+
if (
|
|
17
|
+
resolved_path.exists()
|
|
18
|
+
and resolved_path.suffix != ".py"
|
|
19
|
+
and not resolved_path.name.startswith(("python", "pypy"))
|
|
20
|
+
):
|
|
21
|
+
return [str(resolved_path), "acp"]
|
|
22
|
+
|
|
23
|
+
return [sys.executable, "-m", "pythinker_code.cli", "acp"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _default_toad_command() -> list[str]:
|
|
27
|
+
if sys.version_info < (3, 14):
|
|
28
|
+
typer.echo("`pythinker term` requires Python 3.14+ because Toad requires it.", err=True)
|
|
29
|
+
raise typer.Exit(code=1)
|
|
30
|
+
if importlib.util.find_spec("toad") is None:
|
|
31
|
+
typer.echo(
|
|
32
|
+
"Toad dependency is missing. Install pythinker-code with Python 3.14+ "
|
|
33
|
+
"to use `pythinker term`.",
|
|
34
|
+
err=True,
|
|
35
|
+
)
|
|
36
|
+
raise typer.Exit(code=1)
|
|
37
|
+
return [sys.executable, "-m", "toad.cli"]
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def _extract_project_dir(extra_args: list[str]) -> Path | None:
|
|
41
|
+
work_dir: str | None = None
|
|
42
|
+
idx = 0
|
|
43
|
+
while idx < len(extra_args):
|
|
44
|
+
arg = extra_args[idx]
|
|
45
|
+
if arg in ("--work-dir", "-w"):
|
|
46
|
+
if idx + 1 < len(extra_args):
|
|
47
|
+
work_dir = extra_args[idx + 1]
|
|
48
|
+
idx += 2
|
|
49
|
+
continue
|
|
50
|
+
elif arg.startswith("--work-dir=") or arg.startswith("-w="):
|
|
51
|
+
work_dir = arg.split("=", 1)[1]
|
|
52
|
+
elif arg.startswith("-w") and len(arg) > 2:
|
|
53
|
+
work_dir = arg[2:]
|
|
54
|
+
idx += 1
|
|
55
|
+
|
|
56
|
+
if not work_dir:
|
|
57
|
+
return None
|
|
58
|
+
|
|
59
|
+
return Path(work_dir).expanduser().resolve()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def run_term(ctx: typer.Context) -> None:
|
|
63
|
+
extra_args = list(ctx.args)
|
|
64
|
+
acp_args = _default_acp_command()
|
|
65
|
+
acp_command = shlex.join(acp_args)
|
|
66
|
+
toad_parts = _default_toad_command()
|
|
67
|
+
args = [*toad_parts, "acp", acp_command]
|
|
68
|
+
project_dir = _extract_project_dir(extra_args)
|
|
69
|
+
if project_dir is not None:
|
|
70
|
+
args.append(str(project_dir))
|
|
71
|
+
|
|
72
|
+
result = subprocess.run(args)
|
|
73
|
+
if result.returncode != 0:
|
|
74
|
+
raise typer.Exit(code=result.returncode)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"""Vis command for Pythinker Agent Tracing Visualizer."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
cli = typer.Typer(help="Run Pythinker Agent Tracing Visualizer.")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@cli.callback(invoke_without_command=True)
|
|
11
|
+
def vis(
|
|
12
|
+
ctx: typer.Context,
|
|
13
|
+
host: Annotated[
|
|
14
|
+
str | None,
|
|
15
|
+
typer.Option("--host", "-h", help="Bind to specific IP address"),
|
|
16
|
+
] = None,
|
|
17
|
+
network: Annotated[
|
|
18
|
+
bool,
|
|
19
|
+
typer.Option("--network", "-n", help="Enable network access (bind to 0.0.0.0)"),
|
|
20
|
+
] = False,
|
|
21
|
+
port: Annotated[int, typer.Option("--port", "-p", help="Port to bind to")] = 5495,
|
|
22
|
+
open_browser: Annotated[
|
|
23
|
+
bool, typer.Option("--open/--no-open", help="Open browser automatically")
|
|
24
|
+
] = True,
|
|
25
|
+
reload: Annotated[bool, typer.Option("--reload", help="Enable auto-reload")] = False,
|
|
26
|
+
):
|
|
27
|
+
"""Launch the agent tracing visualizer."""
|
|
28
|
+
from pythinker_code.vis.app import run_vis_server
|
|
29
|
+
|
|
30
|
+
# Determine bind address (same logic as pythinker web)
|
|
31
|
+
if host:
|
|
32
|
+
bind_host = host
|
|
33
|
+
elif network:
|
|
34
|
+
bind_host = "0.0.0.0"
|
|
35
|
+
else:
|
|
36
|
+
bind_host = "127.0.0.1"
|
|
37
|
+
|
|
38
|
+
run_vis_server(host=bind_host, port=port, open_browser=open_browser, reload=reload)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""Web UI command for Pythinker CLI."""
|
|
2
|
+
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
|
|
5
|
+
import typer
|
|
6
|
+
|
|
7
|
+
cli = typer.Typer(help="Run Pythinker CLI web interface.")
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@cli.callback(invoke_without_command=True)
|
|
11
|
+
def web(
|
|
12
|
+
ctx: typer.Context,
|
|
13
|
+
host: Annotated[
|
|
14
|
+
str | None,
|
|
15
|
+
typer.Option("--host", "-h", help="Bind to specific IP address"),
|
|
16
|
+
] = None,
|
|
17
|
+
network: Annotated[
|
|
18
|
+
bool,
|
|
19
|
+
typer.Option("--network", "-n", help="Enable network access (bind to 0.0.0.0)"),
|
|
20
|
+
] = False,
|
|
21
|
+
port: Annotated[int, typer.Option("--port", "-p", help="Port to bind to")] = 5494,
|
|
22
|
+
reload: Annotated[bool, typer.Option("--reload", help="Enable auto-reload")] = False,
|
|
23
|
+
open_browser: Annotated[
|
|
24
|
+
bool, typer.Option("--open/--no-open", help="Open browser automatically")
|
|
25
|
+
] = True,
|
|
26
|
+
auth_token: Annotated[
|
|
27
|
+
str | None,
|
|
28
|
+
typer.Option("--auth-token", help="Bearer token for API authentication."),
|
|
29
|
+
] = None,
|
|
30
|
+
allowed_origins: Annotated[
|
|
31
|
+
str | None,
|
|
32
|
+
typer.Option(
|
|
33
|
+
"--allowed-origins",
|
|
34
|
+
help="Comma-separated list of allowed Origin values.",
|
|
35
|
+
),
|
|
36
|
+
] = None,
|
|
37
|
+
dangerously_omit_auth: Annotated[
|
|
38
|
+
bool,
|
|
39
|
+
typer.Option(
|
|
40
|
+
"--dangerously-omit-auth",
|
|
41
|
+
help="Disable auth checks (dangerous in public networks).",
|
|
42
|
+
),
|
|
43
|
+
] = False,
|
|
44
|
+
restrict_sensitive_apis: Annotated[
|
|
45
|
+
bool | None,
|
|
46
|
+
typer.Option(
|
|
47
|
+
"--restrict-sensitive-apis/--no-restrict-sensitive-apis",
|
|
48
|
+
help="Disable sensitive APIs (config write, open-in, file access limits).",
|
|
49
|
+
),
|
|
50
|
+
] = None,
|
|
51
|
+
lan_only: Annotated[
|
|
52
|
+
bool,
|
|
53
|
+
typer.Option(
|
|
54
|
+
"--lan-only/--public",
|
|
55
|
+
help="Only allow access from local network (default) or allow public access.",
|
|
56
|
+
),
|
|
57
|
+
] = True,
|
|
58
|
+
):
|
|
59
|
+
"""Run Pythinker CLI web interface."""
|
|
60
|
+
from pythinker_code.web.app import run_web_server
|
|
61
|
+
|
|
62
|
+
# Determine bind address
|
|
63
|
+
if host:
|
|
64
|
+
bind_host = host
|
|
65
|
+
elif network:
|
|
66
|
+
bind_host = "0.0.0.0"
|
|
67
|
+
else:
|
|
68
|
+
bind_host = "127.0.0.1"
|
|
69
|
+
|
|
70
|
+
run_web_server(
|
|
71
|
+
host=bind_host,
|
|
72
|
+
port=port,
|
|
73
|
+
reload=reload,
|
|
74
|
+
open_browser=open_browser,
|
|
75
|
+
auth_token=auth_token,
|
|
76
|
+
allowed_origins=allowed_origins,
|
|
77
|
+
dangerously_omit_auth=dangerously_omit_auth,
|
|
78
|
+
restrict_sensitive_apis=restrict_sensitive_apis,
|
|
79
|
+
lan_only=lan_only,
|
|
80
|
+
)
|
pythinker_code/config.py
ADDED
|
@@ -0,0 +1,453 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Literal, Self
|
|
6
|
+
|
|
7
|
+
import tomlkit
|
|
8
|
+
from pydantic import (
|
|
9
|
+
AliasChoices,
|
|
10
|
+
BaseModel,
|
|
11
|
+
Field,
|
|
12
|
+
SecretStr,
|
|
13
|
+
ValidationError,
|
|
14
|
+
field_serializer,
|
|
15
|
+
model_validator,
|
|
16
|
+
)
|
|
17
|
+
from tomlkit.exceptions import TOMLKitError
|
|
18
|
+
|
|
19
|
+
from pythinker_code.exception import ConfigError
|
|
20
|
+
from pythinker_code.hooks.config import HookDef
|
|
21
|
+
from pythinker_code.llm import ModelCapability, ProviderType
|
|
22
|
+
from pythinker_code.share import get_share_dir
|
|
23
|
+
from pythinker_code.utils.logging import logger
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class OAuthRef(BaseModel):
|
|
27
|
+
"""Reference to OAuth credentials stored outside the config file."""
|
|
28
|
+
|
|
29
|
+
storage: Literal["keyring", "file"] = "file"
|
|
30
|
+
"""Credential storage backend."""
|
|
31
|
+
key: str
|
|
32
|
+
"""Storage key to locate OAuth credentials."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class LLMProvider(BaseModel):
|
|
36
|
+
"""LLM provider configuration."""
|
|
37
|
+
|
|
38
|
+
type: ProviderType
|
|
39
|
+
"""Provider type"""
|
|
40
|
+
base_url: str
|
|
41
|
+
"""API base URL"""
|
|
42
|
+
api_key: SecretStr
|
|
43
|
+
"""API key"""
|
|
44
|
+
env: dict[str, str] | None = None
|
|
45
|
+
"""Environment variables to set before creating the provider instance"""
|
|
46
|
+
custom_headers: dict[str, str] | None = None
|
|
47
|
+
"""Custom headers to include in API requests"""
|
|
48
|
+
reasoning_key: str | None = None
|
|
49
|
+
"""Message field name carrying reasoning content for OpenAI-compatible APIs.
|
|
50
|
+
Applies to provider type ``openai_legacy``. Defaults to ``reasoning_content``
|
|
51
|
+
when unset. Use an empty string to disable reasoning round-tripping."""
|
|
52
|
+
oauth: OAuthRef | None = None
|
|
53
|
+
"""OAuth credential reference (do not store tokens here)."""
|
|
54
|
+
|
|
55
|
+
@field_serializer("api_key", when_used="json")
|
|
56
|
+
def dump_secret(self, v: SecretStr):
|
|
57
|
+
return v.get_secret_value()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class LLMModel(BaseModel):
|
|
61
|
+
"""LLM model configuration."""
|
|
62
|
+
|
|
63
|
+
provider: str
|
|
64
|
+
"""Provider name"""
|
|
65
|
+
model: str
|
|
66
|
+
"""Model name"""
|
|
67
|
+
max_context_size: int
|
|
68
|
+
"""Maximum context size (unit: tokens)"""
|
|
69
|
+
capabilities: set[ModelCapability] | None = None
|
|
70
|
+
"""Model capabilities"""
|
|
71
|
+
display_name: str | None = None
|
|
72
|
+
"""Human-readable model name (sourced from the provider's models API when available)"""
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class LoopControl(BaseModel):
|
|
76
|
+
"""Agent loop control configuration."""
|
|
77
|
+
|
|
78
|
+
max_steps_per_turn: int = Field(
|
|
79
|
+
default=1000,
|
|
80
|
+
ge=1,
|
|
81
|
+
validation_alias=AliasChoices("max_steps_per_turn", "max_steps_per_run"),
|
|
82
|
+
)
|
|
83
|
+
"""Maximum number of steps in one turn"""
|
|
84
|
+
max_retries_per_step: int = Field(default=3, ge=1)
|
|
85
|
+
"""Maximum number of retries in one step"""
|
|
86
|
+
max_ralph_iterations: int = Field(default=0, ge=-1)
|
|
87
|
+
"""Extra iterations after the first turn in Ralph mode. Use -1 for unlimited."""
|
|
88
|
+
reserved_context_size: int = Field(default=50_000, ge=1000)
|
|
89
|
+
"""Reserved token count for LLM response generation. Auto-compaction triggers when
|
|
90
|
+
either context_tokens + reserved_context_size >= max_context_size or
|
|
91
|
+
context_tokens >= max_context_size * compaction_trigger_ratio. Default is 50000."""
|
|
92
|
+
compaction_trigger_ratio: float = Field(default=0.85, ge=0.5, le=0.99)
|
|
93
|
+
"""Context usage ratio threshold for auto-compaction. Default is 0.85 (85%).
|
|
94
|
+
Auto-compaction triggers when context_tokens >= max_context_size * compaction_trigger_ratio
|
|
95
|
+
or when context_tokens + reserved_context_size >= max_context_size."""
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class BackgroundConfig(BaseModel):
|
|
99
|
+
"""Background task runtime configuration."""
|
|
100
|
+
|
|
101
|
+
max_running_tasks: int = Field(default=4, ge=1)
|
|
102
|
+
read_max_bytes: int = Field(default=30_000, ge=1024)
|
|
103
|
+
notification_tail_lines: int = Field(default=20, ge=1)
|
|
104
|
+
notification_tail_chars: int = Field(default=3_000, ge=256)
|
|
105
|
+
wait_poll_interval_ms: int = Field(default=500, ge=50)
|
|
106
|
+
worker_heartbeat_interval_ms: int = Field(default=5_000, ge=100)
|
|
107
|
+
worker_stale_after_ms: int = Field(default=15_000, ge=1000)
|
|
108
|
+
kill_grace_period_ms: int = Field(default=2_000, ge=100)
|
|
109
|
+
keep_alive_on_exit: bool = Field(
|
|
110
|
+
default=False,
|
|
111
|
+
description="Keep background tasks alive when CLI exits. Default: kill on exit.",
|
|
112
|
+
)
|
|
113
|
+
agent_task_timeout_s: int = Field(default=900, ge=60)
|
|
114
|
+
"""Maximum runtime in seconds for a background agent task. Default: 900 (15 min)."""
|
|
115
|
+
print_wait_ceiling_s: int = Field(default=3600, ge=1)
|
|
116
|
+
"""Hard ceiling for how long ``--print`` mode waits for background tasks before
|
|
117
|
+
killing them and exiting. The effective wait is
|
|
118
|
+
``min(max(active_task.timeout_s or agent_task_timeout_s), print_wait_ceiling_s)``.
|
|
119
|
+
Default: 3600 (1 hour)."""
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class NotificationConfig(BaseModel):
|
|
123
|
+
"""Notification runtime configuration."""
|
|
124
|
+
|
|
125
|
+
claim_stale_after_ms: int = Field(default=15_000, ge=1000)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
class PythinkerAISearchConfig(BaseModel):
|
|
129
|
+
"""Pythinker AI Search configuration."""
|
|
130
|
+
|
|
131
|
+
base_url: str
|
|
132
|
+
"""Base URL for Pythinker AI Search service."""
|
|
133
|
+
api_key: SecretStr
|
|
134
|
+
"""API key for Pythinker AI Search service."""
|
|
135
|
+
custom_headers: dict[str, str] | None = None
|
|
136
|
+
"""Custom headers to include in API requests."""
|
|
137
|
+
oauth: OAuthRef | None = None
|
|
138
|
+
"""OAuth credential reference (do not store tokens here)."""
|
|
139
|
+
|
|
140
|
+
@field_serializer("api_key", when_used="json")
|
|
141
|
+
def dump_secret(self, v: SecretStr):
|
|
142
|
+
return v.get_secret_value()
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
class PythinkerAIFetchConfig(BaseModel):
|
|
146
|
+
"""Pythinker AI Fetch configuration."""
|
|
147
|
+
|
|
148
|
+
base_url: str
|
|
149
|
+
"""Base URL for Pythinker AI Fetch service."""
|
|
150
|
+
api_key: SecretStr
|
|
151
|
+
"""API key for Pythinker AI Fetch service."""
|
|
152
|
+
custom_headers: dict[str, str] | None = None
|
|
153
|
+
"""Custom headers to include in API requests."""
|
|
154
|
+
oauth: OAuthRef | None = None
|
|
155
|
+
"""OAuth credential reference (do not store tokens here)."""
|
|
156
|
+
|
|
157
|
+
@field_serializer("api_key", when_used="json")
|
|
158
|
+
def dump_secret(self, v: SecretStr):
|
|
159
|
+
return v.get_secret_value()
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class Services(BaseModel):
|
|
163
|
+
"""Services configuration."""
|
|
164
|
+
|
|
165
|
+
pythinker_ai_search: PythinkerAISearchConfig | None = None
|
|
166
|
+
"""Pythinker AI Search configuration."""
|
|
167
|
+
pythinker_ai_fetch: PythinkerAIFetchConfig | None = None
|
|
168
|
+
"""Pythinker AI Fetch configuration."""
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class MCPClientConfig(BaseModel):
|
|
172
|
+
"""MCP client configuration."""
|
|
173
|
+
|
|
174
|
+
tool_call_timeout_ms: int = 60000
|
|
175
|
+
"""Timeout for tool calls in milliseconds."""
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
class MCPConfig(BaseModel):
|
|
179
|
+
"""MCP configuration."""
|
|
180
|
+
|
|
181
|
+
client: MCPClientConfig = Field(
|
|
182
|
+
default_factory=MCPClientConfig, description="MCP client configuration"
|
|
183
|
+
)
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class Config(BaseModel):
|
|
187
|
+
"""Main configuration structure."""
|
|
188
|
+
|
|
189
|
+
is_from_default_location: bool = Field(
|
|
190
|
+
default=False,
|
|
191
|
+
description="Whether the config was loaded from the default location",
|
|
192
|
+
exclude=True,
|
|
193
|
+
)
|
|
194
|
+
source_file: Path | None = Field(
|
|
195
|
+
default=None,
|
|
196
|
+
description="Path to the loaded config file. None when loaded from --config text.",
|
|
197
|
+
exclude=True,
|
|
198
|
+
)
|
|
199
|
+
default_model: str = Field(default="", description="Default model to use")
|
|
200
|
+
default_thinking: bool = Field(default=False, description="Default thinking mode")
|
|
201
|
+
default_yolo: bool = Field(default=False, description="Default yolo (auto-approve) mode")
|
|
202
|
+
skip_auto_prompt_injection: bool = Field(
|
|
203
|
+
default=False,
|
|
204
|
+
description=(
|
|
205
|
+
"If true, suppress the auto-mode system reminder. "
|
|
206
|
+
"Yolo mode does not inject a system reminder."
|
|
207
|
+
),
|
|
208
|
+
)
|
|
209
|
+
default_plan_mode: bool = Field(default=False, description="Default plan mode for new sessions")
|
|
210
|
+
default_editor: str = Field(
|
|
211
|
+
default="",
|
|
212
|
+
description="Default external editor command (e.g. 'vim', 'code --wait')",
|
|
213
|
+
)
|
|
214
|
+
theme: Literal["dark", "light"] = Field(
|
|
215
|
+
default="dark",
|
|
216
|
+
description="Terminal color theme. Use 'light' for light terminal backgrounds.",
|
|
217
|
+
)
|
|
218
|
+
show_thinking_stream: bool = Field(
|
|
219
|
+
default=True,
|
|
220
|
+
description=(
|
|
221
|
+
"If true, stream the raw reasoning text in the live area as a "
|
|
222
|
+
"6-line scrolling preview and commit the full reasoning markdown "
|
|
223
|
+
"to history when the block ends. Default true. Set to false to "
|
|
224
|
+
"show only the compact 'Thinking ...' indicator and a one-line "
|
|
225
|
+
"trace summary."
|
|
226
|
+
),
|
|
227
|
+
)
|
|
228
|
+
models: dict[str, LLMModel] = Field(default_factory=dict, description="List of LLM models")
|
|
229
|
+
providers: dict[str, LLMProvider] = Field(
|
|
230
|
+
default_factory=dict, description="List of LLM providers"
|
|
231
|
+
)
|
|
232
|
+
loop_control: LoopControl = Field(default_factory=LoopControl, description="Agent loop control")
|
|
233
|
+
background: BackgroundConfig = Field(
|
|
234
|
+
default_factory=BackgroundConfig, description="Background task configuration"
|
|
235
|
+
)
|
|
236
|
+
notifications: NotificationConfig = Field(
|
|
237
|
+
default_factory=NotificationConfig, description="Notification configuration"
|
|
238
|
+
)
|
|
239
|
+
services: Services = Field(default_factory=Services, description="Services configuration")
|
|
240
|
+
mcp: MCPConfig = Field(default_factory=MCPConfig, description="MCP configuration")
|
|
241
|
+
hooks: list[HookDef] = Field(default_factory=list, description="Hook definitions") # pyright: ignore[reportUnknownVariableType]
|
|
242
|
+
merge_all_available_skills: bool = Field(
|
|
243
|
+
default=True,
|
|
244
|
+
description=(
|
|
245
|
+
"Merge skills from all existing brand directories (pythinker/claude/codex) "
|
|
246
|
+
"instead of using only the first one found. Defaults to true so users "
|
|
247
|
+
"who keep skills in multiple brand directories see everything out of "
|
|
248
|
+
"the box; set to false to restore the first-match-only behaviour."
|
|
249
|
+
),
|
|
250
|
+
)
|
|
251
|
+
extra_skill_dirs: list[str] = Field(
|
|
252
|
+
default_factory=list,
|
|
253
|
+
description=(
|
|
254
|
+
"Extra directories to discover skills from, added on top of the "
|
|
255
|
+
"built-in / user / project locations. Each entry may be an absolute "
|
|
256
|
+
"path, ``~``-prefixed (expanded against $HOME), or relative to the "
|
|
257
|
+
"project root (the nearest ``.git`` directory above the work dir). "
|
|
258
|
+
"Missing paths are silently skipped."
|
|
259
|
+
),
|
|
260
|
+
)
|
|
261
|
+
telemetry: bool = Field(
|
|
262
|
+
default=False,
|
|
263
|
+
description=(
|
|
264
|
+
"Enable anonymous telemetry to help improve pythinker-code. Set to true to enable."
|
|
265
|
+
),
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
@model_validator(mode="after")
|
|
269
|
+
def validate_model(self) -> Self:
|
|
270
|
+
if self.default_model and self.default_model not in self.models:
|
|
271
|
+
raise ValueError(f"Default model {self.default_model} not found in models")
|
|
272
|
+
for model in self.models.values():
|
|
273
|
+
if model.provider not in self.providers:
|
|
274
|
+
raise ValueError(f"Provider {model.provider} not found in providers")
|
|
275
|
+
return self
|
|
276
|
+
|
|
277
|
+
|
|
278
|
+
def get_config_file() -> Path:
|
|
279
|
+
"""Get the configuration file path."""
|
|
280
|
+
return get_share_dir() / "config.toml"
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
def get_default_config() -> Config:
|
|
284
|
+
"""Get the default configuration."""
|
|
285
|
+
return Config(
|
|
286
|
+
default_model="",
|
|
287
|
+
models={},
|
|
288
|
+
providers={},
|
|
289
|
+
services=Services(),
|
|
290
|
+
)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
def load_config(config_file: Path | None = None) -> Config:
|
|
294
|
+
"""
|
|
295
|
+
Load configuration from config file.
|
|
296
|
+
If the config file does not exist, create it with default configuration.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
config_file (Path | None): Path to the configuration file. If None, use default path.
|
|
300
|
+
|
|
301
|
+
Returns:
|
|
302
|
+
Validated Config object.
|
|
303
|
+
|
|
304
|
+
Raises:
|
|
305
|
+
ConfigError: If the configuration file is invalid.
|
|
306
|
+
"""
|
|
307
|
+
default_config_file = get_config_file().expanduser().resolve(strict=False)
|
|
308
|
+
if config_file is None:
|
|
309
|
+
config_file = default_config_file
|
|
310
|
+
config_file = config_file.expanduser().resolve(strict=False)
|
|
311
|
+
is_default_config_file = config_file == default_config_file
|
|
312
|
+
logger.debug("Loading config from file: {file}", file=config_file)
|
|
313
|
+
|
|
314
|
+
# If the user hasn't provided an explicit config path, migrate legacy JSON config once.
|
|
315
|
+
if is_default_config_file and not config_file.exists():
|
|
316
|
+
_migrate_json_config_to_toml()
|
|
317
|
+
|
|
318
|
+
if not config_file.exists():
|
|
319
|
+
config = get_default_config()
|
|
320
|
+
logger.debug("No config file found, creating default config: {config}", config=config)
|
|
321
|
+
save_config(config, config_file)
|
|
322
|
+
config.is_from_default_location = is_default_config_file
|
|
323
|
+
config.source_file = config_file
|
|
324
|
+
return config
|
|
325
|
+
|
|
326
|
+
try:
|
|
327
|
+
config_text = config_file.read_text(encoding="utf-8")
|
|
328
|
+
if config_file.suffix.lower() == ".json":
|
|
329
|
+
data = json.loads(config_text)
|
|
330
|
+
else:
|
|
331
|
+
data = tomlkit.loads(config_text)
|
|
332
|
+
config = Config.model_validate(data)
|
|
333
|
+
except json.JSONDecodeError as e:
|
|
334
|
+
raise ConfigError(f"Invalid JSON in configuration file {config_file}: {e}") from e
|
|
335
|
+
except TOMLKitError as e:
|
|
336
|
+
raise ConfigError(f"Invalid TOML in configuration file {config_file}: {e}") from e
|
|
337
|
+
except ValidationError as e:
|
|
338
|
+
raise ConfigError(f"Invalid configuration file {config_file}: {e}") from e
|
|
339
|
+
config.is_from_default_location = is_default_config_file
|
|
340
|
+
config.source_file = config_file
|
|
341
|
+
return config
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def load_config_from_string(config_string: str) -> Config:
|
|
345
|
+
"""
|
|
346
|
+
Load configuration from a TOML or JSON string.
|
|
347
|
+
|
|
348
|
+
Args:
|
|
349
|
+
config_string (str): TOML or JSON configuration text.
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
Validated Config object.
|
|
353
|
+
|
|
354
|
+
Raises:
|
|
355
|
+
ConfigError: If the configuration text is invalid.
|
|
356
|
+
"""
|
|
357
|
+
if not config_string.strip():
|
|
358
|
+
raise ConfigError("Configuration text cannot be empty")
|
|
359
|
+
|
|
360
|
+
json_error: json.JSONDecodeError | None = None
|
|
361
|
+
try:
|
|
362
|
+
data = json.loads(config_string)
|
|
363
|
+
except json.JSONDecodeError as exc:
|
|
364
|
+
json_error = exc
|
|
365
|
+
data = None
|
|
366
|
+
|
|
367
|
+
if data is None:
|
|
368
|
+
try:
|
|
369
|
+
data = tomlkit.loads(config_string)
|
|
370
|
+
except TOMLKitError as toml_error:
|
|
371
|
+
raise ConfigError(
|
|
372
|
+
f"Invalid configuration text: {json_error}; {toml_error}"
|
|
373
|
+
) from toml_error
|
|
374
|
+
|
|
375
|
+
try:
|
|
376
|
+
config = Config.model_validate(data)
|
|
377
|
+
except ValidationError as e:
|
|
378
|
+
raise ConfigError(f"Invalid configuration text: {e}") from e
|
|
379
|
+
config.is_from_default_location = False
|
|
380
|
+
config.source_file = None
|
|
381
|
+
return config
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def save_config(config: Config, config_file: Path | None = None):
|
|
385
|
+
"""
|
|
386
|
+
Save configuration to config file.
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
config (Config): Config object to save.
|
|
390
|
+
config_file (Path | None): Path to the configuration file. If None, use default path.
|
|
391
|
+
"""
|
|
392
|
+
config_file = config_file or get_config_file()
|
|
393
|
+
logger.debug("Saving config to file: {file}", file=config_file)
|
|
394
|
+
config_file.parent.mkdir(parents=True, exist_ok=True)
|
|
395
|
+
config_data = config.model_dump(mode="json", exclude_none=True)
|
|
396
|
+
with open(config_file, "w", encoding="utf-8") as f:
|
|
397
|
+
if config_file.suffix.lower() == ".json":
|
|
398
|
+
f.write(json.dumps(config_data, ensure_ascii=False, indent=2))
|
|
399
|
+
else:
|
|
400
|
+
f.write(tomlkit.dumps(config_data)) # type: ignore[reportUnknownMemberType]
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def _migrate_json_config_to_toml() -> None:
|
|
404
|
+
old_json_config_file = get_share_dir() / "config.json"
|
|
405
|
+
new_toml_config_file = get_share_dir() / "config.toml"
|
|
406
|
+
|
|
407
|
+
if not old_json_config_file.exists():
|
|
408
|
+
return
|
|
409
|
+
if new_toml_config_file.exists():
|
|
410
|
+
return
|
|
411
|
+
|
|
412
|
+
logger.info(
|
|
413
|
+
"Migrating legacy config file from {old} to {new}",
|
|
414
|
+
old=old_json_config_file,
|
|
415
|
+
new=new_toml_config_file,
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
try:
|
|
419
|
+
with open(old_json_config_file, encoding="utf-8") as f:
|
|
420
|
+
data = json.load(f)
|
|
421
|
+
config = Config.model_validate(data)
|
|
422
|
+
except json.JSONDecodeError as e:
|
|
423
|
+
logger.warning(
|
|
424
|
+
"Ignoring invalid legacy JSON config file {file}: {err}",
|
|
425
|
+
file=old_json_config_file,
|
|
426
|
+
err=e,
|
|
427
|
+
)
|
|
428
|
+
config = get_default_config()
|
|
429
|
+
except ValidationError as e:
|
|
430
|
+
logger.warning(
|
|
431
|
+
"Ignoring incompatible legacy config file {file}: {err}",
|
|
432
|
+
file=old_json_config_file,
|
|
433
|
+
err=e,
|
|
434
|
+
)
|
|
435
|
+
config = get_default_config()
|
|
436
|
+
|
|
437
|
+
# Write new TOML config, then keep a backup of the original JSON file.
|
|
438
|
+
save_config(config, new_toml_config_file)
|
|
439
|
+
backup_path = _next_legacy_config_backup_path(old_json_config_file)
|
|
440
|
+
old_json_config_file.replace(backup_path)
|
|
441
|
+
logger.info("Legacy config backed up to {file}", file=backup_path)
|
|
442
|
+
|
|
443
|
+
|
|
444
|
+
def _next_legacy_config_backup_path(config_file: Path) -> Path:
|
|
445
|
+
backup_path = config_file.with_name("config.json.bak")
|
|
446
|
+
if not backup_path.exists():
|
|
447
|
+
return backup_path
|
|
448
|
+
index = 1
|
|
449
|
+
while True:
|
|
450
|
+
candidate = config_file.with_name(f"config.json.bak.{index}")
|
|
451
|
+
if not candidate.exists():
|
|
452
|
+
return candidate
|
|
453
|
+
index += 1
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from functools import cache
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
NAME = "Pythinker CLI"
|
|
7
|
+
|
|
8
|
+
if TYPE_CHECKING:
|
|
9
|
+
VERSION: str
|
|
10
|
+
USER_AGENT: str
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
@cache
|
|
14
|
+
def get_version() -> str:
|
|
15
|
+
from importlib import metadata
|
|
16
|
+
|
|
17
|
+
return metadata.version("pythinker-code")
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
@cache
|
|
21
|
+
def get_user_agent() -> str:
|
|
22
|
+
return f"PythinkerCLI/{get_version()}"
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def __getattr__(name: str) -> str:
|
|
26
|
+
if name == "VERSION":
|
|
27
|
+
return get_version()
|
|
28
|
+
if name == "USER_AGENT":
|
|
29
|
+
return get_user_agent()
|
|
30
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
__all__ = ["NAME", "VERSION", "USER_AGENT", "get_version", "get_user_agent"]
|