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.
Files changed (62) hide show
  1. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/PKG-INFO +1 -1
  2. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/docs/verification.md +2 -1
  3. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/pyproject.toml +1 -1
  4. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/__init__.py +1 -1
  5. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/cli.py +35 -1
  6. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/uv.lock +1 -1
  7. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/.github/workflows/publish.yml +0 -0
  8. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/.gitignore +0 -0
  9. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/LICENSE +0 -0
  10. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/README.md +0 -0
  11. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/docs/handoff-research.md +0 -0
  12. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/install.sh +0 -0
  13. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/__init__.py +0 -0
  14. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/fake_providers.py +0 -0
  15. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_agents.py +0 -0
  16. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_audit_regressions.py +0 -0
  17. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_catalog.py +0 -0
  18. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_cli.py +0 -0
  19. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_compatible_streaming.py +0 -0
  20. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_config.py +0 -0
  21. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_detector.py +0 -0
  22. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_direct_providers_e2e.py +0 -0
  23. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_handoff.py +0 -0
  24. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_native_agents.py +0 -0
  25. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_native_launcher.py +0 -0
  26. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_native_print.py +0 -0
  27. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_native_rpc.py +0 -0
  28. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_parity_commands.py +0 -0
  29. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_repair_verification.py +0 -0
  30. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_session.py +0 -0
  31. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_tools.py +0 -0
  32. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/tests/test_utils.py +0 -0
  33. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/__main__.py +0 -0
  34. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/__init__.py +0 -0
  35. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/base.py +0 -0
  36. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/catalog.py +0 -0
  37. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/chat_messages.py +0 -0
  38. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/claude_agent.py +0 -0
  39. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/deepseek_agent.py +0 -0
  40. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/google_agent.py +0 -0
  41. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/groq_agent.py +0 -0
  42. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/native_agent.py +0 -0
  43. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/native_common.py +0 -0
  44. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/native_print.py +0 -0
  45. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/native_rpc.py +0 -0
  46. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/ollama_agent.py +0 -0
  47. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/openai_agent.py +0 -0
  48. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/agents/openrouter_agent.py +0 -0
  49. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/config.py +0 -0
  50. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/detector.py +0 -0
  51. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/endpoints.py +0 -0
  52. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/handoff.py +0 -0
  53. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/limits.py +0 -0
  54. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/session.py +0 -0
  55. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/shortcuts.py +0 -0
  56. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/theme.py +0 -0
  57. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/tools/__init__.py +0 -0
  58. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/tools/code_runner.py +0 -0
  59. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/tools/file_editor.py +0 -0
  60. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/tools/git_ops.py +0 -0
  61. {thwip_cli-1.6.1 → thwip_cli-1.6.2}/thwip/tools/terminal.py +0 -0
  62. {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.1
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.1. The Python suite
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 |
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "thwip-cli"
7
- version = "1.6.1"
7
+ version = "1.6.2"
8
8
  description = "Universal coding agent multiplexer: detect, switch, and route between AI coding agents seamlessly"
9
9
  readme = "README.md"
10
10
  license = "MIT"
@@ -1,4 +1,4 @@
1
1
  """thwip: Universal Coding Agent Multiplexer."""
2
2
 
3
- __version__ = "1.6.1"
3
+ __version__ = "1.6.2"
4
4
  __app_name__ = "thwip"
@@ -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
- await self._run_interruptible(self.process_user_message(self._expand_mentions(user_input)))
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]")
@@ -1022,7 +1022,7 @@ wheels = [
1022
1022
 
1023
1023
  [[package]]
1024
1024
  name = "thwip-cli"
1025
- version = "1.6.1"
1025
+ version = "1.6.2"
1026
1026
  source = { editable = "." }
1027
1027
  dependencies = [
1028
1028
  { name = "anthropic" },
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