drydock-cli 3.0.21__tar.gz → 3.0.23__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 (50) hide show
  1. {drydock_cli-3.0.21/drydock_cli.egg-info → drydock_cli-3.0.23}/PKG-INFO +1 -1
  2. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/__init__.py +1 -1
  3. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/detect.py +5 -3
  4. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/tools/__init__.py +6 -1
  5. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/tui/app.py +55 -7
  6. {drydock_cli-3.0.21 → drydock_cli-3.0.23/drydock_cli.egg-info}/PKG-INFO +1 -1
  7. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/pyproject.toml +1 -1
  8. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_detect.py +2 -0
  9. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_guards_and_tools.py +9 -0
  10. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/LICENSE +0 -0
  11. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/NOTICE +0 -0
  12. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/README.md +0 -0
  13. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/__main__.py +0 -0
  14. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/agent.py +0 -0
  15. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/bash_safety.py +0 -0
  16. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/cli.py +0 -0
  17. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/compaction.py +0 -0
  18. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/config.py +0 -0
  19. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/guards.py +0 -0
  20. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/loop_detect.py +0 -0
  21. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/providers.py +0 -0
  22. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/tool_registry.py +0 -0
  23. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/tui/__init__.py +0 -0
  24. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/tui/approval.py +0 -0
  25. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/tui/messages.py +0 -0
  26. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/tui/widgets.py +0 -0
  27. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock/tuning.py +0 -0
  28. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock_cli.egg-info/SOURCES.txt +0 -0
  29. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock_cli.egg-info/dependency_links.txt +0 -0
  30. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock_cli.egg-info/entry_points.txt +0 -0
  31. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock_cli.egg-info/requires.txt +0 -0
  32. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/drydock_cli.egg-info/top_level.txt +0 -0
  33. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/setup.cfg +0 -0
  34. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_approval.py +0 -0
  35. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_back_command.py +0 -0
  36. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_bash_safety.py +0 -0
  37. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_cli_agents.py +0 -0
  38. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_compaction.py +0 -0
  39. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_config.py +0 -0
  40. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_hallucinated_tools.py +0 -0
  41. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_leaked_tool_call.py +0 -0
  42. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_loop_detect.py +0 -0
  43. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_plan_autocontinue.py +0 -0
  44. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_providers_unreachable.py +0 -0
  45. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_stop.py +0 -0
  46. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_subagent.py +0 -0
  47. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_todo.py +0 -0
  48. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_tools_undo.py +0 -0
  49. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/tests/test_tui.py +0 -0
  50. {drydock_cli-3.0.21 → drydock_cli-3.0.23}/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.21
3
+ Version: 3.0.23
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.21"
10
+ __version__ = "3.0.23"
@@ -61,9 +61,11 @@ def onboarding_message(found: list[dict]) -> str:
61
61
  if not found:
62
62
  ports = ", ".join(b.split("//")[1] for _, b in CANDIDATES)
63
63
  return (
64
- "No local LLM detected (" + ports + "). Start a server "
65
- "(llama.cpp / vLLM / Ollama / LM Studio), or set base_url in "
66
- "~/.drydock/config.toml, then restart."
64
+ "No local LLM detected (" + ports + "). Point Drydock at your model "
65
+ "right here:\n"
66
+ " /model url <http://host:port/v1> then /model <model-name>\n"
67
+ "(saved to ~/.drydock/config.toml). Or start a local server "
68
+ "(llama.cpp / vLLM / Ollama / LM Studio) and restart."
67
69
  )
68
70
  best = found[0]
69
71
  model = best["models"][0] if best["models"] else "?"
@@ -460,7 +460,12 @@ def tool_bash(params: dict, config: dict) -> str:
460
460
  output += f"\n[exit code: {result.returncode}]"
461
461
  return output.strip() or "(no output)"
462
462
  except subprocess.TimeoutExpired:
463
- return f"Error: command timed out after {timeout}s"
463
+ bigger = min(timeout * 4, 600)
464
+ return (
465
+ f"Error: command timed out after {timeout}s. If it is legitimately "
466
+ f"slow (a big query, build, download, or test run), retry with a "
467
+ f"larger timeout — pass timeout: {bigger}. Otherwise it may be hung."
468
+ )
464
469
  except Exception as e:
465
470
  return f"Error: {e}"
466
471
 
@@ -405,7 +405,8 @@ class DrydockApp(App):
405
405
  self._info(
406
406
  "Commands:\n"
407
407
  " /help this help\n"
408
- " /model [name] show or switch the model\n"
408
+ " /model show/set model, provider, endpoint URL\n"
409
+ " /model <name> · /model url <url> · /model provider <p>\n"
409
410
  " /cwd [path] show or change the working directory\n"
410
411
  " /undo revert the last file write/edit\n"
411
412
  " /back rewind the last turn from the model's context\n"
@@ -418,14 +419,61 @@ class DrydockApp(App):
418
419
  else:
419
420
  self._mount(ErrorMessage(f"unknown command: {cmd} (try /help)"))
420
421
 
421
- def _cmd_model(self, name: str) -> None:
422
- if not name:
423
- self._info(f"model: {self.config.get('model')}")
422
+ def _persist_config(self) -> None:
423
+ """Save the persistable settings (model/provider/base_url/… — save_file
424
+ filters to those) to ~/.drydock/config.toml so setup survives restart."""
425
+ from drydock import config as cfgmod
426
+
427
+ ok = cfgmod.save_file(self.config, cfgmod.default_config_path())
428
+ if not ok:
429
+ self._mount(ErrorMessage("could not write ~/.drydock/config.toml"))
430
+
431
+ def _cmd_model(self, arg: str) -> None:
432
+ """Model + endpoint setup. Subcommands persist so they survive restart:
433
+ /model show model, provider, endpoint
434
+ /model <name> set the model name
435
+ /model url <base_url> set the server URL (e.g. http://host:8000/v1)
436
+ /model provider <name> vllm | ollama | lmstudio | openai
437
+ """
438
+ arg = (arg or "").strip()
439
+ if not arg:
440
+ prov = self.config.get("provider") or "(default)"
441
+ url = self.config.get("base_url") or "(provider default)"
442
+ self._info(
443
+ f"model: {self.config.get('model')}\n"
444
+ f"provider: {prov}\n"
445
+ f"endpoint: {url}\n"
446
+ "Set up: /model <name> · /model url <http://host:port/v1> · "
447
+ "/model provider <vllm|ollama|lmstudio|openai>"
448
+ )
449
+ return
450
+ parts = arg.split(maxsplit=1)
451
+ sub = parts[0].lower()
452
+ val = parts[1].strip() if len(parts) > 1 else ""
453
+ if sub == "url":
454
+ if not val:
455
+ self._info("usage: /model url <http://host:port/v1>")
456
+ return
457
+ self.config["base_url"] = val
458
+ self._persist_config()
459
+ self._info(f"endpoint → {val} (saved). Send a prompt to test it.")
460
+ return
461
+ if sub == "provider":
462
+ from drydock.providers import PROVIDERS
463
+
464
+ if val not in PROVIDERS:
465
+ self._info(f"unknown provider {val!r}. Choose: {', '.join(PROVIDERS)}")
466
+ return
467
+ self.config["provider"] = val
468
+ self._persist_config()
469
+ self._info(f"provider → {val} (saved)")
424
470
  return
425
- self.config["model"] = name
426
- self.system = self._build_system(name) # prompt may be model-specific
471
+ # Otherwise: set the model name (the whole arg, so names with spaces work).
472
+ self.config["model"] = arg
473
+ self.system = self._build_system(arg) # prompt may be model-specific
474
+ self._persist_config()
427
475
  self._refresh_status()
428
- self._info(f"switched model → {name}")
476
+ self._info(f"model → {arg} (saved)")
429
477
 
430
478
  def _cmd_cwd(self, path: str) -> None:
431
479
  if not path:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.0.21
3
+ Version: 3.0.23
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.21"
10
+ version = "3.0.23"
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"
@@ -71,3 +71,5 @@ def test_onboarding_message_when_nothing_found():
71
71
  msg = detect.onboarding_message([])
72
72
  assert "No local LLM detected" in msg
73
73
  assert "config.toml" in msg
74
+ # New users must learn how to point Drydock at their model from the TUI.
75
+ assert "/model url" in msg and "/model" in msg
@@ -313,6 +313,15 @@ def test_file_tools_honor_config_cwd(tmp_path):
313
313
  assert "x.py" in tool_bash({"command": "ls sub"}, cfg)
314
314
 
315
315
 
316
+ def test_tool_bash_timeout_suggests_larger_timeout():
317
+ from drydock.tools import tool_bash
318
+ out = tool_bash({"command": "sleep 2", "timeout": 1}, {})
319
+ assert "timed out after 1s" in out
320
+ # Must tell the model it can retry with a bigger timeout (so a legitimately
321
+ # slow query/build doesn't dead-end — what derailed query-optimize).
322
+ assert "timeout: 4" in out
323
+
324
+
316
325
  def test_absolute_paths_pass_through(tmp_path):
317
326
  fp = str(tmp_path / "abs.py")
318
327
  tool_write({"file_path": fp, "content": "a=1\n"}, {"cwd": "/nonexistent"})
File without changes
File without changes
File without changes
File without changes