drydock-cli 3.0.93__tar.gz → 3.0.94__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 (109) hide show
  1. {drydock_cli-3.0.93/drydock_cli.egg-info → drydock_cli-3.0.94}/PKG-INFO +1 -1
  2. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/tools/__init__.py +15 -1
  3. {drydock_cli-3.0.93 → drydock_cli-3.0.94/drydock_cli.egg-info}/PKG-INFO +1 -1
  4. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock_cli.egg-info/SOURCES.txt +1 -0
  5. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/pyproject.toml +1 -1
  6. drydock_cli-3.0.94/tests/test_bash_sanitize.py +35 -0
  7. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/LICENSE +0 -0
  8. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/NOTICE +0 -0
  9. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/README.md +0 -0
  10. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/__init__.py +0 -0
  11. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/__main__.py +0 -0
  12. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/advisor.py +0 -0
  13. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/agent.py +0 -0
  14. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/bash_safety.py +0 -0
  15. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/builtin_skills/__init__.py +0 -0
  16. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/builtin_skills/rmf-categorize.md +0 -0
  17. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/builtin_skills/rmf-control.md +0 -0
  18. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/builtin_skills/rmf-poam.md +0 -0
  19. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/builtin_skills/rmf-review.md +0 -0
  20. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/builtin_skills/stig-assess.md +0 -0
  21. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/builtin_skills/stig-remediate.md +0 -0
  22. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/cci.py +0 -0
  23. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/cli.py +0 -0
  24. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/compaction.py +0 -0
  25. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/config.py +0 -0
  26. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/detect.py +0 -0
  27. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/extract.py +0 -0
  28. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/gittools.py +0 -0
  29. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/graphrag.py +0 -0
  30. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/guards.py +0 -0
  31. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/loop_detect.py +0 -0
  32. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/mcp.py +0 -0
  33. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/poam.py +0 -0
  34. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/providers.py +0 -0
  35. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/rmf.py +0 -0
  36. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/rmf_graph.py +0 -0
  37. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/skills.py +0 -0
  38. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/stig.py +0 -0
  39. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/tool_registry.py +0 -0
  40. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/tui/__init__.py +0 -0
  41. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/tui/app.py +0 -0
  42. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/tui/approval.py +0 -0
  43. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/tui/messages.py +0 -0
  44. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/tui/widgets.py +0 -0
  45. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/tuning.py +0 -0
  46. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock/web.py +0 -0
  47. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock_cli.egg-info/dependency_links.txt +0 -0
  48. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock_cli.egg-info/entry_points.txt +0 -0
  49. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock_cli.egg-info/requires.txt +0 -0
  50. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/drydock_cli.egg-info/top_level.txt +0 -0
  51. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/setup.cfg +0 -0
  52. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_advisor.py +0 -0
  53. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_approval.py +0 -0
  54. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_back_command.py +0 -0
  55. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_bash_background.py +0 -0
  56. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_bash_binary_output.py +0 -0
  57. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_bash_output_bounding.py +0 -0
  58. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_bash_process_group.py +0 -0
  59. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_bash_safety.py +0 -0
  60. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_bash_shell.py +0 -0
  61. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_bash_stdin.py +0 -0
  62. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_bash_timeout_network.py +0 -0
  63. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_cci.py +0 -0
  64. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_cli_agents.py +0 -0
  65. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_compact_command.py +0 -0
  66. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_compaction.py +0 -0
  67. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_config.py +0 -0
  68. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_config_migration.py +0 -0
  69. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_context_limit_config.py +0 -0
  70. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_detect.py +0 -0
  71. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_dispatch.py +0 -0
  72. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_e2e_connected.py +0 -0
  73. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_empty_response.py +0 -0
  74. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_extract.py +0 -0
  75. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_failure_loop.py +0 -0
  76. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_first_run_setup.py +0 -0
  77. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_gittools.py +0 -0
  78. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_graphify_example.py +0 -0
  79. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_graphrag.py +0 -0
  80. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_guards_and_tools.py +0 -0
  81. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_hallucinated_tools.py +0 -0
  82. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_leaked_tool_call.py +0 -0
  83. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_loop_detect.py +0 -0
  84. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_mcp.py +0 -0
  85. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_oneshot_unreachable.py +0 -0
  86. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_plan_autocontinue.py +0 -0
  87. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_poam.py +0 -0
  88. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_providers_unreachable.py +0 -0
  89. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_read_index.py +0 -0
  90. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_rmf.py +0 -0
  91. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_rmf_graph.py +0 -0
  92. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_rmf_stig_graph.py +0 -0
  93. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_runaway_repetition.py +0 -0
  94. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_server_probe.py +0 -0
  95. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_skills.py +0 -0
  96. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_stig.py +0 -0
  97. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_stop.py +0 -0
  98. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_streaming_newlines.py +0 -0
  99. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_subagent.py +0 -0
  100. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_system_prompt_help.py +0 -0
  101. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_todo.py +0 -0
  102. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_tool_arg_parsing.py +0 -0
  103. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_tools_undo.py +0 -0
  104. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_tui.py +0 -0
  105. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_tuning.py +0 -0
  106. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_viewimage.py +0 -0
  107. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_vision_input.py +0 -0
  108. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_web_tools.py +0 -0
  109. {drydock_cli-3.0.93 → drydock_cli-3.0.94}/tests/test_xccdf.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.0.93
3
+ Version: 3.0.94
4
4
  Summary: Drydock — a local, provider-agnostic terminal coding agent for local LLMs
5
5
  Author: Frank Bobe III
6
6
  License-Expression: Apache-2.0
@@ -46,6 +46,20 @@ def _detect_bash() -> str | None:
46
46
  _BASH_SHELL = _detect_bash()
47
47
 
48
48
 
49
+ # ANSI escape sequences (CSI colour/cursor, OSC title, and lone two-char escapes).
50
+ # Some tools emit these even to a pipe (--color=always, forced-colour test runners),
51
+ # and they're pure display control — noise that wastes the model's tokens.
52
+ _ANSI_RE = re.compile(r"\x1b(?:\[[0-9;?]*[ -/]*[@-~]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-Z\\-_])")
53
+
54
+
55
+ def _sanitize_bash_output(text: str) -> str:
56
+ """Strip ANSI escape sequences and drop NUL bytes from command output. NUL is
57
+ valid UTF-8 (so the errors='replace' decode keeps it) but raw NUL in a JSON
58
+ tool-result trips some LLM servers' tokenizers; ANSI codes are display noise.
59
+ Neither carries information the model needs. Newlines/tabs/Unicode are kept."""
60
+ return _ANSI_RE.sub("", text).replace("\x00", "")
61
+
62
+
49
63
  def _collapse_repeated_lines(text: str, run: int = 20) -> str:
50
64
  """Collapse a run of >= `run` IDENTICAL consecutive lines into one line + a
51
65
  count. Repetitive output (`yes`, a spinning progress log) tokenizes densely —
@@ -1013,7 +1027,7 @@ def tool_bash(params: dict, config: dict) -> str:
1013
1027
  # then note if we hit the byte cap. Bounds both RAM (the cap) and context
1014
1028
  # tokens (the collapse). Snapshot chunks (list()) in case the reader
1015
1029
  # daemon is still appending for a backgrounded child.
1016
- output = _collapse_repeated_lines("".join(list(chunks)))
1030
+ output = _sanitize_bash_output(_collapse_repeated_lines("".join(list(chunks))))
1017
1031
  if backgrounded:
1018
1032
  return (output.rstrip() + "\n[a process was left running in the background; "
1019
1033
  "the command returned. Check it with a follow-up command.]").lstrip("\n")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.0.93
3
+ Version: 3.0.94
4
4
  Summary: Drydock — a local, provider-agnostic terminal coding agent for local LLMs
5
5
  Author: Frank Bobe III
6
6
  License-Expression: Apache-2.0
@@ -54,6 +54,7 @@ tests/test_bash_binary_output.py
54
54
  tests/test_bash_output_bounding.py
55
55
  tests/test_bash_process_group.py
56
56
  tests/test_bash_safety.py
57
+ tests/test_bash_sanitize.py
57
58
  tests/test_bash_shell.py
58
59
  tests/test_bash_stdin.py
59
60
  tests/test_bash_timeout_network.py
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
7
7
  # PyPI distribution name is drydock-cli (the established install name, continued
8
8
  # from the retired v2 fork); the import package + CLI command stay `drydock`.
9
9
  name = "drydock-cli"
10
- version = "3.0.93"
10
+ version = "3.0.94"
11
11
  description = "Drydock — a local, provider-agnostic terminal coding agent for local LLMs"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.11"
@@ -0,0 +1,35 @@
1
+ """Bash output is sanitized before the model sees it: ANSI escape sequences
2
+ (colour/cursor control some tools emit even to a pipe) are stripped, and NUL
3
+ bytes (which trip some LLM servers' JSON handling) are dropped. Text/tabs/
4
+ newlines/Unicode are preserved. Found by probing the output I/O path."""
5
+ from __future__ import annotations
6
+
7
+ import pytest
8
+
9
+ from drydock.tools import tool_bash, _sanitize_bash_output
10
+
11
+
12
+ @pytest.mark.parametrize("raw,clean", [
13
+ ("\x1b[31mRED\x1b[0m normal", "RED normal"), # SGR colour
14
+ ("\x1b[2K\x1b[1Gline", "line"), # cursor control
15
+ ("\x1b]0;window title\x07keep", "keep"), # OSC title
16
+ ("a\x00b\x00c", "abc"), # NUL bytes
17
+ ("plain\ttext\nline2 🚢⚓", "plain\ttext\nline2 🚢⚓"), # preserved
18
+ ])
19
+ def test_sanitizer(raw, clean):
20
+ assert _sanitize_bash_output(raw) == clean
21
+
22
+
23
+ def test_end_to_end_forced_color_stripped():
24
+ out = tool_bash({"command": r"printf '\x1b[31mRED\x1b[0m done\n'", "timeout": 8}, {"cwd": "/tmp"})
25
+ assert "RED done" in out and "\x1b" not in out
26
+
27
+
28
+ def test_end_to_end_nul_dropped():
29
+ out = tool_bash({"command": r"printf 'a\x00b\x00c'", "timeout": 8}, {"cwd": "/tmp"})
30
+ assert "\x00" not in out and "abc" in out
31
+
32
+
33
+ def test_valid_unicode_survives():
34
+ out = tool_bash({"command": r"printf '\xf0\x9f\x9a\xa2 ship\n'", "timeout": 8}, {"cwd": "/tmp"})
35
+ assert "🚢" in out
File without changes
File without changes
File without changes
File without changes