ccmd 2.0.7__tar.gz → 2.0.8__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.7/ccmd.egg-info → ccmd-2.0.8}/PKG-INFO +1 -1
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/__init__.py +1 -1
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/cli.py +86 -30
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/shell_cmds.py +77 -7
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/tui/screens/browser.py +18 -1
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/tui/screens/hub.py +35 -3
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/tui/screens/project_edit.py +6 -2
- {ccmd-2.0.7 → ccmd-2.0.8/ccmd.egg-info}/PKG-INFO +1 -1
- {ccmd-2.0.7 → ccmd-2.0.8}/pyproject.toml +1 -1
- {ccmd-2.0.7 → ccmd-2.0.8}/LICENSE +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/MANIFEST.in +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/README.md +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/__main__.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/config/__init__.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/config/paths.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/config/schema.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/__init__.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/agents/__init__.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/agents/aider.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/agents/base.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/agents/chatgpt.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/agents/claude.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/agents/codex.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/agents/cursor.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/agents/goose.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/agents/grok.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/fs.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/launcher.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/migrate.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/profiles.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/projects.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/scan.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/sessions.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/core/ssh.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/mcp/__init__.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/mcp/server.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/tui/__init__.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/tui/app.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/tui/ascii.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/tui/screens/__init__.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/tui/screens/agents.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/tui/screens/aliases.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/tui/screens/sessions.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/tui/screens/ssh.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/tui/theme.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/tui/widgets/__init__.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd/tui/widgets/spinner.py +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd.egg-info/SOURCES.txt +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd.egg-info/dependency_links.txt +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd.egg-info/entry_points.txt +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd.egg-info/requires.txt +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/ccmd.egg-info/top_level.txt +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/commands.yaml +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/requirements.txt +0 -0
- {ccmd-2.0.7 → ccmd-2.0.8}/setup.cfg +0 -0
|
@@ -20,55 +20,84 @@ from ccmd.core.sessions import SessionStore
|
|
|
20
20
|
from ccmd.core.ssh import SSHStore
|
|
21
21
|
|
|
22
22
|
|
|
23
|
+
def _print_cmd_rows(rows: list, *, verbose: bool = False) -> None:
|
|
24
|
+
if not rows:
|
|
25
|
+
print("no matching commands")
|
|
26
|
+
return
|
|
27
|
+
for r in rows:
|
|
28
|
+
v = r["variants"]
|
|
29
|
+
print(
|
|
30
|
+
f"{r['short']:<10} {r['project']:<20} "
|
|
31
|
+
f"default={r['default_agent']:<8} "
|
|
32
|
+
f"{v.get('claude',''):<10} {v.get('grok',''):<10} "
|
|
33
|
+
f"{v.get('codex',''):<10} {v.get('cursor',''):<10}"
|
|
34
|
+
)
|
|
35
|
+
if verbose or len(rows) == 1:
|
|
36
|
+
print(f"{'':10} path {r['path']}")
|
|
37
|
+
print(
|
|
38
|
+
f"{'':10} all "
|
|
39
|
+
+ " ".join(f"{aid}={fn}" for aid, fn in v.items())
|
|
40
|
+
)
|
|
41
|
+
print(f"\n{len(rows)} project(s) · helper: ccmds · file: use ccmd shell list")
|
|
42
|
+
|
|
43
|
+
|
|
23
44
|
def _cmd_shell(args: argparse.Namespace) -> int:
|
|
24
45
|
from ccmd.core.shell_cmds import (
|
|
46
|
+
format_cmds_block,
|
|
25
47
|
install_shell_hook,
|
|
26
48
|
list_cmd_rows,
|
|
27
49
|
shell_cmds_path,
|
|
28
50
|
write_shell_cmds,
|
|
29
51
|
)
|
|
30
52
|
|
|
31
|
-
sub = args.shell_cmd or "
|
|
53
|
+
sub = args.shell_cmd or "list"
|
|
54
|
+
filt = getattr(args, "filter", None)
|
|
55
|
+
verbose = bool(getattr(args, "verbose", False))
|
|
56
|
+
|
|
32
57
|
if sub == "install":
|
|
33
|
-
path = write_shell_cmds()
|
|
58
|
+
path, shorts = write_shell_cmds()
|
|
34
59
|
rcs = install_shell_hook()
|
|
35
60
|
print(f"wrote {path}")
|
|
36
61
|
for rc in rcs:
|
|
37
62
|
print(f"hooked {rc}")
|
|
38
63
|
print("restart shell or: source ~/.ccmd/shell_cmds.sh")
|
|
39
|
-
print("then:
|
|
64
|
+
print("then: ccmds # list all")
|
|
65
|
+
print(" ana / anac / anag …")
|
|
66
|
+
if verbose:
|
|
67
|
+
_print_cmd_rows(list_cmd_rows(), verbose=False)
|
|
40
68
|
return 0
|
|
41
69
|
if sub == "regen":
|
|
42
|
-
path = write_shell_cmds()
|
|
43
|
-
print(f"regenerated {path}")
|
|
44
|
-
rows = list_cmd_rows()
|
|
45
|
-
|
|
46
|
-
print(
|
|
47
|
-
f" {r['short']:<10} {r['project']:<18} "
|
|
48
|
-
f"default={r['default_agent']:<8} "
|
|
49
|
-
f"{r['short']}c/g/x/u/o/a/p"
|
|
50
|
-
)
|
|
51
|
-
if len(rows) > 30:
|
|
52
|
-
print(f" … +{len(rows) - 30} more (see {path})")
|
|
70
|
+
path, shorts = write_shell_cmds()
|
|
71
|
+
print(f"regenerated {path} ({len(shorts)} projects)")
|
|
72
|
+
rows = list_cmd_rows(filter_text=filt)
|
|
73
|
+
_print_cmd_rows(rows, verbose=verbose)
|
|
53
74
|
return 0
|
|
54
75
|
if sub == "list":
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
f"{r['short']:<10} {r['project']:<20} "
|
|
63
|
-
f"{v.get('claude','')} {v.get('grok','')} {v.get('codex','')} "
|
|
64
|
-
f"{v.get('cursor','')} → {r['path']}"
|
|
65
|
-
)
|
|
66
|
-
print(f"\nfile: {shell_cmds_path()}")
|
|
76
|
+
rows = list_cmd_rows(filter_text=filt)
|
|
77
|
+
_print_cmd_rows(rows, verbose=verbose or bool(filt))
|
|
78
|
+
print(f"file: {shell_cmds_path()}")
|
|
79
|
+
return 0
|
|
80
|
+
if sub == "show" and filt:
|
|
81
|
+
rows = list_cmd_rows(filter_text=filt)
|
|
82
|
+
_print_cmd_rows(rows, verbose=True)
|
|
67
83
|
return 0
|
|
68
|
-
print("usage: ccmd shell install|regen|list", file=sys.stderr)
|
|
84
|
+
print("usage: ccmd shell install|regen|list [--filter NAME] [-v]", file=sys.stderr)
|
|
85
|
+
print(" ccmds # shell helper (after: source ~/.ccmd/shell_cmds.sh)", file=sys.stderr)
|
|
69
86
|
return 1
|
|
70
87
|
|
|
71
88
|
|
|
89
|
+
def _cmd_cmds(args: argparse.Namespace) -> int:
|
|
90
|
+
"""Top-level: ccmd cmds [--list] [filter] — same as shell list."""
|
|
91
|
+
from ccmd.core.shell_cmds import list_cmd_rows, shell_cmds_path
|
|
92
|
+
|
|
93
|
+
filt = args.filter or args.name
|
|
94
|
+
rows = list_cmd_rows(filter_text=filt)
|
|
95
|
+
_print_cmd_rows(rows, verbose=bool(filt) or args.verbose)
|
|
96
|
+
print(f"file: {shell_cmds_path()}")
|
|
97
|
+
print("tip: ccmds (shell function) after source ~/.ccmd/shell_cmds.sh")
|
|
98
|
+
return 0
|
|
99
|
+
|
|
100
|
+
|
|
72
101
|
def _cmd_open(args: argparse.Namespace) -> int:
|
|
73
102
|
reg = ProjectRegistry()
|
|
74
103
|
project = reg.get(args.project)
|
|
@@ -301,9 +330,35 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
301
330
|
sh.add_argument(
|
|
302
331
|
"shell_cmd",
|
|
303
332
|
nargs="?",
|
|
304
|
-
choices=["install", "regen", "list"],
|
|
305
|
-
default="
|
|
306
|
-
help="install
|
|
333
|
+
choices=["install", "regen", "list", "show"],
|
|
334
|
+
default="list",
|
|
335
|
+
help="install|regen|list|show",
|
|
336
|
+
)
|
|
337
|
+
sh.add_argument(
|
|
338
|
+
"--filter",
|
|
339
|
+
"-f",
|
|
340
|
+
dest="filter",
|
|
341
|
+
help="filter by short/project/path (e.g. ana)",
|
|
342
|
+
)
|
|
343
|
+
sh.add_argument(
|
|
344
|
+
"-v",
|
|
345
|
+
"--verbose",
|
|
346
|
+
action="store_true",
|
|
347
|
+
help="show full agent variant map",
|
|
348
|
+
)
|
|
349
|
+
|
|
350
|
+
cmds = sub.add_parser(
|
|
351
|
+
"cmds",
|
|
352
|
+
help="List short shell commands (same as: ccmd shell list / ccmds)",
|
|
353
|
+
)
|
|
354
|
+
cmds.add_argument("name", nargs="?", help="filter (e.g. ana or flow)")
|
|
355
|
+
cmds.add_argument("--filter", "-f", dest="filter", help="filter text")
|
|
356
|
+
cmds.add_argument("-v", "--verbose", action="store_true")
|
|
357
|
+
cmds.add_argument(
|
|
358
|
+
"--list",
|
|
359
|
+
"-l",
|
|
360
|
+
action="store_true",
|
|
361
|
+
help="list all (default)",
|
|
307
362
|
)
|
|
308
363
|
|
|
309
364
|
return p
|
|
@@ -336,6 +391,7 @@ def main(argv: list[str] | None = None) -> int:
|
|
|
336
391
|
"doctor": _cmd_doctor,
|
|
337
392
|
"mcp": _cmd_mcp,
|
|
338
393
|
"shell": _cmd_shell,
|
|
394
|
+
"cmds": _cmd_cmds,
|
|
339
395
|
}
|
|
340
396
|
handler = handlers.get(args.cmd)
|
|
341
397
|
if not handler:
|
|
@@ -243,13 +243,66 @@ def render_shell_cmds(projects: list, shorts: dict[str, str]) -> str:
|
|
|
243
243
|
)
|
|
244
244
|
lines.append("")
|
|
245
245
|
|
|
246
|
-
|
|
247
|
-
lines.
|
|
246
|
+
# helper: list all short commands from the shell
|
|
247
|
+
lines.extend(
|
|
248
|
+
[
|
|
249
|
+
"# list every project short command",
|
|
250
|
+
"ccmds() {",
|
|
251
|
+
' if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then',
|
|
252
|
+
' echo "ccmds list all short commands"',
|
|
253
|
+
' echo "ccmds <name> show one project (e.g. ccmds ana)"',
|
|
254
|
+
' echo "ccmds -l|--list same as bare ccmds"',
|
|
255
|
+
' echo "ccmd shell list full CLI list"',
|
|
256
|
+
" return 0",
|
|
257
|
+
" fi",
|
|
258
|
+
' if [ -z "$1" ] || [ "$1" = "-l" ] || [ "$1" = "--list" ]; then',
|
|
259
|
+
' command ccmd shell list',
|
|
260
|
+
" return $?",
|
|
261
|
+
" fi",
|
|
262
|
+
' command ccmd shell list --filter "$1"',
|
|
263
|
+
"}",
|
|
264
|
+
"",
|
|
265
|
+
"# end ccmd shell cmds",
|
|
266
|
+
"",
|
|
267
|
+
]
|
|
268
|
+
)
|
|
269
|
+
return "\n".join(lines)
|
|
270
|
+
|
|
271
|
+
|
|
272
|
+
def cmds_notice(project, short: str | None = None) -> str:
|
|
273
|
+
"""Human-readable one-liner of shortcuts for a project (for TUI/CLI notices)."""
|
|
274
|
+
s = short or getattr(project, "short", None) or ""
|
|
275
|
+
if not s:
|
|
276
|
+
shorts = allocate_shorts([project])
|
|
277
|
+
s = shorts.get(project.id, project.id[:4])
|
|
278
|
+
variants = " ".join(
|
|
279
|
+
f"{s}{suf}={aid}" for aid, suf in AGENT_SUFFIX.items()
|
|
280
|
+
)
|
|
281
|
+
return (
|
|
282
|
+
f"cmds: {s} (default={project.default_agent}) "
|
|
283
|
+
f"{s}c=claude {s}g=grok {s}x=codex {s}u=cursor "
|
|
284
|
+
f"| ccmds or ccmd shell list"
|
|
285
|
+
)
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
def format_cmds_block(project, short: str | None = None) -> str:
|
|
289
|
+
"""Multi-line block shown after save."""
|
|
290
|
+
s = short or getattr(project, "short", None) or project.id[:4]
|
|
291
|
+
lines = [
|
|
292
|
+
f"shell commands for {project.id}:",
|
|
293
|
+
f" {s:<10} open with default agent ({project.default_agent})",
|
|
294
|
+
]
|
|
295
|
+
for aid, suf in AGENT_SUFFIX.items():
|
|
296
|
+
lines.append(f" {s}{suf:<9} --agent {aid}")
|
|
297
|
+
lines.append("list all: ccmds or ccmd shell list")
|
|
248
298
|
return "\n".join(lines)
|
|
249
299
|
|
|
250
300
|
|
|
251
|
-
def write_shell_cmds(projects: list | None = None) -> Path:
|
|
252
|
-
"""Write ~/.ccmd/shell_cmds.sh and persist short names on projects.
|
|
301
|
+
def write_shell_cmds(projects: list | None = None) -> tuple[Path, dict[str, str]]:
|
|
302
|
+
"""Write ~/.ccmd/shell_cmds.sh and persist short names on projects.
|
|
303
|
+
|
|
304
|
+
Returns (path, {project_id: short}).
|
|
305
|
+
"""
|
|
253
306
|
from ccmd.core.projects import ProjectRegistry
|
|
254
307
|
|
|
255
308
|
reg = ProjectRegistry()
|
|
@@ -278,7 +331,7 @@ def write_shell_cmds(projects: list | None = None) -> Path:
|
|
|
278
331
|
)
|
|
279
332
|
lines.append(f"{'':10} {p.path}\n")
|
|
280
333
|
sheet.write_text("".join(lines), encoding="utf-8")
|
|
281
|
-
return path
|
|
334
|
+
return path, shorts
|
|
282
335
|
|
|
283
336
|
|
|
284
337
|
def install_shell_hook(rc_file: Path | None = None) -> list[Path]:
|
|
@@ -288,6 +341,7 @@ def install_shell_hook(rc_file: Path | None = None) -> list[Path]:
|
|
|
288
341
|
block = (
|
|
289
342
|
f"\n{MARKER_BEGIN}\n"
|
|
290
343
|
f"# managed by ccmd — short project commands (ana/anac/anag…)\n"
|
|
344
|
+
f"# list them anytime: ccmds or ccmds ana\n"
|
|
291
345
|
f'[ -f "$HOME/.ccmd/shell_cmds.sh" ] && . "$HOME/.ccmd/shell_cmds.sh"\n'
|
|
292
346
|
f"{MARKER_END}\n"
|
|
293
347
|
)
|
|
@@ -331,22 +385,38 @@ def install_shell_hook(rc_file: Path | None = None) -> list[Path]:
|
|
|
331
385
|
return updated
|
|
332
386
|
|
|
333
387
|
|
|
334
|
-
def list_cmd_rows(
|
|
388
|
+
def list_cmd_rows(
|
|
389
|
+
projects: list | None = None,
|
|
390
|
+
*,
|
|
391
|
+
filter_text: str | None = None,
|
|
392
|
+
) -> list[dict]:
|
|
335
393
|
from ccmd.core.projects import ProjectRegistry
|
|
336
394
|
|
|
337
395
|
projects = list(projects) if projects is not None else ProjectRegistry().list()
|
|
338
396
|
shorts = allocate_shorts(projects)
|
|
339
397
|
rows = []
|
|
340
398
|
for p in projects:
|
|
341
|
-
s = shorts.get(p.id, "")
|
|
399
|
+
s = shorts.get(p.id, "") or getattr(p, "short", "") or ""
|
|
342
400
|
rows.append(
|
|
343
401
|
{
|
|
344
402
|
"short": s,
|
|
345
403
|
"project": p.id,
|
|
404
|
+
"name": p.name,
|
|
346
405
|
"default_agent": p.default_agent,
|
|
347
406
|
"path": p.path,
|
|
348
407
|
"variants": {a: f"{s}{suf}" for a, suf in AGENT_SUFFIX.items()},
|
|
349
408
|
}
|
|
350
409
|
)
|
|
351
410
|
rows.sort(key=lambda r: r["short"])
|
|
411
|
+
if filter_text:
|
|
412
|
+
q = filter_text.strip().lower()
|
|
413
|
+
rows = [
|
|
414
|
+
r
|
|
415
|
+
for r in rows
|
|
416
|
+
if q in r["short"].lower()
|
|
417
|
+
or q in r["project"].lower()
|
|
418
|
+
or q in r["name"].lower()
|
|
419
|
+
or q in r["path"].lower()
|
|
420
|
+
or any(q in v.lower() for v in r["variants"].values())
|
|
421
|
+
]
|
|
352
422
|
return rows
|
|
@@ -323,14 +323,24 @@ class DirBrowserScreen(Screen[bool | None]):
|
|
|
323
323
|
self.notify(str(e), severity="error")
|
|
324
324
|
return
|
|
325
325
|
|
|
326
|
+
from ccmd.core.shell_cmds import cmds_notice
|
|
327
|
+
|
|
326
328
|
reg = ProjectRegistry()
|
|
327
329
|
existing = reg.find_by_path(resolved)
|
|
328
330
|
if existing:
|
|
329
331
|
existing.default_agent = self._agent
|
|
330
332
|
reg.save(existing)
|
|
333
|
+
# reload short after regen
|
|
334
|
+
existing = reg.get(existing.id) or existing
|
|
335
|
+
self.notify(
|
|
336
|
+
f"updated {existing.id} · agent {self._agent}",
|
|
337
|
+
severity="information",
|
|
338
|
+
timeout=4,
|
|
339
|
+
)
|
|
331
340
|
self.notify(
|
|
332
|
-
|
|
341
|
+
cmds_notice(existing),
|
|
333
342
|
severity="information",
|
|
343
|
+
timeout=8,
|
|
334
344
|
)
|
|
335
345
|
self.dismiss(True)
|
|
336
346
|
return
|
|
@@ -342,9 +352,16 @@ class DirBrowserScreen(Screen[bool | None]):
|
|
|
342
352
|
default_agent=self._agent,
|
|
343
353
|
notes="added via browser",
|
|
344
354
|
)
|
|
355
|
+
p = reg.get(p.id) or p
|
|
345
356
|
self.notify(
|
|
346
357
|
f"added {p.id} · agent {self._agent} → {resolved}",
|
|
347
358
|
severity="information",
|
|
359
|
+
timeout=4,
|
|
360
|
+
)
|
|
361
|
+
self.notify(
|
|
362
|
+
cmds_notice(p),
|
|
363
|
+
severity="information",
|
|
364
|
+
timeout=10,
|
|
348
365
|
)
|
|
349
366
|
self.dismiss(True)
|
|
350
367
|
|
|
@@ -51,6 +51,7 @@ class HubScreen(Screen):
|
|
|
51
51
|
Binding("slash", "focus_filter", "Filter", show=False),
|
|
52
52
|
Binding("/", "focus_filter", "Filter", show=True),
|
|
53
53
|
Binding("escape", "blur_filter", "Esc", show=False),
|
|
54
|
+
Binding("c", "show_cmds", "Cmds", show=True),
|
|
54
55
|
Binding("1", "agent_n_1", show=False),
|
|
55
56
|
Binding("2", "agent_n_2", show=False),
|
|
56
57
|
Binding("3", "agent_n_3", show=False),
|
|
@@ -211,11 +212,34 @@ class HubScreen(Screen):
|
|
|
211
212
|
"agents ]/[ cycle · 1-7 set · A set · a launch",
|
|
212
213
|
*roster,
|
|
213
214
|
"",
|
|
214
|
-
"enter open · d detach · b/f add from disk",
|
|
215
|
-
"shell: source ~/.ccmd/shell_cmds.sh
|
|
215
|
+
"enter open · d detach · b/f add from disk · c show cmds",
|
|
216
|
+
"shell: ccmds | ccmd shell list | source ~/.ccmd/shell_cmds.sh",
|
|
216
217
|
]
|
|
217
218
|
detail.update("\n".join(lines))
|
|
218
219
|
|
|
220
|
+
def action_show_cmds(self) -> None:
|
|
221
|
+
"""Flag-style view: toast the shell shortcuts for the selected project."""
|
|
222
|
+
from ccmd.core.shell_cmds import cmds_notice, format_cmds_block
|
|
223
|
+
|
|
224
|
+
p = self._selected()
|
|
225
|
+
if not p:
|
|
226
|
+
self.notify(
|
|
227
|
+
"all cmds: run ccmds or ccmd shell list",
|
|
228
|
+
severity="information",
|
|
229
|
+
timeout=6,
|
|
230
|
+
)
|
|
231
|
+
return
|
|
232
|
+
p = self.registry.get(p.id) or p
|
|
233
|
+
# multi-line block in status + toast summary
|
|
234
|
+
block = format_cmds_block(p)
|
|
235
|
+
self.query_one("#status", Static).update(block.replace("\n", " · "))
|
|
236
|
+
self.notify(cmds_notice(p), severity="information", timeout=10)
|
|
237
|
+
self.notify(
|
|
238
|
+
f"{p.short or p.id}: c={p.short}c g={p.short}g x={p.short}x u={p.short}u",
|
|
239
|
+
severity="information",
|
|
240
|
+
timeout=8,
|
|
241
|
+
)
|
|
242
|
+
|
|
219
243
|
def _set_default_agent(self, agent_id: str, *, notify: bool = True) -> None:
|
|
220
244
|
p = self._selected()
|
|
221
245
|
if not p:
|
|
@@ -226,8 +250,16 @@ class HubScreen(Screen):
|
|
|
226
250
|
return
|
|
227
251
|
p.default_agent = agent_id
|
|
228
252
|
self.registry.save(p)
|
|
253
|
+
p = self.registry.get(p.id) or p
|
|
229
254
|
if notify:
|
|
230
|
-
|
|
255
|
+
from ccmd.core.shell_cmds import cmds_notice
|
|
256
|
+
|
|
257
|
+
self.notify(
|
|
258
|
+
f"{p.id} → default agent {agent_id}",
|
|
259
|
+
severity="information",
|
|
260
|
+
timeout=3,
|
|
261
|
+
)
|
|
262
|
+
self.notify(cmds_notice(p), severity="information", timeout=8)
|
|
231
263
|
self._load_projects(self.query_one("#filter", Input).value)
|
|
232
264
|
|
|
233
265
|
def _cycle_agent(self, delta: int) -> None:
|
|
@@ -45,8 +45,12 @@ class ProjectEditScreen(Screen[bool | None]):
|
|
|
45
45
|
if not p.is_dir():
|
|
46
46
|
self.notify(f"not a directory: {p}", severity="error")
|
|
47
47
|
return
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
from ccmd.core.shell_cmds import cmds_notice
|
|
49
|
+
|
|
50
|
+
proj = ProjectRegistry().add(name=name, path=p, default_agent=agent)
|
|
51
|
+
proj = ProjectRegistry().get(proj.id) or proj
|
|
52
|
+
self.notify(f"added {name}", severity="information", timeout=3)
|
|
53
|
+
self.notify(cmds_notice(proj), severity="information", timeout=10)
|
|
50
54
|
self.dismiss(True)
|
|
51
55
|
|
|
52
56
|
def action_cancel(self) -> None:
|
|
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
|