drydock-cli 3.1.9__tar.gz → 3.1.11__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 (192) hide show
  1. {drydock_cli-3.1.9/drydock_cli.egg-info → drydock_cli-3.1.11}/PKG-INFO +19 -10
  2. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/README.md +18 -9
  3. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/cli.py +6 -3
  4. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/config.py +89 -13
  5. {drydock_cli-3.1.9 → drydock_cli-3.1.11/drydock_cli.egg-info}/PKG-INFO +19 -10
  6. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/pyproject.toml +1 -1
  7. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_config.py +75 -0
  8. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/LICENSE +0 -0
  9. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/NOTICE +0 -0
  10. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/__init__.py +0 -0
  11. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/__main__.py +0 -0
  12. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/advisor.py +0 -0
  13. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/agent.py +0 -0
  14. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/bash_safety.py +0 -0
  15. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/budget.py +0 -0
  16. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/__init__.py +0 -0
  17. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/document-canvas.md +0 -0
  18. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/fiar-assess.md +0 -0
  19. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/fiar-cap.md +0 -0
  20. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/fiar-evidence.md +0 -0
  21. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/fiar-readiness.md +0 -0
  22. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/ml-data.md +0 -0
  23. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/ml-debug.md +0 -0
  24. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/ml-finetune.md +0 -0
  25. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/ml-metrics.md +0 -0
  26. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/ml-rl.md +0 -0
  27. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/ml-train.md +0 -0
  28. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/nist-ai-rmf.md +0 -0
  29. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/nist-csf.md +0 -0
  30. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/rmf-categorize.md +0 -0
  31. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/rmf-control.md +0 -0
  32. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/rmf-poam.md +0 -0
  33. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/rmf-review.md +0 -0
  34. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/stig-assess.md +0 -0
  35. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/builtin_skills/stig-remediate.md +0 -0
  36. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/cci.py +0 -0
  37. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/compaction.py +0 -0
  38. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/detect.py +0 -0
  39. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/doccanvas.py +0 -0
  40. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/events.py +0 -0
  41. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/extract.py +0 -0
  42. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/fiar.py +0 -0
  43. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/gittools.py +0 -0
  44. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/graphrag.py +0 -0
  45. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/guards.py +0 -0
  46. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/loop_detect.py +0 -0
  47. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/mcp.py +0 -0
  48. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/phases.py +0 -0
  49. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/poam.py +0 -0
  50. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/progress.py +0 -0
  51. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/providers.py +0 -0
  52. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/ratchet.py +0 -0
  53. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/recipes.py +0 -0
  54. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/recovery.py +0 -0
  55. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/resume.py +0 -0
  56. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/rmf.py +0 -0
  57. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/rmf_graph.py +0 -0
  58. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/skills.py +0 -0
  59. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/stig.py +0 -0
  60. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/subagents.py +0 -0
  61. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/suggest.py +0 -0
  62. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/task_state.py +0 -0
  63. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/tool_policy.py +0 -0
  64. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/tool_registry.py +0 -0
  65. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/tool_result.py +0 -0
  66. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/tool_select.py +0 -0
  67. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/tool_validate.py +0 -0
  68. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/tools/__init__.py +0 -0
  69. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/trajectory.py +0 -0
  70. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/tui/__init__.py +0 -0
  71. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/tui/app.py +0 -0
  72. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/tui/approval.py +0 -0
  73. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/tui/messages.py +0 -0
  74. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/tui/widgets.py +0 -0
  75. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/tuning.py +0 -0
  76. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/verification.py +0 -0
  77. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock/web.py +0 -0
  78. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock_cli.egg-info/SOURCES.txt +0 -0
  79. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock_cli.egg-info/dependency_links.txt +0 -0
  80. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock_cli.egg-info/entry_points.txt +0 -0
  81. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock_cli.egg-info/requires.txt +0 -0
  82. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/drydock_cli.egg-info/top_level.txt +0 -0
  83. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/setup.cfg +0 -0
  84. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_advisor.py +0 -0
  85. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_approval.py +0 -0
  86. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_back_command.py +0 -0
  87. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_bash_background.py +0 -0
  88. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_bash_binary_output.py +0 -0
  89. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_bash_crossplatform.py +0 -0
  90. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_bash_output_bounding.py +0 -0
  91. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_bash_process_group.py +0 -0
  92. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_bash_safety.py +0 -0
  93. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_bash_sanitize.py +0 -0
  94. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_bash_shell.py +0 -0
  95. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_bash_stdin.py +0 -0
  96. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_bash_stop_partial.py +0 -0
  97. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_bash_timeout_network.py +0 -0
  98. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_bash_timeout_param.py +0 -0
  99. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_budget.py +0 -0
  100. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_cci.py +0 -0
  101. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_cli_agents.py +0 -0
  102. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_compact_command.py +0 -0
  103. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_compaction.py +0 -0
  104. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_config_migration.py +0 -0
  105. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_context_limit_config.py +0 -0
  106. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_context_limit_issue25.py +0 -0
  107. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_criteria_coverage.py +0 -0
  108. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_cycling_detection.py +0 -0
  109. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_degenerate_argument.py +0 -0
  110. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_detect.py +0 -0
  111. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_dispatch.py +0 -0
  112. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_doccanvas.py +0 -0
  113. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_e2e_connected.py +0 -0
  114. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_edit_replace_all.py +0 -0
  115. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_effort_governor.py +0 -0
  116. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_empty_response.py +0 -0
  117. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_events.py +0 -0
  118. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_extract.py +0 -0
  119. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_failure_loop.py +0 -0
  120. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_fiar.py +0 -0
  121. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_first_run_setup.py +0 -0
  122. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_gittools.py +0 -0
  123. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_glob_edit_edges.py +0 -0
  124. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_graphify_example.py +0 -0
  125. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_graphrag.py +0 -0
  126. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_graphrag_quoted_path.py +0 -0
  127. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_graphrag_sqlite.py +0 -0
  128. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_grep_and_read_robust.py +0 -0
  129. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_guards_and_tools.py +0 -0
  130. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_hallucinated_tools.py +0 -0
  131. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_leaked_tool_call.py +0 -0
  132. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_loop_detect.py +0 -0
  133. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_mcp.py +0 -0
  134. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_model_registry.py +0 -0
  135. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_oneshot_unreachable.py +0 -0
  136. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_overthink_interrupt.py +0 -0
  137. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_phases.py +0 -0
  138. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_plan_autocontinue.py +0 -0
  139. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_poam.py +0 -0
  140. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_progress.py +0 -0
  141. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_providers_unreachable.py +0 -0
  142. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_ratchet.py +0 -0
  143. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_read_index.py +0 -0
  144. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_recipes.py +0 -0
  145. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_recovery.py +0 -0
  146. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_recovery_config.py +0 -0
  147. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_recovery_integration.py +0 -0
  148. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_repeated_outcome.py +0 -0
  149. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_repetition_interrupt.py +0 -0
  150. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_resume_events.py +0 -0
  151. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_resume_restore.py +0 -0
  152. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_resume_snapshot.py +0 -0
  153. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_rmf.py +0 -0
  154. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_rmf_graph.py +0 -0
  155. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_rmf_stig_graph.py +0 -0
  156. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_rolling_plan.py +0 -0
  157. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_runaway_repetition.py +0 -0
  158. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_screenshot.py +0 -0
  159. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_server_probe.py +0 -0
  160. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_skills.py +0 -0
  161. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_sqlite_events.py +0 -0
  162. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_stall_retry.py +0 -0
  163. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_stig.py +0 -0
  164. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_stop.py +0 -0
  165. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_streaming_newlines.py +0 -0
  166. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_subagent.py +0 -0
  167. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_subagents.py +0 -0
  168. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_suggest.py +0 -0
  169. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_system_prompt_help.py +0 -0
  170. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_task_state.py +0 -0
  171. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_timeline.py +0 -0
  172. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_todo.py +0 -0
  173. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_tool_arg_coercion.py +0 -0
  174. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_tool_arg_coercion_more.py +0 -0
  175. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_tool_arg_parsing.py +0 -0
  176. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_tool_canonical.py +0 -0
  177. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_tool_policy.py +0 -0
  178. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_tool_result.py +0 -0
  179. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_tool_select.py +0 -0
  180. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_tool_validate.py +0 -0
  181. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_tools_undo.py +0 -0
  182. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_trajectory.py +0 -0
  183. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_tui.py +0 -0
  184. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_tuning.py +0 -0
  185. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_verification_gate.py +0 -0
  186. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_viewimage.py +0 -0
  187. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_vision_input.py +0 -0
  188. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_web_tools.py +0 -0
  189. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_windows_shell.py +0 -0
  190. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_worker_subagent.py +0 -0
  191. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_write_content_coerce.py +0 -0
  192. {drydock_cli-3.1.9 → drydock_cli-3.1.11}/tests/test_xccdf.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.1.9
3
+ Version: 3.1.11
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
@@ -450,15 +450,24 @@ conventions.
450
450
  ### Custom system prompt
451
451
 
452
452
  For standing instructions applied on **every** turn (stronger than the
453
- per-project `AGENTS.md`, which is framed as optional background), set a custom
454
- system prompt via either:
455
-
456
- - a file: `~/.drydock/system_prompt.md` — write your instructions there, or
457
- - a config key in `~/.drydock/config.toml`: `system_prompt = "..."`.
458
-
459
- The file wins if both are set; contents are capped at 8000 chars. It's injected
460
- after drydock's base prompt and before any project `AGENTS.md`, and applies in
461
- both the TUI and CLI.
453
+ per-project `AGENTS.md`, which is framed as optional background), edit a
454
+ `system_prompt.md` file. Drydock creates a commented template for you, so you
455
+ never have to know where it lives — just open it and write your instructions.
456
+ It has no effect until you add text (the template is all comments, ignored).
457
+
458
+ Two scopes, most-specific wins:
459
+
460
+ - **Per-project** — `<project>/.drydock/system_prompt.md`. Auto-created inside
461
+ any **git repo** (so different projects get different standing orders, and
462
+ drydock never litters `.drydock/` folders in non-project directories).
463
+ - **Global** — `~/.drydock/system_prompt.md`. Auto-created on first run; applies
464
+ to every project. A per-project file **overrides** it.
465
+ - **Or the config key** — `system_prompt = "..."` in `~/.drydock/config.toml`
466
+ (lowest precedence).
467
+
468
+ Contents are capped at 8000 chars, injected after drydock's base prompt and
469
+ before any project `AGENTS.md`, and apply in both the TUI and CLI. Restart
470
+ drydock after editing.
462
471
 
463
472
  ## Safety
464
473
 
@@ -426,15 +426,24 @@ conventions.
426
426
  ### Custom system prompt
427
427
 
428
428
  For standing instructions applied on **every** turn (stronger than the
429
- per-project `AGENTS.md`, which is framed as optional background), set a custom
430
- system prompt via either:
431
-
432
- - a file: `~/.drydock/system_prompt.md` — write your instructions there, or
433
- - a config key in `~/.drydock/config.toml`: `system_prompt = "..."`.
434
-
435
- The file wins if both are set; contents are capped at 8000 chars. It's injected
436
- after drydock's base prompt and before any project `AGENTS.md`, and applies in
437
- both the TUI and CLI.
429
+ per-project `AGENTS.md`, which is framed as optional background), edit a
430
+ `system_prompt.md` file. Drydock creates a commented template for you, so you
431
+ never have to know where it lives — just open it and write your instructions.
432
+ It has no effect until you add text (the template is all comments, ignored).
433
+
434
+ Two scopes, most-specific wins:
435
+
436
+ - **Per-project** — `<project>/.drydock/system_prompt.md`. Auto-created inside
437
+ any **git repo** (so different projects get different standing orders, and
438
+ drydock never litters `.drydock/` folders in non-project directories).
439
+ - **Global** — `~/.drydock/system_prompt.md`. Auto-created on first run; applies
440
+ to every project. A per-project file **overrides** it.
441
+ - **Or the config key** — `system_prompt = "..."` in `~/.drydock/config.toml`
442
+ (lowest precedence).
443
+
444
+ Contents are capped at 8000 chars, injected after drydock's base prompt and
445
+ before any project `AGENTS.md`, and apply in both the TUI and CLI. Restart
446
+ drydock after editing.
438
447
 
439
448
  ## Safety
440
449
 
@@ -13,7 +13,7 @@ import sys
13
13
  from pathlib import Path
14
14
 
15
15
  from drydock import __version__
16
- from drydock.config import load_user_system_prompt
16
+ from drydock.config import load_user_system_prompt, ensure_project_system_prompt
17
17
  from drydock.agent import AgentState, run, TextChunk, ToolStart, ToolEnd, TurnDone
18
18
 
19
19
 
@@ -436,8 +436,11 @@ def main():
436
436
  # greeting into a runaway build. The TUI reads this from config; CLI modes
437
437
  # call load_project_instructions directly (see run_interactive/run_oneshot).
438
438
  config["project_instructions"] = load_project_instructions()
439
- # The user's custom system prompt (~/.drydock/system_prompt.md or the
440
- # `system_prompt` config key) — standing instructions the TUI injects every turn.
439
+ # In a git project, drop a discoverable per-project prompt template at
440
+ # <project>/.drydock/system_prompt.md (inert until edited). Then load the
441
+ # effective custom prompt — project file OVERRIDES the global ~/.drydock one —
442
+ # as standing instructions the TUI/CLI inject every turn.
443
+ ensure_project_system_prompt()
441
444
  config["user_system_prompt"] = load_user_system_prompt(config)
442
445
 
443
446
  # Connect any configured MCP servers and register their tools so the model
@@ -13,6 +13,7 @@ All logic original to Drydock.
13
13
  """
14
14
  from __future__ import annotations
15
15
 
16
+ import re
16
17
  import tomllib
17
18
  from pathlib import Path
18
19
 
@@ -119,27 +120,98 @@ def default_config_path() -> Path:
119
120
 
120
121
 
121
122
  def system_prompt_path() -> Path:
122
- """User's global custom system-prompt file."""
123
+ """User's GLOBAL custom system-prompt file (applies to every project)."""
123
124
  return Path.home() / ".drydock" / "system_prompt.md"
124
125
 
125
126
 
126
- def load_user_system_prompt(config: dict | None = None) -> str:
127
- """The user's custom system prompt, injected every turn as standing orders.
127
+ def project_system_prompt_path(cwd: Path | None = None) -> Path:
128
+ """A PER-PROJECT custom system-prompt file: <project>/.drydock/system_prompt.md.
129
+ Overrides the global one when present — different projects, different orders."""
130
+ return (cwd or Path.cwd()) / ".drydock" / "system_prompt.md"
128
131
 
129
- Source (first non-empty wins): the file ~/.drydock/system_prompt.md, then the
130
- inline `system_prompt` config key. Wrapped with a clear header and capped at
131
- 8000 chars. Returns '' when neither is set. Never raises.
132
- """
133
- text = ""
132
+
133
+ # The template drydock drops so the file is always THERE and discoverable — the
134
+ # user never has to know the path or filename. It's ALL comments, so it has ZERO
135
+ # effect until the user writes real text below them (load_user_system_prompt
136
+ # strips <!-- --> blocks before use).
137
+ _SYSTEM_PROMPT_TEMPLATE = """\
138
+ <!--
139
+ Drydock custom system prompt.
140
+
141
+ Whatever you write in this file (OUTSIDE these comment blocks) is added to the
142
+ system prompt on EVERY turn, in both the TUI and CLI — your standing instructions.
143
+ Examples you might add below:
144
+ - Always run the test suite after editing code.
145
+ - Prefer the standard library; avoid adding new dependencies.
146
+ - State your plan in one sentence before making changes.
147
+
148
+ SCOPE: this file is THIS PROJECT'S prompt if it lives at <project>/.drydock/
149
+ system_prompt.md; the copy at ~/.drydock/system_prompt.md applies to every
150
+ project. A project file OVERRIDES the global one.
151
+
152
+ This template has NO effect until you add your own text below this comment.
153
+ Comment blocks like this one are ignored. Capped at 8000 characters.
154
+ Restart drydock after editing for changes to take effect.
155
+ -->
156
+ """
157
+
158
+
159
+ def _strip_comments(text: str) -> str:
160
+ """Drop HTML comment blocks so the shipped template is inert until edited."""
161
+ return re.sub(r"<!--.*?-->", "", text, flags=re.DOTALL)
162
+
163
+
164
+ def _read_prompt_file(p: Path) -> str:
165
+ """Comment-stripped, whitespace-trimmed contents of a prompt file, or ''."""
134
166
  try:
135
- p = system_prompt_path()
136
167
  if p.exists():
137
- text = p.read_text()
168
+ return _strip_comments(p.read_text()).strip()
169
+ except OSError:
170
+ pass
171
+ return ""
172
+
173
+
174
+ def _write_template(p: Path) -> None:
175
+ """Write the commented template to p if absent. Never overwrites; never raises."""
176
+ try:
177
+ if not p.exists():
178
+ p.parent.mkdir(parents=True, exist_ok=True)
179
+ p.write_text(_SYSTEM_PROMPT_TEMPLATE, encoding="utf-8")
138
180
  except OSError:
139
- text = ""
181
+ pass
182
+
183
+
184
+ def ensure_system_prompt_file(config_dir: Path | None = None) -> None:
185
+ """Create the GLOBAL <config_dir>/system_prompt.md template if absent (config_dir
186
+ defaults to ~/.drydock), so the feature is discoverable. Never overwrites."""
187
+ _write_template((config_dir / "system_prompt.md") if config_dir is not None else system_prompt_path())
188
+
189
+
190
+ def ensure_project_system_prompt(cwd: Path | None = None) -> None:
191
+ """Create <project>/.drydock/system_prompt.md if absent — but ONLY inside a git
192
+ repo, so drydock never litters `.drydock/` folders in arbitrary directories
193
+ (e.g. $HOME, /tmp). A real project you `git init`'d gets a discoverable,
194
+ gitignorable per-project prompt; a throwaway cwd gets nothing. Never raises."""
195
+ cwd = cwd or Path.cwd()
196
+ try:
197
+ if (cwd / ".git").exists(): # file OR dir — covers worktrees too
198
+ _write_template(project_system_prompt_path(cwd))
199
+ except OSError:
200
+ pass
201
+
202
+
203
+ def load_user_system_prompt(config: dict | None = None, cwd: Path | None = None) -> str:
204
+ """The user's custom system prompt, injected every turn as standing orders.
205
+
206
+ Precedence (first with real content wins): the PER-PROJECT file
207
+ <project>/.drydock/system_prompt.md, then the GLOBAL ~/.drydock/system_prompt.md,
208
+ then the inline `system_prompt` config key. Comment blocks are stripped (so the
209
+ shipped template is inert). Capped at 8000 chars. Returns '' when none has real
210
+ content. Never raises.
211
+ """
212
+ text = _read_prompt_file(project_system_prompt_path(cwd)) or _read_prompt_file(system_prompt_path())
140
213
  if not text and config:
141
- text = str(config.get("system_prompt", "") or "")
142
- text = text.strip()
214
+ text = str(config.get("system_prompt", "") or "").strip()
143
215
  if not text:
144
216
  return ""
145
217
  return (
@@ -289,6 +361,10 @@ def resolve(cli_overrides: dict, path: Path | None = None) -> dict:
289
361
  has an editable v3 config with a visible endpoint.
290
362
  """
291
363
  path = path or default_config_path()
364
+ # Drop the discoverable custom-system-prompt template alongside config.toml so
365
+ # the user never has to know its path/name. Inert (all comments) until edited;
366
+ # never overwrites an existing file.
367
+ ensure_system_prompt_file(path.parent)
292
368
  if not path.exists():
293
369
  save_file(dict(DEFAULTS), path)
294
370
  file_cfg = {}
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drydock-cli
3
- Version: 3.1.9
3
+ Version: 3.1.11
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
@@ -450,15 +450,24 @@ conventions.
450
450
  ### Custom system prompt
451
451
 
452
452
  For standing instructions applied on **every** turn (stronger than the
453
- per-project `AGENTS.md`, which is framed as optional background), set a custom
454
- system prompt via either:
455
-
456
- - a file: `~/.drydock/system_prompt.md` — write your instructions there, or
457
- - a config key in `~/.drydock/config.toml`: `system_prompt = "..."`.
458
-
459
- The file wins if both are set; contents are capped at 8000 chars. It's injected
460
- after drydock's base prompt and before any project `AGENTS.md`, and applies in
461
- both the TUI and CLI.
453
+ per-project `AGENTS.md`, which is framed as optional background), edit a
454
+ `system_prompt.md` file. Drydock creates a commented template for you, so you
455
+ never have to know where it lives — just open it and write your instructions.
456
+ It has no effect until you add text (the template is all comments, ignored).
457
+
458
+ Two scopes, most-specific wins:
459
+
460
+ - **Per-project** — `<project>/.drydock/system_prompt.md`. Auto-created inside
461
+ any **git repo** (so different projects get different standing orders, and
462
+ drydock never litters `.drydock/` folders in non-project directories).
463
+ - **Global** — `~/.drydock/system_prompt.md`. Auto-created on first run; applies
464
+ to every project. A per-project file **overrides** it.
465
+ - **Or the config key** — `system_prompt = "..."` in `~/.drydock/config.toml`
466
+ (lowest precedence).
467
+
468
+ Contents are capped at 8000 chars, injected after drydock's base prompt and
469
+ before any project `AGENTS.md`, and apply in both the TUI and CLI. Restart
470
+ drydock after editing.
462
471
 
463
472
  ## Safety
464
473
 
@@ -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.1.9"
10
+ version = "3.1.11"
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"
@@ -125,3 +125,78 @@ def test_user_system_prompt_capped_at_8000(monkeypatch, tmp_path):
125
125
  def test_system_prompt_key_in_defaults():
126
126
  assert "system_prompt" in cfg.DEFAULTS
127
127
  assert cfg.DEFAULTS["system_prompt"] == ""
128
+
129
+
130
+ def test_resolve_creates_inert_system_prompt_template(tmp_path):
131
+ # resolve() drops the template next to config.toml, and it has NO effect until edited
132
+ cfgpath = tmp_path / "config.toml"
133
+ cfg.resolve({}, cfgpath)
134
+ tmpl = tmp_path / "system_prompt.md"
135
+ assert tmpl.exists(), "template should be auto-created"
136
+ assert "<!--" in tmpl.read_text()
137
+ # inert: all-comments template contributes nothing to the prompt
138
+ assert cfg._strip_comments(tmpl.read_text()).strip() == ""
139
+
140
+
141
+ def test_ensure_system_prompt_never_overwrites(tmp_path):
142
+ f = tmp_path / "system_prompt.md"
143
+ f.write_text("MY REAL PROMPT")
144
+ cfg.ensure_system_prompt_file(tmp_path)
145
+ assert f.read_text() == "MY REAL PROMPT"
146
+
147
+
148
+ def test_load_strips_comments_keeps_real_text(monkeypatch, tmp_path):
149
+ d = tmp_path / ".drydock"; d.mkdir()
150
+ (d / "system_prompt.md").write_text("<!-- ignore me -->\nReal instruction here.")
151
+ monkeypatch.setattr(cfg.Path, "home", staticmethod(lambda: tmp_path))
152
+ out = cfg.load_user_system_prompt({})
153
+ assert "Real instruction here." in out
154
+ assert "ignore me" not in out
155
+
156
+
157
+ def test_load_ignores_comment_only_file(monkeypatch, tmp_path):
158
+ d = tmp_path / ".drydock"; d.mkdir()
159
+ (d / "system_prompt.md").write_text(cfg._SYSTEM_PROMPT_TEMPLATE)
160
+ monkeypatch.setattr(cfg.Path, "home", staticmethod(lambda: tmp_path))
161
+ assert cfg.load_user_system_prompt({}) == ""
162
+
163
+
164
+ # ── per-project system prompt (<project>/.drydock/system_prompt.md) ──
165
+
166
+ def test_project_prompt_overrides_global(monkeypatch, tmp_path):
167
+ home = tmp_path / "home"; (home / ".drydock").mkdir(parents=True)
168
+ (home / ".drydock" / "system_prompt.md").write_text("GLOBAL RULES")
169
+ monkeypatch.setattr(cfg.Path, "home", staticmethod(lambda: home))
170
+ proj = tmp_path / "proj"; (proj / ".drydock").mkdir(parents=True)
171
+ (proj / ".drydock" / "system_prompt.md").write_text("PROJECT RULES")
172
+ out = cfg.load_user_system_prompt({}, cwd=proj)
173
+ assert "PROJECT RULES" in out
174
+ assert "GLOBAL RULES" not in out
175
+
176
+
177
+ def test_global_used_when_no_project_file(monkeypatch, tmp_path):
178
+ home = tmp_path / "home"; (home / ".drydock").mkdir(parents=True)
179
+ (home / ".drydock" / "system_prompt.md").write_text("GLOBAL RULES")
180
+ monkeypatch.setattr(cfg.Path, "home", staticmethod(lambda: home))
181
+ proj = tmp_path / "proj"; proj.mkdir() # no .drydock
182
+ out = cfg.load_user_system_prompt({}, cwd=proj)
183
+ assert "GLOBAL RULES" in out
184
+
185
+
186
+ def test_ensure_project_prompt_only_in_git_repo(tmp_path):
187
+ # git repo → template created
188
+ repo = tmp_path / "repo"; (repo / ".git").mkdir(parents=True)
189
+ cfg.ensure_project_system_prompt(repo)
190
+ assert (repo / ".drydock" / "system_prompt.md").exists()
191
+ # non-git dir → nothing created (no litter)
192
+ plain = tmp_path / "plain"; plain.mkdir()
193
+ cfg.ensure_project_system_prompt(plain)
194
+ assert not (plain / ".drydock").exists()
195
+
196
+
197
+ def test_ensure_project_prompt_never_overwrites(tmp_path):
198
+ repo = tmp_path / "repo"; (repo / ".git").mkdir(parents=True)
199
+ (repo / ".drydock").mkdir()
200
+ (repo / ".drydock" / "system_prompt.md").write_text("MINE")
201
+ cfg.ensure_project_system_prompt(repo)
202
+ assert (repo / ".drydock" / "system_prompt.md").read_text() == "MINE"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes