thwip-cli 1.6.1__tar.gz → 1.6.2__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.
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/PKG-INFO +1 -1
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/docs/verification.md +2 -1
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/pyproject.toml +1 -1
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/__init__.py +1 -1
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/cli.py +35 -1
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/uv.lock +1 -1
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/.github/workflows/publish.yml +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/.gitignore +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/LICENSE +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/README.md +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/docs/handoff-research.md +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/install.sh +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/__init__.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/fake_providers.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_agents.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_audit_regressions.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_catalog.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_cli.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_compatible_streaming.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_config.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_detector.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_direct_providers_e2e.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_handoff.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_native_agents.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_native_launcher.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_native_print.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_native_rpc.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_parity_commands.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_repair_verification.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_session.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_tools.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_utils.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/__main__.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/__init__.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/base.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/catalog.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/chat_messages.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/claude_agent.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/deepseek_agent.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/google_agent.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/groq_agent.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/native_agent.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/native_common.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/native_print.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/native_rpc.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/ollama_agent.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/openai_agent.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/openrouter_agent.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/config.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/detector.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/endpoints.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/handoff.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/limits.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/session.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/shortcuts.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/theme.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/tools/__init__.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/tools/code_runner.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/tools/file_editor.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/tools/git_ops.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/tools/terminal.py +0 -0
- {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: thwip-cli
|
|
3
|
-
Version: 1.6.
|
|
3
|
+
Version: 1.6.2
|
|
4
4
|
Summary: Universal coding agent multiplexer: detect, switch, and route between AI coding agents seamlessly
|
|
5
5
|
Project-URL: Homepage, https://github.com/tanmayhutt/thwip-cli
|
|
6
6
|
Project-URL: Repository, https://github.com/tanmayhutt/thwip-cli
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Verification status
|
|
2
2
|
|
|
3
|
-
Verified locally on 2026-09-24 for v1.6.
|
|
3
|
+
Verified locally on 2026-09-24 for v1.6.2. The Python suite
|
|
4
4
|
passes 265 offline tests on Python 3.11, 3.12, and 3.13. Exhaustive behavior across every provider and
|
|
5
5
|
configuration has not been established.
|
|
6
6
|
|
|
@@ -19,6 +19,7 @@ each using its existing sign-in. No API keys were configured.
|
|
|
19
19
|
| Ctrl+C during a response | Turn cancelled, child process terminated, REPL continued, unanswered message removed |
|
|
20
20
|
| Ctrl+C at a Codex permission prompt | Turn cancelled, no file created, no leftover process, REPL continued |
|
|
21
21
|
| Usage-limit failover | With a test-only shim making Codex report "You've hit your usage limit", the real REPL showed the alternatives, switched to Claude Code on `1`, retried the message, and answered; history held one clean pair |
|
|
22
|
+
| Terminal hygiene (v1.6.2) | Cursor-position queries disabled, keyboard echo off during turns, stray input flushed before each prompt; fixes `^[`/`^R` noise and phantom empty prompts seen in a real Ghostty session during a slow Antigravity reply. Permission prompt and chat re-verified live afterwards |
|
|
22
23
|
| Ollama adapter (v1.6.1) | End to end against the fake Ollama routes: model list, streamed text with usage, tool call, HTTP 500 and unreachable server now raise clear errors instead of ending the turn silently |
|
|
23
24
|
| Dependency audits (v1.6.1) | pip-audit reports no known vulnerabilities; npm audit reports zero |
|
|
24
25
|
| Direct API adapters (v1.6.0) | All six (OpenAI, Anthropic, Google, DeepSeek, Groq, OpenRouter) run end to end through their real SDKs over HTTP against `tests/fake_providers.py`: live catalog, streamed text with usage, tool call and result round trip, HTTP 429 to LimitHit. The real REPL was also driven against the fake with direct keys: startup, live `/models`, chat, read_file tool round, provider switches, and 429 failover |
|
|
@@ -68,6 +68,39 @@ from thwip.theme import (
|
|
|
68
68
|
)
|
|
69
69
|
from thwip.tools import ToolManager
|
|
70
70
|
|
|
71
|
+
# prompt_toolkit's cursor-position query is answered by the terminal with an escape sequence.
|
|
72
|
+
# During a long native turn nobody reads stdin, so the answer would be echoed as ^[ ... R and later
|
|
73
|
+
# swallowed as input. The layout does not need it, so disable the query.
|
|
74
|
+
os.environ.setdefault("PROMPT_TOOLKIT_NO_CPR", "1")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class QuietTerminal:
|
|
78
|
+
"""Turn off keyboard echo while a turn runs and drop stray input before the next prompt."""
|
|
79
|
+
|
|
80
|
+
def __enter__(self):
|
|
81
|
+
self._saved = None
|
|
82
|
+
try:
|
|
83
|
+
import termios
|
|
84
|
+
self._fd = sys.stdin.fileno()
|
|
85
|
+
if sys.stdin.isatty():
|
|
86
|
+
self._saved = termios.tcgetattr(self._fd)
|
|
87
|
+
quiet = termios.tcgetattr(self._fd)
|
|
88
|
+
quiet[3] &= ~termios.ECHO
|
|
89
|
+
termios.tcsetattr(self._fd, termios.TCSANOW, quiet)
|
|
90
|
+
except (ImportError, OSError, ValueError, AttributeError):
|
|
91
|
+
self._saved = None
|
|
92
|
+
return self
|
|
93
|
+
|
|
94
|
+
def __exit__(self, *exc):
|
|
95
|
+
try:
|
|
96
|
+
import termios
|
|
97
|
+
if self._saved is not None:
|
|
98
|
+
termios.tcflush(self._fd, termios.TCIFLUSH)
|
|
99
|
+
termios.tcsetattr(self._fd, termios.TCSANOW, self._saved)
|
|
100
|
+
except (ImportError, OSError, ValueError, AttributeError):
|
|
101
|
+
pass
|
|
102
|
+
return False
|
|
103
|
+
|
|
71
104
|
|
|
72
105
|
class TurnInterrupted(Exception):
|
|
73
106
|
"""Raised when the user presses Ctrl+C at a prompt shown during a turn."""
|
|
@@ -217,7 +250,8 @@ class ThwipCLI:
|
|
|
217
250
|
continue
|
|
218
251
|
|
|
219
252
|
# Process chat message with agent; Ctrl+C interrupts the turn, not the REPL.
|
|
220
|
-
|
|
253
|
+
with QuietTerminal():
|
|
254
|
+
await self._run_interruptible(self.process_user_message(self._expand_mentions(user_input)))
|
|
221
255
|
|
|
222
256
|
except (KeyboardInterrupt, EOFError):
|
|
223
257
|
console.print("\n[dim]Exiting thwip. Goodbye![/dim]")
|
|
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
|