kiwi-code 0.0.435__tar.gz → 0.0.436__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.
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/PKG-INFO +1 -1
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/pyproject.toml +1 -1
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_cli/client.py +27 -5
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/tests/test_tui_headless.py +0 -49
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/uv.lock +1 -1
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/.github/workflows/publish.yml +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/.github/workflows/test.yml +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/.gitignore +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/.python-version +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/CLAUDE.md +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/Makefile +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/README.md +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_cli/__init__.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_cli/auth.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_cli/checkpoints.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_cli/cli.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_cli/commands.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_cli/logger.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_cli/models.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_cli/runtime_manager.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_cli/server.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_cli/terminal_mode.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_runtime/__init__.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_runtime/__main__.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_runtime/main.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/__init__.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/inline_file_picker.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/main.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/random_words.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/runtime_agent.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/screens/__init__.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/screens/attach_content.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/screens/command_result.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/screens/dashboard.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/screens/file_browser.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/screens/help.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/screens/id_picker.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/screens/login.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/screens/runtime_cleanup.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/screens/runtime_logs.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/screens/slash_picker.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/slash_commands.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/status_words.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/widgets.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/src/kiwi_tui/worktrees.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/test_hello.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/tests/__init__.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/tests/conftest.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/tests/test_checkpoints.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/tests/test_cli_help.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/tests/test_imports.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/tests/test_reexec_kiwi.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/tests/test_runtime_log_trimming.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/tests/test_slash_commands.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/tests/test_terminal_mode.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/tests/test_tokens.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/tests/test_tui_interactive_runtime.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/tests/test_tui_palette.py +0 -0
- {kiwi_code-0.0.435 → kiwi_code-0.0.436}/tests/test_worktrees.py +0 -0
|
@@ -598,6 +598,17 @@ class AutobotsClientWrapper:
|
|
|
598
598
|
logger.error(f"Error processing SSE data: {e}")
|
|
599
599
|
return False
|
|
600
600
|
|
|
601
|
+
pending_blank = False
|
|
602
|
+
|
|
603
|
+
def _is_field_line(s: str) -> bool:
|
|
604
|
+
return (
|
|
605
|
+
s.startswith("data:")
|
|
606
|
+
or s.startswith(":")
|
|
607
|
+
or s.startswith("event:")
|
|
608
|
+
or s.startswith("id:")
|
|
609
|
+
or s.startswith("retry:")
|
|
610
|
+
)
|
|
611
|
+
|
|
601
612
|
async for chunk in response.aiter_bytes():
|
|
602
613
|
text_chunk = decoder.decode(chunk, final=False)
|
|
603
614
|
buffer += text_chunk
|
|
@@ -606,14 +617,26 @@ class AutobotsClientWrapper:
|
|
|
606
617
|
line, buffer = buffer.split("\n", 1)
|
|
607
618
|
line = line.rstrip("\r")
|
|
608
619
|
|
|
609
|
-
|
|
620
|
+
if pending_blank:
|
|
621
|
+
if line == "":
|
|
622
|
+
pending_blank = False
|
|
623
|
+
if _flush_event():
|
|
624
|
+
return
|
|
625
|
+
continue
|
|
626
|
+
if _is_field_line(line):
|
|
627
|
+
pending_blank = False
|
|
628
|
+
if _flush_event():
|
|
629
|
+
return
|
|
630
|
+
else:
|
|
631
|
+
event_data_lines.append("")
|
|
632
|
+
pending_blank = False
|
|
633
|
+
|
|
610
634
|
if line == "":
|
|
611
|
-
if
|
|
612
|
-
|
|
635
|
+
if event_data_lines:
|
|
636
|
+
pending_blank = True
|
|
613
637
|
continue
|
|
614
638
|
|
|
615
639
|
if line.startswith(":"):
|
|
616
|
-
# keep-alive comments
|
|
617
640
|
continue
|
|
618
641
|
|
|
619
642
|
if line.startswith("data:"):
|
|
@@ -626,7 +649,6 @@ class AutobotsClientWrapper:
|
|
|
626
649
|
if event_data_lines:
|
|
627
650
|
event_data_lines.append(line)
|
|
628
651
|
|
|
629
|
-
# Flush any remaining bytes in decoder + any pending event data.
|
|
630
652
|
decoder.decode(b"", final=True)
|
|
631
653
|
_flush_event()
|
|
632
654
|
except Exception as e:
|
|
@@ -852,55 +852,6 @@ async def test_tui_streaming_requests_autofollow_when_viewport_is_near_bottom(
|
|
|
852
852
|
)
|
|
853
853
|
await pilot.pause()
|
|
854
854
|
assert calls == [{"after_refresh": True}]
|
|
855
|
-
@pytest.mark.asyncio
|
|
856
|
-
async def test_tui_streaming_does_not_force_scroll_when_user_reads_older_messages(
|
|
857
|
-
isolated_home: Path,
|
|
858
|
-
) -> None:
|
|
859
|
-
"""Streaming updates should not yank the viewport back to the bottom."""
|
|
860
|
-
tokens_path = isolated_home / ".kiwi" / "tokens.json"
|
|
861
|
-
tokens_path.parent.mkdir(parents=True, exist_ok=True)
|
|
862
|
-
tokens_path.write_text(
|
|
863
|
-
json.dumps(
|
|
864
|
-
{
|
|
865
|
-
"access_token": "test-access-token",
|
|
866
|
-
"refresh_token": "test-refresh-token",
|
|
867
|
-
"token_type": "Bearer",
|
|
868
|
-
"expires_at": None,
|
|
869
|
-
}
|
|
870
|
-
),
|
|
871
|
-
encoding="utf-8",
|
|
872
|
-
)
|
|
873
|
-
|
|
874
|
-
from kiwi_tui.main import AutobotsTUI
|
|
875
|
-
|
|
876
|
-
app = AutobotsTUI()
|
|
877
|
-
async with app.run_test(size=(80, 24)) as pilot:
|
|
878
|
-
await pilot.pause()
|
|
879
|
-
assert type(app.screen).__name__ == "DashboardScreen"
|
|
880
|
-
screen = app.screen
|
|
881
|
-
|
|
882
|
-
calls: list[dict] = []
|
|
883
|
-
monkeypatch = pytest.MonkeyPatch()
|
|
884
|
-
monkeypatch.setattr(screen, "_messages_is_near_bottom", lambda *args, **kwargs: False)
|
|
885
|
-
monkeypatch.setattr(
|
|
886
|
-
screen,
|
|
887
|
-
"_scroll_messages_to_end",
|
|
888
|
-
lambda *args, **kwargs: calls.append(dict(kwargs)),
|
|
889
|
-
)
|
|
890
|
-
try:
|
|
891
|
-
widget = screen.update_streaming_message({"blocks": [{"text": "stream start"}]})
|
|
892
|
-
await pilot.pause()
|
|
893
|
-
assert widget is not None
|
|
894
|
-
assert calls == []
|
|
895
|
-
|
|
896
|
-
screen.update_streaming_message(
|
|
897
|
-
{"blocks": [{"text": "\n".join(f"stream line {i}" for i in range(60))}]},
|
|
898
|
-
widget,
|
|
899
|
-
)
|
|
900
|
-
await pilot.pause()
|
|
901
|
-
assert calls == []
|
|
902
|
-
finally:
|
|
903
|
-
monkeypatch.undo()
|
|
904
855
|
|
|
905
856
|
@pytest.mark.asyncio
|
|
906
857
|
async def test_tui_quits_cleanly(isolated_home: Path) -> None:
|
|
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
|