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.
- {drydock_cli-3.0.26/drydock_cli.egg-info → drydock_cli-3.0.27}/PKG-INFO +1 -1
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/__init__.py +1 -1
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/cli.py +43 -11
- {drydock_cli-3.0.26 → drydock_cli-3.0.27/drydock_cli.egg-info}/PKG-INFO +1 -1
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock_cli.egg-info/SOURCES.txt +1 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/pyproject.toml +1 -1
- drydock_cli-3.0.27/tests/test_first_run_setup.py +34 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/LICENSE +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/NOTICE +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/README.md +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/__main__.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/agent.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/bash_safety.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/compaction.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/config.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/detect.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/guards.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/loop_detect.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/providers.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tool_registry.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tools/__init__.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tui/__init__.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tui/app.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tui/approval.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tui/messages.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tui/widgets.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock/tuning.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock_cli.egg-info/dependency_links.txt +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock_cli.egg-info/entry_points.txt +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock_cli.egg-info/requires.txt +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/drydock_cli.egg-info/top_level.txt +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/setup.cfg +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_approval.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_back_command.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_bash_safety.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_cli_agents.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_compaction.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_config.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_detect.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_empty_response.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_guards_and_tools.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_hallucinated_tools.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_leaked_tool_call.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_loop_detect.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_plan_autocontinue.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_providers_unreachable.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_stop.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_streaming_newlines.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_subagent.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_todo.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_tool_arg_parsing.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_tools_undo.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_tui.py +0 -0
- {drydock_cli-3.0.26 → drydock_cli-3.0.27}/tests/test_tuning.py +0 -0
|
@@ -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
|
|
202
|
-
#
|
|
203
|
-
#
|
|
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
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
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,
|
|
@@ -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.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
|
|
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
|