limbo-code 0.2.2__tar.gz → 0.3.5__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.3.5/AGENTS.md +168 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/CONTEXT.md +36 -1
- limbo_code-0.3.5/PKG-INFO +317 -0
- limbo_code-0.3.5/README.md +301 -0
- limbo_code-0.3.5/docs/assets/limbo-hero.png +0 -0
- limbo_code-0.3.5/docs/assets/limbo-hero.svg +173 -0
- limbo_code-0.3.5/docs/assets/limbo-models.png +0 -0
- limbo_code-0.3.5/docs/assets/limbo-models.svg +173 -0
- limbo_code-0.3.5/docs/daily-mode.md +103 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/session-management.md +43 -7
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/skills.md +2 -0
- limbo_code-0.3.5/docs/subagents.md +132 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/pyproject.toml +9 -0
- limbo_code-0.3.5/scripts/readme_hero.py +146 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/scripts/scroll_bench.py +3 -1
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/agent.py +761 -143
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/app.py +67 -8
- limbo_code-0.3.5/src/limbo/auth.py +100 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/btw.py +10 -6
- limbo_code-0.3.5/src/limbo/cli_auth.py +187 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/config.py +161 -4
- limbo_code-0.3.5/src/limbo/conversation.py +389 -0
- limbo_code-0.3.5/src/limbo/daily.py +31 -0
- limbo_code-0.3.5/src/limbo/daily_skills/README.md +15 -0
- limbo_code-0.3.5/src/limbo/daily_skills/arxiv/LICENSE +21 -0
- limbo_code-0.3.5/src/limbo/daily_skills/arxiv/SKILL.md +275 -0
- limbo_code-0.3.5/src/limbo/daily_skills/arxiv/scripts/search_arxiv.py +114 -0
- limbo_code-0.3.5/src/limbo/daily_skills/blocked-page-recovery/LICENSE +21 -0
- limbo_code-0.3.5/src/limbo/daily_skills/blocked-page-recovery/SKILL.md +130 -0
- limbo_code-0.3.5/src/limbo/daily_skills/blocked-page-recovery/scripts/recover_page.py +241 -0
- limbo_code-0.3.5/src/limbo/daily_skills/document-to-action-items/LICENSE +21 -0
- limbo_code-0.3.5/src/limbo/daily_skills/document-to-action-items/SKILL.md +74 -0
- limbo_code-0.3.5/src/limbo/daily_skills/docx/LICENSE +21 -0
- limbo_code-0.3.5/src/limbo/daily_skills/docx/SKILL.md +188 -0
- limbo_code-0.3.5/src/limbo/daily_skills/docx/references/revisions-and-comments.md +88 -0
- limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_comments.py +289 -0
- limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_common.py +92 -0
- limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_create.py +177 -0
- limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_edit.py +250 -0
- limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_read.py +149 -0
- limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_revisions.py +146 -0
- limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_template.py +70 -0
- limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_validate.py +156 -0
- limbo_code-0.3.5/src/limbo/daily_skills/grounded-citations/LICENSE +21 -0
- limbo_code-0.3.5/src/limbo/daily_skills/grounded-citations/SKILL.md +248 -0
- limbo_code-0.3.5/src/limbo/daily_skills/grounded-citations/references/citation-formats.md +65 -0
- limbo_code-0.3.5/src/limbo/daily_skills/grounded-citations/references/grounding-rationale.md +64 -0
- limbo_code-0.3.5/src/limbo/daily_skills/grounded-citations/scripts/sources.py +675 -0
- limbo_code-0.3.5/src/limbo/daily_skills/meeting-action-items/LICENSE +21 -0
- limbo_code-0.3.5/src/limbo/daily_skills/meeting-action-items/SKILL.md +80 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/LICENSE +21 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/SKILL.md +117 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/references/forms.md +99 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/references/nano-pdf-editing.md +40 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/references/ocr-extraction.md +165 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/_raster.py +76 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/extract_marker.py +88 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/extract_pymupdf.py +101 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_create.py +130 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_fill_form.py +97 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_form_layout.py +168 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_make_form.py +145 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_merge.py +50 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_meta.py +115 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_page_image.py +99 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_read.py +153 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_secure.py +71 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_split.py +84 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_stamp.py +143 -0
- limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_watermark.py +51 -0
- limbo_code-0.3.5/src/limbo/daily_skills/powerpoint/LICENSE +21 -0
- limbo_code-0.3.5/src/limbo/daily_skills/powerpoint/SKILL.md +212 -0
- limbo_code-0.3.5/src/limbo/daily_skills/powerpoint/scripts/pptx_create.py +214 -0
- limbo_code-0.3.5/src/limbo/daily_skills/powerpoint/scripts/pptx_edit.py +436 -0
- limbo_code-0.3.5/src/limbo/daily_skills/powerpoint/scripts/pptx_from_template.py +88 -0
- limbo_code-0.3.5/src/limbo/daily_skills/powerpoint/scripts/pptx_read.py +131 -0
- limbo_code-0.3.5/src/limbo/daily_skills/powerpoint/scripts/pptx_render.py +93 -0
- limbo_code-0.3.5/src/limbo/daily_skills/weekly-review-planning/LICENSE +21 -0
- limbo_code-0.3.5/src/limbo/daily_skills/weekly-review-planning/SKILL.md +73 -0
- limbo_code-0.3.5/src/limbo/daily_skills/xlsx/LICENSE +21 -0
- limbo_code-0.3.5/src/limbo/daily_skills/xlsx/SKILL.md +188 -0
- limbo_code-0.3.5/src/limbo/daily_skills/xlsx/references/restructuring.md +71 -0
- limbo_code-0.3.5/src/limbo/daily_skills/xlsx/scripts/csv_to_xlsx.py +104 -0
- limbo_code-0.3.5/src/limbo/daily_skills/xlsx/scripts/xlsx_create.py +259 -0
- limbo_code-0.3.5/src/limbo/daily_skills/xlsx/scripts/xlsx_edit.py +263 -0
- limbo_code-0.3.5/src/limbo/daily_skills/xlsx/scripts/xlsx_read.py +160 -0
- limbo_code-0.3.5/src/limbo/daily_skills/xlsx/scripts/xlsx_recalc.py +111 -0
- limbo_code-0.3.5/src/limbo/daily_skills/xlsx/scripts/xlsx_restructure.py +337 -0
- limbo_code-0.3.5/src/limbo/daily_skills/xlsx/scripts/xlsx_to_csv.py +69 -0
- limbo_code-0.3.5/src/limbo/daily_skills/youtube-content/LICENSE +21 -0
- limbo_code-0.3.5/src/limbo/daily_skills/youtube-content/SKILL.md +76 -0
- limbo_code-0.3.5/src/limbo/daily_skills/youtube-content/references/output-formats.md +56 -0
- limbo_code-0.3.5/src/limbo/daily_skills/youtube-content/scripts/fetch_transcript.py +124 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/headless.py +504 -163
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/history.py +61 -12
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/integrations/__init__.py +15 -1
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/integrations/base.py +33 -8
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/integrations/herdr.py +3 -0
- limbo_code-0.3.5/src/limbo/integrations/osc7501.py +111 -0
- limbo_code-0.3.5/src/limbo/journal.py +335 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/catalog.py +64 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/responses_client.py +6 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/retry.py +51 -16
- limbo_code-0.3.5/src/limbo/llm/verify.py +58 -0
- limbo_code-0.3.5/src/limbo/memory.py +254 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/models.py +4 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/prompt.py +54 -4
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/pump.py +99 -20
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/sessions.py +266 -29
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/skills.py +20 -7
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/steer.py +41 -7
- limbo_code-0.3.5/src/limbo/subagents/board.py +657 -0
- limbo_code-0.3.5/src/limbo/subagents/commands.py +104 -0
- limbo_code-0.3.5/src/limbo/subagents/events.py +59 -0
- limbo_code-0.3.5/src/limbo/subagents/hooks.py +100 -0
- limbo_code-0.3.5/src/limbo/subagents/profiles.py +290 -0
- limbo_code-0.3.5/src/limbo/subagents/project_scan.py +265 -0
- limbo_code-0.3.5/src/limbo/subagents/supervisor.py +1413 -0
- limbo_code-0.3.5/src/limbo/subagents/tools.py +437 -0
- limbo_code-0.3.5/src/limbo/subagents/worktree.py +118 -0
- limbo_code-0.3.5/src/limbo/toggles.py +18 -0
- limbo_code-0.3.5/src/limbo/tools/ask_user.py +69 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/base.py +24 -1
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/find.py +1 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/grep.py +1 -0
- limbo_code-0.3.5/src/limbo/tools/hooks.py +248 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/ls.py +1 -0
- limbo_code-0.3.5/src/limbo/tools/memory.py +96 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/read.py +1 -0
- limbo_code-0.3.5/src/limbo/tools/registry.py +326 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/run_code.py +66 -1
- limbo_code-0.3.5/src/limbo/tools/session_search.py +186 -0
- limbo_code-0.3.5/src/limbo/tools/skill_manage.py +228 -0
- limbo_code-0.3.5/src/limbo/tools/web_fetch.py +280 -0
- limbo_code-0.3.5/src/limbo/tools/web_search.py +78 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/trace.py +9 -2
- limbo_code-0.3.5/src/limbo/tracing/__init__.py +1 -0
- limbo_code-0.3.5/src/limbo/tracing/command.py +100 -0
- limbo_code-0.3.5/src/limbo/tracing/index.py +154 -0
- limbo_code-0.3.5/src/limbo/tracing/model.py +369 -0
- limbo_code-0.3.5/src/limbo/tracing/server.py +301 -0
- limbo_code-0.3.5/src/limbo/tracing/static/index.html +560 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/app.py +85 -3
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/app.tcss +210 -60
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/commands.py +8 -2
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/contrast.py +25 -3
- limbo_code-0.3.5/src/limbo/ui/daily_look.py +157 -0
- limbo_code-0.3.5/src/limbo/ui/gradient.py +88 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/key_fixes.py +6 -0
- limbo_code-0.3.5/src/limbo/ui/motion.py +230 -0
- limbo_code-0.3.5/src/limbo/ui/omarchy.py +217 -0
- limbo_code-0.3.5/src/limbo/ui/recent_models.py +31 -0
- limbo_code-0.3.5/src/limbo/ui/screens/agents_panel.py +216 -0
- limbo_code-0.3.5/src/limbo/ui/screens/auth_dialog.py +105 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/screens/btw.py +6 -0
- limbo_code-0.3.5/src/limbo/ui/screens/command_palette.py +206 -0
- limbo_code-0.3.5/src/limbo/ui/screens/dialog.py +75 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/screens/main.py +783 -136
- limbo_code-0.3.5/src/limbo/ui/screens/model_picker.py +195 -0
- limbo_code-0.3.5/src/limbo/ui/screens/session_picker.py +61 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/syntax.py +67 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/theme.py +73 -3
- limbo_code-0.3.5/src/limbo/ui/widgets/activity.py +107 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/widgets/chat.py +52 -6
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/widgets/input.py +61 -3
- limbo_code-0.3.5/src/limbo/ui/widgets/key_hints.py +71 -0
- limbo_code-0.3.5/src/limbo/ui/widgets/landing.py +198 -0
- limbo_code-0.3.5/src/limbo/ui/widgets/mode_divider.py +76 -0
- limbo_code-0.3.5/src/limbo/ui/widgets/pills.py +74 -0
- limbo_code-0.3.5/src/limbo/ui/widgets/sidebar.py +474 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/widgets/status_bar.py +110 -22
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/widgets/tool_card.py +88 -13
- limbo_code-0.3.5/src/limbo/websearch/__init__.py +32 -0
- limbo_code-0.3.5/src/limbo/websearch/base.py +43 -0
- limbo_code-0.3.5/src/limbo/websearch/deepseek.py +124 -0
- limbo_code-0.3.5/tests/conftest.py +103 -0
- limbo_code-0.3.5/tests/fixtures/omarchy/README.md +4 -0
- limbo_code-0.3.5/tests/fixtures/omarchy/catppuccin-latte.toml +31 -0
- limbo_code-0.3.5/tests/fixtures/omarchy/gruvbox.toml +31 -0
- limbo_code-0.3.5/tests/fixtures/omarchy/hackerman.toml +33 -0
- limbo_code-0.3.5/tests/fixtures/omarchy/lumon.toml +34 -0
- limbo_code-0.3.5/tests/fixtures/omarchy/rose-pine.toml +31 -0
- limbo_code-0.3.5/tests/fixtures/omarchy/tokyo-night.toml +31 -0
- limbo_code-0.3.5/tests/test_agent.py +870 -0
- limbo_code-0.3.5/tests/test_anthropic_client.py +149 -0
- limbo_code-0.3.5/tests/test_auth.py +46 -0
- limbo_code-0.3.5/tests/test_btw.py +42 -0
- limbo_code-0.3.5/tests/test_catalog.py +31 -0
- limbo_code-0.3.5/tests/test_cli.py +85 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/tests/test_code_mode_agent.py +13 -55
- limbo_code-0.3.5/tests/test_compaction.py +69 -0
- limbo_code-0.3.5/tests/test_config.py +44 -0
- limbo_code-0.3.5/tests/test_conversation.py +105 -0
- limbo_code-0.3.5/tests/test_daily_mode.py +229 -0
- limbo_code-0.3.5/tests/test_daily_p1.py +273 -0
- limbo_code-0.3.5/tests/test_goal.py +43 -0
- limbo_code-0.3.5/tests/test_headless.py +191 -0
- limbo_code-0.3.5/tests/test_herdr.py +52 -0
- limbo_code-0.3.5/tests/test_history.py +79 -0
- limbo_code-0.3.5/tests/test_interrupt.py +159 -0
- limbo_code-0.3.5/tests/test_llm_client.py +85 -0
- limbo_code-0.3.5/tests/test_model_switch.py +56 -0
- limbo_code-0.3.5/tests/test_osc7501.py +48 -0
- limbo_code-0.3.5/tests/test_plan_mode.py +91 -0
- limbo_code-0.3.5/tests/test_prompt.py +27 -0
- limbo_code-0.3.5/tests/test_pump.py +224 -0
- limbo_code-0.3.5/tests/test_responses_client.py +154 -0
- limbo_code-0.3.5/tests/test_retry.py +111 -0
- limbo_code-0.3.5/tests/test_sessions.py +143 -0
- limbo_code-0.3.5/tests/test_skills.py +33 -0
- limbo_code-0.3.5/tests/test_sse.py +33 -0
- limbo_code-0.3.5/tests/test_steer.py +24 -0
- limbo_code-0.3.5/tests/test_subagents.py +566 -0
- limbo_code-0.3.5/tests/test_task_board.py +177 -0
- limbo_code-0.3.5/tests/test_trace.py +19 -0
- limbo_code-0.3.5/tests/test_usage.py +57 -0
- limbo_code-0.3.5/tests/tools/test_base.py +52 -0
- limbo_code-0.3.5/tests/tools/test_bash.py +38 -0
- limbo_code-0.3.5/tests/tools/test_edit.py +40 -0
- limbo_code-0.3.5/tests/tools/test_grep.py +25 -0
- limbo_code-0.3.5/tests/tools/test_hooks.py +134 -0
- limbo_code-0.3.5/tests/tools/test_ignore.py +20 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/tests/tools/test_mutation_queue.py +0 -19
- limbo_code-0.3.5/tests/tools/test_read.py +29 -0
- limbo_code-0.3.5/tests/tools/test_registry.py +23 -0
- limbo_code-0.3.5/tests/tools/test_run_code.py +67 -0
- limbo_code-0.3.5/tests/tools/test_web_search.py +170 -0
- limbo_code-0.3.5/tests/tools/test_write.py +21 -0
- limbo_code-0.3.5/tests/tracing/test_command.py +54 -0
- limbo_code-0.3.5/tests/tracing/test_index.py +36 -0
- limbo_code-0.3.5/tests/tracing/test_model.py +139 -0
- limbo_code-0.3.5/tests/tracing/test_server.py +101 -0
- limbo_code-0.3.5/tests/ui/__snapshots__/test_snapshots/test_snapshot_idle.raw +211 -0
- limbo_code-0.3.5/tests/ui/__snapshots__/test_snapshots/test_snapshot_wide_layout_with_sidebar.raw +237 -0
- limbo_code-0.3.5/tests/ui/test_app_smoke.py +12 -0
- limbo_code-0.3.5/tests/ui/test_bang_ui.py +146 -0
- limbo_code-0.3.5/tests/ui/test_btw_ui.py +82 -0
- limbo_code-0.3.5/tests/ui/test_command_menu.py +63 -0
- limbo_code-0.3.5/tests/ui/test_compact_ui.py +63 -0
- limbo_code-0.3.5/tests/ui/test_daily_look.py +209 -0
- limbo_code-0.3.5/tests/ui/test_daily_mode_ui.py +69 -0
- limbo_code-0.3.5/tests/ui/test_goal_ui.py +117 -0
- limbo_code-0.3.5/tests/ui/test_input_paste.py +63 -0
- limbo_code-0.3.5/tests/ui/test_interrupt_ui.py +90 -0
- limbo_code-0.3.5/tests/ui/test_main_screen.py +49 -0
- limbo_code-0.3.5/tests/ui/test_model_picker.py +171 -0
- limbo_code-0.3.5/tests/ui/test_omarchy.py +49 -0
- limbo_code-0.3.5/tests/ui/test_path_input.py +96 -0
- limbo_code-0.3.5/tests/ui/test_scroll_follow.py +44 -0
- limbo_code-0.3.5/tests/ui/test_sessions_ui.py +82 -0
- limbo_code-0.3.5/tests/ui/test_sidebar.py +76 -0
- limbo_code-0.3.5/tests/ui/test_skills_ui.py +81 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/tests/ui/test_snapshots.py +29 -60
- limbo_code-0.3.5/tests/ui/test_steer_ui.py +146 -0
- limbo_code-0.3.5/tests/ui/test_tracing_viewer_ui.py +50 -0
- limbo_code-0.3.5/tests/ui/test_widgets.py +38 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/uv.lock +18 -0
- limbo_code-0.2.2/AGENTS.md +0 -127
- limbo_code-0.2.2/PKG-INFO +0 -311
- limbo_code-0.2.2/README.md +0 -297
- limbo_code-0.2.2/src/limbo/tools/registry.py +0 -101
- limbo_code-0.2.2/src/limbo/ui/screens/model_picker.py +0 -122
- limbo_code-0.2.2/src/limbo/ui/screens/session_picker.py +0 -61
- limbo_code-0.2.2/tests/conftest.py +0 -42
- limbo_code-0.2.2/tests/test_agent.py +0 -2397
- limbo_code-0.2.2/tests/test_anthropic_client.py +0 -644
- limbo_code-0.2.2/tests/test_attachments.py +0 -78
- limbo_code-0.2.2/tests/test_btw.py +0 -90
- limbo_code-0.2.2/tests/test_catalog.py +0 -372
- limbo_code-0.2.2/tests/test_cli.py +0 -296
- limbo_code-0.2.2/tests/test_clipboard.py +0 -205
- limbo_code-0.2.2/tests/test_code_mode.py +0 -77
- limbo_code-0.2.2/tests/test_compaction.py +0 -163
- limbo_code-0.2.2/tests/test_config.py +0 -293
- limbo_code-0.2.2/tests/test_goal.py +0 -228
- limbo_code-0.2.2/tests/test_headless.py +0 -578
- limbo_code-0.2.2/tests/test_herdr.py +0 -164
- limbo_code-0.2.2/tests/test_history.py +0 -94
- limbo_code-0.2.2/tests/test_integrations.py +0 -145
- limbo_code-0.2.2/tests/test_interrupt.py +0 -570
- limbo_code-0.2.2/tests/test_llm_client.py +0 -755
- limbo_code-0.2.2/tests/test_llm_scaffold.py +0 -89
- limbo_code-0.2.2/tests/test_model_switch.py +0 -140
- limbo_code-0.2.2/tests/test_models.py +0 -23
- limbo_code-0.2.2/tests/test_prompt.py +0 -103
- limbo_code-0.2.2/tests/test_pump.py +0 -423
- limbo_code-0.2.2/tests/test_responses_client.py +0 -626
- limbo_code-0.2.2/tests/test_retry.py +0 -306
- limbo_code-0.2.2/tests/test_sessions.py +0 -468
- limbo_code-0.2.2/tests/test_skills.py +0 -187
- limbo_code-0.2.2/tests/test_sse.py +0 -92
- limbo_code-0.2.2/tests/test_steer.py +0 -66
- limbo_code-0.2.2/tests/test_trace.py +0 -132
- limbo_code-0.2.2/tests/test_usage.py +0 -159
- limbo_code-0.2.2/tests/tools/test_base.py +0 -184
- limbo_code-0.2.2/tests/tools/test_bash.py +0 -263
- limbo_code-0.2.2/tests/tools/test_edit.py +0 -292
- limbo_code-0.2.2/tests/tools/test_find.py +0 -100
- limbo_code-0.2.2/tests/tools/test_grep.py +0 -117
- limbo_code-0.2.2/tests/tools/test_ignore.py +0 -53
- limbo_code-0.2.2/tests/tools/test_ls.py +0 -73
- limbo_code-0.2.2/tests/tools/test_read.py +0 -240
- limbo_code-0.2.2/tests/tools/test_registry.py +0 -38
- limbo_code-0.2.2/tests/tools/test_run_code.py +0 -277
- limbo_code-0.2.2/tests/tools/test_write.py +0 -62
- limbo_code-0.2.2/tests/ui/__snapshots__/test_snapshots/test_snapshot_idle.raw +0 -172
- limbo_code-0.2.2/tests/ui/__snapshots__/test_snapshots/test_snapshot_session_picker.raw +0 -168
- limbo_code-0.2.2/tests/ui/__snapshots__/test_snapshots/test_snapshot_thinking_and_running_tool.raw +0 -175
- limbo_code-0.2.2/tests/ui/__snapshots__/test_snapshots/test_snapshot_tool_error_and_error_message.raw +0 -176
- limbo_code-0.2.2/tests/ui/test_app_smoke.py +0 -36
- limbo_code-0.2.2/tests/ui/test_bang_ui.py +0 -357
- limbo_code-0.2.2/tests/ui/test_btw_ui.py +0 -158
- limbo_code-0.2.2/tests/ui/test_code_mode_ui.py +0 -122
- limbo_code-0.2.2/tests/ui/test_command_menu.py +0 -224
- limbo_code-0.2.2/tests/ui/test_commands.py +0 -104
- limbo_code-0.2.2/tests/ui/test_compact_ui.py +0 -188
- limbo_code-0.2.2/tests/ui/test_contrast.py +0 -41
- limbo_code-0.2.2/tests/ui/test_copy.py +0 -233
- limbo_code-0.2.2/tests/ui/test_game2048.py +0 -277
- limbo_code-0.2.2/tests/ui/test_goal_ui.py +0 -278
- limbo_code-0.2.2/tests/ui/test_input_attachments.py +0 -168
- limbo_code-0.2.2/tests/ui/test_input_history.py +0 -222
- limbo_code-0.2.2/tests/ui/test_input_paste.py +0 -314
- limbo_code-0.2.2/tests/ui/test_interrupt_ui.py +0 -241
- limbo_code-0.2.2/tests/ui/test_key_fixes.py +0 -38
- limbo_code-0.2.2/tests/ui/test_main_screen.py +0 -216
- limbo_code-0.2.2/tests/ui/test_model_picker.py +0 -393
- limbo_code-0.2.2/tests/ui/test_paste_store.py +0 -172
- limbo_code-0.2.2/tests/ui/test_path_input.py +0 -275
- limbo_code-0.2.2/tests/ui/test_scroll_follow.py +0 -85
- limbo_code-0.2.2/tests/ui/test_sessions_ui.py +0 -294
- limbo_code-0.2.2/tests/ui/test_skills_ui.py +0 -174
- limbo_code-0.2.2/tests/ui/test_startup_art.py +0 -140
- limbo_code-0.2.2/tests/ui/test_steer_ui.py +0 -539
- limbo_code-0.2.2/tests/ui/test_theme.py +0 -106
- limbo_code-0.2.2/tests/ui/test_widgets.py +0 -620
- {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/grill-with-docs/SKILL.md +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/grill-with-docs/agents/openai.yaml +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/improve-codebase-architecture/HTML-REPORT.md +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/improve-codebase-architecture/SKILL.md +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/improve-codebase-architecture/agents/openai.yaml +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/tdd/SKILL.md +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/tdd/agents/openai.yaml +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/tdd/mocking.md +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/tdd/tests.md +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/.github/workflows/publish.yml +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/.github/workflows/test.yml +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/.gitignore +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/Makefile +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/design/bang-verification.md +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/design/confirm_view.html +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/design/limbo-ui-minimal.md +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/design/limbo-ui-redesign.md +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/design/prototype-minimal-confirm.png +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/design/prototype-minimal.html +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/design/prototype-minimal.png +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/design/prototype.html +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/design/prototype.png +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/design/rfc-bang-command.md +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/design/rfc-headless-cli.md +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/limbo-current-ui.png +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/limbo-new-ui.png +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-dark/1_idle.svg +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-dark/2_thinking_tool_running.svg +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-dark/3_tool_success.svg +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-dark/4_tool_error.svg +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-dark/5_llm_error.svg +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-dark/6_edit_diff.svg +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-light/1_idle.svg +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-light/2_thinking_tool_running.svg +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-light/3_tool_success.svg +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-light/4_tool_error.svg +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-light/5_llm_error.svg +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-light/6_edit_diff.svg +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/ui-redesign-proposal.md +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/scripts/check_contrast.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/scripts/gen_banner.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/scripts/ui_walkthrough.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/skills-lock.json +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/__init__.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/__main__.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/attachments.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/code_mode.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/compaction.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/goal.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/__init__.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/anthropic_client.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/client.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/factory.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/openai_client.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/scaffold.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/sse.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/usage.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/model_switch.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/__init__.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/bash.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/edit.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/ignore.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/mutation_queue.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/write.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/__init__.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/banner.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/clipboard.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/paste_store.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/path_input.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/screens/__init__.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/screens/game2048.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/screens/verify_picker.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/widgets/__init__.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/widgets/command_menu.py +0 -0
- {limbo_code-0.2.2 → limbo_code-0.3.5}/tests/test_integration.py +0 -0
|
@@ -0,0 +1,168 @@
|
|
|
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. A `--headless` flag runs a plain-terminal REPL frontend instead of the TUI (Herdr/script control, RFC LIM-60).
|
|
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). `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
|
+
|
|
7
|
+
## Quick Start
|
|
8
|
+
|
|
9
|
+
```bash
|
|
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
|
+
```
|
|
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
|
+
|
|
18
|
+
## Architecture
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
src/limbo/
|
|
22
|
+
├── app.py # CLI entry: args, config, launch
|
|
23
|
+
├── config.py # Pydantic config: llm/ui/tools/compaction/providers
|
|
24
|
+
├── models.py # Message, Attachment, ToolResult, LLMEvent
|
|
25
|
+
├── agent.py # Conversation loop (see below)
|
|
26
|
+
├── attachments.py # Attachment policy: vision gate, inline vs path-reference degrade
|
|
27
|
+
├── compaction.py # Context compaction decision/prompt logic (LIM-14)
|
|
28
|
+
├── history.py # tool_call↔result pairing + resume repair
|
|
29
|
+
├── model_switch.py # /model domain logic: validate, swap client, persist (UI is an adapter)
|
|
30
|
+
├── code_mode.py # Code Mode (/code-mode on|off): SDK rendering + CODE_ONLY instruction
|
|
31
|
+
│ # (deepseek-harness parity: catalog collapses to run_code)
|
|
32
|
+
├── prompt.py # System-prompt assembly (tools section derived from the registry)
|
|
33
|
+
├── steer.py # Mid-turn steer queue (LIM-20): queueing semantics, cancel boundary
|
|
34
|
+
├── goal.py # /goal closed-loop state machine, prompt templates, verify executor (LIM-40)
|
|
35
|
+
├── pump.py # Turn pump (frontend-agnostic, non-UI): single-flight busy flag,
|
|
36
|
+
│ # steer follow-up drain, /compact mini-turn, goal closed loop
|
|
37
|
+
├── headless.py # --headless frontend (RFC LIM-60): line-oriented REPL on the
|
|
38
|
+
│ # primary screen for Herdr (agent prompt/send-keys/read/wait);
|
|
39
|
+
│ # same TurnPump seam as the TUI, no textual import
|
|
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, the subagent supervisor
|
|
43
|
+
│ # (subscribe/start_background/take_wake_input)
|
|
44
|
+
├── sessions.py # Session JSONL file format: records, save/load/list/export
|
|
45
|
+
├── journal.py # SessionJournal: when/in what order records reach the file
|
|
46
|
+
│ # (watermark, rewrite fallback, queue re-journal, meta patches)
|
|
47
|
+
├── trace.py # Append-only JSONL run log (sessions/traces/)
|
|
48
|
+
├── tracing/ # /tracing-viewer: loopback web viewer over trace files (stdlib
|
|
49
|
+
│ # http.server; index.py offsets, model.py tree, static/index.html)
|
|
50
|
+
├── integrations/ # External mux/orchestrator lifecycle reporters: base.py (protocol
|
|
51
|
+
│ # + CompositeReporter + AgentState + TurnOutcome), herdr.py,
|
|
52
|
+
│ # osc7501.py (terminal program status; TTY only, LIMBO_PROGRAM_STATUS=0
|
|
53
|
+
│ # opts out, tmux passthrough); create_reporters() fans out
|
|
54
|
+
├── skills.py # SKILL.md discovery (user + project dirs)
|
|
55
|
+
├── daily_skills/ # Bundled daily starter skills (ported from hermes-agent, MIT; excluded
|
|
56
|
+
│ # from ruff/mypy)
|
|
57
|
+
├── daily.py # Daily mode notices (docs/daily-mode.md); the posture itself is
|
|
58
|
+
│ # ToolRegistry.daily_mode + prompt.py + SessionMeta.daily_mode
|
|
59
|
+
├── memory.py # Persistent memory (~/.limbo/memory/USER.md, NOTES.md): MemoryStore
|
|
60
|
+
│ # (capped, atomic batch ops, .bak), frozen per-session MemorySnapshot
|
|
61
|
+
├── subagents/ # Background subagents (docs/subagents.md): board.py (event-sourced
|
|
62
|
+
│ # task board + pure reconcile()), supervisor.py (scheduler, runners,
|
|
63
|
+
│ # notice delivery, user/main verbs), tools.py (main: plan_tasks/
|
|
64
|
+
│ # task_status/task_send/answer/task_control; child: checkpoint/need/
|
|
65
|
+
│ # ask_main/finish/propose_task), profiles.py (agent profiles),
|
|
66
|
+
│ # project_scan.py (<project_facts>), hooks.py (read-only/no-edit/path
|
|
67
|
+
│ # claims), worktree.py, commands.py (/task /agents @name), events.py
|
|
68
|
+
├── llm/ # catalog.py (provider/model specs), factory.py (dialect→client),
|
|
69
|
+
│ # openai_client.py, anthropic_client.py, responses_client.py, retry.py, sse.py,
|
|
70
|
+
│ # usage.py (token accounting: usage normalization + prompt-size estimation),
|
|
71
|
+
│ # scaffold.py (plumbing shared by dialect clients: credentials, retry, images)
|
|
72
|
+
├── tools/ # base.py (BaseTool + path resolution + truncation), registry.py (dispatch;
|
|
73
|
+
│ # code_mode flag filters definitions() to [run_code], all_definitions() feeds
|
|
74
|
+
│ # the SDK), hooks.py (before/after tool hook chain + plan mode), mutation_queue.py (per-file locks), ignore.py (.gitignore),
|
|
75
|
+
│ # read/bash/edit/write/grep/find/ls/run_code.py (the Code Mode transport),
|
|
76
|
+
│ # web_search.py (registered only when the search provider has a key),
|
|
77
|
+
│ # daily mode only: web_fetch.py, memory.py, session_search.py,
|
|
78
|
+
│ # ask_user.py (end_turn), skill_manage.py (writes ~/.limbo/daily/skills)
|
|
79
|
+
├── websearch/ # pluggable web-search backends: base.py (SearchProvider/SearchResult),
|
|
80
|
+
│ # deepseek.py (+ key inheritance from the deepseek model provider), PROVIDERS
|
|
81
|
+
└── ui/ # app.py + app.tcss (ALL styles here; theme vars only, no bare hex),
|
|
82
|
+
# theme.py (limbo-dark/-light, RFC LIM-16), daily_look.py (daily mode
|
|
83
|
+
# "Limbo Dawn": <theme>+daily warm variant, app.set_daily_look), commands.py (slash registry
|
|
84
|
+
# + resolve(): the single builtin-vs-skill collision site), path_input.py
|
|
85
|
+
# ('/'-path fallback), paste_store.py (pure paste/attachment marker
|
|
86
|
+
# state machine; the input widget is its adapter),
|
|
87
|
+
# screens/ (main, session_picker, model_picker, game2048),
|
|
88
|
+
# widgets/ (chat, input, status_bar, tool_card, command_menu)
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Agent Loop
|
|
92
|
+
|
|
93
|
+
- `Agent.run()` yields `AgentEvent`: `TextDelta`, `ThinkingDelta`, `ToolCallRequest`, `ToolResultEvent`, `ErrorEvent`, `CompactionEvent`, `UsageUpdate`, `SteerEvent`
|
|
94
|
+
- 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
|
|
95
|
+
- 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`
|
|
96
|
+
- `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
|
|
97
|
+
- 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
|
|
98
|
+
- Reasoning is stored on assistant messages and replayed per dialect (Anthropic thinking signatures, Kimi `reasoning_content`, Responses encrypted items)
|
|
99
|
+
- 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`)
|
|
100
|
+
- 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`
|
|
101
|
+
|
|
102
|
+
## LLM & Config
|
|
103
|
+
|
|
104
|
+
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.
|
|
105
|
+
|
|
106
|
+
```toml
|
|
107
|
+
[llm] # model, api_key, base_url, temperature, max_iterations,
|
|
108
|
+
# max_tokens, thinking_effort, max_retries, timeout, ...
|
|
109
|
+
[tools] # bash_enabled = true, parallel = true
|
|
110
|
+
[compaction] # enabled, reserve_tokens = 16384, keep_recent_tokens = 20000
|
|
111
|
+
[goal] # max_rounds = 10, verify_timeout_ms = 600000
|
|
112
|
+
[queue] # after_failed_run = "continue" | "keep" | "withdraw" (queued
|
|
113
|
+
# messages after an LLM-error turn; keep/withdraw end the task)
|
|
114
|
+
[subagents] # enabled, auto = "off"|"suggest"|"on", max_parallel = 3, max_tasks = 12,
|
|
115
|
+
# task_token_budget, cheap_model, wake_main_on, on_dep_fail, on_resume
|
|
116
|
+
[daily] # workdir = "~/limbo-daily", model, memory, user_chars, notes_chars,
|
|
117
|
+
# memory_nudge_turns, learn_after_tool_calls, fetch_max_chars, fetch_timeout
|
|
118
|
+
[ui] # theme, show_banner
|
|
119
|
+
[providers.<id>] # base_url / api_key / api_key_env / headers
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
`/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.
|
|
123
|
+
|
|
124
|
+
Skills: `~/.limbo/skills/<name>/SKILL.md` (user) and `<workdir>/.agents/skills/<name>/SKILL.md` (project, wins collisions); daily sessions also see the bundled `src/limbo/daily_skills/` (lowest) and `~/.limbo/daily/skills/` (highest), coding sessions never do — injected as a `<available_skills>` catalog in the system prompt and invocable via `/<name> [args]`.
|
|
125
|
+
|
|
126
|
+
## UI
|
|
127
|
+
|
|
128
|
+
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 /daily-mode /memory /learn /help /2048 /goal /agents /task` + skill commands; `@<agent> text` messages a subagent. '/'-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).
|
|
129
|
+
|
|
130
|
+
`/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.
|
|
131
|
+
|
|
132
|
+
Subagents (`subagents/`, docs/subagents.md): the main agent's `plan_tasks` creates tasks on the board (suggest mode: `PlanPicker` asks the user first); the `Supervisor` runs each as a child `Agent` with its own session file, gated by profile hooks. Child tools that end the turn (`need`, `ask_main`, `finish`) return `ToolResult(end_turn=True)`. Notices reach the main agent as one `source="subagent"` steer item; an idle main agent is woken through `MainWake` → `Conversation.take_wake_input()`. Board records live in the main session file (`type: "task"`), so the board survives restarts; tasks running at exit come back paused (`on_resume`).
|
|
133
|
+
|
|
134
|
+
Daily mode (`limbo daily` / `--daily` / `/daily-mode on|off`, docs/daily-mode.md): a session posture like Code Mode — same loop, a general-assistant system prompt, plus `web_fetch` and `memory` tools; persisted as `SessionMeta.daily_mode`, `/new` keeps the current mode. Memory is a snapshot taken when the Agent is built: `USER.md` and `NOTES.md` enter daily-mode prompts only (coding sessions never see memory); `memory` tool writes reach disk at once but the prompt changes only next session (prefix cache stays intact). `/learn` (and auto after `learn_after_tool_calls` tool calls) plans a `skill-reviewer` fork subagent whose only write tool is `skill_manage`; daily children inherit daily mode minus `MAIN_ONLY_TOOLS`.
|
|
135
|
+
|
|
136
|
+
## Key Decisions
|
|
137
|
+
|
|
138
|
+
- Async agent, sync tools via `asyncio.to_thread()`
|
|
139
|
+
- Code Mode (`/code-mode on|off`): `ToolRegistry.code_mode` collapses
|
|
140
|
+
`definitions()` to `[run_code]`; `RunCodeTool` runs a model-written Python
|
|
141
|
+
program on a fresh event loop, forwarding `tools.*` sub-calls through the
|
|
142
|
+
same registry. Only the curated program output (print + return) enters
|
|
143
|
+
history — sub-calls never do. `SessionMeta.code_mode` persists the mode
|
|
144
|
+
across resume
|
|
145
|
+
- 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.**
|
|
146
|
+
- 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
|
|
147
|
+
|
|
148
|
+
## Testing
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
make check # single entrypoint: sync + tests + lint + type check
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Or individually — always `python -m pytest`, never bare `uv run pytest` (which
|
|
155
|
+
silently falls through to a global pytest on PATH when the venv lacks pytest):
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
uv sync # first time / after pulling; dev group is default
|
|
159
|
+
uv run python -m pytest tests/ -v # pytest-asyncio, respx HTTP mocks, pytest-textual-snapshot
|
|
160
|
+
uv run ruff check src tests && uv run mypy src # lint + type check
|
|
161
|
+
uv run python -m pytest --snapshot-update # after intentional visual changes; review the SVG diff
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Adding Things
|
|
165
|
+
|
|
166
|
+
- **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`
|
|
167
|
+
- **Web-search backend**: subclass `SearchProvider` in `websearch/<id>.py` with a `build(config)` factory (None when it has no key), add it to `websearch.PROVIDERS`; users pick it with `[web_search] provider`
|
|
168
|
+
- **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
|
|
@@ -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
|
|
@@ -45,6 +60,26 @@ proposals should name modules with these terms.
|
|
|
45
60
|
(converging on the latest config model), persist to config.toml. The UI
|
|
46
61
|
screen is an adapter translating verdicts into chat messages.
|
|
47
62
|
|
|
63
|
+
## Subagents
|
|
64
|
+
|
|
65
|
+
- **Task board** — the delegated work of one main session
|
|
66
|
+
(`subagents/board.py`): tasks with status, `depends_on` (dependency
|
|
67
|
+
edges: a failure notifies by default), `owner` (ownership edges: a
|
|
68
|
+
failure cascades), waits, checkpoints, questions and outcomes.
|
|
69
|
+
Event-sourced into the main session file; `reconcile()` is the pure
|
|
70
|
+
scheduling rule.
|
|
71
|
+
- **Subagent** — a child Agent working on one task with its own context and
|
|
72
|
+
session file. It reports through checkpoint / need / ask_main / finish
|
|
73
|
+
and never plans tasks itself (depth 1).
|
|
74
|
+
- **Profile** — what kind of subagent a task gets (tools, model, prompt);
|
|
75
|
+
built-in, user (`~/.limbo/agents`) or project (`.agents/agents`).
|
|
76
|
+
- **Supervisor** — owns the board, the running subagents and notice
|
|
77
|
+
delivery for one Conversation (`subagents/supervisor.py`).
|
|
78
|
+
- **Notice** — a subagent event for the main agent: result, question,
|
|
79
|
+
alert, info (delivered, at most once) or progress (UI only). Delivered as
|
|
80
|
+
one `<subagent_updates>` steer item; questions and alerts wake an idle
|
|
81
|
+
main agent, results wait until the wave is done.
|
|
82
|
+
|
|
48
83
|
## Safety
|
|
49
84
|
|
|
50
85
|
Limbo has no safety fences: file tools are not scoped to the workdir,
|
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: limbo-code
|
|
3
|
+
Version: 0.3.5
|
|
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
|
+
Provides-Extra: daily
|
|
14
|
+
Requires-Dist: pypdf>=4.0; extra == 'daily'
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
|
|
17
|
+
# Limbo
|
|
18
|
+
|
|
19
|
+
**A terminal AI coding agent that is small enough to understand and sturdy
|
|
20
|
+
enough to trust with long work.**
|
|
21
|
+
|
|
22
|
+
Tell Limbo what you want in plain language. It explores your repository, reads
|
|
23
|
+
and edits files, runs commands, and streams its reasoning and every tool call
|
|
24
|
+
as it goes. When the work runs long, Limbo keeps it safe: every turn is
|
|
25
|
+
journaled to disk, a crash or a closed terminal picks up where it stopped, and
|
|
26
|
+
you can branch, hand off, or replay any session.
|
|
27
|
+
|
|
28
|
+

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

|
|
88
|
+
|
|
89
|
+
### Fast turns
|
|
90
|
+
|
|
91
|
+
- Tool calls issued together run concurrently; edits to the same file are
|
|
92
|
+
serialized.
|
|
93
|
+
- **Code Mode** (`/code-mode`) collapses the tool catalog into a single
|
|
94
|
+
`run_code` tool: the model writes one Python program against a generated SDK,
|
|
95
|
+
turning many round trips into one.
|
|
96
|
+
|
|
97
|
+
### Nothing hidden
|
|
98
|
+
|
|
99
|
+
- **`/tracing-viewer`** opens a local web page with a live view of every LLM
|
|
100
|
+
request, tool call, and token count in the session.
|
|
101
|
+
- **`/export`** dumps the session as a full JSONL log (request bodies, usage,
|
|
102
|
+
tool results, errors) or as readable Markdown.
|
|
103
|
+
|
|
104
|
+
### A TUI worth living in
|
|
105
|
+
|
|
106
|
+
- **Limbo Dusk**, the default theme, with `limbo-dark` and `limbo-light`
|
|
107
|
+
alternatives, all checked for contrast.
|
|
108
|
+
- **Follows Omarchy.** On an Omarchy desktop Limbo picks up the system theme
|
|
109
|
+
and switches live when you change it.
|
|
110
|
+
- **Limbo Motion.** Subtle animation that tells the story from unsettled to
|
|
111
|
+
settled. Set `[ui] motion = "reduced"` or `"off"` if you prefer stillness
|
|
112
|
+
(SSH sessions default to reduced).
|
|
113
|
+
- `Ctrl+P` command palette, image and file paste with `Ctrl+V`, collapsible
|
|
114
|
+
tool output with `Ctrl+O`.
|
|
115
|
+
|
|
116
|
+
### Daily mode for everything else
|
|
117
|
+
|
|
118
|
+
`limbo daily` (or `/daily-mode on`) turns Limbo into an everyday assistant for
|
|
119
|
+
non-coding work, in its own working directory:
|
|
120
|
+
|
|
121
|
+
- **Memory.** `USER.md` (who you are) and `NOTES.md` (what it learned) under
|
|
122
|
+
`~/.limbo/memory/`, loaded into daily sessions only, never into coding ones.
|
|
123
|
+
- **More tools.** `web_fetch` reads pages and PDFs, `session_search` recalls
|
|
124
|
+
past conversations, and `ask_user` asks you a multiple-choice question.
|
|
125
|
+
- **A starter pack of skills** for PDFs, Word, Excel, slides, citations,
|
|
126
|
+
meeting notes, weekly reviews, YouTube and arXiv. Daily mode has its own
|
|
127
|
+
skill directory (`~/.limbo/daily/skills/`) that coding sessions never see.
|
|
128
|
+
- **It learns.** `/learn`, or an automatic review after a long stretch of
|
|
129
|
+
work, has a background subagent write what it learned into skills. Every
|
|
130
|
+
write lands in the daily skill directory and keeps a `.bak`.
|
|
131
|
+
|
|
132
|
+
See [docs/daily-mode.md](docs/daily-mode.md).
|
|
133
|
+
|
|
134
|
+
### Scriptable
|
|
135
|
+
|
|
136
|
+
`limbo --headless` gives the same agent as a line-oriented REPL, and
|
|
137
|
+
`limbo --json` prints one JSON event per line so scripts can drive it without
|
|
138
|
+
parsing the screen. Inside the [Herdr](https://herdr.dev) multiplexer, Limbo
|
|
139
|
+
reports its working, idle, and blocked state to the pane.
|
|
140
|
+
|
|
141
|
+
## Quick start
|
|
142
|
+
|
|
143
|
+
Requires Python 3.11 or later.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pip install limbo-code
|
|
147
|
+
cd ~/code/my-project
|
|
148
|
+
limbo # first run asks for an API key
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Prefer to set up keys first:
|
|
152
|
+
|
|
153
|
+
```bash
|
|
154
|
+
limbo auth login # choose a provider, paste a key, verified and saved
|
|
155
|
+
limbo auth list # where each provider's key comes from
|
|
156
|
+
limbo models # what you can run right now
|
|
157
|
+
limbo -m glm-4.7 # one run on a different model
|
|
158
|
+
limbo -m kimi-k3:high # ...with a thinking level
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
Then just describe the task: *"find why the login test is flaky and fix it"*.
|
|
162
|
+
|
|
163
|
+
## Everyday use
|
|
164
|
+
|
|
165
|
+
| Key | Action |
|
|
166
|
+
|-----|--------|
|
|
167
|
+
| `Enter` / `Shift+Enter` | send / new line |
|
|
168
|
+
| `Alt+Enter` | queue a follow-up for after the current task |
|
|
169
|
+
| `Esc` | interrupt |
|
|
170
|
+
| `Ctrl+L` | model picker |
|
|
171
|
+
| `Shift+Tab` | cycle thinking level |
|
|
172
|
+
| `Ctrl+P` | command palette |
|
|
173
|
+
| `Ctrl+S` | sessions |
|
|
174
|
+
| `Ctrl+O` | expand or collapse tool output |
|
|
175
|
+
|
|
176
|
+
| Command | What it does |
|
|
177
|
+
|---------|--------------|
|
|
178
|
+
| `/model [name]` | switch model (picker when bare); persists to config |
|
|
179
|
+
| `/plan [on\|off]` | read-only mode: explore and propose, mutations blocked |
|
|
180
|
+
| `/goal <task>` | iterate until a confirmed verify command passes |
|
|
181
|
+
| `/code-mode [on\|off]` | one `run_code` tool: a Python program, many tool calls |
|
|
182
|
+
| `/btw <question>` | side question that stays out of history |
|
|
183
|
+
| `/agents` `/task` `@name` | background subagents: the task board, direct control, talk to one |
|
|
184
|
+
| `/compact` | compact the conversation now |
|
|
185
|
+
| `/sessions` `/new` | switch to a past session, or start fresh |
|
|
186
|
+
| `/fork [n]` | branch into a new session (optionally keep only the first n turns) |
|
|
187
|
+
| `/handoff <brief>` | clean context seeded with a brief, linked to this session |
|
|
188
|
+
| `/export [path]` | full JSONL log, or Markdown when the path ends in `.md` |
|
|
189
|
+
| `/tracing-viewer [id\|off]` | live trace of this session in your browser |
|
|
190
|
+
| `!<command>` | run a shell command yourself, outside the conversation |
|
|
191
|
+
|
|
192
|
+
From the shell: `limbo --continue` resumes the latest session for the current
|
|
193
|
+
directory, `limbo --resume <id-prefix>` resumes any session.
|
|
194
|
+
|
|
195
|
+
## Models and providers
|
|
196
|
+
|
|
197
|
+
| Provider | Wire format | Endpoint |
|
|
198
|
+
|----------|-------------|----------|
|
|
199
|
+
| `deepseek` | openai-completions | `https://api.deepseek.com/v1` |
|
|
200
|
+
| `moonshotai` (Kimi) | openai-completions | `https://api.moonshot.ai/v1` |
|
|
201
|
+
| `kimi-coding` | anthropic-messages | `https://api.kimi.com/coding` |
|
|
202
|
+
| `glm` (GLM Coding Plan) | openai-completions | `https://open.bigmodel.cn/api/coding/paas/v4` |
|
|
203
|
+
| `codex` (OpenAI Codex) | openai-responses | `https://api.openai.com/v1` (relay expected) |
|
|
204
|
+
|
|
205
|
+
Unknown model ids fall back to generic OpenAI-compatible defaults, and the
|
|
206
|
+
model picker can connect any OpenAI-compatible endpoint. Everything can also
|
|
207
|
+
live in `~/.limbo/config.toml`:
|
|
208
|
+
|
|
209
|
+
```toml
|
|
210
|
+
[llm]
|
|
211
|
+
model = "deepseek-v4-pro" # endpoint and key variable come from the catalog
|
|
212
|
+
|
|
213
|
+
[providers.codex] # override one provider only
|
|
214
|
+
base_url = "https://my-relay.example.com/v1"
|
|
215
|
+
api_key_env = "CODEX_API_KEY"
|
|
216
|
+
headers = { x-relay = "on" }
|
|
217
|
+
|
|
218
|
+
[ui]
|
|
219
|
+
theme = "auto" # Omarchy if installed, else limbo-dusk
|
|
220
|
+
motion = "full" # full | reduced | off
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
### Web search
|
|
224
|
+
|
|
225
|
+
The `web_search` tool is offered whenever its provider has a key. The only
|
|
226
|
+
provider today is DeepSeek
|
|
227
|
+
([search API](https://api-docs.deepseek.com/guides/search)); without its own
|
|
228
|
+
key it reuses the DeepSeek model key (`[providers.deepseek]`, `limbo auth
|
|
229
|
+
login`, `$DEEPSEEK_API_KEY`, or `[llm] api_key` while a DeepSeek model is
|
|
230
|
+
active).
|
|
231
|
+
|
|
232
|
+
```toml
|
|
233
|
+
[web_search]
|
|
234
|
+
provider = "deepseek" # backends live in limbo.websearch.PROVIDERS
|
|
235
|
+
api_key = "sk-..." # optional: a separate key just for search
|
|
236
|
+
count = 10 # default results per search
|
|
237
|
+
enabled = true
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
## Make it yours
|
|
241
|
+
|
|
242
|
+
- **Project instructions.** Limbo reads `AGENTS.md` from the working directory
|
|
243
|
+
and `~/.limbo/AGENTS.md` into its system prompt.
|
|
244
|
+
- **Skills.** Drop a `SKILL.md` into `<project>/.agents/skills/<name>/` or
|
|
245
|
+
`~/.limbo/skills/<name>/`. The model loads it when the task matches, or you
|
|
246
|
+
call it as `/<name>`. See [docs/skills.md](docs/skills.md).
|
|
247
|
+
|
|
248
|
+
## Safety
|
|
249
|
+
|
|
250
|
+
**There are no fences.** Tools run immediately and unconfined: file tools
|
|
251
|
+
honor absolute paths and symlinks anywhere, there is no sensitive-file
|
|
252
|
+
blocklist, and `bash` runs every command as written, `rm -rf` included. Use
|
|
253
|
+
`/plan` when you only want the agent to look, run Limbo with trusted models,
|
|
254
|
+
and keep it in repositories you can afford to modify or lose.
|
|
255
|
+
|
|
256
|
+
```toml
|
|
257
|
+
[tools]
|
|
258
|
+
bash_enabled = false # if you need a hard stop
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## Development
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
uv sync # dev tools are a PEP 735 group, installed by default
|
|
265
|
+
make check # sync + tests + lint + typecheck
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Individually, always `python -m pytest`, never `uv run pytest` (which can fall
|
|
269
|
+
through to a global pytest on `PATH`):
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
uv run python -m pytest tests/ -v
|
|
273
|
+
uv run ruff check src tests
|
|
274
|
+
uv run mypy src
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
The screenshot above is rendered from the real UI by
|
|
278
|
+
`scripts/readme_hero.py` (`uv run python scripts/readme_hero.py`), so it can't
|
|
279
|
+
drift from what ships.
|
|
280
|
+
|
|
281
|
+
## Acknowledgements
|
|
282
|
+
|
|
283
|
+
Limbo borrows openly from other agents. Thank you to:
|
|
284
|
+
|
|
285
|
+
- **[pi](https://github.com/badlogic/pi-mono) and pi-durable.** The durable
|
|
286
|
+
session design comes from pi-durable: the "commit the intent, then the
|
|
287
|
+
effect" journal, the replay policy for tool calls interrupted by a crash,
|
|
288
|
+
the placeholders that say what is known about an unfinished call, the
|
|
289
|
+
steer and follow-up inbox (`Alt+Enter`), the task graph edges behind
|
|
290
|
+
background subagents, and context overflow detection. From the pi coding
|
|
291
|
+
agent itself: the built-in tool behavior (output limits, multi-edit,
|
|
292
|
+
process-tree kill, ripgrep), the provider conventions and model catalog,
|
|
293
|
+
compaction and the session entry model, and collapsing large pastes.
|
|
294
|
+
- **[Crush](https://github.com/charmbracelet/crush)** by Charm. The Limbo
|
|
295
|
+
Dusk TUI was designed after Crush: the gradient wordmark, `╱` dialog rules,
|
|
296
|
+
Limbo Motion (its staggered entrance is borrowed directly), the `Ctrl+P`
|
|
297
|
+
command palette and the `Ctrl+S` session picker.
|
|
298
|
+
- **[Hermes Agent](https://github.com/NousResearch/hermes-agent)** by Nous
|
|
299
|
+
Research. Daily mode is modeled on Hermes: the `USER.md` / `NOTES.md`
|
|
300
|
+
memory with its frozen per-session snapshot and the memory nudge,
|
|
301
|
+
`session_search`, `ask_user`, and skill self-learning (the skill reviewer's
|
|
302
|
+
prompt is adapted from Hermes). The bundled daily skills in
|
|
303
|
+
`src/limbo/daily_skills/` are ported from Hermes under the MIT license, and
|
|
304
|
+
each keeps its `LICENSE`.
|
|
305
|
+
|
|
306
|
+
## Learn more
|
|
307
|
+
|
|
308
|
+
- [`AGENTS.md`](AGENTS.md): architecture and design decisions
|
|
309
|
+
- [`CONTEXT.md`](CONTEXT.md): domain glossary
|
|
310
|
+
- [`docs/session-management.md`](docs/session-management.md): sessions,
|
|
311
|
+
recovery, fork and handoff, tracing viewer
|
|
312
|
+
- [`docs/skills.md`](docs/skills.md): writing skills
|
|
313
|
+
- [`docs/daily-mode.md`](docs/daily-mode.md): daily mode, memory and
|
|
314
|
+
self-learning skills
|
|
315
|
+
- [`docs/subagents.md`](docs/subagents.md): background subagents, the task
|
|
316
|
+
board and how to steer them
|
|
317
|
+
- [`design/`](design/): RFCs for headless mode, bang commands, and the UI
|