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.
Files changed (54) hide show
  1. {ccmd-2.0.4/ccmd.egg-info → ccmd-2.0.5}/PKG-INFO +1 -1
  2. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/__init__.py +1 -1
  3. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/browser.py +15 -17
  4. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/hub.py +26 -30
  5. {ccmd-2.0.4 → ccmd-2.0.5/ccmd.egg-info}/PKG-INFO +1 -1
  6. {ccmd-2.0.4 → ccmd-2.0.5}/pyproject.toml +1 -1
  7. {ccmd-2.0.4 → ccmd-2.0.5}/LICENSE +0 -0
  8. {ccmd-2.0.4 → ccmd-2.0.5}/MANIFEST.in +0 -0
  9. {ccmd-2.0.4 → ccmd-2.0.5}/README.md +0 -0
  10. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/__main__.py +0 -0
  11. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/cli.py +0 -0
  12. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/config/__init__.py +0 -0
  13. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/config/paths.py +0 -0
  14. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/config/schema.py +0 -0
  15. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/__init__.py +0 -0
  16. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/__init__.py +0 -0
  17. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/aider.py +0 -0
  18. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/base.py +0 -0
  19. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/chatgpt.py +0 -0
  20. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/claude.py +0 -0
  21. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/codex.py +0 -0
  22. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/cursor.py +0 -0
  23. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/goose.py +0 -0
  24. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/agents/grok.py +0 -0
  25. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/fs.py +0 -0
  26. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/launcher.py +0 -0
  27. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/migrate.py +0 -0
  28. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/profiles.py +0 -0
  29. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/projects.py +0 -0
  30. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/scan.py +0 -0
  31. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/sessions.py +0 -0
  32. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/core/ssh.py +0 -0
  33. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/mcp/__init__.py +0 -0
  34. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/mcp/server.py +0 -0
  35. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/__init__.py +0 -0
  36. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/app.py +0 -0
  37. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/ascii.py +0 -0
  38. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/__init__.py +0 -0
  39. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/agents.py +0 -0
  40. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/aliases.py +0 -0
  41. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/project_edit.py +0 -0
  42. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/sessions.py +0 -0
  43. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/screens/ssh.py +0 -0
  44. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/theme.py +0 -0
  45. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/widgets/__init__.py +0 -0
  46. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd/tui/widgets/spinner.py +0 -0
  47. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd.egg-info/SOURCES.txt +0 -0
  48. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd.egg-info/dependency_links.txt +0 -0
  49. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd.egg-info/entry_points.txt +0 -0
  50. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd.egg-info/requires.txt +0 -0
  51. {ccmd-2.0.4 → ccmd-2.0.5}/ccmd.egg-info/top_level.txt +0 -0
  52. {ccmd-2.0.4 → ccmd-2.0.5}/commands.yaml +0 -0
  53. {ccmd-2.0.4 → ccmd-2.0.5}/requirements.txt +0 -0
  54. {ccmd-2.0.4 → ccmd-2.0.5}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ccmd
3
- Version: 2.0.4
3
+ Version: 2.0.5
4
4
  Summary: ccmd — Agentic Project Hub. Pick a project, pick an agent, launch.
5
5
  Author-email: De Catalyst <Robert5560newton@gmail.com>
6
6
  License: MIT
@@ -1,4 +1,4 @@
1
1
  """ccmd 2.0 — Agentic Project Hub."""
2
2
 
3
- __version__ = "2.0.4"
3
+ __version__ = "2.0.5"
4
4
  __all__ = ["__version__"]
@@ -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
- " open · up · s add project · ]/[ agent · 1-7 agent · r roots · g path · esc",
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"[bold #00ffc8]{escape(str(self._cwd))}[/]{mark}{hidden} · "
104
- f"agent [bold #c84bff]{escape(ag_s)}[/]"
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
- "[bold #c84bff]browse roots[/] · pick a starting point"
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
- f"[bold #00ffc8]{escape(str(resolved.name or resolved))}[/]",
190
- f"path {escape(str(resolved))}",
191
- f"project {'yes' if project else 'no (still selectable)'}",
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
- "[#6a6a80]children[/]",
192
+ "children",
195
193
  ]
196
194
  for name, _, _ in kids[:12]:
197
- lines.append(f" · {escape(name)}/")
195
+ lines.append(f" - {name}/")
198
196
  if len(kids) > 12:
199
- lines.append(f" +{len(kids) - 12} more")
197
+ lines.append(f" ... +{len(kids) - 12} more")
200
198
  lines.extend(
201
199
  [
202
200
  "",
203
- "[#6a6a80]enter open folder · s select as project[/]",
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
- parts.append(f"[{a['color']}]{i}:{a['logo']}{a['id']}[/]")
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
- yield Static("Select a project", id="detail")
91
- yield Static("", id="status")
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
- " open · f /mnt · b browse · / filter · esc list · a agent · ] cycle · q quit",
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
- "[#6a6a80]This list is your saved registry.\n"
178
- "Press [bold]f[/] to browse /mnt and subdirs,\n"
179
- "or [bold]b[/] for home + all roots.\n"
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 = "" if a["id"] == p.default_agent else "·"
193
- ready = "" if a["installed"] else ""
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
- f"[bold #00ffc8]{escape(p.name)}[/]",
202
- f"id {escape(p.id)}",
203
- f"path {escape(p.path)}",
204
- f"root {escape(_root_label(p.path))}",
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 [bold]{escape(agent_line)}[/] (default)",
207
- f"ssh {escape(p.ssh_host or '')}",
208
- f"env {escape(', '.join(p.env_files) or '')}",
209
- f"tags {escape(', '.join(p.tags) or '')}",
210
- f"opened {escape(p.last_opened_at or 'never')}",
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
- "[bold #c84bff]agents[/] keys: ] [ cycle · 1-7 set · A set · a launch",
208
+ "agents ]/[ cycle · 1-7 set · A set · a launch",
213
209
  *roster,
214
210
  "",
215
- "[#6a6a80]enter open · d detach · b/f add from disk[/]",
211
+ "enter open · d detach · b/f add from disk",
216
212
  ]
217
213
  detail.update("\n".join(lines))
218
214
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ccmd
3
- Version: 2.0.4
3
+ Version: 2.0.5
4
4
  Summary: ccmd — Agentic Project Hub. Pick a project, pick an agent, launch.
5
5
  Author-email: De Catalyst <Robert5560newton@gmail.com>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ccmd"
7
- version = "2.0.4"
7
+ version = "2.0.5"
8
8
  description = "ccmd — Agentic Project Hub. Pick a project, pick an agent, launch."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
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