deepy-cli 0.1.13__tar.gz → 0.1.14__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.
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/PKG-INFO +1 -1
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/pyproject.toml +1 -1
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/__init__.py +1 -1
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/tools/builtin.py +2 -45
- deepy_cli-0.1.14/src/deepy/tools/shell_output.py +45 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/local_command.py +15 -4
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/README.md +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/__main__.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/cli.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/config/__init__.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/config/settings.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/__init__.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/AskUserQuestion.md +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/WebFetch.md +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/WebSearch.md +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/__init__.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/edit.md +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/modify.md +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/read.md +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/shell.md +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/write.md +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/errors.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/__init__.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/agent.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/compaction.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/context.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/events.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/model_capabilities.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/provider.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/replay.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/runner.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/thinking.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/prompts/__init__.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/prompts/compact.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/prompts/rules.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/prompts/runtime_context.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/prompts/system.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/prompts/tool_docs.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/sessions/__init__.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/sessions/jsonl.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/sessions/manager.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/skills.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/status.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/tools/__init__.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/tools/agents.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/tools/file_state.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/tools/result.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/tools/shell_utils.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/__init__.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/app.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/ask_user_question.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/exit_summary.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/file_mentions.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/loading_text.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/markdown.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/message_view.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/model_picker.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/prompt_buffer.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/prompt_input.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/session_list.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/session_picker.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/slash_commands.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/styles.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/terminal.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/theme_picker.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/thinking_state.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/welcome.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/update_check.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/usage.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/utils/__init__.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/utils/debug_logger.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/utils/error_logger.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/utils/json.py +0 -0
- {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/utils/notify.py +0 -0
|
@@ -26,6 +26,7 @@ from deepy.utils import json as json_utils
|
|
|
26
26
|
|
|
27
27
|
from .file_state import FileSnippet, FileState
|
|
28
28
|
from .result import ToolResult
|
|
29
|
+
from .shell_output import decode_shell_output
|
|
29
30
|
from .shell_utils import RuntimeEnvironment
|
|
30
31
|
from .shell_utils import build_disable_extglob_command
|
|
31
32
|
from .shell_utils import build_shell_init_command
|
|
@@ -2159,54 +2160,10 @@ def _read_captured_output(stream, *, marker: str | None = None) -> tuple[str, st
|
|
|
2159
2160
|
truncated = len(data) > MAX_BASH_CAPTURE_CHARS
|
|
2160
2161
|
if truncated:
|
|
2161
2162
|
data = data[:MAX_BASH_CAPTURE_CHARS]
|
|
2162
|
-
text, encoding =
|
|
2163
|
+
text, encoding = decode_shell_output(data, marker=marker)
|
|
2163
2164
|
return text, encoding, truncated
|
|
2164
2165
|
|
|
2165
2166
|
|
|
2166
|
-
def _decode_shell_output(data: bytes, *, marker: str | None = None) -> tuple[str, str]:
|
|
2167
|
-
if not data:
|
|
2168
|
-
return "", "empty"
|
|
2169
|
-
if marker:
|
|
2170
|
-
marker_bytes = marker.encode("ascii")
|
|
2171
|
-
marker_index = data.find(marker_bytes)
|
|
2172
|
-
if marker_index >= 0:
|
|
2173
|
-
sentinel_start = marker_index
|
|
2174
|
-
if sentinel_start > 0 and data[sentinel_start - 1 : sentinel_start] == b"\n":
|
|
2175
|
-
sentinel_start -= 1
|
|
2176
|
-
if sentinel_start > 0 and data[sentinel_start - 1 : sentinel_start] == b"\r":
|
|
2177
|
-
sentinel_start -= 1
|
|
2178
|
-
visible, visible_encoding = _decode_shell_output_bytes(data[:sentinel_start])
|
|
2179
|
-
sentinel = data[sentinel_start:].decode("utf-8", errors="replace")
|
|
2180
|
-
return visible + sentinel, visible_encoding
|
|
2181
|
-
return _decode_shell_output_bytes(data)
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
def _decode_shell_output_bytes(data: bytes) -> tuple[str, str]:
|
|
2185
|
-
if not data:
|
|
2186
|
-
return "", "empty"
|
|
2187
|
-
if data.startswith((b"\xff\xfe", b"\xfe\xff")):
|
|
2188
|
-
return data.decode("utf-16", errors="replace"), "utf-16"
|
|
2189
|
-
if _looks_like_utf16le(data):
|
|
2190
|
-
return data.decode("utf-16le", errors="replace"), "utf-16le"
|
|
2191
|
-
try:
|
|
2192
|
-
return data.decode("utf-8-sig"), "utf-8-sig" if data.startswith(b"\xef\xbb\xbf") else "utf-8"
|
|
2193
|
-
except UnicodeDecodeError:
|
|
2194
|
-
pass
|
|
2195
|
-
try:
|
|
2196
|
-
return data.decode("gb18030"), "gb18030"
|
|
2197
|
-
except UnicodeDecodeError:
|
|
2198
|
-
return data.decode("utf-8", errors="replace"), "utf-8-replace"
|
|
2199
|
-
|
|
2200
|
-
|
|
2201
|
-
def _looks_like_utf16le(data: bytes) -> bool:
|
|
2202
|
-
if len(data) < 4:
|
|
2203
|
-
return False
|
|
2204
|
-
sample = data[: min(len(data), 4096)]
|
|
2205
|
-
odd_nuls = sample[1::2].count(0)
|
|
2206
|
-
even_nuls = sample[0::2].count(0)
|
|
2207
|
-
return odd_nuls >= max(2, len(sample) // 8) and odd_nuls > even_nuls * 2
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
2167
|
def _build_shell_command(
|
|
2211
2168
|
command: str,
|
|
2212
2169
|
marker: str,
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def decode_shell_output(data: bytes, *, marker: str | None = None) -> tuple[str, str]:
|
|
5
|
+
if not data:
|
|
6
|
+
return "", "empty"
|
|
7
|
+
if marker:
|
|
8
|
+
marker_bytes = marker.encode("ascii")
|
|
9
|
+
marker_index = data.find(marker_bytes)
|
|
10
|
+
if marker_index >= 0:
|
|
11
|
+
sentinel_start = marker_index
|
|
12
|
+
if sentinel_start > 0 and data[sentinel_start - 1 : sentinel_start] == b"\n":
|
|
13
|
+
sentinel_start -= 1
|
|
14
|
+
if sentinel_start > 0 and data[sentinel_start - 1 : sentinel_start] == b"\r":
|
|
15
|
+
sentinel_start -= 1
|
|
16
|
+
visible, visible_encoding = decode_shell_output_bytes(data[:sentinel_start])
|
|
17
|
+
sentinel = data[sentinel_start:].decode("utf-8", errors="replace")
|
|
18
|
+
return visible + sentinel, visible_encoding
|
|
19
|
+
return decode_shell_output_bytes(data)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def decode_shell_output_bytes(data: bytes) -> tuple[str, str]:
|
|
23
|
+
if not data:
|
|
24
|
+
return "", "empty"
|
|
25
|
+
if data.startswith((b"\xff\xfe", b"\xfe\xff")):
|
|
26
|
+
return data.decode("utf-16", errors="replace"), "utf-16"
|
|
27
|
+
if _looks_like_utf16le(data):
|
|
28
|
+
return data.decode("utf-16le", errors="replace"), "utf-16le"
|
|
29
|
+
try:
|
|
30
|
+
return data.decode("utf-8-sig"), "utf-8-sig" if data.startswith(b"\xef\xbb\xbf") else "utf-8"
|
|
31
|
+
except UnicodeDecodeError:
|
|
32
|
+
pass
|
|
33
|
+
try:
|
|
34
|
+
return data.decode("gb18030"), "gb18030"
|
|
35
|
+
except UnicodeDecodeError:
|
|
36
|
+
return data.decode("utf-8", errors="replace"), "utf-8-replace"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _looks_like_utf16le(data: bytes) -> bool:
|
|
40
|
+
if len(data) < 4:
|
|
41
|
+
return False
|
|
42
|
+
sample = data[: min(len(data), 4096)]
|
|
43
|
+
odd_nuls = sample[1::2].count(0)
|
|
44
|
+
even_nuls = sample[0::2].count(0)
|
|
45
|
+
return odd_nuls >= max(2, len(sample) // 8) and odd_nuls > even_nuls * 2
|
|
@@ -18,6 +18,7 @@ from pathlib import Path
|
|
|
18
18
|
from typing import Any
|
|
19
19
|
|
|
20
20
|
from deepy.tools.result import ToolResult
|
|
21
|
+
from deepy.tools.shell_output import decode_shell_output_bytes
|
|
21
22
|
from deepy.tools.shell_utils import RuntimeEnvironment, detect_runtime_environment
|
|
22
23
|
from deepy.utils import json as json_utils
|
|
23
24
|
|
|
@@ -113,6 +114,7 @@ def run_local_command(
|
|
|
113
114
|
capture_limit = max(display_limit, context_limit) + len(_TRUNCATED_MARKER)
|
|
114
115
|
|
|
115
116
|
if runtime.os_family == "windows":
|
|
117
|
+
_prepare_windows_process_env(process_env)
|
|
116
118
|
return _run_windows_pipes(
|
|
117
119
|
command,
|
|
118
120
|
cwd=cwd,
|
|
@@ -347,11 +349,11 @@ def _run_windows_pipes(
|
|
|
347
349
|
if len(captured) >= capture_limit:
|
|
348
350
|
capture_truncated = True
|
|
349
351
|
exit_code = process.poll()
|
|
350
|
-
output = _sanitize_terminal_output(_decode_output(bytes(captured)))
|
|
352
|
+
output = _sanitize_terminal_output(_decode_output(bytes(captured), windows_compatible=True))
|
|
351
353
|
error = _command_error(exit_code, timed_out=timed_out, interrupted=interrupted)
|
|
352
354
|
except Exception as exc:
|
|
353
355
|
exit_code = None
|
|
354
|
-
output = _sanitize_terminal_output(_decode_output(bytes(captured)))
|
|
356
|
+
output = _sanitize_terminal_output(_decode_output(bytes(captured), windows_compatible=True))
|
|
355
357
|
error = str(exc)
|
|
356
358
|
|
|
357
359
|
display_output, display_truncated = _limit_output(output, display_limit)
|
|
@@ -503,8 +505,17 @@ def _limit_output(output: str, limit: int) -> tuple[str, bool]:
|
|
|
503
505
|
return output[:keep] + marker, True
|
|
504
506
|
|
|
505
507
|
|
|
506
|
-
def _decode_output(output: bytes) -> str:
|
|
507
|
-
|
|
508
|
+
def _decode_output(output: bytes, *, windows_compatible: bool = False) -> str:
|
|
509
|
+
if windows_compatible:
|
|
510
|
+
text, _ = decode_shell_output_bytes(output)
|
|
511
|
+
else:
|
|
512
|
+
text = output.decode("utf-8", errors="replace")
|
|
513
|
+
return _normalize_line_endings(text)
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
def _prepare_windows_process_env(env: dict[str, str]) -> None:
|
|
517
|
+
env.setdefault("PYTHONUTF8", "1")
|
|
518
|
+
env.setdefault("PYTHONIOENCODING", "utf-8")
|
|
508
519
|
|
|
509
520
|
|
|
510
521
|
def _sanitize_terminal_output(output: str) -> str:
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|