emdash-cli 0.1.30__tar.gz → 0.1.60__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 (60) hide show
  1. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/PKG-INFO +2 -4
  2. emdash_cli-0.1.60/emdash_cli/__init__.py +23 -0
  3. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/client.py +162 -22
  4. emdash_cli-0.1.60/emdash_cli/clipboard.py +92 -0
  5. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/__init__.py +2 -2
  6. emdash_cli-0.1.60/emdash_cli/commands/agent/__init__.py +14 -0
  7. emdash_cli-0.1.60/emdash_cli/commands/agent/cli.py +100 -0
  8. emdash_cli-0.1.60/emdash_cli/commands/agent/constants.py +61 -0
  9. emdash_cli-0.1.60/emdash_cli/commands/agent/file_utils.py +178 -0
  10. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/__init__.py +49 -0
  11. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/agents.py +449 -0
  12. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/auth.py +69 -0
  13. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/doctor.py +319 -0
  14. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/hooks.py +121 -0
  15. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/index.py +183 -0
  16. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/mcp.py +183 -0
  17. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/misc.py +319 -0
  18. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/registry.py +72 -0
  19. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/rules.py +411 -0
  20. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/sessions.py +168 -0
  21. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/setup.py +715 -0
  22. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/skills.py +478 -0
  23. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/todos.py +119 -0
  24. emdash_cli-0.1.60/emdash_cli/commands/agent/handlers/verify.py +653 -0
  25. emdash_cli-0.1.60/emdash_cli/commands/agent/help.py +236 -0
  26. emdash_cli-0.1.60/emdash_cli/commands/agent/interactive.py +837 -0
  27. emdash_cli-0.1.60/emdash_cli/commands/agent/menus.py +760 -0
  28. emdash_cli-0.1.60/emdash_cli/commands/agent/onboarding.py +619 -0
  29. emdash_cli-0.1.60/emdash_cli/commands/agent/session_restore.py +210 -0
  30. emdash_cli-0.1.60/emdash_cli/commands/agent.py +10 -0
  31. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/index.py +111 -13
  32. emdash_cli-0.1.60/emdash_cli/commands/registry.py +635 -0
  33. emdash_cli-0.1.60/emdash_cli/commands/server.py +176 -0
  34. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/skills.py +72 -6
  35. emdash_cli-0.1.60/emdash_cli/design.py +328 -0
  36. emdash_cli-0.1.60/emdash_cli/diff_renderer.py +438 -0
  37. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/main.py +2 -2
  38. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/server_manager.py +70 -10
  39. emdash_cli-0.1.60/emdash_cli/session_store.py +321 -0
  40. emdash_cli-0.1.60/emdash_cli/sse_renderer.py +1240 -0
  41. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/pyproject.toml +3 -8
  42. emdash_cli-0.1.30/emdash_cli/__init__.py +0 -8
  43. emdash_cli-0.1.30/emdash_cli/clipboard.py +0 -123
  44. emdash_cli-0.1.30/emdash_cli/commands/agent.py +0 -859
  45. emdash_cli-0.1.30/emdash_cli/commands/server.py +0 -117
  46. emdash_cli-0.1.30/emdash_cli/commands/swarm.py +0 -86
  47. emdash_cli-0.1.30/emdash_cli/sse_renderer.py +0 -648
  48. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/analyze.py +0 -0
  49. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/auth.py +0 -0
  50. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/db.py +0 -0
  51. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/embed.py +0 -0
  52. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/plan.py +0 -0
  53. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/projectmd.py +0 -0
  54. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/research.py +0 -0
  55. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/rules.py +0 -0
  56. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/search.py +0 -0
  57. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/spec.py +0 -0
  58. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/tasks.py +0 -0
  59. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/commands/team.py +0 -0
  60. {emdash_cli-0.1.30 → emdash_cli-0.1.60}/emdash_cli/keyboard.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: emdash-cli
3
- Version: 0.1.30
3
+ Version: 0.1.60
4
4
  Summary: EmDash CLI - Command-line interface for code intelligence
5
5
  Author: Em Dash Team
6
6
  Requires-Python: >=3.10,<4.0
@@ -10,10 +10,8 @@ Classifier: Programming Language :: Python :: 3.11
10
10
  Classifier: Programming Language :: Python :: 3.12
11
11
  Classifier: Programming Language :: Python :: 3.13
12
12
  Classifier: Programming Language :: Python :: 3.14
13
- Provides-Extra: images
14
13
  Requires-Dist: click (>=8.1.7,<9.0.0)
15
- Requires-Dist: emdash-core (>=0.1.30)
14
+ Requires-Dist: emdash-core (>=0.1.60)
16
15
  Requires-Dist: httpx (>=0.25.0)
17
- Requires-Dist: pillow (>=10.0.0) ; extra == "images"
18
16
  Requires-Dist: prompt_toolkit (>=3.0.43,<4.0.0)
19
17
  Requires-Dist: rich (>=13.7.0)
@@ -0,0 +1,23 @@
1
+ """EmDash CLI - Command-line interface for code intelligence."""
2
+
3
+ from importlib.metadata import version, PackageNotFoundError
4
+ from pathlib import Path
5
+
6
+ # Load .env files early so env vars are available for server subprocess
7
+ try:
8
+ from dotenv import load_dotenv
9
+ # Try to find .env in current dir or parent dirs
10
+ current = Path.cwd()
11
+ for _ in range(5):
12
+ env_path = current / ".env"
13
+ if env_path.exists():
14
+ load_dotenv(env_path, override=True)
15
+ break
16
+ current = current.parent
17
+ except ImportError:
18
+ pass # dotenv not installed
19
+
20
+ try:
21
+ __version__ = version("emdash-cli")
22
+ except PackageNotFoundError:
23
+ __version__ = "0.0.0-dev"
@@ -53,6 +53,7 @@ class EmdashClient:
53
53
  max_iterations: int = _get_max_iterations(),
54
54
  options: Optional[dict] = None,
55
55
  images: Optional[list[dict]] = None,
56
+ history: Optional[list[dict]] = None,
56
57
  ) -> Iterator[str]:
57
58
  """Stream agent chat response via SSE.
58
59
 
@@ -63,6 +64,7 @@ class EmdashClient:
63
64
  max_iterations: Max agent iterations
64
65
  options: Additional options (mode, save, no_graph_tools, etc.)
65
66
  images: List of images [{"data": base64_str, "format": "png"}]
67
+ history: Pre-loaded conversation history from saved session
66
68
 
67
69
  Yields:
68
70
  SSE lines from the response
@@ -91,6 +93,8 @@ class EmdashClient:
91
93
  payload["session_id"] = session_id
92
94
  if images:
93
95
  payload["images"] = images
96
+ if history:
97
+ payload["history"] = history
94
98
 
95
99
  try:
96
100
  with self._client.stream(
@@ -138,6 +142,135 @@ class EmdashClient:
138
142
  # Stream was closed early (interrupted)
139
143
  pass
140
144
 
145
+ def plan_approve_stream(self, session_id: str) -> Iterator[str]:
146
+ """Approve a pending plan and start implementation.
147
+
148
+ Args:
149
+ session_id: Session ID with pending plan
150
+
151
+ Yields:
152
+ SSE lines from the response
153
+ """
154
+ try:
155
+ with self._client.stream(
156
+ "POST",
157
+ f"{self.base_url}/api/agent/chat/{session_id}/plan/approve",
158
+ ) as response:
159
+ response.raise_for_status()
160
+ for line in response.iter_lines():
161
+ yield line
162
+ except GeneratorExit:
163
+ pass
164
+
165
+ def plan_reject_stream(self, session_id: str, feedback: str = "") -> Iterator[str]:
166
+ """Reject a pending plan with feedback.
167
+
168
+ Args:
169
+ session_id: Session ID with pending plan
170
+ feedback: Feedback explaining rejection
171
+
172
+ Yields:
173
+ SSE lines from the response
174
+ """
175
+ try:
176
+ with self._client.stream(
177
+ "POST",
178
+ f"{self.base_url}/api/agent/chat/{session_id}/plan/reject",
179
+ params={"feedback": feedback},
180
+ ) as response:
181
+ response.raise_for_status()
182
+ for line in response.iter_lines():
183
+ yield line
184
+ except GeneratorExit:
185
+ pass
186
+
187
+ def planmode_approve_stream(self, session_id: str) -> Iterator[str]:
188
+ """Approve entering plan mode.
189
+
190
+ Args:
191
+ session_id: Session ID requesting plan mode
192
+
193
+ Yields:
194
+ SSE lines from the response
195
+ """
196
+ try:
197
+ with self._client.stream(
198
+ "POST",
199
+ f"{self.base_url}/api/agent/chat/{session_id}/planmode/approve",
200
+ ) as response:
201
+ response.raise_for_status()
202
+ for line in response.iter_lines():
203
+ yield line
204
+ except GeneratorExit:
205
+ pass
206
+
207
+ def planmode_reject_stream(self, session_id: str, feedback: str = "") -> Iterator[str]:
208
+ """Reject entering plan mode.
209
+
210
+ Args:
211
+ session_id: Session ID requesting plan mode
212
+ feedback: Feedback explaining rejection
213
+
214
+ Yields:
215
+ SSE lines from the response
216
+ """
217
+ try:
218
+ with self._client.stream(
219
+ "POST",
220
+ f"{self.base_url}/api/agent/chat/{session_id}/planmode/reject",
221
+ params={"feedback": feedback},
222
+ ) as response:
223
+ response.raise_for_status()
224
+ for line in response.iter_lines():
225
+ yield line
226
+ except GeneratorExit:
227
+ pass
228
+
229
+ def clarification_answer_stream(self, session_id: str, answer: str) -> Iterator[str]:
230
+ """Answer a pending clarification question.
231
+
232
+ Args:
233
+ session_id: Session ID with pending clarification
234
+ answer: User's answer to the clarification question
235
+
236
+ Yields:
237
+ SSE lines from the response
238
+ """
239
+ try:
240
+ with self._client.stream(
241
+ "POST",
242
+ f"{self.base_url}/api/agent/chat/{session_id}/clarification/answer",
243
+ params={"answer": answer},
244
+ ) as response:
245
+ response.raise_for_status()
246
+ for line in response.iter_lines():
247
+ yield line
248
+ except GeneratorExit:
249
+ pass
250
+
251
+ def get(self, path: str) -> "httpx.Response":
252
+ """Make a GET request to the API.
253
+
254
+ Args:
255
+ path: API path (e.g., "/api/agent/sessions")
256
+
257
+ Returns:
258
+ HTTP response
259
+ """
260
+ return self._client.get(f"{self.base_url}{path}")
261
+
262
+ def post(self, path: str, json: dict | None = None) -> "httpx.Response":
263
+ """Make a POST request to the API.
264
+
265
+ Args:
266
+ path: API path (e.g., "/api/agent/chat/123/compact")
267
+ json: Optional JSON body
268
+
269
+ Returns:
270
+ HTTP response
271
+ """
272
+ return self._client.post(f"{self.base_url}{path}", json=json)
273
+
141
274
  def list_sessions(self) -> list[dict]:
142
275
  """List active agent sessions.
143
276
 
@@ -539,31 +672,38 @@ class EmdashClient:
539
672
  response.raise_for_status()
540
673
  return response.json()
541
674
 
542
- # ==================== Swarm ====================
675
+ # ==================== Todos ====================
543
676
 
544
- def swarm_run_stream(
545
- self,
546
- tasks: list[str],
547
- model: Optional[str] = None,
548
- auto_merge: bool = False,
549
- ) -> Iterator[str]:
550
- """Run multi-agent swarm with SSE streaming."""
551
- payload = {"tasks": tasks, "auto_merge": auto_merge}
552
- if model:
553
- payload["model"] = model
677
+ def get_todos(self, session_id: str) -> dict:
678
+ """Get the current todo list for a session.
554
679
 
555
- with self._client.stream(
556
- "POST",
557
- f"{self.base_url}/api/swarm/run",
558
- json=payload,
559
- ) as response:
560
- response.raise_for_status()
561
- for line in response.iter_lines():
562
- yield line
680
+ Args:
681
+ session_id: Session ID
563
682
 
564
- def swarm_status(self) -> dict:
565
- """Get swarm execution status."""
566
- response = self._client.get(f"{self.base_url}/api/swarm/status")
683
+ Returns:
684
+ Dict with todos list and summary
685
+ """
686
+ response = self._client.get(
687
+ f"{self.base_url}/api/agent/chat/{session_id}/todos"
688
+ )
689
+ response.raise_for_status()
690
+ return response.json()
691
+
692
+ def add_todo(self, session_id: str, title: str, description: str = "") -> dict:
693
+ """Add a new todo item to the agent's task list.
694
+
695
+ Args:
696
+ session_id: Session ID
697
+ title: Todo title
698
+ description: Optional description
699
+
700
+ Returns:
701
+ Dict with created task info
702
+ """
703
+ response = self._client.post(
704
+ f"{self.base_url}/api/agent/chat/{session_id}/todos",
705
+ params={"title": title, "description": description},
706
+ )
567
707
  response.raise_for_status()
568
708
  return response.json()
569
709
 
@@ -0,0 +1,92 @@
1
+ """Clipboard utilities for image handling.
2
+
3
+ Uses platform-native clipboard access (no Pillow dependency).
4
+ """
5
+
6
+ import base64
7
+ from typing import Optional, Tuple
8
+
9
+ from emdash_core.utils.image import (
10
+ read_clipboard_image,
11
+ is_clipboard_image_available,
12
+ get_image_info,
13
+ ClipboardImageError,
14
+ )
15
+
16
+
17
+ def get_clipboard_image() -> Optional[Tuple[str, str]]:
18
+ """Get image from clipboard if available.
19
+
20
+ Returns:
21
+ Tuple of (base64_data, format) if image found, None otherwise.
22
+ """
23
+ try:
24
+ if not is_clipboard_image_available():
25
+ return None
26
+
27
+ image_data = read_clipboard_image()
28
+ if image_data is None:
29
+ return None
30
+
31
+ # Encode to base64
32
+ base64_data = base64.b64encode(image_data).decode('utf-8')
33
+ return base64_data, 'png'
34
+
35
+ except ClipboardImageError:
36
+ return None
37
+ except Exception:
38
+ return None
39
+
40
+
41
+ def get_image_from_path(path: str) -> Optional[Tuple[str, str]]:
42
+ """Load image from file path.
43
+
44
+ Only PNG files are fully supported. Other formats will be read as raw bytes.
45
+
46
+ Args:
47
+ path: Path to image file
48
+
49
+ Returns:
50
+ Tuple of (base64_data, format) if successful, None otherwise.
51
+ """
52
+ try:
53
+ with open(path, 'rb') as f:
54
+ image_data = f.read()
55
+
56
+ # Determine format from file extension
57
+ ext = path.lower().split('.')[-1]
58
+ if ext in ('jpg', 'jpeg'):
59
+ img_format = 'jpeg'
60
+ elif ext == 'png':
61
+ img_format = 'png'
62
+ elif ext == 'gif':
63
+ img_format = 'gif'
64
+ elif ext == 'webp':
65
+ img_format = 'webp'
66
+ else:
67
+ img_format = 'png'
68
+
69
+ base64_data = base64.b64encode(image_data).decode('utf-8')
70
+ return base64_data, img_format
71
+
72
+ except Exception:
73
+ return None
74
+
75
+
76
+ def get_image_dimensions(base64_data: str) -> Optional[Tuple[int, int]]:
77
+ """Get dimensions of base64-encoded PNG image.
78
+
79
+ Args:
80
+ base64_data: Base64-encoded image data
81
+
82
+ Returns:
83
+ Tuple of (width, height) if successful, None otherwise.
84
+ """
85
+ try:
86
+ image_bytes = base64.b64decode(base64_data)
87
+ info = get_image_info(image_bytes)
88
+ if info.get("width") and info.get("height"):
89
+ return info["width"], info["height"]
90
+ return None
91
+ except Exception:
92
+ return None
@@ -7,12 +7,12 @@ from .analyze import analyze
7
7
  from .embed import embed
8
8
  from .index import index
9
9
  from .plan import plan
10
+ from .registry import registry
10
11
  from .rules import rules
11
12
  from .search import search
12
13
  from .server import server
13
14
  from .skills import skills
14
15
  from .team import team
15
- from .swarm import swarm
16
16
  from .projectmd import projectmd
17
17
  from .research import research
18
18
  from .spec import spec
@@ -26,12 +26,12 @@ __all__ = [
26
26
  "embed",
27
27
  "index",
28
28
  "plan",
29
+ "registry",
29
30
  "rules",
30
31
  "search",
31
32
  "server",
32
33
  "skills",
33
34
  "team",
34
- "swarm",
35
35
  "projectmd",
36
36
  "research",
37
37
  "spec",
@@ -0,0 +1,14 @@
1
+ """Agent CLI commands package.
2
+
3
+ This package contains the refactored agent CLI code, split into:
4
+ - cli.py: Click command definitions
5
+ - constants.py: Enums and constants
6
+ - file_utils.py: File reference expansion utilities
7
+ - menus.py: Interactive prompt_toolkit menus
8
+ - interactive.py: Main REPL loop
9
+ - handlers/: Slash command handlers
10
+ """
11
+
12
+ from .cli import agent, agent_code
13
+
14
+ __all__ = ["agent", "agent_code"]
@@ -0,0 +1,100 @@
1
+ """Click CLI commands for the agent."""
2
+
3
+ import os
4
+
5
+ import click
6
+ from rich.console import Console
7
+
8
+ from ...client import EmdashClient
9
+ from ...server_manager import get_server_manager
10
+ from ...sse_renderer import SSERenderer
11
+ from .interactive import run_interactive, run_single_task
12
+
13
+ console = Console()
14
+
15
+
16
+ @click.group()
17
+ def agent():
18
+ """AI agent commands."""
19
+ pass
20
+
21
+
22
+ @agent.command("code")
23
+ @click.argument("task", required=False)
24
+ @click.option("--model", "-m", default=None, help="Model to use")
25
+ @click.option("--mode", type=click.Choice(["plan", "code"]), default="code",
26
+ help="Starting mode")
27
+ @click.option("--quiet", "-q", is_flag=True, help="Less verbose output")
28
+ @click.option("--max-iterations", default=int(os.getenv("EMDASH_MAX_ITERATIONS", "100")), help="Max agent iterations")
29
+ @click.option("--no-graph-tools", is_flag=True, help="Skip graph exploration tools")
30
+ @click.option("--save", is_flag=True, help="Save specs to specs/<feature>/")
31
+ def agent_code(
32
+ task: str | None,
33
+ model: str | None,
34
+ mode: str,
35
+ quiet: bool,
36
+ max_iterations: int,
37
+ no_graph_tools: bool,
38
+ save: bool,
39
+ ):
40
+ """Start the coding agent.
41
+
42
+ With TASK: Run single task and exit
43
+ Without TASK: Start interactive REPL mode
44
+
45
+ MODES:
46
+ plan - Explore codebase and create plans (read-only)
47
+ code - Execute code changes (default)
48
+
49
+ SLASH COMMANDS (in interactive mode):
50
+ /plan - Switch to plan mode
51
+ /code - Switch to code mode
52
+ /help - Show available commands
53
+ /reset - Reset session
54
+
55
+ Examples:
56
+ emdash # Interactive code mode
57
+ emdash agent code # Same as above
58
+ emdash agent code --mode plan # Start in plan mode
59
+ emdash agent code "Fix the login bug" # Single task
60
+ """
61
+ # Get server URL (starts server if needed)
62
+ server = get_server_manager()
63
+ base_url = server.get_server_url()
64
+
65
+ client = EmdashClient(base_url)
66
+ renderer = SSERenderer(console=console, verbose=not quiet)
67
+
68
+ options = {
69
+ "mode": mode,
70
+ "no_graph_tools": no_graph_tools,
71
+ "save": save,
72
+ }
73
+
74
+ if task:
75
+ # Single task mode
76
+ run_single_task(client, renderer, task, model, max_iterations, options)
77
+ else:
78
+ # Interactive REPL mode
79
+ run_interactive(client, renderer, model, max_iterations, options)
80
+
81
+
82
+ @agent.command("sessions")
83
+ def list_sessions():
84
+ """List active agent sessions."""
85
+ server = get_server_manager()
86
+ base_url = server.get_server_url()
87
+
88
+ client = EmdashClient(base_url)
89
+ sessions = client.list_sessions()
90
+
91
+ if not sessions:
92
+ console.print("[dim]No active sessions[/dim]")
93
+ return
94
+
95
+ for s in sessions:
96
+ console.print(
97
+ f" {s['session_id'][:8]}... "
98
+ f"[dim]({s.get('model', 'unknown')}, "
99
+ f"{s.get('message_count', 0)} messages)[/dim]"
100
+ )
@@ -0,0 +1,61 @@
1
+ """Constants and enums for the agent CLI."""
2
+
3
+ from enum import Enum
4
+
5
+
6
+ class AgentMode(Enum):
7
+ """Agent operation modes."""
8
+ PLAN = "plan"
9
+ CODE = "code"
10
+
11
+
12
+ # Slash commands available in interactive mode
13
+ SLASH_COMMANDS = {
14
+ # Mode switching
15
+ "/plan": "Switch to plan mode (explore codebase, create plans)",
16
+ "/code": "Switch to code mode (execute file changes)",
17
+ "/mode": "Show current mode",
18
+ # Generation commands
19
+ "/pr [url]": "Review a pull request",
20
+ "/projectmd": "Generate PROJECT.md for the codebase",
21
+ "/research [goal]": "Deep research on a topic",
22
+ # Status commands
23
+ "/status": "Show index and PROJECT.md status",
24
+ "/diff": "Show uncommitted changes in GitHub-style diff view",
25
+ "/agents": "Manage agents (interactive menu, or /agents [create|show|edit|delete] <name>)",
26
+ # Todo management
27
+ "/todos": "Show current agent todo list",
28
+ "/todo-add [title]": "Add a todo item for the agent (e.g., /todo-add Fix tests)",
29
+ # Session management
30
+ "/session": "Save, load, or list sessions (e.g., /session save my-task)",
31
+ "/spec": "Show current specification",
32
+ "/reset": "Reset session state",
33
+ # Hooks
34
+ "/hooks": "Manage hooks (list, add, remove, toggle)",
35
+ # Rules
36
+ "/rules": "Manage rules (list, add, delete)",
37
+ # Skills
38
+ "/skills": "Manage skills (list, show, add, delete)",
39
+ # Index
40
+ "/index": "Manage codebase index (status, start, hook install/uninstall)",
41
+ # MCP
42
+ "/mcp": "Manage global MCP servers (list, edit)",
43
+ # Registry
44
+ "/registry": "Browse and install community skills, rules, agents, verifiers",
45
+ # Auth
46
+ "/auth": "GitHub authentication (login, logout, status)",
47
+ # Context
48
+ "/context": "Show current context frame (tokens, reranked items)",
49
+ "/compact": "Compact message history using LLM summarization",
50
+ # Image
51
+ "/paste": "Attach image from clipboard (or use Ctrl+V)",
52
+ # Diagnostics
53
+ "/doctor": "Check Python environment and diagnose issues",
54
+ # Verification
55
+ "/verify": "Run verification checks on current work",
56
+ "/verify-loop [task]": "Run task in loop until verifications pass",
57
+ # Setup wizard
58
+ "/setup": "Setup wizard for rules, agents, skills, and verifiers",
59
+ "/help": "Show available commands",
60
+ "/quit": "Exit the agent",
61
+ }