ccmd 2.0.3__tar.gz → 2.0.5__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.
- {ccmd-2.0.3/ccmd.egg-info → ccmd-2.0.5}/PKG-INFO +1 -1
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/__init__.py +1 -1
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/tui/screens/browser.py +14 -14
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/tui/screens/hub.py +32 -29
- {ccmd-2.0.3 → ccmd-2.0.5/ccmd.egg-info}/PKG-INFO +1 -1
- {ccmd-2.0.3 → ccmd-2.0.5}/pyproject.toml +1 -1
- {ccmd-2.0.3 → ccmd-2.0.5}/LICENSE +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/MANIFEST.in +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/README.md +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/__main__.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/cli.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/config/__init__.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/config/paths.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/config/schema.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/__init__.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/agents/__init__.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/agents/aider.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/agents/base.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/agents/chatgpt.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/agents/claude.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/agents/codex.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/agents/cursor.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/agents/goose.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/agents/grok.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/fs.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/launcher.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/migrate.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/profiles.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/projects.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/scan.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/sessions.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/core/ssh.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/mcp/__init__.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/mcp/server.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/tui/__init__.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/tui/app.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/tui/ascii.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/tui/screens/__init__.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/tui/screens/agents.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/tui/screens/aliases.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/tui/screens/project_edit.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/tui/screens/sessions.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/tui/screens/ssh.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/tui/theme.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/tui/widgets/__init__.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd/tui/widgets/spinner.py +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd.egg-info/SOURCES.txt +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd.egg-info/dependency_links.txt +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd.egg-info/entry_points.txt +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd.egg-info/requires.txt +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/ccmd.egg-info/top_level.txt +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/commands.yaml +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/requirements.txt +0 -0
- {ccmd-2.0.3 → ccmd-2.0.5}/setup.cfg +0 -0
|
@@ -69,8 +69,8 @@ class DirBrowserScreen(Screen[bool | None]):
|
|
|
69
69
|
self._agent = load_config().default_agent or "claude"
|
|
70
70
|
|
|
71
71
|
def compose(self) -> ComposeResult:
|
|
72
|
-
yield Static("directory browser", id="subtitle")
|
|
73
|
-
yield Static("", id="path-bar")
|
|
72
|
+
yield Static("directory browser", id="subtitle", markup=False)
|
|
73
|
+
yield Static("", id="path-bar", markup=False)
|
|
74
74
|
yield Input(placeholder="filter this folder…", id="filter")
|
|
75
75
|
with Horizontal(id="main"):
|
|
76
76
|
with Vertical(id="projects-panel"):
|
|
@@ -78,10 +78,11 @@ class DirBrowserScreen(Screen[bool | None]):
|
|
|
78
78
|
yield OptionList(id="dir-list")
|
|
79
79
|
with Vertical(id="detail-panel"):
|
|
80
80
|
yield Static("preview", classes="panel-title")
|
|
81
|
-
yield Static("", id="preview")
|
|
81
|
+
yield Static("", id="preview", markup=False)
|
|
82
82
|
yield Static(
|
|
83
|
-
"
|
|
83
|
+
"enter open · backspace up · s add · ]/[ agent · 1-7 agent · r roots · g path · esc",
|
|
84
84
|
id="footer-bar",
|
|
85
|
+
markup=False,
|
|
85
86
|
)
|
|
86
87
|
yield Footer()
|
|
87
88
|
|
|
@@ -98,9 +99,8 @@ class DirBrowserScreen(Screen[bool | None]):
|
|
|
98
99
|
ag = get_agent(self._agent)
|
|
99
100
|
ag_s = f"{ag.ascii_logo} {self._agent}" if ag else self._agent
|
|
100
101
|
bar.update(
|
|
101
|
-
f"
|
|
102
|
-
f"
|
|
103
|
-
+ (f" [#6a6a80]{extra}[/]" if extra else "")
|
|
102
|
+
f"{self._cwd}{mark}{hidden} · agent {ag_s}"
|
|
103
|
+
+ (f" {extra}" if extra else "")
|
|
104
104
|
)
|
|
105
105
|
|
|
106
106
|
def _render_list(self) -> None:
|
|
@@ -142,7 +142,7 @@ class DirBrowserScreen(Screen[bool | None]):
|
|
|
142
142
|
def _render_roots(self) -> None:
|
|
143
143
|
self._mode = "roots"
|
|
144
144
|
self.query_one("#path-bar", Static).update(
|
|
145
|
-
"
|
|
145
|
+
"browse roots · pick a starting point"
|
|
146
146
|
)
|
|
147
147
|
self.query_one("#list-title", Static).update("roots")
|
|
148
148
|
ol = self.query_one("#dir-list", OptionList)
|
|
@@ -184,21 +184,21 @@ class DirBrowserScreen(Screen[bool | None]):
|
|
|
184
184
|
kids = list_dir_entries(resolved, show_hidden=self._show_hidden, dirs_only=True)
|
|
185
185
|
project = is_projectish(resolved)
|
|
186
186
|
lines = [
|
|
187
|
-
|
|
187
|
+
str(resolved.name or resolved),
|
|
188
188
|
f"path {resolved}",
|
|
189
|
-
f"project {'yes
|
|
189
|
+
f"project {'yes' if project else 'no (still selectable)'}",
|
|
190
190
|
f"subdirs {len(kids)}",
|
|
191
191
|
"",
|
|
192
|
-
"
|
|
192
|
+
"children",
|
|
193
193
|
]
|
|
194
194
|
for name, _, _ in kids[:12]:
|
|
195
|
-
lines.append(f"
|
|
195
|
+
lines.append(f" - {name}/")
|
|
196
196
|
if len(kids) > 12:
|
|
197
|
-
lines.append(f"
|
|
197
|
+
lines.append(f" ... +{len(kids) - 12} more")
|
|
198
198
|
lines.extend(
|
|
199
199
|
[
|
|
200
200
|
"",
|
|
201
|
-
"
|
|
201
|
+
"enter open folder · s select as project",
|
|
202
202
|
]
|
|
203
203
|
)
|
|
204
204
|
prev.update("\n".join(lines))
|
|
@@ -23,12 +23,11 @@ AGENT_ORDER = [a.id for a in all_agents()]
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
def _agent_bar() -> str:
|
|
26
|
+
"""Plain text — no Rich tags (logos/paths break markup easily)."""
|
|
26
27
|
parts: list[str] = []
|
|
27
28
|
for i, a in enumerate(detect_agents(), start=1):
|
|
28
|
-
if a["installed"]
|
|
29
|
-
|
|
30
|
-
else:
|
|
31
|
-
parts.append(f"[#6a6a80]{i}:{a['id']}[/]")
|
|
29
|
+
flag = "+" if a["installed"] else "-"
|
|
30
|
+
parts.append(f"{i}:{a['logo']}{a['id']}{flag}")
|
|
32
31
|
return " ".join(parts)
|
|
33
32
|
|
|
34
33
|
|
|
@@ -86,11 +85,13 @@ class HubScreen(Screen):
|
|
|
86
85
|
yield OptionList(id="project-list")
|
|
87
86
|
with Vertical(id="detail-panel"):
|
|
88
87
|
yield Static("detail", classes="panel-title")
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
# markup=False: paths, timestamps, and key hints must never hit Rich
|
|
89
|
+
yield Static("Select a project", id="detail", markup=False)
|
|
90
|
+
yield Static("", id="status", markup=False)
|
|
91
91
|
yield Static(
|
|
92
|
-
"
|
|
92
|
+
"enter open · f /mnt · b browse · / filter · esc list · a agent · ] cycle · q quit",
|
|
93
93
|
id="footer-bar",
|
|
94
|
+
markup=False,
|
|
94
95
|
)
|
|
95
96
|
yield Footer()
|
|
96
97
|
|
|
@@ -173,10 +174,10 @@ class HubScreen(Screen):
|
|
|
173
174
|
if not p:
|
|
174
175
|
detail.update(
|
|
175
176
|
"Select a project\n\n"
|
|
176
|
-
"
|
|
177
|
-
"Press
|
|
178
|
-
"or
|
|
179
|
-
"
|
|
177
|
+
"This list is your saved registry.\n"
|
|
178
|
+
"Press f to browse /mnt and subdirs,\n"
|
|
179
|
+
"or b for home + all roots.\n"
|
|
180
|
+
"Press ] to cycle default agent."
|
|
180
181
|
)
|
|
181
182
|
return
|
|
182
183
|
agent = get_agent(p.default_agent)
|
|
@@ -186,29 +187,28 @@ class HubScreen(Screen):
|
|
|
186
187
|
else p.default_agent
|
|
187
188
|
)
|
|
188
189
|
exists = "yes" if p.exists() else "MISSING"
|
|
189
|
-
# agent roster
|
|
190
190
|
roster: list[str] = []
|
|
191
191
|
for i, a in enumerate(self._agent_info, start=1):
|
|
192
|
-
mark = "
|
|
193
|
-
ready = "
|
|
192
|
+
mark = "*" if a["id"] == p.default_agent else " "
|
|
193
|
+
ready = "ok" if a["installed"] else "no"
|
|
194
194
|
roster.append(f" {mark} {i} {a['logo']} {a['id']:<8} {ready}")
|
|
195
195
|
|
|
196
196
|
lines = [
|
|
197
|
-
|
|
197
|
+
p.name,
|
|
198
198
|
f"id {p.id}",
|
|
199
199
|
f"path {p.path}",
|
|
200
200
|
f"root {_root_label(p.path)}",
|
|
201
201
|
f"exists {exists}",
|
|
202
|
-
f"agent
|
|
203
|
-
f"ssh {p.ssh_host or '
|
|
204
|
-
f"env {', '.join(p.env_files) or '
|
|
205
|
-
f"tags {', '.join(p.tags) or '
|
|
202
|
+
f"agent {agent_line} (default)",
|
|
203
|
+
f"ssh {p.ssh_host or '-'}",
|
|
204
|
+
f"env {', '.join(p.env_files) or '-'}",
|
|
205
|
+
f"tags {', '.join(p.tags) or '-'}",
|
|
206
206
|
f"opened {p.last_opened_at or 'never'}",
|
|
207
207
|
"",
|
|
208
|
-
"
|
|
208
|
+
"agents ]/[ cycle · 1-7 set · A set · a launch",
|
|
209
209
|
*roster,
|
|
210
210
|
"",
|
|
211
|
-
"
|
|
211
|
+
"enter open · d detach · b/f add from disk",
|
|
212
212
|
]
|
|
213
213
|
detail.update("\n".join(lines))
|
|
214
214
|
|
|
@@ -455,19 +455,22 @@ class HubScreen(Screen):
|
|
|
455
455
|
|
|
456
456
|
|
|
457
457
|
def _root_label(path: str) -> str:
|
|
458
|
-
"""Short badge for where the project lives.
|
|
458
|
+
"""Short badge for where the project lives.
|
|
459
|
+
|
|
460
|
+
Uses angle brackets, not square ones — square brackets break Rich markup.
|
|
461
|
+
"""
|
|
459
462
|
p = path.replace("\\", "/")
|
|
460
463
|
if p.startswith("/mnt/vylth"):
|
|
461
|
-
return "
|
|
464
|
+
return "<vylth>"
|
|
462
465
|
if p.startswith("/mnt/c"):
|
|
463
|
-
return "
|
|
466
|
+
return "<win-c>"
|
|
464
467
|
if p.startswith("/mnt/d"):
|
|
465
|
-
return "
|
|
468
|
+
return "<win-d>"
|
|
466
469
|
if p.startswith("/mnt/"):
|
|
467
470
|
part = p.split("/")[2] if len(p.split("/")) > 2 else "mnt"
|
|
468
|
-
return f"
|
|
471
|
+
return f"<mnt/{part}>"
|
|
469
472
|
if p.startswith(str(Path.home())):
|
|
470
|
-
return "
|
|
473
|
+
return "<home>"
|
|
471
474
|
if p.startswith("/opt"):
|
|
472
|
-
return "
|
|
473
|
-
return "
|
|
475
|
+
return "<opt>"
|
|
476
|
+
return "<disk>"
|
|
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
|
|
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
|
|
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
|