drydock-cli 3.0.108__tar.gz → 3.0.109__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.
- {drydock_cli-3.0.108/drydock_cli.egg-info → drydock_cli-3.0.109}/PKG-INFO +1 -1
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/tools/__init__.py +25 -12
- {drydock_cli-3.0.108 → drydock_cli-3.0.109/drydock_cli.egg-info}/PKG-INFO +1 -1
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/pyproject.toml +1 -1
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_windows_shell.py +20 -8
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/LICENSE +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/NOTICE +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/README.md +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/__init__.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/__main__.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/advisor.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/agent.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/bash_safety.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/builtin_skills/__init__.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/builtin_skills/rmf-categorize.md +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/builtin_skills/rmf-control.md +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/builtin_skills/rmf-poam.md +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/builtin_skills/rmf-review.md +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/builtin_skills/stig-assess.md +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/builtin_skills/stig-remediate.md +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/cci.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/cli.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/compaction.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/config.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/detect.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/extract.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/gittools.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/graphrag.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/guards.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/loop_detect.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/mcp.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/poam.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/providers.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/rmf.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/rmf_graph.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/skills.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/stig.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/tool_registry.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/tui/__init__.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/tui/app.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/tui/approval.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/tui/messages.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/tui/widgets.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/tuning.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock/web.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock_cli.egg-info/SOURCES.txt +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock_cli.egg-info/dependency_links.txt +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock_cli.egg-info/entry_points.txt +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock_cli.egg-info/requires.txt +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/drydock_cli.egg-info/top_level.txt +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/setup.cfg +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_advisor.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_approval.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_back_command.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_bash_background.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_bash_binary_output.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_bash_crossplatform.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_bash_output_bounding.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_bash_process_group.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_bash_safety.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_bash_sanitize.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_bash_shell.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_bash_stdin.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_bash_stop_partial.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_bash_timeout_network.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_bash_timeout_param.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_cci.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_cli_agents.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_compact_command.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_compaction.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_config.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_config_migration.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_context_limit_config.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_detect.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_dispatch.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_e2e_connected.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_edit_replace_all.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_empty_response.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_extract.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_failure_loop.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_first_run_setup.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_gittools.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_glob_edit_edges.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_graphify_example.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_graphrag.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_grep_and_read_robust.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_guards_and_tools.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_hallucinated_tools.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_leaked_tool_call.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_loop_detect.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_mcp.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_oneshot_unreachable.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_overthink_interrupt.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_plan_autocontinue.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_poam.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_providers_unreachable.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_read_index.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_rmf.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_rmf_graph.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_rmf_stig_graph.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_runaway_repetition.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_server_probe.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_skills.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_stall_retry.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_stig.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_stop.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_streaming_newlines.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_subagent.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_system_prompt_help.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_todo.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_tool_arg_coercion.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_tool_arg_coercion_more.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_tool_arg_parsing.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_tools_undo.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_tui.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_tuning.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_viewimage.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_vision_input.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_web_tools.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_write_content_coerce.py +0 -0
- {drydock_cli-3.0.108 → drydock_cli-3.0.109}/tests/test_xccdf.py +0 -0
|
@@ -6,6 +6,7 @@ Each tool is a function (params, config) -> str.
|
|
|
6
6
|
from __future__ import annotations
|
|
7
7
|
|
|
8
8
|
import os
|
|
9
|
+
import sys
|
|
9
10
|
import re
|
|
10
11
|
import difflib
|
|
11
12
|
import glob as _glob
|
|
@@ -92,28 +93,40 @@ def _detect_bash() -> str | None:
|
|
|
92
93
|
return None
|
|
93
94
|
|
|
94
95
|
|
|
95
|
-
|
|
96
|
+
# Windows even if a non-native Python (rare) reports a posix os.name — sys.platform
|
|
97
|
+
# is the reliable signal on CPython.
|
|
98
|
+
_IS_WINDOWS = os.name == "nt" or sys.platform.startswith("win")
|
|
96
99
|
|
|
97
100
|
|
|
98
101
|
def _detect_shell() -> tuple[str, str]:
|
|
99
102
|
"""Pick the shell tool_bash runs commands in — returns (kind, path).
|
|
100
103
|
|
|
104
|
+
Override with env DRYDOCK_SHELL=powershell|cmd|bash to force one. Otherwise:
|
|
101
105
|
POSIX → bash (so the model's bash-isms work), else /bin/sh.
|
|
102
106
|
Windows → PowerShell (what users actually launch drydock in — pwsh, else
|
|
103
|
-
Windows PowerShell), else cmd.exe. WSL / Git-Bash is
|
|
104
|
-
if you'd rather use them, install bash and it's picked up on POSIX-like paths.
|
|
107
|
+
Windows PowerShell), else cmd.exe. WSL / Git-Bash is NEVER required on Windows.
|
|
105
108
|
"""
|
|
106
109
|
import shutil
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
110
|
+
|
|
111
|
+
def _powershell():
|
|
112
|
+
p = shutil.which("pwsh") or shutil.which("powershell")
|
|
113
|
+
return ("powershell", p) if p else None
|
|
114
|
+
|
|
115
|
+
def _bash():
|
|
116
|
+
b = _detect_bash()
|
|
117
|
+
return ("bash", b) if b else ("sh", shutil.which("sh") or "/bin/sh")
|
|
118
|
+
|
|
119
|
+
forced = os.environ.get("DRYDOCK_SHELL", "").strip().lower()
|
|
120
|
+
if forced in ("powershell", "pwsh"):
|
|
121
|
+
return _powershell() or ("cmd", shutil.which("cmd") or "cmd.exe")
|
|
122
|
+
if forced == "cmd":
|
|
112
123
|
return ("cmd", shutil.which("cmd") or "cmd.exe")
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
124
|
+
if forced in ("bash", "sh"):
|
|
125
|
+
return _bash()
|
|
126
|
+
|
|
127
|
+
if _IS_WINDOWS:
|
|
128
|
+
return _powershell() or ("cmd", shutil.which("cmd") or "cmd.exe")
|
|
129
|
+
return _bash()
|
|
117
130
|
|
|
118
131
|
|
|
119
132
|
# Resolved once at import (in whatever environment drydock runs — Linux/macOS,
|
|
@@ -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.
|
|
10
|
+
version = "3.0.109"
|
|
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"
|
|
@@ -32,21 +32,16 @@ def test_powershell_invocation_argv(monkeypatch):
|
|
|
32
32
|
"""When the shell is PowerShell, Popen is called [pwsh -NoProfile
|
|
33
33
|
-NonInteractive -Command <cmd>] with shell=False — not shell=True."""
|
|
34
34
|
seen = {}
|
|
35
|
-
real = subprocess.Popen
|
|
36
35
|
|
|
37
|
-
class FakeProc:
|
|
38
|
-
stdout = None
|
|
39
|
-
pid = 1
|
|
40
|
-
def wait(self): return 0
|
|
41
|
-
def poll(self): return 0
|
|
42
36
|
def spy(popen_cmd, *a, **kw):
|
|
43
|
-
seen["cmd"] = popen_cmd
|
|
37
|
+
seen["cmd"] = popen_cmd
|
|
38
|
+
seen["shell"] = kw.get("shell")
|
|
44
39
|
raise RuntimeError("stop-after-capture") # don't actually run pwsh on Linux
|
|
45
40
|
|
|
46
41
|
monkeypatch.setattr(T, "_SHELL_KIND", "powershell")
|
|
47
42
|
monkeypatch.setattr(T, "_SHELL_PATH", r"C:\ps\pwsh.exe")
|
|
48
43
|
monkeypatch.setattr(subprocess, "Popen", spy)
|
|
49
|
-
|
|
44
|
+
T.tool_bash({"command": "Get-ChildItem", "timeout": 5}, {"cwd": "."})
|
|
50
45
|
assert seen["shell"] is False
|
|
51
46
|
assert seen["cmd"][:4] == [r"C:\ps\pwsh.exe", "-NoProfile", "-NonInteractive", "-Command"]
|
|
52
47
|
assert seen["cmd"][4] == "Get-ChildItem"
|
|
@@ -65,3 +60,20 @@ def test_prompt_note_windows_cmd():
|
|
|
65
60
|
|
|
66
61
|
def test_prompt_note_empty_on_posix():
|
|
67
62
|
assert tuning._shell_env_note() == ""
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def test_env_override_forces_powershell():
|
|
66
|
+
with mock.patch.dict("os.environ", {"DRYDOCK_SHELL": "powershell"}), \
|
|
67
|
+
mock.patch("shutil.which", side_effect=lambda n: r"C:\pwsh.exe" if n in ("pwsh", "powershell") else None):
|
|
68
|
+
assert T._detect_shell()[0] == "powershell"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def test_env_override_forces_cmd():
|
|
72
|
+
with mock.patch.dict("os.environ", {"DRYDOCK_SHELL": "cmd"}), \
|
|
73
|
+
mock.patch("shutil.which", return_value=r"C:\cmd.exe"):
|
|
74
|
+
assert T._detect_shell()[0] == "cmd"
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def test_env_override_forces_bash():
|
|
78
|
+
with mock.patch.dict("os.environ", {"DRYDOCK_SHELL": "bash"}):
|
|
79
|
+
assert T._detect_shell()[0] in ("bash", "sh")
|
|
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
|
|
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
|