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.
Files changed (411) hide show
  1. limbo_code-0.3.5/AGENTS.md +168 -0
  2. {limbo_code-0.2.2 → limbo_code-0.3.5}/CONTEXT.md +36 -1
  3. limbo_code-0.3.5/PKG-INFO +317 -0
  4. limbo_code-0.3.5/README.md +301 -0
  5. limbo_code-0.3.5/docs/assets/limbo-hero.png +0 -0
  6. limbo_code-0.3.5/docs/assets/limbo-hero.svg +173 -0
  7. limbo_code-0.3.5/docs/assets/limbo-models.png +0 -0
  8. limbo_code-0.3.5/docs/assets/limbo-models.svg +173 -0
  9. limbo_code-0.3.5/docs/daily-mode.md +103 -0
  10. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/session-management.md +43 -7
  11. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/skills.md +2 -0
  12. limbo_code-0.3.5/docs/subagents.md +132 -0
  13. {limbo_code-0.2.2 → limbo_code-0.3.5}/pyproject.toml +9 -0
  14. limbo_code-0.3.5/scripts/readme_hero.py +146 -0
  15. {limbo_code-0.2.2 → limbo_code-0.3.5}/scripts/scroll_bench.py +3 -1
  16. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/agent.py +761 -143
  17. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/app.py +67 -8
  18. limbo_code-0.3.5/src/limbo/auth.py +100 -0
  19. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/btw.py +10 -6
  20. limbo_code-0.3.5/src/limbo/cli_auth.py +187 -0
  21. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/config.py +161 -4
  22. limbo_code-0.3.5/src/limbo/conversation.py +389 -0
  23. limbo_code-0.3.5/src/limbo/daily.py +31 -0
  24. limbo_code-0.3.5/src/limbo/daily_skills/README.md +15 -0
  25. limbo_code-0.3.5/src/limbo/daily_skills/arxiv/LICENSE +21 -0
  26. limbo_code-0.3.5/src/limbo/daily_skills/arxiv/SKILL.md +275 -0
  27. limbo_code-0.3.5/src/limbo/daily_skills/arxiv/scripts/search_arxiv.py +114 -0
  28. limbo_code-0.3.5/src/limbo/daily_skills/blocked-page-recovery/LICENSE +21 -0
  29. limbo_code-0.3.5/src/limbo/daily_skills/blocked-page-recovery/SKILL.md +130 -0
  30. limbo_code-0.3.5/src/limbo/daily_skills/blocked-page-recovery/scripts/recover_page.py +241 -0
  31. limbo_code-0.3.5/src/limbo/daily_skills/document-to-action-items/LICENSE +21 -0
  32. limbo_code-0.3.5/src/limbo/daily_skills/document-to-action-items/SKILL.md +74 -0
  33. limbo_code-0.3.5/src/limbo/daily_skills/docx/LICENSE +21 -0
  34. limbo_code-0.3.5/src/limbo/daily_skills/docx/SKILL.md +188 -0
  35. limbo_code-0.3.5/src/limbo/daily_skills/docx/references/revisions-and-comments.md +88 -0
  36. limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_comments.py +289 -0
  37. limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_common.py +92 -0
  38. limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_create.py +177 -0
  39. limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_edit.py +250 -0
  40. limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_read.py +149 -0
  41. limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_revisions.py +146 -0
  42. limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_template.py +70 -0
  43. limbo_code-0.3.5/src/limbo/daily_skills/docx/scripts/docx_validate.py +156 -0
  44. limbo_code-0.3.5/src/limbo/daily_skills/grounded-citations/LICENSE +21 -0
  45. limbo_code-0.3.5/src/limbo/daily_skills/grounded-citations/SKILL.md +248 -0
  46. limbo_code-0.3.5/src/limbo/daily_skills/grounded-citations/references/citation-formats.md +65 -0
  47. limbo_code-0.3.5/src/limbo/daily_skills/grounded-citations/references/grounding-rationale.md +64 -0
  48. limbo_code-0.3.5/src/limbo/daily_skills/grounded-citations/scripts/sources.py +675 -0
  49. limbo_code-0.3.5/src/limbo/daily_skills/meeting-action-items/LICENSE +21 -0
  50. limbo_code-0.3.5/src/limbo/daily_skills/meeting-action-items/SKILL.md +80 -0
  51. limbo_code-0.3.5/src/limbo/daily_skills/pdf/LICENSE +21 -0
  52. limbo_code-0.3.5/src/limbo/daily_skills/pdf/SKILL.md +117 -0
  53. limbo_code-0.3.5/src/limbo/daily_skills/pdf/references/forms.md +99 -0
  54. limbo_code-0.3.5/src/limbo/daily_skills/pdf/references/nano-pdf-editing.md +40 -0
  55. limbo_code-0.3.5/src/limbo/daily_skills/pdf/references/ocr-extraction.md +165 -0
  56. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/_raster.py +76 -0
  57. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/extract_marker.py +88 -0
  58. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/extract_pymupdf.py +101 -0
  59. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_create.py +130 -0
  60. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_fill_form.py +97 -0
  61. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_form_layout.py +168 -0
  62. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_make_form.py +145 -0
  63. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_merge.py +50 -0
  64. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_meta.py +115 -0
  65. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_page_image.py +99 -0
  66. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_read.py +153 -0
  67. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_secure.py +71 -0
  68. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_split.py +84 -0
  69. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_stamp.py +143 -0
  70. limbo_code-0.3.5/src/limbo/daily_skills/pdf/scripts/pdf_watermark.py +51 -0
  71. limbo_code-0.3.5/src/limbo/daily_skills/powerpoint/LICENSE +21 -0
  72. limbo_code-0.3.5/src/limbo/daily_skills/powerpoint/SKILL.md +212 -0
  73. limbo_code-0.3.5/src/limbo/daily_skills/powerpoint/scripts/pptx_create.py +214 -0
  74. limbo_code-0.3.5/src/limbo/daily_skills/powerpoint/scripts/pptx_edit.py +436 -0
  75. limbo_code-0.3.5/src/limbo/daily_skills/powerpoint/scripts/pptx_from_template.py +88 -0
  76. limbo_code-0.3.5/src/limbo/daily_skills/powerpoint/scripts/pptx_read.py +131 -0
  77. limbo_code-0.3.5/src/limbo/daily_skills/powerpoint/scripts/pptx_render.py +93 -0
  78. limbo_code-0.3.5/src/limbo/daily_skills/weekly-review-planning/LICENSE +21 -0
  79. limbo_code-0.3.5/src/limbo/daily_skills/weekly-review-planning/SKILL.md +73 -0
  80. limbo_code-0.3.5/src/limbo/daily_skills/xlsx/LICENSE +21 -0
  81. limbo_code-0.3.5/src/limbo/daily_skills/xlsx/SKILL.md +188 -0
  82. limbo_code-0.3.5/src/limbo/daily_skills/xlsx/references/restructuring.md +71 -0
  83. limbo_code-0.3.5/src/limbo/daily_skills/xlsx/scripts/csv_to_xlsx.py +104 -0
  84. limbo_code-0.3.5/src/limbo/daily_skills/xlsx/scripts/xlsx_create.py +259 -0
  85. limbo_code-0.3.5/src/limbo/daily_skills/xlsx/scripts/xlsx_edit.py +263 -0
  86. limbo_code-0.3.5/src/limbo/daily_skills/xlsx/scripts/xlsx_read.py +160 -0
  87. limbo_code-0.3.5/src/limbo/daily_skills/xlsx/scripts/xlsx_recalc.py +111 -0
  88. limbo_code-0.3.5/src/limbo/daily_skills/xlsx/scripts/xlsx_restructure.py +337 -0
  89. limbo_code-0.3.5/src/limbo/daily_skills/xlsx/scripts/xlsx_to_csv.py +69 -0
  90. limbo_code-0.3.5/src/limbo/daily_skills/youtube-content/LICENSE +21 -0
  91. limbo_code-0.3.5/src/limbo/daily_skills/youtube-content/SKILL.md +76 -0
  92. limbo_code-0.3.5/src/limbo/daily_skills/youtube-content/references/output-formats.md +56 -0
  93. limbo_code-0.3.5/src/limbo/daily_skills/youtube-content/scripts/fetch_transcript.py +124 -0
  94. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/headless.py +504 -163
  95. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/history.py +61 -12
  96. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/integrations/__init__.py +15 -1
  97. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/integrations/base.py +33 -8
  98. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/integrations/herdr.py +3 -0
  99. limbo_code-0.3.5/src/limbo/integrations/osc7501.py +111 -0
  100. limbo_code-0.3.5/src/limbo/journal.py +335 -0
  101. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/catalog.py +64 -0
  102. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/responses_client.py +6 -0
  103. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/retry.py +51 -16
  104. limbo_code-0.3.5/src/limbo/llm/verify.py +58 -0
  105. limbo_code-0.3.5/src/limbo/memory.py +254 -0
  106. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/models.py +4 -0
  107. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/prompt.py +54 -4
  108. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/pump.py +99 -20
  109. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/sessions.py +266 -29
  110. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/skills.py +20 -7
  111. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/steer.py +41 -7
  112. limbo_code-0.3.5/src/limbo/subagents/board.py +657 -0
  113. limbo_code-0.3.5/src/limbo/subagents/commands.py +104 -0
  114. limbo_code-0.3.5/src/limbo/subagents/events.py +59 -0
  115. limbo_code-0.3.5/src/limbo/subagents/hooks.py +100 -0
  116. limbo_code-0.3.5/src/limbo/subagents/profiles.py +290 -0
  117. limbo_code-0.3.5/src/limbo/subagents/project_scan.py +265 -0
  118. limbo_code-0.3.5/src/limbo/subagents/supervisor.py +1413 -0
  119. limbo_code-0.3.5/src/limbo/subagents/tools.py +437 -0
  120. limbo_code-0.3.5/src/limbo/subagents/worktree.py +118 -0
  121. limbo_code-0.3.5/src/limbo/toggles.py +18 -0
  122. limbo_code-0.3.5/src/limbo/tools/ask_user.py +69 -0
  123. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/base.py +24 -1
  124. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/find.py +1 -0
  125. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/grep.py +1 -0
  126. limbo_code-0.3.5/src/limbo/tools/hooks.py +248 -0
  127. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/ls.py +1 -0
  128. limbo_code-0.3.5/src/limbo/tools/memory.py +96 -0
  129. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/read.py +1 -0
  130. limbo_code-0.3.5/src/limbo/tools/registry.py +326 -0
  131. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/run_code.py +66 -1
  132. limbo_code-0.3.5/src/limbo/tools/session_search.py +186 -0
  133. limbo_code-0.3.5/src/limbo/tools/skill_manage.py +228 -0
  134. limbo_code-0.3.5/src/limbo/tools/web_fetch.py +280 -0
  135. limbo_code-0.3.5/src/limbo/tools/web_search.py +78 -0
  136. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/trace.py +9 -2
  137. limbo_code-0.3.5/src/limbo/tracing/__init__.py +1 -0
  138. limbo_code-0.3.5/src/limbo/tracing/command.py +100 -0
  139. limbo_code-0.3.5/src/limbo/tracing/index.py +154 -0
  140. limbo_code-0.3.5/src/limbo/tracing/model.py +369 -0
  141. limbo_code-0.3.5/src/limbo/tracing/server.py +301 -0
  142. limbo_code-0.3.5/src/limbo/tracing/static/index.html +560 -0
  143. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/app.py +85 -3
  144. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/app.tcss +210 -60
  145. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/commands.py +8 -2
  146. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/contrast.py +25 -3
  147. limbo_code-0.3.5/src/limbo/ui/daily_look.py +157 -0
  148. limbo_code-0.3.5/src/limbo/ui/gradient.py +88 -0
  149. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/key_fixes.py +6 -0
  150. limbo_code-0.3.5/src/limbo/ui/motion.py +230 -0
  151. limbo_code-0.3.5/src/limbo/ui/omarchy.py +217 -0
  152. limbo_code-0.3.5/src/limbo/ui/recent_models.py +31 -0
  153. limbo_code-0.3.5/src/limbo/ui/screens/agents_panel.py +216 -0
  154. limbo_code-0.3.5/src/limbo/ui/screens/auth_dialog.py +105 -0
  155. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/screens/btw.py +6 -0
  156. limbo_code-0.3.5/src/limbo/ui/screens/command_palette.py +206 -0
  157. limbo_code-0.3.5/src/limbo/ui/screens/dialog.py +75 -0
  158. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/screens/main.py +783 -136
  159. limbo_code-0.3.5/src/limbo/ui/screens/model_picker.py +195 -0
  160. limbo_code-0.3.5/src/limbo/ui/screens/session_picker.py +61 -0
  161. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/syntax.py +67 -0
  162. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/theme.py +73 -3
  163. limbo_code-0.3.5/src/limbo/ui/widgets/activity.py +107 -0
  164. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/widgets/chat.py +52 -6
  165. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/widgets/input.py +61 -3
  166. limbo_code-0.3.5/src/limbo/ui/widgets/key_hints.py +71 -0
  167. limbo_code-0.3.5/src/limbo/ui/widgets/landing.py +198 -0
  168. limbo_code-0.3.5/src/limbo/ui/widgets/mode_divider.py +76 -0
  169. limbo_code-0.3.5/src/limbo/ui/widgets/pills.py +74 -0
  170. limbo_code-0.3.5/src/limbo/ui/widgets/sidebar.py +474 -0
  171. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/widgets/status_bar.py +110 -22
  172. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/widgets/tool_card.py +88 -13
  173. limbo_code-0.3.5/src/limbo/websearch/__init__.py +32 -0
  174. limbo_code-0.3.5/src/limbo/websearch/base.py +43 -0
  175. limbo_code-0.3.5/src/limbo/websearch/deepseek.py +124 -0
  176. limbo_code-0.3.5/tests/conftest.py +103 -0
  177. limbo_code-0.3.5/tests/fixtures/omarchy/README.md +4 -0
  178. limbo_code-0.3.5/tests/fixtures/omarchy/catppuccin-latte.toml +31 -0
  179. limbo_code-0.3.5/tests/fixtures/omarchy/gruvbox.toml +31 -0
  180. limbo_code-0.3.5/tests/fixtures/omarchy/hackerman.toml +33 -0
  181. limbo_code-0.3.5/tests/fixtures/omarchy/lumon.toml +34 -0
  182. limbo_code-0.3.5/tests/fixtures/omarchy/rose-pine.toml +31 -0
  183. limbo_code-0.3.5/tests/fixtures/omarchy/tokyo-night.toml +31 -0
  184. limbo_code-0.3.5/tests/test_agent.py +870 -0
  185. limbo_code-0.3.5/tests/test_anthropic_client.py +149 -0
  186. limbo_code-0.3.5/tests/test_auth.py +46 -0
  187. limbo_code-0.3.5/tests/test_btw.py +42 -0
  188. limbo_code-0.3.5/tests/test_catalog.py +31 -0
  189. limbo_code-0.3.5/tests/test_cli.py +85 -0
  190. {limbo_code-0.2.2 → limbo_code-0.3.5}/tests/test_code_mode_agent.py +13 -55
  191. limbo_code-0.3.5/tests/test_compaction.py +69 -0
  192. limbo_code-0.3.5/tests/test_config.py +44 -0
  193. limbo_code-0.3.5/tests/test_conversation.py +105 -0
  194. limbo_code-0.3.5/tests/test_daily_mode.py +229 -0
  195. limbo_code-0.3.5/tests/test_daily_p1.py +273 -0
  196. limbo_code-0.3.5/tests/test_goal.py +43 -0
  197. limbo_code-0.3.5/tests/test_headless.py +191 -0
  198. limbo_code-0.3.5/tests/test_herdr.py +52 -0
  199. limbo_code-0.3.5/tests/test_history.py +79 -0
  200. limbo_code-0.3.5/tests/test_interrupt.py +159 -0
  201. limbo_code-0.3.5/tests/test_llm_client.py +85 -0
  202. limbo_code-0.3.5/tests/test_model_switch.py +56 -0
  203. limbo_code-0.3.5/tests/test_osc7501.py +48 -0
  204. limbo_code-0.3.5/tests/test_plan_mode.py +91 -0
  205. limbo_code-0.3.5/tests/test_prompt.py +27 -0
  206. limbo_code-0.3.5/tests/test_pump.py +224 -0
  207. limbo_code-0.3.5/tests/test_responses_client.py +154 -0
  208. limbo_code-0.3.5/tests/test_retry.py +111 -0
  209. limbo_code-0.3.5/tests/test_sessions.py +143 -0
  210. limbo_code-0.3.5/tests/test_skills.py +33 -0
  211. limbo_code-0.3.5/tests/test_sse.py +33 -0
  212. limbo_code-0.3.5/tests/test_steer.py +24 -0
  213. limbo_code-0.3.5/tests/test_subagents.py +566 -0
  214. limbo_code-0.3.5/tests/test_task_board.py +177 -0
  215. limbo_code-0.3.5/tests/test_trace.py +19 -0
  216. limbo_code-0.3.5/tests/test_usage.py +57 -0
  217. limbo_code-0.3.5/tests/tools/test_base.py +52 -0
  218. limbo_code-0.3.5/tests/tools/test_bash.py +38 -0
  219. limbo_code-0.3.5/tests/tools/test_edit.py +40 -0
  220. limbo_code-0.3.5/tests/tools/test_grep.py +25 -0
  221. limbo_code-0.3.5/tests/tools/test_hooks.py +134 -0
  222. limbo_code-0.3.5/tests/tools/test_ignore.py +20 -0
  223. {limbo_code-0.2.2 → limbo_code-0.3.5}/tests/tools/test_mutation_queue.py +0 -19
  224. limbo_code-0.3.5/tests/tools/test_read.py +29 -0
  225. limbo_code-0.3.5/tests/tools/test_registry.py +23 -0
  226. limbo_code-0.3.5/tests/tools/test_run_code.py +67 -0
  227. limbo_code-0.3.5/tests/tools/test_web_search.py +170 -0
  228. limbo_code-0.3.5/tests/tools/test_write.py +21 -0
  229. limbo_code-0.3.5/tests/tracing/test_command.py +54 -0
  230. limbo_code-0.3.5/tests/tracing/test_index.py +36 -0
  231. limbo_code-0.3.5/tests/tracing/test_model.py +139 -0
  232. limbo_code-0.3.5/tests/tracing/test_server.py +101 -0
  233. limbo_code-0.3.5/tests/ui/__snapshots__/test_snapshots/test_snapshot_idle.raw +211 -0
  234. limbo_code-0.3.5/tests/ui/__snapshots__/test_snapshots/test_snapshot_wide_layout_with_sidebar.raw +237 -0
  235. limbo_code-0.3.5/tests/ui/test_app_smoke.py +12 -0
  236. limbo_code-0.3.5/tests/ui/test_bang_ui.py +146 -0
  237. limbo_code-0.3.5/tests/ui/test_btw_ui.py +82 -0
  238. limbo_code-0.3.5/tests/ui/test_command_menu.py +63 -0
  239. limbo_code-0.3.5/tests/ui/test_compact_ui.py +63 -0
  240. limbo_code-0.3.5/tests/ui/test_daily_look.py +209 -0
  241. limbo_code-0.3.5/tests/ui/test_daily_mode_ui.py +69 -0
  242. limbo_code-0.3.5/tests/ui/test_goal_ui.py +117 -0
  243. limbo_code-0.3.5/tests/ui/test_input_paste.py +63 -0
  244. limbo_code-0.3.5/tests/ui/test_interrupt_ui.py +90 -0
  245. limbo_code-0.3.5/tests/ui/test_main_screen.py +49 -0
  246. limbo_code-0.3.5/tests/ui/test_model_picker.py +171 -0
  247. limbo_code-0.3.5/tests/ui/test_omarchy.py +49 -0
  248. limbo_code-0.3.5/tests/ui/test_path_input.py +96 -0
  249. limbo_code-0.3.5/tests/ui/test_scroll_follow.py +44 -0
  250. limbo_code-0.3.5/tests/ui/test_sessions_ui.py +82 -0
  251. limbo_code-0.3.5/tests/ui/test_sidebar.py +76 -0
  252. limbo_code-0.3.5/tests/ui/test_skills_ui.py +81 -0
  253. {limbo_code-0.2.2 → limbo_code-0.3.5}/tests/ui/test_snapshots.py +29 -60
  254. limbo_code-0.3.5/tests/ui/test_steer_ui.py +146 -0
  255. limbo_code-0.3.5/tests/ui/test_tracing_viewer_ui.py +50 -0
  256. limbo_code-0.3.5/tests/ui/test_widgets.py +38 -0
  257. {limbo_code-0.2.2 → limbo_code-0.3.5}/uv.lock +18 -0
  258. limbo_code-0.2.2/AGENTS.md +0 -127
  259. limbo_code-0.2.2/PKG-INFO +0 -311
  260. limbo_code-0.2.2/README.md +0 -297
  261. limbo_code-0.2.2/src/limbo/tools/registry.py +0 -101
  262. limbo_code-0.2.2/src/limbo/ui/screens/model_picker.py +0 -122
  263. limbo_code-0.2.2/src/limbo/ui/screens/session_picker.py +0 -61
  264. limbo_code-0.2.2/tests/conftest.py +0 -42
  265. limbo_code-0.2.2/tests/test_agent.py +0 -2397
  266. limbo_code-0.2.2/tests/test_anthropic_client.py +0 -644
  267. limbo_code-0.2.2/tests/test_attachments.py +0 -78
  268. limbo_code-0.2.2/tests/test_btw.py +0 -90
  269. limbo_code-0.2.2/tests/test_catalog.py +0 -372
  270. limbo_code-0.2.2/tests/test_cli.py +0 -296
  271. limbo_code-0.2.2/tests/test_clipboard.py +0 -205
  272. limbo_code-0.2.2/tests/test_code_mode.py +0 -77
  273. limbo_code-0.2.2/tests/test_compaction.py +0 -163
  274. limbo_code-0.2.2/tests/test_config.py +0 -293
  275. limbo_code-0.2.2/tests/test_goal.py +0 -228
  276. limbo_code-0.2.2/tests/test_headless.py +0 -578
  277. limbo_code-0.2.2/tests/test_herdr.py +0 -164
  278. limbo_code-0.2.2/tests/test_history.py +0 -94
  279. limbo_code-0.2.2/tests/test_integrations.py +0 -145
  280. limbo_code-0.2.2/tests/test_interrupt.py +0 -570
  281. limbo_code-0.2.2/tests/test_llm_client.py +0 -755
  282. limbo_code-0.2.2/tests/test_llm_scaffold.py +0 -89
  283. limbo_code-0.2.2/tests/test_model_switch.py +0 -140
  284. limbo_code-0.2.2/tests/test_models.py +0 -23
  285. limbo_code-0.2.2/tests/test_prompt.py +0 -103
  286. limbo_code-0.2.2/tests/test_pump.py +0 -423
  287. limbo_code-0.2.2/tests/test_responses_client.py +0 -626
  288. limbo_code-0.2.2/tests/test_retry.py +0 -306
  289. limbo_code-0.2.2/tests/test_sessions.py +0 -468
  290. limbo_code-0.2.2/tests/test_skills.py +0 -187
  291. limbo_code-0.2.2/tests/test_sse.py +0 -92
  292. limbo_code-0.2.2/tests/test_steer.py +0 -66
  293. limbo_code-0.2.2/tests/test_trace.py +0 -132
  294. limbo_code-0.2.2/tests/test_usage.py +0 -159
  295. limbo_code-0.2.2/tests/tools/test_base.py +0 -184
  296. limbo_code-0.2.2/tests/tools/test_bash.py +0 -263
  297. limbo_code-0.2.2/tests/tools/test_edit.py +0 -292
  298. limbo_code-0.2.2/tests/tools/test_find.py +0 -100
  299. limbo_code-0.2.2/tests/tools/test_grep.py +0 -117
  300. limbo_code-0.2.2/tests/tools/test_ignore.py +0 -53
  301. limbo_code-0.2.2/tests/tools/test_ls.py +0 -73
  302. limbo_code-0.2.2/tests/tools/test_read.py +0 -240
  303. limbo_code-0.2.2/tests/tools/test_registry.py +0 -38
  304. limbo_code-0.2.2/tests/tools/test_run_code.py +0 -277
  305. limbo_code-0.2.2/tests/tools/test_write.py +0 -62
  306. limbo_code-0.2.2/tests/ui/__snapshots__/test_snapshots/test_snapshot_idle.raw +0 -172
  307. limbo_code-0.2.2/tests/ui/__snapshots__/test_snapshots/test_snapshot_session_picker.raw +0 -168
  308. limbo_code-0.2.2/tests/ui/__snapshots__/test_snapshots/test_snapshot_thinking_and_running_tool.raw +0 -175
  309. limbo_code-0.2.2/tests/ui/__snapshots__/test_snapshots/test_snapshot_tool_error_and_error_message.raw +0 -176
  310. limbo_code-0.2.2/tests/ui/test_app_smoke.py +0 -36
  311. limbo_code-0.2.2/tests/ui/test_bang_ui.py +0 -357
  312. limbo_code-0.2.2/tests/ui/test_btw_ui.py +0 -158
  313. limbo_code-0.2.2/tests/ui/test_code_mode_ui.py +0 -122
  314. limbo_code-0.2.2/tests/ui/test_command_menu.py +0 -224
  315. limbo_code-0.2.2/tests/ui/test_commands.py +0 -104
  316. limbo_code-0.2.2/tests/ui/test_compact_ui.py +0 -188
  317. limbo_code-0.2.2/tests/ui/test_contrast.py +0 -41
  318. limbo_code-0.2.2/tests/ui/test_copy.py +0 -233
  319. limbo_code-0.2.2/tests/ui/test_game2048.py +0 -277
  320. limbo_code-0.2.2/tests/ui/test_goal_ui.py +0 -278
  321. limbo_code-0.2.2/tests/ui/test_input_attachments.py +0 -168
  322. limbo_code-0.2.2/tests/ui/test_input_history.py +0 -222
  323. limbo_code-0.2.2/tests/ui/test_input_paste.py +0 -314
  324. limbo_code-0.2.2/tests/ui/test_interrupt_ui.py +0 -241
  325. limbo_code-0.2.2/tests/ui/test_key_fixes.py +0 -38
  326. limbo_code-0.2.2/tests/ui/test_main_screen.py +0 -216
  327. limbo_code-0.2.2/tests/ui/test_model_picker.py +0 -393
  328. limbo_code-0.2.2/tests/ui/test_paste_store.py +0 -172
  329. limbo_code-0.2.2/tests/ui/test_path_input.py +0 -275
  330. limbo_code-0.2.2/tests/ui/test_scroll_follow.py +0 -85
  331. limbo_code-0.2.2/tests/ui/test_sessions_ui.py +0 -294
  332. limbo_code-0.2.2/tests/ui/test_skills_ui.py +0 -174
  333. limbo_code-0.2.2/tests/ui/test_startup_art.py +0 -140
  334. limbo_code-0.2.2/tests/ui/test_steer_ui.py +0 -539
  335. limbo_code-0.2.2/tests/ui/test_theme.py +0 -106
  336. limbo_code-0.2.2/tests/ui/test_widgets.py +0 -620
  337. {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/grill-with-docs/SKILL.md +0 -0
  338. {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/grill-with-docs/agents/openai.yaml +0 -0
  339. {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/improve-codebase-architecture/HTML-REPORT.md +0 -0
  340. {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/improve-codebase-architecture/SKILL.md +0 -0
  341. {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/improve-codebase-architecture/agents/openai.yaml +0 -0
  342. {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/tdd/SKILL.md +0 -0
  343. {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/tdd/agents/openai.yaml +0 -0
  344. {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/tdd/mocking.md +0 -0
  345. {limbo_code-0.2.2 → limbo_code-0.3.5}/.agents/skills/tdd/tests.md +0 -0
  346. {limbo_code-0.2.2 → limbo_code-0.3.5}/.github/workflows/publish.yml +0 -0
  347. {limbo_code-0.2.2 → limbo_code-0.3.5}/.github/workflows/test.yml +0 -0
  348. {limbo_code-0.2.2 → limbo_code-0.3.5}/.gitignore +0 -0
  349. {limbo_code-0.2.2 → limbo_code-0.3.5}/Makefile +0 -0
  350. {limbo_code-0.2.2 → limbo_code-0.3.5}/design/bang-verification.md +0 -0
  351. {limbo_code-0.2.2 → limbo_code-0.3.5}/design/confirm_view.html +0 -0
  352. {limbo_code-0.2.2 → limbo_code-0.3.5}/design/limbo-ui-minimal.md +0 -0
  353. {limbo_code-0.2.2 → limbo_code-0.3.5}/design/limbo-ui-redesign.md +0 -0
  354. {limbo_code-0.2.2 → limbo_code-0.3.5}/design/prototype-minimal-confirm.png +0 -0
  355. {limbo_code-0.2.2 → limbo_code-0.3.5}/design/prototype-minimal.html +0 -0
  356. {limbo_code-0.2.2 → limbo_code-0.3.5}/design/prototype-minimal.png +0 -0
  357. {limbo_code-0.2.2 → limbo_code-0.3.5}/design/prototype.html +0 -0
  358. {limbo_code-0.2.2 → limbo_code-0.3.5}/design/prototype.png +0 -0
  359. {limbo_code-0.2.2 → limbo_code-0.3.5}/design/rfc-bang-command.md +0 -0
  360. {limbo_code-0.2.2 → limbo_code-0.3.5}/design/rfc-headless-cli.md +0 -0
  361. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/limbo-current-ui.png +0 -0
  362. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/limbo-new-ui.png +0 -0
  363. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-dark/1_idle.svg +0 -0
  364. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-dark/2_thinking_tool_running.svg +0 -0
  365. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-dark/3_tool_success.svg +0 -0
  366. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-dark/4_tool_error.svg +0 -0
  367. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-dark/5_llm_error.svg +0 -0
  368. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-dark/6_edit_diff.svg +0 -0
  369. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-light/1_idle.svg +0 -0
  370. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-light/2_thinking_tool_running.svg +0 -0
  371. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-light/3_tool_success.svg +0 -0
  372. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-light/4_tool_error.svg +0 -0
  373. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-light/5_llm_error.svg +0 -0
  374. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/assets/walkthrough/limbo-light/6_edit_diff.svg +0 -0
  375. {limbo_code-0.2.2 → limbo_code-0.3.5}/docs/ui-redesign-proposal.md +0 -0
  376. {limbo_code-0.2.2 → limbo_code-0.3.5}/scripts/check_contrast.py +0 -0
  377. {limbo_code-0.2.2 → limbo_code-0.3.5}/scripts/gen_banner.py +0 -0
  378. {limbo_code-0.2.2 → limbo_code-0.3.5}/scripts/ui_walkthrough.py +0 -0
  379. {limbo_code-0.2.2 → limbo_code-0.3.5}/skills-lock.json +0 -0
  380. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/__init__.py +0 -0
  381. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/__main__.py +0 -0
  382. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/attachments.py +0 -0
  383. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/code_mode.py +0 -0
  384. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/compaction.py +0 -0
  385. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/goal.py +0 -0
  386. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/__init__.py +0 -0
  387. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/anthropic_client.py +0 -0
  388. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/client.py +0 -0
  389. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/factory.py +0 -0
  390. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/openai_client.py +0 -0
  391. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/scaffold.py +0 -0
  392. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/sse.py +0 -0
  393. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/llm/usage.py +0 -0
  394. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/model_switch.py +0 -0
  395. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/__init__.py +0 -0
  396. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/bash.py +0 -0
  397. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/edit.py +0 -0
  398. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/ignore.py +0 -0
  399. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/mutation_queue.py +0 -0
  400. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/tools/write.py +0 -0
  401. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/__init__.py +0 -0
  402. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/banner.py +0 -0
  403. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/clipboard.py +0 -0
  404. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/paste_store.py +0 -0
  405. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/path_input.py +0 -0
  406. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/screens/__init__.py +0 -0
  407. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/screens/game2048.py +0 -0
  408. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/screens/verify_picker.py +0 -0
  409. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/widgets/__init__.py +0 -0
  410. {limbo_code-0.2.2 → limbo_code-0.3.5}/src/limbo/ui/widgets/command_menu.py +0 -0
  411. {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 save/trace unit.
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
+ ![Limbo](docs/assets/limbo-hero.png)
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
+ ![The Ctrl+L model picker](docs/assets/limbo-models.png)
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