emdash-cli 0.1.4__py3-none-any.whl → 0.1.25__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 +6 -1
- emdash_cli/client.py +32 -18
- emdash_cli/commands/__init__.py +2 -0
- emdash_cli/commands/agent.py +171 -222
- emdash_cli/commands/index.py +76 -8
- emdash_cli/commands/skills.py +337 -0
- emdash_cli/keyboard.py +146 -0
- emdash_cli/main.py +5 -1
- emdash_cli/sse_renderer.py +265 -49
- {emdash_cli-0.1.4.dist-info → emdash_cli-0.1.25.dist-info}/METADATA +2 -2
- {emdash_cli-0.1.4.dist-info → emdash_cli-0.1.25.dist-info}/RECORD +13 -11
- {emdash_cli-0.1.4.dist-info → emdash_cli-0.1.25.dist-info}/WHEEL +0 -0
- {emdash_cli-0.1.4.dist-info → emdash_cli-0.1.25.dist-info}/entry_points.txt +0 -0
emdash_cli/sse_renderer.py
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
import json
|
|
4
4
|
import sys
|
|
5
5
|
import time
|
|
6
|
+
import threading
|
|
6
7
|
from typing import Iterator, Optional
|
|
7
8
|
|
|
8
9
|
from rich.console import Console
|
|
@@ -41,6 +42,7 @@ class SSERenderer:
|
|
|
41
42
|
self._session_id = None
|
|
42
43
|
self._spec = None
|
|
43
44
|
self._spec_submitted = False
|
|
45
|
+
self._plan_submitted = None # Plan data when submit_plan tool is called
|
|
44
46
|
self._pending_clarification = None
|
|
45
47
|
|
|
46
48
|
# Live display state
|
|
@@ -54,62 +56,126 @@ class SSERenderer:
|
|
|
54
56
|
self._subagent_tool_count = 0
|
|
55
57
|
self._subagent_current_tool = None
|
|
56
58
|
|
|
57
|
-
|
|
59
|
+
# Spinner animation thread
|
|
60
|
+
self._spinner_thread: Optional[threading.Thread] = None
|
|
61
|
+
self._spinner_running = False
|
|
62
|
+
self._spinner_message = "thinking"
|
|
63
|
+
self._spinner_lock = threading.Lock()
|
|
64
|
+
|
|
65
|
+
# Extended thinking storage
|
|
66
|
+
self._last_thinking: Optional[str] = None
|
|
67
|
+
|
|
68
|
+
def render_stream(
|
|
69
|
+
self,
|
|
70
|
+
lines: Iterator[str],
|
|
71
|
+
interrupt_event: Optional[threading.Event] = None,
|
|
72
|
+
) -> dict:
|
|
58
73
|
"""Render SSE stream to terminal.
|
|
59
74
|
|
|
60
75
|
Args:
|
|
61
76
|
lines: Iterator of SSE lines from HTTP response
|
|
77
|
+
interrupt_event: Optional event to signal interruption (e.g., ESC pressed)
|
|
62
78
|
|
|
63
79
|
Returns:
|
|
64
|
-
Dict with session_id, content, spec, and other metadata
|
|
80
|
+
Dict with session_id, content, spec, interrupted flag, and other metadata
|
|
65
81
|
"""
|
|
66
82
|
current_event = None
|
|
67
83
|
final_response = ""
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
interrupted = False
|
|
85
|
+
self._last_thinking = None # Reset thinking storage
|
|
86
|
+
|
|
87
|
+
# Start spinner while waiting for first event
|
|
88
|
+
if self.verbose:
|
|
89
|
+
self._start_spinner("thinking")
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
for line in lines:
|
|
93
|
+
# Check for interrupt signal
|
|
94
|
+
if interrupt_event and interrupt_event.is_set():
|
|
95
|
+
self._stop_spinner()
|
|
96
|
+
self.console.print("\n[yellow]Interrupted[/yellow]")
|
|
97
|
+
interrupted = True
|
|
98
|
+
break
|
|
99
|
+
|
|
100
|
+
line = line.strip()
|
|
101
|
+
|
|
102
|
+
if line.startswith("event: "):
|
|
103
|
+
current_event = line[7:]
|
|
104
|
+
elif line.startswith("data: "):
|
|
105
|
+
try:
|
|
106
|
+
data = json.loads(line[6:])
|
|
107
|
+
# Ensure data is a dict (could be null/None from JSON)
|
|
108
|
+
if data is None:
|
|
109
|
+
data = {}
|
|
110
|
+
if current_event:
|
|
111
|
+
result = self._handle_event(current_event, data)
|
|
112
|
+
if result:
|
|
113
|
+
final_response = result
|
|
114
|
+
except json.JSONDecodeError:
|
|
115
|
+
pass
|
|
116
|
+
elif line == ": ping":
|
|
117
|
+
# SSE keep-alive - ensure spinner is running
|
|
118
|
+
if self.verbose and not self._spinner_running:
|
|
119
|
+
self._start_spinner("waiting")
|
|
120
|
+
finally:
|
|
121
|
+
# Always stop spinner when stream ends
|
|
122
|
+
self._stop_spinner()
|
|
90
123
|
|
|
91
124
|
return {
|
|
92
125
|
"content": final_response,
|
|
93
126
|
"session_id": self._session_id,
|
|
94
127
|
"spec": self._spec,
|
|
95
128
|
"spec_submitted": self._spec_submitted,
|
|
129
|
+
"plan_submitted": self._plan_submitted,
|
|
96
130
|
"clarification": self._pending_clarification,
|
|
131
|
+
"interrupted": interrupted,
|
|
132
|
+
"thinking": self._last_thinking,
|
|
97
133
|
}
|
|
98
134
|
|
|
135
|
+
def _start_spinner(self, message: str = "thinking") -> None:
|
|
136
|
+
"""Start the animated spinner in a background thread."""
|
|
137
|
+
if self._spinner_running:
|
|
138
|
+
return
|
|
139
|
+
|
|
140
|
+
self._spinner_message = message
|
|
141
|
+
self._spinner_running = True
|
|
142
|
+
self._spinner_thread = threading.Thread(target=self._spinner_loop, daemon=True)
|
|
143
|
+
self._spinner_thread.start()
|
|
144
|
+
|
|
145
|
+
def _stop_spinner(self) -> None:
|
|
146
|
+
"""Stop the spinner and clear the line."""
|
|
147
|
+
if not self._spinner_running:
|
|
148
|
+
return
|
|
149
|
+
|
|
150
|
+
self._spinner_running = False
|
|
151
|
+
if self._spinner_thread:
|
|
152
|
+
self._spinner_thread.join(timeout=0.2)
|
|
153
|
+
self._spinner_thread = None
|
|
154
|
+
|
|
155
|
+
# Clear the spinner line
|
|
156
|
+
with self._spinner_lock:
|
|
157
|
+
sys.stdout.write("\r" + " " * 60 + "\r")
|
|
158
|
+
sys.stdout.flush()
|
|
159
|
+
|
|
160
|
+
def _spinner_loop(self) -> None:
|
|
161
|
+
"""Background thread that animates the spinner."""
|
|
162
|
+
while self._spinner_running:
|
|
163
|
+
with self._spinner_lock:
|
|
164
|
+
self._spinner_idx = (self._spinner_idx + 1) % len(SPINNER_FRAMES)
|
|
165
|
+
spinner = SPINNER_FRAMES[self._spinner_idx]
|
|
166
|
+
sys.stdout.write(f"\r \033[33m{spinner}\033[0m \033[2m{self._spinner_message}...\033[0m")
|
|
167
|
+
sys.stdout.flush()
|
|
168
|
+
time.sleep(0.1)
|
|
169
|
+
|
|
99
170
|
def _show_waiting(self) -> None:
|
|
100
|
-
"""Show waiting animation."""
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
# Use carriage return to update in place
|
|
104
|
-
sys.stdout.write(f"\r [dim]{spinner}[/dim] waiting...")
|
|
105
|
-
sys.stdout.flush()
|
|
171
|
+
"""Show waiting animation (starts spinner if not running)."""
|
|
172
|
+
if not self._spinner_running:
|
|
173
|
+
self._start_spinner("waiting")
|
|
106
174
|
|
|
107
175
|
def _clear_waiting(self) -> None:
|
|
108
|
-
"""Clear waiting line."""
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
sys.stdout.flush()
|
|
112
|
-
self._waiting_for_next = False
|
|
176
|
+
"""Clear waiting line (stops spinner)."""
|
|
177
|
+
self._stop_spinner()
|
|
178
|
+
self._waiting_for_next = False
|
|
113
179
|
|
|
114
180
|
def _handle_event(self, event_type: str, data: dict) -> Optional[str]:
|
|
115
181
|
"""Handle individual SSE event."""
|
|
@@ -126,8 +192,10 @@ class SSERenderer:
|
|
|
126
192
|
self._render_tool_start(data)
|
|
127
193
|
elif event_type == "tool_result":
|
|
128
194
|
self._render_tool_result(data)
|
|
129
|
-
#
|
|
195
|
+
# Start spinner while waiting for next tool/response
|
|
130
196
|
self._waiting_for_next = True
|
|
197
|
+
if self.verbose:
|
|
198
|
+
self._start_spinner("thinking")
|
|
131
199
|
elif event_type == "thinking":
|
|
132
200
|
self._render_thinking(data)
|
|
133
201
|
elif event_type == "progress":
|
|
@@ -138,12 +206,16 @@ class SSERenderer:
|
|
|
138
206
|
return self._render_response(data)
|
|
139
207
|
elif event_type == "clarification":
|
|
140
208
|
self._render_clarification(data)
|
|
209
|
+
elif event_type == "plan_submitted":
|
|
210
|
+
self._render_plan_submitted(data)
|
|
141
211
|
elif event_type == "error":
|
|
142
212
|
self._render_error(data)
|
|
143
213
|
elif event_type == "warning":
|
|
144
214
|
self._render_warning(data)
|
|
145
215
|
elif event_type == "session_end":
|
|
146
216
|
self._render_session_end(data)
|
|
217
|
+
elif event_type == "context_frame":
|
|
218
|
+
self._render_context_frame(data)
|
|
147
219
|
|
|
148
220
|
return None
|
|
149
221
|
|
|
@@ -231,6 +303,10 @@ class SSERenderer:
|
|
|
231
303
|
description = args.get("description", "")
|
|
232
304
|
prompt = args.get("prompt", "")
|
|
233
305
|
|
|
306
|
+
# Reset sub-agent tracking
|
|
307
|
+
self._subagent_tool_count = 0
|
|
308
|
+
self._subagent_current_tool = None
|
|
309
|
+
|
|
234
310
|
# Truncate prompt for display
|
|
235
311
|
prompt_display = prompt[:60] + "..." if len(prompt) > 60 else prompt
|
|
236
312
|
|
|
@@ -304,6 +380,10 @@ class SSERenderer:
|
|
|
304
380
|
success = data.get("success", True)
|
|
305
381
|
result_data = data.get("data") or {}
|
|
306
382
|
|
|
383
|
+
# Clear the progress line
|
|
384
|
+
sys.stdout.write(f"\r{' ' * 120}\r")
|
|
385
|
+
sys.stdout.flush()
|
|
386
|
+
|
|
307
387
|
# Calculate duration
|
|
308
388
|
duration = ""
|
|
309
389
|
if self._current_tool and self._current_tool.get("start_time"):
|
|
@@ -313,20 +393,20 @@ class SSERenderer:
|
|
|
313
393
|
if success:
|
|
314
394
|
agent_type = result_data.get("agent_type", "Agent")
|
|
315
395
|
iterations = result_data.get("iterations", 0)
|
|
316
|
-
tools_used = result_data.get("tools_used", [])
|
|
317
396
|
files_count = len(result_data.get("files_explored", []))
|
|
318
397
|
|
|
319
398
|
self.console.print(
|
|
320
399
|
f" [green]✓[/green] {agent_type} completed{duration}"
|
|
321
400
|
)
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
401
|
+
# Show stats using our tracked tool count
|
|
402
|
+
stats = []
|
|
403
|
+
if iterations > 0:
|
|
404
|
+
stats.append(f"{iterations} turns")
|
|
405
|
+
if files_count > 0:
|
|
406
|
+
stats.append(f"{files_count} files")
|
|
407
|
+
if self._subagent_tool_count > 0:
|
|
408
|
+
stats.append(f"{self._subagent_tool_count} tools")
|
|
409
|
+
if stats:
|
|
330
410
|
self.console.print(f" [dim]{' · '.join(stats)}[/dim]")
|
|
331
411
|
else:
|
|
332
412
|
error = result_data.get("error", data.get("summary", "failed"))
|
|
@@ -334,6 +414,7 @@ class SSERenderer:
|
|
|
334
414
|
|
|
335
415
|
self.console.print()
|
|
336
416
|
self._current_tool = None
|
|
417
|
+
self._subagent_tool_count = 0
|
|
337
418
|
|
|
338
419
|
def _format_args_summary(self, args: dict) -> str:
|
|
339
420
|
"""Format args into a compact summary string."""
|
|
@@ -350,12 +431,32 @@ class SSERenderer:
|
|
|
350
431
|
return " ".join(parts)
|
|
351
432
|
|
|
352
433
|
def _render_thinking(self, data: dict) -> None:
|
|
353
|
-
"""Render thinking event.
|
|
434
|
+
"""Render thinking event.
|
|
435
|
+
|
|
436
|
+
Handles both short progress messages and extended thinking content.
|
|
437
|
+
"""
|
|
354
438
|
if not self.verbose:
|
|
355
439
|
return
|
|
356
440
|
|
|
357
441
|
message = data.get("message", "")
|
|
358
|
-
|
|
442
|
+
|
|
443
|
+
# Check if this is extended thinking (long content) vs short progress message
|
|
444
|
+
if len(message) > 200:
|
|
445
|
+
# Extended thinking - show summary with collapsible indicator
|
|
446
|
+
self._stop_spinner()
|
|
447
|
+
lines = message.strip().split("\n")
|
|
448
|
+
preview = lines[0][:80] + "..." if len(lines[0]) > 80 else lines[0]
|
|
449
|
+
line_count = len(lines)
|
|
450
|
+
char_count = len(message)
|
|
451
|
+
|
|
452
|
+
self.console.print(f" [dim]┃[/dim] [dim italic]💭 Thinking ({char_count:,} chars, {line_count} lines)[/dim italic]")
|
|
453
|
+
self.console.print(f" [dim]┃[/dim] [dim] {preview}[/dim]")
|
|
454
|
+
|
|
455
|
+
# Store thinking for potential later display
|
|
456
|
+
self._last_thinking = message
|
|
457
|
+
else:
|
|
458
|
+
# Short progress message
|
|
459
|
+
self.console.print(f" [dim]┃[/dim] [dim italic]💭 {message}[/dim italic]")
|
|
359
460
|
|
|
360
461
|
def _render_progress(self, data: dict) -> None:
|
|
361
462
|
"""Render progress event."""
|
|
@@ -414,6 +515,67 @@ class SSERenderer:
|
|
|
414
515
|
else:
|
|
415
516
|
self._pending_clarification = None
|
|
416
517
|
|
|
518
|
+
def _render_plan_submitted(self, data: dict) -> None:
|
|
519
|
+
"""Render plan submission event and store for menu display."""
|
|
520
|
+
from rich.panel import Panel
|
|
521
|
+
from rich.table import Table
|
|
522
|
+
from rich.text import Text
|
|
523
|
+
|
|
524
|
+
title = data.get("title", "Plan")
|
|
525
|
+
summary = data.get("summary", "")
|
|
526
|
+
files_to_modify = data.get("files_to_modify", [])
|
|
527
|
+
implementation_steps = data.get("implementation_steps", [])
|
|
528
|
+
risks = data.get("risks", [])
|
|
529
|
+
testing_strategy = data.get("testing_strategy", "")
|
|
530
|
+
|
|
531
|
+
# Store the plan data for the CLI to show the menu
|
|
532
|
+
self._plan_submitted = data
|
|
533
|
+
|
|
534
|
+
# Build plan display
|
|
535
|
+
self.console.print()
|
|
536
|
+
self.console.print(Panel(
|
|
537
|
+
f"[bold]{title}[/bold]\n\n{summary}",
|
|
538
|
+
title="[cyan]📋 Plan[/cyan]",
|
|
539
|
+
border_style="cyan",
|
|
540
|
+
))
|
|
541
|
+
|
|
542
|
+
# Critical Files table (always shown)
|
|
543
|
+
if files_to_modify:
|
|
544
|
+
files_table = Table(title="Critical Files", show_header=True, header_style="bold cyan")
|
|
545
|
+
files_table.add_column("File", style="yellow")
|
|
546
|
+
files_table.add_column("Lines", style="dim")
|
|
547
|
+
files_table.add_column("Changes", style="white")
|
|
548
|
+
|
|
549
|
+
for f in files_to_modify:
|
|
550
|
+
if isinstance(f, dict):
|
|
551
|
+
files_table.add_row(
|
|
552
|
+
f.get("path", ""),
|
|
553
|
+
f.get("lines", ""),
|
|
554
|
+
f.get("changes", "")
|
|
555
|
+
)
|
|
556
|
+
else:
|
|
557
|
+
files_table.add_row(str(f), "", "")
|
|
558
|
+
|
|
559
|
+
self.console.print(files_table)
|
|
560
|
+
|
|
561
|
+
# Implementation Steps (only if provided)
|
|
562
|
+
if implementation_steps:
|
|
563
|
+
self.console.print("\n[bold cyan]Implementation Steps[/bold cyan]")
|
|
564
|
+
for i, step in enumerate(implementation_steps, 1):
|
|
565
|
+
self.console.print(f" [dim]{i}.[/dim] {step}")
|
|
566
|
+
|
|
567
|
+
# Risks (only if provided)
|
|
568
|
+
if risks:
|
|
569
|
+
self.console.print("\n[bold yellow]⚠ Risks[/bold yellow]")
|
|
570
|
+
for risk in risks:
|
|
571
|
+
self.console.print(f" [yellow]•[/yellow] {risk}")
|
|
572
|
+
|
|
573
|
+
# Testing (only if provided)
|
|
574
|
+
if testing_strategy:
|
|
575
|
+
self.console.print(f"\n[bold green]Testing:[/bold green] {testing_strategy}")
|
|
576
|
+
|
|
577
|
+
self.console.print()
|
|
578
|
+
|
|
417
579
|
def _render_error(self, data: dict) -> None:
|
|
418
580
|
"""Render error event."""
|
|
419
581
|
message = data.get("message", "Unknown error")
|
|
@@ -438,5 +600,59 @@ class SSERenderer:
|
|
|
438
600
|
if not success:
|
|
439
601
|
error = data.get("error", "Unknown error")
|
|
440
602
|
self.console.print(f"\n[red]Session ended with error: {error}[/red]")
|
|
441
|
-
|
|
442
|
-
|
|
603
|
+
|
|
604
|
+
def _render_context_frame(self, data: dict) -> None:
|
|
605
|
+
"""Render context frame update (post-agentic loop summary)."""
|
|
606
|
+
adding = data.get("adding") or {}
|
|
607
|
+
reading = data.get("reading") or {}
|
|
608
|
+
|
|
609
|
+
# Get stats from the adding data
|
|
610
|
+
step_count = adding.get("step_count", 0)
|
|
611
|
+
entities_found = adding.get("entities_found", 0)
|
|
612
|
+
context_tokens = adding.get("context_tokens", 0)
|
|
613
|
+
context_breakdown = adding.get("context_breakdown", {})
|
|
614
|
+
largest_messages = adding.get("largest_messages", [])
|
|
615
|
+
|
|
616
|
+
# Get reading stats
|
|
617
|
+
item_count = reading.get("item_count", 0)
|
|
618
|
+
|
|
619
|
+
# Only show if there's something to report
|
|
620
|
+
if step_count == 0 and item_count == 0 and context_tokens == 0:
|
|
621
|
+
return
|
|
622
|
+
|
|
623
|
+
self.console.print()
|
|
624
|
+
self.console.print("[dim]───── Context Frame ─────[/dim]")
|
|
625
|
+
|
|
626
|
+
# Show total context
|
|
627
|
+
if context_tokens > 0:
|
|
628
|
+
self.console.print(f" [bold]Total: {context_tokens:,} tokens[/bold]")
|
|
629
|
+
|
|
630
|
+
# Show breakdown
|
|
631
|
+
if context_breakdown:
|
|
632
|
+
breakdown_parts = []
|
|
633
|
+
for key, tokens in context_breakdown.items():
|
|
634
|
+
if tokens > 0:
|
|
635
|
+
breakdown_parts.append(f"{key}: {tokens:,}")
|
|
636
|
+
if breakdown_parts:
|
|
637
|
+
self.console.print(f" [dim]Breakdown: {' | '.join(breakdown_parts)}[/dim]")
|
|
638
|
+
|
|
639
|
+
# Show largest messages (context hogs)
|
|
640
|
+
if largest_messages:
|
|
641
|
+
self.console.print(f" [yellow]Largest messages:[/yellow]")
|
|
642
|
+
for msg in largest_messages[:5]:
|
|
643
|
+
label = msg.get("label", "unknown")
|
|
644
|
+
tokens = msg.get("tokens", 0)
|
|
645
|
+
preview = msg.get("preview", "")[:50].replace("\n", " ")
|
|
646
|
+
self.console.print(f" [dim]{tokens:,} tokens[/dim] - {label}: {preview}...")
|
|
647
|
+
|
|
648
|
+
# Show other stats
|
|
649
|
+
stats = []
|
|
650
|
+
if step_count > 0:
|
|
651
|
+
stats.append(f"{step_count} steps")
|
|
652
|
+
if entities_found > 0:
|
|
653
|
+
stats.append(f"{entities_found} entities")
|
|
654
|
+
if item_count > 0:
|
|
655
|
+
stats.append(f"{item_count} context items")
|
|
656
|
+
|
|
657
|
+
if stats:
|
|
658
|
+
self.console.print(f" [dim]{' · '.join(stats)}[/dim]")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: emdash-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.25
|
|
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
|
|
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3.12
|
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.13
|
|
12
12
|
Classifier: Programming Language :: Python :: 3.14
|
|
13
13
|
Requires-Dist: click (>=8.1.7,<9.0.0)
|
|
14
|
-
Requires-Dist: emdash-core (>=0.1.
|
|
14
|
+
Requires-Dist: emdash-core (>=0.1.25)
|
|
15
15
|
Requires-Dist: httpx (>=0.25.0)
|
|
16
16
|
Requires-Dist: prompt_toolkit (>=3.0.43,<4.0.0)
|
|
17
17
|
Requires-Dist: rich (>=13.7.0)
|
|
@@ -1,26 +1,28 @@
|
|
|
1
|
-
emdash_cli/__init__.py,sha256=
|
|
2
|
-
emdash_cli/client.py,sha256=
|
|
3
|
-
emdash_cli/commands/__init__.py,sha256=
|
|
4
|
-
emdash_cli/commands/agent.py,sha256=
|
|
1
|
+
emdash_cli/__init__.py,sha256=Rnn2O7B8OCEKlVtNRbWOU2-GN75_KLmhEJgOZzY-KwE,232
|
|
2
|
+
emdash_cli/client.py,sha256=sPgX2CEfiHc4TXN9TYqFhc8DVKgPyk20Efm66KQAOhE,16761
|
|
3
|
+
emdash_cli/commands/__init__.py,sha256=D9edXBHm69tueUtE4DggTA1_Yjsl9YZaKjBVDY2D_gQ,712
|
|
4
|
+
emdash_cli/commands/agent.py,sha256=RCgSYW6OBx3UFGVAM_byhyrtBKlsXN2xfJaT3w51uDY,27735
|
|
5
5
|
emdash_cli/commands/analyze.py,sha256=c9ztbv0Ra7g2AlDmMOy-9L51fDVuoqbuzxnRfomoFIQ,4403
|
|
6
6
|
emdash_cli/commands/auth.py,sha256=SpWdqO1bJCgt4x1B4Pr7hNOucwTuBFJ1oGPOzXtvwZM,3816
|
|
7
7
|
emdash_cli/commands/db.py,sha256=nZK7gLDVE2lAQVYrMx6Swscml5OAtkbg-EcSNSvRIlA,2922
|
|
8
8
|
emdash_cli/commands/embed.py,sha256=kqP5jtYCsZ2_s_I1DjzIUgaod1VUvPiRO0jIIY0HtCs,3244
|
|
9
|
-
emdash_cli/commands/index.py,sha256=
|
|
9
|
+
emdash_cli/commands/index.py,sha256=uFNC5whhU9JdF_59FeM99OPdzKLBTJLkLO6vp9pt944,6959
|
|
10
10
|
emdash_cli/commands/plan.py,sha256=BRiyIhfy_zz2PYy4Qo3a0t77GwHhdssZk6NImOkPi-w,2189
|
|
11
11
|
emdash_cli/commands/projectmd.py,sha256=4y4cn_yFw85jMUm52nGjpqnd-YWvs6ZNEMWJGeJC17Q,1605
|
|
12
12
|
emdash_cli/commands/research.py,sha256=xtI9_9emY7-rGQD5xJALTxtgTFmI4dplYW148dtTaTs,1553
|
|
13
13
|
emdash_cli/commands/rules.py,sha256=n85CCG0WNIBEsUK9STJetPmZxoypQtest5BGPsXl0ac,2712
|
|
14
14
|
emdash_cli/commands/search.py,sha256=DrSv_oN2xF1NaKCBICdyII7eupVRsDQ2ysW-TPSU0X0,1661
|
|
15
15
|
emdash_cli/commands/server.py,sha256=UTmLAVolT0krN9xCtMcCSvmQZ9k1QwpFFmXGg9BulRY,3459
|
|
16
|
+
emdash_cli/commands/skills.py,sha256=8N4279Hr8u2L8AgVjSTRVBLJBcXhN5DN7dn5fME62bs,9989
|
|
16
17
|
emdash_cli/commands/spec.py,sha256=qafDmzKyRH035p3xTm_VTUsQLDZblIzIg-dxjEPv6tM,1494
|
|
17
18
|
emdash_cli/commands/swarm.py,sha256=s_cntuorNdtNNTD2Qs1p2IcHghMrBMOQuturPS3y9mM,2661
|
|
18
19
|
emdash_cli/commands/tasks.py,sha256=TdyunjSV5w7jpNFwv0fTL-_No5Fyvdm7Z2nXqxWSJec,1635
|
|
19
20
|
emdash_cli/commands/team.py,sha256=K1-IJg6iG-9HMF_3JmpNDlNs1PYbb-ThFHU9KU_jKRo,1430
|
|
20
|
-
emdash_cli/
|
|
21
|
+
emdash_cli/keyboard.py,sha256=haYYAuhYGtdjomzhIFy_3Z3eN3BXfMdb4uRQjwB0tbk,4593
|
|
22
|
+
emdash_cli/main.py,sha256=c-faWp-jzf9a0BbXhVoPvPQfGWSryXpYfswehqZCYPM,2593
|
|
21
23
|
emdash_cli/server_manager.py,sha256=RrLteSHUmcFV4cyHJAEmgM9qHru2mJS08QNLWno6Y3Y,7051
|
|
22
|
-
emdash_cli/sse_renderer.py,sha256=
|
|
23
|
-
emdash_cli-0.1.
|
|
24
|
-
emdash_cli-0.1.
|
|
25
|
-
emdash_cli-0.1.
|
|
26
|
-
emdash_cli-0.1.
|
|
24
|
+
emdash_cli/sse_renderer.py,sha256=PEbD53ZohMp9yvii_1ELGwVKb8nnA_n17jICeaURkuY,23738
|
|
25
|
+
emdash_cli-0.1.25.dist-info/METADATA,sha256=RFuFUhHJlRcpfzwztnQXTWheB853OVlyWd1tAIzRKsE,662
|
|
26
|
+
emdash_cli-0.1.25.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
27
|
+
emdash_cli-0.1.25.dist-info/entry_points.txt,sha256=31CuYD0k-tM8csFWDunc-JoZTxXaifj3oIXz4V0p6F0,122
|
|
28
|
+
emdash_cli-0.1.25.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|