pythinker-code 0.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pythinker_code/CHANGELOG.md +60 -0
- pythinker_code/__init__.py +0 -0
- pythinker_code/__main__.py +97 -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 +301 -0
- pythinker_code/acp/mcp.py +46 -0
- pythinker_code/acp/server.py +497 -0
- pythinker_code/acp/session.py +502 -0
- pythinker_code/acp/tools.py +174 -0
- pythinker_code/acp/types.py +13 -0
- pythinker_code/acp/version.py +45 -0
- pythinker_code/agents/default/agent.yaml +55 -0
- pythinker_code/agents/default/code_reviewer.yaml +47 -0
- pythinker_code/agents/default/coder.yaml +42 -0
- pythinker_code/agents/default/debugger.yaml +35 -0
- pythinker_code/agents/default/explore.yaml +59 -0
- pythinker_code/agents/default/implementer.yaml +46 -0
- pythinker_code/agents/default/plan.yaml +42 -0
- pythinker_code/agents/default/review.yaml +47 -0
- pythinker_code/agents/default/security_reviewer.yaml +37 -0
- pythinker_code/agents/default/system.md +192 -0
- pythinker_code/agents/default/verifier.yaml +46 -0
- pythinker_code/agents/okabe/agent.yaml +22 -0
- pythinker_code/agentspec.py +163 -0
- pythinker_code/app.py +847 -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/github_feedback.py +228 -0
- pythinker_code/auth/lm_studio.py +418 -0
- pythinker_code/auth/minimax.py +203 -0
- pythinker_code/auth/oauth.py +1145 -0
- pythinker_code/auth/ollama.py +293 -0
- pythinker_code/auth/openai.py +783 -0
- pythinker_code/auth/opencode_go.py +203 -0
- pythinker_code/auth/openrouter.py +225 -0
- pythinker_code/auth/platforms.py +475 -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 +668 -0
- pythinker_code/background/models.py +118 -0
- pythinker_code/background/store.py +243 -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 +268 -0
- pythinker_code/cli/debug.py +11 -0
- pythinker_code/cli/export.py +322 -0
- pythinker_code/cli/info.py +62 -0
- pythinker_code/cli/mcp.py +362 -0
- pythinker_code/cli/plugin.py +351 -0
- pythinker_code/cli/review.py +74 -0
- pythinker_code/cli/secscan.py +11 -0
- pythinker_code/cli/security_scan.py +35 -0
- pythinker_code/cli/toad.py +74 -0
- pythinker_code/cli/update.py +26 -0
- pythinker_code/cli/vis.py +38 -0
- pythinker_code/cli/web.py +80 -0
- pythinker_code/config.py +511 -0
- pythinker_code/constant.py +33 -0
- pythinker_code/events.py +106 -0
- pythinker_code/exception.py +43 -0
- pythinker_code/extensions.py +151 -0
- pythinker_code/hooks/__init__.py +4 -0
- pythinker_code/hooks/config.py +34 -0
- pythinker_code/hooks/engine.py +383 -0
- pythinker_code/hooks/events.py +190 -0
- pythinker_code/hooks/runner.py +92 -0
- pythinker_code/llm.py +441 -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 +166 -0
- pythinker_code/plugin/tool.py +173 -0
- pythinker_code/prompt_templates.py +181 -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 +552 -0
- pythinker_code/soul/approval.py +267 -0
- pythinker_code/soul/btw.py +220 -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/permission.py +368 -0
- pythinker_code/soul/pythinkersoul.py +1763 -0
- pythinker_code/soul/slash.py +340 -0
- pythinker_code/soul/toolset.py +826 -0
- pythinker_code/subagents/__init__.py +21 -0
- pythinker_code/subagents/builder.py +43 -0
- pythinker_code/subagents/core.py +86 -0
- pythinker_code/subagents/discovery.py +234 -0
- pythinker_code/subagents/git_context.py +172 -0
- pythinker_code/subagents/models.py +56 -0
- pythinker_code/subagents/output.py +71 -0
- pythinker_code/subagents/registry.py +28 -0
- pythinker_code/subagents/runner.py +442 -0
- pythinker_code/subagents/store.py +200 -0
- pythinker_code/telemetry/__init__.py +217 -0
- pythinker_code/telemetry/config.py +113 -0
- pythinker_code/telemetry/crash.py +191 -0
- pythinker_code/telemetry/errors.py +113 -0
- pythinker_code/telemetry/metrics.py +208 -0
- pythinker_code/telemetry/otel.py +303 -0
- pythinker_code/telemetry/sentry.py +212 -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 +326 -0
- pythinker_code/tools/agent/description.md +65 -0
- pythinker_code/tools/ask_user/__init__.py +162 -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 +31 -0
- pythinker_code/tools/file/glob.md +17 -0
- pythinker_code/tools/file/glob.py +163 -0
- pythinker_code/tools/file/grep.md +6 -0
- pythinker_code/tools/file/grep_local.py +904 -0
- pythinker_code/tools/file/plan_mode.py +45 -0
- pythinker_code/tools/file/read.md +16 -0
- pythinker_code/tools/file/read.py +303 -0
- pythinker_code/tools/file/read_media.md +24 -0
- pythinker_code/tools/file/read_media.py +220 -0
- pythinker_code/tools/file/replace.md +7 -0
- pythinker_code/tools/file/replace.py +204 -0
- pythinker_code/tools/file/utils.py +257 -0
- pythinker_code/tools/file/write.md +5 -0
- pythinker_code/tools/file/write.py +186 -0
- pythinker_code/tools/plan/__init__.py +362 -0
- pythinker_code/tools/plan/description.md +29 -0
- pythinker_code/tools/plan/enter.py +193 -0
- pythinker_code/tools/plan/enter_description.md +35 -0
- pythinker_code/tools/plan/handoff.py +69 -0
- pythinker_code/tools/plan/heroes.py +277 -0
- pythinker_code/tools/shell/__init__.py +263 -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 +200 -0
- pythinker_code/tools/web/__init__.py +4 -0
- pythinker_code/tools/web/fetch.md +1 -0
- pythinker_code/tools/web/fetch.py +261 -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 +1806 -0
- pythinker_code/ui/shell/components/__init__.py +110 -0
- pythinker_code/ui/shell/components/base.py +25 -0
- pythinker_code/ui/shell/components/bash_execution.py +249 -0
- pythinker_code/ui/shell/components/bordered_loader.py +62 -0
- pythinker_code/ui/shell/components/diff.py +308 -0
- pythinker_code/ui/shell/components/footer.py +231 -0
- pythinker_code/ui/shell/components/key_hints.py +27 -0
- pythinker_code/ui/shell/components/messages.py +152 -0
- pythinker_code/ui/shell/components/render_utils.py +198 -0
- pythinker_code/ui/shell/components/settings_list.py +369 -0
- pythinker_code/ui/shell/components/special_messages.py +125 -0
- pythinker_code/ui/shell/components/tool_execution.py +261 -0
- pythinker_code/ui/shell/console.py +109 -0
- pythinker_code/ui/shell/debug.py +190 -0
- pythinker_code/ui/shell/echo.py +30 -0
- pythinker_code/ui/shell/export_import.py +117 -0
- pythinker_code/ui/shell/keyboard.py +300 -0
- pythinker_code/ui/shell/keymap.py +84 -0
- pythinker_code/ui/shell/mcp_status.py +112 -0
- pythinker_code/ui/shell/model_picker.py +318 -0
- pythinker_code/ui/shell/oauth.py +273 -0
- pythinker_code/ui/shell/placeholders.py +578 -0
- pythinker_code/ui/shell/prompt.py +2888 -0
- pythinker_code/ui/shell/replay.py +215 -0
- pythinker_code/ui/shell/selector.py +364 -0
- pythinker_code/ui/shell/selectors/__init__.py +38 -0
- pythinker_code/ui/shell/selectors/extension.py +37 -0
- pythinker_code/ui/shell/selectors/oauth.py +63 -0
- pythinker_code/ui/shell/selectors/settings.py +349 -0
- pythinker_code/ui/shell/selectors/show_images.py +29 -0
- pythinker_code/ui/shell/selectors/theme.py +28 -0
- pythinker_code/ui/shell/selectors/thinking.py +42 -0
- pythinker_code/ui/shell/session_picker.py +227 -0
- pythinker_code/ui/shell/setup.py +212 -0
- pythinker_code/ui/shell/slash.py +1433 -0
- pythinker_code/ui/shell/spinner_words.py +222 -0
- pythinker_code/ui/shell/startup.py +32 -0
- pythinker_code/ui/shell/task_browser.py +486 -0
- pythinker_code/ui/shell/tool_renderers/__init__.py +197 -0
- pythinker_code/ui/shell/tool_renderers/_render_utils.py +168 -0
- pythinker_code/ui/shell/tool_renderers/agent.py +140 -0
- pythinker_code/ui/shell/tool_renderers/ask_user.py +93 -0
- pythinker_code/ui/shell/tool_renderers/background.py +144 -0
- pythinker_code/ui/shell/tool_renderers/bash.py +103 -0
- pythinker_code/ui/shell/tool_renderers/edit.py +163 -0
- pythinker_code/ui/shell/tool_renderers/find.py +81 -0
- pythinker_code/ui/shell/tool_renderers/generic.py +60 -0
- pythinker_code/ui/shell/tool_renderers/grep.py +98 -0
- pythinker_code/ui/shell/tool_renderers/plan.py +98 -0
- pythinker_code/ui/shell/tool_renderers/read.py +103 -0
- pythinker_code/ui/shell/tool_renderers/think.py +66 -0
- pythinker_code/ui/shell/tool_renderers/todo.py +164 -0
- pythinker_code/ui/shell/tool_renderers/web.py +128 -0
- pythinker_code/ui/shell/tool_renderers/write.py +102 -0
- pythinker_code/ui/shell/update.py +352 -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 +539 -0
- pythinker_code/ui/shell/visualize/_blocks.py +802 -0
- pythinker_code/ui/shell/visualize/_btw_panel.py +227 -0
- pythinker_code/ui/shell/visualize/_input_router.py +48 -0
- pythinker_code/ui/shell/visualize/_interactive.py +531 -0
- pythinker_code/ui/shell/visualize/_live_view.py +891 -0
- pythinker_code/ui/shell/visualize/_question_panel.py +586 -0
- pythinker_code/ui/shell/visualize/_worklog.py +245 -0
- pythinker_code/ui/theme.py +395 -0
- pythinker_code/ui/tui_config.py +82 -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 +38 -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 +935 -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 +83 -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 +714 -0
- pythinker_code/vis/api/statistics.py +209 -0
- pythinker_code/vis/api/system.py +19 -0
- pythinker_code/vis/app.py +199 -0
- pythinker_code/vis/static/assets/highlighted-body-B3W2YXNL-CY1rtwrX.js +1 -0
- pythinker_code/vis/static/assets/index-DSRInNnm.css +1 -0
- pythinker_code/vis/static/assets/index-DgmTI2M_.js +185 -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 +217 -0
- pythinker_code/web/api/open_in.py +233 -0
- pythinker_code/web/api/sessions.py +1256 -0
- pythinker_code/web/app.py +449 -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-DpSMr1jx.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-DpsahJyV.js +1 -0
- pythinker_code/web/static/assets/architectureDiagram-VXUJARFQ-DqiRv9Eg.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-WgtUvqbp.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-rK0RPuZd.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-B0rlvkH-.js +1 -0
- pythinker_code/web/static/assets/chunk-4BX2VUAB-DIkMuLV-.js +1 -0
- pythinker_code/web/static/assets/chunk-55IACEB6-CORdm4k4.js +1 -0
- pythinker_code/web/static/assets/chunk-B4BG7PRW-D9xDhwHO.js +165 -0
- pythinker_code/web/static/assets/chunk-DI55MBZ5-BDmF9Bh-.js +220 -0
- pythinker_code/web/static/assets/chunk-FMBD7UC4-BCse_HmM.js +15 -0
- pythinker_code/web/static/assets/chunk-QN33PNHL-DCpBmTzA.js +1 -0
- pythinker_code/web/static/assets/chunk-QZHKN3VN-BqLuqobw.js +1 -0
- pythinker_code/web/static/assets/chunk-TZMSLE5B-8K2ogOKS.js +1 -0
- pythinker_code/web/static/assets/clarity-D53aC0YG.js +1 -0
- pythinker_code/web/static/assets/classDiagram-2ON5EDUG-D_ZHSii2.js +1 -0
- pythinker_code/web/static/assets/classDiagram-v2-WZHVMYZB-D_ZHSii2.js +1 -0
- pythinker_code/web/static/assets/clojure-P80f7IUj.js +1 -0
- pythinker_code/web/static/assets/clone-GSXejyY1.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-DWTFYA28.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-BRI7ES-N.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-B6BxUuKW.js +321 -0
- pythinker_code/web/static/assets/d-85-TOEBH.js +1 -0
- pythinker_code/web/static/assets/dagre-6UL2VRFP-Ci5GdWfi.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-0hhAylV4.js +24 -0
- pythinker_code/web/static/assets/diagram-QEK2KX5R-8fxgaW6d.js +43 -0
- pythinker_code/web/static/assets/diagram-S2PKOQOG-FRr0_atE.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-B3T-hJUM.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-D0S3u7ot.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-CHrN2a23.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-CfcXZWg0.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-8jMJwCqE.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-BXrFnzMy.js +153 -0
- pythinker_code/web/static/assets/index-BpoLgcEt.js +1 -0
- pythinker_code/web/static/assets/index-BrfQJnRD.js +5 -0
- pythinker_code/web/static/assets/index-C4gFzubz.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/infoDiagram-WHAUD3N6-DdxonBf3.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-BXf4aQei.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-DLpPPOu8.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-DH73UoAH.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-bAer2-sK.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-CuqbwKXv.js +465 -0
- pythinker_code/web/static/assets/mermaid-mWjccvbQ.js +1 -0
- pythinker_code/web/static/assets/mermaid.core-Nx-rTKiV.js +191 -0
- pythinker_code/web/static/assets/min-DbfD8Ywu.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-C6l761Ue.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-fNg41mT9.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-DJz3Kx87.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-B4SbrfE9.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-CoSUjLAG.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-PjhBNHi3.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-DOwESt8-.js +1 -0
- pythinker_code/web/static/assets/stateDiagram-v2-4FDKWEC3-yl3OHWiP.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-CkCLnAgi.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-CZS5XwTf.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-DkqqHNLh.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 +433 -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 +1072 -0
- pythinker_code/wire/types.py +698 -0
- pythinker_code-0.8.0.dist-info/METADATA +706 -0
- pythinker_code-0.8.0.dist-info/RECORD +790 -0
- pythinker_code-0.8.0.dist-info/WHEEL +4 -0
- pythinker_code-0.8.0.dist-info/entry_points.txt +4 -0
- pythinker_code-0.8.0.dist-info/licenses/LICENSE +202 -0
- pythinker_code-0.8.0.dist-info/licenses/NOTICE +14 -0
pythinker_code/config.py
ADDED
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import json
|
|
5
|
+
import os
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Literal, Self
|
|
8
|
+
|
|
9
|
+
import tomlkit
|
|
10
|
+
from pydantic import (
|
|
11
|
+
AliasChoices,
|
|
12
|
+
BaseModel,
|
|
13
|
+
Field,
|
|
14
|
+
SecretStr,
|
|
15
|
+
ValidationError,
|
|
16
|
+
field_serializer,
|
|
17
|
+
model_validator,
|
|
18
|
+
)
|
|
19
|
+
from tomlkit.exceptions import TOMLKitError
|
|
20
|
+
|
|
21
|
+
from pythinker_code.exception import ConfigError
|
|
22
|
+
from pythinker_code.hooks.config import HookDef
|
|
23
|
+
from pythinker_code.llm import ModelCapability, ProviderType
|
|
24
|
+
from pythinker_code.share import get_share_dir
|
|
25
|
+
from pythinker_code.utils.logging import logger
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class OAuthRef(BaseModel):
|
|
29
|
+
"""Reference to OAuth credentials stored outside the config file."""
|
|
30
|
+
|
|
31
|
+
storage: Literal["keyring", "file"] = "file"
|
|
32
|
+
"""Credential storage backend."""
|
|
33
|
+
key: str
|
|
34
|
+
"""Storage key to locate OAuth credentials."""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class LLMProvider(BaseModel):
|
|
38
|
+
"""LLM provider configuration."""
|
|
39
|
+
|
|
40
|
+
type: ProviderType
|
|
41
|
+
"""Provider type"""
|
|
42
|
+
base_url: str
|
|
43
|
+
"""API base URL"""
|
|
44
|
+
api_key: SecretStr
|
|
45
|
+
"""API key"""
|
|
46
|
+
env: dict[str, str] | None = None
|
|
47
|
+
"""Environment variables to set before creating the provider instance"""
|
|
48
|
+
custom_headers: dict[str, str] | None = None
|
|
49
|
+
"""Custom headers to include in API requests"""
|
|
50
|
+
reasoning_key: str | None = None
|
|
51
|
+
"""Message field name carrying reasoning content for OpenAI-compatible APIs.
|
|
52
|
+
Applies to provider type ``openai_legacy``. Defaults to ``reasoning_content``
|
|
53
|
+
when unset. Use an empty string to disable reasoning round-tripping."""
|
|
54
|
+
oauth: OAuthRef | None = None
|
|
55
|
+
"""OAuth credential reference (do not store tokens here)."""
|
|
56
|
+
|
|
57
|
+
@field_serializer("api_key", when_used="json")
|
|
58
|
+
def dump_secret(self, v: SecretStr):
|
|
59
|
+
return v.get_secret_value()
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class LLMModel(BaseModel):
|
|
63
|
+
"""LLM model configuration."""
|
|
64
|
+
|
|
65
|
+
provider: str
|
|
66
|
+
"""Provider name"""
|
|
67
|
+
model: str
|
|
68
|
+
"""Model name"""
|
|
69
|
+
max_context_size: int
|
|
70
|
+
"""Maximum context size (unit: tokens)"""
|
|
71
|
+
capabilities: set[ModelCapability] | None = None
|
|
72
|
+
"""Model capabilities"""
|
|
73
|
+
display_name: str | None = None
|
|
74
|
+
"""Human-readable model name (sourced from the provider's models API when available)"""
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class LoopControl(BaseModel):
|
|
78
|
+
"""Agent loop control configuration."""
|
|
79
|
+
|
|
80
|
+
max_steps_per_turn: int = Field(
|
|
81
|
+
default=1000,
|
|
82
|
+
ge=1,
|
|
83
|
+
validation_alias=AliasChoices("max_steps_per_turn", "max_steps_per_run"),
|
|
84
|
+
)
|
|
85
|
+
"""Maximum number of steps in one turn"""
|
|
86
|
+
max_retries_per_step: int = Field(default=3, ge=1)
|
|
87
|
+
"""Maximum number of retries in one step"""
|
|
88
|
+
max_ralph_iterations: int = Field(default=0, ge=-1)
|
|
89
|
+
"""Extra iterations after the first turn in Ralph mode. Use -1 for unlimited."""
|
|
90
|
+
reserved_context_size: int = Field(default=50_000, ge=1000)
|
|
91
|
+
"""Reserved token count for LLM response generation. Auto-compaction triggers when
|
|
92
|
+
either context_tokens + reserved_context_size >= max_context_size or
|
|
93
|
+
context_tokens >= max_context_size * compaction_trigger_ratio. Default is 50000."""
|
|
94
|
+
compaction_trigger_ratio: float = Field(default=0.85, ge=0.5, le=0.99)
|
|
95
|
+
"""Context usage ratio threshold for auto-compaction. Default is 0.85 (85%).
|
|
96
|
+
Auto-compaction triggers when context_tokens >= max_context_size * compaction_trigger_ratio
|
|
97
|
+
or when context_tokens + reserved_context_size >= max_context_size."""
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class BackgroundConfig(BaseModel):
|
|
101
|
+
"""Background task runtime configuration."""
|
|
102
|
+
|
|
103
|
+
max_running_tasks: int = Field(default=4, ge=1)
|
|
104
|
+
read_max_bytes: int = Field(default=30_000, ge=1024)
|
|
105
|
+
notification_tail_lines: int = Field(default=20, ge=1)
|
|
106
|
+
notification_tail_chars: int = Field(default=3_000, ge=256)
|
|
107
|
+
wait_poll_interval_ms: int = Field(default=500, ge=50)
|
|
108
|
+
worker_heartbeat_interval_ms: int = Field(default=5_000, ge=100)
|
|
109
|
+
worker_stale_after_ms: int = Field(default=15_000, ge=1000)
|
|
110
|
+
kill_grace_period_ms: int = Field(default=2_000, ge=100)
|
|
111
|
+
keep_alive_on_exit: bool = Field(
|
|
112
|
+
default=False,
|
|
113
|
+
description="Keep background tasks alive when CLI exits. Default: kill on exit.",
|
|
114
|
+
)
|
|
115
|
+
agent_task_timeout_s: int = Field(default=900, ge=60)
|
|
116
|
+
"""Maximum runtime in seconds for a background agent task. Default: 900 (15 min)."""
|
|
117
|
+
print_wait_ceiling_s: int = Field(default=3600, ge=1)
|
|
118
|
+
"""Hard ceiling for how long ``--print`` mode waits for background tasks before
|
|
119
|
+
killing them and exiting. The effective wait is
|
|
120
|
+
``min(max(active_task.timeout_s or agent_task_timeout_s), print_wait_ceiling_s)``.
|
|
121
|
+
Default: 3600 (1 hour)."""
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class NotificationConfig(BaseModel):
|
|
125
|
+
"""Notification runtime configuration."""
|
|
126
|
+
|
|
127
|
+
claim_stale_after_ms: int = Field(default=15_000, ge=1000)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class PythinkerAISearchConfig(BaseModel):
|
|
131
|
+
"""Pythinker AI Search configuration."""
|
|
132
|
+
|
|
133
|
+
base_url: str
|
|
134
|
+
"""Base URL for Pythinker AI Search service."""
|
|
135
|
+
api_key: SecretStr
|
|
136
|
+
"""API key for Pythinker AI Search service."""
|
|
137
|
+
custom_headers: dict[str, str] | None = None
|
|
138
|
+
"""Custom headers to include in API requests."""
|
|
139
|
+
oauth: OAuthRef | None = None
|
|
140
|
+
"""OAuth credential reference (do not store tokens here)."""
|
|
141
|
+
|
|
142
|
+
@field_serializer("api_key", when_used="json")
|
|
143
|
+
def dump_secret(self, v: SecretStr):
|
|
144
|
+
return v.get_secret_value()
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class PythinkerAIFetchConfig(BaseModel):
|
|
148
|
+
"""Pythinker AI Fetch configuration."""
|
|
149
|
+
|
|
150
|
+
base_url: str
|
|
151
|
+
"""Base URL for Pythinker AI Fetch service."""
|
|
152
|
+
api_key: SecretStr
|
|
153
|
+
"""API key for Pythinker AI Fetch service."""
|
|
154
|
+
custom_headers: dict[str, str] | None = None
|
|
155
|
+
"""Custom headers to include in API requests."""
|
|
156
|
+
oauth: OAuthRef | None = None
|
|
157
|
+
"""OAuth credential reference (do not store tokens here)."""
|
|
158
|
+
|
|
159
|
+
@field_serializer("api_key", when_used="json")
|
|
160
|
+
def dump_secret(self, v: SecretStr):
|
|
161
|
+
return v.get_secret_value()
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
class Services(BaseModel):
|
|
165
|
+
"""Services configuration."""
|
|
166
|
+
|
|
167
|
+
pythinker_ai_search: PythinkerAISearchConfig | None = None
|
|
168
|
+
"""Pythinker AI Search configuration."""
|
|
169
|
+
pythinker_ai_fetch: PythinkerAIFetchConfig | None = None
|
|
170
|
+
"""Pythinker AI Fetch configuration."""
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class FeedbackConfig(BaseModel):
|
|
174
|
+
"""User-submitted feedback endpoint configuration."""
|
|
175
|
+
|
|
176
|
+
endpoint_url: str = Field(
|
|
177
|
+
default="",
|
|
178
|
+
description=(
|
|
179
|
+
"Full URL for the /feedback slash command. Overrides the built-in "
|
|
180
|
+
"Pythinker platform endpoint when set."
|
|
181
|
+
),
|
|
182
|
+
)
|
|
183
|
+
api_key: SecretStr | None = Field(
|
|
184
|
+
default=None,
|
|
185
|
+
description="Optional bearer token for the feedback endpoint.",
|
|
186
|
+
)
|
|
187
|
+
custom_headers: dict[str, str] | None = Field(
|
|
188
|
+
default=None,
|
|
189
|
+
description="Optional extra headers for feedback endpoint requests.",
|
|
190
|
+
)
|
|
191
|
+
github_client_id: str = Field(
|
|
192
|
+
default="",
|
|
193
|
+
description=(
|
|
194
|
+
"Optional GitHub OAuth App client ID. When set, /feedback uses GitHub "
|
|
195
|
+
"device login and creates issues as the signed-in GitHub user."
|
|
196
|
+
),
|
|
197
|
+
)
|
|
198
|
+
github_repo: str = Field(
|
|
199
|
+
default="mohamed-elkholy95/Pythinker-Code",
|
|
200
|
+
description="GitHub owner/repo used by /feedback GitHub OAuth submissions.",
|
|
201
|
+
)
|
|
202
|
+
|
|
203
|
+
@field_serializer("api_key", when_used="json")
|
|
204
|
+
def dump_secret(self, v: SecretStr | None):
|
|
205
|
+
return v.get_secret_value() if v is not None else None
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class MCPClientConfig(BaseModel):
|
|
209
|
+
"""MCP client configuration."""
|
|
210
|
+
|
|
211
|
+
tool_call_timeout_ms: int = 60000
|
|
212
|
+
"""Timeout for tool calls in milliseconds."""
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
class TUIConfig(BaseModel):
|
|
216
|
+
"""TUI rendering style configuration."""
|
|
217
|
+
|
|
218
|
+
style: Literal["pythinker", "card"] = Field(
|
|
219
|
+
default="card",
|
|
220
|
+
description=(
|
|
221
|
+
"TUI rendering style. 'card' (default) uses highlighted user "
|
|
222
|
+
"messages and bordered tool cards. 'pythinker' keeps the legacy "
|
|
223
|
+
"worklog-based rendering. Override at runtime with the env var "
|
|
224
|
+
"PYTHINKER_TUI_STYLE=pythinker."
|
|
225
|
+
),
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
class MCPConfig(BaseModel):
|
|
230
|
+
"""MCP configuration."""
|
|
231
|
+
|
|
232
|
+
client: MCPClientConfig = Field(
|
|
233
|
+
default_factory=MCPClientConfig, description="MCP client configuration"
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class Config(BaseModel):
|
|
238
|
+
"""Main configuration structure."""
|
|
239
|
+
|
|
240
|
+
is_from_default_location: bool = Field(
|
|
241
|
+
default=False,
|
|
242
|
+
description="Whether the config was loaded from the default location",
|
|
243
|
+
exclude=True,
|
|
244
|
+
)
|
|
245
|
+
source_file: Path | None = Field(
|
|
246
|
+
default=None,
|
|
247
|
+
description="Path to the loaded config file. None when loaded from --config text.",
|
|
248
|
+
exclude=True,
|
|
249
|
+
)
|
|
250
|
+
default_model: str = Field(default="", description="Default model to use")
|
|
251
|
+
default_thinking: bool = Field(default=False, description="Default thinking mode")
|
|
252
|
+
default_yolo: bool = Field(default=False, description="Default yolo (auto-approve) mode")
|
|
253
|
+
skip_auto_prompt_injection: bool = Field(
|
|
254
|
+
default=False,
|
|
255
|
+
description=(
|
|
256
|
+
"If true, suppress the auto-mode system reminder. "
|
|
257
|
+
"Yolo mode does not inject a system reminder."
|
|
258
|
+
),
|
|
259
|
+
)
|
|
260
|
+
default_plan_mode: bool = Field(default=False, description="Default plan mode for new sessions")
|
|
261
|
+
default_editor: str = Field(
|
|
262
|
+
default="",
|
|
263
|
+
description="Default external editor command (e.g. 'vim', 'code --wait')",
|
|
264
|
+
)
|
|
265
|
+
theme: Literal["dark", "light"] = Field(
|
|
266
|
+
default="dark",
|
|
267
|
+
description="Terminal color theme. Use 'light' for light terminal backgrounds.",
|
|
268
|
+
)
|
|
269
|
+
show_thinking_stream: bool = Field(
|
|
270
|
+
default=True,
|
|
271
|
+
description=(
|
|
272
|
+
"If true, stream the raw reasoning text in the live area as a "
|
|
273
|
+
"6-line scrolling preview and commit the full reasoning markdown "
|
|
274
|
+
"to history when the block ends. Default true. Set to false to "
|
|
275
|
+
"show only the compact 'Thinking ...' indicator and a one-line "
|
|
276
|
+
"trace summary."
|
|
277
|
+
),
|
|
278
|
+
)
|
|
279
|
+
models: dict[str, LLMModel] = Field(default_factory=dict, description="List of LLM models")
|
|
280
|
+
providers: dict[str, LLMProvider] = Field(
|
|
281
|
+
default_factory=dict, description="List of LLM providers"
|
|
282
|
+
)
|
|
283
|
+
loop_control: LoopControl = Field(default_factory=LoopControl, description="Agent loop control")
|
|
284
|
+
background: BackgroundConfig = Field(
|
|
285
|
+
default_factory=BackgroundConfig, description="Background task configuration"
|
|
286
|
+
)
|
|
287
|
+
notifications: NotificationConfig = Field(
|
|
288
|
+
default_factory=NotificationConfig, description="Notification configuration"
|
|
289
|
+
)
|
|
290
|
+
services: Services = Field(default_factory=Services, description="Services configuration")
|
|
291
|
+
feedback: FeedbackConfig = Field(
|
|
292
|
+
default_factory=FeedbackConfig,
|
|
293
|
+
description="User-submitted feedback endpoint configuration",
|
|
294
|
+
)
|
|
295
|
+
mcp: MCPConfig = Field(default_factory=MCPConfig, description="MCP configuration")
|
|
296
|
+
tui: TUIConfig = Field(default_factory=TUIConfig, description="TUI rendering configuration")
|
|
297
|
+
hooks: list[HookDef] = Field(default_factory=list, description="Hook definitions") # pyright: ignore[reportUnknownVariableType]
|
|
298
|
+
merge_all_available_skills: bool = Field(
|
|
299
|
+
default=True,
|
|
300
|
+
description=(
|
|
301
|
+
"Merge skills from all existing brand directories (pythinker/claude/codex) "
|
|
302
|
+
"instead of using only the first one found. Defaults to true so users "
|
|
303
|
+
"who keep skills in multiple brand directories see everything out of "
|
|
304
|
+
"the box; set to false to restore the first-match-only behaviour."
|
|
305
|
+
),
|
|
306
|
+
)
|
|
307
|
+
extra_skill_dirs: list[str] = Field(
|
|
308
|
+
default_factory=list,
|
|
309
|
+
description=(
|
|
310
|
+
"Extra directories to discover skills from, added on top of the "
|
|
311
|
+
"built-in / user / project locations. Each entry may be an absolute "
|
|
312
|
+
"path, ``~``-prefixed (expanded against $HOME), or relative to the "
|
|
313
|
+
"project root (the nearest ``.git`` directory above the work dir). "
|
|
314
|
+
"Missing paths are silently skipped."
|
|
315
|
+
),
|
|
316
|
+
)
|
|
317
|
+
telemetry: bool = Field(
|
|
318
|
+
default=True,
|
|
319
|
+
description=(
|
|
320
|
+
"Enable anonymous telemetry to help improve pythinker-code. Set to false to opt out."
|
|
321
|
+
),
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
@model_validator(mode="after")
|
|
325
|
+
def validate_model(self) -> Self:
|
|
326
|
+
if self.default_model and self.default_model not in self.models:
|
|
327
|
+
raise ValueError(f"Default model {self.default_model} not found in models")
|
|
328
|
+
for model in self.models.values():
|
|
329
|
+
if model.provider not in self.providers:
|
|
330
|
+
raise ValueError(f"Provider {model.provider} not found in providers")
|
|
331
|
+
return self
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def get_config_file() -> Path:
|
|
335
|
+
"""Get the configuration file path."""
|
|
336
|
+
return get_share_dir() / "config.toml"
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def get_default_config() -> Config:
|
|
340
|
+
"""Get the default configuration."""
|
|
341
|
+
return Config(
|
|
342
|
+
default_model="",
|
|
343
|
+
models={},
|
|
344
|
+
providers={},
|
|
345
|
+
services=Services(),
|
|
346
|
+
)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def load_config(config_file: Path | None = None) -> Config:
|
|
350
|
+
"""
|
|
351
|
+
Load configuration from config file.
|
|
352
|
+
If the config file does not exist, create it with default configuration.
|
|
353
|
+
|
|
354
|
+
Args:
|
|
355
|
+
config_file (Path | None): Path to the configuration file. If None, use default path.
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
Validated Config object.
|
|
359
|
+
|
|
360
|
+
Raises:
|
|
361
|
+
ConfigError: If the configuration file is invalid.
|
|
362
|
+
"""
|
|
363
|
+
default_config_file = get_config_file().expanduser().resolve(strict=False)
|
|
364
|
+
if config_file is None:
|
|
365
|
+
config_file = default_config_file
|
|
366
|
+
config_file = config_file.expanduser().resolve(strict=False)
|
|
367
|
+
is_default_config_file = config_file == default_config_file
|
|
368
|
+
logger.debug("Loading config from file: {file}", file=config_file)
|
|
369
|
+
|
|
370
|
+
# If the user hasn't provided an explicit config path, migrate legacy JSON config once.
|
|
371
|
+
if is_default_config_file and not config_file.exists():
|
|
372
|
+
_migrate_json_config_to_toml()
|
|
373
|
+
|
|
374
|
+
if not config_file.exists():
|
|
375
|
+
config = get_default_config()
|
|
376
|
+
logger.debug("No config file found, creating default config: {config}", config=config)
|
|
377
|
+
save_config(config, config_file)
|
|
378
|
+
config.is_from_default_location = is_default_config_file
|
|
379
|
+
config.source_file = config_file
|
|
380
|
+
return config
|
|
381
|
+
|
|
382
|
+
try:
|
|
383
|
+
config_text = config_file.read_text(encoding="utf-8")
|
|
384
|
+
if config_file.suffix.lower() == ".json":
|
|
385
|
+
data = json.loads(config_text)
|
|
386
|
+
else:
|
|
387
|
+
data = tomlkit.loads(config_text)
|
|
388
|
+
config = Config.model_validate(data)
|
|
389
|
+
except json.JSONDecodeError as e:
|
|
390
|
+
raise ConfigError(f"Invalid JSON in configuration file {config_file}: {e}") from e
|
|
391
|
+
except TOMLKitError as e:
|
|
392
|
+
raise ConfigError(f"Invalid TOML in configuration file {config_file}: {e}") from e
|
|
393
|
+
except ValidationError as e:
|
|
394
|
+
raise ConfigError(f"Invalid configuration file {config_file}: {e}") from e
|
|
395
|
+
config.is_from_default_location = is_default_config_file
|
|
396
|
+
config.source_file = config_file
|
|
397
|
+
return config
|
|
398
|
+
|
|
399
|
+
|
|
400
|
+
def load_config_from_string(config_string: str) -> Config:
|
|
401
|
+
"""
|
|
402
|
+
Load configuration from a TOML or JSON string.
|
|
403
|
+
|
|
404
|
+
Args:
|
|
405
|
+
config_string (str): TOML or JSON configuration text.
|
|
406
|
+
|
|
407
|
+
Returns:
|
|
408
|
+
Validated Config object.
|
|
409
|
+
|
|
410
|
+
Raises:
|
|
411
|
+
ConfigError: If the configuration text is invalid.
|
|
412
|
+
"""
|
|
413
|
+
if not config_string.strip():
|
|
414
|
+
raise ConfigError("Configuration text cannot be empty")
|
|
415
|
+
|
|
416
|
+
json_error: json.JSONDecodeError | None = None
|
|
417
|
+
try:
|
|
418
|
+
data = json.loads(config_string)
|
|
419
|
+
except json.JSONDecodeError as exc:
|
|
420
|
+
json_error = exc
|
|
421
|
+
data = None
|
|
422
|
+
|
|
423
|
+
if data is None:
|
|
424
|
+
try:
|
|
425
|
+
data = tomlkit.loads(config_string)
|
|
426
|
+
except TOMLKitError as toml_error:
|
|
427
|
+
raise ConfigError(
|
|
428
|
+
f"Invalid configuration text: {json_error}; {toml_error}"
|
|
429
|
+
) from toml_error
|
|
430
|
+
|
|
431
|
+
try:
|
|
432
|
+
config = Config.model_validate(data)
|
|
433
|
+
except ValidationError as e:
|
|
434
|
+
raise ConfigError(f"Invalid configuration text: {e}") from e
|
|
435
|
+
config.is_from_default_location = False
|
|
436
|
+
config.source_file = None
|
|
437
|
+
return config
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
def save_config(config: Config, config_file: Path | None = None):
|
|
441
|
+
"""
|
|
442
|
+
Save configuration to config file.
|
|
443
|
+
|
|
444
|
+
Args:
|
|
445
|
+
config (Config): Config object to save.
|
|
446
|
+
config_file (Path | None): Path to the configuration file. If None, use default path.
|
|
447
|
+
"""
|
|
448
|
+
config_file = config_file or get_config_file()
|
|
449
|
+
logger.debug("Saving config to file: {file}", file=config_file)
|
|
450
|
+
config_file.parent.mkdir(parents=True, exist_ok=True)
|
|
451
|
+
config_data = config.model_dump(mode="json", exclude_none=True)
|
|
452
|
+
with open(config_file, "w", encoding="utf-8") as f:
|
|
453
|
+
if config_file.suffix.lower() == ".json":
|
|
454
|
+
f.write(json.dumps(config_data, ensure_ascii=False, indent=2))
|
|
455
|
+
else:
|
|
456
|
+
f.write(tomlkit.dumps(config_data)) # type: ignore[reportUnknownMemberType]
|
|
457
|
+
with contextlib.suppress(OSError):
|
|
458
|
+
os.chmod(config_file, 0o600)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def _migrate_json_config_to_toml() -> None:
|
|
462
|
+
old_json_config_file = get_share_dir() / "config.json"
|
|
463
|
+
new_toml_config_file = get_share_dir() / "config.toml"
|
|
464
|
+
|
|
465
|
+
if not old_json_config_file.exists():
|
|
466
|
+
return
|
|
467
|
+
if new_toml_config_file.exists():
|
|
468
|
+
return
|
|
469
|
+
|
|
470
|
+
logger.info(
|
|
471
|
+
"Migrating legacy config file from {old} to {new}",
|
|
472
|
+
old=old_json_config_file,
|
|
473
|
+
new=new_toml_config_file,
|
|
474
|
+
)
|
|
475
|
+
|
|
476
|
+
try:
|
|
477
|
+
with open(old_json_config_file, encoding="utf-8") as f:
|
|
478
|
+
data = json.load(f)
|
|
479
|
+
config = Config.model_validate(data)
|
|
480
|
+
except json.JSONDecodeError as e:
|
|
481
|
+
logger.warning(
|
|
482
|
+
"Ignoring invalid legacy JSON config file {file}: {err}",
|
|
483
|
+
file=old_json_config_file,
|
|
484
|
+
err=e,
|
|
485
|
+
)
|
|
486
|
+
config = get_default_config()
|
|
487
|
+
except ValidationError as e:
|
|
488
|
+
logger.warning(
|
|
489
|
+
"Ignoring incompatible legacy config file {file}: {err}",
|
|
490
|
+
file=old_json_config_file,
|
|
491
|
+
err=e,
|
|
492
|
+
)
|
|
493
|
+
config = get_default_config()
|
|
494
|
+
|
|
495
|
+
# Write new TOML config, then keep a backup of the original JSON file.
|
|
496
|
+
save_config(config, new_toml_config_file)
|
|
497
|
+
backup_path = _next_legacy_config_backup_path(old_json_config_file)
|
|
498
|
+
old_json_config_file.replace(backup_path)
|
|
499
|
+
logger.info("Legacy config backed up to {file}", file=backup_path)
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def _next_legacy_config_backup_path(config_file: Path) -> Path:
|
|
503
|
+
backup_path = config_file.with_name("config.json.bak")
|
|
504
|
+
if not backup_path.exists():
|
|
505
|
+
return backup_path
|
|
506
|
+
index = 1
|
|
507
|
+
while True:
|
|
508
|
+
candidate = config_file.with_name(f"config.json.bak.{index}")
|
|
509
|
+
if not candidate.exists():
|
|
510
|
+
return candidate
|
|
511
|
+
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"]
|
pythinker_code/events.py
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
"""In-process event bus.
|
|
2
|
+
|
|
3
|
+
. A
|
|
4
|
+
minimal pub/sub primitive that extension/plugin code can use to broadcast
|
|
5
|
+
state changes (assistant message, tool call started/ended, model changed,
|
|
6
|
+
etc.) without holding direct references to listeners.
|
|
7
|
+
|
|
8
|
+
Handlers may be sync or async. Async handlers are scheduled on the running
|
|
9
|
+
event loop; if no loop is running they're executed via ``asyncio.run``
|
|
10
|
+
(useful in tests). Exceptions in any handler are logged and swallowed so a
|
|
11
|
+
broken extension can't take down the host.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import asyncio
|
|
17
|
+
import inspect
|
|
18
|
+
from collections.abc import Callable
|
|
19
|
+
from typing import Any
|
|
20
|
+
|
|
21
|
+
from pythinker_code.utils.logging import logger
|
|
22
|
+
|
|
23
|
+
__all__ = [
|
|
24
|
+
"EventBus",
|
|
25
|
+
"EventHandler",
|
|
26
|
+
"create_event_bus",
|
|
27
|
+
]
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
EventHandler = Callable[[Any], Any]
|
|
31
|
+
|
|
32
|
+
_background_tasks: set[asyncio.Task[None]] = set()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class EventBus:
|
|
36
|
+
"""Channel-keyed pub/sub.
|
|
37
|
+
|
|
38
|
+
Use :func:`create_event_bus` rather than constructing directly — that
|
|
39
|
+
keeps the constructor signature private and matches Pythinker's factory.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
def __init__(self) -> None:
|
|
43
|
+
self._handlers: dict[str, list[EventHandler]] = {}
|
|
44
|
+
|
|
45
|
+
def emit(self, channel: str, data: Any = None) -> None:
|
|
46
|
+
"""Fire *data* on *channel*. Handlers run sequentially; failures log."""
|
|
47
|
+
for handler in list(self._handlers.get(channel, ())):
|
|
48
|
+
self._dispatch(channel, handler, data)
|
|
49
|
+
|
|
50
|
+
def on(self, channel: str, handler: EventHandler) -> Callable[[], None]:
|
|
51
|
+
"""Subscribe *handler* to *channel*. Returns an unsubscribe fn."""
|
|
52
|
+
self._handlers.setdefault(channel, []).append(handler)
|
|
53
|
+
|
|
54
|
+
def _off() -> None:
|
|
55
|
+
handlers = self._handlers.get(channel)
|
|
56
|
+
if not handlers:
|
|
57
|
+
return
|
|
58
|
+
try:
|
|
59
|
+
handlers.remove(handler)
|
|
60
|
+
except ValueError:
|
|
61
|
+
return
|
|
62
|
+
if not handlers:
|
|
63
|
+
self._handlers.pop(channel, None)
|
|
64
|
+
|
|
65
|
+
return _off
|
|
66
|
+
|
|
67
|
+
def clear(self) -> None:
|
|
68
|
+
"""Remove every handler on every channel. Intended for tests."""
|
|
69
|
+
self._handlers.clear()
|
|
70
|
+
|
|
71
|
+
# ----------------------------------------------------------------------
|
|
72
|
+
|
|
73
|
+
def _dispatch(self, channel: str, handler: EventHandler, data: Any) -> None:
|
|
74
|
+
try:
|
|
75
|
+
result = handler(data)
|
|
76
|
+
except Exception: # noqa: BLE001 — extension must not crash host
|
|
77
|
+
logger.exception("Event handler error on channel '{channel}'", channel=channel)
|
|
78
|
+
return
|
|
79
|
+
if inspect.iscoroutine(result):
|
|
80
|
+
self._schedule_coro(channel, result)
|
|
81
|
+
|
|
82
|
+
@staticmethod
|
|
83
|
+
def _schedule_coro(channel: str, coro: Any) -> None:
|
|
84
|
+
async def _runner() -> None:
|
|
85
|
+
try:
|
|
86
|
+
await coro
|
|
87
|
+
except Exception: # noqa: BLE001
|
|
88
|
+
logger.exception("Async event handler error on '{channel}'", channel=channel)
|
|
89
|
+
|
|
90
|
+
try:
|
|
91
|
+
loop = asyncio.get_running_loop()
|
|
92
|
+
except RuntimeError:
|
|
93
|
+
# No running loop — execute inline. Mostly hit in tests.
|
|
94
|
+
try:
|
|
95
|
+
asyncio.run(_runner())
|
|
96
|
+
except Exception: # noqa: BLE001
|
|
97
|
+
logger.exception("Failed to run async handler for '{channel}'", channel=channel)
|
|
98
|
+
return
|
|
99
|
+
_task = loop.create_task(_runner())
|
|
100
|
+
_background_tasks.add(_task)
|
|
101
|
+
_task.add_done_callback(_background_tasks.discard)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def create_event_bus() -> EventBus:
|
|
105
|
+
"""Factory mirroring the ``createEventBus()``."""
|
|
106
|
+
return EventBus()
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class PythinkerCLIException(Exception):
|
|
5
|
+
"""Base exception class for Pythinker CLI."""
|
|
6
|
+
|
|
7
|
+
pass
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ConfigError(PythinkerCLIException, ValueError):
|
|
11
|
+
"""Configuration error."""
|
|
12
|
+
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AgentSpecError(PythinkerCLIException, ValueError):
|
|
17
|
+
"""Agent specification error."""
|
|
18
|
+
|
|
19
|
+
pass
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class InvalidToolError(PythinkerCLIException, ValueError):
|
|
23
|
+
"""Invalid tool error."""
|
|
24
|
+
|
|
25
|
+
pass
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class SystemPromptTemplateError(PythinkerCLIException, ValueError):
|
|
29
|
+
"""System prompt template error."""
|
|
30
|
+
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class MCPConfigError(PythinkerCLIException, ValueError):
|
|
35
|
+
"""MCP config error."""
|
|
36
|
+
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class MCPRuntimeError(PythinkerCLIException, RuntimeError):
|
|
41
|
+
"""MCP runtime error."""
|
|
42
|
+
|
|
43
|
+
pass
|