drydock-cli 3.0.101__tar.gz → 3.0.103__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 (117) hide show
  1. {drydock_cli-3.0.101/drydock_cli.egg-info → drydock_cli-3.0.103}/PKG-INFO +1 -1
  2. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/tools/__init__.py +15 -8
  3. {drydock_cli-3.0.101 → drydock_cli-3.0.103/drydock_cli.egg-info}/PKG-INFO +1 -1
  4. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock_cli.egg-info/SOURCES.txt +2 -0
  5. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/pyproject.toml +1 -1
  6. drydock_cli-3.0.103/tests/test_glob_edit_edges.py +28 -0
  7. drydock_cli-3.0.103/tests/test_tool_arg_coercion_more.py +37 -0
  8. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/LICENSE +0 -0
  9. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/NOTICE +0 -0
  10. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/README.md +0 -0
  11. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/__init__.py +0 -0
  12. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/__main__.py +0 -0
  13. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/advisor.py +0 -0
  14. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/agent.py +0 -0
  15. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/bash_safety.py +0 -0
  16. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/builtin_skills/__init__.py +0 -0
  17. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/builtin_skills/rmf-categorize.md +0 -0
  18. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/builtin_skills/rmf-control.md +0 -0
  19. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/builtin_skills/rmf-poam.md +0 -0
  20. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/builtin_skills/rmf-review.md +0 -0
  21. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/builtin_skills/stig-assess.md +0 -0
  22. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/builtin_skills/stig-remediate.md +0 -0
  23. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/cci.py +0 -0
  24. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/cli.py +0 -0
  25. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/compaction.py +0 -0
  26. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/config.py +0 -0
  27. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/detect.py +0 -0
  28. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/extract.py +0 -0
  29. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/gittools.py +0 -0
  30. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/graphrag.py +0 -0
  31. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/guards.py +0 -0
  32. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/loop_detect.py +0 -0
  33. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/mcp.py +0 -0
  34. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/poam.py +0 -0
  35. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/providers.py +0 -0
  36. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/rmf.py +0 -0
  37. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/rmf_graph.py +0 -0
  38. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/skills.py +0 -0
  39. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/stig.py +0 -0
  40. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/tool_registry.py +0 -0
  41. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/tui/__init__.py +0 -0
  42. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/tui/app.py +0 -0
  43. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/tui/approval.py +0 -0
  44. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/tui/messages.py +0 -0
  45. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/tui/widgets.py +0 -0
  46. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/tuning.py +0 -0
  47. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock/web.py +0 -0
  48. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock_cli.egg-info/dependency_links.txt +0 -0
  49. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock_cli.egg-info/entry_points.txt +0 -0
  50. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock_cli.egg-info/requires.txt +0 -0
  51. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/drydock_cli.egg-info/top_level.txt +0 -0
  52. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/setup.cfg +0 -0
  53. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_advisor.py +0 -0
  54. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_approval.py +0 -0
  55. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_back_command.py +0 -0
  56. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_bash_background.py +0 -0
  57. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_bash_binary_output.py +0 -0
  58. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_bash_output_bounding.py +0 -0
  59. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_bash_process_group.py +0 -0
  60. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_bash_safety.py +0 -0
  61. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_bash_sanitize.py +0 -0
  62. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_bash_shell.py +0 -0
  63. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_bash_stdin.py +0 -0
  64. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_bash_stop_partial.py +0 -0
  65. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_bash_timeout_network.py +0 -0
  66. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_bash_timeout_param.py +0 -0
  67. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_cci.py +0 -0
  68. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_cli_agents.py +0 -0
  69. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_compact_command.py +0 -0
  70. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_compaction.py +0 -0
  71. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_config.py +0 -0
  72. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_config_migration.py +0 -0
  73. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_context_limit_config.py +0 -0
  74. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_detect.py +0 -0
  75. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_dispatch.py +0 -0
  76. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_e2e_connected.py +0 -0
  77. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_edit_replace_all.py +0 -0
  78. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_empty_response.py +0 -0
  79. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_extract.py +0 -0
  80. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_failure_loop.py +0 -0
  81. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_first_run_setup.py +0 -0
  82. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_gittools.py +0 -0
  83. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_graphify_example.py +0 -0
  84. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_graphrag.py +0 -0
  85. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_grep_and_read_robust.py +0 -0
  86. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_guards_and_tools.py +0 -0
  87. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_hallucinated_tools.py +0 -0
  88. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_leaked_tool_call.py +0 -0
  89. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_loop_detect.py +0 -0
  90. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_mcp.py +0 -0
  91. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_oneshot_unreachable.py +0 -0
  92. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_plan_autocontinue.py +0 -0
  93. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_poam.py +0 -0
  94. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_providers_unreachable.py +0 -0
  95. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_read_index.py +0 -0
  96. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_rmf.py +0 -0
  97. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_rmf_graph.py +0 -0
  98. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_rmf_stig_graph.py +0 -0
  99. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_runaway_repetition.py +0 -0
  100. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_server_probe.py +0 -0
  101. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_skills.py +0 -0
  102. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_stig.py +0 -0
  103. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_stop.py +0 -0
  104. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_streaming_newlines.py +0 -0
  105. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_subagent.py +0 -0
  106. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_system_prompt_help.py +0 -0
  107. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_todo.py +0 -0
  108. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_tool_arg_coercion.py +0 -0
  109. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_tool_arg_parsing.py +0 -0
  110. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_tools_undo.py +0 -0
  111. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_tui.py +0 -0
  112. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_tuning.py +0 -0
  113. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_viewimage.py +0 -0
  114. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_vision_input.py +0 -0
  115. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_web_tools.py +0 -0
  116. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/tests/test_write_content_coerce.py +0 -0
  117. {drydock_cli-3.0.101 → drydock_cli-3.0.103}/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.101
3
+ Version: 3.0.103
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
@@ -833,6 +833,11 @@ def tool_edit(params: dict, config: dict) -> str:
833
833
  # crashing with a TypeError in the substring search / replace.
834
834
  old = _as_text(params.get("old_string", ""))
835
835
  new = _as_text(params.get("new_string", ""))
836
+ if old == "":
837
+ # An empty old_string matches between every char (count = len+1) → the
838
+ # generic "found N times" is baffling. Say what's actually wrong.
839
+ return ("Error: old_string is empty. Edit replaces existing text — give the "
840
+ "exact text to find. To create a file or add to the end, use Write.")
836
841
  if has_conflict_markers(new):
837
842
  return conflict_marker_refusal(fp)
838
843
  # If a directory was passed, try to infer the intended file (avoids a
@@ -1138,8 +1143,10 @@ def tool_bash(params: dict, config: dict) -> str:
1138
1143
 
1139
1144
 
1140
1145
  def tool_glob(params: dict, config: dict) -> str:
1141
- pattern = params["pattern"]
1142
- base = params.get("path") or config.get("cwd") or "."
1146
+ pattern = _as_str_arg(params.get("pattern"))
1147
+ if not pattern:
1148
+ return "Error: Glob needs a 'pattern' (e.g. '**/*.py')."
1149
+ base = _as_str_arg(params.get("path")) or config.get("cwd") or "."
1143
1150
  try:
1144
1151
  matches = sorted(_glob.glob(os.path.join(base, pattern), recursive=True))
1145
1152
  if not matches:
@@ -1154,7 +1161,7 @@ def tool_glob(params: dict, config: dict) -> str:
1154
1161
  def tool_grep(params: dict, config: dict) -> str:
1155
1162
  pattern = _as_str_arg(params.get("pattern"))
1156
1163
  path = _as_str_arg(params.get("path")) or config.get("cwd") or "."
1157
- include = params.get("include", "")
1164
+ include = _as_str_arg(params.get("include", ""))
1158
1165
  try:
1159
1166
  cmd = ["grep", "-rn", "--color=never"]
1160
1167
  if include:
@@ -1249,7 +1256,7 @@ def tool_consult(params: dict, config: dict) -> str:
1249
1256
  """Ask the configured second/advisor model (e.g. Gemini) for a second opinion."""
1250
1257
  from drydock import advisor
1251
1258
 
1252
- question = (params.get("question") or params.get("prompt") or "").strip()
1259
+ question = _as_str_arg(params.get("question") or params.get("prompt")).strip()
1253
1260
  if not question:
1254
1261
  return "Error: Consult needs a `question`."
1255
1262
  return advisor.consult(question, config, context=params.get("context", "") or "")
@@ -1391,7 +1398,7 @@ def tool_gitcommit(params: dict, config: dict) -> str:
1391
1398
  try:
1392
1399
  return gittools.commit(
1393
1400
  _git_cwd(config),
1394
- params.get("message") or "",
1401
+ _as_text(params.get("message")),
1395
1402
  add_all=params.get("add_all", True) is not False,
1396
1403
  )
1397
1404
  except gittools.GitError as e:
@@ -1571,7 +1578,7 @@ def tool_websearch(params: dict, config: dict) -> str:
1571
1578
  """Search the internet (DuckDuckGo). Read-only; clean message when offline."""
1572
1579
  from drydock import web
1573
1580
 
1574
- query = (params.get("query") or "").strip()
1581
+ query = _as_str_arg(params.get("query")).strip()
1575
1582
  if not query:
1576
1583
  return "Error: `WebSearch` needs a `query`."
1577
1584
  try:
@@ -1589,7 +1596,7 @@ def tool_webfetch(params: dict, config: dict) -> str:
1589
1596
  """Fetch a URL and return readable text. Read-only; clean message offline."""
1590
1597
  from drydock import web
1591
1598
 
1592
- url = (params.get("url") or "").strip()
1599
+ url = _as_str_arg(params.get("url")).strip()
1593
1600
  if not url:
1594
1601
  return "Error: `WebFetch` needs a `url`."
1595
1602
  try:
@@ -1609,7 +1616,7 @@ def tool_knowledge(params: dict, config: dict) -> str:
1609
1616
  If no index exists, says so cleanly rather than erroring."""
1610
1617
  from drydock import graphrag
1611
1618
 
1612
- query = (params.get("query") or "").strip()
1619
+ query = _as_str_arg(params.get("query")).strip()
1613
1620
  if not query:
1614
1621
  return "Error: `Knowledge` needs a `query` describing what to look up."
1615
1622
  cwd = config.get("cwd") or os.getcwd()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.0.101
3
+ Version: 3.0.103
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_extract.py
76
76
  tests/test_failure_loop.py
77
77
  tests/test_first_run_setup.py
78
78
  tests/test_gittools.py
79
+ tests/test_glob_edit_edges.py
79
80
  tests/test_graphify_example.py
80
81
  tests/test_graphrag.py
81
82
  tests/test_grep_and_read_robust.py
@@ -102,6 +103,7 @@ tests/test_subagent.py
102
103
  tests/test_system_prompt_help.py
103
104
  tests/test_todo.py
104
105
  tests/test_tool_arg_coercion.py
106
+ tests/test_tool_arg_coercion_more.py
105
107
  tests/test_tool_arg_parsing.py
106
108
  tests/test_tools_undo.py
107
109
  tests/test_tui.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.101"
10
+ version = "3.0.103"
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,28 @@
1
+ """More edge cases from probing: Glob must not crash on a missing/list pattern,
2
+ and an empty Edit old_string should say what's wrong (not the baffling generic
3
+ 'found N times')."""
4
+ from __future__ import annotations
5
+
6
+ from drydock.tools import tool_glob, tool_edit
7
+
8
+
9
+ def test_glob_missing_pattern_is_error_not_crash():
10
+ out = tool_glob({}, {"cwd": "/tmp"})
11
+ assert out.startswith("Error:") and "pattern" in out
12
+
13
+
14
+ def test_glob_list_pattern_unwrapped(tmp_path):
15
+ (tmp_path / "a.py").write_text("x")
16
+ out = tool_glob({"pattern": ["*.py"], "path": str(tmp_path)}, {"cwd": str(tmp_path)})
17
+ assert "a.py" in out
18
+
19
+
20
+ def test_glob_no_matches(tmp_path):
21
+ out = tool_glob({"pattern": "*.nope", "path": str(tmp_path)}, {"cwd": str(tmp_path)})
22
+ assert out.strip() == "(no matches)"
23
+
24
+
25
+ def test_edit_empty_old_string_clear_error(tmp_path):
26
+ f = tmp_path / "e.txt"; f.write_text("hello\n")
27
+ out = tool_edit({"file_path": str(f), "old_string": "", "new_string": "X"}, {"cwd": str(tmp_path)})
28
+ assert "empty" in out.lower() and f.read_text() == "hello\n"
@@ -0,0 +1,37 @@
1
+ """Wrong-type args crashed more tools with AttributeError ('.strip()' on a
2
+ list/int): GitCommit message, Knowledge query, Consult question, WebFetch url,
3
+ WebSearch query. All coerced now — no tool should raise on a wrong-type arg."""
4
+ from __future__ import annotations
5
+
6
+ import drydock.tools as T
7
+
8
+
9
+ def _no_crash(fn, **kw):
10
+ out = fn(kw, {"cwd": "/tmp"}) # must return a string, not raise
11
+ assert isinstance(out, str)
12
+ return out
13
+
14
+
15
+ def test_gitcommit_message_list():
16
+ _no_crash(T.tool_gitcommit, message=["line1", "line2"])
17
+
18
+
19
+ def test_gitcommit_message_int():
20
+ _no_crash(T.tool_gitcommit, message=42)
21
+
22
+
23
+ def test_knowledge_query_list():
24
+ _no_crash(T.tool_knowledge, query=["find x"])
25
+
26
+
27
+ def test_consult_question_list():
28
+ _no_crash(T.tool_consult, question=["help me"])
29
+
30
+
31
+ def test_webfetch_url_list():
32
+ _no_crash(T.tool_webfetch, url=["http://example.invalid"])
33
+
34
+
35
+ def test_websearch_query_int():
36
+ out = _no_crash(T.tool_websearch, query=42)
37
+ assert "42" in out
File without changes
File without changes
File without changes
File without changes