drydock-cli 3.0.26__tar.gz → 3.0.27__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.26/drydock_cli.egg-info → drydock_cli-3.0.27}/PKG-INFO +1 -1
  2. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/__init__.py +1 -1
  3. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/cli.py +43 -11
  4. {drydock_cli-3.0.26 → drydock_cli-3.0.27/drydock_cli.egg-info}/PKG-INFO +1 -1
  5. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock_cli.egg-info/SOURCES.txt +1 -0
  6. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/pyproject.toml +1 -1
  7. drydock_cli-3.0.27/tests/test_first_run_setup.py +34 -0
  8. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/LICENSE +0 -0
  9. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/NOTICE +0 -0
  10. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/README.md +0 -0
  11. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/__main__.py +0 -0
  12. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/agent.py +0 -0
  13. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/bash_safety.py +0 -0
  14. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/compaction.py +0 -0
  15. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/config.py +0 -0
  16. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/detect.py +0 -0
  17. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/guards.py +0 -0
  18. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/loop_detect.py +0 -0
  19. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/providers.py +0 -0
  20. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tool_registry.py +0 -0
  21. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tools/__init__.py +0 -0
  22. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tui/__init__.py +0 -0
  23. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tui/app.py +0 -0
  24. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tui/approval.py +0 -0
  25. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tui/messages.py +0 -0
  26. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tui/widgets.py +0 -0
  27. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tuning.py +0 -0
  28. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock_cli.egg-info/dependency_links.txt +0 -0
  29. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock_cli.egg-info/entry_points.txt +0 -0
  30. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock_cli.egg-info/requires.txt +0 -0
  31. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock_cli.egg-info/top_level.txt +0 -0
  32. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/setup.cfg +0 -0
  33. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_approval.py +0 -0
  34. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_back_command.py +0 -0
  35. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_bash_safety.py +0 -0
  36. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_cli_agents.py +0 -0
  37. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_compaction.py +0 -0
  38. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_config.py +0 -0
  39. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_detect.py +0 -0
  40. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_empty_response.py +0 -0
  41. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_guards_and_tools.py +0 -0
  42. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_hallucinated_tools.py +0 -0
  43. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_leaked_tool_call.py +0 -0
  44. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_loop_detect.py +0 -0
  45. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_plan_autocontinue.py +0 -0
  46. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_providers_unreachable.py +0 -0
  47. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_stop.py +0 -0
  48. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_streaming_newlines.py +0 -0
  49. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_subagent.py +0 -0
  50. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_todo.py +0 -0
  51. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_tool_arg_parsing.py +0 -0
  52. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_tools_undo.py +0 -0
  53. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_tui.py +0 -0
  54. {drydock_cli-3.0.26 → drydock_cli-3.0.27}/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.26
3
+ Version: 3.0.27
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.26"
10
+ __version__ = "3.0.27"
@@ -168,6 +168,35 @@ def _summarize(inputs: dict) -> str:
168
168
 
169
169
  # ── Entry point ───────────────────────────────────────────────────────────
170
170
 
171
+ def _first_run_setup(cfg: dict, cfg_path, cfgmod) -> tuple[dict, str]:
172
+ """Interactive first-launch: ask for the model server URL + model name and
173
+ persist them to config.toml. Autodetect (if anything is listening) supplies
174
+ the suggested defaults. Returns (updated cfg, onboarding line)."""
175
+ from drydock import detect
176
+
177
+ found = detect.detect_local_llms()
178
+ default_url = found[0]["base_url"] if found else "http://127.0.0.1:8000/v1"
179
+ default_model = (
180
+ found[0]["models"][0]
181
+ if found and found[0].get("models")
182
+ else (cfg.get("model") or "gemma4")
183
+ )
184
+ print("\n ⚓ Drydock — first-time setup")
185
+ print(" Point Drydock at your local model server (any OpenAI-compatible URL).\n")
186
+ try:
187
+ url = input(f" Model server URL [{default_url}]: ").strip() or default_url
188
+ model = input(f" Model name [{default_model}]: ").strip() or default_model
189
+ except (EOFError, KeyboardInterrupt):
190
+ url, model = default_url, default_model
191
+ print()
192
+ cfg["base_url"] = url
193
+ cfg["model"] = model
194
+ cfg.setdefault("provider", found[0]["provider"] if found else "vllm")
195
+ cfgmod.save_file(cfg, cfg_path)
196
+ print(f"\n Saved to {cfg_path} — change it anytime with /model or by editing that file.\n")
197
+ return cfg, f"⚓ Using {model} at {url}"
198
+
199
+
171
200
  def main():
172
201
  parser = argparse.ArgumentParser(description="DryDock — local coding agent")
173
202
  parser.add_argument(
@@ -198,19 +227,22 @@ def main():
198
227
  "temperature": args.temperature,
199
228
  }, cfg_path)
200
229
 
201
- # First-launch autodetect: if no config existed and the user didn't pin an
202
- # endpoint, probe localhost for a running local LLM and wire up the first
203
- # one found (persisted, so subsequent runs skip detection).
230
+ # First launch: ask the user for their model server URL + model name and
231
+ # persist them to config.toml. Interactive only when stdin isn't a TTY
232
+ # (piped / -p / cron) we fall back to silent autodetect so nothing blocks.
204
233
  onboarding = ""
205
234
  if first_run and not args.provider and not args.base_url:
206
- from drydock import detect
207
-
208
- found = detect.detect_local_llms()
209
- onboarding = detect.onboarding_message(found)
210
- if found:
211
- cfg["provider"] = found[0]["provider"]
212
- cfg["base_url"] = found[0]["base_url"]
213
- cfgmod.save_file(cfg, cfg_path)
235
+ if sys.stdin.isatty() and not args.prompt:
236
+ cfg, onboarding = _first_run_setup(cfg, cfg_path, cfgmod)
237
+ else:
238
+ from drydock import detect
239
+
240
+ found = detect.detect_local_llms()
241
+ onboarding = detect.onboarding_message(found)
242
+ if found:
243
+ cfg["provider"] = found[0]["provider"]
244
+ cfg["base_url"] = found[0]["base_url"]
245
+ cfgmod.save_file(cfg, cfg_path)
214
246
 
215
247
  config = {
216
248
  **cfg,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.0.26
3
+ Version: 3.0.27
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
@@ -35,6 +35,7 @@ tests/test_compaction.py
35
35
  tests/test_config.py
36
36
  tests/test_detect.py
37
37
  tests/test_empty_response.py
38
+ tests/test_first_run_setup.py
38
39
  tests/test_guards_and_tools.py
39
40
  tests/test_hallucinated_tools.py
40
41
  tests/test_leaked_tool_call.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.26"
10
+ version = "3.0.27"
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"
@@ -0,0 +1,34 @@
1
+ """First-launch setup: prompt for model server URL + name, persist to config."""
2
+ from __future__ import annotations
3
+
4
+ from drydock import cli
5
+ from drydock import config as cfgmod
6
+
7
+
8
+ def test_first_run_setup_persists_url_and_model(tmp_path, monkeypatch):
9
+ # No server running → defaults; user types a custom URL + model.
10
+ monkeypatch.setattr(cli, "_first_run_setup", cli._first_run_setup) # ensure present
11
+ import drydock.detect as detect
12
+ monkeypatch.setattr(detect, "detect_local_llms", lambda: [])
13
+ answers = iter(["http://127.0.0.1:8000/v1", "gemma4"])
14
+ monkeypatch.setattr("builtins.input", lambda *_: next(answers))
15
+
16
+ cfg_path = tmp_path / "config.toml"
17
+ cfg, onboarding = cli._first_run_setup(dict(cfgmod.DEFAULTS), cfg_path, cfgmod)
18
+
19
+ assert cfg["base_url"] == "http://127.0.0.1:8000/v1"
20
+ assert cfg["model"] == "gemma4"
21
+ # Persisted to disk.
22
+ on_disk = cfgmod.load_file(cfg_path)
23
+ assert on_disk["base_url"] == "http://127.0.0.1:8000/v1"
24
+ assert on_disk["model"] == "gemma4"
25
+ assert "gemma4" in onboarding and "127.0.0.1:8000" in onboarding
26
+
27
+
28
+ def test_first_run_setup_uses_defaults_on_empty_input(tmp_path, monkeypatch):
29
+ import drydock.detect as detect
30
+ monkeypatch.setattr(detect, "detect_local_llms", lambda: [])
31
+ monkeypatch.setattr("builtins.input", lambda *_: "") # user just hits Enter
32
+ cfg, _ = cli._first_run_setup(dict(cfgmod.DEFAULTS), tmp_path / "c.toml", cfgmod)
33
+ assert cfg["base_url"] == "http://127.0.0.1:8000/v1" # the documented default
34
+ assert cfg["model"] == "gemma4"
File without changes
File without changes
File without changes
File without changes