limbo-code 0.1.9__tar.gz → 0.1.11__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.9 → limbo_code-0.1.11}/.github/workflows/test.yml +13 -1
- {limbo_code-0.1.9 → limbo_code-0.1.11}/AGENTS.md +26 -9
- limbo_code-0.1.11/Makefile +23 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/PKG-INFO +34 -31
- {limbo_code-0.1.9 → limbo_code-0.1.11}/README.md +33 -22
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/session-management.md +2 -2
- {limbo_code-0.1.9 → limbo_code-0.1.11}/pyproject.toml +5 -1
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/agent.py +354 -26
- limbo_code-0.1.11/src/limbo/btw.py +72 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/config.py +10 -1
- limbo_code-0.1.11/src/limbo/goal.py +289 -0
- limbo_code-0.1.11/src/limbo/goal_driver.py +315 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/history.py +22 -5
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/llm/anthropic_client.py +23 -3
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/llm/catalog.py +18 -5
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/llm/openai_client.py +82 -4
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/llm/responses_client.py +39 -19
- limbo_code-0.1.11/src/limbo/llm/sse.py +49 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/models.py +9 -1
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/prompt.py +9 -1
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/sessions.py +3 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/tools/base.py +33 -4
- limbo_code-0.1.11/src/limbo/tools/bash.py +426 -0
- limbo_code-0.1.11/src/limbo/tools/edit.py +253 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/tools/find.py +28 -0
- limbo_code-0.1.11/src/limbo/tools/grep.py +147 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/tools/ls.py +24 -3
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/tools/read.py +69 -7
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/tools/registry.py +25 -3
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/trace.py +10 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/app.tcss +49 -0
- limbo_code-0.1.11/src/limbo/ui/screens/btw.py +91 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/screens/main.py +340 -14
- limbo_code-0.1.11/src/limbo/ui/screens/verify_picker.py +59 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/widgets/chat.py +11 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/widgets/input.py +8 -7
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/widgets/status_bar.py +41 -1
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/widgets/tool_card.py +10 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_agent.py +282 -10
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_anthropic_client.py +38 -0
- limbo_code-0.1.11/tests/test_btw.py +90 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_catalog.py +18 -1
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_config.py +5 -5
- limbo_code-0.1.11/tests/test_goal.py +245 -0
- limbo_code-0.1.11/tests/test_goal_driver.py +357 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_history.py +27 -0
- limbo_code-0.1.11/tests/test_interrupt.py +564 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_llm_client.py +100 -2
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_llm_scaffold.py +3 -3
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_model_switch.py +10 -10
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_prompt.py +18 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_responses_client.py +44 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_sessions.py +1 -1
- limbo_code-0.1.11/tests/test_sse.py +92 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_trace.py +13 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/tools/test_base.py +5 -2
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/tools/test_bash.py +77 -2
- limbo_code-0.1.11/tests/tools/test_edit.py +289 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/tools/test_find.py +37 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/tools/test_grep.py +45 -37
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/tools/test_ls.py +19 -1
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/tools/test_read.py +66 -0
- limbo_code-0.1.11/tests/ui/__snapshots__/test_snapshots/test_snapshot_idle.raw +172 -0
- limbo_code-0.1.11/tests/ui/__snapshots__/test_snapshots/test_snapshot_thinking_and_running_tool.raw +174 -0
- limbo_code-0.1.11/tests/ui/__snapshots__/test_snapshots/test_snapshot_tool_error_and_error_message.raw +175 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_app_smoke.py +1 -1
- limbo_code-0.1.11/tests/ui/test_btw_ui.py +158 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_compact_ui.py +1 -1
- limbo_code-0.1.11/tests/ui/test_goal_ui.py +300 -0
- limbo_code-0.1.11/tests/ui/test_interrupt_ui.py +241 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_model_picker.py +16 -16
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_steer_ui.py +21 -11
- {limbo_code-0.1.9 → limbo_code-0.1.11}/uv.lock +12 -9
- limbo_code-0.1.9/src/limbo/llm/sse.py +0 -31
- limbo_code-0.1.9/src/limbo/tools/bash.py +0 -204
- limbo_code-0.1.9/src/limbo/tools/edit.py +0 -74
- limbo_code-0.1.9/src/limbo/tools/grep.py +0 -190
- limbo_code-0.1.9/tests/tools/test_edit.py +0 -130
- limbo_code-0.1.9/tests/ui/__snapshots__/test_snapshots/test_snapshot_idle.raw +0 -172
- limbo_code-0.1.9/tests/ui/__snapshots__/test_snapshots/test_snapshot_thinking_and_running_tool.raw +0 -174
- limbo_code-0.1.9/tests/ui/__snapshots__/test_snapshots/test_snapshot_tool_error_and_error_message.raw +0 -175
- {limbo_code-0.1.9 → limbo_code-0.1.11}/.agents/skills/grill-with-docs/SKILL.md +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/.agents/skills/grill-with-docs/agents/openai.yaml +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/.agents/skills/improve-codebase-architecture/HTML-REPORT.md +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/.agents/skills/improve-codebase-architecture/SKILL.md +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/.agents/skills/improve-codebase-architecture/agents/openai.yaml +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/.agents/skills/tdd/SKILL.md +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/.agents/skills/tdd/agents/openai.yaml +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/.agents/skills/tdd/mocking.md +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/.agents/skills/tdd/tests.md +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/.github/workflows/publish.yml +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/.gitignore +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/CONTEXT.md +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/design/confirm_view.html +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/design/limbo-ui-minimal.md +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/design/limbo-ui-redesign.md +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/design/prototype-minimal-confirm.png +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/design/prototype-minimal.html +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/design/prototype-minimal.png +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/design/prototype.html +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/design/prototype.png +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/assets/limbo-current-ui.png +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/assets/limbo-new-ui.png +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/assets/walkthrough/limbo-dark/1_idle.svg +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/assets/walkthrough/limbo-dark/2_thinking_tool_running.svg +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/assets/walkthrough/limbo-dark/3_tool_success.svg +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/assets/walkthrough/limbo-dark/4_tool_error.svg +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/assets/walkthrough/limbo-dark/5_llm_error.svg +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/assets/walkthrough/limbo-dark/6_edit_diff.svg +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/assets/walkthrough/limbo-light/1_idle.svg +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/assets/walkthrough/limbo-light/2_thinking_tool_running.svg +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/assets/walkthrough/limbo-light/3_tool_success.svg +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/assets/walkthrough/limbo-light/4_tool_error.svg +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/assets/walkthrough/limbo-light/5_llm_error.svg +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/assets/walkthrough/limbo-light/6_edit_diff.svg +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/skills.md +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/docs/ui-redesign-proposal.md +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/scripts/check_contrast.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/scripts/gen_banner.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/scripts/ui_walkthrough.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/skills-lock.json +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/__init__.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/__main__.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/app.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/attachments.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/compaction.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/llm/__init__.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/llm/client.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/llm/factory.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/llm/retry.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/llm/scaffold.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/llm/usage.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/model_switch.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/skills.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/steer.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/tools/__init__.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/tools/ignore.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/tools/mutation_queue.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/tools/write.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/__init__.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/app.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/banner.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/clipboard.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/commands.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/contrast.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/screens/__init__.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/screens/game2048.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/screens/model_picker.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/screens/session_picker.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/syntax.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/theme.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/widgets/__init__.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/ui/widgets/command_menu.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/src/limbo/user_paths.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/conftest.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_attachments.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_cli.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_clipboard.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_compaction.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_integration.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_models.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_retry.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_skills.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_steer.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_usage.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/test_user_paths.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/tools/test_ignore.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/tools/test_mutation_queue.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/tools/test_registry.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/tools/test_write.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/__snapshots__/test_snapshots/test_snapshot_session_picker.raw +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_command_menu.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_commands.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_contrast.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_game2048.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_input_attachments.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_input_history.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_input_paste.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_main_screen.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_scroll_follow.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_sessions_ui.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_skills_ui.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_snapshots.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_startup_art.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_theme.py +0 -0
- {limbo_code-0.1.9 → limbo_code-0.1.11}/tests/ui/test_widgets.py +0 -0
|
@@ -25,12 +25,24 @@ jobs:
|
|
|
25
25
|
with:
|
|
26
26
|
python-version: ${{ matrix.python-version }}
|
|
27
27
|
|
|
28
|
+
# grep hard-depends on ripgrep (no Python fallback since LIM-24).
|
|
29
|
+
- name: Install ripgrep
|
|
30
|
+
run: sudo apt-get install -y ripgrep
|
|
31
|
+
|
|
28
32
|
# Install from uv.lock so CI renders snapshots with the exact same
|
|
29
33
|
# textual/rich/syrupy versions the baselines were generated with.
|
|
34
|
+
# Dev tools (pytest/ruff/mypy) are a PEP 735 dependency group and are
|
|
35
|
+
# installed by default — no --extra flag.
|
|
30
36
|
- name: Install dependencies
|
|
31
|
-
run: uv sync --frozen
|
|
37
|
+
run: uv sync --frozen
|
|
32
38
|
env:
|
|
33
39
|
UV_PYTHON: ${{ matrix.python-version }}
|
|
34
40
|
|
|
35
41
|
- name: Run tests
|
|
36
42
|
run: uv run python -m pytest -v
|
|
43
|
+
|
|
44
|
+
- name: Lint
|
|
45
|
+
run: uv run ruff check src tests
|
|
46
|
+
|
|
47
|
+
- name: Type check
|
|
48
|
+
run: uv run mypy src
|
|
@@ -2,15 +2,19 @@
|
|
|
2
2
|
|
|
3
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
4
|
|
|
5
|
-
**Tools execute immediately — no confirmation flow.**
|
|
5
|
+
**Tools execute immediately — no confirmation flow.** Convenience guardrails (not a security boundary): a workdir scope on file tools (+ session-scoped grants from user-mentioned paths), a sensitive-file skip list shared by `read`/`grep`/`find`, and a heuristic dangerous-command filter on `bash` (best-effort, rejects matches outright; `bash` is otherwise not covered by any guardrail).
|
|
6
6
|
|
|
7
7
|
## Quick Start
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
11
|
-
limbo --workdir /path/to/project # config: ~/.limbo/config.toml
|
|
10
|
+
uv sync # installs runtime + dev tools (PEP 735 dev group, on by default)
|
|
11
|
+
uv run limbo --workdir /path/to/project # config: ~/.limbo/config.toml
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
+
Prefer `uv` over `pip`: dev tools (pytest/ruff/mypy) are a `[dependency-groups]`
|
|
15
|
+
group, NOT an extra — `pip install -e ".[dev]"` no longer exists. `pip install -e .`
|
|
16
|
+
still works for a runtime-only install.
|
|
17
|
+
|
|
14
18
|
## Architecture
|
|
15
19
|
|
|
16
20
|
```
|
|
@@ -25,6 +29,8 @@ src/limbo/
|
|
|
25
29
|
├── model_switch.py # /model domain logic: validate, swap client, persist (UI is an adapter)
|
|
26
30
|
├── prompt.py # System-prompt assembly (tools section derived from the registry)
|
|
27
31
|
├── steer.py # Mid-turn steer queue (LIM-20): queueing semantics, cancel boundary
|
|
32
|
+
├── goal.py # /goal closed-loop state machine, prompt templates, verify executor (LIM-40)
|
|
33
|
+
├── goal_driver.py # /goal orchestrator (frontend-agnostic, non-UI): turn → verify → next round
|
|
28
34
|
├── sessions.py # Session JSONL save/load/list/export
|
|
29
35
|
├── trace.py # Append-only JSONL run log (sessions/traces/)
|
|
30
36
|
├── skills.py # SKILL.md discovery (user + project dirs)
|
|
@@ -33,7 +39,7 @@ src/limbo/
|
|
|
33
39
|
│ # openai_client.py, anthropic_client.py, responses_client.py, retry.py, sse.py,
|
|
34
40
|
│ # usage.py (token accounting: usage normalization + prompt-size estimation),
|
|
35
41
|
│ # scaffold.py (plumbing shared by dialect clients: credentials, retry, images)
|
|
36
|
-
├── tools/ # base.py (BaseTool +
|
|
42
|
+
├── tools/ # base.py (BaseTool + path guardrail + truncation), registry.py (dispatch, grants),
|
|
37
43
|
│ # mutation_queue.py (per-file locks), ignore.py (.gitignore),
|
|
38
44
|
│ # read/bash/edit/write/grep/find/ls.py
|
|
39
45
|
└── ui/ # app.py + app.tcss (ALL styles here; theme vars only, no bare hex),
|
|
@@ -47,7 +53,7 @@ src/limbo/
|
|
|
47
53
|
- `Agent.run()` yields `AgentEvent`: `TextDelta`, `ThinkingDelta`, `ToolCallRequest`, `ToolResultEvent`, `ErrorEvent`, `CompactionEvent`, `UsageUpdate`, `SteerEvent`
|
|
48
54
|
- 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
55
|
- 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
|
|
56
|
+
- `finish_reason` `length`/`max_tokens` → whole batch failed without execution; the error result names the effective `max_tokens` and steers the model to write large scripts via `write` instead of re-issuing the same call. 3 consecutive length stops abort the turn with actionable guidance (trace `kind=length_stop_loop`); a text-only truncated response emits a "may be incomplete" warning
|
|
51
57
|
- Mid-turn user input queues as *steer* (LIM-20): injected at loop top, or as turn-end follow-up
|
|
52
58
|
- Reasoning is stored on assistant messages and replayed per dialect (Anthropic thinking signatures, Kimi `reasoning_content`, Responses encrypted items)
|
|
53
59
|
- Usage counters normalize in `llm/usage.py` (`input_tokens`+`cache_read`, `prompt_tokens`, DeepSeek cache hits) and feed the compaction trigger via `PromptSizeEstimator`
|
|
@@ -61,6 +67,7 @@ Providers/models live in `llm/catalog.py`; unknown models get generic OpenAI-com
|
|
|
61
67
|
# max_tokens, thinking_effort, max_retries, timeout, ...
|
|
62
68
|
[tools] # bash_enabled = true, parallel = true
|
|
63
69
|
[compaction] # enabled, reserve_tokens = 16384, keep_recent_tokens = 20000
|
|
70
|
+
[goal] # max_rounds = 10, verify_timeout_ms = 600000
|
|
64
71
|
[safety] # dangerous_commands, sensitive_files, auto_grant_user_paths
|
|
65
72
|
[ui] # theme, show_banner
|
|
66
73
|
[providers.<id>] # base_url / api_key / api_key_env / headers
|
|
@@ -72,7 +79,9 @@ Skills: `~/.limbo/skills/<name>/SKILL.md` (user) and `<workdir>/.agents/skills/<
|
|
|
72
79
|
|
|
73
80
|
## UI
|
|
74
81
|
|
|
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).
|
|
82
|
+
Single column: status bar (state/elapsed/tokens/model/workdir/queued + rainbow 🎯GOAL badge while a goal loop runs) → 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 /goal` + skill commands. After palette changes run `python scripts/check_contrast.py` (≥ 4.5:1).
|
|
83
|
+
|
|
84
|
+
`/goal <text>` starts closed-loop mode (LIM-40): the first round is a proposal round — the model explores the repo and proposes acceptance command(s) in a `<verify_proposal>` block; the user confirms in a picker (accept / edit / skip), then `GoalDriver` (non-UI, shared by any frontend) runs each round as a complete `Agent.run()` turn and executes the verify command; exit 0 ends the loop, otherwise the failure output is fed back verbatim into the next round until `max_rounds`, when a no-tool wrap-up turn summarizes and any user message resumes with a fresh budget. Goal state persists on `SessionMeta.goal`. Esc during verify cancels the subprocess.
|
|
76
85
|
|
|
77
86
|
## Key Decisions
|
|
78
87
|
|
|
@@ -83,9 +92,17 @@ Single column: status bar (state/elapsed/tokens/model/workdir/queued) → scroll
|
|
|
83
92
|
## Testing
|
|
84
93
|
|
|
85
94
|
```bash
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
95
|
+
make check # single entrypoint: sync + tests + lint + type check
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Or individually — always `python -m pytest`, never bare `uv run pytest` (which
|
|
99
|
+
silently falls through to a global pytest on PATH when the venv lacks pytest):
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
uv sync # first time / after pulling; dev group is default
|
|
103
|
+
uv run python -m pytest tests/ -v # pytest-asyncio, respx HTTP mocks, pytest-textual-snapshot
|
|
104
|
+
uv run ruff check src tests && uv run mypy src # lint + type check
|
|
105
|
+
uv run python -m pytest --snapshot-update # after intentional visual changes; review the SVG diff
|
|
89
106
|
```
|
|
90
107
|
|
|
91
108
|
## Adding Things
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Single entrypoint for dev checks — humans and agents should only need
|
|
2
|
+
# `make check`. Dev tools (pytest/ruff/mypy) are a PEP 735 dependency group,
|
|
3
|
+
# so plain `uv sync` installs them.
|
|
4
|
+
#
|
|
5
|
+
# Note: tests run via `uv run python -m pytest`, NOT `uv run pytest` — the
|
|
6
|
+
# latter silently falls through to a global pytest on PATH when the venv
|
|
7
|
+
# lacks pytest, which can run the suite against the wrong source tree.
|
|
8
|
+
|
|
9
|
+
.PHONY: sync test lint typecheck check
|
|
10
|
+
|
|
11
|
+
sync:
|
|
12
|
+
uv sync
|
|
13
|
+
|
|
14
|
+
test:
|
|
15
|
+
uv run python -m pytest tests/ -v
|
|
16
|
+
|
|
17
|
+
lint:
|
|
18
|
+
uv run ruff check src tests
|
|
19
|
+
|
|
20
|
+
typecheck:
|
|
21
|
+
uv run mypy src
|
|
22
|
+
|
|
23
|
+
check: sync test lint typecheck
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: limbo-code
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.11
|
|
4
4
|
Summary: A minimal terminal AI coding agent
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Requires-Dist: httpx>=0.27
|
|
@@ -10,14 +10,6 @@ Requires-Dist: pygments>=2.17
|
|
|
10
10
|
Requires-Dist: textual>=8.0
|
|
11
11
|
Requires-Dist: toml>=0.10
|
|
12
12
|
Requires-Dist: tomlkit>=0.13
|
|
13
|
-
Provides-Extra: dev
|
|
14
|
-
Requires-Dist: mypy>=1.10; extra == 'dev'
|
|
15
|
-
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
|
|
16
|
-
Requires-Dist: pytest-textual-snapshot>=1.0; extra == 'dev'
|
|
17
|
-
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
18
|
-
Requires-Dist: respx>=0.21; extra == 'dev'
|
|
19
|
-
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
20
|
-
Requires-Dist: types-pygments>=2.17; extra == 'dev'
|
|
21
13
|
Description-Content-Type: text/markdown
|
|
22
14
|
|
|
23
15
|
# Limbo
|
|
@@ -29,7 +21,7 @@ A minimal terminal AI coding agent.
|
|
|
29
21
|
Requires Python 3.11 or later.
|
|
30
22
|
|
|
31
23
|
```bash
|
|
32
|
-
pip install -
|
|
24
|
+
pip install limbo-code
|
|
33
25
|
```
|
|
34
26
|
|
|
35
27
|
## Configure
|
|
@@ -39,7 +31,7 @@ Create `~/.limbo/config.toml`:
|
|
|
39
31
|
```toml
|
|
40
32
|
[llm]
|
|
41
33
|
api_key = "your-api-key"
|
|
42
|
-
model = "deepseek-
|
|
34
|
+
model = "deepseek-v4-pro"
|
|
43
35
|
base_url = "https://api.deepseek.com/v1"
|
|
44
36
|
```
|
|
45
37
|
|
|
@@ -226,23 +218,24 @@ flight.
|
|
|
226
218
|
## Safety
|
|
227
219
|
|
|
228
220
|
Limbo executes every tool call immediately, without asking for confirmation.
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
221
|
+
|
|
222
|
+
File tools (`read`, `edit`, `write`, `grep`, `find`, `ls`) are scoped to the
|
|
223
|
+
current working directory plus any session grants (paths the user mentions in
|
|
224
|
+
a message), and `read`/`grep`/`find` skip a small blocklist of sensitive files
|
|
225
|
+
(`.env`, SSH keys) to avoid leaking secrets into the model context by accident.
|
|
226
|
+
**These are convenience guardrails against accidents, not a security boundary.**
|
|
227
|
+
The path check resolves the path before each operation, so a symlink swapped
|
|
228
|
+
between the check and the operation (a time-of-check-to-time-of-use race) could
|
|
229
|
+
escape it.
|
|
234
230
|
|
|
235
231
|
Bash is an exception: it is started in the working directory but is **not**
|
|
236
|
-
|
|
237
|
-
the workdir.
|
|
238
|
-
or `git reset --hard` are **rejected outright
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
(`git -C /foo reset --hard`), variable assignments before the command name
|
|
244
|
-
(`VAR=1 rm -rf /`), and similar shell constructs. Only run Limbo with
|
|
245
|
-
trusted commands and in repositories you can afford to modify or lose.
|
|
232
|
+
covered by any of these guardrails. Commands can `cd ..`, use absolute paths,
|
|
233
|
+
and read or write outside the workdir. Commands that match dangerous patterns
|
|
234
|
+
such as `rm` or `git reset --hard` are **rejected outright** by a best-effort
|
|
235
|
+
heuristic filter — ordinary shell constructs can bypass it, so it only
|
|
236
|
+
protects against accidents. The pattern list is configurable but cannot be
|
|
237
|
+
disabled from the UI. Only run Limbo with trusted commands and in
|
|
238
|
+
repositories you can afford to modify or lose.
|
|
246
239
|
|
|
247
240
|
If you need to work with untrusted projects, disable the bash tool entirely:
|
|
248
241
|
|
|
@@ -253,15 +246,25 @@ bash_enabled = false
|
|
|
253
246
|
|
|
254
247
|
## Development
|
|
255
248
|
|
|
256
|
-
|
|
249
|
+
Set up the environment (dev tools are a PEP 735 dependency group, so plain
|
|
250
|
+
`uv sync` installs pytest/ruff/mypy — no `--extra` flag):
|
|
251
|
+
|
|
252
|
+
```bash
|
|
253
|
+
uv sync
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
Run everything (sync + tests + lint + type check) with one command:
|
|
257
257
|
|
|
258
258
|
```bash
|
|
259
|
-
|
|
259
|
+
make check
|
|
260
260
|
```
|
|
261
261
|
|
|
262
|
-
|
|
262
|
+
Or run the pieces individually. Always use `python -m pytest`: `uv run pytest`
|
|
263
|
+
silently falls through to a global pytest on PATH when the venv lacks pytest,
|
|
264
|
+
which can run the suite against the wrong source tree.
|
|
263
265
|
|
|
264
266
|
```bash
|
|
265
|
-
|
|
266
|
-
|
|
267
|
+
uv run python -m pytest tests/ -v # tests
|
|
268
|
+
uv run ruff check src tests # lint
|
|
269
|
+
uv run mypy src # type check
|
|
267
270
|
```
|
|
@@ -7,7 +7,7 @@ A minimal terminal AI coding agent.
|
|
|
7
7
|
Requires Python 3.11 or later.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
pip install -
|
|
10
|
+
pip install limbo-code
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
## Configure
|
|
@@ -17,7 +17,7 @@ Create `~/.limbo/config.toml`:
|
|
|
17
17
|
```toml
|
|
18
18
|
[llm]
|
|
19
19
|
api_key = "your-api-key"
|
|
20
|
-
model = "deepseek-
|
|
20
|
+
model = "deepseek-v4-pro"
|
|
21
21
|
base_url = "https://api.deepseek.com/v1"
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -204,23 +204,24 @@ flight.
|
|
|
204
204
|
## Safety
|
|
205
205
|
|
|
206
206
|
Limbo executes every tool call immediately, without asking for confirmation.
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
207
|
+
|
|
208
|
+
File tools (`read`, `edit`, `write`, `grep`, `find`, `ls`) are scoped to the
|
|
209
|
+
current working directory plus any session grants (paths the user mentions in
|
|
210
|
+
a message), and `read`/`grep`/`find` skip a small blocklist of sensitive files
|
|
211
|
+
(`.env`, SSH keys) to avoid leaking secrets into the model context by accident.
|
|
212
|
+
**These are convenience guardrails against accidents, not a security boundary.**
|
|
213
|
+
The path check resolves the path before each operation, so a symlink swapped
|
|
214
|
+
between the check and the operation (a time-of-check-to-time-of-use race) could
|
|
215
|
+
escape it.
|
|
212
216
|
|
|
213
217
|
Bash is an exception: it is started in the working directory but is **not**
|
|
214
|
-
|
|
215
|
-
the workdir.
|
|
216
|
-
or `git reset --hard` are **rejected outright
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
(`git -C /foo reset --hard`), variable assignments before the command name
|
|
222
|
-
(`VAR=1 rm -rf /`), and similar shell constructs. Only run Limbo with
|
|
223
|
-
trusted commands and in repositories you can afford to modify or lose.
|
|
218
|
+
covered by any of these guardrails. Commands can `cd ..`, use absolute paths,
|
|
219
|
+
and read or write outside the workdir. Commands that match dangerous patterns
|
|
220
|
+
such as `rm` or `git reset --hard` are **rejected outright** by a best-effort
|
|
221
|
+
heuristic filter — ordinary shell constructs can bypass it, so it only
|
|
222
|
+
protects against accidents. The pattern list is configurable but cannot be
|
|
223
|
+
disabled from the UI. Only run Limbo with trusted commands and in
|
|
224
|
+
repositories you can afford to modify or lose.
|
|
224
225
|
|
|
225
226
|
If you need to work with untrusted projects, disable the bash tool entirely:
|
|
226
227
|
|
|
@@ -231,15 +232,25 @@ bash_enabled = false
|
|
|
231
232
|
|
|
232
233
|
## Development
|
|
233
234
|
|
|
234
|
-
|
|
235
|
+
Set up the environment (dev tools are a PEP 735 dependency group, so plain
|
|
236
|
+
`uv sync` installs pytest/ruff/mypy — no `--extra` flag):
|
|
237
|
+
|
|
238
|
+
```bash
|
|
239
|
+
uv sync
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
Run everything (sync + tests + lint + type check) with one command:
|
|
235
243
|
|
|
236
244
|
```bash
|
|
237
|
-
|
|
245
|
+
make check
|
|
238
246
|
```
|
|
239
247
|
|
|
240
|
-
|
|
248
|
+
Or run the pieces individually. Always use `python -m pytest`: `uv run pytest`
|
|
249
|
+
silently falls through to a global pytest on PATH when the venv lacks pytest,
|
|
250
|
+
which can run the suite against the wrong source tree.
|
|
241
251
|
|
|
242
252
|
```bash
|
|
243
|
-
|
|
244
|
-
|
|
253
|
+
uv run python -m pytest tests/ -v # tests
|
|
254
|
+
uv run ruff check src tests # lint
|
|
255
|
+
uv run mypy src # type check
|
|
245
256
|
```
|
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
JSONL,首行为 meta,后续为消息行。**向后兼容**:无 meta 行的旧文件可正常加载。
|
|
27
27
|
|
|
28
28
|
```jsonl
|
|
29
|
-
{"type":"meta","id":"20260720-103000-123456-42","workdir":"/path/to/proj","model":"deepseek-
|
|
29
|
+
{"type":"meta","id":"20260720-103000-123456-42","workdir":"/path/to/proj","model":"deepseek-v4-pro","title":"修复 grep 工具 bug","created_at":"...","updated_at":"..."}
|
|
30
30
|
{"role":"system","content":"..."}
|
|
31
31
|
{"role":"user","content":"..."}
|
|
32
32
|
{"role":"assistant","content":"...","tool_calls":[...]}
|
|
@@ -107,7 +107,7 @@ append-only,崩溃/打断最多丢失写入中的一条)。`/export` 默认
|
|
|
107
107
|
| `llm_response` | `duration`、`ttft`、`finish_reason`、`usage`(原始用量,含 provider 缓存字段)、`cached_tokens`(归一化缓存命中)、content/reasoning 长度、tool_calls 摘要 |
|
|
108
108
|
| `llm_error` | `exception_type`、`error`、`traceback` |
|
|
109
109
|
| `tool_call` / `tool_result` | `id`、`name`、`arguments`、`success`、`output`/`error`、耗时;崩溃时带 `exception_type` + `traceback` |
|
|
110
|
-
| `error` | `kind`(如 max_iterations)、`message` |
|
|
110
|
+
| `error` | `kind`(如 max_iterations / length_stop / length_stop_loop)、`message` |
|
|
111
111
|
| `turn_end` | `iterations`、`duration`、`status`(completed) |
|
|
112
112
|
| `session_save_error` | `error` |
|
|
113
113
|
|
|
@@ -16,7 +16,11 @@ dependencies = [
|
|
|
16
16
|
"pygments>=2.17",
|
|
17
17
|
]
|
|
18
18
|
|
|
19
|
-
[
|
|
19
|
+
[dependency-groups]
|
|
20
|
+
# Dev tools live in a PEP 735 dependency group (not an extra) so `uv sync`
|
|
21
|
+
# installs them by default — no `--extra`/`--group` flag needed. Always run
|
|
22
|
+
# tests via `uv run python -m pytest` so a missing venv fails loudly instead
|
|
23
|
+
# of silently falling through to a global pytest on PATH.
|
|
20
24
|
dev = [
|
|
21
25
|
"pytest>=8.0",
|
|
22
26
|
"pytest-asyncio>=0.23",
|