@@ -90,6 +90,8 @@ def _deep_merge(base: dict, override: dict) -> dict:
9090# Helpers for hunt command (confirm + execute actions)
9191# ---------------------------------------------------------------------------
9292
93+ _SEPARATOR = "─" * 70
94+
9395
9496def _list_installed_skills () -> set [str ]:
9597 """Return set of currently installed skill directory names."""
@@ -142,9 +144,9 @@ def _prompt_confirm_actions(
142144 if not actions :
143145 return []
144146
145- print ("\n " + "─" * 70 )
147+ print ("\n " + _SEPARATOR )
146148 print (" READY TO ACT — here's what I'll do:" )
147- print ("─" * 70 )
149+ print (_SEPARATOR )
148150
149151 installs = [a for a in actions if a .action == "install" ]
150152 disables = [a for a in actions if a .action == "disable" ]
@@ -163,14 +165,15 @@ def _prompt_confirm_actions(
163165
164166 print ("\n Note: YELLOW skills are included — review security findings" )
165167 print (" above before confirming. You can remove any from the list." )
166- print ("\n " + "─" * 70 )
168+ print ("\n " + _SEPARATOR )
167169
168170 # --yes flag: auto-confirm all
169171 if auto_yes :
170172 print (" Auto-confirmed (--yes)." )
171173 return actions
172174
173- # Get user input
175+ # Get user input — callers are responsible for checking sys.stdin.isatty()
176+ # before calling this function without auto_yes=True.
174177 response = input (" Proceed? [y/N] or type numbers to skip (e.g. '1,3'): " ).strip ().lower ()
175178
176179 if response in ("y" , "yes" ):
@@ -193,6 +196,45 @@ def _prompt_confirm_actions(
193196 return []
194197
195198
199+ def _print_dry_run (
200+ actions : list [PendingAction ],
201+ project_root : str = "." ,
202+ intent : str | None = None ,
203+ ) -> None :
204+ """Print what would be done without executing anything (dry-run mode).
205+
206+ Args:
207+ actions: Pending install/disable actions that would be executed.
208+ project_root: The project root that was scanned (used to build the hint command).
209+ intent: The --intent value that was passed, if any (included in hint command).
210+ """
211+ print ("\n " + _SEPARATOR )
212+ print (" HUNT COMPLETE — dry-run mode (no changes made)" )
213+ print (_SEPARATOR )
214+
215+ installs = [a for a in actions if a .action == "install" ]
216+ disables = [a for a in actions if a .action == "disable" ]
217+
218+ if installs :
219+ print (f"\n Would install ({ len (installs )} skill(s) → ~/.claude/skills/):" )
220+ for i , act in enumerate (installs , 1 ):
221+ print (f" { i } . { act .skill_name :40} { act .repo_url } " )
222+ if act .reason :
223+ print (f" ({ act .reason } )" )
224+
225+ if disables :
226+ print (f"\n Would disable ({ len (disables )} dangerous skill(s) — soft-disable, reversible):" )
227+ for i , act in enumerate (disables , 1 ):
228+ print (f" { i } . { act .skill_name :40} { act .reason } " )
229+
230+ hint_cmd = f"agent-hunter hunt { project_root } --yes"
231+ if intent :
232+ hint_cmd += f' --intent "{ intent } "'
233+ print ("\n To apply these changes, re-run with --yes:" )
234+ print (f" { hint_cmd } " )
235+ print (_SEPARATOR + "\n " )
236+
237+
196238# ---------------------------------------------------------------------------
197239# Command: hunt
198240# ---------------------------------------------------------------------------
@@ -243,6 +285,13 @@ def cmd_hunt(args: list[str]) -> int:
243285 config = _load_config ()
244286 hunt_cfg = config .get ("hunt" , {})
245287
288+ # --- Privacy notice (emitted before any project files are read) ---
289+ print (
290+ "[agent-hunter] I'll read your project files to identify your tech stack. "
291+ "Only framework/library names will be used — no file paths, variable names, "
292+ "or project-specific code."
293+ )
294+
246295 # --- Extract context ---
247296 print (f"[agent-hunter] Extracting context from: { root_path } " )
248297 if intent :
@@ -353,7 +402,7 @@ def cmd_hunt(args: list[str]) -> int:
353402 print ("[agent-hunter] No new actions to take. All recommendations are already installed." )
354403 return 0
355404
356- # --- Step 8: Confirm actions ---
405+ # --- Step 8: Dry-run by default; execute only with --yes ---
357406 if print_actions :
358407 # SKILL.md mode: print JSON for the LLM to read, then exit without executing.
359408 # SKILL.md presents this list in chat and calls installer directly.
@@ -371,39 +420,25 @@ def cmd_hunt(args: list[str]) -> int:
371420 print (json .dumps (output , indent = 2 ))
372421 return 0
373422
374- # Non-interactive without --yes: dry-run only. Print commands, do not execute.
375- # Satisfies SKILL.md §Core Constraints: "No auto-install. Show the command, user runs it."
376- if not yes and not sys .stdin .isatty ():
377- print ("\n [agent-hunter] Non-interactive mode — pass --yes to execute:" )
378- for a in actions :
379- if a .action == "install" :
380- print (f" git clone { a .repo_url } ~/.claude/skills/{ a .skill_name } " )
381- elif a .action == "disable" :
382- print (f" mv ~/.claude/skills/{ a .skill_name } ~/.claude/skills/_{ a .skill_name } " )
383- elif a .action == "uninstall" :
384- print (f" rm -rf ~/.claude/skills/{ a .skill_name } " )
423+ if not yes :
424+ # Dry-run mode: show what would happen but do not execute.
425+ _print_dry_run (actions , project_root = project_root , intent = intent )
385426 return 0
386427
387- confirmed_actions = _prompt_confirm_actions (actions , auto_yes = yes )
388-
389- if not confirmed_actions :
390- print ("[agent-hunter] No actions confirmed. Exiting." )
391- return 1
392-
393- # --- Step 9: Execute confirmed actions ---
394- print ("\n [agent-hunter] Executing confirmed actions...\n " )
428+ # --- Step 9: Execute confirmed actions (--yes provided) ---
429+ print ("\n [agent-hunter] Executing confirmed actions (--yes)...\n " )
395430 installer = Installer ()
396- results = installer .execute_actions (confirmed_actions )
431+ results = installer .execute_actions (actions )
397432
398433 # Summary
399434 successful = sum (1 for r in results if r .success )
400435 failed = len (results ) - successful
401436
402- print ("\n " + "─" * 70 )
437+ print ("\n " + _SEPARATOR )
403438 print (f" Summary: { successful } /{ len (results )} action(s) succeeded" )
404439 if failed > 0 :
405440 print (f" ⚠️ { failed } action(s) failed — review messages above" )
406- print ("─" * 70 + "\n " )
441+ print (_SEPARATOR + "\n " )
407442
408443 return 0 if failed == 0 else 1
409444
@@ -456,13 +491,17 @@ def cmd_rollback(_args: list[str]) -> int:
456491 agent-hunter v0.1.0 — Repo-aware skill package manager for Claude Code
457492
458493Usage:
459- agent-hunter hunt [project_root] Find top 3 skills/MCPs for your project
460- agent-hunter audit Health-check installed skills
461- agent-hunter rollback Restore registry to last good state
494+ agent-hunter hunt [project_root] [--yes] Find top 3 skills/MCPs for your project
495+ (dry-run by default; --yes to install)
496+ agent-hunter audit Health-check installed skills
497+ agent-hunter rollback Restore registry to last good state
462498
463499Options:
464500 -h, --help Show this message and exit
465501
502+ Flags (hunt):
503+ --yes Execute recommended installs/disables (default: report-only)
504+
466505Environment:
467506 GITHUB_TOKEN GitHub personal access token (optional, enables broader discovery)
468507
0 commit comments