cc-session-control 0.7.3__tar.gz → 0.7.4__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 (65) hide show
  1. {cc_session_control-0.7.3/src/cc_session_control.egg-info → cc_session_control-0.7.4}/PKG-INFO +1 -1
  2. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/__init__.py +1 -1
  3. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/rc.py +27 -0
  4. {cc_session_control-0.7.3 → cc_session_control-0.7.4/src/cc_session_control.egg-info}/PKG-INFO +1 -1
  5. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_rc.py +4 -0
  6. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_trust.py +80 -1
  7. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/LICENSE +0 -0
  8. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/README.md +0 -0
  9. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/pyproject.toml +0 -0
  10. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/setup.cfg +0 -0
  11. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/__main__.py +0 -0
  12. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/__init__.py +0 -0
  13. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/agent_ops.py +0 -0
  14. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/resume_list.py +0 -0
  15. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/session_ops.py +0 -0
  16. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/skill_ops.py +0 -0
  17. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/app.py +0 -0
  18. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/cli.py +0 -0
  19. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/clipboard.py +0 -0
  20. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/config.py +0 -0
  21. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/__init__.py +0 -0
  22. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/cleanup.py +0 -0
  23. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/environments.py +0 -0
  24. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/liveness.py +0 -0
  25. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/proc.py +0 -0
  26. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/registry.py +0 -0
  27. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/sessions.py +0 -0
  28. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/snapshot.py +0 -0
  29. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/tmux.py +0 -0
  30. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/models.py +0 -0
  31. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/skill/SKILL.md +0 -0
  32. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/theme.py +0 -0
  33. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/__init__.py +0 -0
  34. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_base.py +0 -0
  35. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_colspec.py +0 -0
  36. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_confirm.py +0 -0
  37. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_keytable.py +0 -0
  38. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_rows.py +0 -0
  39. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_session_row.py +0 -0
  40. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_sessions_cleanup.py +0 -0
  41. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/agents.py +0 -0
  42. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/rc.py +0 -0
  43. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/sessions.py +0 -0
  44. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control.egg-info/SOURCES.txt +0 -0
  45. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control.egg-info/dependency_links.txt +0 -0
  46. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control.egg-info/entry_points.txt +0 -0
  47. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control.egg-info/requires.txt +0 -0
  48. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control.egg-info/top_level.txt +0 -0
  49. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_agent_ops.py +0 -0
  50. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_agents.py +0 -0
  51. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_app.py +0 -0
  52. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_cleanup.py +0 -0
  53. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_cli.py +0 -0
  54. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_data.py +0 -0
  55. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_environments.py +0 -0
  56. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_liveness.py +0 -0
  57. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_proc.py +0 -0
  58. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_registry.py +0 -0
  59. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_resume_list.py +0 -0
  60. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_sessions.py +0 -0
  61. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_skill_ops.py +0 -0
  62. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_smoke.py +0 -0
  63. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_snapshot.py +0 -0
  64. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_theme.py +0 -0
  65. {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_views.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cc-session-control
3
- Version: 0.7.3
3
+ Version: 0.7.4
4
4
  Summary: TUI manager for Claude Code sessions and Remote Control
5
5
  Author: dzshzx
6
6
  License-Expression: MIT
@@ -1,3 +1,3 @@
1
1
  """cc-session-control — TUI manager for Claude Code sessions and Remote Control."""
2
2
 
3
- __version__ = "0.7.3"
3
+ __version__ = "0.7.4"
@@ -14,6 +14,7 @@ import json
14
14
  import os
15
15
  import re
16
16
  import shlex
17
+ import tempfile
17
18
  import time
18
19
 
19
20
  from ..config import cfg
@@ -161,6 +162,27 @@ def _trusted_in(projects: dict) -> set[str]:
161
162
  }
162
163
 
163
164
 
165
+ # Platform temp roots — working space, never projects. Keeping a temp root
166
+ # trusted (so throwaway sessions skip the dialog) must not surface it in the
167
+ # launcher; this is a MEMBERSHIP rule, the trust state itself is untouched.
168
+ _TEMP_ROOTS = frozenset(
169
+ os.path.normpath(p) for p in (tempfile.gettempdir(), "/tmp", "/var/tmp")
170
+ )
171
+
172
+
173
+ def _is_temp_path(path: str) -> bool:
174
+ """PURE: is `path` a platform temp root, or beneath one?
175
+
176
+ Same segment-boundary matching as `models.effective_trust` (normpath
177
+ only; `/tmpfoo` is not under `/tmp`).
178
+ """
179
+ target = os.path.normpath(path)
180
+ for root in _TEMP_ROOTS:
181
+ if target == root or target.startswith(root.rstrip("/") + "/"):
182
+ return True
183
+ return False
184
+
185
+
164
186
  def trusted_projects() -> list[str]:
165
187
  """Absolute paths of every effectively-trusted claude.json project entry.
166
188
 
@@ -259,6 +281,11 @@ def scan() -> list[RCProject]:
259
281
  # projects that ARE actionable (in the autostart list, or with a
260
282
  # live/dead tmux window) stay listed.
261
283
  continue
284
+ if _is_temp_path(path) and path not in enabled and win is None:
285
+ # Temp dirs reached via trust discovery alone are dropped —
286
+ # same escape hatch as above: explicitly actionable entries
287
+ # (autostart list, existing rc window) stay listed.
288
+ continue
262
289
  if win is not None:
263
290
  status = "dead" if win.dead else "running"
264
291
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cc-session-control
3
- Version: 0.7.3
3
+ Version: 0.7.4
4
4
  Summary: TUI manager for Claude Code sessions and Remote Control
5
5
  Author: dzshzx
6
6
  License-Expression: MIT
@@ -193,6 +193,8 @@ def test_scan_populates_spawn_mode(tmp_path, monkeypatch):
193
193
  monkeypatch.setattr(rc.cfg, "claude_json", cj)
194
194
  monkeypatch.setattr(rc, "list_enabled", lambda: [])
195
195
  monkeypatch.setattr(rc, "_tmux_windows", lambda: [])
196
+ # tmp_path is under the real temp root — neutralize the membership filter.
197
+ monkeypatch.setattr(rc, "_TEMP_ROOTS", frozenset())
196
198
 
197
199
  rows = {p.name: p for p in rc.scan()}
198
200
  assert rows["proj"].spawn_mode == "new-window"
@@ -243,6 +245,8 @@ def test_scan_marks_missing_directory(tmp_path, monkeypatch):
243
245
  rc, "_tmux_windows",
244
246
  lambda: [TmuxWindow("@1", "gone-running", False, 5, gone_running)],
245
247
  )
248
+ # tmp_path is under the real temp root — neutralize the membership filter.
249
+ monkeypatch.setattr(rc, "_TEMP_ROOTS", frozenset())
246
250
 
247
251
  rows = {p.directory: p for p in rc.scan()}
248
252
  assert set(rows) == {str(alive), gone_enabled, gone_running}
@@ -92,8 +92,9 @@ def test_empty_inputs():
92
92
 
93
93
  # --- scan-level membership (path-keyed, inheritance-aware) ------------------
94
94
 
95
- def _wire_scan(tmp_path, monkeypatch, projects, enabled=()):
95
+ def _wire_scan(tmp_path, monkeypatch, projects, enabled=(), temp_roots=()):
96
96
  import json
97
+ import os
97
98
 
98
99
  from cc_session_control.data import rc
99
100
 
@@ -102,6 +103,10 @@ def _wire_scan(tmp_path, monkeypatch, projects, enabled=()):
102
103
  monkeypatch.setattr(rc.cfg, "claude_json", cj)
103
104
  monkeypatch.setattr(rc, "list_enabled", lambda: list(enabled))
104
105
  monkeypatch.setattr(rc, "_tmux_windows", lambda: [])
106
+ # pytest tmp_path lives under the REAL platform temp root, so the temp-dir
107
+ # membership filter is neutralized unless a test injects roots explicitly.
108
+ monkeypatch.setattr(rc, "_TEMP_ROOTS",
109
+ frozenset(os.path.normpath(p) for p in temp_roots))
105
110
  return rc
106
111
 
107
112
 
@@ -133,6 +138,80 @@ def test_scan_excludes_untrusted_entry(tmp_path, monkeypatch):
133
138
  assert rc.scan() == []
134
139
 
135
140
 
141
+ # --- temp-dir membership filter (trust untouched, discovery only) -----------
142
+
143
+ def test_is_temp_path_segment_boundary(monkeypatch):
144
+ from cc_session_control.data import rc
145
+
146
+ monkeypatch.setattr(rc, "_TEMP_ROOTS", frozenset({"/tmp"}))
147
+ assert rc._is_temp_path("/tmp") is True
148
+ assert rc._is_temp_path("/tmp/x/y") is True
149
+ assert rc._is_temp_path("/tmpfoo") is False
150
+ assert rc._is_temp_path("/home/u/tmp") is False
151
+
152
+
153
+ def test_scan_drops_temp_root_and_subtree(tmp_path, monkeypatch):
154
+ # The motivating shape: /tmp itself trusted (kept trusted on purpose so
155
+ # scratchpad sessions skip the dialog) must not surface as a project.
156
+ troot = tmp_path / "t"
157
+ sub = troot / "scratch"
158
+ sub.mkdir(parents=True)
159
+ rc = _wire_scan(tmp_path, monkeypatch, {
160
+ str(troot): {"hasTrustDialogAccepted": True},
161
+ str(sub): {"hasTrustDialogAccepted": False},
162
+ }, temp_roots={str(troot)})
163
+
164
+ assert rc.scan() == []
165
+ # Trust itself is NOT touched — the start gate still passes.
166
+ assert rc.is_trusted(str(sub)) is True
167
+
168
+
169
+ def test_scan_keeps_enabled_temp_project(tmp_path, monkeypatch):
170
+ troot = tmp_path / "t"
171
+ sub = troot / "demo"
172
+ sub.mkdir(parents=True)
173
+ rc = _wire_scan(tmp_path, monkeypatch, {
174
+ str(troot): {"hasTrustDialogAccepted": True},
175
+ }, enabled=(str(sub),), temp_roots={str(troot)})
176
+
177
+ rows = {p.directory for p in rc.scan()}
178
+ assert rows == {str(sub)}
179
+
180
+
181
+ def test_scan_keeps_temp_project_with_rc_window(tmp_path, monkeypatch):
182
+ from cc_session_control.data import tmux
183
+
184
+ troot = tmp_path / "t"
185
+ sub = troot / "served"
186
+ sub.mkdir(parents=True)
187
+ # A server's claude leaves the suppressed-False footprint, so the path
188
+ # IS enumerated (windows join rows, they don't create membership).
189
+ rc = _wire_scan(tmp_path, monkeypatch, {
190
+ str(troot): {"hasTrustDialogAccepted": True},
191
+ str(sub): {"hasTrustDialogAccepted": False},
192
+ }, temp_roots={str(troot)})
193
+ monkeypatch.setattr(rc, "_tmux_windows", lambda: [
194
+ tmux.TmuxWindow(wid="@1", name="served", dead=False, pid=42,
195
+ path=str(sub)),
196
+ ])
197
+
198
+ rows = {p.directory: p for p in rc.scan()}
199
+ assert set(rows) == {str(sub)}
200
+ assert rows[str(sub)].status == "running"
201
+
202
+
203
+ def test_scan_temp_root_does_not_cover_sibling(tmp_path, monkeypatch):
204
+ # Segment boundary: a "t-ext" sibling of temp root "t" stays listed.
205
+ troot = tmp_path / "t"
206
+ sibling = tmp_path / "t-ext"
207
+ sibling.mkdir(parents=True)
208
+ rc = _wire_scan(tmp_path, monkeypatch, {
209
+ str(sibling): {"hasTrustDialogAccepted": True},
210
+ }, temp_roots={str(troot)})
211
+
212
+ assert {p.directory for p in rc.scan()} == {str(sibling)}
213
+
214
+
136
215
  # --- rc-enabled migration (legacy short names → absolute paths) -------------
137
216
 
138
217
  def test_migrate_lines_resolves_against_legacy_root(monkeypatch):