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.
Files changed (74) hide show
  1. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/PKG-INFO +1 -1
  2. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/pyproject.toml +1 -1
  3. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/__init__.py +1 -1
  4. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/tools/builtin.py +2 -45
  5. deepy_cli-0.1.14/src/deepy/tools/shell_output.py +45 -0
  6. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/local_command.py +15 -4
  7. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/README.md +0 -0
  8. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/__main__.py +0 -0
  9. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/cli.py +0 -0
  10. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/config/__init__.py +0 -0
  11. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/config/settings.py +0 -0
  12. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/__init__.py +0 -0
  13. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/AskUserQuestion.md +0 -0
  14. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/WebFetch.md +0 -0
  15. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/WebSearch.md +0 -0
  16. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/__init__.py +0 -0
  17. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/edit.md +0 -0
  18. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/modify.md +0 -0
  19. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/read.md +0 -0
  20. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/shell.md +0 -0
  21. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/data/tools/write.md +0 -0
  22. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/errors.py +0 -0
  23. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/__init__.py +0 -0
  24. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/agent.py +0 -0
  25. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/compaction.py +0 -0
  26. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/context.py +0 -0
  27. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/events.py +0 -0
  28. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/model_capabilities.py +0 -0
  29. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/provider.py +0 -0
  30. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/replay.py +0 -0
  31. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/runner.py +0 -0
  32. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/llm/thinking.py +0 -0
  33. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/prompts/__init__.py +0 -0
  34. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/prompts/compact.py +0 -0
  35. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/prompts/rules.py +0 -0
  36. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/prompts/runtime_context.py +0 -0
  37. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/prompts/system.py +0 -0
  38. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/prompts/tool_docs.py +0 -0
  39. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/sessions/__init__.py +0 -0
  40. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/sessions/jsonl.py +0 -0
  41. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/sessions/manager.py +0 -0
  42. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/skills.py +0 -0
  43. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/status.py +0 -0
  44. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/tools/__init__.py +0 -0
  45. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/tools/agents.py +0 -0
  46. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/tools/file_state.py +0 -0
  47. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/tools/result.py +0 -0
  48. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/tools/shell_utils.py +0 -0
  49. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/__init__.py +0 -0
  50. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/app.py +0 -0
  51. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/ask_user_question.py +0 -0
  52. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/exit_summary.py +0 -0
  53. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/file_mentions.py +0 -0
  54. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/loading_text.py +0 -0
  55. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/markdown.py +0 -0
  56. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/message_view.py +0 -0
  57. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/model_picker.py +0 -0
  58. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/prompt_buffer.py +0 -0
  59. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/prompt_input.py +0 -0
  60. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/session_list.py +0 -0
  61. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/session_picker.py +0 -0
  62. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/slash_commands.py +0 -0
  63. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/styles.py +0 -0
  64. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/terminal.py +0 -0
  65. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/theme_picker.py +0 -0
  66. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/thinking_state.py +0 -0
  67. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/ui/welcome.py +0 -0
  68. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/update_check.py +0 -0
  69. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/usage.py +0 -0
  70. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/utils/__init__.py +0 -0
  71. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/utils/debug_logger.py +0 -0
  72. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/utils/error_logger.py +0 -0
  73. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/utils/json.py +0 -0
  74. {deepy_cli-0.1.13 → deepy_cli-0.1.14}/src/deepy/utils/notify.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: deepy-cli
3
- Version: 0.1.13
3
+ Version: 0.1.14
4
4
  Summary: Deepy - Vibe coding for DeepSeek models in your terminal
5
5
  Keywords: deepseek,coding-agent,terminal,cli,agents
6
6
  Author: kirineko
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "deepy-cli"
3
- version = "0.1.13"
3
+ version = "0.1.14"
4
4
  description = "Deepy - Vibe coding for DeepSeek models in your terminal"
5
5
  readme = "README.md"
6
6
  authors = [
@@ -1,6 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
- __version__ = "0.1.13"
3
+ __version__ = "0.1.14"
4
4
 
5
5
 
6
6
  def main() -> None:
@@ -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 = _decode_shell_output(data, marker=marker)
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
- return _normalize_line_endings(output.decode("utf-8", errors="replace"))
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