emdash-cli 0.1.30__py3-none-any.whl → 0.1.35__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 CHANGED
@@ -1,6 +1,21 @@
1
1
  """EmDash CLI - Command-line interface for code intelligence."""
2
2
 
3
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
4
19
 
5
20
  try:
6
21
  __version__ = version("emdash-cli")
emdash_cli/client.py CHANGED
@@ -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,123 @@ 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
+
141
262
  def list_sessions(self) -> list[dict]:
142
263
  """List active agent sessions.
143
264