limbo-code 0.1.1__tar.gz → 0.1.9__tar.gz
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.
- {limbo_code-0.1.1 → limbo_code-0.1.9}/.github/workflows/test.yml +9 -3
- {limbo_code-0.1.1 → limbo_code-0.1.9}/.gitignore +1 -0
- limbo_code-0.1.9/AGENTS.md +94 -0
- limbo_code-0.1.9/CONTEXT.md +43 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/PKG-INFO +99 -4
- {limbo_code-0.1.1 → limbo_code-0.1.9}/README.md +93 -2
- limbo_code-0.1.9/docs/assets/walkthrough/limbo-dark/1_idle.svg +165 -0
- limbo_code-0.1.9/docs/assets/walkthrough/limbo-dark/2_thinking_tool_running.svg +166 -0
- limbo_code-0.1.9/docs/assets/walkthrough/limbo-dark/3_tool_success.svg +167 -0
- limbo_code-0.1.9/docs/assets/walkthrough/limbo-dark/4_tool_error.svg +168 -0
- limbo_code-0.1.9/docs/assets/walkthrough/limbo-dark/5_llm_error.svg +166 -0
- limbo_code-0.1.9/docs/assets/walkthrough/limbo-dark/6_edit_diff.svg +168 -0
- limbo_code-0.1.9/docs/assets/walkthrough/limbo-light/1_idle.svg +166 -0
- limbo_code-0.1.9/docs/assets/walkthrough/limbo-light/2_thinking_tool_running.svg +167 -0
- limbo_code-0.1.9/docs/assets/walkthrough/limbo-light/3_tool_success.svg +168 -0
- limbo_code-0.1.9/docs/assets/walkthrough/limbo-light/4_tool_error.svg +169 -0
- limbo_code-0.1.9/docs/assets/walkthrough/limbo-light/5_llm_error.svg +167 -0
- limbo_code-0.1.9/docs/assets/walkthrough/limbo-light/6_edit_diff.svg +169 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/docs/skills.md +14 -2
- {limbo_code-0.1.1 → limbo_code-0.1.9}/pyproject.toml +15 -3
- limbo_code-0.1.9/scripts/check_contrast.py +28 -0
- limbo_code-0.1.9/scripts/gen_banner.py +103 -0
- limbo_code-0.1.9/scripts/ui_walkthrough.py +130 -0
- limbo_code-0.1.9/src/limbo/__init__.py +8 -0
- limbo_code-0.1.9/src/limbo/agent.py +947 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/app.py +12 -8
- limbo_code-0.1.9/src/limbo/attachments.py +65 -0
- limbo_code-0.1.9/src/limbo/compaction.py +173 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/config.py +92 -2
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/history.py +0 -19
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/llm/anthropic_client.py +88 -53
- limbo_code-0.1.9/src/limbo/llm/catalog.py +396 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/llm/client.py +6 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/llm/factory.py +3 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/llm/openai_client.py +87 -36
- limbo_code-0.1.9/src/limbo/llm/responses_client.py +389 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/llm/retry.py +8 -3
- limbo_code-0.1.9/src/limbo/llm/scaffold.py +92 -0
- limbo_code-0.1.9/src/limbo/llm/sse.py +31 -0
- limbo_code-0.1.9/src/limbo/llm/usage.py +160 -0
- limbo_code-0.1.9/src/limbo/model_switch.py +103 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/models.py +36 -2
- limbo_code-0.1.9/src/limbo/prompt.py +103 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/sessions.py +96 -6
- limbo_code-0.1.9/src/limbo/skills.py +190 -0
- limbo_code-0.1.9/src/limbo/steer.py +76 -0
- limbo_code-0.1.9/src/limbo/tools/base.py +294 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/tools/bash.py +39 -7
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/tools/edit.py +23 -19
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/tools/find.py +12 -6
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/tools/grep.py +18 -10
- limbo_code-0.1.9/src/limbo/tools/mutation_queue.py +36 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/tools/read.py +56 -18
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/tools/registry.py +35 -2
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/tools/write.py +7 -5
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/ui/app.py +17 -6
- limbo_code-0.1.9/src/limbo/ui/app.tcss +316 -0
- limbo_code-0.1.9/src/limbo/ui/banner.py +117 -0
- limbo_code-0.1.9/src/limbo/ui/clipboard.py +314 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/ui/commands.py +4 -0
- limbo_code-0.1.9/src/limbo/ui/contrast.py +139 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/ui/screens/main.py +267 -17
- limbo_code-0.1.9/src/limbo/ui/screens/model_picker.py +122 -0
- limbo_code-0.1.9/src/limbo/ui/syntax.py +84 -0
- limbo_code-0.1.9/src/limbo/ui/theme.py +101 -0
- limbo_code-0.1.9/src/limbo/ui/widgets/chat.py +320 -0
- limbo_code-0.1.9/src/limbo/ui/widgets/input.py +409 -0
- limbo_code-0.1.9/src/limbo/ui/widgets/status_bar.py +116 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/ui/widgets/tool_card.py +18 -8
- limbo_code-0.1.9/src/limbo/user_paths.py +82 -0
- limbo_code-0.1.9/tests/conftest.py +21 -0
- limbo_code-0.1.9/tests/test_agent.py +2038 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/test_anthropic_client.py +277 -2
- limbo_code-0.1.9/tests/test_attachments.py +78 -0
- limbo_code-0.1.9/tests/test_catalog.py +293 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/test_cli.py +12 -0
- limbo_code-0.1.9/tests/test_clipboard.py +205 -0
- limbo_code-0.1.9/tests/test_compaction.py +163 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/test_config.py +74 -3
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/test_history.py +0 -13
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/test_llm_client.py +276 -2
- limbo_code-0.1.9/tests/test_llm_scaffold.py +89 -0
- limbo_code-0.1.9/tests/test_model_switch.py +140 -0
- limbo_code-0.1.9/tests/test_prompt.py +85 -0
- limbo_code-0.1.9/tests/test_responses_client.py +582 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/test_retry.py +4 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/test_sessions.py +181 -4
- limbo_code-0.1.9/tests/test_skills.py +187 -0
- limbo_code-0.1.9/tests/test_steer.py +66 -0
- limbo_code-0.1.9/tests/test_usage.py +159 -0
- limbo_code-0.1.9/tests/test_user_paths.py +87 -0
- limbo_code-0.1.9/tests/tools/test_base.py +244 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/tools/test_bash.py +66 -6
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/tools/test_find.py +20 -0
- limbo_code-0.1.9/tests/tools/test_mutation_queue.py +47 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/tools/test_read.py +89 -2
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/tools/test_registry.py +32 -0
- limbo_code-0.1.9/tests/ui/__snapshots__/test_snapshots/test_snapshot_idle.raw +172 -0
- limbo_code-0.1.9/tests/ui/__snapshots__/test_snapshots/test_snapshot_session_picker.raw +168 -0
- limbo_code-0.1.9/tests/ui/__snapshots__/test_snapshots/test_snapshot_thinking_and_running_tool.raw +174 -0
- limbo_code-0.1.9/tests/ui/__snapshots__/test_snapshots/test_snapshot_tool_error_and_error_message.raw +175 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/ui/test_app_smoke.py +9 -4
- limbo_code-0.1.9/tests/ui/test_compact_ui.py +188 -0
- limbo_code-0.1.9/tests/ui/test_contrast.py +41 -0
- limbo_code-0.1.9/tests/ui/test_input_attachments.py +168 -0
- limbo_code-0.1.9/tests/ui/test_input_paste.py +314 -0
- limbo_code-0.1.9/tests/ui/test_main_screen.py +284 -0
- limbo_code-0.1.9/tests/ui/test_model_picker.py +393 -0
- limbo_code-0.1.9/tests/ui/test_scroll_follow.py +85 -0
- limbo_code-0.1.9/tests/ui/test_snapshots.py +136 -0
- limbo_code-0.1.9/tests/ui/test_startup_art.py +137 -0
- limbo_code-0.1.9/tests/ui/test_steer_ui.py +532 -0
- limbo_code-0.1.9/tests/ui/test_theme.py +104 -0
- limbo_code-0.1.9/uv.lock +936 -0
- limbo_code-0.1.1/AGENTS.md +0 -183
- limbo_code-0.1.1/src/limbo/__init__.py +0 -3
- limbo_code-0.1.1/src/limbo/agent.py +0 -456
- limbo_code-0.1.1/src/limbo/llm/catalog.py +0 -234
- limbo_code-0.1.1/src/limbo/skills.py +0 -89
- limbo_code-0.1.1/src/limbo/tools/base.py +0 -102
- limbo_code-0.1.1/src/limbo/ui/app.tcss +0 -174
- limbo_code-0.1.1/src/limbo/ui/banner.py +0 -83
- limbo_code-0.1.1/src/limbo/ui/widgets/chat.py +0 -149
- limbo_code-0.1.1/src/limbo/ui/widgets/input.py +0 -199
- limbo_code-0.1.1/src/limbo/ui/widgets/status_bar.py +0 -32
- limbo_code-0.1.1/tests/test_agent.py +0 -729
- limbo_code-0.1.1/tests/test_catalog.py +0 -160
- limbo_code-0.1.1/tests/test_skills.py +0 -89
- limbo_code-0.1.1/tests/tools/test_base.py +0 -85
- limbo_code-0.1.1/tests/ui/test_main_screen.py +0 -150
- limbo_code-0.1.1/tests/ui/test_startup_art.py +0 -94
- {limbo_code-0.1.1 → limbo_code-0.1.9}/.agents/skills/grill-with-docs/SKILL.md +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/.agents/skills/grill-with-docs/agents/openai.yaml +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/.agents/skills/improve-codebase-architecture/HTML-REPORT.md +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/.agents/skills/improve-codebase-architecture/SKILL.md +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/.agents/skills/improve-codebase-architecture/agents/openai.yaml +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/.agents/skills/tdd/SKILL.md +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/.agents/skills/tdd/agents/openai.yaml +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/.agents/skills/tdd/mocking.md +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/.agents/skills/tdd/tests.md +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/.github/workflows/publish.yml +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/design/confirm_view.html +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/design/limbo-ui-minimal.md +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/design/limbo-ui-redesign.md +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/design/prototype-minimal-confirm.png +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/design/prototype-minimal.html +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/design/prototype-minimal.png +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/design/prototype.html +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/design/prototype.png +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/docs/assets/limbo-current-ui.png +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/docs/assets/limbo-new-ui.png +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/docs/session-management.md +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/docs/ui-redesign-proposal.md +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/skills-lock.json +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/__main__.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/llm/__init__.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/tools/__init__.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/tools/ignore.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/tools/ls.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/trace.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/ui/__init__.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/ui/screens/__init__.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/ui/screens/game2048.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/ui/screens/session_picker.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/ui/widgets/__init__.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/src/limbo/ui/widgets/command_menu.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/test_integration.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/test_models.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/test_trace.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/tools/test_edit.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/tools/test_grep.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/tools/test_ignore.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/tools/test_ls.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/tools/test_write.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/ui/test_command_menu.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/ui/test_commands.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/ui/test_game2048.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/ui/test_input_history.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/ui/test_sessions_ui.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/ui/test_skills_ui.py +0 -0
- {limbo_code-0.1.1 → limbo_code-0.1.9}/tests/ui/test_widgets.py +0 -0
|
@@ -17,14 +17,20 @@ jobs:
|
|
|
17
17
|
steps:
|
|
18
18
|
- uses: actions/checkout@v4
|
|
19
19
|
|
|
20
|
+
- name: Install uv
|
|
21
|
+
uses: astral-sh/setup-uv@v5
|
|
22
|
+
|
|
20
23
|
- name: Set up Python
|
|
21
24
|
uses: actions/setup-python@v5
|
|
22
25
|
with:
|
|
23
26
|
python-version: ${{ matrix.python-version }}
|
|
24
|
-
cache: pip
|
|
25
27
|
|
|
28
|
+
# Install from uv.lock so CI renders snapshots with the exact same
|
|
29
|
+
# textual/rich/syrupy versions the baselines were generated with.
|
|
26
30
|
- name: Install dependencies
|
|
27
|
-
run:
|
|
31
|
+
run: uv sync --frozen --extra dev
|
|
32
|
+
env:
|
|
33
|
+
UV_PYTHON: ${{ matrix.python-version }}
|
|
28
34
|
|
|
29
35
|
- name: Run tests
|
|
30
|
-
run: python -m pytest -v
|
|
36
|
+
run: uv run python -m pytest -v
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
# Limbo — Agent Guide
|
|
2
|
+
|
|
3
|
+
**Limbo** is a minimal terminal AI coding agent (Python 3.11+, Textual). Users converse with an LLM in a TUI to explore, read, edit, and write code via 7 tools.
|
|
4
|
+
|
|
5
|
+
**Tools execute immediately — no confirmation flow.** Guardrails: a workdir fence on file tools (+ session-scoped grants from user-mentioned paths), a sensitive-file blocklist on `read`, and a heuristic dangerous-command filter on `bash` (rejects matches outright; bypassable via subshells/variables — documented in the tool description).
|
|
6
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pip install -e .
|
|
11
|
+
limbo --workdir /path/to/project # config: ~/.limbo/config.toml
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Architecture
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
src/limbo/
|
|
18
|
+
├── app.py # CLI entry: args, config, launch
|
|
19
|
+
├── config.py # Pydantic config: llm/ui/safety/tools/compaction/providers
|
|
20
|
+
├── models.py # Message, Attachment, ToolResult, LLMEvent
|
|
21
|
+
├── agent.py # Conversation loop (see below)
|
|
22
|
+
├── attachments.py # Attachment policy: vision gate, inline vs path-reference degrade
|
|
23
|
+
├── compaction.py # Context compaction decision/prompt logic (LIM-14)
|
|
24
|
+
├── history.py # tool_call↔result pairing + resume repair
|
|
25
|
+
├── model_switch.py # /model domain logic: validate, swap client, persist (UI is an adapter)
|
|
26
|
+
├── prompt.py # System-prompt assembly (tools section derived from the registry)
|
|
27
|
+
├── steer.py # Mid-turn steer queue (LIM-20): queueing semantics, cancel boundary
|
|
28
|
+
├── sessions.py # Session JSONL save/load/list/export
|
|
29
|
+
├── trace.py # Append-only JSONL run log (sessions/traces/)
|
|
30
|
+
├── skills.py # SKILL.md discovery (user + project dirs)
|
|
31
|
+
├── user_paths.py # Fence grants from paths in user messages
|
|
32
|
+
├── llm/ # catalog.py (provider/model specs), factory.py (dialect→client),
|
|
33
|
+
│ # openai_client.py, anthropic_client.py, responses_client.py, retry.py, sse.py,
|
|
34
|
+
│ # usage.py (token accounting: usage normalization + prompt-size estimation),
|
|
35
|
+
│ # scaffold.py (plumbing shared by dialect clients: credentials, retry, images)
|
|
36
|
+
├── tools/ # base.py (BaseTool + fence + truncation), registry.py (dispatch, grants),
|
|
37
|
+
│ # mutation_queue.py (per-file locks), ignore.py (.gitignore),
|
|
38
|
+
│ # read/bash/edit/write/grep/find/ls.py
|
|
39
|
+
└── ui/ # app.py + app.tcss (ALL styles here; theme vars only, no bare hex),
|
|
40
|
+
# theme.py (limbo-dark/-light, RFC LIM-16), commands.py (slash registry),
|
|
41
|
+
# screens/ (main, session_picker, model_picker, game2048),
|
|
42
|
+
# widgets/ (chat, input, status_bar, tool_card, command_menu)
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Agent Loop
|
|
46
|
+
|
|
47
|
+
- `Agent.run()` yields `AgentEvent`: `TextDelta`, `ThinkingDelta`, `ToolCallRequest`, `ToolResultEvent`, `ErrorEvent`, `CompactionEvent`, `UsageUpdate`, `SteerEvent`
|
|
48
|
+
- Loop top: auto-compaction check → steer drain → LLM call. Turn ends on a tool-call-free response; `max_iterations` (default 50) cancels pending calls with placeholder results
|
|
49
|
+
- Tool calls in one turn run **concurrently** (`[tools] parallel`); results stream in completion order, recorded in source order; same-file mutations serialized by `mutation_queue`
|
|
50
|
+
- `finish_reason` `length`/`max_tokens` → whole batch failed without execution, model re-issues
|
|
51
|
+
- Mid-turn user input queues as *steer* (LIM-20): injected at loop top, or as turn-end follow-up
|
|
52
|
+
- Reasoning is stored on assistant messages and replayed per dialect (Anthropic thinking signatures, Kimi `reasoning_content`, Responses encrypted items)
|
|
53
|
+
- Usage counters normalize in `llm/usage.py` (`input_tokens`+`cache_read`, `prompt_tokens`, DeepSeek cache hits) and feed the compaction trigger via `PromptSizeEstimator`
|
|
54
|
+
|
|
55
|
+
## LLM & Config
|
|
56
|
+
|
|
57
|
+
Providers/models live in `llm/catalog.py`; unknown models get generic OpenAI-compatible defaults. Resolution: `[providers.<id>]` overrides → explicit `[llm]` values → catalog defaults → env-var API keys.
|
|
58
|
+
|
|
59
|
+
```toml
|
|
60
|
+
[llm] # model, api_key, base_url, temperature, max_iterations,
|
|
61
|
+
# max_tokens, thinking_effort, max_retries, timeout, ...
|
|
62
|
+
[tools] # bash_enabled = true, parallel = true
|
|
63
|
+
[compaction] # enabled, reserve_tokens = 16384, keep_recent_tokens = 20000
|
|
64
|
+
[safety] # dangerous_commands, sensitive_files, auto_grant_user_paths
|
|
65
|
+
[ui] # theme, show_banner
|
|
66
|
+
[providers.<id>] # base_url / api_key / api_key_env / headers
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`/model` rebuilds the client mid-session (refused while busy) and persists via tomlkit. Sessions resume via `--continue` / `--resume` / `/sessions`; resume repairs dangling tool calls and restores grants. Trace events: `session_start`, `user_message`, `llm_request` (full body), `llm_response`, `tool_call`, `tool_result`, `compaction*`, `error`, `turn_end`. `/export [path]` merges meta + trace + message snapshot (Markdown if `.md`).
|
|
70
|
+
|
|
71
|
+
Skills: `~/.limbo/skills/<name>/SKILL.md` (user) and `<workdir>/.agents/skills/<name>/SKILL.md` (project, wins collisions) — injected as a `<available_skills>` catalog in the system prompt and invocable via `/<name> [args]`.
|
|
72
|
+
|
|
73
|
+
## UI
|
|
74
|
+
|
|
75
|
+
Single column: status bar (state/elapsed/tokens/model/workdir/queued) → scrolling chat flow (user `❯`, streaming Markdown, tool cards, errors) → input box (Enter submits, Shift+Enter newline, paste markers, `ctrl+v` image attach) → hint line. Slash commands: `/sessions /new /export /compact /model /help /2048` + skill commands. After palette changes run `python scripts/check_contrast.py` (≥ 4.5:1).
|
|
76
|
+
|
|
77
|
+
## Key Decisions
|
|
78
|
+
|
|
79
|
+
- Async agent, sync tools via `asyncio.to_thread()`
|
|
80
|
+
- System prompt = hardcoded tool guidelines + `<workdir>/AGENTS.md` & `~/.limbo/AGENTS.md` (XML-wrapped) + skills catalog. **This file is injected into every request — keep it accurate and lean.**
|
|
81
|
+
- Session files fully rewritten per save (atomic, 0600); trace holds full-fidelity record
|
|
82
|
+
|
|
83
|
+
## Testing
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
pytest tests/ -v # pytest-asyncio, respx HTTP mocks, pytest-textual-snapshot
|
|
87
|
+
ruff check src tests && mypy src # lint + type check
|
|
88
|
+
pytest --snapshot-update # after intentional visual changes; review the SVG diff
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Adding Things
|
|
92
|
+
|
|
93
|
+
- **Tool**: subclass `BaseTool` in `tools/<name>.py` (`name`/`description`/`parameters` + `run()`; use `resolve_existing()`/`resolve_creatable()`, raise `ToolError`), register in `ToolRegistry.__init__()`, wrap mutations in `mutation_lock_for(path)`, add `tests/tools/test_<name>.py`
|
|
94
|
+
- **Provider/model**: add specs in `llm/catalog.py`; new wire format → implement `LLMClient` + `register_client()` in `llm/factory.py`. User-specific endpoint/key tweaks belong in `[providers.<id>]` config, not the catalog
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# Context — Limbo domain glossary
|
|
2
|
+
|
|
3
|
+
Domain language for Limbo's concepts. Architecture reviews and refactor
|
|
4
|
+
proposals should name modules with these terms.
|
|
5
|
+
|
|
6
|
+
## Token accounting
|
|
7
|
+
|
|
8
|
+
- **Usage** — the provider-reported token counters on one completion. Each
|
|
9
|
+
dialect reports them differently (OpenAI `prompt_tokens`, Anthropic
|
|
10
|
+
`input_tokens` + cache counters, DeepSeek `prompt_cache_hit_tokens`,
|
|
11
|
+
Responses `input_tokens_details`). Normalized by `llm/usage.py` into
|
|
12
|
+
`UsageTotals` (prompt / cached / total); the agent loop and compaction
|
|
13
|
+
never read provider field names.
|
|
14
|
+
- **Prompt-size estimation** — predicting the *next* request's prompt size:
|
|
15
|
+
the last real usage figure plus the chars/4 estimate of everything
|
|
16
|
+
appended since (the **watermark**). Owned by `PromptSizeEstimator` in
|
|
17
|
+
`llm/usage.py`; invalidated (reset) when compaction rewrites history.
|
|
18
|
+
|
|
19
|
+
## Conversation
|
|
20
|
+
|
|
21
|
+
- **Turn** — one user input through the agent loop until a tool-call-free
|
|
22
|
+
response (or max_iterations). Turns are the save/trace unit.
|
|
23
|
+
- **Steer** — a user message queued mid-turn, injected at consistency
|
|
24
|
+
points (loop top, turn end, run head). The `SteerQueue` (`steer.py`) owns
|
|
25
|
+
queueing semantics — id generation, FIFO, and the cancel boundary (an
|
|
26
|
+
item can be cancelled by id only until drained); the Agent owns drain
|
|
27
|
+
timing.
|
|
28
|
+
- **Compaction** — summarizing the old region of history into a synthetic
|
|
29
|
+
summary message, keeping the recent tail. Triggered automatically
|
|
30
|
+
(loop-top, before context overflow) or manually (`/compact`).
|
|
31
|
+
- **Attachment policy** — how a submitted attachment reaches the model
|
|
32
|
+
(`attachments.py`): images become multimodal blocks for vision models
|
|
33
|
+
(encoded per dialect, `llm/scaffold.py`), everything else degrades to
|
|
34
|
+
text — small files inline (≤50KB), the rest as path references.
|
|
35
|
+
- **Model switch** — `/model` mid-session (`model_switch.py`): validate the
|
|
36
|
+
target (API key, thinking-effort compatibility), swap the client
|
|
37
|
+
(converging on the latest config model), persist to config.toml. The UI
|
|
38
|
+
screen is an adapter translating verdicts into chat messages.
|
|
39
|
+
|
|
40
|
+
## Safety
|
|
41
|
+
|
|
42
|
+
- **Fence** — the workdir boundary on file tools, widened by session-scoped
|
|
43
|
+
**grants** (paths a real user mentions in a submitted message).
|
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: limbo-code
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: A minimal terminal AI coding agent
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Requires-Dist: httpx>=0.27
|
|
7
7
|
Requires-Dist: openai>=1.30
|
|
8
8
|
Requires-Dist: pydantic>=2.0
|
|
9
|
-
Requires-Dist:
|
|
9
|
+
Requires-Dist: pygments>=2.17
|
|
10
|
+
Requires-Dist: textual>=8.0
|
|
10
11
|
Requires-Dist: toml>=0.10
|
|
12
|
+
Requires-Dist: tomlkit>=0.13
|
|
11
13
|
Provides-Extra: dev
|
|
12
14
|
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
13
15
|
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
16
|
+
Requires-Dist: pytest-textual-snapshot>=1.0; extra == 'dev'
|
|
14
17
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
15
18
|
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
16
19
|
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
20
|
+
Requires-Dist: types-pygments>=2.17; extra == 'dev'
|
|
17
21
|
Description-Content-Type: text/markdown
|
|
18
22
|
|
|
19
23
|
# Limbo
|
|
@@ -55,6 +59,40 @@ Built-in providers:
|
|
|
55
59
|
| `deepseek` | openai-completions | `https://api.deepseek.com/v1` | `DEEPSEEK_API_KEY` |
|
|
56
60
|
| `moonshotai` (Kimi) | openai-completions | `https://api.moonshot.ai/v1` | `MOONSHOT_API_KEY` |
|
|
57
61
|
| `kimi-coding` (Kimi For Coding) | anthropic-messages | `https://api.kimi.com/coding` | `KIMI_API_KEY` |
|
|
62
|
+
| `glm` (GLM Coding Plan) | openai-completions | `https://open.bigmodel.cn/api/coding/paas/v4` | `ZHIPUAI_API_KEY` |
|
|
63
|
+
| `codex` (OpenAI Codex) | openai-responses | `https://api.openai.com/v1` (override with your relay) | `CODEX_API_KEY` |
|
|
64
|
+
|
|
65
|
+
Built-in Codex models include `gpt-5.5`, `gpt-5.6-sol`/`terra`/`luna`,
|
|
66
|
+
`gpt-5.4`, `gpt-5.4-mini`, and `gpt-5.3-codex-spark`. **`gpt-5.5` is
|
|
67
|
+
verified by live test against the target relay** (its 1M context is
|
|
68
|
+
relay-reported); the other entries mirror pi's built-in catalog and are
|
|
69
|
+
unverified — availability depends on the relay in use, and unknown or
|
|
70
|
+
unsupported models fall back to the generic OpenAI-compatible defaults.
|
|
71
|
+
Codex speaks the
|
|
72
|
+
OpenAI **Responses API** (`POST {base_url}/responses`), served by a
|
|
73
|
+
dedicated client (`src/limbo/llm/responses_client.py`, plain httpx SSE):
|
|
74
|
+
system messages become `instructions`, tool definitions are flattened, and
|
|
75
|
+
reasoning models stream `reasoning` summaries and replay encrypted
|
|
76
|
+
reasoning items across turns. Limbo targets **API-key relays**, not the
|
|
77
|
+
ChatGPT subscription backend (OAuth) — point the provider at your relay
|
|
78
|
+
and make sure its model IDs match the catalog:
|
|
79
|
+
|
|
80
|
+
```toml
|
|
81
|
+
[llm]
|
|
82
|
+
model = "gpt-5.5"
|
|
83
|
+
|
|
84
|
+
[providers.codex]
|
|
85
|
+
base_url = "https://my-relay.example.com/v1"
|
|
86
|
+
api_key_env = "CODEX_API_KEY"
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Built-in GLM Coding Plan models: `glm-4.7`, `glm-5.1`, `glm-5.2` (1M
|
|
90
|
+
context), `glm-5-turbo`, `glm-5v-turbo` (vision), and `glm-4.5-air`. The
|
|
91
|
+
Coding Plan is a subscription with its own endpoint and keys — a coding-plan
|
|
92
|
+
key only works on the `/api/coding/paas/v4` endpoints (not the pay-per-token
|
|
93
|
+
`/api/paas/v4` ones) and vice versa. For the international endpoint set
|
|
94
|
+
`[providers.glm] base_url = "https://api.z.ai/api/coding/paas/v4"` (see
|
|
95
|
+
below).
|
|
58
96
|
|
|
59
97
|
Built-in Kimi models include `kimi-k3` (1M context, pay-per-token), and for
|
|
60
98
|
Kimi For Coding subscriptions: `k3` (1M context), `kimi-for-coding`, and
|
|
@@ -78,14 +116,47 @@ assistant thinking blocks with their signature.
|
|
|
78
116
|
|
|
79
117
|
For the mainland-China Moonshot endpoint, set `base_url =
|
|
80
118
|
"https://api.moonshot.cn/v1"` explicitly (a configured `base_url` always
|
|
81
|
-
wins over the catalog
|
|
119
|
+
wins over the catalog — unless a `[providers.<id>]` override says otherwise,
|
|
120
|
+
see below).
|
|
121
|
+
|
|
122
|
+
### Per-provider overrides
|
|
123
|
+
|
|
124
|
+
An optional `[providers.<id>]` section overrides a single catalog provider
|
|
125
|
+
without touching the global `[llm]` settings — e.g. pointing a provider at a
|
|
126
|
+
relay, renaming its credential env var, or adding extra headers:
|
|
127
|
+
|
|
128
|
+
```toml
|
|
129
|
+
[providers.glm]
|
|
130
|
+
base_url = "https://api.z.ai/api/coding/paas/v4" # international endpoint
|
|
131
|
+
|
|
132
|
+
[providers.codex]
|
|
133
|
+
base_url = "https://my-relay.example.com/v1"
|
|
134
|
+
api_key_env = "CODEX_API_KEY" # rename the env var read for the key
|
|
135
|
+
headers = { x-relay = "on" } # extra headers on every request
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
All fields are optional; unset fields fall back to the catalog. Resolution
|
|
139
|
+
order for `base_url` (first hit wins):
|
|
140
|
+
|
|
141
|
+
1. `[providers.<id>] base_url`
|
|
142
|
+
2. `[llm] base_url` (when changed from the DeepSeek default)
|
|
143
|
+
3. the catalog provider's built-in endpoint
|
|
144
|
+
|
|
145
|
+
and for the API key: `[providers.<id>] api_key` → `[llm] api_key` → the
|
|
146
|
+
environment variable (`[providers.<id>] api_key_env` rename, else the
|
|
147
|
+
catalog's default env var).
|
|
148
|
+
|
|
149
|
+
> **Note:** rule 1 is the single exception to the long-standing "an explicit
|
|
150
|
+
> `[llm] base_url` always wins" behavior — a per-provider override is more
|
|
151
|
+
> specific than the global setting. If you configure both, the
|
|
152
|
+
> `[providers.<id>]` value is used for that provider's models.
|
|
82
153
|
|
|
83
154
|
### Optional LLM settings
|
|
84
155
|
|
|
85
156
|
```toml
|
|
86
157
|
[llm]
|
|
87
158
|
temperature = 0.2 # 0.0 - 2.0, default 0.2
|
|
88
|
-
max_iterations =
|
|
159
|
+
max_iterations = 50 # safety limit on tool-turn loops, default 50
|
|
89
160
|
max_tokens = 8192 # output cap; default = the model's catalog value
|
|
90
161
|
thinking_effort = "high" # reasoning control; default = provider behavior
|
|
91
162
|
```
|
|
@@ -100,6 +171,13 @@ thinking_effort = "high" # reasoning control; default = provider behavior
|
|
|
100
171
|
- `kimi-k2-thinking`, `kimi-k2.5`+ (DeepSeek-style): any non-`off` value →
|
|
101
172
|
`thinking: {type: enabled}`; `"off"` → `thinking: {type: disabled}`
|
|
102
173
|
(except `kimi-k2.7-code*`, where thinking is always on).
|
|
174
|
+
- `glm-*` (z.ai-style): like DeepSeek-style but with `clear_thinking: false`
|
|
175
|
+
so thinking is preserved across turns; `glm-5.2` additionally maps
|
|
176
|
+
`low`/`high`/`max` to `reasoning_effort` (`low` clamps to `high`).
|
|
177
|
+
- `gpt-5.*` codex models (Responses API): `low` | `medium` | `high` |
|
|
178
|
+
`xhigh` → `reasoning: {effort, summary: auto}`; 5.6-generation models
|
|
179
|
+
(`gpt-5.6-*`) also accept `max`. Temperature is omitted for reasoning
|
|
180
|
+
models.
|
|
103
181
|
- Non-reasoning models: ignored.
|
|
104
182
|
|
|
105
183
|
Reasoning output streams into the chat as muted thinking blocks and is
|
|
@@ -126,8 +204,25 @@ you no longer need them.
|
|
|
126
204
|
|
|
127
205
|
```bash
|
|
128
206
|
limbo --workdir /path/to/project
|
|
207
|
+
limbo --model glm-4.7 # override the configured model for this run
|
|
129
208
|
```
|
|
130
209
|
|
|
210
|
+
### Switching models at runtime
|
|
211
|
+
|
|
212
|
+
Use `/model` in the TUI: without an argument it opens a picker that lists
|
|
213
|
+
catalog models grouped by provider (context window, reasoning capability,
|
|
214
|
+
and the current model are annotated; providers without a resolvable API key
|
|
215
|
+
are dimmed with a hint). `/model <name>` switches directly — unknown names
|
|
216
|
+
fall back to generic OpenAI-compatible defaults. The picker re-reads
|
|
217
|
+
`config.toml` every time it opens, so edits (e.g. a newly added
|
|
218
|
+
`[providers.<id>]`) apply without a restart.
|
|
219
|
+
|
|
220
|
+
A switch takes effect immediately (no restart): the LLM client is rebuilt,
|
|
221
|
+
and the new model is written back to `config.toml` (comments preserved via
|
|
222
|
+
tomlkit) so the next launch keeps it. If the write fails, the switch still
|
|
223
|
+
applies for the current session. Switching is refused while a turn is in
|
|
224
|
+
flight.
|
|
225
|
+
|
|
131
226
|
## Safety
|
|
132
227
|
|
|
133
228
|
Limbo executes every tool call immediately, without asking for confirmation.
|
|
@@ -37,6 +37,40 @@ Built-in providers:
|
|
|
37
37
|
| `deepseek` | openai-completions | `https://api.deepseek.com/v1` | `DEEPSEEK_API_KEY` |
|
|
38
38
|
| `moonshotai` (Kimi) | openai-completions | `https://api.moonshot.ai/v1` | `MOONSHOT_API_KEY` |
|
|
39
39
|
| `kimi-coding` (Kimi For Coding) | anthropic-messages | `https://api.kimi.com/coding` | `KIMI_API_KEY` |
|
|
40
|
+
| `glm` (GLM Coding Plan) | openai-completions | `https://open.bigmodel.cn/api/coding/paas/v4` | `ZHIPUAI_API_KEY` |
|
|
41
|
+
| `codex` (OpenAI Codex) | openai-responses | `https://api.openai.com/v1` (override with your relay) | `CODEX_API_KEY` |
|
|
42
|
+
|
|
43
|
+
Built-in Codex models include `gpt-5.5`, `gpt-5.6-sol`/`terra`/`luna`,
|
|
44
|
+
`gpt-5.4`, `gpt-5.4-mini`, and `gpt-5.3-codex-spark`. **`gpt-5.5` is
|
|
45
|
+
verified by live test against the target relay** (its 1M context is
|
|
46
|
+
relay-reported); the other entries mirror pi's built-in catalog and are
|
|
47
|
+
unverified — availability depends on the relay in use, and unknown or
|
|
48
|
+
unsupported models fall back to the generic OpenAI-compatible defaults.
|
|
49
|
+
Codex speaks the
|
|
50
|
+
OpenAI **Responses API** (`POST {base_url}/responses`), served by a
|
|
51
|
+
dedicated client (`src/limbo/llm/responses_client.py`, plain httpx SSE):
|
|
52
|
+
system messages become `instructions`, tool definitions are flattened, and
|
|
53
|
+
reasoning models stream `reasoning` summaries and replay encrypted
|
|
54
|
+
reasoning items across turns. Limbo targets **API-key relays**, not the
|
|
55
|
+
ChatGPT subscription backend (OAuth) — point the provider at your relay
|
|
56
|
+
and make sure its model IDs match the catalog:
|
|
57
|
+
|
|
58
|
+
```toml
|
|
59
|
+
[llm]
|
|
60
|
+
model = "gpt-5.5"
|
|
61
|
+
|
|
62
|
+
[providers.codex]
|
|
63
|
+
base_url = "https://my-relay.example.com/v1"
|
|
64
|
+
api_key_env = "CODEX_API_KEY"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Built-in GLM Coding Plan models: `glm-4.7`, `glm-5.1`, `glm-5.2` (1M
|
|
68
|
+
context), `glm-5-turbo`, `glm-5v-turbo` (vision), and `glm-4.5-air`. The
|
|
69
|
+
Coding Plan is a subscription with its own endpoint and keys — a coding-plan
|
|
70
|
+
key only works on the `/api/coding/paas/v4` endpoints (not the pay-per-token
|
|
71
|
+
`/api/paas/v4` ones) and vice versa. For the international endpoint set
|
|
72
|
+
`[providers.glm] base_url = "https://api.z.ai/api/coding/paas/v4"` (see
|
|
73
|
+
below).
|
|
40
74
|
|
|
41
75
|
Built-in Kimi models include `kimi-k3` (1M context, pay-per-token), and for
|
|
42
76
|
Kimi For Coding subscriptions: `k3` (1M context), `kimi-for-coding`, and
|
|
@@ -60,14 +94,47 @@ assistant thinking blocks with their signature.
|
|
|
60
94
|
|
|
61
95
|
For the mainland-China Moonshot endpoint, set `base_url =
|
|
62
96
|
"https://api.moonshot.cn/v1"` explicitly (a configured `base_url` always
|
|
63
|
-
wins over the catalog
|
|
97
|
+
wins over the catalog — unless a `[providers.<id>]` override says otherwise,
|
|
98
|
+
see below).
|
|
99
|
+
|
|
100
|
+
### Per-provider overrides
|
|
101
|
+
|
|
102
|
+
An optional `[providers.<id>]` section overrides a single catalog provider
|
|
103
|
+
without touching the global `[llm]` settings — e.g. pointing a provider at a
|
|
104
|
+
relay, renaming its credential env var, or adding extra headers:
|
|
105
|
+
|
|
106
|
+
```toml
|
|
107
|
+
[providers.glm]
|
|
108
|
+
base_url = "https://api.z.ai/api/coding/paas/v4" # international endpoint
|
|
109
|
+
|
|
110
|
+
[providers.codex]
|
|
111
|
+
base_url = "https://my-relay.example.com/v1"
|
|
112
|
+
api_key_env = "CODEX_API_KEY" # rename the env var read for the key
|
|
113
|
+
headers = { x-relay = "on" } # extra headers on every request
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
All fields are optional; unset fields fall back to the catalog. Resolution
|
|
117
|
+
order for `base_url` (first hit wins):
|
|
118
|
+
|
|
119
|
+
1. `[providers.<id>] base_url`
|
|
120
|
+
2. `[llm] base_url` (when changed from the DeepSeek default)
|
|
121
|
+
3. the catalog provider's built-in endpoint
|
|
122
|
+
|
|
123
|
+
and for the API key: `[providers.<id>] api_key` → `[llm] api_key` → the
|
|
124
|
+
environment variable (`[providers.<id>] api_key_env` rename, else the
|
|
125
|
+
catalog's default env var).
|
|
126
|
+
|
|
127
|
+
> **Note:** rule 1 is the single exception to the long-standing "an explicit
|
|
128
|
+
> `[llm] base_url` always wins" behavior — a per-provider override is more
|
|
129
|
+
> specific than the global setting. If you configure both, the
|
|
130
|
+
> `[providers.<id>]` value is used for that provider's models.
|
|
64
131
|
|
|
65
132
|
### Optional LLM settings
|
|
66
133
|
|
|
67
134
|
```toml
|
|
68
135
|
[llm]
|
|
69
136
|
temperature = 0.2 # 0.0 - 2.0, default 0.2
|
|
70
|
-
max_iterations =
|
|
137
|
+
max_iterations = 50 # safety limit on tool-turn loops, default 50
|
|
71
138
|
max_tokens = 8192 # output cap; default = the model's catalog value
|
|
72
139
|
thinking_effort = "high" # reasoning control; default = provider behavior
|
|
73
140
|
```
|
|
@@ -82,6 +149,13 @@ thinking_effort = "high" # reasoning control; default = provider behavior
|
|
|
82
149
|
- `kimi-k2-thinking`, `kimi-k2.5`+ (DeepSeek-style): any non-`off` value →
|
|
83
150
|
`thinking: {type: enabled}`; `"off"` → `thinking: {type: disabled}`
|
|
84
151
|
(except `kimi-k2.7-code*`, where thinking is always on).
|
|
152
|
+
- `glm-*` (z.ai-style): like DeepSeek-style but with `clear_thinking: false`
|
|
153
|
+
so thinking is preserved across turns; `glm-5.2` additionally maps
|
|
154
|
+
`low`/`high`/`max` to `reasoning_effort` (`low` clamps to `high`).
|
|
155
|
+
- `gpt-5.*` codex models (Responses API): `low` | `medium` | `high` |
|
|
156
|
+
`xhigh` → `reasoning: {effort, summary: auto}`; 5.6-generation models
|
|
157
|
+
(`gpt-5.6-*`) also accept `max`. Temperature is omitted for reasoning
|
|
158
|
+
models.
|
|
85
159
|
- Non-reasoning models: ignored.
|
|
86
160
|
|
|
87
161
|
Reasoning output streams into the chat as muted thinking blocks and is
|
|
@@ -108,8 +182,25 @@ you no longer need them.
|
|
|
108
182
|
|
|
109
183
|
```bash
|
|
110
184
|
limbo --workdir /path/to/project
|
|
185
|
+
limbo --model glm-4.7 # override the configured model for this run
|
|
111
186
|
```
|
|
112
187
|
|
|
188
|
+
### Switching models at runtime
|
|
189
|
+
|
|
190
|
+
Use `/model` in the TUI: without an argument it opens a picker that lists
|
|
191
|
+
catalog models grouped by provider (context window, reasoning capability,
|
|
192
|
+
and the current model are annotated; providers without a resolvable API key
|
|
193
|
+
are dimmed with a hint). `/model <name>` switches directly — unknown names
|
|
194
|
+
fall back to generic OpenAI-compatible defaults. The picker re-reads
|
|
195
|
+
`config.toml` every time it opens, so edits (e.g. a newly added
|
|
196
|
+
`[providers.<id>]`) apply without a restart.
|
|
197
|
+
|
|
198
|
+
A switch takes effect immediately (no restart): the LLM client is rebuilt,
|
|
199
|
+
and the new model is written back to `config.toml` (comments preserved via
|
|
200
|
+
tomlkit) so the next launch keeps it. If the write fails, the switch still
|
|
201
|
+
applies for the current session. Switching is refused while a turn is in
|
|
202
|
+
flight.
|
|
203
|
+
|
|
113
204
|
## Safety
|
|
114
205
|
|
|
115
206
|
Limbo executes every tool call immediately, without asking for confirmation.
|