copper-pilot-cli 0.1.1__tar.gz → 0.1.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 (82) hide show
  1. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/HISTORY.md +11 -0
  2. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/PKG-INFO +9 -23
  3. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/README.md +1 -0
  4. copper_pilot_cli-0.1.2/VERSION +1 -0
  5. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/pyproject.toml +10 -1
  6. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_app.py +1 -1
  7. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_widgets.py +3 -1
  8. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_release_gates.py +17 -0
  9. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_tui.py +51 -20
  10. copper_pilot_cli-0.1.1/VERSION +0 -1
  11. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/.gitignore +0 -0
  12. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/CODE_OF_CONDUCT.md +0 -0
  13. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/CONTRIBUTING.md +0 -0
  14. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/LICENSE +0 -0
  15. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/NOTICE +0 -0
  16. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/SECURITY.md +0 -0
  17. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/SUPPORT.md +0 -0
  18. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/UPSTREAM.md +0 -0
  19. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/docs/assets/esp32-mini-1-h4.gif +0 -0
  20. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/docs/assets/logo-dark.png +0 -0
  21. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/docs/assets/logo-light.png +0 -0
  22. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/docs/assets/logo.png +0 -0
  23. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/docs/cli.md +0 -0
  24. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/docs/development.md +0 -0
  25. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/examples/001_esp32/.copperpilot/PLANS.md +0 -0
  26. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/examples/001_esp32/.copperpilot/README.md +0 -0
  27. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/examples/001_esp32/.copperpilot/TOOLS.md +0 -0
  28. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/examples/001_esp32/.copperpilot/plans/esp32-dev-board.plan.md +0 -0
  29. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/examples/001_esp32/.gitignore +0 -0
  30. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/examples/001_esp32/README.md +0 -0
  31. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/examples/001_esp32/esp32/3d-models/SW-SMD_L3.9-W2.9-H2.0-LS4.8.step +0 -0
  32. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/examples/001_esp32/esp32/3d-models/TYPE-C-31-M-12.step +0 -0
  33. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/examples/001_esp32/esp32/esp32.kicad_pcb +0 -0
  34. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/examples/001_esp32/esp32/esp32.kicad_pro +0 -0
  35. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/examples/001_esp32/esp32/esp32.kicad_sch +0 -0
  36. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/examples/001_esp32/esp32/esp32.png +0 -0
  37. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/examples/001_esp32/helper.py +0 -0
  38. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/examples/001_esp32/main.py +0 -0
  39. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/__init__.py +0 -0
  40. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/__main__.py +0 -0
  41. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/_upstream/__init__.py +0 -0
  42. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/_upstream/dcode_0_1_69/PROVENANCE.json +0 -0
  43. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/_upstream/dcode_0_1_69/__init__.py +0 -0
  44. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/_upstream/dcode_0_1_69/diff_utils.py +0 -0
  45. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/_version.py +0 -0
  46. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/clipboard.py +0 -0
  47. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_api.py +0 -0
  48. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_auth.py +0 -0
  49. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_config.py +0 -0
  50. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_features.py +0 -0
  51. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_graph.py +0 -0
  52. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_hooks.py +0 -0
  53. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_main.py +0 -0
  54. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_preferences.py +0 -0
  55. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_presentation.py +0 -0
  56. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_protocol.py +0 -0
  57. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_theme.py +0 -0
  58. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_tools.py +0 -0
  59. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_update.py +0 -0
  60. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/copper_workspace.py +0 -0
  61. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/deepagents_tools.py +0 -0
  62. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/diagnostics.py +0 -0
  63. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/langchain.py +0 -0
  64. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/media_utils.py +0 -0
  65. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/py.typed +0 -0
  66. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/sessions.py +0 -0
  67. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/src/copper_pilot_cli/textual_patches.py +0 -0
  68. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_auth.py +0 -0
  69. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_clipboard.py +0 -0
  70. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_e2e.py +0 -0
  71. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_esp32_example.py +0 -0
  72. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_features.py +0 -0
  73. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_langchain.py +0 -0
  74. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_live_chat_e2e.py +0 -0
  75. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_main.py +0 -0
  76. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_preferences.py +0 -0
  77. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_presentation.py +0 -0
  78. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_protocol.py +0 -0
  79. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_sessions.py +0 -0
  80. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_tools.py +0 -0
  81. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_upstream_provenance.py +0 -0
  82. {copper_pilot_cli-0.1.1 → copper_pilot_cli-0.1.2}/tests/test_workspace.py +0 -0
@@ -2,6 +2,17 @@ Changelog
2
2
  =========
3
3
 
4
4
 
5
+ (unreleased)
6
+ ------------
7
+ - Ci(pyproject.toml) [AdityaNG]
8
+ - Fix(tests/test_tui.py) [AdityaNG]
9
+
10
+
11
+ 0.1.1 (2026-09-17)
12
+ ------------------
13
+ - Release: version 0.1.1 🚀 [AdityaNG]
14
+
15
+
5
16
  0.1.0 (2026-09-17)
6
17
  ------------------
7
18
  - Release: version 0.1.0 🚀 [AdityaNG]
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: copper-pilot-cli
3
- Version: 0.1.1
3
+ Version: 0.1.2
4
4
  Summary: CopperPilot hosted agent for the terminal and LangChain
5
5
  Project-URL: Homepage, https://copperpilot.ai
6
6
  Project-URL: Repository, https://github.com/CopperPilot/copper-pilot-cli
@@ -8,30 +8,15 @@ Project-URL: Issues, https://github.com/CopperPilot/copper-pilot-cli/issues
8
8
  Project-URL: Documentation, https://github.com/CopperPilot/copper-pilot-cli#readme
9
9
  Project-URL: Changelog, https://github.com/CopperPilot/copper-pilot-cli/releases
10
10
  Author: CopperPilot
11
- License: MIT License
12
-
13
- Copyright (c) 2026 CopperPilot, Inc.
14
- Copyright (c) LangChain, Inc.
15
-
16
- Permission is hereby granted, free of charge, to any person obtaining a copy
17
- of this software and associated documentation files (the "Software"), to deal
18
- in the Software without restriction, including without limitation the rights
19
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
20
- copies of the Software, and to permit persons to whom the Software is
21
- furnished to do so, subject to the following conditions:
22
-
23
- The above copyright notice and this permission notice shall be included in all
24
- copies or substantial portions of the Software.
25
-
26
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
27
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
28
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
29
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
30
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
31
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
32
- SOFTWARE.
11
+ License-Expression: MIT
33
12
  License-File: LICENSE
34
13
  License-File: NOTICE
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Programming Language :: Python :: 3
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Programming Language :: Python :: 3.14
35
20
  Requires-Python: <4,>=3.12
36
21
  Requires-Dist: aiosqlite<1,>=0.22
37
22
  Requires-Dist: certifi>=2024.8.30
@@ -80,6 +65,7 @@ Description-Content-Type: text/markdown
80
65
  <a href="https://github.com/CopperPilot/copper-pilot-cli/blob/main/LICENSE"><img src="https://img.shields.io/pypi/l/copper-pilot-cli" alt="License"></a>
81
66
  <a href="https://pypi.org/project/copper-pilot-cli/"><img src="https://img.shields.io/pypi/v/copper-pilot-cli" alt="PyPI version"></a>
82
67
  <a href="https://pypi.org/project/copper-pilot-cli/"><img src="https://img.shields.io/pypi/pyversions/copper-pilot-cli" alt="Python"></a>
68
+ <a href="https://pepy.tech/projects/copper-pilot-cli"><img src="https://img.shields.io/pepy/dt/copper-pilot-cli" alt="Downloads"></a>
83
69
  <a href="https://github.com/CopperPilot/copper-pilot-cli/actions/workflows/ci.yml"><img src="https://github.com/CopperPilot/copper-pilot-cli/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
84
70
  </div>
85
71
 
@@ -16,6 +16,7 @@
16
16
  <a href="https://github.com/CopperPilot/copper-pilot-cli/blob/main/LICENSE"><img src="https://img.shields.io/pypi/l/copper-pilot-cli" alt="License"></a>
17
17
  <a href="https://pypi.org/project/copper-pilot-cli/"><img src="https://img.shields.io/pypi/v/copper-pilot-cli" alt="PyPI version"></a>
18
18
  <a href="https://pypi.org/project/copper-pilot-cli/"><img src="https://img.shields.io/pypi/pyversions/copper-pilot-cli" alt="Python"></a>
19
+ <a href="https://pepy.tech/projects/copper-pilot-cli"><img src="https://img.shields.io/pepy/dt/copper-pilot-cli" alt="Downloads"></a>
19
20
  <a href="https://github.com/CopperPilot/copper-pilot-cli/actions/workflows/ci.yml"><img src="https://github.com/CopperPilot/copper-pilot-cli/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
20
21
  </div>
21
22
 
@@ -0,0 +1 @@
1
+ 0.1.2
@@ -7,9 +7,18 @@ name = "copper-pilot-cli"
7
7
  dynamic = ["version"]
8
8
  description = "CopperPilot hosted agent for the terminal and LangChain"
9
9
  readme = "README.md"
10
- license = { file = "LICENSE" }
10
+ license = "MIT"
11
+ license-files = ["LICENSE", "NOTICE"]
11
12
  requires-python = ">=3.12,<4"
12
13
  authors = [{ name = "CopperPilot" }]
14
+ classifiers = [
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Programming Language :: Python :: 3 :: Only",
18
+ "Programming Language :: Python :: 3.12",
19
+ "Programming Language :: Python :: 3.13",
20
+ "Programming Language :: Python :: 3.14",
21
+ ]
13
22
  dependencies = [
14
23
  "aiosqlite>=0.22,<1",
15
24
  "certifi>=2024.8.30",
@@ -1004,7 +1004,7 @@ class CopperPilotApp(App[None]):
1004
1004
  finally:
1005
1005
  if self._loading is not None:
1006
1006
  self._loading.resume()
1007
- if composer.is_mounted:
1007
+ if composer.is_mounted and composer.query(TextArea):
1008
1008
  composer.focus()
1009
1009
  if not isinstance(decision, ApprovalDecision):
1010
1010
  if tool:
@@ -183,7 +183,9 @@ class ChatInput(Vertical):
183
183
  yield OptionList(id="completions")
184
184
 
185
185
  def focus(self, scroll_visible: bool = True) -> ChatInput:
186
- self.query_one(TextArea).focus(scroll_visible)
186
+ text_area = self.query(TextArea)
187
+ if text_area:
188
+ text_area.first().focus(scroll_visible)
187
189
  return self
188
190
 
189
191
  def update_slash_commands(self, commands: Sequence[Any]) -> None:
@@ -159,3 +159,20 @@ def test_project_urls_include_community_links() -> None:
159
159
  urls = tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8"))["project"]["urls"]
160
160
  for key in ("Homepage", "Repository", "Issues", "Documentation", "Changelog"):
161
161
  assert key in urls, key
162
+
163
+
164
+ def test_pypi_badge_metadata_is_declared() -> None:
165
+ project = tomllib.loads((ROOT / "pyproject.toml").read_text(encoding="utf-8"))["project"]
166
+ assert project.get("license") == "MIT"
167
+ assert "LICENSE" in project.get("license-files", [])
168
+ classifiers = project.get("classifiers", [])
169
+ assert "License :: OSI Approved :: MIT License" in classifiers
170
+ for version in ("3.12", "3.13", "3.14"):
171
+ assert f"Programming Language :: Python :: {version}" in classifiers
172
+
173
+
174
+ def test_readme_header_includes_download_badge() -> None:
175
+ readme = (ROOT / "README.md").read_text(encoding="utf-8")
176
+ assert "img.shields.io/pypi/l/copper-pilot-cli" in readme
177
+ assert "img.shields.io/pypi/pyversions/copper-pilot-cli" in readme
178
+ assert "img.shields.io/pepy/dt/copper-pilot-cli" in readme
@@ -63,6 +63,20 @@ class FakeRuntime:
63
63
  yield (), "custom", {"type": "copper.text", "data": "chat response"}
64
64
 
65
65
 
66
+ async def _wait_until(pilot, predicate, *, seconds: float = 2) -> None:
67
+ """Advance the event loop until `predicate()` is true.
68
+
69
+ Avoid `pilot.pause()` here: its `_wait_for_screen()` can deadlock with
70
+ cancelled-turn widget teardown (`AwaitRemove` / prune).
71
+ """
72
+ async with asyncio.timeout(seconds):
73
+ while not predicate():
74
+ await asyncio.sleep(0.02)
75
+ screen = getattr(pilot.app, "screen", None)
76
+ if screen is not None:
77
+ screen._on_timer_update()
78
+
79
+
66
80
  @pytest.mark.asyncio
67
81
  async def test_tui_mounts_branded_composer_and_status(tmp_path) -> None:
68
82
  app = CopperPilotApp(
@@ -359,12 +373,15 @@ async def test_cancel_during_active_turn_does_not_quit_or_crash(tmp_path) -> Non
359
373
  app = CopperPilotApp(runtime, LocalToolBroker(tmp_path), tmp_path, "thread")
360
374
  async with app.run_test(size=(100, 30)) as pilot:
361
375
  await app._submit("long task")
362
- await pilot.pause()
363
- assert app._turn_task is not None
376
+ await _wait_until(
377
+ pilot,
378
+ lambda: (
379
+ app._turn_task is not None and app._loading is not None and app._loading.is_mounted
380
+ ),
381
+ )
364
382
  await app.action_cancel_or_quit()
365
- await pilot.pause()
383
+ await _wait_until(pilot, lambda: app._turn_task is None)
366
384
  assert runtime.cancelled is True
367
- assert app._turn_task is None
368
385
 
369
386
 
370
387
  @pytest.mark.asyncio
@@ -391,10 +408,12 @@ async def test_cancel_settles_active_tool_and_removes_loading(tmp_path) -> None:
391
408
  app = CopperPilotApp(runtime, LocalToolBroker(tmp_path), tmp_path, "thread")
392
409
  async with app.run_test(size=(100, 30)) as pilot:
393
410
  await app._submit("long task")
394
- await pilot.pause()
395
- assert list(app.query(LoadingWidget))
411
+ await _wait_until(
412
+ pilot,
413
+ lambda: app._loading is not None and app._loading.is_mounted,
414
+ )
396
415
  await app.action_cancel_or_quit()
397
- await pilot.pause()
416
+ await _wait_until(pilot, lambda: app._turn_task is None)
398
417
  assert app._tools["read-1"].status == "error"
399
418
  assert not list(app.query(LoadingWidget))
400
419
 
@@ -418,11 +437,15 @@ async def test_cancel_still_cleans_up_when_transport_cancel_fails(tmp_path) -> N
418
437
  app = CopperPilotApp(runtime, LocalToolBroker(tmp_path), tmp_path, "thread")
419
438
  async with app.run_test(size=(100, 30)) as pilot:
420
439
  await app._submit("long task")
421
- await pilot.pause()
440
+ await _wait_until(
441
+ pilot,
442
+ lambda: (
443
+ app._turn_task is not None and app._loading is not None and app._loading.is_mounted
444
+ ),
445
+ )
422
446
  await app.action_cancel_or_quit()
423
- await pilot.pause()
447
+ await _wait_until(pilot, lambda: app._turn_task is None)
424
448
  assert runtime.cancelled is True
425
- assert app._turn_task is None
426
449
 
427
450
 
428
451
  @pytest.mark.asyncio
@@ -437,9 +460,7 @@ async def test_hosted_error_event_is_rendered_only_once(tmp_path) -> None:
437
460
  app = CopperPilotApp(runtime, LocalToolBroker(tmp_path), tmp_path, "thread")
438
461
  async with app.run_test(size=(100, 30)) as pilot:
439
462
  await app._submit("fail")
440
- async with asyncio.timeout(2):
441
- while app._turn_task is not None:
442
- await pilot.pause()
463
+ await _wait_until(pilot, lambda: app._turn_task is None)
443
464
  assert len(list(app.query(ErrorMessage))) == 1
444
465
 
445
466
 
@@ -626,7 +647,10 @@ async def test_tool_approval_uses_dcode_style_preview_and_resumes_row(tmp_path)
626
647
  }
627
648
  )
628
649
  decision = asyncio.create_task(app.request_approval(request))
629
- await pilot.pause()
650
+ await _wait_until(
651
+ pilot,
652
+ lambda: any(menu.has_focus for menu in app.query(ApprovalMenu)),
653
+ )
630
654
  menu = app.query_one(ApprovalMenu)
631
655
  assert "copper" in str(menu.query_one(".approval-preview Static").render())
632
656
  await pilot.press("y")
@@ -656,7 +680,10 @@ async def test_inline_approval_tab_collects_rejection_feedback(tmp_path) -> None
656
680
  }
657
681
  )
658
682
  decision = asyncio.create_task(app.request_approval(request))
659
- await pilot.pause()
683
+ await _wait_until(
684
+ pilot,
685
+ lambda: any(menu.has_focus for menu in app.query(ApprovalMenu)),
686
+ )
660
687
  await pilot.press("tab")
661
688
  reason = app.query_one(ApprovalMenu).query_one(Input)
662
689
  assert reason.display is True
@@ -689,13 +716,17 @@ async def test_cancelling_auto_notice_keeps_current_approval_pending(tmp_path, m
689
716
  }
690
717
  )
691
718
  decision = asyncio.create_task(app.request_approval(request))
692
- await pilot.pause()
719
+ await _wait_until(
720
+ pilot,
721
+ lambda: any(menu.has_focus for menu in app.query(ApprovalMenu)),
722
+ )
693
723
  await pilot.press("a")
694
- await pilot.pause()
695
- assert isinstance(app.screen, ChoiceScreen)
724
+ await _wait_until(pilot, lambda: isinstance(app.screen, ChoiceScreen))
696
725
  app.screen.dismiss("cancel")
697
- await pilot.pause()
698
- assert list(app.query(ApprovalMenu))
726
+ await _wait_until(
727
+ pilot,
728
+ lambda: any(menu.has_focus for menu in app.query(ApprovalMenu)),
729
+ )
699
730
  assert decision.done() is False
700
731
  await pilot.press("y")
701
732
  assert await decision is True
@@ -1 +0,0 @@
1
- 0.1.1