emdash-cli 0.1.46__py3-none-any.whl → 0.1.70__py3-none-any.whl

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 (39) hide show
  1. emdash_cli/client.py +12 -28
  2. emdash_cli/commands/__init__.py +2 -2
  3. emdash_cli/commands/agent/constants.py +78 -0
  4. emdash_cli/commands/agent/handlers/__init__.py +10 -0
  5. emdash_cli/commands/agent/handlers/agents.py +67 -39
  6. emdash_cli/commands/agent/handlers/index.py +183 -0
  7. emdash_cli/commands/agent/handlers/misc.py +119 -0
  8. emdash_cli/commands/agent/handlers/registry.py +72 -0
  9. emdash_cli/commands/agent/handlers/rules.py +48 -31
  10. emdash_cli/commands/agent/handlers/sessions.py +1 -1
  11. emdash_cli/commands/agent/handlers/setup.py +187 -54
  12. emdash_cli/commands/agent/handlers/skills.py +42 -4
  13. emdash_cli/commands/agent/handlers/telegram.py +523 -0
  14. emdash_cli/commands/agent/handlers/todos.py +55 -34
  15. emdash_cli/commands/agent/handlers/verify.py +10 -5
  16. emdash_cli/commands/agent/help.py +236 -0
  17. emdash_cli/commands/agent/interactive.py +278 -47
  18. emdash_cli/commands/agent/menus.py +116 -84
  19. emdash_cli/commands/agent/onboarding.py +619 -0
  20. emdash_cli/commands/agent/session_restore.py +210 -0
  21. emdash_cli/commands/index.py +111 -13
  22. emdash_cli/commands/registry.py +635 -0
  23. emdash_cli/commands/skills.py +72 -6
  24. emdash_cli/design.py +328 -0
  25. emdash_cli/diff_renderer.py +438 -0
  26. emdash_cli/integrations/__init__.py +1 -0
  27. emdash_cli/integrations/telegram/__init__.py +15 -0
  28. emdash_cli/integrations/telegram/bot.py +402 -0
  29. emdash_cli/integrations/telegram/bridge.py +980 -0
  30. emdash_cli/integrations/telegram/config.py +155 -0
  31. emdash_cli/integrations/telegram/formatter.py +392 -0
  32. emdash_cli/main.py +52 -2
  33. emdash_cli/sse_renderer.py +632 -171
  34. {emdash_cli-0.1.46.dist-info → emdash_cli-0.1.70.dist-info}/METADATA +2 -2
  35. emdash_cli-0.1.70.dist-info/RECORD +63 -0
  36. emdash_cli/commands/swarm.py +0 -86
  37. emdash_cli-0.1.46.dist-info/RECORD +0 -49
  38. {emdash_cli-0.1.46.dist-info → emdash_cli-0.1.70.dist-info}/WHEEL +0 -0
  39. {emdash_cli-0.1.46.dist-info → emdash_cli-0.1.70.dist-info}/entry_points.txt +0 -0
@@ -1,12 +1,24 @@
1
1
  """Interactive menus for the agent CLI.
2
2
 
3
- Contains all prompt_toolkit-based interactive menus.
3
+ Contains all prompt_toolkit-based interactive menus with zen design language.
4
4
  """
5
5
 
6
6
  from pathlib import Path
7
7
 
8
8
  from rich.console import Console
9
9
 
10
+ from ...design import (
11
+ Colors,
12
+ STATUS_ACTIVE,
13
+ STATUS_INACTIVE,
14
+ STATUS_ERROR,
15
+ DOT_BULLET,
16
+ ARROW_PROMPT,
17
+ header,
18
+ footer,
19
+ menu_hint,
20
+ )
21
+
10
22
  console = Console()
11
23
 
12
24
 
@@ -77,17 +89,19 @@ def get_clarification_response(clarification: dict) -> str | None:
77
89
  lines = []
78
90
  for i, opt in enumerate(options):
79
91
  if i == selected_index[0]:
80
- lines.append(("class:selected", f" ❯ [{i+1}] {opt}\n"))
92
+ lines.append(("class:selected", f" {STATUS_ACTIVE} "))
93
+ lines.append(("class:selected", f"{i+1}. {opt}\n"))
81
94
  else:
82
- lines.append(("class:option", f" [{i+1}] {opt}\n"))
83
- lines.append(("class:hint", "\n↑/↓ to move, Enter to select, 1-9 for quick select, o for other"))
95
+ lines.append(("class:option", f" {STATUS_INACTIVE} "))
96
+ lines.append(("class:option", f"{i+1}. {opt}\n"))
97
+ lines.append(("class:hint", f"\n{ARROW_PROMPT} ↑↓ move Enter select 1-9 quick o other"))
84
98
  return lines
85
99
 
86
- # Style
100
+ # Style (zen palette)
87
101
  style = Style.from_dict({
88
- "selected": "#00cc66 bold",
89
- "option": "#888888",
90
- "hint": "#888888 italic",
102
+ "selected": f"{Colors.SUCCESS} bold",
103
+ "option": Colors.MUTED,
104
+ "hint": f"{Colors.DIM} italic",
91
105
  })
92
106
 
93
107
  # Calculate height based on options
@@ -213,22 +227,22 @@ def show_plan_approval_menu() -> tuple[str, str]:
213
227
  lines = [("class:title", "Approve this plan?\n\n")]
214
228
  for i, (key, desc) in enumerate(options):
215
229
  if i == selected_index[0]:
216
- lines.append(("class:selected", f" {key:8} "))
217
- lines.append(("class:selected-desc", f"- {desc}\n"))
230
+ lines.append(("class:selected", f" {STATUS_ACTIVE} {key}\n"))
231
+ lines.append(("class:selected-desc", f" {desc}\n"))
218
232
  else:
219
- lines.append(("class:option", f" {key:8} "))
220
- lines.append(("class:desc", f"- {desc}\n"))
221
- lines.append(("class:hint", "\n↑/↓ to move, Enter to select, y/n for quick select"))
233
+ lines.append(("class:option", f" {STATUS_INACTIVE} {key}\n"))
234
+ lines.append(("class:desc", f" {desc}\n"))
235
+ lines.append(("class:hint", f"\n{ARROW_PROMPT} y approve n feedback Esc cancel"))
222
236
  return lines
223
237
 
224
- # Style
238
+ # Style (zen palette)
225
239
  style = Style.from_dict({
226
- "title": "#00ccff bold",
227
- "selected": "#00cc66 bold",
228
- "selected-desc": "#00cc66",
229
- "option": "#888888",
230
- "desc": "#666666",
231
- "hint": "#888888 italic",
240
+ "title": f"{Colors.PRIMARY} bold",
241
+ "selected": f"{Colors.SUCCESS} bold",
242
+ "selected-desc": Colors.SUCCESS,
243
+ "option": Colors.MUTED,
244
+ "desc": Colors.DIM,
245
+ "hint": f"{Colors.DIM} italic",
232
246
  })
233
247
 
234
248
  # Layout
@@ -369,45 +383,46 @@ def show_agents_interactive_menu() -> tuple[str, str]:
369
383
 
370
384
  for i, (name, desc, is_builtin, is_action) in enumerate(menu_items):
371
385
  is_selected = i == selected_index[0]
372
- prefix = "❯ " if is_selected else " "
386
+ indicator = STATUS_ACTIVE if is_selected else STATUS_INACTIVE
373
387
 
374
388
  if is_action:
375
389
  # Action item (like Create New)
376
390
  if is_selected:
377
- lines.append(("class:action-selected", f"{prefix}{name}\n"))
391
+ lines.append(("class:action-selected", f" {indicator} {name}\n"))
378
392
  else:
379
- lines.append(("class:action", f"{prefix}{name}\n"))
393
+ lines.append(("class:action", f" {indicator} {name}\n"))
380
394
  elif is_builtin:
381
395
  # Built-in agent
382
396
  if is_selected:
383
- lines.append(("class:builtin-selected", f"{prefix}{name}"))
384
- lines.append(("class:desc-selected", f" - {desc}\n"))
397
+ lines.append(("class:builtin-selected", f" {indicator} {name}\n"))
398
+ lines.append(("class:desc-selected", f" {desc}\n"))
385
399
  else:
386
- lines.append(("class:builtin", f"{prefix}{name}"))
387
- lines.append(("class:desc", f" - {desc}\n"))
400
+ lines.append(("class:builtin", f" {indicator} {name}\n"))
401
+ lines.append(("class:desc", f" {desc}\n"))
388
402
  else:
389
403
  # Custom agent
390
404
  if is_selected:
391
- lines.append(("class:custom-selected", f"{prefix}{name}"))
392
- lines.append(("class:desc-selected", f" - {desc}\n"))
405
+ lines.append(("class:custom-selected", f" {indicator} {name}\n"))
406
+ lines.append(("class:desc-selected", f" {desc}\n"))
393
407
  else:
394
- lines.append(("class:custom", f"{prefix}{name}"))
395
- lines.append(("class:desc", f" - {desc}\n"))
408
+ lines.append(("class:custom", f" {indicator} {name}\n"))
409
+ lines.append(("class:desc", f" {desc}\n"))
396
410
 
397
- lines.append(("class:hint", "\n↑/↓ navigateEnter viewn newe editd deleteq quit"))
411
+ lines.append(("class:hint", f"\n{ARROW_PROMPT} ↑↓ navigate Enter view n new e edit d delete q quit"))
398
412
  return lines
399
413
 
414
+ # Style (zen palette)
400
415
  style = Style.from_dict({
401
- "title": "#00ccff bold",
402
- "builtin": "#888888",
403
- "builtin-selected": "#00cc66 bold",
404
- "custom": "#00ccff",
405
- "custom-selected": "#00cc66 bold",
406
- "action": "#ffcc00",
407
- "action-selected": "#ffcc00 bold",
408
- "desc": "#666666",
409
- "desc-selected": "#00cc66",
410
- "hint": "#888888 italic",
416
+ "title": f"{Colors.PRIMARY} bold",
417
+ "builtin": Colors.MUTED,
418
+ "builtin-selected": f"{Colors.SUCCESS} bold",
419
+ "custom": Colors.PRIMARY,
420
+ "custom-selected": f"{Colors.SUCCESS} bold",
421
+ "action": Colors.WARNING,
422
+ "action-selected": f"{Colors.WARNING} bold",
423
+ "desc": Colors.DIM,
424
+ "desc-selected": Colors.SUCCESS,
425
+ "hint": f"{Colors.DIM} italic",
411
426
  })
412
427
 
413
428
  height = len(menu_items) + 4 # items + title + hint + padding
@@ -439,33 +454,42 @@ def show_agents_interactive_menu() -> tuple[str, str]:
439
454
 
440
455
 
441
456
  def prompt_agent_name() -> str:
442
- """Prompt user for new agent name."""
457
+ """Prompt user for new agent name with zen styling."""
443
458
  from prompt_toolkit import PromptSession
444
459
 
445
460
  console.print()
446
- console.print("[bold]Create New Agent[/bold]")
447
- console.print("[dim]Enter a name for your agent (e.g., code-reviewer, bug-finder)[/dim]")
461
+ console.print(f"[{Colors.MUTED}]{header('Create Agent', 35)}[/{Colors.MUTED}]")
462
+ console.print()
463
+ console.print(f" [{Colors.DIM}]Enter a name for your agent[/{Colors.DIM}]")
464
+ console.print(f" [{Colors.DIM}](e.g., code-reviewer, bug-finder)[/{Colors.DIM}]")
465
+ console.print()
448
466
 
449
467
  try:
450
468
  session = PromptSession()
451
- name = session.prompt("Agent name > ").strip()
469
+ name = session.prompt(f" {ARROW_PROMPT} ").strip()
452
470
  return name.lower().replace(" ", "-") if name else ""
453
471
  except (KeyboardInterrupt, EOFError):
454
472
  return ""
455
473
 
456
474
 
457
475
  def confirm_delete(agent_name: str) -> bool:
458
- """Confirm agent deletion."""
476
+ """Confirm agent deletion with zen styling."""
459
477
  from prompt_toolkit import PromptSession
460
478
 
461
479
  console.print()
462
- console.print(f"[yellow]Delete agent '{agent_name}'?[/yellow]")
463
- console.print("[dim]This will remove the agent file. Type 'yes' to confirm.[/dim]")
480
+ console.print(f"[{Colors.MUTED}]{header('Delete Agent', 35)}[/{Colors.MUTED}]")
481
+ console.print()
482
+ console.print(f" [{Colors.ERROR}]{STATUS_ERROR}[/{Colors.ERROR}] This will permanently delete:")
483
+ console.print()
484
+ console.print(f" [{Colors.WARNING}]{agent_name}[/{Colors.WARNING}]")
485
+ console.print()
486
+ console.print(f" [{Colors.DIM}]Type 'delete' to confirm[/{Colors.DIM}]")
487
+ console.print()
464
488
 
465
489
  try:
466
490
  session = PromptSession()
467
- response = session.prompt("Confirm > ").strip().lower()
468
- return response in ("yes", "y")
491
+ response = session.prompt(f" {ARROW_PROMPT} ").strip().lower()
492
+ return response == "delete"
469
493
  except (KeyboardInterrupt, EOFError):
470
494
  return False
471
495
 
@@ -536,38 +560,39 @@ def show_sessions_interactive_menu(sessions: list, active_session: str | None) -
536
560
 
537
561
  for i, (name, summary, mode, msg_count, updated, is_active) in enumerate(menu_items):
538
562
  is_selected = i == selected_index[0]
539
- prefix = "❯ " if is_selected else " "
540
- active_marker = " *" if is_active else ""
563
+ indicator = STATUS_ACTIVE if is_selected else STATUS_INACTIVE
564
+ active_marker = f" {DOT_BULLET}" if is_active else ""
541
565
 
542
566
  if is_selected:
543
- lines.append(("class:name-selected", f"{prefix}{name}{active_marker}"))
567
+ lines.append(("class:name-selected", f" {indicator} {name}{active_marker}"))
544
568
  lines.append(("class:mode-selected", f" [{mode}]"))
545
569
  lines.append(("class:info-selected", f" {msg_count} msgs\n"))
546
570
  if summary:
547
571
  truncated = summary[:50] + "..." if len(summary) > 50 else summary
548
- lines.append(("class:summary-selected", f" {truncated}\n"))
572
+ lines.append(("class:summary-selected", f" {truncated}\n"))
549
573
  else:
550
- lines.append(("class:name", f"{prefix}{name}{active_marker}"))
574
+ lines.append(("class:name", f" {indicator} {name}{active_marker}"))
551
575
  lines.append(("class:mode", f" [{mode}]"))
552
576
  lines.append(("class:info", f" {msg_count} msgs\n"))
553
577
  if summary:
554
578
  truncated = summary[:50] + "..." if len(summary) > 50 else summary
555
- lines.append(("class:summary", f" {truncated}\n"))
579
+ lines.append(("class:summary", f" {truncated}\n"))
556
580
 
557
- lines.append(("class:hint", "\n↑/↓ navigateEnter loadd deleteq quit"))
581
+ lines.append(("class:hint", f"\n{ARROW_PROMPT} ↑↓ navigate Enter load d delete q quit"))
558
582
  return lines
559
583
 
584
+ # Style (zen palette)
560
585
  style = Style.from_dict({
561
- "title": "#00ccff bold",
562
- "name": "#00ccff",
563
- "name-selected": "#00cc66 bold",
564
- "mode": "#888888",
565
- "mode-selected": "#00cc66",
566
- "info": "#666666",
567
- "info-selected": "#00cc66",
568
- "summary": "#555555 italic",
569
- "summary-selected": "#00cc66 italic",
570
- "hint": "#444444 italic",
586
+ "title": f"{Colors.PRIMARY} bold",
587
+ "name": Colors.PRIMARY,
588
+ "name-selected": f"{Colors.SUCCESS} bold",
589
+ "mode": Colors.MUTED,
590
+ "mode-selected": Colors.SUCCESS,
591
+ "info": Colors.DIM,
592
+ "info-selected": Colors.SUCCESS,
593
+ "summary": f"{Colors.DIM} italic",
594
+ "summary-selected": f"{Colors.SUCCESS} italic",
595
+ "hint": f"{Colors.DIM} italic",
571
596
  })
572
597
 
573
598
  height = len(menu_items) * 2 + 4 # items (with summaries) + title + hint + padding
@@ -599,17 +624,23 @@ def show_sessions_interactive_menu(sessions: list, active_session: str | None) -
599
624
 
600
625
 
601
626
  def confirm_session_delete(session_name: str) -> bool:
602
- """Confirm session deletion."""
627
+ """Confirm session deletion with zen styling."""
603
628
  from prompt_toolkit import PromptSession
604
629
 
605
630
  console.print()
606
- console.print(f"[yellow]Delete session '{session_name}'?[/yellow]")
607
- console.print("[dim]Type 'yes' to confirm.[/dim]")
631
+ console.print(f"[{Colors.MUTED}]{header('Delete Session', 35)}[/{Colors.MUTED}]")
632
+ console.print()
633
+ console.print(f" [{Colors.ERROR}]{STATUS_ERROR}[/{Colors.ERROR}] This will permanently delete:")
634
+ console.print()
635
+ console.print(f" [{Colors.WARNING}]{session_name}[/{Colors.WARNING}]")
636
+ console.print()
637
+ console.print(f" [{Colors.DIM}]Type 'delete' to confirm[/{Colors.DIM}]")
638
+ console.print()
608
639
 
609
640
  try:
610
641
  session = PromptSession()
611
- response = session.prompt("Confirm > ").strip().lower()
612
- return response in ("yes", "y")
642
+ response = session.prompt(f" {ARROW_PROMPT} ").strip().lower()
643
+ return response == "delete"
613
644
  except (KeyboardInterrupt, EOFError):
614
645
  return False
615
646
 
@@ -673,21 +704,22 @@ def show_plan_mode_approval_menu() -> tuple[str, str]:
673
704
  lines = [("class:title", "Enter plan mode?\n\n")]
674
705
  for i, (key, desc) in enumerate(options):
675
706
  if i == selected_index[0]:
676
- lines.append(("class:selected", f" {key:8} "))
677
- lines.append(("class:selected-desc", f"- {desc}\n"))
707
+ lines.append(("class:selected", f" {STATUS_ACTIVE} {key}\n"))
708
+ lines.append(("class:selected-desc", f" {desc}\n"))
678
709
  else:
679
- lines.append(("class:option", f" {key:8} "))
680
- lines.append(("class:desc", f"- {desc}\n"))
681
- lines.append(("class:hint", "\n↑/↓ to move, Enter to select, y/n for quick select"))
710
+ lines.append(("class:option", f" {STATUS_INACTIVE} {key}\n"))
711
+ lines.append(("class:desc", f" {desc}\n"))
712
+ lines.append(("class:hint", f"\n{ARROW_PROMPT} y approve n skip Esc cancel"))
682
713
  return lines
683
714
 
715
+ # Style (zen palette)
684
716
  style = Style.from_dict({
685
- "title": "#ffcc00 bold",
686
- "selected": "#00cc66 bold",
687
- "selected-desc": "#00cc66",
688
- "option": "#888888",
689
- "desc": "#666666",
690
- "hint": "#888888 italic",
717
+ "title": f"{Colors.WARNING} bold",
718
+ "selected": f"{Colors.SUCCESS} bold",
719
+ "selected-desc": Colors.SUCCESS,
720
+ "option": Colors.MUTED,
721
+ "desc": Colors.DIM,
722
+ "hint": f"{Colors.DIM} italic",
691
723
  })
692
724
 
693
725
  layout = Layout(