ccmd 2.0.4__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.4/ccmd.egg-info → ccmd-2.0.5}/PKG-INFO +1 -1
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/__init__.py +1 -1
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/browser.py +15 -17
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/hub.py +26 -30
- {ccmd-2.0.4 → ccmd-2.0.5/ccmd.egg-info}/PKG-INFO +1 -1
- {ccmd-2.0.4 → ccmd-2.0.5}/pyproject.toml +1 -1
- {ccmd-2.0.4 → ccmd-2.0.5}/LICENSE +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/MANIFEST.in +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/README.md +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/__main__.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/cli.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/config/__init__.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/config/paths.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/config/schema.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/__init__.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/__init__.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/aider.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/base.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/chatgpt.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/claude.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/codex.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/cursor.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/goose.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/grok.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/fs.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/launcher.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/migrate.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/profiles.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/projects.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/scan.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/sessions.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/ssh.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/mcp/__init__.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/mcp/server.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/__init__.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/app.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/ascii.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/__init__.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/agents.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/aliases.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/project_edit.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/sessions.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/ssh.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/theme.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/widgets/__init__.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/widgets/spinner.py +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd.egg-info/SOURCES.txt +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd.egg-info/dependency_links.txt +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd.egg-info/entry_points.txt +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd.egg-info/requires.txt +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/ccmd.egg-info/top_level.txt +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/commands.yaml +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/requirements.txt +0 -0
- {ccmd-2.0.4 → ccmd-2.0.5}/setup.cfg +0 -0
|
@@ -12,8 +12,6 @@ from textual.screen import Screen
|
|
|
12
12
|
from textual.widgets import Footer, Input, OptionList, Static
|
|
13
13
|
from textual.widgets.option_list import Option
|
|
14
14
|
|
|
15
|
-
from rich.markup import escape
|
|
16
|
-
|
|
17
15
|
from ccmd.config.schema import load_config
|
|
18
16
|
from ccmd.core.agents import all_agents, get_agent
|
|
19
17
|
from ccmd.core.fs import default_browse_roots, is_projectish, list_dir_entries
|
|
@@ -71,8 +69,8 @@ class DirBrowserScreen(Screen[bool | None]):
|
|
|
71
69
|
self._agent = load_config().default_agent or "claude"
|
|
72
70
|
|
|
73
71
|
def compose(self) -> ComposeResult:
|
|
74
|
-
yield Static("directory browser", id="subtitle")
|
|
75
|
-
yield Static("", id="path-bar")
|
|
72
|
+
yield Static("directory browser", id="subtitle", markup=False)
|
|
73
|
+
yield Static("", id="path-bar", markup=False)
|
|
76
74
|
yield Input(placeholder="filter this folder…", id="filter")
|
|
77
75
|
with Horizontal(id="main"):
|
|
78
76
|
with Vertical(id="projects-panel"):
|
|
@@ -80,10 +78,11 @@ class DirBrowserScreen(Screen[bool | None]):
|
|
|
80
78
|
yield OptionList(id="dir-list")
|
|
81
79
|
with Vertical(id="detail-panel"):
|
|
82
80
|
yield Static("preview", classes="panel-title")
|
|
83
|
-
yield Static("", id="preview")
|
|
81
|
+
yield Static("", id="preview", markup=False)
|
|
84
82
|
yield Static(
|
|
85
|
-
"
|
|
83
|
+
"enter open · backspace up · s add · ]/[ agent · 1-7 agent · r roots · g path · esc",
|
|
86
84
|
id="footer-bar",
|
|
85
|
+
markup=False,
|
|
87
86
|
)
|
|
88
87
|
yield Footer()
|
|
89
88
|
|
|
@@ -100,9 +99,8 @@ class DirBrowserScreen(Screen[bool | None]):
|
|
|
100
99
|
ag = get_agent(self._agent)
|
|
101
100
|
ag_s = f"{ag.ascii_logo} {self._agent}" if ag else self._agent
|
|
102
101
|
bar.update(
|
|
103
|
-
f"
|
|
104
|
-
f"
|
|
105
|
-
+ (f" [#6a6a80]{escape(extra)}[/]" if extra else "")
|
|
102
|
+
f"{self._cwd}{mark}{hidden} · agent {ag_s}"
|
|
103
|
+
+ (f" {extra}" if extra else "")
|
|
106
104
|
)
|
|
107
105
|
|
|
108
106
|
def _render_list(self) -> None:
|
|
@@ -144,7 +142,7 @@ class DirBrowserScreen(Screen[bool | None]):
|
|
|
144
142
|
def _render_roots(self) -> None:
|
|
145
143
|
self._mode = "roots"
|
|
146
144
|
self.query_one("#path-bar", Static).update(
|
|
147
|
-
"
|
|
145
|
+
"browse roots · pick a starting point"
|
|
148
146
|
)
|
|
149
147
|
self.query_one("#list-title", Static).update("roots")
|
|
150
148
|
ol = self.query_one("#dir-list", OptionList)
|
|
@@ -186,21 +184,21 @@ class DirBrowserScreen(Screen[bool | None]):
|
|
|
186
184
|
kids = list_dir_entries(resolved, show_hidden=self._show_hidden, dirs_only=True)
|
|
187
185
|
project = is_projectish(resolved)
|
|
188
186
|
lines = [
|
|
189
|
-
|
|
190
|
-
f"path {
|
|
191
|
-
f"project {'yes
|
|
187
|
+
str(resolved.name or resolved),
|
|
188
|
+
f"path {resolved}",
|
|
189
|
+
f"project {'yes' if project else 'no (still selectable)'}",
|
|
192
190
|
f"subdirs {len(kids)}",
|
|
193
191
|
"",
|
|
194
|
-
"
|
|
192
|
+
"children",
|
|
195
193
|
]
|
|
196
194
|
for name, _, _ in kids[:12]:
|
|
197
|
-
lines.append(f"
|
|
195
|
+
lines.append(f" - {name}/")
|
|
198
196
|
if len(kids) > 12:
|
|
199
|
-
lines.append(f"
|
|
197
|
+
lines.append(f" ... +{len(kids) - 12} more")
|
|
200
198
|
lines.extend(
|
|
201
199
|
[
|
|
202
200
|
"",
|
|
203
|
-
"
|
|
201
|
+
"enter open folder · s select as project",
|
|
204
202
|
]
|
|
205
203
|
)
|
|
206
204
|
prev.update("\n".join(lines))
|
|
@@ -4,7 +4,6 @@ from __future__ import annotations
|
|
|
4
4
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
|
-
from rich.markup import escape
|
|
8
7
|
from textual import on, work
|
|
9
8
|
from textual.app import ComposeResult
|
|
10
9
|
from textual.binding import Binding
|
|
@@ -24,12 +23,11 @@ AGENT_ORDER = [a.id for a in all_agents()]
|
|
|
24
23
|
|
|
25
24
|
|
|
26
25
|
def _agent_bar() -> str:
|
|
26
|
+
"""Plain text — no Rich tags (logos/paths break markup easily)."""
|
|
27
27
|
parts: list[str] = []
|
|
28
28
|
for i, a in enumerate(detect_agents(), start=1):
|
|
29
|
-
if a["installed"]
|
|
30
|
-
|
|
31
|
-
else:
|
|
32
|
-
parts.append(f"[#6a6a80]{i}:{a['id']}[/]")
|
|
29
|
+
flag = "+" if a["installed"] else "-"
|
|
30
|
+
parts.append(f"{i}:{a['logo']}{a['id']}{flag}")
|
|
33
31
|
return " ".join(parts)
|
|
34
32
|
|
|
35
33
|
|
|
@@ -87,11 +85,13 @@ class HubScreen(Screen):
|
|
|
87
85
|
yield OptionList(id="project-list")
|
|
88
86
|
with Vertical(id="detail-panel"):
|
|
89
87
|
yield Static("detail", classes="panel-title")
|
|
90
|
-
|
|
91
|
-
|
|
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)
|
|
92
91
|
yield Static(
|
|
93
|
-
"
|
|
92
|
+
"enter open · f /mnt · b browse · / filter · esc list · a agent · ] cycle · q quit",
|
|
94
93
|
id="footer-bar",
|
|
94
|
+
markup=False,
|
|
95
95
|
)
|
|
96
96
|
yield Footer()
|
|
97
97
|
|
|
@@ -174,10 +174,10 @@ class HubScreen(Screen):
|
|
|
174
174
|
if not p:
|
|
175
175
|
detail.update(
|
|
176
176
|
"Select a project\n\n"
|
|
177
|
-
"
|
|
178
|
-
"Press
|
|
179
|
-
"or
|
|
180
|
-
"Press ] to cycle default agent.
|
|
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."
|
|
181
181
|
)
|
|
182
182
|
return
|
|
183
183
|
agent = get_agent(p.default_agent)
|
|
@@ -189,30 +189,26 @@ class HubScreen(Screen):
|
|
|
189
189
|
exists = "yes" if p.exists() else "MISSING"
|
|
190
190
|
roster: list[str] = []
|
|
191
191
|
for i, a in enumerate(self._agent_info, start=1):
|
|
192
|
-
mark = "
|
|
193
|
-
ready = "
|
|
194
|
-
roster.append(
|
|
195
|
-
f" {mark} {i} {escape(str(a['logo']))} "
|
|
196
|
-
f"{escape(a['id']):<8} {ready}"
|
|
197
|
-
)
|
|
192
|
+
mark = "*" if a["id"] == p.default_agent else " "
|
|
193
|
+
ready = "ok" if a["installed"] else "no"
|
|
194
|
+
roster.append(f" {mark} {i} {a['logo']} {a['id']:<8} {ready}")
|
|
198
195
|
|
|
199
|
-
# Escape anything that can contain [ ] so Rich markup doesn't explode
|
|
200
196
|
lines = [
|
|
201
|
-
|
|
202
|
-
f"id {
|
|
203
|
-
f"path {
|
|
204
|
-
f"root {
|
|
197
|
+
p.name,
|
|
198
|
+
f"id {p.id}",
|
|
199
|
+
f"path {p.path}",
|
|
200
|
+
f"root {_root_label(p.path)}",
|
|
205
201
|
f"exists {exists}",
|
|
206
|
-
f"agent
|
|
207
|
-
f"ssh {
|
|
208
|
-
f"env {
|
|
209
|
-
f"tags {
|
|
210
|
-
f"opened {
|
|
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
|
+
f"opened {p.last_opened_at or 'never'}",
|
|
211
207
|
"",
|
|
212
|
-
"
|
|
208
|
+
"agents ]/[ cycle · 1-7 set · A set · a launch",
|
|
213
209
|
*roster,
|
|
214
210
|
"",
|
|
215
|
-
"
|
|
211
|
+
"enter open · d detach · b/f add from disk",
|
|
216
212
|
]
|
|
217
213
|
detail.update("\n".join(lines))
|
|
218
214
|
|
|
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
|