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.
- {cc_session_control-0.7.3/src/cc_session_control.egg-info → cc_session_control-0.7.4}/PKG-INFO +1 -1
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/__init__.py +1 -1
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/rc.py +27 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4/src/cc_session_control.egg-info}/PKG-INFO +1 -1
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_rc.py +4 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_trust.py +80 -1
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/LICENSE +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/README.md +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/pyproject.toml +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/setup.cfg +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/__main__.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/__init__.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/agent_ops.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/resume_list.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/session_ops.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/skill_ops.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/app.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/cli.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/clipboard.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/config.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/__init__.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/cleanup.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/environments.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/liveness.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/proc.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/registry.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/sessions.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/snapshot.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/tmux.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/models.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/skill/SKILL.md +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/theme.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/__init__.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_base.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_colspec.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_confirm.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_keytable.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_rows.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_session_row.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_sessions_cleanup.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/agents.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/rc.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/sessions.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control.egg-info/SOURCES.txt +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control.egg-info/dependency_links.txt +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control.egg-info/entry_points.txt +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control.egg-info/requires.txt +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control.egg-info/top_level.txt +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_agent_ops.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_agents.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_app.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_cleanup.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_cli.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_data.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_environments.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_liveness.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_proc.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_registry.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_resume_list.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_sessions.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_skill_ops.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_smoke.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_snapshot.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_theme.py +0 -0
- {cc_session_control-0.7.3 → cc_session_control-0.7.4}/tests/test_views.py +0 -0
|
@@ -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:
|
|
@@ -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):
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/__init__.py
RENAMED
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/agent_ops.py
RENAMED
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/resume_list.py
RENAMED
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/session_ops.py
RENAMED
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/actions/skill_ops.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/__init__.py
RENAMED
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/cleanup.py
RENAMED
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/environments.py
RENAMED
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/liveness.py
RENAMED
|
File without changes
|
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/registry.py
RENAMED
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/sessions.py
RENAMED
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/data/snapshot.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_colspec.py
RENAMED
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_confirm.py
RENAMED
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_keytable.py
RENAMED
|
File without changes
|
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/_session_row.py
RENAMED
|
File without changes
|
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/agents.py
RENAMED
|
File without changes
|
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control/views/sessions.py
RENAMED
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control.egg-info/requires.txt
RENAMED
|
File without changes
|
{cc_session_control-0.7.3 → cc_session_control-0.7.4}/src/cc_session_control.egg-info/top_level.txt
RENAMED
|
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
|