drydock-cli 3.0.58__tar.gz → 3.0.59__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.58/drydock_cli.egg-info → drydock_cli-3.0.59}/PKG-INFO +1 -1
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/tools/__init__.py +28 -2
- {drydock_cli-3.0.58 → drydock_cli-3.0.59/drydock_cli.egg-info}/PKG-INFO +1 -1
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/pyproject.toml +1 -1
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_bash_output_bounding.py +12 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/LICENSE +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/NOTICE +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/README.md +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/__init__.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/__main__.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/agent.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/bash_safety.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/cli.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/compaction.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/config.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/detect.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/gittools.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/graphrag.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/guards.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/loop_detect.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/mcp.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/providers.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/skills.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/tool_registry.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/tui/__init__.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/tui/app.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/tui/approval.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/tui/messages.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/tui/widgets.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/tuning.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock/web.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock_cli.egg-info/SOURCES.txt +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock_cli.egg-info/dependency_links.txt +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock_cli.egg-info/entry_points.txt +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock_cli.egg-info/requires.txt +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/drydock_cli.egg-info/top_level.txt +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/setup.cfg +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_approval.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_back_command.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_bash_process_group.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_bash_safety.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_bash_timeout_network.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_cli_agents.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_compact_command.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_compaction.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_config.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_config_migration.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_context_limit_config.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_detect.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_dispatch.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_empty_response.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_failure_loop.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_first_run_setup.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_gittools.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_graphrag.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_guards_and_tools.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_hallucinated_tools.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_leaked_tool_call.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_loop_detect.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_mcp.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_oneshot_unreachable.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_plan_autocontinue.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_providers_unreachable.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_read_index.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_runaway_repetition.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_skills.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_stop.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_streaming_newlines.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_subagent.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_todo.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_tool_arg_parsing.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_tools_undo.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_tui.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_tuning.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_vision_input.py +0 -0
- {drydock_cli-3.0.58 → drydock_cli-3.0.59}/tests/test_web_tools.py +0 -0
|
@@ -22,6 +22,29 @@ from pathlib import Path
|
|
|
22
22
|
# once it's hit — bounding RAM regardless of how much it tries to produce.
|
|
23
23
|
_MAX_BASH_OUTPUT_BYTES = 256 * 1024 # 256 KB — plenty of context, safe for RAM
|
|
24
24
|
|
|
25
|
+
|
|
26
|
+
def _collapse_repeated_lines(text: str, run: int = 20) -> str:
|
|
27
|
+
"""Collapse a run of >= `run` IDENTICAL consecutive lines into one line + a
|
|
28
|
+
count. Repetitive output (`yes`, a spinning progress log) tokenizes densely —
|
|
29
|
+
32 KB of "y\\n" is ~24k tokens — so even after byte-capping it can eat a big
|
|
30
|
+
slice of the context window. Collapsing makes it cheap without losing the
|
|
31
|
+
signal. Non-repetitive output (a normal build log) is left untouched."""
|
|
32
|
+
lines = text.split("\n")
|
|
33
|
+
out: list[str] = []
|
|
34
|
+
i, n = 0, len(lines)
|
|
35
|
+
while i < n:
|
|
36
|
+
j = i
|
|
37
|
+
while j < n and lines[j] == lines[i]:
|
|
38
|
+
j += 1
|
|
39
|
+
count = j - i
|
|
40
|
+
if count >= run:
|
|
41
|
+
out.append(lines[i])
|
|
42
|
+
out.append(f"[... {count - 1} more identical lines collapsed ...]")
|
|
43
|
+
else:
|
|
44
|
+
out.extend(lines[i:j])
|
|
45
|
+
i = j
|
|
46
|
+
return "\n".join(out)
|
|
47
|
+
|
|
25
48
|
from drydock.tool_registry import ToolDef, register
|
|
26
49
|
from drydock.guards import (
|
|
27
50
|
conflict_marker_refusal,
|
|
@@ -781,10 +804,13 @@ def tool_bash(params: dict, config: dict) -> str:
|
|
|
781
804
|
msg += _OFFLINE_HINT
|
|
782
805
|
return msg
|
|
783
806
|
proc.wait()
|
|
784
|
-
|
|
807
|
+
# Collapse repetitive runs FIRST (turns 256 KB of "y\n" into ~2 lines),
|
|
808
|
+
# then note if we hit the byte cap. Bounds both RAM (the cap) and context
|
|
809
|
+
# tokens (the collapse).
|
|
810
|
+
output = _collapse_repeated_lines("".join(chunks))
|
|
785
811
|
if capped.is_set():
|
|
786
812
|
return (
|
|
787
|
-
output
|
|
813
|
+
output.rstrip()
|
|
788
814
|
+ f"\n[output truncated at {_MAX_BASH_OUTPUT_BYTES // 1024} KB — "
|
|
789
815
|
"the command produced more; redirect to a file and inspect it in "
|
|
790
816
|
"pieces (head/tail/grep) instead of dumping it all]"
|
|
@@ -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.59"
|
|
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"
|
|
@@ -35,3 +35,15 @@ def test_exit_code_still_reported(tmp_path):
|
|
|
35
35
|
cfg = {"cwd": str(tmp_path)}
|
|
36
36
|
out = tool_bash({"command": "echo oops; exit 3"}, cfg)
|
|
37
37
|
assert "oops" in out and "exit code: 3" in out
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def test_repetitive_output_is_collapsed(tmp_path):
|
|
41
|
+
# PRD "protect context size": a repetitive dump must not eat the window.
|
|
42
|
+
out = tool_bash({"command": "yes | head -c 2000000"}, {"cwd": str(tmp_path)})
|
|
43
|
+
assert len(out) < 500 # ~24k chars before the collapse
|
|
44
|
+
assert "identical lines collapsed" in out
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
def test_collapse_preserves_distinct_lines(tmp_path):
|
|
48
|
+
out = tool_bash({"command": "printf 'a\\nb\\nc\\n'"}, {"cwd": str(tmp_path)})
|
|
49
|
+
assert out == "a\nb\nc" # nothing collapsed
|
|
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
|