emdash-cli 0.1.46__py3-none-any.whl → 0.1.67__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 +10 -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 +475 -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 +222 -37
  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 +865 -0
  30. emdash_cli/integrations/telegram/config.py +155 -0
  31. emdash_cli/integrations/telegram/formatter.py +385 -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.67.dist-info}/METADATA +2 -2
  35. emdash_cli-0.1.67.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.67.dist-info}/WHEEL +0 -0
  39. {emdash_cli-0.1.46.dist-info → emdash_cli-0.1.67.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,236 @@
1
+ """Contextual help system for emdash CLI.
2
+
3
+ Provides detailed help for commands with zen styling.
4
+ """
5
+
6
+ from rich.console import Console
7
+
8
+ from ...design import (
9
+ Colors,
10
+ STATUS_ACTIVE,
11
+ DOT_BULLET,
12
+ ARROW_PROMPT,
13
+ header,
14
+ footer,
15
+ SEPARATOR_WIDTH,
16
+ )
17
+
18
+ console = Console()
19
+
20
+ # Detailed help for each command
21
+ COMMAND_HELP = {
22
+ "/help": {
23
+ "description": "Show available commands and help",
24
+ "usage": ["/help", "/help <command>"],
25
+ "examples": ["/help", "/help agents"],
26
+ },
27
+ "/plan": {
28
+ "description": "Switch to plan mode for exploration and architecture",
29
+ "usage": ["/plan"],
30
+ "details": """Plan mode is read-only. The agent will explore your codebase,
31
+ analyze architecture, and create implementation plans without
32
+ making any changes.""",
33
+ "examples": ["/plan"],
34
+ },
35
+ "/code": {
36
+ "description": "Switch to code mode for implementation",
37
+ "usage": ["/code"],
38
+ "details": """Code mode allows the agent to make changes to your codebase.
39
+ Use this after approving a plan or for direct implementation tasks.""",
40
+ "examples": ["/code"],
41
+ },
42
+ "/agents": {
43
+ "description": "Manage custom agents for specialized tasks",
44
+ "usage": [
45
+ "/agents",
46
+ "/agents create <name>",
47
+ "/agents show <name>",
48
+ "/agents edit <name>",
49
+ "/agents delete <name>",
50
+ ],
51
+ "details": """Custom agents extend emdash with specialized capabilities.
52
+ Each agent has its own prompt, tools, and behavior configuration.""",
53
+ "examples": [
54
+ "/agents",
55
+ "/agents create code-reviewer",
56
+ "/agents edit planner",
57
+ ],
58
+ },
59
+ "/rules": {
60
+ "description": "Configure rules that guide agent behavior",
61
+ "usage": ["/rules", "/rules create", "/rules list"],
62
+ "details": """Rules define coding standards, preferences, and project
63
+ conventions. The agent follows these guidelines when working
64
+ on your codebase.""",
65
+ "examples": ["/rules", "/rules create"],
66
+ },
67
+ "/skills": {
68
+ "description": "Manage reusable skill templates",
69
+ "usage": ["/skills", "/skills create", "/skills list"],
70
+ "details": """Skills are reusable prompt templates that can be invoked
71
+ for common tasks like code review, testing, or documentation.""",
72
+ "examples": ["/skills", "/skills create"],
73
+ },
74
+ "/session": {
75
+ "description": "Manage conversation sessions",
76
+ "usage": ["/session", "/session save <name>", "/session load <name>"],
77
+ "details": """Sessions preserve conversation context. Save sessions to
78
+ continue work later or switch between different tasks.""",
79
+ "examples": [
80
+ "/session",
81
+ "/session save my-feature",
82
+ "/session load my-feature",
83
+ ],
84
+ },
85
+ "/verify": {
86
+ "description": "Run verification checks on the codebase",
87
+ "usage": ["/verify", "/verify <check>"],
88
+ "details": """Runs configured verifiers (tests, linting, type checking)
89
+ to ensure code quality. Use /verify-loop for automatic fixing.""",
90
+ "examples": ["/verify", "/verify tests"],
91
+ },
92
+ "/verify-loop": {
93
+ "description": "Run verification loop with automatic fixing",
94
+ "usage": ["/verify-loop <task>"],
95
+ "details": """Runs verifiers repeatedly, letting the agent fix issues
96
+ until all checks pass or you stop the loop.""",
97
+ "examples": ["/verify-loop fix the failing tests"],
98
+ },
99
+ "/pr": {
100
+ "description": "Review or create pull requests",
101
+ "usage": ["/pr <url>", "/pr create"],
102
+ "details": """Review GitHub pull requests or create new ones.
103
+ Provides detailed analysis of changes and suggestions.""",
104
+ "examples": [
105
+ "/pr https://github.com/org/repo/pull/123",
106
+ "/pr create",
107
+ ],
108
+ },
109
+ "/research": {
110
+ "description": "Research a topic using web search",
111
+ "usage": ["/research <query>"],
112
+ "details": """Searches the web for information and provides a summary.
113
+ Useful for finding documentation, examples, or solutions.""",
114
+ "examples": ["/research react hooks best practices"],
115
+ },
116
+ "/todos": {
117
+ "description": "View and manage task list",
118
+ "usage": ["/todos", "/todo-add <task>"],
119
+ "details": """Track tasks and progress. The agent can also add todos
120
+ during planning and implementation.""",
121
+ "examples": ["/todos", "/todo-add implement auth"],
122
+ },
123
+ "/context": {
124
+ "description": "View current context information",
125
+ "usage": ["/context"],
126
+ "details": """Shows token usage, context breakdown, and reranked items
127
+ in the current session.""",
128
+ "examples": ["/context"],
129
+ },
130
+ "/compact": {
131
+ "description": "Compact conversation context",
132
+ "usage": ["/compact"],
133
+ "details": """Summarizes the conversation to reduce context size while
134
+ preserving important information. Use when hitting limits.""",
135
+ "examples": ["/compact"],
136
+ },
137
+ "/status": {
138
+ "description": "Show current status and configuration",
139
+ "usage": ["/status"],
140
+ "details": """Displays current mode, model, session info, and
141
+ active configuration.""",
142
+ "examples": ["/status"],
143
+ },
144
+ "/doctor": {
145
+ "description": "Run diagnostic checks",
146
+ "usage": ["/doctor"],
147
+ "details": """Checks environment, dependencies, and configuration
148
+ for potential issues.""",
149
+ "examples": ["/doctor"],
150
+ },
151
+ "/auth": {
152
+ "description": "Manage authentication",
153
+ "usage": ["/auth login", "/auth logout", "/auth status"],
154
+ "details": """Connect or disconnect from GitHub. Authentication enables
155
+ PR reviews, issue management, and repository access.""",
156
+ "examples": ["/auth login", "/auth status"],
157
+ },
158
+ "/setup": {
159
+ "description": "Run interactive setup wizard",
160
+ "usage": ["/setup", "/setup rules", "/setup agents"],
161
+ "details": """AI-assisted setup for configuring rules, agents, skills,
162
+ and verifiers with templates and guidance.""",
163
+ "examples": ["/setup", "/setup rules"],
164
+ },
165
+ "/reset": {
166
+ "description": "Reset current session",
167
+ "usage": ["/reset"],
168
+ "details": """Clears the current session context. Use to start fresh
169
+ without closing the CLI.""",
170
+ "examples": ["/reset"],
171
+ },
172
+ "/quit": {
173
+ "description": "Exit emdash",
174
+ "usage": ["/quit", "/exit", "/q"],
175
+ "examples": ["/quit"],
176
+ },
177
+ }
178
+
179
+
180
+ def show_command_help(command: str) -> None:
181
+ """Show detailed help for a specific command."""
182
+ # Normalize command
183
+ if not command.startswith("/"):
184
+ command = "/" + command
185
+
186
+ help_info = COMMAND_HELP.get(command)
187
+
188
+ if not help_info:
189
+ console.print(f" [{Colors.ERROR}]Unknown command: {command}[/{Colors.ERROR}]")
190
+ console.print(f" [{Colors.DIM}]Type /help to see all commands[/{Colors.DIM}]")
191
+ return
192
+
193
+ console.print()
194
+ console.print(f"[{Colors.MUTED}]{header(command, SEPARATOR_WIDTH)}[/{Colors.MUTED}]")
195
+ console.print()
196
+ console.print(f" {help_info['description']}")
197
+ console.print()
198
+
199
+ # Usage
200
+ console.print(f" [{Colors.PRIMARY}]Usage:[/{Colors.PRIMARY}]")
201
+ for usage in help_info.get("usage", []):
202
+ console.print(f" {usage}")
203
+ console.print()
204
+
205
+ # Details
206
+ if "details" in help_info:
207
+ console.print(f" [{Colors.DIM}]{help_info['details']}[/{Colors.DIM}]")
208
+ console.print()
209
+
210
+ # Examples
211
+ if "examples" in help_info:
212
+ console.print(f" [{Colors.PRIMARY}]Examples:[/{Colors.PRIMARY}]")
213
+ for example in help_info["examples"]:
214
+ console.print(f" [{Colors.SUCCESS}]{example}[/{Colors.SUCCESS}]")
215
+ console.print()
216
+
217
+ console.print(f"[{Colors.MUTED}]{footer(SEPARATOR_WIDTH)}[/{Colors.MUTED}]")
218
+ console.print()
219
+
220
+
221
+ def show_quick_tips() -> None:
222
+ """Show quick tips for new users."""
223
+ console.print()
224
+ console.print(f"[{Colors.MUTED}]{header('Quick Tips', 35)}[/{Colors.MUTED}]")
225
+ console.print()
226
+ console.print(f" [{Colors.DIM}]Keyboard shortcuts:[/{Colors.DIM}]")
227
+ console.print(f" {DOT_BULLET} Ctrl+C to cancel")
228
+ console.print(f" {DOT_BULLET} Esc during execution to interrupt")
229
+ console.print(f" {DOT_BULLET} Alt+Enter for multiline input")
230
+ console.print()
231
+ console.print(f" [{Colors.DIM}]File references:[/{Colors.DIM}]")
232
+ console.print(f" {DOT_BULLET} Use @filename to include files")
233
+ console.print(f" {DOT_BULLET} Tab completion for @file paths")
234
+ console.print()
235
+ console.print(f"[{Colors.MUTED}]{footer(35)}[/{Colors.MUTED}]")
236
+ console.print()
@@ -1,6 +1,8 @@
1
1
  """Interactive REPL mode for the agent CLI."""
2
2
 
3
3
  import subprocess
4
+ import sys
5
+ import time
4
6
  import threading
5
7
  from pathlib import Path
6
8
 
@@ -9,6 +11,47 @@ from rich.panel import Panel
9
11
  from rich.markdown import Markdown
10
12
 
11
13
  from .constants import AgentMode, SLASH_COMMANDS
14
+ from .onboarding import is_first_run, run_onboarding
15
+ from .help import show_command_help
16
+ from .session_restore import get_recent_session, show_session_restore_prompt
17
+ from ...design import (
18
+ header, footer, Colors, STATUS_ACTIVE, DOT_BULLET,
19
+ ARROW_PROMPT, SEPARATOR_WIDTH,
20
+ )
21
+
22
+
23
+ def show_welcome_banner(
24
+ version: str,
25
+ git_repo: str | None,
26
+ git_branch: str | None,
27
+ mode: str,
28
+ model: str,
29
+ console: Console,
30
+ ) -> None:
31
+ """Display clean welcome banner with zen styling."""
32
+ console.print()
33
+
34
+ # Simple header
35
+ console.print(f"[{Colors.MUTED}]{header('emdash', SEPARATOR_WIDTH)}[/{Colors.MUTED}]")
36
+ console.print(f" [{Colors.DIM}]v{version}[/{Colors.DIM}]")
37
+ console.print()
38
+
39
+ # Info section
40
+ if git_repo:
41
+ branch_display = f" [{Colors.WARNING}]{git_branch}[/{Colors.WARNING}]" if git_branch else ""
42
+ console.print(f" [{Colors.DIM}]repo[/{Colors.DIM}] [{Colors.SUCCESS}]{git_repo}[/{Colors.SUCCESS}]{branch_display}")
43
+
44
+ mode_color = Colors.WARNING if mode == "plan" else Colors.SUCCESS
45
+ console.print(f" [{Colors.DIM}]mode[/{Colors.DIM}] [{mode_color}]{mode}[/{mode_color}]")
46
+ console.print(f" [{Colors.DIM}]model[/{Colors.DIM}] [{Colors.MUTED}]{model}[/{Colors.MUTED}]")
47
+ console.print()
48
+
49
+ console.print(f"[{Colors.MUTED}]{footer(SEPARATOR_WIDTH)}[/{Colors.MUTED}]")
50
+ console.print()
51
+
52
+ # Quick tips
53
+ console.print(f" [{Colors.DIM}]› /help commands › @file include files › Ctrl+C cancel[/{Colors.DIM}]")
54
+ console.print()
12
55
  from .file_utils import expand_file_references, fuzzy_find_files
13
56
  from .menus import (
14
57
  get_clarification_response,
@@ -23,7 +66,9 @@ from .handlers import (
23
66
  handle_hooks,
24
67
  handle_rules,
25
68
  handle_skills,
69
+ handle_index,
26
70
  handle_mcp,
71
+ handle_registry,
27
72
  handle_auth,
28
73
  handle_doctor,
29
74
  handle_verify,
@@ -34,6 +79,9 @@ from .handlers import (
34
79
  handle_projectmd,
35
80
  handle_research,
36
81
  handle_context,
82
+ handle_compact,
83
+ handle_diff,
84
+ handle_telegram,
37
85
  )
38
86
 
39
87
  console = Console()
@@ -141,18 +189,32 @@ def run_interactive(
141
189
  # Pending todos to add when session starts
142
190
  pending_todos: list[str] = []
143
191
 
144
- # Style for prompt
192
+ # Style for prompt (emdash signature style)
193
+ # Toolbar info (will be set later, but need closure access)
194
+ toolbar_branch: str | None = None
195
+ toolbar_model: str = "unknown"
196
+
145
197
  PROMPT_STYLE = Style.from_dict({
146
- "prompt.mode.plan": "#ffcc00 bold",
147
- "prompt.mode.code": "#00cc66 bold",
148
- "prompt.prefix": "#888888",
149
- "prompt.image": "#00ccff",
150
- "completion-menu": "bg:#1a1a2e #ffffff",
151
- "completion-menu.completion": "bg:#1a1a2e #ffffff",
152
- "completion-menu.completion.current": "bg:#4a4a6e #ffffff bold",
153
- "completion-menu.meta.completion": "bg:#1a1a2e #888888",
154
- "completion-menu.meta.completion.current": "bg:#4a4a6e #aaaaaa",
155
- "command": "#00ccff bold",
198
+ "prompt.mode.plan": f"{Colors.WARNING} bold",
199
+ "prompt.mode.code": f"{Colors.PRIMARY} bold",
200
+ "prompt.prefix": Colors.MUTED,
201
+ "prompt.cursor": f"{Colors.PRIMARY}",
202
+ "prompt.image": Colors.ACCENT,
203
+ "completion-menu": "bg:#1a1a2e #e8ecf0",
204
+ "completion-menu.completion": "bg:#1a1a2e #e8ecf0",
205
+ "completion-menu.completion.current": f"bg:#2a2a3e {Colors.SUCCESS} bold",
206
+ "completion-menu.meta.completion": f"bg:#1a1a2e {Colors.MUTED}",
207
+ "completion-menu.meta.completion.current": f"bg:#2a2a3e {Colors.SUBTLE}",
208
+ "command": f"{Colors.PRIMARY} bold",
209
+ # Zen bottom toolbar styles
210
+ "bottom-toolbar": f"bg:#1a1a1a {Colors.DIM}",
211
+ "bottom-toolbar.brand": f"bg:#1a1a1a {Colors.PRIMARY}",
212
+ "bottom-toolbar.branch": f"bg:#1a1a1a {Colors.WARNING}",
213
+ "bottom-toolbar.model": f"bg:#1a1a1a {Colors.ACCENT}",
214
+ "bottom-toolbar.mode-code": f"bg:#1a1a1a {Colors.SUCCESS}",
215
+ "bottom-toolbar.mode-plan": f"bg:#1a1a1a {Colors.WARNING}",
216
+ "bottom-toolbar.session": f"bg:#1a1a1a {Colors.SUCCESS}",
217
+ "bottom-toolbar.no-session": f"bg:#1a1a1a {Colors.MUTED}",
156
218
  })
157
219
 
158
220
  class SlashCommandCompleter(Completer):
@@ -229,6 +291,8 @@ def run_interactive(
229
291
  if image_data:
230
292
  base64_data, img_format = image_data
231
293
  attached_images.append({"data": base64_data, "format": img_format})
294
+ # Show feedback that image was attached
295
+ console.print(f" [{Colors.SUCCESS}]✓ Image {len(attached_images)} attached[/{Colors.SUCCESS}]")
232
296
  # Refresh prompt to show updated image list
233
297
  event.app.invalidate()
234
298
  return
@@ -275,39 +339,88 @@ def run_interactive(
275
339
  except Exception:
276
340
  pass
277
341
 
342
+ def get_bottom_toolbar():
343
+ """Bottom status bar with zen aesthetic - em-dashes and warm colors."""
344
+ nonlocal current_mode, session_id, toolbar_branch, toolbar_model
345
+
346
+ # Zen symbols
347
+ em = "─"
348
+ dot = "∷"
349
+
350
+ # Build toolbar with zen aesthetic
351
+ parts = [
352
+ ("class:bottom-toolbar", f" {em}{em} "),
353
+ ("class:bottom-toolbar.brand", "◈ emdash"),
354
+ ]
355
+
356
+ # Branch with stippled bullet
357
+ if toolbar_branch:
358
+ parts.append(("class:bottom-toolbar", f" {dot} "))
359
+ parts.append(("class:bottom-toolbar.branch", toolbar_branch))
360
+
361
+ # Model with stippled bullet
362
+ if toolbar_model and toolbar_model != "unknown":
363
+ parts.append(("class:bottom-toolbar", f" {dot} "))
364
+ parts.append(("class:bottom-toolbar.model", toolbar_model))
365
+
366
+ # Mode indicator
367
+ parts.append(("class:bottom-toolbar", f" {em}{em} "))
368
+ if current_mode == AgentMode.PLAN:
369
+ parts.append(("class:bottom-toolbar.mode-plan", "▹ plan"))
370
+ else:
371
+ parts.append(("class:bottom-toolbar.mode-code", "▸ code"))
372
+
373
+ # Session indicator
374
+ if session_id:
375
+ parts.append(("class:bottom-toolbar.session", " ●"))
376
+ else:
377
+ parts.append(("class:bottom-toolbar.no-session", " ○"))
378
+
379
+ parts.append(("class:bottom-toolbar", " "))
380
+
381
+ return parts
382
+
278
383
  session = PromptSession(
279
384
  history=history,
280
385
  completer=SlashCommandCompleter(),
281
386
  style=PROMPT_STYLE,
282
387
  complete_while_typing=True,
283
388
  multiline=True,
284
- prompt_continuation="... ",
389
+ prompt_continuation=" ",
285
390
  key_bindings=kb,
391
+ bottom_toolbar=get_bottom_toolbar,
286
392
  )
287
393
 
288
394
  # Watch for image paths being pasted/dropped
289
395
  session.default_buffer.on_text_changed += check_for_image_path
290
396
 
291
397
  def get_prompt():
292
- """Get formatted prompt."""
293
- nonlocal attached_images
398
+ """Get formatted prompt with distinctive emdash styling."""
399
+ nonlocal attached_images, current_mode
294
400
  parts = []
295
401
  # Show attached images above prompt
296
402
  if attached_images:
297
- image_tags = " ".join(f"[Image #{i+1}]" for i in range(len(attached_images)))
298
- parts.append(("class:prompt.image", f" {image_tags}\n"))
299
- parts.append(("class:prompt.prefix", "> "))
403
+ image_tags = " ".join(f"[Image {i+1}]" for i in range(len(attached_images)))
404
+ parts.append(("class:prompt.image", f" {image_tags}\n"))
405
+ # Distinctive em-dash prompt with mode indicator
406
+ mode_class = "class:prompt.mode.plan" if current_mode == AgentMode.PLAN else "class:prompt.mode.code"
407
+ # Use em-dash as the signature prompt element
408
+ parts.append(("class:prompt.prefix", " "))
409
+ parts.append((mode_class, f"─── "))
410
+ parts.append(("class:prompt.cursor", "█ "))
300
411
  return parts
301
412
 
302
413
  def show_help():
303
- """Show available commands."""
414
+ """Show available commands with zen styling."""
304
415
  console.print()
305
- console.print("[bold cyan]Available Commands[/bold cyan]")
416
+ console.print(f"[{Colors.MUTED}]{header('Commands', SEPARATOR_WIDTH)}[/{Colors.MUTED}]")
306
417
  console.print()
307
418
  for cmd, desc in SLASH_COMMANDS.items():
308
- console.print(f" [cyan]{cmd:12}[/cyan] {desc}")
419
+ console.print(f" [{Colors.PRIMARY}]{cmd:18}[/{Colors.PRIMARY}] [{Colors.DIM}]{desc}[/{Colors.DIM}]")
420
+ console.print()
421
+ console.print(f"[{Colors.MUTED}]{footer(SEPARATOR_WIDTH)}[/{Colors.MUTED}]")
309
422
  console.print()
310
- console.print("[dim]Type your task or question to interact with the agent.[/dim]")
423
+ console.print(f" [{Colors.DIM}]Type your task or question to interact with the agent.[/{Colors.DIM}]")
311
424
  console.print()
312
425
 
313
426
  def handle_slash_command(cmd: str) -> bool:
@@ -322,33 +435,38 @@ def run_interactive(
322
435
  return False
323
436
 
324
437
  elif command == "/help":
325
- show_help()
438
+ if args:
439
+ # Show contextual help for specific command
440
+ show_command_help(args)
441
+ else:
442
+ show_help()
326
443
 
327
444
  elif command == "/plan":
328
445
  current_mode = AgentMode.PLAN
329
446
  # Reset session so next chat creates a new session with plan mode
330
447
  if session_id:
331
448
  session_id = None
332
- console.print("[bold green] Plan mode activated[/bold green] [dim](session reset)[/dim]")
449
+ console.print(f" [{Colors.SUCCESS}]{STATUS_ACTIVE}[/{Colors.SUCCESS}] [{Colors.WARNING}]plan mode[/{Colors.WARNING}] [{Colors.DIM}](session reset)[/{Colors.DIM}]")
333
450
  else:
334
- console.print("[bold green] Plan mode activated[/bold green]")
451
+ console.print(f" [{Colors.SUCCESS}]{STATUS_ACTIVE}[/{Colors.SUCCESS}] [{Colors.WARNING}]plan mode[/{Colors.WARNING}]")
335
452
 
336
453
  elif command == "/code":
337
454
  current_mode = AgentMode.CODE
338
455
  # Reset session so next chat creates a new session with code mode
339
456
  if session_id:
340
457
  session_id = None
341
- console.print("[green]Switched to code mode (session reset)[/green]")
458
+ console.print(f" [{Colors.SUCCESS}]{STATUS_ACTIVE}[/{Colors.SUCCESS}] [{Colors.SUCCESS}]code mode[/{Colors.SUCCESS}] [{Colors.DIM}](session reset)[/{Colors.DIM}]")
342
459
  else:
343
- console.print("[green]Switched to code mode[/green]")
460
+ console.print(f" [{Colors.SUCCESS}]{STATUS_ACTIVE}[/{Colors.SUCCESS}] [{Colors.SUCCESS}]code mode[/{Colors.SUCCESS}]")
344
461
 
345
462
  elif command == "/mode":
346
- console.print(f"Current mode: [bold]{current_mode.value}[/bold]")
463
+ mode_color = Colors.WARNING if current_mode == AgentMode.PLAN else Colors.SUCCESS
464
+ console.print(f" [{Colors.MUTED}]current mode:[/{Colors.MUTED}] [{mode_color}]{current_mode.value}[/{mode_color}]")
347
465
 
348
466
  elif command == "/reset":
349
467
  session_id = None
350
468
  current_spec = None
351
- console.print("[dim]Session reset[/dim]")
469
+ console.print(f" [{Colors.DIM}]session reset[/{Colors.DIM}]")
352
470
 
353
471
  elif command == "/spec":
354
472
  if current_spec:
@@ -368,6 +486,9 @@ def run_interactive(
368
486
  elif command == "/status":
369
487
  handle_status(client)
370
488
 
489
+ elif command == "/diff":
490
+ handle_diff(args)
491
+
371
492
  elif command == "/agents":
372
493
  handle_agents(args, client, renderer, model, max_iterations, render_with_interrupt)
373
494
 
@@ -404,12 +525,33 @@ def run_interactive(
404
525
  elif command == "/skills":
405
526
  handle_skills(args, client, renderer, model, max_iterations, render_with_interrupt)
406
527
 
528
+ elif command == "/index":
529
+ handle_index(args, client)
530
+
407
531
  elif command == "/context":
408
532
  handle_context(renderer)
409
533
 
534
+ elif command == "/paste" or command == "/image":
535
+ # Attach image from clipboard
536
+ from ...clipboard import get_clipboard_image
537
+ image_data = get_clipboard_image()
538
+ if image_data:
539
+ base64_data, img_format = image_data
540
+ attached_images.append({"data": base64_data, "format": img_format})
541
+ console.print(f" [{Colors.SUCCESS}]✓ Image {len(attached_images)} attached[/{Colors.SUCCESS}]")
542
+ else:
543
+ console.print(f" [{Colors.WARNING}]No image in clipboard[/{Colors.WARNING}]")
544
+ console.print(f" [{Colors.DIM}]Copy an image first (Cmd+Shift+4 for screenshot)[/{Colors.DIM}]")
545
+
546
+ elif command == "/compact":
547
+ handle_compact(client, session_id)
548
+
410
549
  elif command == "/mcp":
411
550
  handle_mcp(args)
412
551
 
552
+ elif command == "/registry":
553
+ handle_registry(args)
554
+
413
555
  elif command == "/auth":
414
556
  handle_auth(args)
415
557
 
@@ -448,12 +590,31 @@ def run_interactive(
448
590
  handle_setup(args, client, renderer, model)
449
591
  return True
450
592
 
593
+ elif command == "/telegram":
594
+ handle_telegram(args)
595
+ return True
596
+
451
597
  else:
452
598
  console.print(f"[yellow]Unknown command: {command}[/yellow]")
453
599
  console.print("[dim]Type /help for available commands[/dim]")
454
600
 
455
601
  return True
456
602
 
603
+ # Check for first run and show onboarding
604
+ if is_first_run():
605
+ run_onboarding()
606
+
607
+ # Check for recent session to restore
608
+ recent_session = get_recent_session(client)
609
+ if recent_session:
610
+ choice, session_data = show_session_restore_prompt(recent_session)
611
+ if choice == "restore" and session_data:
612
+ session_id = session_data.get("name")
613
+ if session_data.get("mode"):
614
+ current_mode = AgentMode(session_data["mode"])
615
+ console.print(f" [{Colors.SUCCESS}]{STATUS_ACTIVE}[/{Colors.SUCCESS}] Session restored: {session_id}")
616
+ console.print()
617
+
457
618
  # Show welcome message
458
619
  from ... import __version__
459
620
 
@@ -472,20 +633,44 @@ def run_interactive(
472
633
  except Exception:
473
634
  pass
474
635
 
475
- # Welcome banner
476
- console.print()
477
- console.print(f"[bold cyan] Emdash Code[/bold cyan] [dim]v{__version__}[/dim]")
636
+ # Get current git branch
637
+ git_branch = None
638
+ try:
639
+ result = subprocess.run(
640
+ ["git", "rev-parse", "--abbrev-ref", "HEAD"],
641
+ capture_output=True,
642
+ text=True,
643
+ timeout=5,
644
+ )
645
+ if result.returncode == 0:
646
+ git_branch = result.stdout.strip()
647
+ except Exception:
648
+ pass
649
+
478
650
  # Get display model name
479
651
  if model:
480
652
  display_model = model
481
653
  else:
482
654
  from emdash_core.agent.providers.factory import DEFAULT_MODEL
483
655
  display_model = DEFAULT_MODEL
484
- if git_repo:
485
- console.print(f"[dim]Repo:[/dim] [bold green]{git_repo}[/bold green] [dim]| Mode:[/dim] [bold]{current_mode.value}[/bold] [dim]| Model:[/dim] {display_model}")
486
- else:
487
- console.print(f"[dim]Mode:[/dim] [bold]{current_mode.value}[/bold] [dim]| Model:[/dim] {display_model}")
488
- console.print()
656
+
657
+ # Shorten model name for display
658
+ if "/" in display_model:
659
+ display_model = display_model.split("/")[-1]
660
+
661
+ # Update toolbar variables for the bottom bar
662
+ toolbar_branch = git_branch
663
+ toolbar_model = display_model
664
+
665
+ # Welcome banner
666
+ show_welcome_banner(
667
+ version=__version__,
668
+ git_repo=git_repo,
669
+ git_branch=git_branch,
670
+ mode=current_mode.value,
671
+ model=display_model,
672
+ console=console,
673
+ )
489
674
 
490
675
  while True:
491
676
  try:
@@ -606,7 +791,7 @@ def run_interactive(
606
791
  if choice == "approve":
607
792
  current_mode = AgentMode.PLAN
608
793
  console.print()
609
- console.print("[bold green] Plan mode activated[/bold green]")
794
+ console.print(f" [{Colors.SUCCESS}]{STATUS_ACTIVE}[/{Colors.SUCCESS}] [{Colors.WARNING}]plan mode activated[/{Colors.WARNING}]")
610
795
  console.print()
611
796
  # Use the planmode approve endpoint
612
797
  stream = client.planmode_approve_stream(session_id)