drydock-cli 3.0.90__tar.gz → 3.0.92__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 (107) hide show
  1. {drydock_cli-3.0.90/drydock_cli.egg-info → drydock_cli-3.0.92}/PKG-INFO +5 -2
  2. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/README.md +4 -1
  3. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/tools/__init__.py +25 -1
  4. {drydock_cli-3.0.90 → drydock_cli-3.0.92/drydock_cli.egg-info}/PKG-INFO +5 -2
  5. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock_cli.egg-info/SOURCES.txt +2 -0
  6. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/pyproject.toml +1 -1
  7. drydock_cli-3.0.92/tests/test_bash_shell.py +40 -0
  8. drydock_cli-3.0.92/tests/test_graphify_example.py +36 -0
  9. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/LICENSE +0 -0
  10. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/NOTICE +0 -0
  11. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/__init__.py +0 -0
  12. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/__main__.py +0 -0
  13. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/advisor.py +0 -0
  14. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/agent.py +0 -0
  15. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/bash_safety.py +0 -0
  16. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/builtin_skills/__init__.py +0 -0
  17. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/builtin_skills/rmf-categorize.md +0 -0
  18. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/builtin_skills/rmf-control.md +0 -0
  19. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/builtin_skills/rmf-poam.md +0 -0
  20. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/builtin_skills/rmf-review.md +0 -0
  21. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/builtin_skills/stig-assess.md +0 -0
  22. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/builtin_skills/stig-remediate.md +0 -0
  23. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/cci.py +0 -0
  24. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/cli.py +0 -0
  25. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/compaction.py +0 -0
  26. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/config.py +0 -0
  27. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/detect.py +0 -0
  28. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/extract.py +0 -0
  29. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/gittools.py +0 -0
  30. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/graphrag.py +0 -0
  31. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/guards.py +0 -0
  32. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/loop_detect.py +0 -0
  33. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/mcp.py +0 -0
  34. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/poam.py +0 -0
  35. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/providers.py +0 -0
  36. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/rmf.py +0 -0
  37. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/rmf_graph.py +0 -0
  38. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/skills.py +0 -0
  39. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/stig.py +0 -0
  40. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/tool_registry.py +0 -0
  41. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/tui/__init__.py +0 -0
  42. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/tui/app.py +0 -0
  43. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/tui/approval.py +0 -0
  44. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/tui/messages.py +0 -0
  45. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/tui/widgets.py +0 -0
  46. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/tuning.py +0 -0
  47. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock/web.py +0 -0
  48. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock_cli.egg-info/dependency_links.txt +0 -0
  49. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock_cli.egg-info/entry_points.txt +0 -0
  50. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock_cli.egg-info/requires.txt +0 -0
  51. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/drydock_cli.egg-info/top_level.txt +0 -0
  52. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/setup.cfg +0 -0
  53. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_advisor.py +0 -0
  54. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_approval.py +0 -0
  55. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_back_command.py +0 -0
  56. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_bash_background.py +0 -0
  57. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_bash_output_bounding.py +0 -0
  58. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_bash_process_group.py +0 -0
  59. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_bash_safety.py +0 -0
  60. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_bash_stdin.py +0 -0
  61. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_bash_timeout_network.py +0 -0
  62. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_cci.py +0 -0
  63. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_cli_agents.py +0 -0
  64. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_compact_command.py +0 -0
  65. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_compaction.py +0 -0
  66. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_config.py +0 -0
  67. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_config_migration.py +0 -0
  68. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_context_limit_config.py +0 -0
  69. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_detect.py +0 -0
  70. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_dispatch.py +0 -0
  71. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_e2e_connected.py +0 -0
  72. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_empty_response.py +0 -0
  73. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_extract.py +0 -0
  74. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_failure_loop.py +0 -0
  75. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_first_run_setup.py +0 -0
  76. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_gittools.py +0 -0
  77. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_graphrag.py +0 -0
  78. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_guards_and_tools.py +0 -0
  79. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_hallucinated_tools.py +0 -0
  80. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_leaked_tool_call.py +0 -0
  81. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_loop_detect.py +0 -0
  82. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_mcp.py +0 -0
  83. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_oneshot_unreachable.py +0 -0
  84. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_plan_autocontinue.py +0 -0
  85. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_poam.py +0 -0
  86. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_providers_unreachable.py +0 -0
  87. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_read_index.py +0 -0
  88. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_rmf.py +0 -0
  89. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_rmf_graph.py +0 -0
  90. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_rmf_stig_graph.py +0 -0
  91. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_runaway_repetition.py +0 -0
  92. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_server_probe.py +0 -0
  93. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_skills.py +0 -0
  94. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_stig.py +0 -0
  95. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_stop.py +0 -0
  96. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_streaming_newlines.py +0 -0
  97. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_subagent.py +0 -0
  98. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_system_prompt_help.py +0 -0
  99. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_todo.py +0 -0
  100. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_tool_arg_parsing.py +0 -0
  101. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_tools_undo.py +0 -0
  102. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_tui.py +0 -0
  103. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_tuning.py +0 -0
  104. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_viewimage.py +0 -0
  105. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_vision_input.py +0 -0
  106. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/tests/test_web_tools.py +0 -0
  107. {drydock_cli-3.0.90 → drydock_cli-3.0.92}/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.90
3
+ Version: 3.0.92
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
@@ -77,7 +77,10 @@ A full agentic CLI harness — every tool below is clean-room and dependency-fre
77
77
  calls the `Consult` tool for a second opinion when stuck, and you can `/ask
78
78
  <question>` directly. Opt-in, user-configured — no extra dependency.
79
79
  - **MCP** — connect to Model Context Protocol servers (`~/.drydock/mcp.json`);
80
- their tools appear as `mcp__<server>__<tool>`. List them with `/mcp`.
80
+ their tools appear as `mcp__<server>__<tool>`. List them with `/mcp`. Works with
81
+ third-party servers out of the box — e.g. [Graphify](https://github.com/safishamsi/graphify)
82
+ for a queryable code knowledge graph: see [docs/graphify.md](docs/graphify.md)
83
+ and the copy-paste [example config](examples/mcp/graphify.json).
81
84
  - **Skills** — reusable `/<name>` commands authored as markdown in
82
85
  `~/.drydock/skills/` (or `<project>/.drydock/skills/`); `$ARGS` substitution.
83
86
  - **Loops** — `/loop <count> <prompt>` runs a prompt iteratively (Esc stops).
@@ -53,7 +53,10 @@ A full agentic CLI harness — every tool below is clean-room and dependency-fre
53
53
  calls the `Consult` tool for a second opinion when stuck, and you can `/ask
54
54
  <question>` directly. Opt-in, user-configured — no extra dependency.
55
55
  - **MCP** — connect to Model Context Protocol servers (`~/.drydock/mcp.json`);
56
- their tools appear as `mcp__<server>__<tool>`. List them with `/mcp`.
56
+ their tools appear as `mcp__<server>__<tool>`. List them with `/mcp`. Works with
57
+ third-party servers out of the box — e.g. [Graphify](https://github.com/safishamsi/graphify)
58
+ for a queryable code knowledge graph: see [docs/graphify.md](docs/graphify.md)
59
+ and the copy-paste [example config](examples/mcp/graphify.json).
57
60
  - **Skills** — reusable `/<name>` commands authored as markdown in
58
61
  `~/.drydock/skills/` (or `<project>/.drydock/skills/`); `$ARGS` substitution.
59
62
  - **Loops** — `/loop <count> <prompt>` runs a prompt iteratively (Esc stops).
@@ -23,6 +23,29 @@ from pathlib import Path
23
23
  _MAX_BASH_OUTPUT_BYTES = 256 * 1024 # 256 KB — plenty of context, safe for RAM
24
24
 
25
25
 
26
+ def _detect_bash() -> str | None:
27
+ """Absolute path to bash, or None to fall back to Popen's default /bin/sh.
28
+
29
+ tool_bash runs commands under bash so the bash syntax the model naturally
30
+ writes — [[ ]], <<< herestrings, arrays, {1..n} brace expansion, process
31
+ substitution <(...), $'...' — actually works. On Debian/Ubuntu /bin/sh is
32
+ dash, which silently rejects all of those ("Syntax error: ... unexpected"),
33
+ a confusing failure the model then loops on."""
34
+ import shutil
35
+ found = shutil.which("bash")
36
+ if found:
37
+ return found
38
+ for p in ("/bin/bash", "/usr/bin/bash", "/usr/local/bin/bash"):
39
+ if os.path.exists(p):
40
+ return p
41
+ return None
42
+
43
+
44
+ # Resolved once at import (in whatever environment drydock runs — host or the
45
+ # task container). None → bash unavailable, fall back to the default shell.
46
+ _BASH_SHELL = _detect_bash()
47
+
48
+
26
49
  def _collapse_repeated_lines(text: str, run: int = 20) -> str:
27
50
  """Collapse a run of >= `run` IDENTICAL consecutive lines into one line + a
28
51
  count. Repetitive output (`yes`, a spinning progress log) tokenizes densely —
@@ -910,7 +933,8 @@ def tool_bash(params: dict, config: dict) -> str:
910
933
  proc = None
911
934
  try:
912
935
  proc = subprocess.Popen(
913
- cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
936
+ cmd, shell=True, executable=_BASH_SHELL,
937
+ stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
914
938
  # stdin=DEVNULL so a command that reads stdin gets immediate EOF
915
939
  # (correct for a non-interactive tool) instead of inheriting the
916
940
  # TUI's terminal — where it would steal the user's keystrokes or hang
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.0.90
3
+ Version: 3.0.92
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
@@ -77,7 +77,10 @@ A full agentic CLI harness — every tool below is clean-room and dependency-fre
77
77
  calls the `Consult` tool for a second opinion when stuck, and you can `/ask
78
78
  <question>` directly. Opt-in, user-configured — no extra dependency.
79
79
  - **MCP** — connect to Model Context Protocol servers (`~/.drydock/mcp.json`);
80
- their tools appear as `mcp__<server>__<tool>`. List them with `/mcp`.
80
+ their tools appear as `mcp__<server>__<tool>`. List them with `/mcp`. Works with
81
+ third-party servers out of the box — e.g. [Graphify](https://github.com/safishamsi/graphify)
82
+ for a queryable code knowledge graph: see [docs/graphify.md](docs/graphify.md)
83
+ and the copy-paste [example config](examples/mcp/graphify.json).
81
84
  - **Skills** — reusable `/<name>` commands authored as markdown in
82
85
  `~/.drydock/skills/` (or `<project>/.drydock/skills/`); `$ARGS` substitution.
83
86
  - **Loops** — `/loop <count> <prompt>` runs a prompt iteratively (Esc stops).
@@ -53,6 +53,7 @@ tests/test_bash_background.py
53
53
  tests/test_bash_output_bounding.py
54
54
  tests/test_bash_process_group.py
55
55
  tests/test_bash_safety.py
56
+ tests/test_bash_shell.py
56
57
  tests/test_bash_stdin.py
57
58
  tests/test_bash_timeout_network.py
58
59
  tests/test_cci.py
@@ -70,6 +71,7 @@ tests/test_extract.py
70
71
  tests/test_failure_loop.py
71
72
  tests/test_first_run_setup.py
72
73
  tests/test_gittools.py
74
+ tests/test_graphify_example.py
73
75
  tests/test_graphrag.py
74
76
  tests/test_guards_and_tools.py
75
77
  tests/test_hallucinated_tools.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.90"
10
+ version = "3.0.92"
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,40 @@
1
+ """tool_bash runs commands under bash (executable=_BASH_SHELL) so the bash syntax
2
+ the model naturally writes works — on Debian/Ubuntu /bin/sh is dash, which rejects
3
+ [[ ]], <<<, arrays, {1..n}, and process substitution with confusing syntax errors
4
+ the model then loops on. Found while investigating the /bin/sh limitation."""
5
+ from __future__ import annotations
6
+
7
+ import pytest
8
+
9
+ from drydock.tools import tool_bash, _BASH_SHELL, _detect_bash
10
+
11
+
12
+ def _run(cmd):
13
+ return tool_bash({"command": cmd, "timeout": 6}, {"cwd": "/tmp"}).strip()
14
+
15
+
16
+ def test_bash_is_detected():
17
+ assert _BASH_SHELL and _BASH_SHELL.endswith("bash")
18
+
19
+
20
+ @pytest.mark.skipif(not _BASH_SHELL, reason="bash not installed")
21
+ @pytest.mark.parametrize("cmd,expected", [
22
+ ('cat <<< "hello world"', "hello world"),
23
+ ('[[ 5 -gt 3 ]] && echo yes', "yes"),
24
+ ('echo {1..5}', "1 2 3 4 5"),
25
+ ('a=(x y z); echo ${a[1]}', "y"),
26
+ ('diff <(echo a) <(echo a) && echo same', "same"),
27
+ ])
28
+ def test_bashisms_work(cmd, expected):
29
+ assert expected in _run(cmd)
30
+
31
+
32
+ def test_plain_commands_and_exit_codes_unaffected():
33
+ assert "hello" in _run("echo hello")
34
+ assert "[exit code: 1]" in _run("false")
35
+ assert "hi" in _run("echo hi | cat")
36
+
37
+
38
+ def test_detect_bash_returns_path_or_none():
39
+ r = _detect_bash()
40
+ assert r is None or (isinstance(r, str) and "bash" in r)
@@ -0,0 +1,36 @@
1
+ """The bundled Graphify MCP example (docs/graphify.md + examples/mcp/graphify.json)
2
+ must stay valid + in the shape drydock's MCP loader expects, so the copy-paste
3
+ recipe never rots."""
4
+ from __future__ import annotations
5
+
6
+ import json
7
+ from pathlib import Path
8
+
9
+ _ROOT = Path(__file__).resolve().parent.parent
10
+ _EXAMPLE = _ROOT / "examples" / "mcp" / "graphify.json"
11
+ _DOC = _ROOT / "docs" / "graphify.md"
12
+
13
+
14
+ def test_example_is_valid_json_with_mcpservers_shape():
15
+ cfg = json.loads(_EXAMPLE.read_text())
16
+ assert "mcpServers" in cfg and "graphify" in cfg["mcpServers"]
17
+ srv = cfg["mcpServers"]["graphify"]
18
+ assert srv["command"] == "graphify-mcp"
19
+ assert "--transport" in srv["args"] and "stdio" in srv["args"]
20
+ # the same shape drydock/mcp.py consumes: command + args list
21
+ assert isinstance(srv["args"], list)
22
+
23
+
24
+ def test_example_matches_drydock_mcp_loader_shape():
25
+ """Instantiating an MCPServer from the example must not raise (parses the
26
+ command/args the way connect_all does), without starting it."""
27
+ from drydock.mcp import MCPServer
28
+ cfg = json.loads(_EXAMPLE.read_text())["mcpServers"]["graphify"]
29
+ srv = MCPServer("graphify", cfg["command"], cfg["args"], cfg.get("env"))
30
+ assert srv.command == "graphify-mcp" and srv.args[0] == "--transport"
31
+
32
+
33
+ def test_doc_exists_and_links_example():
34
+ text = _DOC.read_text()
35
+ assert "graphify-mcp" in text and "OPENAI_BASE_URL" in text
36
+ assert "examples/mcp/graphify.json" in text
File without changes
File without changes
File without changes