emdash-cli 0.1.4__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.
- emdash_cli/__init__.py +3 -0
- emdash_cli/client.py +556 -0
- emdash_cli/commands/__init__.py +37 -0
- emdash_cli/commands/agent.py +883 -0
- emdash_cli/commands/analyze.py +137 -0
- emdash_cli/commands/auth.py +121 -0
- emdash_cli/commands/db.py +95 -0
- emdash_cli/commands/embed.py +103 -0
- emdash_cli/commands/index.py +134 -0
- emdash_cli/commands/plan.py +77 -0
- emdash_cli/commands/projectmd.py +51 -0
- emdash_cli/commands/research.py +47 -0
- emdash_cli/commands/rules.py +93 -0
- emdash_cli/commands/search.py +56 -0
- emdash_cli/commands/server.py +117 -0
- emdash_cli/commands/spec.py +49 -0
- emdash_cli/commands/swarm.py +86 -0
- emdash_cli/commands/tasks.py +52 -0
- emdash_cli/commands/team.py +51 -0
- emdash_cli/main.py +104 -0
- emdash_cli/server_manager.py +231 -0
- emdash_cli/sse_renderer.py +442 -0
- emdash_cli-0.1.4.dist-info/METADATA +17 -0
- emdash_cli-0.1.4.dist-info/RECORD +26 -0
- emdash_cli-0.1.4.dist-info/WHEEL +4 -0
- emdash_cli-0.1.4.dist-info/entry_points.txt +5 -0
|
@@ -0,0 +1,442 @@
|
|
|
1
|
+
"""SSE event renderer for Rich terminal output."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
import sys
|
|
5
|
+
import time
|
|
6
|
+
from typing import Iterator, Optional
|
|
7
|
+
|
|
8
|
+
from rich.console import Console
|
|
9
|
+
from rich.markdown import Markdown
|
|
10
|
+
from rich.panel import Panel
|
|
11
|
+
from rich.text import Text
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# Spinner frames for loading animation
|
|
15
|
+
SPINNER_FRAMES = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class SSERenderer:
|
|
19
|
+
"""Renders SSE events to Rich terminal output with live updates.
|
|
20
|
+
|
|
21
|
+
Features:
|
|
22
|
+
- Animated spinner while tools execute
|
|
23
|
+
- Special UI for spawning sub-agents
|
|
24
|
+
- Clean, minimal output
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
def __init__(
|
|
28
|
+
self,
|
|
29
|
+
console: Optional[Console] = None,
|
|
30
|
+
verbose: bool = True,
|
|
31
|
+
):
|
|
32
|
+
"""Initialize the renderer.
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
console: Rich console to render to (creates one if not provided)
|
|
36
|
+
verbose: Whether to show tool calls and progress
|
|
37
|
+
"""
|
|
38
|
+
self.console = console or Console()
|
|
39
|
+
self.verbose = verbose
|
|
40
|
+
self._partial_response = ""
|
|
41
|
+
self._session_id = None
|
|
42
|
+
self._spec = None
|
|
43
|
+
self._spec_submitted = False
|
|
44
|
+
self._pending_clarification = None
|
|
45
|
+
|
|
46
|
+
# Live display state
|
|
47
|
+
self._current_tool = None
|
|
48
|
+
self._tool_count = 0
|
|
49
|
+
self._completed_tools: list[dict] = []
|
|
50
|
+
self._spinner_idx = 0
|
|
51
|
+
self._waiting_for_next = False
|
|
52
|
+
|
|
53
|
+
# Sub-agent state (for inline updates)
|
|
54
|
+
self._subagent_tool_count = 0
|
|
55
|
+
self._subagent_current_tool = None
|
|
56
|
+
|
|
57
|
+
def render_stream(self, lines: Iterator[str]) -> dict:
|
|
58
|
+
"""Render SSE stream to terminal.
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
lines: Iterator of SSE lines from HTTP response
|
|
62
|
+
|
|
63
|
+
Returns:
|
|
64
|
+
Dict with session_id, content, spec, and other metadata
|
|
65
|
+
"""
|
|
66
|
+
current_event = None
|
|
67
|
+
final_response = ""
|
|
68
|
+
|
|
69
|
+
for line in lines:
|
|
70
|
+
line = line.strip()
|
|
71
|
+
|
|
72
|
+
if line.startswith("event: "):
|
|
73
|
+
current_event = line[7:]
|
|
74
|
+
elif line.startswith("data: "):
|
|
75
|
+
try:
|
|
76
|
+
data = json.loads(line[6:])
|
|
77
|
+
# Ensure data is a dict (could be null/None from JSON)
|
|
78
|
+
if data is None:
|
|
79
|
+
data = {}
|
|
80
|
+
if current_event:
|
|
81
|
+
result = self._handle_event(current_event, data)
|
|
82
|
+
if result:
|
|
83
|
+
final_response = result
|
|
84
|
+
except json.JSONDecodeError:
|
|
85
|
+
pass
|
|
86
|
+
elif line == ": ping":
|
|
87
|
+
# SSE keep-alive - show loading if waiting
|
|
88
|
+
if self._waiting_for_next and self.verbose:
|
|
89
|
+
self._show_waiting()
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
"content": final_response,
|
|
93
|
+
"session_id": self._session_id,
|
|
94
|
+
"spec": self._spec,
|
|
95
|
+
"spec_submitted": self._spec_submitted,
|
|
96
|
+
"clarification": self._pending_clarification,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
def _show_waiting(self) -> None:
|
|
100
|
+
"""Show waiting animation."""
|
|
101
|
+
self._spinner_idx = (self._spinner_idx + 1) % len(SPINNER_FRAMES)
|
|
102
|
+
spinner = SPINNER_FRAMES[self._spinner_idx]
|
|
103
|
+
# Use carriage return to update in place
|
|
104
|
+
sys.stdout.write(f"\r [dim]{spinner}[/dim] waiting...")
|
|
105
|
+
sys.stdout.flush()
|
|
106
|
+
|
|
107
|
+
def _clear_waiting(self) -> None:
|
|
108
|
+
"""Clear waiting line."""
|
|
109
|
+
if self._waiting_for_next:
|
|
110
|
+
sys.stdout.write("\r" + " " * 40 + "\r")
|
|
111
|
+
sys.stdout.flush()
|
|
112
|
+
self._waiting_for_next = False
|
|
113
|
+
|
|
114
|
+
def _handle_event(self, event_type: str, data: dict) -> Optional[str]:
|
|
115
|
+
"""Handle individual SSE event."""
|
|
116
|
+
# Ensure data is a dict
|
|
117
|
+
if not isinstance(data, dict):
|
|
118
|
+
data = {}
|
|
119
|
+
|
|
120
|
+
# Clear waiting indicator when new event arrives
|
|
121
|
+
self._clear_waiting()
|
|
122
|
+
|
|
123
|
+
if event_type == "session_start":
|
|
124
|
+
self._render_session_start(data)
|
|
125
|
+
elif event_type == "tool_start":
|
|
126
|
+
self._render_tool_start(data)
|
|
127
|
+
elif event_type == "tool_result":
|
|
128
|
+
self._render_tool_result(data)
|
|
129
|
+
# Set waiting for next tool/response
|
|
130
|
+
self._waiting_for_next = True
|
|
131
|
+
elif event_type == "thinking":
|
|
132
|
+
self._render_thinking(data)
|
|
133
|
+
elif event_type == "progress":
|
|
134
|
+
self._render_progress(data)
|
|
135
|
+
elif event_type == "partial_response":
|
|
136
|
+
self._render_partial(data)
|
|
137
|
+
elif event_type == "response":
|
|
138
|
+
return self._render_response(data)
|
|
139
|
+
elif event_type == "clarification":
|
|
140
|
+
self._render_clarification(data)
|
|
141
|
+
elif event_type == "error":
|
|
142
|
+
self._render_error(data)
|
|
143
|
+
elif event_type == "warning":
|
|
144
|
+
self._render_warning(data)
|
|
145
|
+
elif event_type == "session_end":
|
|
146
|
+
self._render_session_end(data)
|
|
147
|
+
|
|
148
|
+
return None
|
|
149
|
+
|
|
150
|
+
def _render_session_start(self, data: dict) -> None:
|
|
151
|
+
"""Render session start event."""
|
|
152
|
+
if data.get("session_id"):
|
|
153
|
+
self._session_id = data["session_id"]
|
|
154
|
+
|
|
155
|
+
if not self.verbose:
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
agent = data.get("agent_name", "Agent")
|
|
159
|
+
model = data.get("model", "unknown")
|
|
160
|
+
|
|
161
|
+
# Extract model name from full path
|
|
162
|
+
if "/" in model:
|
|
163
|
+
model = model.split("/")[-1]
|
|
164
|
+
|
|
165
|
+
self.console.print()
|
|
166
|
+
self.console.print(f"[bold cyan]{agent}[/bold cyan] [dim]({model})[/dim]")
|
|
167
|
+
self._tool_count = 0
|
|
168
|
+
self._completed_tools = []
|
|
169
|
+
|
|
170
|
+
def _render_tool_start(self, data: dict) -> None:
|
|
171
|
+
"""Render tool start event."""
|
|
172
|
+
if not self.verbose:
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
name = data.get("name", "unknown")
|
|
176
|
+
args = data.get("args", {})
|
|
177
|
+
subagent_id = data.get("subagent_id")
|
|
178
|
+
subagent_type = data.get("subagent_type")
|
|
179
|
+
|
|
180
|
+
self._tool_count += 1
|
|
181
|
+
self._current_tool = {"name": name, "args": args, "start_time": time.time()}
|
|
182
|
+
|
|
183
|
+
# Special handling for task tool (spawning sub-agents)
|
|
184
|
+
if name == "task":
|
|
185
|
+
self._render_agent_spawn_start(args)
|
|
186
|
+
return
|
|
187
|
+
|
|
188
|
+
# Sub-agent events: update in place on single line
|
|
189
|
+
if subagent_id:
|
|
190
|
+
self._subagent_tool_count += 1
|
|
191
|
+
self._subagent_current_tool = name
|
|
192
|
+
self._render_subagent_progress(subagent_type or "Agent", name, args)
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
# Format args summary (compact)
|
|
196
|
+
args_summary = self._format_args_summary(args)
|
|
197
|
+
|
|
198
|
+
# Show spinner with tool name
|
|
199
|
+
spinner = SPINNER_FRAMES[0]
|
|
200
|
+
self.console.print(
|
|
201
|
+
f" [dim]┃[/dim] [yellow]{spinner}[/yellow] [bold]{name}[/bold] {args_summary}",
|
|
202
|
+
end="\r"
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
def _render_subagent_progress(self, agent_type: str, tool_name: str, args: dict) -> None:
|
|
206
|
+
"""Render sub-agent progress on a single updating line."""
|
|
207
|
+
self._spinner_idx = (self._spinner_idx + 1) % len(SPINNER_FRAMES)
|
|
208
|
+
spinner = SPINNER_FRAMES[self._spinner_idx]
|
|
209
|
+
|
|
210
|
+
# Get a short summary of what's being done
|
|
211
|
+
summary = ""
|
|
212
|
+
if "path" in args:
|
|
213
|
+
path = str(args["path"])
|
|
214
|
+
# Shorten long paths
|
|
215
|
+
if len(path) > 40:
|
|
216
|
+
summary = "..." + path[-37:]
|
|
217
|
+
else:
|
|
218
|
+
summary = path
|
|
219
|
+
elif "pattern" in args:
|
|
220
|
+
summary = str(args["pattern"])[:30]
|
|
221
|
+
|
|
222
|
+
# Clear line and show progress
|
|
223
|
+
line = f" [dim]│[/dim] [yellow]{spinner}[/yellow] [dim cyan]({agent_type})[/dim cyan] {self._subagent_tool_count} tools... [bold]{tool_name}[/bold] [dim]{summary}[/dim]"
|
|
224
|
+
# Pad to clear previous content
|
|
225
|
+
sys.stdout.write(f"\r{' ' * 120}\r")
|
|
226
|
+
self.console.print(line, end="")
|
|
227
|
+
|
|
228
|
+
def _render_agent_spawn_start(self, args: dict) -> None:
|
|
229
|
+
"""Render sub-agent spawn start with special UI."""
|
|
230
|
+
agent_type = args.get("subagent_type", "Explore")
|
|
231
|
+
description = args.get("description", "")
|
|
232
|
+
prompt = args.get("prompt", "")
|
|
233
|
+
|
|
234
|
+
# Truncate prompt for display
|
|
235
|
+
prompt_display = prompt[:60] + "..." if len(prompt) > 60 else prompt
|
|
236
|
+
|
|
237
|
+
self.console.print()
|
|
238
|
+
self.console.print(
|
|
239
|
+
f" [bold magenta]◆ Spawning {agent_type} Agent[/bold magenta]"
|
|
240
|
+
)
|
|
241
|
+
if description:
|
|
242
|
+
self.console.print(f" [dim]{description}[/dim]")
|
|
243
|
+
self.console.print(f" [cyan]→[/cyan] {prompt_display}")
|
|
244
|
+
|
|
245
|
+
def _render_tool_result(self, data: dict) -> None:
|
|
246
|
+
"""Render tool result event."""
|
|
247
|
+
name = data.get("name", "unknown")
|
|
248
|
+
success = data.get("success", True)
|
|
249
|
+
summary = data.get("summary")
|
|
250
|
+
subagent_id = data.get("subagent_id")
|
|
251
|
+
|
|
252
|
+
# Detect spec submission
|
|
253
|
+
if name == "submit_spec" and success:
|
|
254
|
+
self._spec_submitted = True
|
|
255
|
+
spec_data = data.get("data", {})
|
|
256
|
+
if spec_data:
|
|
257
|
+
self._spec = spec_data.get("content")
|
|
258
|
+
|
|
259
|
+
if not self.verbose:
|
|
260
|
+
return
|
|
261
|
+
|
|
262
|
+
# Special handling for task tool result
|
|
263
|
+
if name == "task":
|
|
264
|
+
self._render_agent_spawn_result(data)
|
|
265
|
+
return
|
|
266
|
+
|
|
267
|
+
# Sub-agent events: don't print result lines, just keep updating progress
|
|
268
|
+
if subagent_id:
|
|
269
|
+
# Progress is already shown by _render_tool_start, nothing to do here
|
|
270
|
+
return
|
|
271
|
+
|
|
272
|
+
# Calculate duration
|
|
273
|
+
duration = ""
|
|
274
|
+
if self._current_tool and self._current_tool.get("start_time"):
|
|
275
|
+
elapsed = time.time() - self._current_tool["start_time"]
|
|
276
|
+
if elapsed >= 0.1:
|
|
277
|
+
duration = f" [dim]{elapsed:.1f}s[/dim]"
|
|
278
|
+
|
|
279
|
+
args_summary = ""
|
|
280
|
+
if self._current_tool:
|
|
281
|
+
args_summary = self._format_args_summary(self._current_tool.get("args", {}))
|
|
282
|
+
|
|
283
|
+
if success:
|
|
284
|
+
status_icon = "[green]✓[/green]"
|
|
285
|
+
result_text = f"[dim]{summary}[/dim]" if summary else ""
|
|
286
|
+
else:
|
|
287
|
+
status_icon = "[red]✗[/red]"
|
|
288
|
+
result_text = f"[red]{summary}[/red]" if summary else "[red]failed[/red]"
|
|
289
|
+
|
|
290
|
+
# Overwrite the spinner line
|
|
291
|
+
self.console.print(
|
|
292
|
+
f" [dim]┃[/dim] {status_icon} [bold]{name}[/bold] {args_summary}{duration} {result_text}"
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
self._completed_tools.append({
|
|
296
|
+
"name": name,
|
|
297
|
+
"success": success,
|
|
298
|
+
"summary": summary,
|
|
299
|
+
})
|
|
300
|
+
self._current_tool = None
|
|
301
|
+
|
|
302
|
+
def _render_agent_spawn_result(self, data: dict) -> None:
|
|
303
|
+
"""Render sub-agent spawn result with special UI."""
|
|
304
|
+
success = data.get("success", True)
|
|
305
|
+
result_data = data.get("data") or {}
|
|
306
|
+
|
|
307
|
+
# Calculate duration
|
|
308
|
+
duration = ""
|
|
309
|
+
if self._current_tool and self._current_tool.get("start_time"):
|
|
310
|
+
elapsed = time.time() - self._current_tool["start_time"]
|
|
311
|
+
duration = f" [dim]({elapsed:.1f}s)[/dim]"
|
|
312
|
+
|
|
313
|
+
if success:
|
|
314
|
+
agent_type = result_data.get("agent_type", "Agent")
|
|
315
|
+
iterations = result_data.get("iterations", 0)
|
|
316
|
+
tools_used = result_data.get("tools_used", [])
|
|
317
|
+
files_count = len(result_data.get("files_explored", []))
|
|
318
|
+
|
|
319
|
+
self.console.print(
|
|
320
|
+
f" [green]✓[/green] {agent_type} completed{duration}"
|
|
321
|
+
)
|
|
322
|
+
if iterations > 0 or files_count > 0:
|
|
323
|
+
stats = []
|
|
324
|
+
if iterations > 0:
|
|
325
|
+
stats.append(f"{iterations} turns")
|
|
326
|
+
if files_count > 0:
|
|
327
|
+
stats.append(f"{files_count} files")
|
|
328
|
+
if tools_used:
|
|
329
|
+
stats.append(f"{len(tools_used)} tools")
|
|
330
|
+
self.console.print(f" [dim]{' · '.join(stats)}[/dim]")
|
|
331
|
+
else:
|
|
332
|
+
error = result_data.get("error", data.get("summary", "failed"))
|
|
333
|
+
self.console.print(f" [red]✗[/red] Agent failed: {error}")
|
|
334
|
+
|
|
335
|
+
self.console.print()
|
|
336
|
+
self._current_tool = None
|
|
337
|
+
|
|
338
|
+
def _format_args_summary(self, args: dict) -> str:
|
|
339
|
+
"""Format args into a compact summary string."""
|
|
340
|
+
if not args:
|
|
341
|
+
return ""
|
|
342
|
+
|
|
343
|
+
parts = []
|
|
344
|
+
for k, v in list(args.items())[:2]:
|
|
345
|
+
v_str = str(v)
|
|
346
|
+
if len(v_str) > 40:
|
|
347
|
+
v_str = v_str[:37] + "..."
|
|
348
|
+
parts.append(f"[dim]{v_str}[/dim]")
|
|
349
|
+
|
|
350
|
+
return " ".join(parts)
|
|
351
|
+
|
|
352
|
+
def _render_thinking(self, data: dict) -> None:
|
|
353
|
+
"""Render thinking event."""
|
|
354
|
+
if not self.verbose:
|
|
355
|
+
return
|
|
356
|
+
|
|
357
|
+
message = data.get("message", "")
|
|
358
|
+
self.console.print(f" [dim]┃[/dim] [dim italic]💭 {message}[/dim italic]")
|
|
359
|
+
|
|
360
|
+
def _render_progress(self, data: dict) -> None:
|
|
361
|
+
"""Render progress event."""
|
|
362
|
+
if not self.verbose:
|
|
363
|
+
return
|
|
364
|
+
|
|
365
|
+
message = data.get("message", "")
|
|
366
|
+
percent = data.get("percent")
|
|
367
|
+
|
|
368
|
+
if percent is not None:
|
|
369
|
+
bar_width = 20
|
|
370
|
+
filled = int(bar_width * percent / 100)
|
|
371
|
+
bar = "█" * filled + "░" * (bar_width - filled)
|
|
372
|
+
self.console.print(f" [dim]┃[/dim] [dim]{bar} {percent:.0f}% {message}[/dim]")
|
|
373
|
+
else:
|
|
374
|
+
self.console.print(f" [dim]┃[/dim] [dim]{message}[/dim]")
|
|
375
|
+
|
|
376
|
+
def _render_partial(self, data: dict) -> None:
|
|
377
|
+
"""Render partial response (streaming text)."""
|
|
378
|
+
content = data.get("content", "")
|
|
379
|
+
self._partial_response += content
|
|
380
|
+
|
|
381
|
+
def _render_response(self, data: dict) -> str:
|
|
382
|
+
"""Render final response."""
|
|
383
|
+
content = data.get("content", "")
|
|
384
|
+
|
|
385
|
+
self.console.print()
|
|
386
|
+
self.console.print(Markdown(content))
|
|
387
|
+
|
|
388
|
+
return content
|
|
389
|
+
|
|
390
|
+
def _render_clarification(self, data: dict) -> None:
|
|
391
|
+
"""Render clarification request."""
|
|
392
|
+
question = data.get("question", "")
|
|
393
|
+
context = data.get("context", "")
|
|
394
|
+
options = data.get("options", [])
|
|
395
|
+
|
|
396
|
+
self.console.print()
|
|
397
|
+
self.console.print(Panel(
|
|
398
|
+
question,
|
|
399
|
+
title="[yellow]❓ Question[/yellow]",
|
|
400
|
+
border_style="yellow",
|
|
401
|
+
padding=(0, 1),
|
|
402
|
+
))
|
|
403
|
+
|
|
404
|
+
if options:
|
|
405
|
+
for i, opt in enumerate(options, 1):
|
|
406
|
+
self.console.print(f" [yellow][{i}][/yellow] {opt}")
|
|
407
|
+
self.console.print()
|
|
408
|
+
|
|
409
|
+
self._pending_clarification = {
|
|
410
|
+
"question": question,
|
|
411
|
+
"context": context,
|
|
412
|
+
"options": options,
|
|
413
|
+
}
|
|
414
|
+
else:
|
|
415
|
+
self._pending_clarification = None
|
|
416
|
+
|
|
417
|
+
def _render_error(self, data: dict) -> None:
|
|
418
|
+
"""Render error event."""
|
|
419
|
+
message = data.get("message", "Unknown error")
|
|
420
|
+
details = data.get("details")
|
|
421
|
+
|
|
422
|
+
self.console.print(f"\n[red bold]✗ Error:[/red bold] {message}")
|
|
423
|
+
|
|
424
|
+
if details:
|
|
425
|
+
self.console.print(f"[dim]{details}[/dim]")
|
|
426
|
+
|
|
427
|
+
def _render_warning(self, data: dict) -> None:
|
|
428
|
+
"""Render warning event."""
|
|
429
|
+
message = data.get("message", "")
|
|
430
|
+
self.console.print(f"[yellow]⚠ {message}[/yellow]")
|
|
431
|
+
|
|
432
|
+
def _render_session_end(self, data: dict) -> None:
|
|
433
|
+
"""Render session end event."""
|
|
434
|
+
if not self.verbose:
|
|
435
|
+
return
|
|
436
|
+
|
|
437
|
+
success = data.get("success", True)
|
|
438
|
+
if not success:
|
|
439
|
+
error = data.get("error", "Unknown error")
|
|
440
|
+
self.console.print(f"\n[red]Session ended with error: {error}[/red]")
|
|
441
|
+
elif self._tool_count > 0:
|
|
442
|
+
self.console.print(f"\n[dim]───── {self._tool_count} tools ─────[/dim]")
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: emdash-cli
|
|
3
|
+
Version: 0.1.4
|
|
4
|
+
Summary: EmDash CLI - Command-line interface for code intelligence
|
|
5
|
+
Author: Em Dash Team
|
|
6
|
+
Requires-Python: >=3.10,<4.0
|
|
7
|
+
Classifier: Programming Language :: Python :: 3
|
|
8
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
9
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
13
|
+
Requires-Dist: click (>=8.1.7,<9.0.0)
|
|
14
|
+
Requires-Dist: emdash-core (>=0.1.4)
|
|
15
|
+
Requires-Dist: httpx (>=0.25.0)
|
|
16
|
+
Requires-Dist: prompt_toolkit (>=3.0.43,<4.0.0)
|
|
17
|
+
Requires-Dist: rich (>=13.7.0)
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
emdash_cli/__init__.py,sha256=q-eC58WAvKd48CbdQVJ5kSGA86_V18JmkE1qXkjEyjw,88
|
|
2
|
+
emdash_cli/client.py,sha256=7eWchJugC4xtKs8Ob63_kJMdnjyoN3be_1qP8qMujZE,16275
|
|
3
|
+
emdash_cli/commands/__init__.py,sha256=zf0lQ6S1UgoIg6NS7Oehxnb3iGD0Wxai0QU1nobgi9U,671
|
|
4
|
+
emdash_cli/commands/agent.py,sha256=HAz9exNc2iXiNu5OBJJRJrTKS1z31lkdDzVcIpfItAg,29618
|
|
5
|
+
emdash_cli/commands/analyze.py,sha256=c9ztbv0Ra7g2AlDmMOy-9L51fDVuoqbuzxnRfomoFIQ,4403
|
|
6
|
+
emdash_cli/commands/auth.py,sha256=SpWdqO1bJCgt4x1B4Pr7hNOucwTuBFJ1oGPOzXtvwZM,3816
|
|
7
|
+
emdash_cli/commands/db.py,sha256=nZK7gLDVE2lAQVYrMx6Swscml5OAtkbg-EcSNSvRIlA,2922
|
|
8
|
+
emdash_cli/commands/embed.py,sha256=kqP5jtYCsZ2_s_I1DjzIUgaod1VUvPiRO0jIIY0HtCs,3244
|
|
9
|
+
emdash_cli/commands/index.py,sha256=njVUEirFPTSsqAR0QRaS_rMKWBe4REBT4hBRWrNnYxI,4607
|
|
10
|
+
emdash_cli/commands/plan.py,sha256=BRiyIhfy_zz2PYy4Qo3a0t77GwHhdssZk6NImOkPi-w,2189
|
|
11
|
+
emdash_cli/commands/projectmd.py,sha256=4y4cn_yFw85jMUm52nGjpqnd-YWvs6ZNEMWJGeJC17Q,1605
|
|
12
|
+
emdash_cli/commands/research.py,sha256=xtI9_9emY7-rGQD5xJALTxtgTFmI4dplYW148dtTaTs,1553
|
|
13
|
+
emdash_cli/commands/rules.py,sha256=n85CCG0WNIBEsUK9STJetPmZxoypQtest5BGPsXl0ac,2712
|
|
14
|
+
emdash_cli/commands/search.py,sha256=DrSv_oN2xF1NaKCBICdyII7eupVRsDQ2ysW-TPSU0X0,1661
|
|
15
|
+
emdash_cli/commands/server.py,sha256=UTmLAVolT0krN9xCtMcCSvmQZ9k1QwpFFmXGg9BulRY,3459
|
|
16
|
+
emdash_cli/commands/spec.py,sha256=qafDmzKyRH035p3xTm_VTUsQLDZblIzIg-dxjEPv6tM,1494
|
|
17
|
+
emdash_cli/commands/swarm.py,sha256=s_cntuorNdtNNTD2Qs1p2IcHghMrBMOQuturPS3y9mM,2661
|
|
18
|
+
emdash_cli/commands/tasks.py,sha256=TdyunjSV5w7jpNFwv0fTL-_No5Fyvdm7Z2nXqxWSJec,1635
|
|
19
|
+
emdash_cli/commands/team.py,sha256=K1-IJg6iG-9HMF_3JmpNDlNs1PYbb-ThFHU9KU_jKRo,1430
|
|
20
|
+
emdash_cli/main.py,sha256=pxUzyh01R-FDETMRf8nrBvP9QW3DVgeTJZ1Vsradw-E,2502
|
|
21
|
+
emdash_cli/server_manager.py,sha256=RrLteSHUmcFV4cyHJAEmgM9qHru2mJS08QNLWno6Y3Y,7051
|
|
22
|
+
emdash_cli/sse_renderer.py,sha256=lm09h7a4zjahNvwEKkgxWWQjcB7i-k0v6OS4AWVpUSQ,15384
|
|
23
|
+
emdash_cli-0.1.4.dist-info/METADATA,sha256=_9lj3mUt5L3rZqbN4HA7Ctol5k1I0h7rZrCY5sUUmBQ,660
|
|
24
|
+
emdash_cli-0.1.4.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
25
|
+
emdash_cli-0.1.4.dist-info/entry_points.txt,sha256=31CuYD0k-tM8csFWDunc-JoZTxXaifj3oIXz4V0p6F0,122
|
|
26
|
+
emdash_cli-0.1.4.dist-info/RECORD,,
|