drydock-cli 3.0.133__tar.gz → 3.0.135__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 (146) hide show
  1. {drydock_cli-3.0.133/drydock_cli.egg-info → drydock_cli-3.0.135}/PKG-INFO +16 -2
  2. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/README.md +15 -1
  3. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/cli.py +24 -14
  4. {drydock_cli-3.0.133 → drydock_cli-3.0.135/drydock_cli.egg-info}/PKG-INFO +16 -2
  5. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/pyproject.toml +1 -1
  6. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_config_migration.py +3 -3
  7. drydock_cli-3.0.135/tests/test_first_run_setup.py +81 -0
  8. drydock_cli-3.0.133/tests/test_first_run_setup.py +0 -34
  9. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/LICENSE +0 -0
  10. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/NOTICE +0 -0
  11. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/__init__.py +0 -0
  12. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/__main__.py +0 -0
  13. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/advisor.py +0 -0
  14. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/agent.py +0 -0
  15. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/bash_safety.py +0 -0
  16. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/__init__.py +0 -0
  17. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/ml-data.md +0 -0
  18. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/ml-debug.md +0 -0
  19. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/ml-finetune.md +0 -0
  20. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/ml-metrics.md +0 -0
  21. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/ml-rl.md +0 -0
  22. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/ml-train.md +0 -0
  23. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/nist-ai-rmf.md +0 -0
  24. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/nist-csf.md +0 -0
  25. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/rmf-categorize.md +0 -0
  26. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/rmf-control.md +0 -0
  27. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/rmf-poam.md +0 -0
  28. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/rmf-review.md +0 -0
  29. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/stig-assess.md +0 -0
  30. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/builtin_skills/stig-remediate.md +0 -0
  31. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/cci.py +0 -0
  32. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/compaction.py +0 -0
  33. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/config.py +0 -0
  34. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/detect.py +0 -0
  35. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/events.py +0 -0
  36. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/extract.py +0 -0
  37. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/gittools.py +0 -0
  38. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/graphrag.py +0 -0
  39. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/guards.py +0 -0
  40. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/loop_detect.py +0 -0
  41. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/mcp.py +0 -0
  42. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/poam.py +0 -0
  43. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/providers.py +0 -0
  44. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/recipes.py +0 -0
  45. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/rmf.py +0 -0
  46. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/rmf_graph.py +0 -0
  47. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/skills.py +0 -0
  48. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/stig.py +0 -0
  49. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/suggest.py +0 -0
  50. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/task_state.py +0 -0
  51. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/tool_registry.py +0 -0
  52. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/tool_result.py +0 -0
  53. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/tools/__init__.py +0 -0
  54. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/tui/__init__.py +0 -0
  55. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/tui/app.py +0 -0
  56. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/tui/approval.py +0 -0
  57. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/tui/messages.py +0 -0
  58. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/tui/widgets.py +0 -0
  59. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/tuning.py +0 -0
  60. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/verification.py +0 -0
  61. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock/web.py +0 -0
  62. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock_cli.egg-info/SOURCES.txt +0 -0
  63. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock_cli.egg-info/dependency_links.txt +0 -0
  64. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock_cli.egg-info/entry_points.txt +0 -0
  65. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock_cli.egg-info/requires.txt +0 -0
  66. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/drydock_cli.egg-info/top_level.txt +0 -0
  67. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/setup.cfg +0 -0
  68. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_advisor.py +0 -0
  69. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_approval.py +0 -0
  70. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_back_command.py +0 -0
  71. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_bash_background.py +0 -0
  72. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_bash_binary_output.py +0 -0
  73. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_bash_crossplatform.py +0 -0
  74. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_bash_output_bounding.py +0 -0
  75. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_bash_process_group.py +0 -0
  76. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_bash_safety.py +0 -0
  77. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_bash_sanitize.py +0 -0
  78. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_bash_shell.py +0 -0
  79. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_bash_stdin.py +0 -0
  80. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_bash_stop_partial.py +0 -0
  81. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_bash_timeout_network.py +0 -0
  82. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_bash_timeout_param.py +0 -0
  83. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_cci.py +0 -0
  84. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_cli_agents.py +0 -0
  85. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_compact_command.py +0 -0
  86. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_compaction.py +0 -0
  87. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_config.py +0 -0
  88. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_context_limit_config.py +0 -0
  89. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_detect.py +0 -0
  90. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_dispatch.py +0 -0
  91. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_e2e_connected.py +0 -0
  92. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_edit_replace_all.py +0 -0
  93. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_empty_response.py +0 -0
  94. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_events.py +0 -0
  95. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_extract.py +0 -0
  96. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_failure_loop.py +0 -0
  97. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_gittools.py +0 -0
  98. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_glob_edit_edges.py +0 -0
  99. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_graphify_example.py +0 -0
  100. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_graphrag.py +0 -0
  101. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_graphrag_quoted_path.py +0 -0
  102. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_grep_and_read_robust.py +0 -0
  103. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_guards_and_tools.py +0 -0
  104. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_hallucinated_tools.py +0 -0
  105. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_leaked_tool_call.py +0 -0
  106. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_loop_detect.py +0 -0
  107. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_mcp.py +0 -0
  108. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_oneshot_unreachable.py +0 -0
  109. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_overthink_interrupt.py +0 -0
  110. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_plan_autocontinue.py +0 -0
  111. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_poam.py +0 -0
  112. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_providers_unreachable.py +0 -0
  113. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_read_index.py +0 -0
  114. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_recipes.py +0 -0
  115. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_repetition_interrupt.py +0 -0
  116. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_rmf.py +0 -0
  117. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_rmf_graph.py +0 -0
  118. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_rmf_stig_graph.py +0 -0
  119. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_rolling_plan.py +0 -0
  120. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_runaway_repetition.py +0 -0
  121. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_screenshot.py +0 -0
  122. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_server_probe.py +0 -0
  123. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_skills.py +0 -0
  124. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_stall_retry.py +0 -0
  125. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_stig.py +0 -0
  126. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_stop.py +0 -0
  127. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_streaming_newlines.py +0 -0
  128. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_subagent.py +0 -0
  129. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_suggest.py +0 -0
  130. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_system_prompt_help.py +0 -0
  131. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_task_state.py +0 -0
  132. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_todo.py +0 -0
  133. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_tool_arg_coercion.py +0 -0
  134. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_tool_arg_coercion_more.py +0 -0
  135. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_tool_arg_parsing.py +0 -0
  136. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_tool_result.py +0 -0
  137. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_tools_undo.py +0 -0
  138. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_tui.py +0 -0
  139. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_tuning.py +0 -0
  140. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_verification_gate.py +0 -0
  141. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_viewimage.py +0 -0
  142. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_vision_input.py +0 -0
  143. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_web_tools.py +0 -0
  144. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_windows_shell.py +0 -0
  145. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_write_content_coerce.py +0 -0
  146. {drydock_cli-3.0.133 → drydock_cli-3.0.135}/tests/test_xccdf.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.0.133
3
+ Version: 3.0.135
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
@@ -83,6 +83,17 @@ A full agentic CLI harness — every tool below is clean-room and dependency-fre
83
83
  and the copy-paste [example config](examples/mcp/graphify.json).
84
84
  - **Skills** — reusable `/<name>` commands authored as markdown in
85
85
  `~/.drydock/skills/` (or `<project>/.drydock/skills/`); `$ARGS` substitution.
86
+ Bundled skill families: **RMF** (`/rmf-*`), **STIG** (`/stig-*`), **NIST
87
+ governance** (`/nist-ai-rmf`, `/nist-csf`), and **ML engineering** (`/ml-train`,
88
+ `/ml-metrics`, `/ml-finetune`, `/ml-debug`, `/ml-rl`, `/ml-data`).
89
+ - **Screen capture** — the `Screenshot` tool grabs the screen and the vision model
90
+ *sees* it (Windows/macOS/Linux) — review a GUI, read what's displayed, debug a render.
91
+ - **Governed reliability** — a deterministic controller wraps the loop: the objective
92
+ + acceptance criteria live in structured state that survives context compaction; a
93
+ **verification gate** blocks a self-declared "done" until a test/check actually runs
94
+ and passes; every run writes a durable event trace (inspect with `/events`).
95
+ - **Cross-platform** — runs natively on Linux, macOS, and **Windows via PowerShell/cmd**
96
+ (no WSL or Git-Bash required); `/shell` shows which shell your commands run in.
86
97
  - **Loops** — `/loop <count> <prompt>` runs a prompt iteratively (Esc stops).
87
98
 
88
99
  ## Slash commands
@@ -103,6 +114,8 @@ Typed into the prompt. The agent also knows these, so you can just **ask it**
103
114
  | `/mcp` | List connected MCP servers + their tools |
104
115
  | `/rmf bootstrap [families]` | Ingest the NIST SP 800-53 catalog (RMF automation) |
105
116
  | `/rmf-control` · `/rmf-categorize` · `/rmf-review` · `/rmf-poam` | Bundled RMF skills |
117
+ | `/nist-ai-rmf` · `/nist-csf` | NIST AI RMF 1.0 · Cybersecurity Framework 2.0 (defensive governance) |
118
+ | `/ml-train` · `/ml-metrics` · `/ml-finetune` · `/ml-debug` · `/ml-rl` · `/ml-data` | ML-engineering skills (PyTorch, full/LoRA fine-tune, metrics, RL, data prep) |
106
119
  | `/stig new <xccdf>` | Generate a blank `.ckl` from a DISA STIG benchmark |
107
120
  | `/stig <ckl>` · `/stig <ckl> open` | Summarize a checklist · list findings by status |
108
121
  | `/stig graph <ckl>` | Ingest a checklist into the RMF graph (auto-links rules→controls via CCI) |
@@ -111,6 +124,7 @@ Typed into the prompt. The agent also knows these, so you can just **ask it**
111
124
  | `/undo` · `/back` | Revert the last write · rewind the last turn |
112
125
  | `/compact` · `/context [n]` | Shrink context now · view/set the context-window budget |
113
126
  | `/advisor` · `/ask <q>` | Set up a 2nd 'advisor' model (Gemini etc.) · consult it |
127
+ | `/events` · `/shell` | Show this run's execution trace · which shell Bash uses (Win/macOS/Linux) |
114
128
  | `/status` · `/clear` · `/help` · `/quit` | Session stats · reset · help · exit |
115
129
 
116
130
  ### Knowledge base (GraphRAG) — ingesting your documents
@@ -239,7 +253,7 @@ drydock
239
253
  ```
240
254
 
241
255
  Requires Python 3.11+. From source instead:
242
- `git clone https://github.com/fbobe321/drydock-v3.git && cd drydock-v3 && pip install -e .`
256
+ `git clone https://github.com/fbobe321/drydock.git && cd drydock && pip install -e .`
243
257
 
244
258
  On first launch with no config, Drydock probes localhost for a running local
245
259
  LLM (llama.cpp/vLLM `:8000`, Ollama `:11434`, LM Studio `:1234`) and wires up
@@ -59,6 +59,17 @@ A full agentic CLI harness — every tool below is clean-room and dependency-fre
59
59
  and the copy-paste [example config](examples/mcp/graphify.json).
60
60
  - **Skills** — reusable `/<name>` commands authored as markdown in
61
61
  `~/.drydock/skills/` (or `<project>/.drydock/skills/`); `$ARGS` substitution.
62
+ Bundled skill families: **RMF** (`/rmf-*`), **STIG** (`/stig-*`), **NIST
63
+ governance** (`/nist-ai-rmf`, `/nist-csf`), and **ML engineering** (`/ml-train`,
64
+ `/ml-metrics`, `/ml-finetune`, `/ml-debug`, `/ml-rl`, `/ml-data`).
65
+ - **Screen capture** — the `Screenshot` tool grabs the screen and the vision model
66
+ *sees* it (Windows/macOS/Linux) — review a GUI, read what's displayed, debug a render.
67
+ - **Governed reliability** — a deterministic controller wraps the loop: the objective
68
+ + acceptance criteria live in structured state that survives context compaction; a
69
+ **verification gate** blocks a self-declared "done" until a test/check actually runs
70
+ and passes; every run writes a durable event trace (inspect with `/events`).
71
+ - **Cross-platform** — runs natively on Linux, macOS, and **Windows via PowerShell/cmd**
72
+ (no WSL or Git-Bash required); `/shell` shows which shell your commands run in.
62
73
  - **Loops** — `/loop <count> <prompt>` runs a prompt iteratively (Esc stops).
63
74
 
64
75
  ## Slash commands
@@ -79,6 +90,8 @@ Typed into the prompt. The agent also knows these, so you can just **ask it**
79
90
  | `/mcp` | List connected MCP servers + their tools |
80
91
  | `/rmf bootstrap [families]` | Ingest the NIST SP 800-53 catalog (RMF automation) |
81
92
  | `/rmf-control` · `/rmf-categorize` · `/rmf-review` · `/rmf-poam` | Bundled RMF skills |
93
+ | `/nist-ai-rmf` · `/nist-csf` | NIST AI RMF 1.0 · Cybersecurity Framework 2.0 (defensive governance) |
94
+ | `/ml-train` · `/ml-metrics` · `/ml-finetune` · `/ml-debug` · `/ml-rl` · `/ml-data` | ML-engineering skills (PyTorch, full/LoRA fine-tune, metrics, RL, data prep) |
82
95
  | `/stig new <xccdf>` | Generate a blank `.ckl` from a DISA STIG benchmark |
83
96
  | `/stig <ckl>` · `/stig <ckl> open` | Summarize a checklist · list findings by status |
84
97
  | `/stig graph <ckl>` | Ingest a checklist into the RMF graph (auto-links rules→controls via CCI) |
@@ -87,6 +100,7 @@ Typed into the prompt. The agent also knows these, so you can just **ask it**
87
100
  | `/undo` · `/back` | Revert the last write · rewind the last turn |
88
101
  | `/compact` · `/context [n]` | Shrink context now · view/set the context-window budget |
89
102
  | `/advisor` · `/ask <q>` | Set up a 2nd 'advisor' model (Gemini etc.) · consult it |
103
+ | `/events` · `/shell` | Show this run's execution trace · which shell Bash uses (Win/macOS/Linux) |
90
104
  | `/status` · `/clear` · `/help` · `/quit` | Session stats · reset · help · exit |
91
105
 
92
106
  ### Knowledge base (GraphRAG) — ingesting your documents
@@ -215,7 +229,7 @@ drydock
215
229
  ```
216
230
 
217
231
  Requires Python 3.11+. From source instead:
218
- `git clone https://github.com/fbobe321/drydock-v3.git && cd drydock-v3 && pip install -e .`
232
+ `git clone https://github.com/fbobe321/drydock.git && cd drydock && pip install -e .`
219
233
 
220
234
  On first launch with no config, Drydock probes localhost for a running local
221
235
  LLM (llama.cpp/vLLM `:8000`, Ollama `:11434`, LM Studio `:1234`) and wires up
@@ -248,6 +248,27 @@ def _summarize(inputs: dict) -> str:
248
248
 
249
249
  # ── Entry point ───────────────────────────────────────────────────────────
250
250
 
251
+ def _resolve_first_run(cfg: dict, args, cfg_path, cfgmod, interactive: bool) -> tuple[dict, str]:
252
+ """First-launch model-server resolution. AUTODETECT a live local server and
253
+ wire it up with ZERO prompts (the frictionless path). Only when nothing is
254
+ detected AND we're interactive do we ask the user to enter their own URL — so
255
+ the easy install (server already running) needs no input, while a custom
256
+ endpoint is still one prompt (or `--base-url`, or `/model url` later)."""
257
+ from drydock import detect
258
+
259
+ found = detect.detect_local_llms()
260
+ if found:
261
+ cfg["provider"] = found[0]["provider"]
262
+ cfg["base_url"] = found[0]["base_url"]
263
+ if found[0].get("models") and not getattr(args, "model", None):
264
+ cfg["model"] = found[0]["models"][0]
265
+ cfgmod.save_file(cfg, cfg_path)
266
+ return cfg, detect.onboarding_message(found)
267
+ if interactive:
268
+ return _first_run_setup(cfg, cfg_path, cfgmod)
269
+ return cfg, detect.onboarding_message(found)
270
+
271
+
251
272
  def _first_run_setup(cfg: dict, cfg_path, cfgmod) -> tuple[dict, str]:
252
273
  """Interactive first-launch: ask for the model server URL + model name and
253
274
  persist them to config.toml. Autodetect (if anything is listening) supplies
@@ -346,22 +367,11 @@ def main():
346
367
  "context_limit": args.context_limit,
347
368
  }, cfg_path)
348
369
 
349
- # First launch: ask the user for their model server URL + model name and
350
- # persist them to config.toml. Interactive only — when stdin isn't a TTY
351
- # (piped / -p / cron) we fall back to silent autodetect so nothing blocks.
370
+ # First launch: autodetect a local server and wire it up (see _resolve_first_run).
352
371
  onboarding = ""
353
372
  if first_run and not args.provider and not args.base_url:
354
- if sys.stdin.isatty() and not args.prompt:
355
- cfg, onboarding = _first_run_setup(cfg, cfg_path, cfgmod)
356
- else:
357
- from drydock import detect
358
-
359
- found = detect.detect_local_llms()
360
- onboarding = detect.onboarding_message(found)
361
- if found:
362
- cfg["provider"] = found[0]["provider"]
363
- cfg["base_url"] = found[0]["base_url"]
364
- cfgmod.save_file(cfg, cfg_path)
373
+ interactive = sys.stdin.isatty() and not args.prompt
374
+ cfg, onboarding = _resolve_first_run(cfg, args, cfg_path, cfgmod, interactive)
365
375
 
366
376
  config = {
367
377
  # context_limit now comes from cfg (DEFAULTS < config.toml < --context-limit)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.0.133
3
+ Version: 3.0.135
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
@@ -83,6 +83,17 @@ A full agentic CLI harness — every tool below is clean-room and dependency-fre
83
83
  and the copy-paste [example config](examples/mcp/graphify.json).
84
84
  - **Skills** — reusable `/<name>` commands authored as markdown in
85
85
  `~/.drydock/skills/` (or `<project>/.drydock/skills/`); `$ARGS` substitution.
86
+ Bundled skill families: **RMF** (`/rmf-*`), **STIG** (`/stig-*`), **NIST
87
+ governance** (`/nist-ai-rmf`, `/nist-csf`), and **ML engineering** (`/ml-train`,
88
+ `/ml-metrics`, `/ml-finetune`, `/ml-debug`, `/ml-rl`, `/ml-data`).
89
+ - **Screen capture** — the `Screenshot` tool grabs the screen and the vision model
90
+ *sees* it (Windows/macOS/Linux) — review a GUI, read what's displayed, debug a render.
91
+ - **Governed reliability** — a deterministic controller wraps the loop: the objective
92
+ + acceptance criteria live in structured state that survives context compaction; a
93
+ **verification gate** blocks a self-declared "done" until a test/check actually runs
94
+ and passes; every run writes a durable event trace (inspect with `/events`).
95
+ - **Cross-platform** — runs natively on Linux, macOS, and **Windows via PowerShell/cmd**
96
+ (no WSL or Git-Bash required); `/shell` shows which shell your commands run in.
86
97
  - **Loops** — `/loop <count> <prompt>` runs a prompt iteratively (Esc stops).
87
98
 
88
99
  ## Slash commands
@@ -103,6 +114,8 @@ Typed into the prompt. The agent also knows these, so you can just **ask it**
103
114
  | `/mcp` | List connected MCP servers + their tools |
104
115
  | `/rmf bootstrap [families]` | Ingest the NIST SP 800-53 catalog (RMF automation) |
105
116
  | `/rmf-control` · `/rmf-categorize` · `/rmf-review` · `/rmf-poam` | Bundled RMF skills |
117
+ | `/nist-ai-rmf` · `/nist-csf` | NIST AI RMF 1.0 · Cybersecurity Framework 2.0 (defensive governance) |
118
+ | `/ml-train` · `/ml-metrics` · `/ml-finetune` · `/ml-debug` · `/ml-rl` · `/ml-data` | ML-engineering skills (PyTorch, full/LoRA fine-tune, metrics, RL, data prep) |
106
119
  | `/stig new <xccdf>` | Generate a blank `.ckl` from a DISA STIG benchmark |
107
120
  | `/stig <ckl>` · `/stig <ckl> open` | Summarize a checklist · list findings by status |
108
121
  | `/stig graph <ckl>` | Ingest a checklist into the RMF graph (auto-links rules→controls via CCI) |
@@ -111,6 +124,7 @@ Typed into the prompt. The agent also knows these, so you can just **ask it**
111
124
  | `/undo` · `/back` | Revert the last write · rewind the last turn |
112
125
  | `/compact` · `/context [n]` | Shrink context now · view/set the context-window budget |
113
126
  | `/advisor` · `/ask <q>` | Set up a 2nd 'advisor' model (Gemini etc.) · consult it |
127
+ | `/events` · `/shell` | Show this run's execution trace · which shell Bash uses (Win/macOS/Linux) |
114
128
  | `/status` · `/clear` · `/help` · `/quit` | Session stats · reset · help · exit |
115
129
 
116
130
  ### Knowledge base (GraphRAG) — ingesting your documents
@@ -239,7 +253,7 @@ drydock
239
253
  ```
240
254
 
241
255
  Requires Python 3.11+. From source instead:
242
- `git clone https://github.com/fbobe321/drydock-v3.git && cd drydock-v3 && pip install -e .`
256
+ `git clone https://github.com/fbobe321/drydock.git && cd drydock && pip install -e .`
243
257
 
244
258
  On first launch with no config, Drydock probes localhost for a running local
245
259
  LLM (llama.cpp/vLLM `:8000`, Ollama `:11434`, LM Studio `:1234`) and wires up
@@ -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.133"
10
+ version = "3.0.135"
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"
@@ -77,8 +77,8 @@ def test_partial_config_backfills_missing_keys(tmp_path):
77
77
 
78
78
 
79
79
  def test_explicit_user_values_are_respected(tmp_path):
80
- m = _load(tmp_path, 'base_url = "http://192.168.50.129:8000/v1"\nmodel = "gemma4"\n')
81
- assert m["base_url"] == "http://192.168.50.129:8000/v1"
80
+ m = _load(tmp_path, 'base_url = "http://192.0.2.10:8000/v1"\nmodel = "gemma4"\n')
81
+ assert m["base_url"] == "http://192.0.2.10:8000/v1"
82
82
 
83
83
 
84
84
  def test_default_base_url_is_concrete_not_empty():
@@ -108,7 +108,7 @@ def test_v2_file_is_migrated_and_backed_up(tmp_path):
108
108
  def test_real_v3_file_is_left_untouched(tmp_path):
109
109
  """A genuine v3 file (has a known key) must NOT be migrated/rewritten."""
110
110
  p = tmp_path / "config.toml"
111
- p.write_text('base_url = "http://192.168.50.21:8000/v1"\n# my hand edit\n')
111
+ p.write_text('base_url = "http://192.0.2.20:8000/v1"\n# my hand edit\n')
112
112
  cfg.resolve({}, path=p)
113
113
  assert not p.with_name("config.toml.bak").exists()
114
114
  assert "# my hand edit" in p.read_text() # untouched
@@ -0,0 +1,81 @@
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"
35
+
36
+
37
+ class _Args:
38
+ def __init__(self, model=None): self.model = model
39
+
40
+
41
+ def test_autowire_uses_detected_server_no_prompt(tmp_path, monkeypatch):
42
+ """First launch with a live server: wire it up automatically, NO input()."""
43
+ from drydock import cli, config as cfgmod, detect
44
+ monkeypatch.setattr(detect, "detect_local_llms",
45
+ lambda: [{"provider": "ollama", "base_url": "http://localhost:11434/v1",
46
+ "models": ["llama3"]}])
47
+ # input() must NOT be called on the auto-wire path
48
+ monkeypatch.setattr("builtins.input", lambda *a: (_ for _ in ()).throw(AssertionError("prompted!")))
49
+ cfg_path = tmp_path / "c.toml"
50
+ cfg, onboarding = cli._resolve_first_run(dict(cfgmod.DEFAULTS), _Args(), cfg_path, cfgmod, interactive=True)
51
+ assert cfg["base_url"] == "http://localhost:11434/v1"
52
+ assert cfg["provider"] == "ollama" and cfg["model"] == "llama3"
53
+ assert "Detected" in onboarding and cfg_path.exists() # persisted
54
+
55
+
56
+ def test_autowire_respects_explicit_model(tmp_path, monkeypatch):
57
+ from drydock import cli, config as cfgmod, detect
58
+ monkeypatch.setattr(detect, "detect_local_llms",
59
+ lambda: [{"provider": "vllm", "base_url": "http://localhost:8000/v1",
60
+ "models": ["served-model"]}])
61
+ cfg, _ = cli._resolve_first_run(dict(cfgmod.DEFAULTS), _Args(model="gemma4"),
62
+ tmp_path / "c.toml", cfgmod, interactive=True)
63
+ assert cfg["model"] == "gemma4" # --model wins over the detected id
64
+
65
+
66
+ def test_nothing_detected_noninteractive_shows_help(tmp_path, monkeypatch):
67
+ from drydock import cli, config as cfgmod, detect
68
+ monkeypatch.setattr(detect, "detect_local_llms", lambda: [])
69
+ monkeypatch.setattr("builtins.input", lambda *a: (_ for _ in ()).throw(AssertionError("prompted!")))
70
+ cfg, onboarding = cli._resolve_first_run(dict(cfgmod.DEFAULTS), _Args(), tmp_path / "c.toml",
71
+ cfgmod, interactive=False)
72
+ assert "No local LLM detected" in onboarding # tells the user how to set a URL
73
+
74
+
75
+ def test_nothing_detected_interactive_prompts(tmp_path, monkeypatch):
76
+ from drydock import cli, config as cfgmod, detect
77
+ monkeypatch.setattr(detect, "detect_local_llms", lambda: [])
78
+ monkeypatch.setattr("builtins.input", lambda *a: "http://myhost:9000/v1")
79
+ cfg, _ = cli._resolve_first_run(dict(cfgmod.DEFAULTS), _Args(), tmp_path / "c.toml",
80
+ cfgmod, interactive=True)
81
+ assert cfg["base_url"] == "http://myhost:9000/v1" # user's URL honored
@@ -1,34 +0,0 @@
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