cc-session-control 0.7.1__tar.gz → 0.7.3__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.1/src/cc_session_control.egg-info → cc_session_control-0.7.3}/PKG-INFO +9 -8
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/README.md +8 -7
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/__init__.py +1 -1
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/app.py +6 -23
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/cli.py +35 -32
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/config.py +2 -37
- cc_session_control-0.7.3/src/cc_session_control/data/rc.py +429 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/data/tmux.py +43 -25
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/models.py +27 -0
- cc_session_control-0.7.3/src/cc_session_control/theme.py +189 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/views/_base.py +16 -3
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/views/rc.py +15 -9
- {cc_session_control-0.7.1 → cc_session_control-0.7.3/src/cc_session_control.egg-info}/PKG-INFO +9 -8
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control.egg-info/SOURCES.txt +3 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_cli.py +14 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_data.py +58 -26
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_rc.py +72 -48
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_smoke.py +0 -7
- cc_session_control-0.7.3/tests/test_theme.py +178 -0
- cc_session_control-0.7.3/tests/test_trust.py +181 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_views.py +22 -3
- cc_session_control-0.7.1/src/cc_session_control/data/rc.py +0 -324
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/LICENSE +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/pyproject.toml +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/setup.cfg +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/__main__.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/actions/__init__.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/actions/agent_ops.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/actions/resume_list.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/actions/session_ops.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/actions/skill_ops.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/clipboard.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/data/__init__.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/data/cleanup.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/data/environments.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/data/liveness.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/data/proc.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/data/registry.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/data/sessions.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/data/snapshot.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/skill/SKILL.md +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/views/__init__.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/views/_colspec.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/views/_confirm.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/views/_keytable.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/views/_rows.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/views/_session_row.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/views/_sessions_cleanup.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/views/agents.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control/views/sessions.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control.egg-info/dependency_links.txt +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control.egg-info/entry_points.txt +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control.egg-info/requires.txt +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/src/cc_session_control.egg-info/top_level.txt +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_agent_ops.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_agents.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_app.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_cleanup.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_environments.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_liveness.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_proc.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_registry.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_resume_list.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_sessions.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_skill_ops.py +0 -0
- {cc_session_control-0.7.1 → cc_session_control-0.7.3}/tests/test_snapshot.py +0 -0
{cc_session_control-0.7.1/src/cc_session_control.egg-info → cc_session_control-0.7.3}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: cc-session-control
|
|
3
|
-
Version: 0.7.
|
|
3
|
+
Version: 0.7.3
|
|
4
4
|
Summary: TUI manager for Claude Code sessions and Remote Control
|
|
5
5
|
Author: dzshzx
|
|
6
6
|
License-Expression: MIT
|
|
@@ -66,8 +66,9 @@ uv tool install --reinstall git+https://github.com/dzshzx/cc-session-control.git
|
|
|
66
66
|
```
|
|
67
67
|
|
|
68
68
|
`csctl` manages the Claude Code state on the machine where it is installed: the
|
|
69
|
-
local `~/.claude`, local `tmux`, and
|
|
70
|
-
each machine whose sessions you want
|
|
69
|
+
local `~/.claude`, local `tmux`, and the projects recorded in the local
|
|
70
|
+
`~/.claude.json`. Install it separately on each machine whose sessions you want
|
|
71
|
+
to manage. For working *on* the code
|
|
71
72
|
instead of using it, see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
72
73
|
|
|
73
74
|
## Usage
|
|
@@ -78,9 +79,9 @@ csctl
|
|
|
78
79
|
|
|
79
80
|
# Remote Control management (no TUI)
|
|
80
81
|
csctl rc status # Show all projects and RC status
|
|
81
|
-
csctl rc add . # Add current
|
|
82
|
-
csctl rc add
|
|
83
|
-
csctl rc rm
|
|
82
|
+
csctl rc add . # Add current directory to RC list and start
|
|
83
|
+
csctl rc add ~/code/app # Add by directory path
|
|
84
|
+
csctl rc rm ~/code/app # Remove and stop
|
|
84
85
|
csctl rc up # Start all listed projects
|
|
85
86
|
csctl rc stop all # Stop all RC servers
|
|
86
87
|
csctl rc list # Show auto-start list
|
|
@@ -103,7 +104,7 @@ csctl skill install --force # Replace an existing skill directory
|
|
|
103
104
|
csctl skill uninstall
|
|
104
105
|
|
|
105
106
|
# Options
|
|
106
|
-
csctl --
|
|
107
|
+
csctl --theme light # Force the TUI palette (auto/dark/light)
|
|
107
108
|
csctl --version
|
|
108
109
|
```
|
|
109
110
|
|
|
@@ -111,9 +112,9 @@ csctl --version
|
|
|
111
112
|
|
|
112
113
|
| Environment Variable | Default | Description |
|
|
113
114
|
|---|---|---|
|
|
114
|
-
| `CSCTL_WORKSPACE` | `~/workspace` | Workspace root directory |
|
|
115
115
|
| `CSCTL_RC_SESSION` | `rc` | tmux session name for RC servers |
|
|
116
116
|
| `CSCTL_RC_STAGGER` | `2` | Seconds between starting RC servers |
|
|
117
|
+
| `CSCTL_THEME` | `auto` | TUI palette: `auto` (detect the terminal background via OSC 11 / `$COLORFGBG`) / `dark` / `light`. tmux typically doesn't answer the OSC 11 query, so inside tmux `auto` falls back to `dark` — set this (or `--theme`) explicitly for a light terminal |
|
|
117
118
|
| `XDG_CONFIG_HOME` | `~/.config` | Config directory base |
|
|
118
119
|
|
|
119
120
|
RC auto-start list is stored at `$XDG_CONFIG_HOME/csctl/rc-enabled`.
|
|
@@ -43,8 +43,9 @@ uv tool install --reinstall git+https://github.com/dzshzx/cc-session-control.git
|
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
`csctl` manages the Claude Code state on the machine where it is installed: the
|
|
46
|
-
local `~/.claude`, local `tmux`, and
|
|
47
|
-
each machine whose sessions you want
|
|
46
|
+
local `~/.claude`, local `tmux`, and the projects recorded in the local
|
|
47
|
+
`~/.claude.json`. Install it separately on each machine whose sessions you want
|
|
48
|
+
to manage. For working *on* the code
|
|
48
49
|
instead of using it, see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
49
50
|
|
|
50
51
|
## Usage
|
|
@@ -55,9 +56,9 @@ csctl
|
|
|
55
56
|
|
|
56
57
|
# Remote Control management (no TUI)
|
|
57
58
|
csctl rc status # Show all projects and RC status
|
|
58
|
-
csctl rc add . # Add current
|
|
59
|
-
csctl rc add
|
|
60
|
-
csctl rc rm
|
|
59
|
+
csctl rc add . # Add current directory to RC list and start
|
|
60
|
+
csctl rc add ~/code/app # Add by directory path
|
|
61
|
+
csctl rc rm ~/code/app # Remove and stop
|
|
61
62
|
csctl rc up # Start all listed projects
|
|
62
63
|
csctl rc stop all # Stop all RC servers
|
|
63
64
|
csctl rc list # Show auto-start list
|
|
@@ -80,7 +81,7 @@ csctl skill install --force # Replace an existing skill directory
|
|
|
80
81
|
csctl skill uninstall
|
|
81
82
|
|
|
82
83
|
# Options
|
|
83
|
-
csctl --
|
|
84
|
+
csctl --theme light # Force the TUI palette (auto/dark/light)
|
|
84
85
|
csctl --version
|
|
85
86
|
```
|
|
86
87
|
|
|
@@ -88,9 +89,9 @@ csctl --version
|
|
|
88
89
|
|
|
89
90
|
| Environment Variable | Default | Description |
|
|
90
91
|
|---|---|---|
|
|
91
|
-
| `CSCTL_WORKSPACE` | `~/workspace` | Workspace root directory |
|
|
92
92
|
| `CSCTL_RC_SESSION` | `rc` | tmux session name for RC servers |
|
|
93
93
|
| `CSCTL_RC_STAGGER` | `2` | Seconds between starting RC servers |
|
|
94
|
+
| `CSCTL_THEME` | `auto` | TUI palette: `auto` (detect the terminal background via OSC 11 / `$COLORFGBG`) / `dark` / `light`. tmux typically doesn't answer the OSC 11 query, so inside tmux `auto` falls back to `dark` — set this (or `--theme`) explicitly for a light terminal |
|
|
94
95
|
| `XDG_CONFIG_HOME` | `~/.config` | Config directory base |
|
|
95
96
|
|
|
96
97
|
RC auto-start list is stored at `$XDG_CONFIG_HOME/csctl/rc-enabled`.
|
|
@@ -10,6 +10,7 @@ from typing import TYPE_CHECKING, Protocol, runtime_checkable
|
|
|
10
10
|
|
|
11
11
|
import urwid
|
|
12
12
|
|
|
13
|
+
from . import theme
|
|
13
14
|
from .data import proc
|
|
14
15
|
from .data.snapshot import WorldSnapshot, build_world_snapshot
|
|
15
16
|
from .views.agents import AgentsView
|
|
@@ -47,28 +48,6 @@ class TabView(Protocol):
|
|
|
47
48
|
def handle_key(self, key: str) -> None: ...
|
|
48
49
|
def captures_text(self) -> bool: ... # True while a text mode owns EVERY key (incl. tab/q)
|
|
49
50
|
|
|
50
|
-
# 6-tuple: (name, fg_16, bg_16, mono, fg_256, bg_256)
|
|
51
|
-
# ONE semantic set — views reference these names only (no per-tab duplicates;
|
|
52
|
-
# the old rc_running/rc_stopped were aliases of alive/dead and are gone).
|
|
53
|
-
# 256-color foregrounds are kept ≥ 4.5:1 against their background (WCAG-AA-ish,
|
|
54
|
-
# checked via relative luminance): #aaa/#111 ≈ 8.1, #7ab/#111 ≈ 7.4,
|
|
55
|
-
# #9cc/#181818 ≈ 10.0, #bbb/#111 ≈ 9.8, #d66/#111 ≈ 5.5.
|
|
56
|
-
PALETTE = [
|
|
57
|
-
("header", "white,bold", "black", "bold", "#fff,bold", "#111"),
|
|
58
|
-
("footer", "light gray", "black", None, "#aaa", "#111"),
|
|
59
|
-
("tab_on", "white,bold", "dark cyan", "bold,standout", "#fff,bold", "#068"),
|
|
60
|
-
("tab_off", "light cyan", "black", None, "#7ab", "#111"),
|
|
61
|
-
("alive", "light green", "black", None, "#6d6", "#111"),
|
|
62
|
-
("status_busy", "light green,bold", "black", "bold", "#6d6,bold", "#111"),
|
|
63
|
-
("status_err", "light red", "black", None, "#d66", "#111"),
|
|
64
|
-
("dead", "light gray", "black", None, "#ccc", "#111"),
|
|
65
|
-
("selected", "white,bold", "dark cyan", "standout", "#fff,bold", "#068"),
|
|
66
|
-
("notify", "yellow,bold", "black", "bold", "#ff0,bold", "#111"),
|
|
67
|
-
("status", "light gray", "black", None, "#bbb", "#111"),
|
|
68
|
-
("body", "light gray", "black", None, "#ccc", "#111"),
|
|
69
|
-
("col_header", "dark cyan", "black", None, "#9cc", "#181818"),
|
|
70
|
-
]
|
|
71
|
-
|
|
72
51
|
# Launcher-first order (ADR-0001): startup lands on 项目 (the tmux-first
|
|
73
52
|
# dispatch entry), then 会话 / 后台. In lockstep with `self.views` below.
|
|
74
53
|
TAB_NAMES = ["项目", "会话", "后台"]
|
|
@@ -80,6 +59,10 @@ FOOTER_PREFIX = " Tab 切换 · q 退出 · r 刷新 · "
|
|
|
80
59
|
|
|
81
60
|
|
|
82
61
|
def _make_screen() -> urwid.raw_display.Screen:
|
|
62
|
+
# The theme query (OSC 11) runs here, before `loop.run()` puts the tty
|
|
63
|
+
# into urwid's hands; the palette itself lives in `theme.py` (ONE semantic
|
|
64
|
+
# attr set, dark/light generated from a single spec).
|
|
65
|
+
mode = theme.detect_mode()
|
|
83
66
|
screen = urwid.raw_display.Screen()
|
|
84
67
|
try:
|
|
85
68
|
curses.setupterm()
|
|
@@ -88,7 +71,7 @@ def _make_screen() -> urwid.raw_display.Screen:
|
|
|
88
71
|
screen.set_terminal_properties(colors=256)
|
|
89
72
|
except Exception:
|
|
90
73
|
pass
|
|
91
|
-
screen.register_palette(
|
|
74
|
+
screen.register_palette(theme.palette(mode))
|
|
92
75
|
return screen
|
|
93
76
|
|
|
94
77
|
|
|
@@ -5,7 +5,6 @@ from __future__ import annotations
|
|
|
5
5
|
import argparse
|
|
6
6
|
import os
|
|
7
7
|
import sys
|
|
8
|
-
from pathlib import Path
|
|
9
8
|
|
|
10
9
|
|
|
11
10
|
def _build_parser() -> argparse.ArgumentParser:
|
|
@@ -16,7 +15,10 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
16
15
|
description="TUI manager for Claude Code sessions and Remote Control",
|
|
17
16
|
)
|
|
18
17
|
parser.add_argument("--version", action="version", version=f"csctl {__version__}")
|
|
19
|
-
parser.add_argument(
|
|
18
|
+
parser.add_argument(
|
|
19
|
+
"--theme", choices=("auto", "dark", "light"),
|
|
20
|
+
help="TUI palette (default: auto-detect the terminal background; env CSCTL_THEME)",
|
|
21
|
+
)
|
|
20
22
|
|
|
21
23
|
sub = parser.add_subparsers(dest="command")
|
|
22
24
|
|
|
@@ -25,12 +27,12 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
25
27
|
rc_sub = rc_parser.add_subparsers(dest="rc_command")
|
|
26
28
|
rc_sub.add_parser("status", help="Show RC status for all projects")
|
|
27
29
|
rc_add = rc_sub.add_parser("add", help="Add project to RC list and start")
|
|
28
|
-
rc_add.add_argument("project", nargs="?", default=".", help="Project
|
|
30
|
+
rc_add.add_argument("project", nargs="?", default=".", help="Project directory (default: current dir)")
|
|
29
31
|
rc_rm = rc_sub.add_parser("rm", help="Remove project from RC list and stop")
|
|
30
|
-
rc_rm.add_argument("project", help="Project
|
|
32
|
+
rc_rm.add_argument("project", help="Project directory")
|
|
31
33
|
rc_sub.add_parser("up", help="Start all listed projects")
|
|
32
34
|
rc_stop = rc_sub.add_parser("stop", help="Stop RC for a project")
|
|
33
|
-
rc_stop.add_argument("target", help="Project
|
|
35
|
+
rc_stop.add_argument("target", help="Project directory or 'all'")
|
|
34
36
|
rc_sub.add_parser("list", help="Show enabled project list")
|
|
35
37
|
|
|
36
38
|
# prune subcommand
|
|
@@ -67,10 +69,10 @@ def _build_parser() -> argparse.ArgumentParser:
|
|
|
67
69
|
return parser
|
|
68
70
|
|
|
69
71
|
|
|
70
|
-
def
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
cfg.
|
|
72
|
+
def _apply_global_flags(args: argparse.Namespace) -> None:
|
|
73
|
+
from .config import cfg
|
|
74
|
+
if args.theme:
|
|
75
|
+
cfg.theme = args.theme
|
|
74
76
|
|
|
75
77
|
|
|
76
78
|
def _cmd_rc(args: argparse.Namespace) -> None:
|
|
@@ -83,36 +85,36 @@ def _cmd_rc(args: argparse.Namespace) -> None:
|
|
|
83
85
|
sub = args.rc_command
|
|
84
86
|
|
|
85
87
|
if sub == "status":
|
|
86
|
-
|
|
88
|
+
from .data.sessions import scan as scan_sessions
|
|
89
|
+
|
|
90
|
+
# Same ordering as the 项目 tab (rc.order_by_activity — single
|
|
91
|
+
# source); costs one transcript scan, like `csctl resume`.
|
|
92
|
+
projects = rc.order_by_activity(rc.scan(), scan_sessions())
|
|
87
93
|
for p in projects:
|
|
88
94
|
icon = {"running": "[running]", "dead": "[dead ]", "stopped": "[stopped]"}.get(p.status, p.status)
|
|
89
95
|
auto = "auto" if p.auto_start else " "
|
|
90
96
|
missing = "" if p.dir_exists else " (directory missing)"
|
|
91
|
-
print(f" {icon} {auto} {p.name}{missing}")
|
|
97
|
+
print(f" {icon} {auto} {p.name} {p.directory}{missing}")
|
|
92
98
|
|
|
93
99
|
elif sub == "add":
|
|
94
|
-
|
|
95
|
-
if
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
else:
|
|
101
|
-
print(f"Current directory is not under {ws}. Specify project name explicitly.")
|
|
102
|
-
sys.exit(1)
|
|
103
|
-
if not rc.is_trusted(proj):
|
|
104
|
-
print(f"Not trusted: {proj} — run 'claude' in that directory first to accept the trust dialog")
|
|
100
|
+
path = os.path.abspath(args.project)
|
|
101
|
+
if not os.path.isdir(path):
|
|
102
|
+
print(f"No such directory: {path}")
|
|
103
|
+
sys.exit(1)
|
|
104
|
+
if not rc.is_trusted(path):
|
|
105
|
+
print(f"Not trusted: {path} — run 'claude' in that directory first to accept the trust dialog")
|
|
105
106
|
sys.exit(1)
|
|
106
|
-
rc.list_add(
|
|
107
|
-
print(f"Added to list: {
|
|
108
|
-
ok = rc.start_one(
|
|
107
|
+
rc.list_add(path)
|
|
108
|
+
print(f"Added to list: {path}")
|
|
109
|
+
ok = rc.start_one(path)
|
|
109
110
|
if ok:
|
|
110
|
-
print(f"Started
|
|
111
|
+
print(f"Started RC server for {path}")
|
|
111
112
|
|
|
112
113
|
elif sub == "rm":
|
|
113
|
-
|
|
114
|
-
rc.
|
|
115
|
-
|
|
114
|
+
path = os.path.abspath(args.project)
|
|
115
|
+
rc.list_rm(path)
|
|
116
|
+
rc.stop_one(path)
|
|
117
|
+
print(f"Removed and stopped: {path}")
|
|
116
118
|
|
|
117
119
|
elif sub == "up":
|
|
118
120
|
enabled = rc.list_enabled()
|
|
@@ -127,8 +129,9 @@ def _cmd_rc(args: argparse.Namespace) -> None:
|
|
|
127
129
|
rc.stop_all()
|
|
128
130
|
print("Stopped all")
|
|
129
131
|
else:
|
|
130
|
-
|
|
131
|
-
|
|
132
|
+
path = os.path.abspath(args.target)
|
|
133
|
+
ok = rc.stop_one(path)
|
|
134
|
+
print(f"Stopped {path}" if ok else f"Not running: {path}")
|
|
132
135
|
|
|
133
136
|
elif sub == "list":
|
|
134
137
|
for name in rc.list_enabled():
|
|
@@ -311,7 +314,7 @@ def _cmd_tui(args: argparse.Namespace) -> None:
|
|
|
311
314
|
def main() -> None:
|
|
312
315
|
parser = _build_parser()
|
|
313
316
|
args = parser.parse_args()
|
|
314
|
-
|
|
317
|
+
_apply_global_flags(args)
|
|
315
318
|
|
|
316
319
|
if args.command == "rc":
|
|
317
320
|
_cmd_rc(args)
|
|
@@ -2,7 +2,6 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
-
import json
|
|
6
5
|
import os
|
|
7
6
|
from pathlib import Path
|
|
8
7
|
|
|
@@ -18,18 +17,8 @@ class Config:
|
|
|
18
17
|
self.rc_stagger: int = int(os.environ.get("CSCTL_RC_STAGGER", "2"))
|
|
19
18
|
# Age threshold (days) for the time/global-keyed cleanup strategy.
|
|
20
19
|
self.cleanup_age_days: int = int(os.environ.get("CSCTL_CLEANUP_AGE_DAYS", "14"))
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
@property
|
|
24
|
-
def workspace(self) -> Path:
|
|
25
|
-
if self._workspace is not None:
|
|
26
|
-
return self._workspace
|
|
27
|
-
self._workspace = _detect_workspace(self.claude_json)
|
|
28
|
-
return self._workspace
|
|
29
|
-
|
|
30
|
-
@workspace.setter
|
|
31
|
-
def workspace(self, value: Path) -> None:
|
|
32
|
-
self._workspace = value
|
|
20
|
+
# TUI palette: "auto" (detect the terminal background) | "dark" | "light".
|
|
21
|
+
self.theme: str = os.environ.get("CSCTL_THEME", "auto")
|
|
33
22
|
|
|
34
23
|
@property
|
|
35
24
|
def projects_root(self) -> Path:
|
|
@@ -106,28 +95,4 @@ class Config:
|
|
|
106
95
|
return self.claude_home / "skills"
|
|
107
96
|
|
|
108
97
|
|
|
109
|
-
def _detect_workspace(claude_json: Path) -> Path:
|
|
110
|
-
env = os.environ.get("CSCTL_WORKSPACE")
|
|
111
|
-
if env:
|
|
112
|
-
return Path(env)
|
|
113
|
-
|
|
114
|
-
default = Path.home() / "workspace"
|
|
115
|
-
if default.is_dir():
|
|
116
|
-
return default
|
|
117
|
-
|
|
118
|
-
try:
|
|
119
|
-
with open(claude_json) as f:
|
|
120
|
-
data = json.load(f)
|
|
121
|
-
dirs = [k for k in data.get("projects", {}) if "/" in k]
|
|
122
|
-
if dirs:
|
|
123
|
-
from os.path import commonpath
|
|
124
|
-
common = Path(commonpath(dirs))
|
|
125
|
-
if common.is_dir() and common != Path.home():
|
|
126
|
-
return common
|
|
127
|
-
except Exception:
|
|
128
|
-
pass
|
|
129
|
-
|
|
130
|
-
return Path.cwd()
|
|
131
|
-
|
|
132
|
-
|
|
133
98
|
cfg = Config()
|