limbo-code 0.2.1__tar.gz → 0.3__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.2.1 → limbo_code-0.3}/AGENTS.md +22 -10
- {limbo_code-0.2.1 → limbo_code-0.3}/CONTEXT.md +16 -1
- limbo_code-0.3/PKG-INFO +245 -0
- limbo_code-0.3/README.md +231 -0
- limbo_code-0.3/docs/assets/limbo-hero.png +0 -0
- limbo_code-0.3/docs/assets/limbo-hero.svg +173 -0
- limbo_code-0.3/docs/assets/limbo-models.png +0 -0
- limbo_code-0.3/docs/assets/limbo-models.svg +173 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/session-management.md +43 -7
- limbo_code-0.3/scripts/readme_hero.py +146 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/scripts/scroll_bench.py +3 -1
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/agent.py +633 -141
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/app.py +41 -4
- limbo_code-0.3/src/limbo/auth.py +100 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/btw.py +10 -6
- limbo_code-0.3/src/limbo/cli_auth.py +187 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/config.py +47 -4
- limbo_code-0.3/src/limbo/conversation.py +282 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/headless.py +275 -155
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/history.py +61 -12
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/integrations/__init__.py +15 -1
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/integrations/base.py +33 -8
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/integrations/herdr.py +3 -0
- limbo_code-0.3/src/limbo/integrations/osc7501.py +111 -0
- limbo_code-0.3/src/limbo/journal.py +301 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/llm/catalog.py +64 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/llm/responses_client.py +6 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/llm/retry.py +51 -16
- limbo_code-0.3/src/limbo/llm/verify.py +58 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/prompt.py +3 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/pump.py +93 -19
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/sessions.py +242 -29
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/steer.py +32 -7
- limbo_code-0.3/src/limbo/toggles.py +18 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/tools/base.py +9 -1
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/tools/find.py +1 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/tools/grep.py +1 -0
- limbo_code-0.3/src/limbo/tools/hooks.py +248 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/tools/ls.py +1 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/tools/read.py +1 -0
- limbo_code-0.3/src/limbo/tools/registry.py +233 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/tools/run_code.py +66 -1
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/trace.py +9 -2
- limbo_code-0.3/src/limbo/tracing/__init__.py +1 -0
- limbo_code-0.3/src/limbo/tracing/command.py +100 -0
- limbo_code-0.3/src/limbo/tracing/index.py +154 -0
- limbo_code-0.3/src/limbo/tracing/model.py +369 -0
- limbo_code-0.3/src/limbo/tracing/server.py +301 -0
- limbo_code-0.3/src/limbo/tracing/static/index.html +560 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/app.py +47 -3
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/app.tcss +181 -61
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/banner.py +24 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/contrast.py +11 -3
- limbo_code-0.3/src/limbo/ui/gradient.py +88 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/key_fixes.py +6 -0
- limbo_code-0.3/src/limbo/ui/motion.py +224 -0
- limbo_code-0.3/src/limbo/ui/omarchy.py +227 -0
- limbo_code-0.3/src/limbo/ui/recent_models.py +31 -0
- limbo_code-0.3/src/limbo/ui/screens/auth_dialog.py +105 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/screens/btw.py +6 -0
- limbo_code-0.3/src/limbo/ui/screens/command_palette.py +206 -0
- limbo_code-0.3/src/limbo/ui/screens/dialog.py +75 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/screens/main.py +481 -131
- limbo_code-0.3/src/limbo/ui/screens/model_picker.py +195 -0
- limbo_code-0.3/src/limbo/ui/screens/session_picker.py +61 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/syntax.py +67 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/theme.py +81 -3
- limbo_code-0.3/src/limbo/ui/widgets/activity.py +107 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/widgets/chat.py +52 -8
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/widgets/input.py +14 -2
- limbo_code-0.3/src/limbo/ui/widgets/key_hints.py +62 -0
- limbo_code-0.3/src/limbo/ui/widgets/landing.py +180 -0
- limbo_code-0.3/src/limbo/ui/widgets/pills.py +64 -0
- limbo_code-0.3/src/limbo/ui/widgets/sidebar.py +342 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/widgets/status_bar.py +80 -21
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/widgets/tool_card.py +87 -12
- {limbo_code-0.2.1 → limbo_code-0.3}/tests/conftest.py +37 -0
- limbo_code-0.3/tests/fixtures/omarchy/README.md +4 -0
- limbo_code-0.3/tests/fixtures/omarchy/catppuccin-latte.toml +31 -0
- limbo_code-0.3/tests/fixtures/omarchy/gruvbox.toml +31 -0
- limbo_code-0.3/tests/fixtures/omarchy/hackerman.toml +33 -0
- limbo_code-0.3/tests/fixtures/omarchy/lumon.toml +34 -0
- limbo_code-0.3/tests/fixtures/omarchy/rose-pine.toml +31 -0
- limbo_code-0.3/tests/fixtures/omarchy/tokyo-night.toml +31 -0
- limbo_code-0.3/tests/test_agent.py +870 -0
- limbo_code-0.3/tests/test_anthropic_client.py +149 -0
- limbo_code-0.3/tests/test_auth.py +46 -0
- limbo_code-0.3/tests/test_btw.py +42 -0
- limbo_code-0.3/tests/test_catalog.py +31 -0
- limbo_code-0.3/tests/test_cli.py +65 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/tests/test_code_mode_agent.py +13 -55
- limbo_code-0.3/tests/test_compaction.py +69 -0
- limbo_code-0.3/tests/test_config.py +44 -0
- limbo_code-0.3/tests/test_conversation.py +105 -0
- limbo_code-0.3/tests/test_goal.py +43 -0
- limbo_code-0.3/tests/test_headless.py +191 -0
- limbo_code-0.3/tests/test_herdr.py +52 -0
- limbo_code-0.3/tests/test_history.py +79 -0
- limbo_code-0.3/tests/test_interrupt.py +159 -0
- limbo_code-0.3/tests/test_llm_client.py +85 -0
- limbo_code-0.3/tests/test_model_switch.py +56 -0
- limbo_code-0.3/tests/test_osc7501.py +48 -0
- limbo_code-0.3/tests/test_plan_mode.py +91 -0
- limbo_code-0.3/tests/test_prompt.py +27 -0
- limbo_code-0.3/tests/test_pump.py +224 -0
- limbo_code-0.3/tests/test_responses_client.py +154 -0
- limbo_code-0.3/tests/test_retry.py +111 -0
- limbo_code-0.3/tests/test_sessions.py +143 -0
- limbo_code-0.3/tests/test_skills.py +33 -0
- limbo_code-0.3/tests/test_sse.py +33 -0
- limbo_code-0.3/tests/test_steer.py +24 -0
- limbo_code-0.3/tests/test_trace.py +19 -0
- limbo_code-0.3/tests/test_usage.py +57 -0
- limbo_code-0.3/tests/tools/test_base.py +52 -0
- limbo_code-0.3/tests/tools/test_bash.py +38 -0
- limbo_code-0.3/tests/tools/test_edit.py +40 -0
- limbo_code-0.3/tests/tools/test_grep.py +25 -0
- limbo_code-0.3/tests/tools/test_hooks.py +134 -0
- limbo_code-0.3/tests/tools/test_ignore.py +20 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/tests/tools/test_mutation_queue.py +0 -19
- limbo_code-0.3/tests/tools/test_read.py +29 -0
- limbo_code-0.3/tests/tools/test_registry.py +23 -0
- limbo_code-0.3/tests/tools/test_run_code.py +67 -0
- limbo_code-0.3/tests/tools/test_write.py +21 -0
- limbo_code-0.3/tests/tracing/test_command.py +54 -0
- limbo_code-0.3/tests/tracing/test_index.py +36 -0
- limbo_code-0.3/tests/tracing/test_model.py +139 -0
- limbo_code-0.3/tests/tracing/test_server.py +101 -0
- limbo_code-0.3/tests/ui/__snapshots__/test_snapshots/test_snapshot_idle.raw +211 -0
- limbo_code-0.3/tests/ui/__snapshots__/test_snapshots/test_snapshot_wide_layout_with_sidebar.raw +237 -0
- limbo_code-0.3/tests/ui/test_app_smoke.py +12 -0
- limbo_code-0.3/tests/ui/test_bang_ui.py +146 -0
- limbo_code-0.3/tests/ui/test_btw_ui.py +82 -0
- limbo_code-0.3/tests/ui/test_command_menu.py +63 -0
- limbo_code-0.3/tests/ui/test_compact_ui.py +63 -0
- limbo_code-0.3/tests/ui/test_goal_ui.py +117 -0
- limbo_code-0.3/tests/ui/test_input_paste.py +63 -0
- limbo_code-0.3/tests/ui/test_interrupt_ui.py +90 -0
- limbo_code-0.3/tests/ui/test_main_screen.py +49 -0
- limbo_code-0.3/tests/ui/test_model_picker.py +171 -0
- limbo_code-0.3/tests/ui/test_omarchy.py +49 -0
- limbo_code-0.3/tests/ui/test_path_input.py +96 -0
- limbo_code-0.3/tests/ui/test_scroll_follow.py +44 -0
- limbo_code-0.3/tests/ui/test_sessions_ui.py +82 -0
- limbo_code-0.3/tests/ui/test_sidebar.py +76 -0
- limbo_code-0.3/tests/ui/test_skills_ui.py +81 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/tests/ui/test_snapshots.py +29 -60
- limbo_code-0.3/tests/ui/test_steer_ui.py +146 -0
- limbo_code-0.3/tests/ui/test_tracing_viewer_ui.py +50 -0
- limbo_code-0.3/tests/ui/test_widgets.py +38 -0
- limbo_code-0.2.1/PKG-INFO +0 -311
- limbo_code-0.2.1/README.md +0 -297
- limbo_code-0.2.1/src/limbo/tools/registry.py +0 -101
- limbo_code-0.2.1/src/limbo/ui/screens/model_picker.py +0 -122
- limbo_code-0.2.1/src/limbo/ui/screens/session_picker.py +0 -61
- limbo_code-0.2.1/tests/test_agent.py +0 -2397
- limbo_code-0.2.1/tests/test_anthropic_client.py +0 -644
- limbo_code-0.2.1/tests/test_attachments.py +0 -78
- limbo_code-0.2.1/tests/test_btw.py +0 -90
- limbo_code-0.2.1/tests/test_catalog.py +0 -372
- limbo_code-0.2.1/tests/test_cli.py +0 -296
- limbo_code-0.2.1/tests/test_clipboard.py +0 -205
- limbo_code-0.2.1/tests/test_code_mode.py +0 -77
- limbo_code-0.2.1/tests/test_compaction.py +0 -163
- limbo_code-0.2.1/tests/test_config.py +0 -293
- limbo_code-0.2.1/tests/test_goal.py +0 -228
- limbo_code-0.2.1/tests/test_headless.py +0 -578
- limbo_code-0.2.1/tests/test_herdr.py +0 -164
- limbo_code-0.2.1/tests/test_history.py +0 -94
- limbo_code-0.2.1/tests/test_integrations.py +0 -145
- limbo_code-0.2.1/tests/test_interrupt.py +0 -570
- limbo_code-0.2.1/tests/test_llm_client.py +0 -755
- limbo_code-0.2.1/tests/test_llm_scaffold.py +0 -89
- limbo_code-0.2.1/tests/test_model_switch.py +0 -140
- limbo_code-0.2.1/tests/test_models.py +0 -23
- limbo_code-0.2.1/tests/test_prompt.py +0 -103
- limbo_code-0.2.1/tests/test_pump.py +0 -423
- limbo_code-0.2.1/tests/test_responses_client.py +0 -626
- limbo_code-0.2.1/tests/test_retry.py +0 -306
- limbo_code-0.2.1/tests/test_sessions.py +0 -468
- limbo_code-0.2.1/tests/test_skills.py +0 -187
- limbo_code-0.2.1/tests/test_sse.py +0 -92
- limbo_code-0.2.1/tests/test_steer.py +0 -66
- limbo_code-0.2.1/tests/test_trace.py +0 -132
- limbo_code-0.2.1/tests/test_usage.py +0 -159
- limbo_code-0.2.1/tests/tools/test_base.py +0 -184
- limbo_code-0.2.1/tests/tools/test_bash.py +0 -263
- limbo_code-0.2.1/tests/tools/test_edit.py +0 -292
- limbo_code-0.2.1/tests/tools/test_find.py +0 -100
- limbo_code-0.2.1/tests/tools/test_grep.py +0 -117
- limbo_code-0.2.1/tests/tools/test_ignore.py +0 -53
- limbo_code-0.2.1/tests/tools/test_ls.py +0 -73
- limbo_code-0.2.1/tests/tools/test_read.py +0 -240
- limbo_code-0.2.1/tests/tools/test_registry.py +0 -38
- limbo_code-0.2.1/tests/tools/test_run_code.py +0 -277
- limbo_code-0.2.1/tests/tools/test_write.py +0 -62
- limbo_code-0.2.1/tests/ui/__snapshots__/test_snapshots/test_snapshot_idle.raw +0 -172
- limbo_code-0.2.1/tests/ui/__snapshots__/test_snapshots/test_snapshot_session_picker.raw +0 -168
- limbo_code-0.2.1/tests/ui/__snapshots__/test_snapshots/test_snapshot_thinking_and_running_tool.raw +0 -174
- limbo_code-0.2.1/tests/ui/__snapshots__/test_snapshots/test_snapshot_tool_error_and_error_message.raw +0 -175
- limbo_code-0.2.1/tests/ui/test_app_smoke.py +0 -36
- limbo_code-0.2.1/tests/ui/test_bang_ui.py +0 -357
- limbo_code-0.2.1/tests/ui/test_btw_ui.py +0 -158
- limbo_code-0.2.1/tests/ui/test_code_mode_ui.py +0 -122
- limbo_code-0.2.1/tests/ui/test_command_menu.py +0 -224
- limbo_code-0.2.1/tests/ui/test_commands.py +0 -104
- limbo_code-0.2.1/tests/ui/test_compact_ui.py +0 -188
- limbo_code-0.2.1/tests/ui/test_contrast.py +0 -41
- limbo_code-0.2.1/tests/ui/test_copy.py +0 -233
- limbo_code-0.2.1/tests/ui/test_game2048.py +0 -277
- limbo_code-0.2.1/tests/ui/test_goal_ui.py +0 -278
- limbo_code-0.2.1/tests/ui/test_input_attachments.py +0 -168
- limbo_code-0.2.1/tests/ui/test_input_history.py +0 -222
- limbo_code-0.2.1/tests/ui/test_input_paste.py +0 -314
- limbo_code-0.2.1/tests/ui/test_interrupt_ui.py +0 -241
- limbo_code-0.2.1/tests/ui/test_key_fixes.py +0 -38
- limbo_code-0.2.1/tests/ui/test_main_screen.py +0 -216
- limbo_code-0.2.1/tests/ui/test_model_picker.py +0 -393
- limbo_code-0.2.1/tests/ui/test_paste_store.py +0 -172
- limbo_code-0.2.1/tests/ui/test_path_input.py +0 -275
- limbo_code-0.2.1/tests/ui/test_scroll_follow.py +0 -85
- limbo_code-0.2.1/tests/ui/test_sessions_ui.py +0 -294
- limbo_code-0.2.1/tests/ui/test_skills_ui.py +0 -174
- limbo_code-0.2.1/tests/ui/test_startup_art.py +0 -137
- limbo_code-0.2.1/tests/ui/test_steer_ui.py +0 -539
- limbo_code-0.2.1/tests/ui/test_theme.py +0 -104
- limbo_code-0.2.1/tests/ui/test_widgets.py +0 -620
- {limbo_code-0.2.1 → limbo_code-0.3}/.agents/skills/grill-with-docs/SKILL.md +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/.agents/skills/grill-with-docs/agents/openai.yaml +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/.agents/skills/improve-codebase-architecture/HTML-REPORT.md +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/.agents/skills/improve-codebase-architecture/SKILL.md +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/.agents/skills/improve-codebase-architecture/agents/openai.yaml +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/.agents/skills/tdd/SKILL.md +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/.agents/skills/tdd/agents/openai.yaml +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/.agents/skills/tdd/mocking.md +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/.agents/skills/tdd/tests.md +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/.github/workflows/publish.yml +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/.github/workflows/test.yml +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/.gitignore +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/Makefile +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/design/bang-verification.md +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/design/confirm_view.html +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/design/limbo-ui-minimal.md +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/design/limbo-ui-redesign.md +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/design/prototype-minimal-confirm.png +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/design/prototype-minimal.html +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/design/prototype-minimal.png +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/design/prototype.html +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/design/prototype.png +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/design/rfc-bang-command.md +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/design/rfc-headless-cli.md +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/assets/limbo-current-ui.png +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/assets/limbo-new-ui.png +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/assets/walkthrough/limbo-dark/1_idle.svg +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/assets/walkthrough/limbo-dark/2_thinking_tool_running.svg +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/assets/walkthrough/limbo-dark/3_tool_success.svg +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/assets/walkthrough/limbo-dark/4_tool_error.svg +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/assets/walkthrough/limbo-dark/5_llm_error.svg +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/assets/walkthrough/limbo-dark/6_edit_diff.svg +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/assets/walkthrough/limbo-light/1_idle.svg +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/assets/walkthrough/limbo-light/2_thinking_tool_running.svg +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/assets/walkthrough/limbo-light/3_tool_success.svg +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/assets/walkthrough/limbo-light/4_tool_error.svg +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/assets/walkthrough/limbo-light/5_llm_error.svg +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/assets/walkthrough/limbo-light/6_edit_diff.svg +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/skills.md +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/docs/ui-redesign-proposal.md +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/pyproject.toml +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/scripts/check_contrast.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/scripts/gen_banner.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/scripts/ui_walkthrough.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/skills-lock.json +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/__init__.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/__main__.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/attachments.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/code_mode.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/compaction.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/goal.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/llm/__init__.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/llm/anthropic_client.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/llm/client.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/llm/factory.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/llm/openai_client.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/llm/scaffold.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/llm/sse.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/llm/usage.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/model_switch.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/models.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/skills.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/tools/__init__.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/tools/bash.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/tools/edit.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/tools/ignore.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/tools/mutation_queue.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/tools/write.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/__init__.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/clipboard.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/commands.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/paste_store.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/path_input.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/screens/__init__.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/screens/game2048.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/screens/verify_picker.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/widgets/__init__.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/src/limbo/ui/widgets/command_menu.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/tests/test_integration.py +0 -0
- {limbo_code-0.2.1 → limbo_code-0.3}/uv.lock +0 -0
|
@@ -2,7 +2,7 @@
|
|
|
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. A `--headless` flag runs a plain-terminal REPL frontend instead of the TUI (Herdr/script control, RFC LIM-60).
|
|
4
4
|
|
|
5
|
-
**Tools execute immediately — no confirmation flow, no safety fences.** File tools are not scoped to the workdir (absolute paths and symlinks are honored as-is), there is no sensitive-file skip list, and `bash` runs every command as given (no dangerous-command filter).
|
|
5
|
+
**Tools execute immediately — no confirmation flow, no safety fences.** File tools are not scoped to the workdir (absolute paths and symlinks are honored as-is), there is no sensitive-file skip list, and `bash` runs every command as given (no dangerous-command filter). `ToolRegistry` runs a `before_tool`/`after_tool` hook chain (`admit()` then `run()`; the agent journals a call's intent only after `admit()` lets it through) (`tools/hooks.py`: `Block` / `Rewrite` / replace result; a raising hook blocks) that is empty by default; the only built-in hook is `/plan` (read-only plan mode: replay-safe tools and read-only bash only, also enforced on Code Mode sub-calls, persisted as `SessionMeta.plan_mode`).
|
|
6
6
|
|
|
7
7
|
## Quick Start
|
|
8
8
|
|
|
@@ -37,10 +37,19 @@ src/limbo/
|
|
|
37
37
|
├── headless.py # --headless frontend (RFC LIM-60): line-oriented REPL on the
|
|
38
38
|
│ # primary screen for Herdr (agent prompt/send-keys/read/wait);
|
|
39
39
|
│ # same TurnPump seam as the TUI, no textual import
|
|
40
|
-
├──
|
|
40
|
+
├── conversation.py # Conversation: the live agent+pump pair both frontends drive —
|
|
41
|
+
│ # new/switch/fork/handoff (load before close), lifecycle reporting
|
|
42
|
+
│ # (drive), /goal verify-proposal flow
|
|
43
|
+
├── sessions.py # Session JSONL file format: records, save/load/list/export
|
|
44
|
+
├── journal.py # SessionJournal: when/in what order records reach the file
|
|
45
|
+
│ # (watermark, rewrite fallback, queue re-journal, meta patches)
|
|
41
46
|
├── trace.py # Append-only JSONL run log (sessions/traces/)
|
|
47
|
+
├── tracing/ # /tracing-viewer: loopback web viewer over trace files (stdlib
|
|
48
|
+
│ # http.server; index.py offsets, model.py tree, static/index.html)
|
|
42
49
|
├── integrations/ # External mux/orchestrator lifecycle reporters: base.py (protocol
|
|
43
|
-
│ # + CompositeReporter + AgentState), herdr.py
|
|
50
|
+
│ # + CompositeReporter + AgentState + TurnOutcome), herdr.py,
|
|
51
|
+
│ # osc7501.py (terminal program status; TTY only, LIMBO_PROGRAM_STATUS=0
|
|
52
|
+
│ # opts out, tmux passthrough); create_reporters() fans out
|
|
44
53
|
├── skills.py # SKILL.md discovery (user + project dirs)
|
|
45
54
|
├── llm/ # catalog.py (provider/model specs), factory.py (dialect→client),
|
|
46
55
|
│ # openai_client.py, anthropic_client.py, responses_client.py, retry.py, sse.py,
|
|
@@ -48,7 +57,7 @@ src/limbo/
|
|
|
48
57
|
│ # scaffold.py (plumbing shared by dialect clients: credentials, retry, images)
|
|
49
58
|
├── tools/ # base.py (BaseTool + path resolution + truncation), registry.py (dispatch;
|
|
50
59
|
│ # code_mode flag filters definitions() to [run_code], all_definitions() feeds
|
|
51
|
-
│ # the SDK), mutation_queue.py (per-file locks), ignore.py (.gitignore),
|
|
60
|
+
│ # the SDK), hooks.py (before/after tool hook chain + plan mode), mutation_queue.py (per-file locks), ignore.py (.gitignore),
|
|
52
61
|
│ # read/bash/edit/write/grep/find/ls/run_code.py (the Code Mode transport)
|
|
53
62
|
└── ui/ # app.py + app.tcss (ALL styles here; theme vars only, no bare hex),
|
|
54
63
|
# theme.py (limbo-dark/-light, RFC LIM-16), commands.py (slash registry
|
|
@@ -62,12 +71,13 @@ src/limbo/
|
|
|
62
71
|
## Agent Loop
|
|
63
72
|
|
|
64
73
|
- `Agent.run()` yields `AgentEvent`: `TextDelta`, `ThinkingDelta`, `ToolCallRequest`, `ToolResultEvent`, `ErrorEvent`, `CompactionEvent`, `UsageUpdate`, `SteerEvent`
|
|
65
|
-
- 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
|
|
74
|
+
- Loop top: auto-compaction check → steer drain → LLM call. A provider context-overflow error (`llm.retry.is_context_overflow`: 413 or known overflow texts, never rate limits) triggers `compact(trigger="overflow")` and one resend per turn; a second overflow or a failed compaction surfaces as `ErrorEvent`. Turn ends on a tool-call-free response; `max_iterations` (default 50) cancels pending calls with placeholder results
|
|
66
75
|
- 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`
|
|
67
76
|
- `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
|
|
68
|
-
- Mid-turn user input queues as *steer* (LIM-20): injected at loop top, or as turn-end follow-up
|
|
77
|
+
- Mid-turn user input queues as *steer* (LIM-20): injected at loop top, or as turn-end follow-up. Alt+Enter (headless: `/queue <text>`) queues a `follow_up` instead: only `TurnPump._supervise` drains it, after the whole task (goal loop included). Every queue change is journaled in the session file (`queue` records: `queued` immediately, `dequeued` after the message carrying it is saved), so queued messages survive a crash and are restored (and re-rendered) on resume
|
|
69
78
|
- Reasoning is stored on assistant messages and replayed per dialect (Anthropic thinking signatures, Kimi `reasoning_content`, Responses encrypted items)
|
|
70
|
-
-
|
|
79
|
+
- A retryable failure after the stream started (dropped connection, 5xx, overload) resends the same request up to `max_retries` times (`RetryEvent`; the partial response is discarded and the TUI strikes it through); failures before the first event are retried inside the client (`llm/retry.py`)
|
|
80
|
+
- Usage counters normalize in `llm/usage.py` (`input_tokens`+`cache_read`, `prompt_tokens`, DeepSeek cache hits) and feed the compaction trigger via `PromptSizeEstimator`; every billed call (turns, compaction summaries, `/btw`) is added to the per-`provider/model` ledger `SessionMeta.usage`, so the token counter survives resume. The Responses client sends `prompt_cache_key = session id`
|
|
71
81
|
|
|
72
82
|
## LLM & Config
|
|
73
83
|
|
|
@@ -79,17 +89,19 @@ Providers/models live in `llm/catalog.py`; unknown models get generic OpenAI-com
|
|
|
79
89
|
[tools] # bash_enabled = true, parallel = true
|
|
80
90
|
[compaction] # enabled, reserve_tokens = 16384, keep_recent_tokens = 20000
|
|
81
91
|
[goal] # max_rounds = 10, verify_timeout_ms = 600000
|
|
92
|
+
[queue] # after_failed_run = "continue" | "keep" | "withdraw" (queued
|
|
93
|
+
# messages after an LLM-error turn; keep/withdraw end the task)
|
|
82
94
|
[ui] # theme, show_banner
|
|
83
95
|
[providers.<id>] # base_url / api_key / api_key_env / headers
|
|
84
96
|
```
|
|
85
97
|
|
|
86
|
-
`/model` rebuilds the client mid-session (refused while busy) and persists via tomlkit. Sessions resume via `--continue` / `--resume` / `/sessions`; resume repairs dangling tool calls. 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`).
|
|
98
|
+
`/model` rebuilds the client mid-session (refused while busy) and persists via tomlkit. Sessions resume via `--continue` / `--resume` / `/sessions`; resume repairs dangling tool calls: each call commits a `tool_intent` record before it runs, so a crashed call is re-run on the next turn if its tool is `replay = "safe"` (read/grep/find/ls), otherwise marked "may have partially run" (intent committed) or "did not run" (no intent); legacy sessions without `meta.tool_intents` get the generic placeholder. Trace events: `session_start`, `user_message`, `llm_request` (full body), `llm_response`, `tool_call`, `tool_result`, `tool_recovery`, `compaction*`, `error`, `turn_end`. `/export [path]` merges meta + trace + message snapshot (Markdown if `.md`). `/fork [n]` copies the active history (first n user turns) into a new self-contained session, `/handoff <note>` starts a clean linked session from a note; both record `parent_session`/`fork_at` and leave the parent untouched. Code Mode sub-calls are traced as `tool_call`/`tool_result` with `parent_call_id`. `limbo --json` (implies `--headless`) prints every event as one JSON object per line.
|
|
87
99
|
|
|
88
100
|
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]`.
|
|
89
101
|
|
|
90
102
|
## UI
|
|
91
103
|
|
|
92
|
-
Single column: status bar (state/elapsed/tokens/model/workdir/queued + rainbow 🎯GOAL badge while a goal loop runs + ⌨code badge while Code Mode is on) → 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 /code-mode /help /2048 /goal` + skill commands. '/'-leading input that matches no command but looks like a path is sent as a normal message with existing files auto-attached (`ui/path_input.py`); a leading space forces plain text. After palette changes run `python scripts/check_contrast.py` (≥ 4.5:1).
|
|
104
|
+
Single column: status bar (state/elapsed/tokens/model/workdir/queued + rainbow 🎯GOAL badge while a goal loop runs + ⌨code badge while Code Mode is on) → scrolling chat flow (user `❯`, streaming Markdown, tool cards, errors) → input box (Enter submits, Shift+Enter newline, Alt+Enter queues a follow-up while busy (`ui/key_fixes.py` maps its modifyOtherKeys/kitty/ESC forms), paste markers, `ctrl+v` image attach) → hint line. Slash commands: `/sessions /new /fork /handoff /export /tracing-viewer /compact /model /code-mode /plan /help /2048 /goal` + skill commands. '/'-leading input that matches no command but looks like a path is sent as a normal message with existing files auto-attached (`ui/path_input.py`); a leading space forces plain text. After palette changes run `python scripts/check_contrast.py` (≥ 4.5:1).
|
|
93
105
|
|
|
94
106
|
`/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 the goal branch of the `TurnPump` (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.
|
|
95
107
|
|
|
@@ -103,7 +115,7 @@ Single column: status bar (state/elapsed/tokens/model/workdir/queued + rainbow
|
|
|
103
115
|
history — sub-calls never do. `SessionMeta.code_mode` persists the mode
|
|
104
116
|
across resume
|
|
105
117
|
- 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.**
|
|
106
|
-
- Session
|
|
118
|
+
- Session JSONL is checkpointed mid-turn (before/after each LLM request, after each tool batch): new messages are appended; full atomic rewrite (0600) only on first save, after a failed append, or when resume repaired dangling calls. Meta changes append `meta_patch` (a title change rewrites just the meta line via `rewrite_meta_line`, which keeps every other record). Compaction is non-destructive: a `compaction` record is appended, summarized messages stay in the file, and loading selects the active range from the latest record's `first_kept_entry_id`. Typed records `meta_patch` (merged over the meta line on load; goal state via `Agent.persist_meta()`) and `tool_intent` sit between message lines; unknown types are skipped. Trace holds the full-fidelity record
|
|
107
119
|
|
|
108
120
|
## Testing
|
|
109
121
|
|
|
@@ -19,7 +19,8 @@ proposals should name modules with these terms.
|
|
|
19
19
|
## Conversation
|
|
20
20
|
|
|
21
21
|
- **Turn** — one user input through the agent loop until a tool-call-free
|
|
22
|
-
response (or max_iterations). Turns are the
|
|
22
|
+
response (or max_iterations). Turns are the trace unit; the session
|
|
23
|
+
file is checkpointed at every consistency point inside a turn.
|
|
23
24
|
- **Turn pump** — the non-UI module that runs turns back-to-back
|
|
24
25
|
(`pump.py`, `TurnPump`): pulls events out of the lazy `Agent.run()`
|
|
25
26
|
stream, owns the single-flight busy flag (turns and `/compact`
|
|
@@ -28,6 +29,20 @@ proposals should name modules with these terms.
|
|
|
28
29
|
verify/wrap-up window, teardown race — become one more turn); frontends
|
|
29
30
|
never drain. Frontends are adapters translating its events into UI
|
|
30
31
|
updates.
|
|
32
|
+
- **Conversation** — the live session a frontend drives
|
|
33
|
+
(`conversation.py`): the current Agent + Turn pump pair, session
|
|
34
|
+
switching (`/new`, `/sessions`, `/fork`, `/handoff`; the target loads
|
|
35
|
+
before the current session closes), lifecycle reporting around each
|
|
36
|
+
run, and the `/goal` verify-proposal flow. The TUI and headless
|
|
37
|
+
frontends are its adapters; busy is the Turn pump's flag.
|
|
38
|
+
- **Session journal** — the write side of the session file
|
|
39
|
+
(`journal.py`, `SessionJournal`): which in-memory messages are already
|
|
40
|
+
on disk, when a checkpoint appends vs rewrites, and the record order
|
|
41
|
+
that keeps a crash recoverable (summarized messages before their
|
|
42
|
+
compaction record, `dequeued` after the message carrying the item). The
|
|
43
|
+
Agent calls its verbs (`checkpoint`, `queued`/`dequeued`,
|
|
44
|
+
`record_intents`, `compacted`, `patch_meta`); `sessions.py` owns the
|
|
45
|
+
file format and loading.
|
|
31
46
|
- **Steer** — a user message queued mid-turn, injected at consistency
|
|
32
47
|
points (loop top, turn end, run head). The `SteerQueue` (`steer.py`) owns
|
|
33
48
|
queueing semantics — id generation, FIFO, and the cancel boundary (an
|
limbo_code-0.3/PKG-INFO
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: limbo-code
|
|
3
|
+
Version: 0.3
|
|
4
|
+
Summary: A minimal terminal AI coding agent
|
|
5
|
+
Requires-Python: >=3.11
|
|
6
|
+
Requires-Dist: httpx>=0.27
|
|
7
|
+
Requires-Dist: openai>=1.30
|
|
8
|
+
Requires-Dist: pydantic>=2.0
|
|
9
|
+
Requires-Dist: pygments>=2.17
|
|
10
|
+
Requires-Dist: textual>=8.0
|
|
11
|
+
Requires-Dist: toml>=0.10
|
|
12
|
+
Requires-Dist: tomlkit>=0.13
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
|
|
15
|
+
# Limbo
|
|
16
|
+
|
|
17
|
+
**A terminal AI coding agent that is small enough to understand and sturdy
|
|
18
|
+
enough to trust with long work.**
|
|
19
|
+
|
|
20
|
+
Tell Limbo what you want in plain language. It explores your repository, reads
|
|
21
|
+
and edits files, runs commands, and streams its reasoning and every tool call
|
|
22
|
+
as it goes. When the work runs long, Limbo keeps it safe: every turn is
|
|
23
|
+
journaled to disk, a crash or a closed terminal picks up where it stopped, and
|
|
24
|
+
you can branch, hand off, or replay any session.
|
|
25
|
+
|
|
26
|
+

|
|
27
|
+
|
|
28
|
+
## Who it's for
|
|
29
|
+
|
|
30
|
+
- **Developers who live in the terminal** and want an agent that fits next to
|
|
31
|
+
their editor, multiplexer, and dotfiles instead of a separate app.
|
|
32
|
+
- **People who use more than one model.** DeepSeek, Kimi, GLM, OpenAI Codex, or
|
|
33
|
+
any OpenAI-compatible endpoint, switchable mid-conversation.
|
|
34
|
+
- **Anyone who wants to see what the agent did.** Every request, tool call, and
|
|
35
|
+
token is recorded and browsable; nothing happens off-screen.
|
|
36
|
+
- **Hackers who want to read the code.** Seven tools, one agent loop, plain
|
|
37
|
+
JSONL on disk. `AGENTS.md` explains every design decision.
|
|
38
|
+
|
|
39
|
+
## What you get
|
|
40
|
+
|
|
41
|
+
### Work that survives
|
|
42
|
+
|
|
43
|
+
- **Durable sessions.** Messages, tool-call intents, queued follow-ups, and
|
|
44
|
+
compactions are appended to a JSONL journal as they happen. Kill the process
|
|
45
|
+
mid-turn and resume: read-only calls that were interrupted are re-run,
|
|
46
|
+
calls that may have had side effects are flagged so the model checks before
|
|
47
|
+
retrying, and nothing you queued is lost.
|
|
48
|
+
- **Resume, branch, hand off.** `limbo --continue` or `/sessions` to pick up,
|
|
49
|
+
`/fork` to try another direction without touching the original, `/handoff`
|
|
50
|
+
to start a clean context with a written brief that links back.
|
|
51
|
+
- **Long runs stay alive.** Automatic compaction summarizes old turns when the
|
|
52
|
+
context fills up, while the full record stays in the journal.
|
|
53
|
+
|
|
54
|
+
### You stay in control
|
|
55
|
+
|
|
56
|
+
- **`/plan`** puts the agent in read-only mode: it explores and proposes a
|
|
57
|
+
step-by-step plan, while writes, edits, and mutating shell commands are
|
|
58
|
+
blocked until you say `/plan off`.
|
|
59
|
+
- **`/goal <task>`** closes the loop: the model proposes a verify command, you
|
|
60
|
+
confirm it, and Limbo iterates until it passes.
|
|
61
|
+
- **Steer while it works.** Type while a turn is running to redirect it;
|
|
62
|
+
`Alt+Enter` queues a follow-up for after the task; `Esc` interrupts.
|
|
63
|
+
- **Side channels.** `/btw <question>` asks something quick without polluting
|
|
64
|
+
the history; `!<command>` runs a shell command yourself, never sent to the
|
|
65
|
+
model.
|
|
66
|
+
|
|
67
|
+
### Any model, set up in seconds
|
|
68
|
+
|
|
69
|
+
- **Guided setup.** On first run with no key, Limbo asks for one and saves it
|
|
70
|
+
to `~/.limbo/auth.json` (readable only by you). `limbo auth login` verifies a
|
|
71
|
+
key against the provider before saving; `limbo models` lists the catalog and
|
|
72
|
+
which models are ready to use.
|
|
73
|
+
- **Switch without restarting.** `Ctrl+L` (or `/model`) opens a searchable
|
|
74
|
+
picker with recent models first. Pick a model whose provider has no key yet
|
|
75
|
+
and it asks for one inline. `Shift+Tab` steps through thinking levels.
|
|
76
|
+
- **Three wire formats** (OpenAI completions, Anthropic messages, OpenAI
|
|
77
|
+
responses) behind one catalog, with per-provider overrides for relays,
|
|
78
|
+
renamed key variables, and extra headers.
|
|
79
|
+
|
|
80
|
+

|
|
81
|
+
|
|
82
|
+
### Fast turns
|
|
83
|
+
|
|
84
|
+
- Tool calls issued together run concurrently; edits to the same file are
|
|
85
|
+
serialized.
|
|
86
|
+
- **Code Mode** (`/code-mode`) collapses the tool catalog into a single
|
|
87
|
+
`run_code` tool: the model writes one Python program against a generated SDK,
|
|
88
|
+
turning many round trips into one.
|
|
89
|
+
|
|
90
|
+
### Nothing hidden
|
|
91
|
+
|
|
92
|
+
- **`/tracing-viewer`** opens a local web page with a live view of every LLM
|
|
93
|
+
request, tool call, and token count in the session.
|
|
94
|
+
- **`/export`** dumps the session as a full JSONL log (request bodies, usage,
|
|
95
|
+
tool results, errors) or as readable Markdown.
|
|
96
|
+
|
|
97
|
+
### A TUI worth living in
|
|
98
|
+
|
|
99
|
+
- **Limbo Dusk**, the default theme, with `limbo-dark` and `limbo-light`
|
|
100
|
+
alternatives, all checked for contrast.
|
|
101
|
+
- **Follows Omarchy.** On an Omarchy desktop Limbo picks up the system theme
|
|
102
|
+
and switches live when you change it.
|
|
103
|
+
- **Limbo Motion.** Subtle animation that tells the story from unsettled to
|
|
104
|
+
settled. Set `[ui] motion = "reduced"` or `"off"` if you prefer stillness
|
|
105
|
+
(SSH sessions default to reduced).
|
|
106
|
+
- `Ctrl+P` command palette, image and file paste with `Ctrl+V`, collapsible
|
|
107
|
+
tool output with `Ctrl+O`.
|
|
108
|
+
|
|
109
|
+
### Scriptable
|
|
110
|
+
|
|
111
|
+
`limbo --headless` gives the same agent as a line-oriented REPL, and
|
|
112
|
+
`limbo --json` prints one JSON event per line so scripts can drive it without
|
|
113
|
+
parsing the screen. Inside the [Herdr](https://herdr.dev) multiplexer, Limbo
|
|
114
|
+
reports its working, idle, and blocked state to the pane.
|
|
115
|
+
|
|
116
|
+
## Quick start
|
|
117
|
+
|
|
118
|
+
Requires Python 3.11 or later.
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
pip install limbo-code
|
|
122
|
+
cd ~/code/my-project
|
|
123
|
+
limbo # first run asks for an API key
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Prefer to set up keys first:
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
limbo auth login # choose a provider, paste a key, verified and saved
|
|
130
|
+
limbo auth list # where each provider's key comes from
|
|
131
|
+
limbo models # what you can run right now
|
|
132
|
+
limbo -m glm-4.7 # one run on a different model
|
|
133
|
+
limbo -m kimi-k3:high # ...with a thinking level
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Then just describe the task: *"find why the login test is flaky and fix it"*.
|
|
137
|
+
|
|
138
|
+
## Everyday use
|
|
139
|
+
|
|
140
|
+
| Key | Action |
|
|
141
|
+
|-----|--------|
|
|
142
|
+
| `Enter` / `Shift+Enter` | send / new line |
|
|
143
|
+
| `Alt+Enter` | queue a follow-up for after the current task |
|
|
144
|
+
| `Esc` | interrupt |
|
|
145
|
+
| `Ctrl+L` | model picker |
|
|
146
|
+
| `Shift+Tab` | cycle thinking level |
|
|
147
|
+
| `Ctrl+P` | command palette |
|
|
148
|
+
| `Ctrl+S` | sessions |
|
|
149
|
+
| `Ctrl+O` | expand or collapse tool output |
|
|
150
|
+
|
|
151
|
+
| Command | What it does |
|
|
152
|
+
|---------|--------------|
|
|
153
|
+
| `/model [name]` | switch model (picker when bare); persists to config |
|
|
154
|
+
| `/plan [on\|off]` | read-only mode: explore and propose, mutations blocked |
|
|
155
|
+
| `/goal <task>` | iterate until a confirmed verify command passes |
|
|
156
|
+
| `/code-mode [on\|off]` | one `run_code` tool: a Python program, many tool calls |
|
|
157
|
+
| `/btw <question>` | side question that stays out of history |
|
|
158
|
+
| `/compact` | compact the conversation now |
|
|
159
|
+
| `/sessions` `/new` | switch to a past session, or start fresh |
|
|
160
|
+
| `/fork [n]` | branch into a new session (optionally keep only the first n turns) |
|
|
161
|
+
| `/handoff <brief>` | clean context seeded with a brief, linked to this session |
|
|
162
|
+
| `/export [path]` | full JSONL log, or Markdown when the path ends in `.md` |
|
|
163
|
+
| `/tracing-viewer [id\|off]` | live trace of this session in your browser |
|
|
164
|
+
| `!<command>` | run a shell command yourself, outside the conversation |
|
|
165
|
+
|
|
166
|
+
From the shell: `limbo --continue` resumes the latest session for the current
|
|
167
|
+
directory, `limbo --resume <id-prefix>` resumes any session.
|
|
168
|
+
|
|
169
|
+
## Models and providers
|
|
170
|
+
|
|
171
|
+
| Provider | Wire format | Endpoint |
|
|
172
|
+
|----------|-------------|----------|
|
|
173
|
+
| `deepseek` | openai-completions | `https://api.deepseek.com/v1` |
|
|
174
|
+
| `moonshotai` (Kimi) | openai-completions | `https://api.moonshot.ai/v1` |
|
|
175
|
+
| `kimi-coding` | anthropic-messages | `https://api.kimi.com/coding` |
|
|
176
|
+
| `glm` (GLM Coding Plan) | openai-completions | `https://open.bigmodel.cn/api/coding/paas/v4` |
|
|
177
|
+
| `codex` (OpenAI Codex) | openai-responses | `https://api.openai.com/v1` (relay expected) |
|
|
178
|
+
|
|
179
|
+
Unknown model ids fall back to generic OpenAI-compatible defaults, and the
|
|
180
|
+
model picker can connect any OpenAI-compatible endpoint. Everything can also
|
|
181
|
+
live in `~/.limbo/config.toml`:
|
|
182
|
+
|
|
183
|
+
```toml
|
|
184
|
+
[llm]
|
|
185
|
+
model = "deepseek-v4-pro" # endpoint and key variable come from the catalog
|
|
186
|
+
|
|
187
|
+
[providers.codex] # override one provider only
|
|
188
|
+
base_url = "https://my-relay.example.com/v1"
|
|
189
|
+
api_key_env = "CODEX_API_KEY"
|
|
190
|
+
headers = { x-relay = "on" }
|
|
191
|
+
|
|
192
|
+
[ui]
|
|
193
|
+
theme = "auto" # Omarchy if installed, else limbo-dusk
|
|
194
|
+
motion = "full" # full | reduced | off
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Make it yours
|
|
198
|
+
|
|
199
|
+
- **Project instructions.** Limbo reads `AGENTS.md` from the working directory
|
|
200
|
+
and `~/.limbo/AGENTS.md` into its system prompt.
|
|
201
|
+
- **Skills.** Drop a `SKILL.md` into `<project>/.agents/skills/<name>/` or
|
|
202
|
+
`~/.limbo/skills/<name>/`. The model loads it when the task matches, or you
|
|
203
|
+
call it as `/<name>`. See [docs/skills.md](docs/skills.md).
|
|
204
|
+
|
|
205
|
+
## Safety
|
|
206
|
+
|
|
207
|
+
**There are no fences.** Tools run immediately and unconfined: file tools
|
|
208
|
+
honor absolute paths and symlinks anywhere, there is no sensitive-file
|
|
209
|
+
blocklist, and `bash` runs every command as written, `rm -rf` included. Use
|
|
210
|
+
`/plan` when you only want the agent to look, run Limbo with trusted models,
|
|
211
|
+
and keep it in repositories you can afford to modify or lose.
|
|
212
|
+
|
|
213
|
+
```toml
|
|
214
|
+
[tools]
|
|
215
|
+
bash_enabled = false # if you need a hard stop
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
## Development
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
uv sync # dev tools are a PEP 735 group, installed by default
|
|
222
|
+
make check # sync + tests + lint + typecheck
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
Individually, always `python -m pytest`, never `uv run pytest` (which can fall
|
|
226
|
+
through to a global pytest on `PATH`):
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
uv run python -m pytest tests/ -v
|
|
230
|
+
uv run ruff check src tests
|
|
231
|
+
uv run mypy src
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
The screenshot above is rendered from the real UI by
|
|
235
|
+
`scripts/readme_hero.py` (`uv run python scripts/readme_hero.py`), so it can't
|
|
236
|
+
drift from what ships.
|
|
237
|
+
|
|
238
|
+
## Learn more
|
|
239
|
+
|
|
240
|
+
- [`AGENTS.md`](AGENTS.md): architecture and design decisions
|
|
241
|
+
- [`CONTEXT.md`](CONTEXT.md): domain glossary
|
|
242
|
+
- [`docs/session-management.md`](docs/session-management.md): sessions,
|
|
243
|
+
recovery, fork and handoff, tracing viewer
|
|
244
|
+
- [`docs/skills.md`](docs/skills.md): writing skills
|
|
245
|
+
- [`design/`](design/): RFCs for headless mode, bang commands, and the UI
|
limbo_code-0.3/README.md
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
# Limbo
|
|
2
|
+
|
|
3
|
+
**A terminal AI coding agent that is small enough to understand and sturdy
|
|
4
|
+
enough to trust with long work.**
|
|
5
|
+
|
|
6
|
+
Tell Limbo what you want in plain language. It explores your repository, reads
|
|
7
|
+
and edits files, runs commands, and streams its reasoning and every tool call
|
|
8
|
+
as it goes. When the work runs long, Limbo keeps it safe: every turn is
|
|
9
|
+
journaled to disk, a crash or a closed terminal picks up where it stopped, and
|
|
10
|
+
you can branch, hand off, or replay any session.
|
|
11
|
+
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
## Who it's for
|
|
15
|
+
|
|
16
|
+
- **Developers who live in the terminal** and want an agent that fits next to
|
|
17
|
+
their editor, multiplexer, and dotfiles instead of a separate app.
|
|
18
|
+
- **People who use more than one model.** DeepSeek, Kimi, GLM, OpenAI Codex, or
|
|
19
|
+
any OpenAI-compatible endpoint, switchable mid-conversation.
|
|
20
|
+
- **Anyone who wants to see what the agent did.** Every request, tool call, and
|
|
21
|
+
token is recorded and browsable; nothing happens off-screen.
|
|
22
|
+
- **Hackers who want to read the code.** Seven tools, one agent loop, plain
|
|
23
|
+
JSONL on disk. `AGENTS.md` explains every design decision.
|
|
24
|
+
|
|
25
|
+
## What you get
|
|
26
|
+
|
|
27
|
+
### Work that survives
|
|
28
|
+
|
|
29
|
+
- **Durable sessions.** Messages, tool-call intents, queued follow-ups, and
|
|
30
|
+
compactions are appended to a JSONL journal as they happen. Kill the process
|
|
31
|
+
mid-turn and resume: read-only calls that were interrupted are re-run,
|
|
32
|
+
calls that may have had side effects are flagged so the model checks before
|
|
33
|
+
retrying, and nothing you queued is lost.
|
|
34
|
+
- **Resume, branch, hand off.** `limbo --continue` or `/sessions` to pick up,
|
|
35
|
+
`/fork` to try another direction without touching the original, `/handoff`
|
|
36
|
+
to start a clean context with a written brief that links back.
|
|
37
|
+
- **Long runs stay alive.** Automatic compaction summarizes old turns when the
|
|
38
|
+
context fills up, while the full record stays in the journal.
|
|
39
|
+
|
|
40
|
+
### You stay in control
|
|
41
|
+
|
|
42
|
+
- **`/plan`** puts the agent in read-only mode: it explores and proposes a
|
|
43
|
+
step-by-step plan, while writes, edits, and mutating shell commands are
|
|
44
|
+
blocked until you say `/plan off`.
|
|
45
|
+
- **`/goal <task>`** closes the loop: the model proposes a verify command, you
|
|
46
|
+
confirm it, and Limbo iterates until it passes.
|
|
47
|
+
- **Steer while it works.** Type while a turn is running to redirect it;
|
|
48
|
+
`Alt+Enter` queues a follow-up for after the task; `Esc` interrupts.
|
|
49
|
+
- **Side channels.** `/btw <question>` asks something quick without polluting
|
|
50
|
+
the history; `!<command>` runs a shell command yourself, never sent to the
|
|
51
|
+
model.
|
|
52
|
+
|
|
53
|
+
### Any model, set up in seconds
|
|
54
|
+
|
|
55
|
+
- **Guided setup.** On first run with no key, Limbo asks for one and saves it
|
|
56
|
+
to `~/.limbo/auth.json` (readable only by you). `limbo auth login` verifies a
|
|
57
|
+
key against the provider before saving; `limbo models` lists the catalog and
|
|
58
|
+
which models are ready to use.
|
|
59
|
+
- **Switch without restarting.** `Ctrl+L` (or `/model`) opens a searchable
|
|
60
|
+
picker with recent models first. Pick a model whose provider has no key yet
|
|
61
|
+
and it asks for one inline. `Shift+Tab` steps through thinking levels.
|
|
62
|
+
- **Three wire formats** (OpenAI completions, Anthropic messages, OpenAI
|
|
63
|
+
responses) behind one catalog, with per-provider overrides for relays,
|
|
64
|
+
renamed key variables, and extra headers.
|
|
65
|
+
|
|
66
|
+

|
|
67
|
+
|
|
68
|
+
### Fast turns
|
|
69
|
+
|
|
70
|
+
- Tool calls issued together run concurrently; edits to the same file are
|
|
71
|
+
serialized.
|
|
72
|
+
- **Code Mode** (`/code-mode`) collapses the tool catalog into a single
|
|
73
|
+
`run_code` tool: the model writes one Python program against a generated SDK,
|
|
74
|
+
turning many round trips into one.
|
|
75
|
+
|
|
76
|
+
### Nothing hidden
|
|
77
|
+
|
|
78
|
+
- **`/tracing-viewer`** opens a local web page with a live view of every LLM
|
|
79
|
+
request, tool call, and token count in the session.
|
|
80
|
+
- **`/export`** dumps the session as a full JSONL log (request bodies, usage,
|
|
81
|
+
tool results, errors) or as readable Markdown.
|
|
82
|
+
|
|
83
|
+
### A TUI worth living in
|
|
84
|
+
|
|
85
|
+
- **Limbo Dusk**, the default theme, with `limbo-dark` and `limbo-light`
|
|
86
|
+
alternatives, all checked for contrast.
|
|
87
|
+
- **Follows Omarchy.** On an Omarchy desktop Limbo picks up the system theme
|
|
88
|
+
and switches live when you change it.
|
|
89
|
+
- **Limbo Motion.** Subtle animation that tells the story from unsettled to
|
|
90
|
+
settled. Set `[ui] motion = "reduced"` or `"off"` if you prefer stillness
|
|
91
|
+
(SSH sessions default to reduced).
|
|
92
|
+
- `Ctrl+P` command palette, image and file paste with `Ctrl+V`, collapsible
|
|
93
|
+
tool output with `Ctrl+O`.
|
|
94
|
+
|
|
95
|
+
### Scriptable
|
|
96
|
+
|
|
97
|
+
`limbo --headless` gives the same agent as a line-oriented REPL, and
|
|
98
|
+
`limbo --json` prints one JSON event per line so scripts can drive it without
|
|
99
|
+
parsing the screen. Inside the [Herdr](https://herdr.dev) multiplexer, Limbo
|
|
100
|
+
reports its working, idle, and blocked state to the pane.
|
|
101
|
+
|
|
102
|
+
## Quick start
|
|
103
|
+
|
|
104
|
+
Requires Python 3.11 or later.
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
pip install limbo-code
|
|
108
|
+
cd ~/code/my-project
|
|
109
|
+
limbo # first run asks for an API key
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Prefer to set up keys first:
|
|
113
|
+
|
|
114
|
+
```bash
|
|
115
|
+
limbo auth login # choose a provider, paste a key, verified and saved
|
|
116
|
+
limbo auth list # where each provider's key comes from
|
|
117
|
+
limbo models # what you can run right now
|
|
118
|
+
limbo -m glm-4.7 # one run on a different model
|
|
119
|
+
limbo -m kimi-k3:high # ...with a thinking level
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Then just describe the task: *"find why the login test is flaky and fix it"*.
|
|
123
|
+
|
|
124
|
+
## Everyday use
|
|
125
|
+
|
|
126
|
+
| Key | Action |
|
|
127
|
+
|-----|--------|
|
|
128
|
+
| `Enter` / `Shift+Enter` | send / new line |
|
|
129
|
+
| `Alt+Enter` | queue a follow-up for after the current task |
|
|
130
|
+
| `Esc` | interrupt |
|
|
131
|
+
| `Ctrl+L` | model picker |
|
|
132
|
+
| `Shift+Tab` | cycle thinking level |
|
|
133
|
+
| `Ctrl+P` | command palette |
|
|
134
|
+
| `Ctrl+S` | sessions |
|
|
135
|
+
| `Ctrl+O` | expand or collapse tool output |
|
|
136
|
+
|
|
137
|
+
| Command | What it does |
|
|
138
|
+
|---------|--------------|
|
|
139
|
+
| `/model [name]` | switch model (picker when bare); persists to config |
|
|
140
|
+
| `/plan [on\|off]` | read-only mode: explore and propose, mutations blocked |
|
|
141
|
+
| `/goal <task>` | iterate until a confirmed verify command passes |
|
|
142
|
+
| `/code-mode [on\|off]` | one `run_code` tool: a Python program, many tool calls |
|
|
143
|
+
| `/btw <question>` | side question that stays out of history |
|
|
144
|
+
| `/compact` | compact the conversation now |
|
|
145
|
+
| `/sessions` `/new` | switch to a past session, or start fresh |
|
|
146
|
+
| `/fork [n]` | branch into a new session (optionally keep only the first n turns) |
|
|
147
|
+
| `/handoff <brief>` | clean context seeded with a brief, linked to this session |
|
|
148
|
+
| `/export [path]` | full JSONL log, or Markdown when the path ends in `.md` |
|
|
149
|
+
| `/tracing-viewer [id\|off]` | live trace of this session in your browser |
|
|
150
|
+
| `!<command>` | run a shell command yourself, outside the conversation |
|
|
151
|
+
|
|
152
|
+
From the shell: `limbo --continue` resumes the latest session for the current
|
|
153
|
+
directory, `limbo --resume <id-prefix>` resumes any session.
|
|
154
|
+
|
|
155
|
+
## Models and providers
|
|
156
|
+
|
|
157
|
+
| Provider | Wire format | Endpoint |
|
|
158
|
+
|----------|-------------|----------|
|
|
159
|
+
| `deepseek` | openai-completions | `https://api.deepseek.com/v1` |
|
|
160
|
+
| `moonshotai` (Kimi) | openai-completions | `https://api.moonshot.ai/v1` |
|
|
161
|
+
| `kimi-coding` | anthropic-messages | `https://api.kimi.com/coding` |
|
|
162
|
+
| `glm` (GLM Coding Plan) | openai-completions | `https://open.bigmodel.cn/api/coding/paas/v4` |
|
|
163
|
+
| `codex` (OpenAI Codex) | openai-responses | `https://api.openai.com/v1` (relay expected) |
|
|
164
|
+
|
|
165
|
+
Unknown model ids fall back to generic OpenAI-compatible defaults, and the
|
|
166
|
+
model picker can connect any OpenAI-compatible endpoint. Everything can also
|
|
167
|
+
live in `~/.limbo/config.toml`:
|
|
168
|
+
|
|
169
|
+
```toml
|
|
170
|
+
[llm]
|
|
171
|
+
model = "deepseek-v4-pro" # endpoint and key variable come from the catalog
|
|
172
|
+
|
|
173
|
+
[providers.codex] # override one provider only
|
|
174
|
+
base_url = "https://my-relay.example.com/v1"
|
|
175
|
+
api_key_env = "CODEX_API_KEY"
|
|
176
|
+
headers = { x-relay = "on" }
|
|
177
|
+
|
|
178
|
+
[ui]
|
|
179
|
+
theme = "auto" # Omarchy if installed, else limbo-dusk
|
|
180
|
+
motion = "full" # full | reduced | off
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Make it yours
|
|
184
|
+
|
|
185
|
+
- **Project instructions.** Limbo reads `AGENTS.md` from the working directory
|
|
186
|
+
and `~/.limbo/AGENTS.md` into its system prompt.
|
|
187
|
+
- **Skills.** Drop a `SKILL.md` into `<project>/.agents/skills/<name>/` or
|
|
188
|
+
`~/.limbo/skills/<name>/`. The model loads it when the task matches, or you
|
|
189
|
+
call it as `/<name>`. See [docs/skills.md](docs/skills.md).
|
|
190
|
+
|
|
191
|
+
## Safety
|
|
192
|
+
|
|
193
|
+
**There are no fences.** Tools run immediately and unconfined: file tools
|
|
194
|
+
honor absolute paths and symlinks anywhere, there is no sensitive-file
|
|
195
|
+
blocklist, and `bash` runs every command as written, `rm -rf` included. Use
|
|
196
|
+
`/plan` when you only want the agent to look, run Limbo with trusted models,
|
|
197
|
+
and keep it in repositories you can afford to modify or lose.
|
|
198
|
+
|
|
199
|
+
```toml
|
|
200
|
+
[tools]
|
|
201
|
+
bash_enabled = false # if you need a hard stop
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
## Development
|
|
205
|
+
|
|
206
|
+
```bash
|
|
207
|
+
uv sync # dev tools are a PEP 735 group, installed by default
|
|
208
|
+
make check # sync + tests + lint + typecheck
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
Individually, always `python -m pytest`, never `uv run pytest` (which can fall
|
|
212
|
+
through to a global pytest on `PATH`):
|
|
213
|
+
|
|
214
|
+
```bash
|
|
215
|
+
uv run python -m pytest tests/ -v
|
|
216
|
+
uv run ruff check src tests
|
|
217
|
+
uv run mypy src
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
The screenshot above is rendered from the real UI by
|
|
221
|
+
`scripts/readme_hero.py` (`uv run python scripts/readme_hero.py`), so it can't
|
|
222
|
+
drift from what ships.
|
|
223
|
+
|
|
224
|
+
## Learn more
|
|
225
|
+
|
|
226
|
+
- [`AGENTS.md`](AGENTS.md): architecture and design decisions
|
|
227
|
+
- [`CONTEXT.md`](CONTEXT.md): domain glossary
|
|
228
|
+
- [`docs/session-management.md`](docs/session-management.md): sessions,
|
|
229
|
+
recovery, fork and handoff, tracing viewer
|
|
230
|
+
- [`docs/skills.md`](docs/skills.md): writing skills
|
|
231
|
+
- [`design/`](design/): RFCs for headless mode, bang commands, and the UI
|
|
Binary file
|