drydock-cli 3.0.102__tar.gz → 3.0.104__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.102/drydock_cli.egg-info → drydock_cli-3.0.104}/PKG-INFO +1 -1
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/extract.py +2 -2
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/tools/__init__.py +11 -5
- {drydock_cli-3.0.102 → drydock_cli-3.0.104/drydock_cli.egg-info}/PKG-INFO +1 -1
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock_cli.egg-info/SOURCES.txt +1 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/pyproject.toml +1 -1
- drydock_cli-3.0.104/tests/test_tool_arg_coercion_more.py +37 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/LICENSE +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/NOTICE +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/README.md +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/__init__.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/__main__.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/advisor.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/agent.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/bash_safety.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/builtin_skills/__init__.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/builtin_skills/rmf-categorize.md +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/builtin_skills/rmf-control.md +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/builtin_skills/rmf-poam.md +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/builtin_skills/rmf-review.md +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/builtin_skills/stig-assess.md +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/builtin_skills/stig-remediate.md +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/cci.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/cli.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/compaction.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/config.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/detect.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/gittools.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/graphrag.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/guards.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/loop_detect.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/mcp.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/poam.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/providers.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/rmf.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/rmf_graph.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/skills.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/stig.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/tool_registry.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/tui/__init__.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/tui/app.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/tui/approval.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/tui/messages.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/tui/widgets.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/tuning.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock/web.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock_cli.egg-info/dependency_links.txt +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock_cli.egg-info/entry_points.txt +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock_cli.egg-info/requires.txt +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/drydock_cli.egg-info/top_level.txt +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/setup.cfg +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_advisor.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_approval.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_back_command.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_bash_background.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_bash_binary_output.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_bash_output_bounding.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_bash_process_group.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_bash_safety.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_bash_sanitize.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_bash_shell.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_bash_stdin.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_bash_stop_partial.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_bash_timeout_network.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_bash_timeout_param.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_cci.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_cli_agents.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_compact_command.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_compaction.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_config.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_config_migration.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_context_limit_config.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_detect.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_dispatch.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_e2e_connected.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_edit_replace_all.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_empty_response.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_extract.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_failure_loop.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_first_run_setup.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_gittools.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_glob_edit_edges.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_graphify_example.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_graphrag.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_grep_and_read_robust.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_guards_and_tools.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_hallucinated_tools.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_leaked_tool_call.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_loop_detect.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_mcp.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_oneshot_unreachable.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_plan_autocontinue.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_poam.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_providers_unreachable.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_read_index.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_rmf.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_rmf_graph.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_rmf_stig_graph.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_runaway_repetition.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_server_probe.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_skills.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_stig.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_stop.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_streaming_newlines.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_subagent.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_system_prompt_help.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_todo.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_tool_arg_coercion.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_tool_arg_parsing.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_tools_undo.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_tui.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_tuning.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_viewimage.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_vision_input.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_web_tools.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_write_content_coerce.py +0 -0
- {drydock_cli-3.0.102 → drydock_cli-3.0.104}/tests/test_xccdf.py +0 -0
|
@@ -62,7 +62,7 @@ def _extract_pdf(path: str | Path) -> str | None:
|
|
|
62
62
|
pass
|
|
63
63
|
# 2) pypdf (optional dependency: drydock-cli[pdf]).
|
|
64
64
|
try:
|
|
65
|
-
import pypdf
|
|
65
|
+
import pypdf # pyright: ignore[reportMissingImports]
|
|
66
66
|
|
|
67
67
|
reader = pypdf.PdfReader(str(path))
|
|
68
68
|
text = "\n\n".join((pg.extract_text() or "") for pg in reader.pages).strip()
|
|
@@ -113,7 +113,7 @@ def pdf_backend_available() -> bool:
|
|
|
113
113
|
if which("pdftotext"):
|
|
114
114
|
return True
|
|
115
115
|
try:
|
|
116
|
-
import pypdf # noqa: F401
|
|
116
|
+
import pypdf # noqa: F401 # pyright: ignore[reportMissingImports]
|
|
117
117
|
|
|
118
118
|
return True
|
|
119
119
|
except ImportError:
|
|
@@ -1067,6 +1067,11 @@ def tool_bash(params: dict, config: dict) -> str:
|
|
|
1067
1067
|
if cancel is not None and cancel.is_set():
|
|
1068
1068
|
kill_process_group(proc)
|
|
1069
1069
|
proc.wait()
|
|
1070
|
+
# The drain thread blocks in read(8192) until the buffer fills or
|
|
1071
|
+
# EOF; killing the process delivers EOF, so join lets it append the
|
|
1072
|
+
# last buffered output BEFORE we read chunks (else partial races
|
|
1073
|
+
# the drain and comes back empty for small-output commands).
|
|
1074
|
+
reader.join(1.0)
|
|
1070
1075
|
partial = _sanitize_bash_output(
|
|
1071
1076
|
_collapse_repeated_lines("".join(list(chunks)))
|
|
1072
1077
|
).rstrip()
|
|
@@ -1090,6 +1095,7 @@ def tool_bash(params: dict, config: dict) -> str:
|
|
|
1090
1095
|
if time.monotonic() - start > timeout:
|
|
1091
1096
|
kill_process_group(proc)
|
|
1092
1097
|
proc.wait()
|
|
1098
|
+
reader.join(1.0) # flush buffered output before building partial
|
|
1093
1099
|
bigger = min(timeout * 4, 1800)
|
|
1094
1100
|
msg = (
|
|
1095
1101
|
f"Error: command timed out after {timeout}s. If it is "
|
|
@@ -1256,7 +1262,7 @@ def tool_consult(params: dict, config: dict) -> str:
|
|
|
1256
1262
|
"""Ask the configured second/advisor model (e.g. Gemini) for a second opinion."""
|
|
1257
1263
|
from drydock import advisor
|
|
1258
1264
|
|
|
1259
|
-
question = (params.get("question") or params.get("prompt")
|
|
1265
|
+
question = _as_str_arg(params.get("question") or params.get("prompt")).strip()
|
|
1260
1266
|
if not question:
|
|
1261
1267
|
return "Error: Consult needs a `question`."
|
|
1262
1268
|
return advisor.consult(question, config, context=params.get("context", "") or "")
|
|
@@ -1398,7 +1404,7 @@ def tool_gitcommit(params: dict, config: dict) -> str:
|
|
|
1398
1404
|
try:
|
|
1399
1405
|
return gittools.commit(
|
|
1400
1406
|
_git_cwd(config),
|
|
1401
|
-
params.get("message")
|
|
1407
|
+
_as_text(params.get("message")),
|
|
1402
1408
|
add_all=params.get("add_all", True) is not False,
|
|
1403
1409
|
)
|
|
1404
1410
|
except gittools.GitError as e:
|
|
@@ -1578,7 +1584,7 @@ def tool_websearch(params: dict, config: dict) -> str:
|
|
|
1578
1584
|
"""Search the internet (DuckDuckGo). Read-only; clean message when offline."""
|
|
1579
1585
|
from drydock import web
|
|
1580
1586
|
|
|
1581
|
-
query = (params.get("query")
|
|
1587
|
+
query = _as_str_arg(params.get("query")).strip()
|
|
1582
1588
|
if not query:
|
|
1583
1589
|
return "Error: `WebSearch` needs a `query`."
|
|
1584
1590
|
try:
|
|
@@ -1596,7 +1602,7 @@ def tool_webfetch(params: dict, config: dict) -> str:
|
|
|
1596
1602
|
"""Fetch a URL and return readable text. Read-only; clean message offline."""
|
|
1597
1603
|
from drydock import web
|
|
1598
1604
|
|
|
1599
|
-
url = (params.get("url")
|
|
1605
|
+
url = _as_str_arg(params.get("url")).strip()
|
|
1600
1606
|
if not url:
|
|
1601
1607
|
return "Error: `WebFetch` needs a `url`."
|
|
1602
1608
|
try:
|
|
@@ -1616,7 +1622,7 @@ def tool_knowledge(params: dict, config: dict) -> str:
|
|
|
1616
1622
|
If no index exists, says so cleanly rather than erroring."""
|
|
1617
1623
|
from drydock import graphrag
|
|
1618
1624
|
|
|
1619
|
-
query = (params.get("query")
|
|
1625
|
+
query = _as_str_arg(params.get("query")).strip()
|
|
1620
1626
|
if not query:
|
|
1621
1627
|
return "Error: `Knowledge` needs a `query` describing what to look up."
|
|
1622
1628
|
cwd = config.get("cwd") or os.getcwd()
|
|
@@ -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.104"
|
|
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,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
|
|
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
|