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,173 @@
|
|
|
1
|
+
"""Plugin tool wrapper — runs plugin-declared tools as subprocesses."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import asyncio
|
|
6
|
+
import json
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import TYPE_CHECKING, Any
|
|
9
|
+
|
|
10
|
+
from loguru import logger
|
|
11
|
+
from pythinker_core.tooling import CallableTool, ToolError, ToolOk
|
|
12
|
+
from pythinker_core.tooling.error import ToolRuntimeError
|
|
13
|
+
|
|
14
|
+
from pythinker_code.plugin import PluginToolSpec
|
|
15
|
+
from pythinker_code.tools.utils import ToolRejectedError
|
|
16
|
+
from pythinker_code.utils.subprocess_env import get_clean_env
|
|
17
|
+
from pythinker_code.wire.types import ToolReturnValue
|
|
18
|
+
|
|
19
|
+
if TYPE_CHECKING:
|
|
20
|
+
from pythinker_code.config import Config
|
|
21
|
+
from pythinker_code.soul.approval import Approval
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _get_host_values(config: Config) -> dict[str, str]:
|
|
25
|
+
"""Extract current host values (api_key, base_url) from config.
|
|
26
|
+
|
|
27
|
+
Reads the latest provider credentials, which may have been
|
|
28
|
+
refreshed by OAuth since plugin install time.
|
|
29
|
+
"""
|
|
30
|
+
from pythinker_code.auth.oauth import OAuthManager
|
|
31
|
+
from pythinker_code.plugin.manager import collect_host_values
|
|
32
|
+
|
|
33
|
+
oauth = OAuthManager(config)
|
|
34
|
+
return collect_host_values(config, oauth)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class PluginTool(CallableTool):
|
|
38
|
+
"""A tool that executes a plugin command in a subprocess.
|
|
39
|
+
|
|
40
|
+
Parameters are passed via stdin as JSON.
|
|
41
|
+
stdout is captured as the tool result.
|
|
42
|
+
Host credentials are injected as environment variables at runtime
|
|
43
|
+
(not baked into config files) to handle OAuth token refresh.
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
tool_spec: PluginToolSpec,
|
|
49
|
+
plugin_dir: Path,
|
|
50
|
+
*,
|
|
51
|
+
inject: dict[str, str],
|
|
52
|
+
config: Config,
|
|
53
|
+
approval: Approval | None = None,
|
|
54
|
+
**kwargs: Any,
|
|
55
|
+
):
|
|
56
|
+
super().__init__(
|
|
57
|
+
name=tool_spec.name,
|
|
58
|
+
description=tool_spec.description,
|
|
59
|
+
parameters=tool_spec.parameters or {"type": "object", "properties": {}},
|
|
60
|
+
**kwargs,
|
|
61
|
+
)
|
|
62
|
+
self._command = tool_spec.command
|
|
63
|
+
self._plugin_dir = plugin_dir
|
|
64
|
+
self._inject = inject # e.g. {"pythinkerCodeAPIKey": "api_key"}
|
|
65
|
+
self._config = config
|
|
66
|
+
self._approval = approval
|
|
67
|
+
|
|
68
|
+
def _build_env(self) -> dict[str, str]:
|
|
69
|
+
"""Build env vars with fresh host credentials for the subprocess."""
|
|
70
|
+
env = get_clean_env()
|
|
71
|
+
if self._inject:
|
|
72
|
+
host_values = _get_host_values(self._config)
|
|
73
|
+
for target_key, source_key in self._inject.items():
|
|
74
|
+
if source_key in host_values:
|
|
75
|
+
# Inject as env var using the plugin's config key name
|
|
76
|
+
# e.g. pythinkerCodeAPIKey=<fresh api_key>
|
|
77
|
+
env[target_key] = host_values[source_key]
|
|
78
|
+
return env
|
|
79
|
+
|
|
80
|
+
async def __call__(self, *args: Any, **kwargs: Any) -> ToolReturnValue:
|
|
81
|
+
if self._approval is not None:
|
|
82
|
+
description = f"Run plugin tool `{self.name}`."
|
|
83
|
+
if not await self._approval.request(self.name, f"plugin:{self.name}", description):
|
|
84
|
+
return ToolRejectedError()
|
|
85
|
+
|
|
86
|
+
params_json = json.dumps(kwargs, ensure_ascii=False)
|
|
87
|
+
|
|
88
|
+
try:
|
|
89
|
+
proc = await asyncio.create_subprocess_exec(
|
|
90
|
+
*self._command,
|
|
91
|
+
stdin=asyncio.subprocess.PIPE,
|
|
92
|
+
stdout=asyncio.subprocess.PIPE,
|
|
93
|
+
stderr=asyncio.subprocess.PIPE,
|
|
94
|
+
cwd=str(self._plugin_dir),
|
|
95
|
+
env=self._build_env(),
|
|
96
|
+
)
|
|
97
|
+
except Exception as exc:
|
|
98
|
+
return ToolRuntimeError(str(exc))
|
|
99
|
+
|
|
100
|
+
try:
|
|
101
|
+
stdout, stderr = await asyncio.wait_for(
|
|
102
|
+
proc.communicate(input=params_json.encode("utf-8")),
|
|
103
|
+
timeout=120,
|
|
104
|
+
)
|
|
105
|
+
except asyncio.CancelledError:
|
|
106
|
+
proc.kill()
|
|
107
|
+
await proc.wait()
|
|
108
|
+
raise
|
|
109
|
+
except TimeoutError:
|
|
110
|
+
proc.kill()
|
|
111
|
+
await proc.wait()
|
|
112
|
+
return ToolError(
|
|
113
|
+
message=f"Plugin tool '{self.name}' timed out after 120s.",
|
|
114
|
+
brief="Timeout",
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
output = stdout.decode("utf-8", errors="replace").strip()
|
|
118
|
+
err_output = stderr.decode("utf-8", errors="replace").strip()
|
|
119
|
+
|
|
120
|
+
if proc.returncode != 0:
|
|
121
|
+
error_msg = err_output or output or f"Exit code {proc.returncode}"
|
|
122
|
+
return ToolError(
|
|
123
|
+
message=f"Plugin tool '{self.name}' failed: {error_msg}",
|
|
124
|
+
brief=f"Exit {proc.returncode}",
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
if err_output:
|
|
128
|
+
logger.debug("Plugin tool {name} stderr: {err}", name=self.name, err=err_output)
|
|
129
|
+
|
|
130
|
+
return ToolOk(output=output)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def load_plugin_tools(
|
|
134
|
+
plugins_dir: Path, config: Config, *, approval: Approval | None = None
|
|
135
|
+
) -> list[PluginTool]:
|
|
136
|
+
"""Scan installed plugins and create PluginTool instances for declared tools."""
|
|
137
|
+
from pythinker_code.plugin import PLUGIN_JSON, PluginError, parse_plugin_json
|
|
138
|
+
|
|
139
|
+
if not plugins_dir.is_dir():
|
|
140
|
+
return []
|
|
141
|
+
|
|
142
|
+
tools: list[PluginTool] = []
|
|
143
|
+
for child in sorted(plugins_dir.iterdir()):
|
|
144
|
+
plugin_json = child / PLUGIN_JSON
|
|
145
|
+
if not child.is_dir() or not plugin_json.is_file():
|
|
146
|
+
continue
|
|
147
|
+
try:
|
|
148
|
+
spec = parse_plugin_json(plugin_json)
|
|
149
|
+
except PluginError:
|
|
150
|
+
continue
|
|
151
|
+
for tool_spec in spec.tools:
|
|
152
|
+
try:
|
|
153
|
+
tool = PluginTool(
|
|
154
|
+
tool_spec,
|
|
155
|
+
plugin_dir=child,
|
|
156
|
+
inject=spec.inject,
|
|
157
|
+
config=config,
|
|
158
|
+
approval=approval,
|
|
159
|
+
)
|
|
160
|
+
except Exception:
|
|
161
|
+
logger.warning(
|
|
162
|
+
"Skipping invalid plugin tool: {name} (from {plugin})",
|
|
163
|
+
name=tool_spec.name,
|
|
164
|
+
plugin=spec.name,
|
|
165
|
+
)
|
|
166
|
+
continue
|
|
167
|
+
tools.append(tool)
|
|
168
|
+
logger.info(
|
|
169
|
+
"Loaded plugin tool: {name} (from {plugin})",
|
|
170
|
+
name=tool_spec.name,
|
|
171
|
+
plugin=spec.name,
|
|
172
|
+
)
|
|
173
|
+
return tools
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
|
|
2
|
+
---
|
|
3
|
+
|
|
4
|
+
The above is a list of messages in an agent conversation. You are now given a task to compact this conversation context according to specific priorities and rules.
|
|
5
|
+
|
|
6
|
+
**Compression Priorities (in order):**
|
|
7
|
+
1. **Current Task State**: What is being worked on RIGHT NOW
|
|
8
|
+
2. **Errors & Solutions**: All encountered errors and their resolutions
|
|
9
|
+
3. **Code Evolution**: Final working versions only (remove intermediate attempts)
|
|
10
|
+
4. **System Context**: Project structure, dependencies, environment setup
|
|
11
|
+
5. **Design Decisions**: Architectural choices and their rationale
|
|
12
|
+
6. **TODO Items**: Unfinished tasks and known issues
|
|
13
|
+
|
|
14
|
+
**Compression Rules:**
|
|
15
|
+
- MUST KEEP: Error messages, stack traces, working solutions, current task
|
|
16
|
+
- MERGE: Similar discussions into single summary points
|
|
17
|
+
- REMOVE: Redundant explanations, failed attempts (keep lessons learned), verbose comments
|
|
18
|
+
- CONDENSE: Long code blocks → keep signatures + key logic only
|
|
19
|
+
|
|
20
|
+
**Special Handling:**
|
|
21
|
+
- For code: Keep full version if < 20 lines, otherwise keep signature + key logic
|
|
22
|
+
- For errors: Keep full error message + final solution
|
|
23
|
+
- For discussions: Extract decisions and action items only
|
|
24
|
+
|
|
25
|
+
**Required Output Structure:**
|
|
26
|
+
|
|
27
|
+
<current_focus>
|
|
28
|
+
[What we're working on now]
|
|
29
|
+
</current_focus>
|
|
30
|
+
|
|
31
|
+
<environment>
|
|
32
|
+
- [Key setup/config points]
|
|
33
|
+
- ...more...
|
|
34
|
+
</environment>
|
|
35
|
+
|
|
36
|
+
<completed_tasks>
|
|
37
|
+
- [Task]: [Brief outcome]
|
|
38
|
+
- ...more...
|
|
39
|
+
</completed_tasks>
|
|
40
|
+
|
|
41
|
+
<active_issues>
|
|
42
|
+
- [Issue]: [Status/Next steps]
|
|
43
|
+
- ...more...
|
|
44
|
+
</active_issues>
|
|
45
|
+
|
|
46
|
+
<code_state>
|
|
47
|
+
|
|
48
|
+
<file>
|
|
49
|
+
[filename]
|
|
50
|
+
|
|
51
|
+
**Summary:**
|
|
52
|
+
[What this code file does]
|
|
53
|
+
|
|
54
|
+
**Key elements:**
|
|
55
|
+
- [Important functions/classes]
|
|
56
|
+
- ...more...
|
|
57
|
+
|
|
58
|
+
**Latest version:**
|
|
59
|
+
[Critical code snippets in this file]
|
|
60
|
+
</file>
|
|
61
|
+
|
|
62
|
+
<file>
|
|
63
|
+
[filename]
|
|
64
|
+
...Similar as above...
|
|
65
|
+
</file>
|
|
66
|
+
|
|
67
|
+
...more files...
|
|
68
|
+
</code_state>
|
|
69
|
+
|
|
70
|
+
<important_context>
|
|
71
|
+
- [Any crucial information not covered above]
|
|
72
|
+
- ...more...
|
|
73
|
+
</important_context>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
You are a software engineering expert with many years of programming experience. Please explore the current project directory to understand the project's architecture and main details.
|
|
2
|
+
|
|
3
|
+
Task requirements:
|
|
4
|
+
1. Analyze the project structure and identify key configuration files (such as pyproject.toml, package.json, Cargo.toml, etc.).
|
|
5
|
+
2. Understand the project's technology stack, build process and runtime architecture.
|
|
6
|
+
3. Identify how the code is organized and main module divisions.
|
|
7
|
+
4. Discover project-specific development conventions, testing strategies, and deployment processes.
|
|
8
|
+
|
|
9
|
+
After the exploration, you should do a thorough summary of your findings and overwrite it into `AGENTS.md` file in the project root. You need to refer to what is already in the file when you do so.
|
|
10
|
+
|
|
11
|
+
For your information, `AGENTS.md` is a file intended to be read by AI coding agents. Expect the reader of this file know nothing about the project.
|
|
12
|
+
|
|
13
|
+
You should compose this file according to the actual project content. Do not make any assumptions or generalizations. Ensure the information is accurate and useful. You must use the natural language that is mainly used in the project's comments and documentation.
|
|
14
|
+
|
|
15
|
+
Popular sections that people usually write in `AGENTS.md` are:
|
|
16
|
+
|
|
17
|
+
- Project overview
|
|
18
|
+
- Build and test commands
|
|
19
|
+
- Code style guidelines
|
|
20
|
+
- Testing instructions
|
|
21
|
+
- Security considerations
|
pythinker_code/py.typed
ADDED
|
File without changes
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import builtins
|
|
5
|
+
import json
|
|
6
|
+
import shutil
|
|
7
|
+
import uuid
|
|
8
|
+
from dataclasses import dataclass
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
from pythinker_core.message import Message
|
|
12
|
+
from pythinker_host.path import HostPath
|
|
13
|
+
|
|
14
|
+
from pythinker_code.metadata import WorkDirMeta, load_metadata, save_metadata
|
|
15
|
+
from pythinker_code.session_state import SessionState, load_session_state, save_session_state
|
|
16
|
+
from pythinker_code.utils.logging import logger
|
|
17
|
+
from pythinker_code.utils.string import shorten
|
|
18
|
+
from pythinker_code.wire.file import WireFile
|
|
19
|
+
from pythinker_code.wire.types import TurnBegin
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass(slots=True, kw_only=True)
|
|
23
|
+
class Session:
|
|
24
|
+
"""A session of a work directory."""
|
|
25
|
+
|
|
26
|
+
# static metadata
|
|
27
|
+
id: str
|
|
28
|
+
"""The session ID."""
|
|
29
|
+
work_dir: HostPath
|
|
30
|
+
"""The absolute path of the work directory."""
|
|
31
|
+
work_dir_meta: WorkDirMeta
|
|
32
|
+
"""The metadata of the work directory."""
|
|
33
|
+
context_file: Path
|
|
34
|
+
"""The absolute path to the file storing the message history."""
|
|
35
|
+
wire_file: WireFile
|
|
36
|
+
"""The wire message log file wrapper."""
|
|
37
|
+
|
|
38
|
+
# session state
|
|
39
|
+
state: SessionState
|
|
40
|
+
"""Persisted session state (approval settings, plan mode, workspace scope, etc.)."""
|
|
41
|
+
|
|
42
|
+
# refreshable metadata
|
|
43
|
+
title: str
|
|
44
|
+
"""The title of the session."""
|
|
45
|
+
updated_at: float
|
|
46
|
+
"""The timestamp of the last update to the session."""
|
|
47
|
+
|
|
48
|
+
@property
|
|
49
|
+
def dir(self) -> Path:
|
|
50
|
+
"""The absolute path of the session directory."""
|
|
51
|
+
path = self.work_dir_meta.sessions_dir / self.id
|
|
52
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
53
|
+
return path
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def subagents_dir(self) -> Path:
|
|
57
|
+
"""The absolute path of the subagent instances directory."""
|
|
58
|
+
path = self.dir / "subagents"
|
|
59
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
60
|
+
return path
|
|
61
|
+
|
|
62
|
+
def is_empty(self) -> bool:
|
|
63
|
+
"""Whether the session has any context history or a custom title."""
|
|
64
|
+
if self.state.custom_title:
|
|
65
|
+
return False
|
|
66
|
+
if not self.wire_file.is_empty():
|
|
67
|
+
return False
|
|
68
|
+
try:
|
|
69
|
+
with self.context_file.open(encoding="utf-8") as f:
|
|
70
|
+
for line in f:
|
|
71
|
+
line = line.strip()
|
|
72
|
+
if not line:
|
|
73
|
+
continue
|
|
74
|
+
role = json.loads(line, strict=False).get("role")
|
|
75
|
+
if isinstance(role, str) and not role.startswith("_"):
|
|
76
|
+
return False
|
|
77
|
+
except FileNotFoundError:
|
|
78
|
+
return True
|
|
79
|
+
except (OSError, ValueError, TypeError):
|
|
80
|
+
logger.exception("Failed to read context file {file}:", file=self.context_file)
|
|
81
|
+
return False
|
|
82
|
+
return True
|
|
83
|
+
|
|
84
|
+
def save_state(self) -> None:
|
|
85
|
+
"""Persist the session state to disk.
|
|
86
|
+
|
|
87
|
+
Reloads externally-mutable fields (title, archive) from disk first
|
|
88
|
+
to avoid overwriting concurrent changes made by the web API.
|
|
89
|
+
"""
|
|
90
|
+
fresh = load_session_state(self.dir)
|
|
91
|
+
self.state.custom_title = fresh.custom_title
|
|
92
|
+
self.state.title_generated = fresh.title_generated
|
|
93
|
+
self.state.title_generate_attempts = fresh.title_generate_attempts
|
|
94
|
+
self.state.archived = fresh.archived
|
|
95
|
+
self.state.archived_at = fresh.archived_at
|
|
96
|
+
self.state.auto_archive_exempt = fresh.auto_archive_exempt
|
|
97
|
+
save_session_state(self.state, self.dir)
|
|
98
|
+
|
|
99
|
+
async def delete(self) -> None:
|
|
100
|
+
"""Delete the session directory."""
|
|
101
|
+
session_dir = self.work_dir_meta.sessions_dir / self.id
|
|
102
|
+
if not session_dir.exists():
|
|
103
|
+
return
|
|
104
|
+
await asyncio.to_thread(shutil.rmtree, session_dir, True)
|
|
105
|
+
|
|
106
|
+
async def refresh(self) -> None:
|
|
107
|
+
self.title = "Untitled"
|
|
108
|
+
self.updated_at = self.context_file.stat().st_mtime if self.context_file.exists() else 0.0
|
|
109
|
+
|
|
110
|
+
if self.state.custom_title:
|
|
111
|
+
self.title = self.state.custom_title
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
try:
|
|
115
|
+
async for record in self.wire_file.iter_records():
|
|
116
|
+
wire_msg = record.to_wire_message()
|
|
117
|
+
if isinstance(wire_msg, TurnBegin):
|
|
118
|
+
self.title = shorten(
|
|
119
|
+
Message(role="user", content=wire_msg.user_input).extract_text(" "),
|
|
120
|
+
width=50,
|
|
121
|
+
)
|
|
122
|
+
return
|
|
123
|
+
except Exception:
|
|
124
|
+
logger.exception(
|
|
125
|
+
"Failed to derive session title from wire file {file}:",
|
|
126
|
+
file=self.wire_file.path,
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
@staticmethod
|
|
130
|
+
async def create(
|
|
131
|
+
work_dir: HostPath,
|
|
132
|
+
session_id: str | None = None,
|
|
133
|
+
_context_file: Path | None = None,
|
|
134
|
+
) -> Session:
|
|
135
|
+
"""Create a new session for a work directory."""
|
|
136
|
+
work_dir = work_dir.canonical()
|
|
137
|
+
logger.debug("Creating new session for work directory: {work_dir}", work_dir=work_dir)
|
|
138
|
+
|
|
139
|
+
metadata = load_metadata()
|
|
140
|
+
work_dir_meta = metadata.get_work_dir_meta(work_dir)
|
|
141
|
+
if work_dir_meta is None:
|
|
142
|
+
work_dir_meta = metadata.new_work_dir_meta(work_dir)
|
|
143
|
+
|
|
144
|
+
if session_id is None:
|
|
145
|
+
session_id = str(uuid.uuid4())
|
|
146
|
+
session_dir = work_dir_meta.sessions_dir / session_id
|
|
147
|
+
session_dir.mkdir(parents=True, exist_ok=True)
|
|
148
|
+
|
|
149
|
+
if _context_file is None:
|
|
150
|
+
context_file = session_dir / "context.jsonl"
|
|
151
|
+
else:
|
|
152
|
+
logger.warning(
|
|
153
|
+
"Using provided context file: {context_file}", context_file=_context_file
|
|
154
|
+
)
|
|
155
|
+
_context_file.parent.mkdir(parents=True, exist_ok=True)
|
|
156
|
+
if _context_file.exists():
|
|
157
|
+
assert _context_file.is_file()
|
|
158
|
+
context_file = _context_file
|
|
159
|
+
|
|
160
|
+
if context_file.exists():
|
|
161
|
+
# truncate if exists
|
|
162
|
+
logger.warning(
|
|
163
|
+
"Context file already exists, truncating: {context_file}", context_file=context_file
|
|
164
|
+
)
|
|
165
|
+
context_file.unlink()
|
|
166
|
+
context_file.touch()
|
|
167
|
+
|
|
168
|
+
save_metadata(metadata)
|
|
169
|
+
|
|
170
|
+
session = Session(
|
|
171
|
+
id=session_id,
|
|
172
|
+
work_dir=work_dir,
|
|
173
|
+
work_dir_meta=work_dir_meta,
|
|
174
|
+
context_file=context_file,
|
|
175
|
+
wire_file=WireFile(path=session_dir / "wire.jsonl"),
|
|
176
|
+
state=SessionState(),
|
|
177
|
+
title="",
|
|
178
|
+
updated_at=0.0,
|
|
179
|
+
)
|
|
180
|
+
await session.refresh()
|
|
181
|
+
return session
|
|
182
|
+
|
|
183
|
+
@staticmethod
|
|
184
|
+
async def find(work_dir: HostPath, session_id: str) -> Session | None:
|
|
185
|
+
"""Find a session by work directory and session ID."""
|
|
186
|
+
work_dir = work_dir.canonical()
|
|
187
|
+
logger.debug(
|
|
188
|
+
"Finding session for work directory: {work_dir}, session ID: {session_id}",
|
|
189
|
+
work_dir=work_dir,
|
|
190
|
+
session_id=session_id,
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
metadata = load_metadata()
|
|
194
|
+
work_dir_meta = metadata.get_work_dir_meta(work_dir)
|
|
195
|
+
if work_dir_meta is None:
|
|
196
|
+
logger.debug("Work directory never been used")
|
|
197
|
+
return None
|
|
198
|
+
|
|
199
|
+
_migrate_session_context_file(work_dir_meta, session_id)
|
|
200
|
+
|
|
201
|
+
session_dir = work_dir_meta.sessions_dir / session_id
|
|
202
|
+
if not session_dir.is_dir():
|
|
203
|
+
logger.debug("Session directory not found: {session_dir}", session_dir=session_dir)
|
|
204
|
+
return None
|
|
205
|
+
|
|
206
|
+
context_file = session_dir / "context.jsonl"
|
|
207
|
+
if not context_file.exists():
|
|
208
|
+
logger.debug(
|
|
209
|
+
"Session context file not found: {context_file}", context_file=context_file
|
|
210
|
+
)
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
session = Session(
|
|
214
|
+
id=session_id,
|
|
215
|
+
work_dir=work_dir,
|
|
216
|
+
work_dir_meta=work_dir_meta,
|
|
217
|
+
context_file=context_file,
|
|
218
|
+
wire_file=WireFile(path=session_dir / "wire.jsonl"),
|
|
219
|
+
state=load_session_state(session_dir),
|
|
220
|
+
title="",
|
|
221
|
+
updated_at=0.0,
|
|
222
|
+
)
|
|
223
|
+
await session.refresh()
|
|
224
|
+
return session
|
|
225
|
+
|
|
226
|
+
@staticmethod
|
|
227
|
+
async def list(work_dir: HostPath) -> builtins.list[Session]:
|
|
228
|
+
"""List all sessions for a work directory."""
|
|
229
|
+
work_dir = work_dir.canonical()
|
|
230
|
+
logger.debug("Listing sessions for work directory: {work_dir}", work_dir=work_dir)
|
|
231
|
+
|
|
232
|
+
metadata = load_metadata()
|
|
233
|
+
work_dir_meta = metadata.get_work_dir_meta(work_dir)
|
|
234
|
+
if work_dir_meta is None:
|
|
235
|
+
logger.debug("Work directory never been used")
|
|
236
|
+
return []
|
|
237
|
+
|
|
238
|
+
session_ids = {
|
|
239
|
+
path.name if path.is_dir() else path.stem
|
|
240
|
+
for path in work_dir_meta.sessions_dir.iterdir()
|
|
241
|
+
if path.is_dir() or path.suffix == ".jsonl"
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
sessions: list[Session] = []
|
|
245
|
+
for session_id in session_ids:
|
|
246
|
+
_migrate_session_context_file(work_dir_meta, session_id)
|
|
247
|
+
session_dir = work_dir_meta.sessions_dir / session_id
|
|
248
|
+
if not session_dir.is_dir():
|
|
249
|
+
logger.debug("Session directory not found: {session_dir}", session_dir=session_dir)
|
|
250
|
+
continue
|
|
251
|
+
context_file = session_dir / "context.jsonl"
|
|
252
|
+
if not context_file.exists():
|
|
253
|
+
logger.debug(
|
|
254
|
+
"Session context file not found: {context_file}", context_file=context_file
|
|
255
|
+
)
|
|
256
|
+
continue
|
|
257
|
+
session = Session(
|
|
258
|
+
id=session_id,
|
|
259
|
+
work_dir=work_dir,
|
|
260
|
+
work_dir_meta=work_dir_meta,
|
|
261
|
+
context_file=context_file,
|
|
262
|
+
wire_file=WireFile(path=session_dir / "wire.jsonl"),
|
|
263
|
+
state=load_session_state(session_dir),
|
|
264
|
+
title="",
|
|
265
|
+
updated_at=0.0,
|
|
266
|
+
)
|
|
267
|
+
if session.is_empty():
|
|
268
|
+
logger.debug(
|
|
269
|
+
"Session context file is empty: {context_file}", context_file=context_file
|
|
270
|
+
)
|
|
271
|
+
continue
|
|
272
|
+
await session.refresh()
|
|
273
|
+
sessions.append(session)
|
|
274
|
+
sessions.sort(key=lambda session: session.updated_at, reverse=True)
|
|
275
|
+
return sessions
|
|
276
|
+
|
|
277
|
+
@classmethod
|
|
278
|
+
async def list_all(cls) -> builtins.list[Session]:
|
|
279
|
+
"""List sessions across all known work directories."""
|
|
280
|
+
all_sessions: list[Session] = []
|
|
281
|
+
for wd in load_metadata().work_dirs:
|
|
282
|
+
sessions = await cls.list(HostPath.unsafe_from_local_path(Path(wd.path)))
|
|
283
|
+
all_sessions.extend(sessions)
|
|
284
|
+
all_sessions.sort(key=lambda s: s.updated_at, reverse=True)
|
|
285
|
+
return all_sessions
|
|
286
|
+
|
|
287
|
+
@staticmethod
|
|
288
|
+
async def continue_(work_dir: HostPath) -> Session | None:
|
|
289
|
+
"""Get the last session for a work directory."""
|
|
290
|
+
work_dir = work_dir.canonical()
|
|
291
|
+
logger.debug("Continuing session for work directory: {work_dir}", work_dir=work_dir)
|
|
292
|
+
|
|
293
|
+
metadata = load_metadata()
|
|
294
|
+
work_dir_meta = metadata.get_work_dir_meta(work_dir)
|
|
295
|
+
if work_dir_meta is None:
|
|
296
|
+
logger.debug("Work directory never been used")
|
|
297
|
+
return None
|
|
298
|
+
if work_dir_meta.last_session_id is None:
|
|
299
|
+
logger.debug("Work directory never had a session")
|
|
300
|
+
return None
|
|
301
|
+
|
|
302
|
+
logger.debug(
|
|
303
|
+
"Found last session for work directory: {session_id}",
|
|
304
|
+
session_id=work_dir_meta.last_session_id,
|
|
305
|
+
)
|
|
306
|
+
return await Session.find(work_dir, work_dir_meta.last_session_id)
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
def _migrate_session_context_file(work_dir_meta: WorkDirMeta, session_id: str) -> None:
|
|
310
|
+
old_context_file = work_dir_meta.sessions_dir / f"{session_id}.jsonl"
|
|
311
|
+
new_context_file = work_dir_meta.sessions_dir / session_id / "context.jsonl"
|
|
312
|
+
if old_context_file.exists() and not new_context_file.exists():
|
|
313
|
+
new_context_file.parent.mkdir(parents=True, exist_ok=True)
|
|
314
|
+
old_context_file.rename(new_context_file)
|
|
315
|
+
logger.info(
|
|
316
|
+
"Migrated session context file from {old} to {new}",
|
|
317
|
+
old=old_context_file,
|
|
318
|
+
new=new_context_file,
|
|
319
|
+
)
|