drydock-cli 3.0.96__tar.gz → 3.0.97__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 (111) hide show
  1. {drydock_cli-3.0.96/drydock_cli.egg-info → drydock_cli-3.0.97}/PKG-INFO +1 -1
  2. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/tools/__init__.py +13 -3
  3. {drydock_cli-3.0.96 → drydock_cli-3.0.97/drydock_cli.egg-info}/PKG-INFO +1 -1
  4. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock_cli.egg-info/SOURCES.txt +1 -0
  5. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/pyproject.toml +1 -1
  6. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_bash_timeout_param.py +0 -1
  7. drydock_cli-3.0.97/tests/test_grep_and_read_robust.py +43 -0
  8. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/LICENSE +0 -0
  9. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/NOTICE +0 -0
  10. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/README.md +0 -0
  11. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/__init__.py +0 -0
  12. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/__main__.py +0 -0
  13. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/advisor.py +0 -0
  14. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/agent.py +0 -0
  15. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/bash_safety.py +0 -0
  16. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/builtin_skills/__init__.py +0 -0
  17. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/builtin_skills/rmf-categorize.md +0 -0
  18. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/builtin_skills/rmf-control.md +0 -0
  19. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/builtin_skills/rmf-poam.md +0 -0
  20. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/builtin_skills/rmf-review.md +0 -0
  21. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/builtin_skills/stig-assess.md +0 -0
  22. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/builtin_skills/stig-remediate.md +0 -0
  23. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/cci.py +0 -0
  24. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/cli.py +0 -0
  25. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/compaction.py +0 -0
  26. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/config.py +0 -0
  27. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/detect.py +0 -0
  28. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/extract.py +0 -0
  29. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/gittools.py +0 -0
  30. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/graphrag.py +0 -0
  31. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/guards.py +0 -0
  32. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/loop_detect.py +0 -0
  33. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/mcp.py +0 -0
  34. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/poam.py +0 -0
  35. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/providers.py +0 -0
  36. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/rmf.py +0 -0
  37. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/rmf_graph.py +0 -0
  38. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/skills.py +0 -0
  39. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/stig.py +0 -0
  40. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/tool_registry.py +0 -0
  41. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/tui/__init__.py +0 -0
  42. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/tui/app.py +0 -0
  43. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/tui/approval.py +0 -0
  44. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/tui/messages.py +0 -0
  45. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/tui/widgets.py +0 -0
  46. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/tuning.py +0 -0
  47. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock/web.py +0 -0
  48. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock_cli.egg-info/dependency_links.txt +0 -0
  49. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock_cli.egg-info/entry_points.txt +0 -0
  50. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock_cli.egg-info/requires.txt +0 -0
  51. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/drydock_cli.egg-info/top_level.txt +0 -0
  52. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/setup.cfg +0 -0
  53. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_advisor.py +0 -0
  54. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_approval.py +0 -0
  55. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_back_command.py +0 -0
  56. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_bash_background.py +0 -0
  57. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_bash_binary_output.py +0 -0
  58. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_bash_output_bounding.py +0 -0
  59. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_bash_process_group.py +0 -0
  60. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_bash_safety.py +0 -0
  61. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_bash_sanitize.py +0 -0
  62. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_bash_shell.py +0 -0
  63. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_bash_stdin.py +0 -0
  64. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_bash_timeout_network.py +0 -0
  65. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_cci.py +0 -0
  66. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_cli_agents.py +0 -0
  67. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_compact_command.py +0 -0
  68. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_compaction.py +0 -0
  69. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_config.py +0 -0
  70. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_config_migration.py +0 -0
  71. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_context_limit_config.py +0 -0
  72. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_detect.py +0 -0
  73. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_dispatch.py +0 -0
  74. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_e2e_connected.py +0 -0
  75. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_empty_response.py +0 -0
  76. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_extract.py +0 -0
  77. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_failure_loop.py +0 -0
  78. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_first_run_setup.py +0 -0
  79. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_gittools.py +0 -0
  80. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_graphify_example.py +0 -0
  81. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_graphrag.py +0 -0
  82. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_guards_and_tools.py +0 -0
  83. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_hallucinated_tools.py +0 -0
  84. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_leaked_tool_call.py +0 -0
  85. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_loop_detect.py +0 -0
  86. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_mcp.py +0 -0
  87. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_oneshot_unreachable.py +0 -0
  88. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_plan_autocontinue.py +0 -0
  89. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_poam.py +0 -0
  90. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_providers_unreachable.py +0 -0
  91. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_read_index.py +0 -0
  92. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_rmf.py +0 -0
  93. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_rmf_graph.py +0 -0
  94. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_rmf_stig_graph.py +0 -0
  95. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_runaway_repetition.py +0 -0
  96. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_server_probe.py +0 -0
  97. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_skills.py +0 -0
  98. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_stig.py +0 -0
  99. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_stop.py +0 -0
  100. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_streaming_newlines.py +0 -0
  101. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_subagent.py +0 -0
  102. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_system_prompt_help.py +0 -0
  103. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_todo.py +0 -0
  104. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_tool_arg_parsing.py +0 -0
  105. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_tools_undo.py +0 -0
  106. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_tui.py +0 -0
  107. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_tuning.py +0 -0
  108. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_viewimage.py +0 -0
  109. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_vision_input.py +0 -0
  110. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/tests/test_web_tools.py +0 -0
  111. {drydock_cli-3.0.96 → drydock_cli-3.0.97}/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.96
3
+ Version: 3.0.97
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
@@ -601,7 +601,10 @@ def tool_read(params: dict, config: dict) -> str:
601
601
  eff_limit = 2000 if limit is None else limit
602
602
  selected = lines[offset:offset + eff_limit]
603
603
  numbered = [f"{i + offset + 1}\t{line.rstrip()}" for i, line in enumerate(selected)]
604
- result = "\n".join(numbered)
604
+ # Drop NUL bytes (valid UTF-8, so errors='replace' keeps them) — raw NUL in
605
+ # a JSON tool result trips some LLM servers. ANSI is left as-is: unlike
606
+ # command output, a file's bytes are content the model may need verbatim.
607
+ result = "\n".join(numbered).replace("\x00", "")
605
608
  if len(lines) > offset + eff_limit:
606
609
  result += f"\n[... {len(lines) - offset - eff_limit} more lines]"
607
610
  return result or "(empty file)"
@@ -1104,9 +1107,16 @@ def tool_grep(params: dict, config: dict) -> str:
1104
1107
  cmd.extend(["--include", include])
1105
1108
  cmd.extend([pattern, path])
1106
1109
  result = subprocess.run(
1107
- cmd, capture_output=True, text=True, timeout=30,
1110
+ cmd, capture_output=True, text=True, encoding="utf-8",
1111
+ errors="replace", timeout=30,
1108
1112
  )
1109
- output = result.stdout.strip()
1113
+ # grep exit codes: 0 = matched, 1 = no match, >=2 = ERROR (invalid regex,
1114
+ # unreadable path). Don't report an error as "(no matches)" — the model
1115
+ # would wrongly conclude the pattern is absent instead of fixing it.
1116
+ if result.returncode >= 2:
1117
+ err = (result.stderr or "").strip() or "grep failed"
1118
+ return f"Error: {err.splitlines()[0]}"
1119
+ output = _sanitize_bash_output(result.stdout).strip()
1110
1120
  if not output:
1111
1121
  return "(no matches)"
1112
1122
  lines = output.split("\n")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.0.96
3
+ Version: 3.0.97
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
@@ -76,6 +76,7 @@ tests/test_first_run_setup.py
76
76
  tests/test_gittools.py
77
77
  tests/test_graphify_example.py
78
78
  tests/test_graphrag.py
79
+ tests/test_grep_and_read_robust.py
79
80
  tests/test_guards_and_tools.py
80
81
  tests/test_hallucinated_tools.py
81
82
  tests/test_leaked_tool_call.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.96"
10
+ version = "3.0.97"
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"
@@ -3,7 +3,6 @@ string ("10"); a 0/negative value made EVERY command time out instantly; an
3
3
  absurd value could hang for hours. _coerce_timeout normalizes all of these."""
4
4
  from __future__ import annotations
5
5
 
6
- import time
7
6
 
8
7
  import pytest
9
8
 
@@ -0,0 +1,43 @@
1
+ """tool_grep must not report a grep ERROR (invalid regex, unreadable path — grep
2
+ exit >=2) as "(no matches)"; and both grep and Read must be binary-safe (no crash,
3
+ no raw NUL). Found by probing the grep/read I/O paths."""
4
+ from __future__ import annotations
5
+
6
+ from drydock.tools import tool_grep, tool_read
7
+
8
+
9
+ def _grep(pattern, path):
10
+ return tool_grep({"pattern": pattern, "path": path}, {"cwd": "/tmp"})
11
+
12
+
13
+ def test_grep_valid_match(tmp_path):
14
+ f = tmp_path / "a.txt"; f.write_text("hello world\nfoo\n")
15
+ assert "hello world" in _grep("hello", str(f))
16
+
17
+
18
+ def test_grep_genuine_no_match(tmp_path):
19
+ f = tmp_path / "a.txt"; f.write_text("hello\n")
20
+ assert _grep("zzznope", str(f)).strip() == "(no matches)"
21
+
22
+
23
+ def test_grep_invalid_regex_is_error_not_no_matches(tmp_path):
24
+ f = tmp_path / "a.txt"; f.write_text("hello\n")
25
+ out = _grep("[unclosed", str(f))
26
+ assert out.startswith("Error:") and "no matches" not in out
27
+
28
+
29
+ def test_grep_missing_path_is_error(tmp_path):
30
+ out = _grep("x", str(tmp_path / "nope_xyz"))
31
+ assert out.startswith("Error:")
32
+
33
+
34
+ def test_grep_binary_does_not_crash(tmp_path):
35
+ f = tmp_path / "b.bin"; f.write_bytes(b"\xff\xfe match \x00\xc0\n")
36
+ out = _grep("match", str(f))
37
+ assert "Error:" not in out or "no matches" in out or "match" in out # no exception
38
+
39
+
40
+ def test_read_drops_nul_bytes(tmp_path):
41
+ f = tmp_path / "n.txt"; f.write_bytes(b"before\x00\x00after text\n")
42
+ out = tool_read({"file_path": str(f)}, {"cwd": str(tmp_path)})
43
+ assert "\x00" not in out and "after text" in out
File without changes
File without changes
File without changes
File without changes