limbo-code 0.1.12__tar.gz → 0.1.13__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {limbo_code-0.1.12 → limbo_code-0.1.13}/PKG-INFO +18 -1
- {limbo_code-0.1.12 → limbo_code-0.1.13}/README.md +17 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/app.py +18 -1
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/config.py +34 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_cli.py +77 -8
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_config.py +31 -1
- {limbo_code-0.1.12 → limbo_code-0.1.13}/.agents/skills/grill-with-docs/SKILL.md +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/.agents/skills/grill-with-docs/agents/openai.yaml +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/.agents/skills/improve-codebase-architecture/HTML-REPORT.md +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/.agents/skills/improve-codebase-architecture/SKILL.md +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/.agents/skills/improve-codebase-architecture/agents/openai.yaml +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/.agents/skills/tdd/SKILL.md +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/.agents/skills/tdd/agents/openai.yaml +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/.agents/skills/tdd/mocking.md +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/.agents/skills/tdd/tests.md +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/.github/workflows/publish.yml +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/.github/workflows/test.yml +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/.gitignore +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/AGENTS.md +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/CONTEXT.md +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/Makefile +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/design/confirm_view.html +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/design/limbo-ui-minimal.md +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/design/limbo-ui-redesign.md +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/design/prototype-minimal-confirm.png +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/design/prototype-minimal.html +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/design/prototype-minimal.png +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/design/prototype.html +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/design/prototype.png +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/limbo-current-ui.png +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/limbo-new-ui.png +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-dark/1_idle.svg +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-dark/2_thinking_tool_running.svg +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-dark/3_tool_success.svg +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-dark/4_tool_error.svg +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-dark/5_llm_error.svg +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-dark/6_edit_diff.svg +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-light/1_idle.svg +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-light/2_thinking_tool_running.svg +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-light/3_tool_success.svg +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-light/4_tool_error.svg +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-light/5_llm_error.svg +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-light/6_edit_diff.svg +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/session-management.md +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/skills.md +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/docs/ui-redesign-proposal.md +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/pyproject.toml +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/scripts/check_contrast.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/scripts/gen_banner.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/scripts/ui_walkthrough.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/skills-lock.json +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/__init__.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/__main__.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/agent.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/attachments.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/btw.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/compaction.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/goal.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/goal_driver.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/history.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/llm/__init__.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/llm/anthropic_client.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/llm/catalog.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/llm/client.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/llm/factory.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/llm/openai_client.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/llm/responses_client.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/llm/retry.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/llm/scaffold.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/llm/sse.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/llm/usage.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/model_switch.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/models.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/prompt.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/sessions.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/skills.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/steer.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/tools/__init__.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/tools/base.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/tools/bash.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/tools/edit.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/tools/find.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/tools/grep.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/tools/ignore.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/tools/ls.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/tools/mutation_queue.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/tools/read.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/tools/registry.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/tools/write.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/trace.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/__init__.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/app.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/app.tcss +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/banner.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/clipboard.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/commands.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/contrast.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/screens/__init__.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/screens/btw.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/screens/game2048.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/screens/main.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/screens/model_picker.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/screens/session_picker.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/screens/verify_picker.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/syntax.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/theme.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/widgets/__init__.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/widgets/chat.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/widgets/command_menu.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/widgets/input.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/widgets/status_bar.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/ui/widgets/tool_card.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/src/limbo/user_paths.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/conftest.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_agent.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_anthropic_client.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_attachments.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_btw.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_catalog.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_clipboard.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_compaction.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_goal.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_goal_driver.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_history.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_integration.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_interrupt.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_llm_client.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_llm_scaffold.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_model_switch.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_models.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_prompt.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_responses_client.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_retry.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_sessions.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_skills.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_sse.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_steer.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_trace.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_usage.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/test_user_paths.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/tools/test_base.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/tools/test_bash.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/tools/test_edit.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/tools/test_find.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/tools/test_grep.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/tools/test_ignore.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/tools/test_ls.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/tools/test_mutation_queue.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/tools/test_read.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/tools/test_registry.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/tools/test_write.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/__snapshots__/test_snapshots/test_snapshot_idle.raw +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/__snapshots__/test_snapshots/test_snapshot_session_picker.raw +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/__snapshots__/test_snapshots/test_snapshot_thinking_and_running_tool.raw +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/__snapshots__/test_snapshots/test_snapshot_tool_error_and_error_message.raw +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_app_smoke.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_btw_ui.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_command_menu.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_commands.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_compact_ui.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_contrast.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_copy.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_game2048.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_goal_ui.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_input_attachments.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_input_history.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_input_paste.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_interrupt_ui.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_main_screen.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_model_picker.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_scroll_follow.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_sessions_ui.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_skills_ui.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_snapshots.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_startup_art.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_steer_ui.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_theme.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/test_widgets.py +0 -0
- {limbo_code-0.1.12 → limbo_code-0.1.13}/uv.lock +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: limbo-code
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.13
|
|
4
4
|
Summary: A minimal terminal AI coding agent
|
|
5
5
|
Requires-Python: >=3.11
|
|
6
6
|
Requires-Dist: httpx>=0.27
|
|
@@ -183,6 +183,23 @@ Optional tool settings:
|
|
|
183
183
|
bash_enabled = true
|
|
184
184
|
```
|
|
185
185
|
|
|
186
|
+
Optional UI settings:
|
|
187
|
+
|
|
188
|
+
```toml
|
|
189
|
+
[ui]
|
|
190
|
+
kitty_keyboard = "auto" # auto (default) | enabled | disabled
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
`kitty_keyboard` controls Textual's kitty keyboard protocol. The default
|
|
194
|
+
`auto` disables it only when Limbo runs inside the **herdr** multiplexer:
|
|
195
|
+
herdr mirrors a pane's `REPORT_ALL_KEYS` request to the host terminal
|
|
196
|
+
(e.g. Ghostty), and Ghostty then encodes IME commits as the physical commit
|
|
197
|
+
key (space) while dropping the composed text — typing Chinese turns into
|
|
198
|
+
spaces (pasting is unaffected). With the protocol disabled, herdr keeps
|
|
199
|
+
IME-compatible host flags and committed IME text arrives as raw UTF-8. Use
|
|
200
|
+
`enabled` to always keep the protocol (e.g. in a direct Ghostty/kitty
|
|
201
|
+
terminal), or `disabled` to force raw/legacy input everywhere.
|
|
202
|
+
|
|
186
203
|
### Session storage
|
|
187
204
|
|
|
188
205
|
Conversations are saved as JSONL files in `~/.limbo/sessions/` so you can
|
|
@@ -169,6 +169,23 @@ Optional tool settings:
|
|
|
169
169
|
bash_enabled = true
|
|
170
170
|
```
|
|
171
171
|
|
|
172
|
+
Optional UI settings:
|
|
173
|
+
|
|
174
|
+
```toml
|
|
175
|
+
[ui]
|
|
176
|
+
kitty_keyboard = "auto" # auto (default) | enabled | disabled
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`kitty_keyboard` controls Textual's kitty keyboard protocol. The default
|
|
180
|
+
`auto` disables it only when Limbo runs inside the **herdr** multiplexer:
|
|
181
|
+
herdr mirrors a pane's `REPORT_ALL_KEYS` request to the host terminal
|
|
182
|
+
(e.g. Ghostty), and Ghostty then encodes IME commits as the physical commit
|
|
183
|
+
key (space) while dropping the composed text — typing Chinese turns into
|
|
184
|
+
spaces (pasting is unaffected). With the protocol disabled, herdr keeps
|
|
185
|
+
IME-compatible host flags and committed IME text arrives as raw UTF-8. Use
|
|
186
|
+
`enabled` to always keep the protocol (e.g. in a direct Ghostty/kitty
|
|
187
|
+
terminal), or `disabled` to force raw/legacy input everywhere.
|
|
188
|
+
|
|
172
189
|
### Session storage
|
|
173
190
|
|
|
174
191
|
Conversations are saved as JSONL files in `~/.limbo/sessions/` so you can
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import argparse
|
|
6
|
+
import os
|
|
6
7
|
import sys
|
|
7
8
|
from pathlib import Path
|
|
8
9
|
|
|
@@ -15,11 +16,24 @@ from limbo.sessions import (
|
|
|
15
16
|
latest_session,
|
|
16
17
|
load_session,
|
|
17
18
|
)
|
|
18
|
-
from limbo.ui.app import LimboApp
|
|
19
19
|
|
|
20
20
|
DEFAULT_SESSION_DIR = Path.home() / ".limbo" / "sessions"
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
def _apply_kitty_keyboard_workaround(config) -> None:
|
|
24
|
+
"""Apply the `ui.kitty_keyboard` setting before Textual initializes.
|
|
25
|
+
|
|
26
|
+
Textual reads TEXTUAL_DISABLE_KITTY_KEY at import time, so this must run
|
|
27
|
+
before the first `textual` import. See KittyKeyboardMode for why typing
|
|
28
|
+
Chinese inside the herdr multiplexer turns into spaces when the kitty
|
|
29
|
+
keyboard protocol is enabled.
|
|
30
|
+
"""
|
|
31
|
+
if config.ui.kitty_keyboard.disable_textual_kitty_key(
|
|
32
|
+
in_herdr=bool(os.environ.get("HERDR_ENV"))
|
|
33
|
+
):
|
|
34
|
+
os.environ["TEXTUAL_DISABLE_KITTY_KEY"] = "1"
|
|
35
|
+
|
|
36
|
+
|
|
23
37
|
def main() -> int:
|
|
24
38
|
parser = argparse.ArgumentParser(description="Limbo TUI coding agent")
|
|
25
39
|
parser.add_argument(
|
|
@@ -55,6 +69,7 @@ def main() -> int:
|
|
|
55
69
|
args = parser.parse_args()
|
|
56
70
|
|
|
57
71
|
config = load_config()
|
|
72
|
+
_apply_kitty_keyboard_workaround(config)
|
|
58
73
|
if args.model:
|
|
59
74
|
config.llm.model = args.model
|
|
60
75
|
spec = resolve_model(config.llm.model)
|
|
@@ -97,6 +112,8 @@ def main() -> int:
|
|
|
97
112
|
if meta.workdir and Path(meta.workdir).is_dir():
|
|
98
113
|
workdir = Path(meta.workdir)
|
|
99
114
|
|
|
115
|
+
from limbo.ui.app import LimboApp # noqa: I001, PLC0415 - deferred so the kitty keyboard workaround runs first
|
|
116
|
+
|
|
100
117
|
app = LimboApp(
|
|
101
118
|
workdir=workdir,
|
|
102
119
|
config=config,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
5
|
import warnings
|
|
6
|
+
from enum import Enum
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from typing import Any
|
|
8
9
|
|
|
@@ -132,12 +133,45 @@ class ProviderOverride(BaseModel):
|
|
|
132
133
|
headers: dict[str, str] = Field(default_factory=dict)
|
|
133
134
|
|
|
134
135
|
|
|
136
|
+
class KittyKeyboardMode(str, Enum):
|
|
137
|
+
"""How Limbo negotiates Textual's kitty keyboard protocol.
|
|
138
|
+
|
|
139
|
+
Textual 8.2.8 requests the kitty keyboard protocol with ``REPORT_ALL_KEYS``.
|
|
140
|
+
Inside the herdr multiplexer, herdr mirrors that request to the host
|
|
141
|
+
terminal (e.g. Ghostty), and Ghostty then encodes IME commits as the
|
|
142
|
+
physical commit key (space) while dropping the composed text — typing
|
|
143
|
+
Chinese turns into spaces (pasting is unaffected, it bypasses the key
|
|
144
|
+
encoder). Disabling the protocol makes herdr negotiate IME-compatible host
|
|
145
|
+
flags so committed IME text arrives as raw UTF-8.
|
|
146
|
+
"""
|
|
147
|
+
|
|
148
|
+
# Disable only inside the herdr multiplexer (detected via HERDR_ENV);
|
|
149
|
+
# keep the protocol everywhere else. Safe default for CJK users.
|
|
150
|
+
AUTO = "auto"
|
|
151
|
+
# Always request the kitty keyboard protocol (Textual's default behavior).
|
|
152
|
+
ENABLED = "enabled"
|
|
153
|
+
# Never request it — forces raw/legacy terminal input everywhere.
|
|
154
|
+
DISABLED = "disabled"
|
|
155
|
+
|
|
156
|
+
def disable_textual_kitty_key(self, *, in_herdr: bool) -> bool:
|
|
157
|
+
"""Whether Textual's kitty keyboard protocol should be disabled."""
|
|
158
|
+
return self is KittyKeyboardMode.DISABLED or (
|
|
159
|
+
self is KittyKeyboardMode.AUTO and in_herdr
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
|
|
135
163
|
class UIConfig(BaseModel):
|
|
136
164
|
# Theme name: built-in "limbo-dark" (default) / "limbo-light", or any
|
|
137
165
|
# Textual built-in theme (e.g. "textual-dark", "dracula", "nord").
|
|
138
166
|
theme: str | None = None
|
|
139
167
|
# Show the startup ASCII-art banner on fresh sessions.
|
|
140
168
|
show_banner: bool = True
|
|
169
|
+
# Kitty keyboard protocol handling (see KittyKeyboardMode):
|
|
170
|
+
# auto - enable normally, except inside the herdr multiplexer where
|
|
171
|
+
# it breaks IME/Chinese input (default)
|
|
172
|
+
# enabled - always request the kitty keyboard protocol
|
|
173
|
+
# disabled - never request it (forces raw/legacy terminal input)
|
|
174
|
+
kitty_keyboard: KittyKeyboardMode = KittyKeyboardMode.AUTO
|
|
141
175
|
|
|
142
176
|
|
|
143
177
|
class SafetyConfig(BaseModel):
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import os
|
|
5
6
|
import sys
|
|
6
7
|
from pathlib import Path
|
|
7
8
|
from unittest.mock import MagicMock
|
|
@@ -9,6 +10,7 @@ from unittest.mock import MagicMock
|
|
|
9
10
|
import pytest
|
|
10
11
|
|
|
11
12
|
import limbo.app as app_module
|
|
13
|
+
import limbo.ui.app as ui_app_module
|
|
12
14
|
from limbo.app import main
|
|
13
15
|
from limbo.config import Config
|
|
14
16
|
|
|
@@ -23,7 +25,7 @@ def fake_config(monkeypatch):
|
|
|
23
25
|
|
|
24
26
|
def test_cli_passes_session_dir(fake_config, monkeypatch):
|
|
25
27
|
mock_app_class = MagicMock()
|
|
26
|
-
monkeypatch.setattr(
|
|
28
|
+
monkeypatch.setattr(ui_app_module, "LimboApp", mock_app_class)
|
|
27
29
|
monkeypatch.setattr(
|
|
28
30
|
sys,
|
|
29
31
|
"argv",
|
|
@@ -40,7 +42,7 @@ def test_cli_passes_session_dir(fake_config, monkeypatch):
|
|
|
40
42
|
|
|
41
43
|
def test_cli_defaults_session_dir_to_none(fake_config, monkeypatch):
|
|
42
44
|
mock_app_class = MagicMock()
|
|
43
|
-
monkeypatch.setattr(
|
|
45
|
+
monkeypatch.setattr(ui_app_module, "LimboApp", mock_app_class)
|
|
44
46
|
monkeypatch.setattr(sys, "argv", ["limbo"])
|
|
45
47
|
|
|
46
48
|
assert main() == 0
|
|
@@ -70,7 +72,7 @@ def test_cli_continue_resumes_latest_for_workdir(fake_config, monkeypatch, tmp_p
|
|
|
70
72
|
path = _write_session(session_dir, "abc123", project)
|
|
71
73
|
|
|
72
74
|
mock_app_class = MagicMock()
|
|
73
|
-
monkeypatch.setattr(
|
|
75
|
+
monkeypatch.setattr(ui_app_module, "LimboApp", mock_app_class)
|
|
74
76
|
monkeypatch.setattr(
|
|
75
77
|
sys,
|
|
76
78
|
"argv",
|
|
@@ -91,7 +93,7 @@ def test_cli_continue_resumes_latest_for_workdir(fake_config, monkeypatch, tmp_p
|
|
|
91
93
|
|
|
92
94
|
|
|
93
95
|
def test_cli_continue_without_sessions_fails(fake_config, monkeypatch, tmp_path, capsys):
|
|
94
|
-
monkeypatch.setattr(
|
|
96
|
+
monkeypatch.setattr(ui_app_module, "LimboApp", MagicMock())
|
|
95
97
|
monkeypatch.setattr(
|
|
96
98
|
sys,
|
|
97
99
|
"argv",
|
|
@@ -118,7 +120,7 @@ def test_cli_resume_by_id_prefix_switches_workdir(
|
|
|
118
120
|
path = _write_session(session_dir, "def456", project)
|
|
119
121
|
|
|
120
122
|
mock_app_class = MagicMock()
|
|
121
|
-
monkeypatch.setattr(
|
|
123
|
+
monkeypatch.setattr(ui_app_module, "LimboApp", mock_app_class)
|
|
122
124
|
monkeypatch.setattr(
|
|
123
125
|
sys,
|
|
124
126
|
"argv",
|
|
@@ -133,7 +135,7 @@ def test_cli_resume_by_id_prefix_switches_workdir(
|
|
|
133
135
|
|
|
134
136
|
|
|
135
137
|
def test_cli_resume_unknown_id_fails(fake_config, monkeypatch, tmp_path, capsys):
|
|
136
|
-
monkeypatch.setattr(
|
|
138
|
+
monkeypatch.setattr(ui_app_module, "LimboApp", MagicMock())
|
|
137
139
|
monkeypatch.setattr(
|
|
138
140
|
sys,
|
|
139
141
|
"argv",
|
|
@@ -151,7 +153,7 @@ def test_cli_resume_ambiguous_id_lists_candidates(
|
|
|
151
153
|
_write_session(session_dir, "abc1", tmp_path)
|
|
152
154
|
_write_session(session_dir, "abc2", tmp_path)
|
|
153
155
|
|
|
154
|
-
monkeypatch.setattr(
|
|
156
|
+
monkeypatch.setattr(ui_app_module, "LimboApp", MagicMock())
|
|
155
157
|
monkeypatch.setattr(
|
|
156
158
|
sys,
|
|
157
159
|
"argv",
|
|
@@ -165,7 +167,7 @@ def test_cli_resume_ambiguous_id_lists_candidates(
|
|
|
165
167
|
|
|
166
168
|
def test_cli_model_flag_overrides_config(fake_config, monkeypatch):
|
|
167
169
|
mock_app_class = MagicMock()
|
|
168
|
-
monkeypatch.setattr(
|
|
170
|
+
monkeypatch.setattr(ui_app_module, "LimboApp", mock_app_class)
|
|
169
171
|
monkeypatch.setattr(sys, "argv", ["limbo", "--model", "glm-4.7"])
|
|
170
172
|
|
|
171
173
|
assert main() == 0
|
|
@@ -173,3 +175,70 @@ def test_cli_model_flag_overrides_config(fake_config, monkeypatch):
|
|
|
173
175
|
assert fake_config.llm.model == "glm-4.7"
|
|
174
176
|
kwargs = mock_app_class.call_args.kwargs
|
|
175
177
|
assert kwargs["config"].llm.model == "glm-4.7"
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def _probe_kitty_env(home: Path, herdr: bool) -> str:
|
|
181
|
+
"""Import limbo.app in a subprocess with a controlled HOME + HERDR_ENV and
|
|
182
|
+
report whether TEXTUAL_DISABLE_KITTY_KEY ended up set."""
|
|
183
|
+
import subprocess
|
|
184
|
+
import sys
|
|
185
|
+
|
|
186
|
+
probe = (
|
|
187
|
+
"import os, sys; "
|
|
188
|
+
"sys.argv = ['limbo']; "
|
|
189
|
+
"import limbo.app; "
|
|
190
|
+
"limbo.app.main(); "
|
|
191
|
+
"print(os.environ.get('TEXTUAL_DISABLE_KITTY_KEY', ''))"
|
|
192
|
+
)
|
|
193
|
+
env = os.environ.copy()
|
|
194
|
+
env["HOME"] = str(home)
|
|
195
|
+
env.pop("TEXTUAL_DISABLE_KITTY_KEY", None)
|
|
196
|
+
if herdr:
|
|
197
|
+
env["HERDR_ENV"] = "1"
|
|
198
|
+
else:
|
|
199
|
+
env.pop("HERDR_ENV", None)
|
|
200
|
+
run = subprocess.run(
|
|
201
|
+
[sys.executable, "-c", probe],
|
|
202
|
+
capture_output=True,
|
|
203
|
+
text=True,
|
|
204
|
+
env=env,
|
|
205
|
+
cwd=os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
|
206
|
+
)
|
|
207
|
+
assert run.returncode == 0, run.stderr
|
|
208
|
+
return run.stdout.strip()
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def test_kitty_keyboard_config_drives_textual_env(tmp_path):
|
|
212
|
+
"""The `ui.kitty_keyboard` config option decides Textual's
|
|
213
|
+
TEXTUAL_DISABLE_KITTY_KEY, not raw environment sniffing.
|
|
214
|
+
|
|
215
|
+
Without the workaround, Textual's kitty keyboard REPORT_ALL_KEYS request
|
|
216
|
+
makes herdr mirror report-all to the host terminal (Ghostty), and Ghostty
|
|
217
|
+
then encodes IME commits as the physical commit key (space) while dropping
|
|
218
|
+
the composed text — typing Chinese turns into spaces. Pasting is
|
|
219
|
+
unaffected because it bypasses the key encoder.
|
|
220
|
+
"""
|
|
221
|
+
home = tmp_path / "home"
|
|
222
|
+
(home / ".limbo").mkdir(parents=True)
|
|
223
|
+
config = home / ".limbo" / "config.toml"
|
|
224
|
+
|
|
225
|
+
# auto (default) + inside herdr -> disable (the IME fix)
|
|
226
|
+
config.write_text('[ui]\nkitty_keyboard = "auto"\n')
|
|
227
|
+
assert _probe_kitty_env(home, herdr=True) == "1"
|
|
228
|
+
# auto + direct terminal -> keep the kitty keyboard protocol
|
|
229
|
+
assert _probe_kitty_env(home, herdr=False) == ""
|
|
230
|
+
# enabled + herdr -> user opted out of the workaround
|
|
231
|
+
config.write_text('[ui]\nkitty_keyboard = "enabled"\n')
|
|
232
|
+
assert _probe_kitty_env(home, herdr=True) == ""
|
|
233
|
+
# disabled everywhere
|
|
234
|
+
config.write_text('[ui]\nkitty_keyboard = "disabled"\n')
|
|
235
|
+
assert _probe_kitty_env(home, herdr=False) == "1"
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def test_kitty_keyboard_config_defaults_to_auto(tmp_path):
|
|
239
|
+
"""Without a config file the default is `auto`: inside herdr the
|
|
240
|
+
workaround is on, elsewhere the kitty keyboard protocol stays enabled."""
|
|
241
|
+
home = tmp_path / "home"
|
|
242
|
+
(home / ".limbo").mkdir(parents=True)
|
|
243
|
+
assert _probe_kitty_env(home, herdr=True) == "1"
|
|
244
|
+
assert _probe_kitty_env(home, herdr=False) == ""
|
|
@@ -4,7 +4,12 @@ from pathlib import Path
|
|
|
4
4
|
|
|
5
5
|
import pytest
|
|
6
6
|
|
|
7
|
-
from limbo.config import
|
|
7
|
+
from limbo.config import (
|
|
8
|
+
Config,
|
|
9
|
+
KittyKeyboardMode,
|
|
10
|
+
load_config,
|
|
11
|
+
save_model_to_config,
|
|
12
|
+
)
|
|
8
13
|
|
|
9
14
|
|
|
10
15
|
def test_default_config():
|
|
@@ -218,3 +223,28 @@ def test_save_model_unwritable_path_returns_false(tmp_path):
|
|
|
218
223
|
assert save_model_to_config("glm-4.7", path) is False
|
|
219
224
|
finally:
|
|
220
225
|
os.chmod(tmp_path, 0o700)
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
def test_kitty_keyboard_default_is_auto():
|
|
229
|
+
cfg = Config()
|
|
230
|
+
assert cfg.ui.kitty_keyboard == KittyKeyboardMode.AUTO
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def test_kitty_keyboard_mode_from_toml(tmp_path):
|
|
234
|
+
path = tmp_path / "kitty.toml"
|
|
235
|
+
path.write_text('[ui]\nkitty_keyboard = "disabled"\n')
|
|
236
|
+
assert load_config(path).ui.kitty_keyboard is KittyKeyboardMode.DISABLED
|
|
237
|
+
|
|
238
|
+
path.write_text('[ui]\nkitty_keyboard = "enabled"\n')
|
|
239
|
+
assert load_config(path).ui.kitty_keyboard is KittyKeyboardMode.ENABLED
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
def test_kitty_keyboard_mode_disable_decision():
|
|
243
|
+
# auto: disable only inside the herdr multiplexer (IME fix), keep the
|
|
244
|
+
# kitty keyboard protocol everywhere else.
|
|
245
|
+
assert KittyKeyboardMode.AUTO.disable_textual_kitty_key(in_herdr=True)
|
|
246
|
+
assert not KittyKeyboardMode.AUTO.disable_textual_kitty_key(in_herdr=False)
|
|
247
|
+
# enabled: never disable (opt out of the workaround).
|
|
248
|
+
assert not KittyKeyboardMode.ENABLED.disable_textual_kitty_key(in_herdr=True)
|
|
249
|
+
# disabled: always force raw/legacy input.
|
|
250
|
+
assert KittyKeyboardMode.DISABLED.disable_textual_kitty_key(in_herdr=False)
|
|
File without changes
|
|
File without changes
|
{limbo_code-0.1.12 → limbo_code-0.1.13}/.agents/skills/improve-codebase-architecture/HTML-REPORT.md
RENAMED
|
File without changes
|
{limbo_code-0.1.12 → limbo_code-0.1.13}/.agents/skills/improve-codebase-architecture/SKILL.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-dark/3_tool_success.svg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-light/3_tool_success.svg
RENAMED
|
File without changes
|
{limbo_code-0.1.12 → limbo_code-0.1.13}/docs/assets/walkthrough/limbo-light/4_tool_error.svg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{limbo_code-0.1.12 → limbo_code-0.1.13}/tests/ui/__snapshots__/test_snapshots/test_snapshot_idle.raw
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|