drydock-cli 3.0.28__tar.gz → 3.0.30__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 (54) hide show
  1. {drydock_cli-3.0.28/drydock_cli.egg-info → drydock_cli-3.0.30}/PKG-INFO +1 -1
  2. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/__init__.py +1 -1
  3. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/config.py +1 -1
  4. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/tools/__init__.py +10 -0
  5. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/tui/app.py +22 -1
  6. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/tui/widgets.py +33 -0
  7. {drydock_cli-3.0.28 → drydock_cli-3.0.30/drydock_cli.egg-info}/PKG-INFO +1 -1
  8. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/pyproject.toml +1 -1
  9. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/LICENSE +0 -0
  10. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/NOTICE +0 -0
  11. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/README.md +0 -0
  12. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/__main__.py +0 -0
  13. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/agent.py +0 -0
  14. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/bash_safety.py +0 -0
  15. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/cli.py +0 -0
  16. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/compaction.py +0 -0
  17. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/detect.py +0 -0
  18. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/guards.py +0 -0
  19. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/loop_detect.py +0 -0
  20. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/providers.py +0 -0
  21. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/tool_registry.py +0 -0
  22. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/tui/__init__.py +0 -0
  23. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/tui/approval.py +0 -0
  24. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/tui/messages.py +0 -0
  25. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock/tuning.py +0 -0
  26. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock_cli.egg-info/SOURCES.txt +0 -0
  27. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock_cli.egg-info/dependency_links.txt +0 -0
  28. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock_cli.egg-info/entry_points.txt +0 -0
  29. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock_cli.egg-info/requires.txt +0 -0
  30. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/drydock_cli.egg-info/top_level.txt +0 -0
  31. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/setup.cfg +0 -0
  32. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_approval.py +0 -0
  33. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_back_command.py +0 -0
  34. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_bash_safety.py +0 -0
  35. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_cli_agents.py +0 -0
  36. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_compaction.py +0 -0
  37. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_config.py +0 -0
  38. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_detect.py +0 -0
  39. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_empty_response.py +0 -0
  40. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_first_run_setup.py +0 -0
  41. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_guards_and_tools.py +0 -0
  42. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_hallucinated_tools.py +0 -0
  43. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_leaked_tool_call.py +0 -0
  44. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_loop_detect.py +0 -0
  45. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_plan_autocontinue.py +0 -0
  46. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_providers_unreachable.py +0 -0
  47. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_stop.py +0 -0
  48. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_streaming_newlines.py +0 -0
  49. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_subagent.py +0 -0
  50. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_todo.py +0 -0
  51. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_tool_arg_parsing.py +0 -0
  52. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_tools_undo.py +0 -0
  53. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_tui.py +0 -0
  54. {drydock_cli-3.0.28 → drydock_cli-3.0.30}/tests/test_tuning.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.0.28
3
+ Version: 3.0.30
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
@@ -7,4 +7,4 @@ try:
7
7
  __version__ = _version("drydock-cli")
8
8
  except PackageNotFoundError:
9
9
  # Running from source before install (e.g. PYTHONPATH dev runs).
10
- __version__ = "3.0.28"
10
+ __version__ = "3.0.30"
@@ -22,7 +22,7 @@ DEFAULTS: dict[str, object] = {
22
22
  "model": "gemma4",
23
23
  "provider": "vllm",
24
24
  "base_url": "",
25
- "max_tokens": 4096,
25
+ "max_tokens": 8192, # 4096 truncated large file writes mid-JSON (→ _raw fail)
26
26
  "temperature": 0.2,
27
27
  "theme": "harbor",
28
28
  }
@@ -217,6 +217,16 @@ def undo_last(config: dict) -> str:
217
217
 
218
218
 
219
219
  def tool_write(params: dict, config: dict) -> str:
220
+ # Truncated/invalid tool-call JSON arrives as {"_raw": ...} with no
221
+ # file_path/content — usually a big file that overran the response. Say so
222
+ # (and how to recover) instead of the misleading "needs a real file_path".
223
+ if "_raw" in params and not params.get("file_path"):
224
+ return (
225
+ "Error: the Write arguments were cut off or not valid JSON, so "
226
+ "nothing was written. This usually means the file was too large for "
227
+ "one response — write it in smaller pieces (Write the first part, "
228
+ "then Edit to append the rest)."
229
+ )
220
230
  fp = params.get("file_path")
221
231
  # Empty/whitespace path is the canonical real-use loop bug — reject it
222
232
  # clearly (with the content length, so the model knows the write was seen)
@@ -34,6 +34,7 @@ from drydock.tui.messages import (
34
34
  AgentTurnDone,
35
35
  )
36
36
  from drydock.tui.widgets import (
37
+ SLASH_COMMANDS,
37
38
  AssistantMessage,
38
39
  ErrorMessage,
39
40
  PromptArea,
@@ -325,7 +326,11 @@ class DrydockApp(App):
325
326
  return ""
326
327
  spin = _SPINNER[self._spinner_i % len(_SPINNER)]
327
328
  elapsed = _fmt_elapsed(time.monotonic() - self._work_start)
328
- toks = _fmt_tokens(self._work_chars // 4)
329
+ # Session output tokens (updated each completed turn) + this turn's live
330
+ # streamed chars. Tool turns are non-streaming, so _work_chars stays 0 —
331
+ # showing the cumulative total keeps the counter meaningful instead of
332
+ # stuck at "0 tokens" through a long multi-step task.
333
+ toks = _fmt_tokens(self.state.total_output_tokens + self._work_chars // 4)
329
334
  effort = self.state.current_effort
330
335
  eff = f" · thinking with {effort} effort" if effort else ""
331
336
  queued = f" · {len(self._queue)} queued" if self._queue else ""
@@ -351,6 +356,22 @@ class DrydockApp(App):
351
356
 
352
357
  # ── input ─────────────────────────────────────────────────────────────
353
358
 
359
+ def on_text_area_changed(self, event) -> None:
360
+ """As the user types a bare slash command, surface the matching commands
361
+ in the (idle) activity line — '/m' shows '/model'. Tab completes them."""
362
+ if self._busy:
363
+ return # #working shows live activity while a turn is running
364
+ try:
365
+ text = self.query_one("#prompt", PromptArea).text
366
+ except Exception: # noqa: BLE001 — widget not mounted yet
367
+ return
368
+ hint = ""
369
+ if text.startswith("/") and " " not in text and "\n" not in text:
370
+ matches = [c for c in SLASH_COMMANDS if c.startswith(text.lower())]
371
+ if matches:
372
+ hint = " " + " ".join(matches) + " · Tab to complete"
373
+ self.query_one("#working", Static).update(hint)
374
+
354
375
  def on_prompt_area_submitted(self, event: PromptArea.Submitted) -> None:
355
376
  text = event.text.strip()
356
377
  prompt = self.query_one("#prompt", PromptArea)
@@ -148,6 +148,25 @@ class PromptHistory:
148
148
  return self._draft
149
149
 
150
150
 
151
+ # The slash commands offered for completion (typing "/m" → /model). Keep in
152
+ # sync with app._handle_slash. Shown as a hint as the user types, and Tab
153
+ # completes the prefix.
154
+ SLASH_COMMANDS = [
155
+ "/help", "/model", "/cwd", "/undo", "/back", "/stop", "/status",
156
+ "/clear", "/quit",
157
+ ]
158
+
159
+
160
+ def _common_prefix(strings: list[str]) -> str:
161
+ if not strings:
162
+ return ""
163
+ pre = strings[0]
164
+ for s in strings[1:]:
165
+ while not s.startswith(pre):
166
+ pre = pre[:-1]
167
+ return pre
168
+
169
+
151
170
  class PromptArea(TextArea):
152
171
  """Multi-line prompt box.
153
172
 
@@ -187,6 +206,20 @@ class PromptArea(TextArea):
187
206
  event.prevent_default()
188
207
  self.insert("\n")
189
208
  return
209
+ if key == "tab":
210
+ t = self.text
211
+ if t.startswith("/") and " " not in t and "\n" not in t:
212
+ matches = [c for c in SLASH_COMMANDS if c.startswith(t.lower())]
213
+ if matches:
214
+ event.stop()
215
+ event.prevent_default()
216
+ # one match → complete it (+ space); several → common prefix.
217
+ self.text = (
218
+ matches[0] + " " if len(matches) == 1
219
+ else _common_prefix(matches)
220
+ )
221
+ self.move_cursor(self.document.end)
222
+ return
190
223
  if key == "up" and self.cursor_at_first_line:
191
224
  event.stop()
192
225
  event.prevent_default()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.0.28
3
+ Version: 3.0.30
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
@@ -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.28"
10
+ version = "3.0.30"
11
11
  description = "Drydock — a local, provider-agnostic terminal coding agent for local LLMs"
12
12
  readme = "README.md"
13
13
  requires-python = ">=3.12"
File without changes
File without changes
File without changes
File without changes