11from __future__ import annotations
22
33from pathlib import Path
4- from typing import Optional
4+ from typing import Annotated
55
66import typer
77
88from .commands import cmd_baseline , cmd_init , cmd_scan , cmd_stats
99from .reporting import Format
1010from .rules_cmd import list_rules , show_rule
1111
12- app = typer .Typer (add_completion = False , help = "SecretScout: defensive secret scanner for repos and folders." )
12+ app = typer .Typer (
13+ add_completion = False ,
14+ help = "SecretScout: defensive secret scanner for repos and folders." ,
15+ )
16+
17+ # ---- Reusable annotations (Ruff B008-safe) ----
18+
19+ PathArg = Annotated [
20+ Path ,
21+ typer .Argument (exists = True , file_okay = False , dir_okay = True ),
22+ ]
23+
24+ FormatOpt = Annotated [
25+ Format ,
26+ typer .Option ("--format" , help = "table|minimal|json|sarif|html" ),
27+ ]
28+
29+ OutputOpt = Annotated [
30+ Path | None ,
31+ typer .Option ("--output" , "-o" , help = "Write report to file (else stdout)." ),
32+ ]
33+
34+ FailOnOpt = Annotated [
35+ str ,
36+ typer .Option ("--fail-on" , help = "Exit 1 if findings severity >= this." ),
37+ ]
38+
39+ BaselineOpt = Annotated [
40+ Path | None ,
41+ typer .Option ("--baseline" , help = "Baseline JSON file to ignore known findings." ),
42+ ]
43+
44+ StagedOpt = Annotated [
45+ bool ,
46+ typer .Option ("--staged" , help = "Scan staged changes (git index)." ),
47+ ]
48+
49+ TrackedOpt = Annotated [
50+ bool ,
51+ typer .Option ("--tracked" , help = "Scan only git tracked files." ),
52+ ]
53+
54+ AllFilesOpt = Annotated [
55+ bool ,
56+ typer .Option ("--all" , help = "Scan all files under PATH (ignores git)." ),
57+ ]
58+
59+ ExcludeOpt = Annotated [
60+ list [str ] | None ,
61+ typer .Option ("--exclude" , help = "Extra exclude glob patterns (repeatable)." ),
62+ ]
63+
64+ NoCacheOpt = Annotated [
65+ bool ,
66+ typer .Option ("--no-cache" , help = "Disable cache." ),
67+ ]
68+
69+ MaxFindingsOpt = Annotated [
70+ int | None ,
71+ typer .Option ("--max-findings" , help = "Limit output findings." ),
72+ ]
73+
74+ # ---- Commands ----
1375
1476
1577@app .command ()
1678def scan (
17- path : Path = typer . Argument ( Path ("." ), exists = True , file_okay = False , dir_okay = True ),
18- format : Format = typer . Option ( "table" , "--format" , help = "table|minimal|json|sarif|html" ) ,
19- output : Optional [ Path ] = typer . Option ( None , "--output" , "-o" , help = "Write report to file (else stdout)." ) ,
20- fail_on : str = typer . Option ( "high" , "--fail-on" , help = "Exit 1 if findings severity >= this." ) ,
21- baseline : Optional [ Path ] = typer . Option ( None , "--baseline" , help = "Baseline JSON file to ignore known findings." ) ,
22- staged : bool = typer . Option ( False , "--staged" , help = "Scan staged changes (git index)." ) ,
23- tracked : bool = typer . Option ( False , "--tracked" , help = "Scan only git tracked files." ) ,
24- all_files : bool = typer . Option ( False , "--all" , help = "Scan all files under PATH (ignores git)." ) ,
25- exclude : list [ str ] = typer . Option ( None , "--exclude" , help = "Extra exclude glob patterns (repeatable)." ) ,
26- no_cache : bool = typer . Option ( False , "--no-cache" , help = "Disable cache." ) ,
27- max_findings : Optional [ int ] = typer . Option ( None , "--max-findings" , help = "Limit output findings." ) ,
79+ path : PathArg = Path ("." ),
80+ format : FormatOpt = "table" ,
81+ output : OutputOpt = None ,
82+ fail_on : FailOnOpt = "high" ,
83+ baseline : BaselineOpt = None ,
84+ staged : StagedOpt = False ,
85+ tracked : TrackedOpt = False ,
86+ all_files : AllFilesOpt = False ,
87+ exclude : ExcludeOpt = None ,
88+ no_cache : NoCacheOpt = False ,
89+ max_findings : MaxFindingsOpt = None ,
2890) -> None :
91+ """Scan a path for potential secrets."""
2992 code = cmd_scan (
3093 path = path ,
3194 fmt = format ,
@@ -43,44 +106,68 @@ def scan(
43106
44107
45108@app .command ()
46- def init (path : Path = typer .Argument (Path ("." ), exists = True , file_okay = False , dir_okay = True )) -> None :
109+ def init (path : PathArg = Path ("." )) -> None :
110+ """Generate default config and ignore files."""
47111 raise typer .Exit (code = cmd_init (path ))
48112
49113
50114@app .command ()
51- def fix (path : Path = typer .Argument (Path ("." ), exists = True , file_okay = False , dir_okay = True )) -> None :
115+ def fix (path : PathArg = Path ("." )) -> None :
116+ """Alias for init (kept for convenience)."""
52117 raise typer .Exit (code = cmd_init (path ))
53118
54119
55120@app .command ()
56121def baseline (
57- path : Path = typer .Argument (Path ("." ), exists = True , file_okay = False , dir_okay = True ),
58- output : Path = typer .Option (Path (".secretscout.baseline.json" ), "--output" , "-o" , help = "Baseline output file." ),
59- tracked : bool = typer .Option (True , "--tracked/--all" , help = "By default, baseline git-tracked files." ),
122+ path : PathArg = Path ("." ),
123+ output : Annotated [
124+ Path ,
125+ typer .Option ("--output" , "-o" , help = "Baseline output file." ),
126+ ] = Path (".secretscout.baseline.json" ),
127+ tracked : Annotated [
128+ bool ,
129+ typer .Option ("--tracked/--all" , help = "By default, baseline git-tracked files." ),
130+ ] = True ,
60131) -> None :
132+ """Create a baseline file to ignore known findings."""
61133 raise typer .Exit (code = cmd_baseline (path , output , tracked ))
62134
63135
64136@app .command ()
65137def stats (
66- path : Path = typer . Argument ( Path ("." ), exists = True , file_okay = False , dir_okay = True ),
67- staged : bool = typer . Option ( False , "--staged" ) ,
68- tracked : bool = typer . Option ( False , "--tracked" ) ,
69- all_files : bool = typer . Option ( False , "--all" ) ,
70- baseline : Optional [ Path ] = typer . Option ( None , "--baseline" ) ,
138+ path : PathArg = Path ("." ),
139+ staged : StagedOpt = False ,
140+ tracked : TrackedOpt = False ,
141+ all_files : AllFilesOpt = False ,
142+ baseline : BaselineOpt = None ,
71143) -> None :
72- raise typer .Exit (code = cmd_stats (path , staged = staged , tracked = tracked , all_files = all_files , baseline = baseline ))
144+ """Show a summary of findings."""
145+ raise typer .Exit (
146+ code = cmd_stats (
147+ path ,
148+ staged = staged ,
149+ tracked = tracked ,
150+ all_files = all_files ,
151+ baseline = baseline ,
152+ )
153+ )
73154
74155
156+ # ---- Rules subcommands ----
157+
75158rules_app = typer .Typer (help = "Manage and inspect rules." )
76159app .add_typer (rules_app , name = "rules" )
77160
78161
79162@rules_app .command ("list" )
80163def rules_list () -> None :
164+ """List available rules."""
81165 list_rules ()
82166
83167
84168@rules_app .command ("show" )
85- def rules_show (rule_id : str ) -> None :
169+ def rules_show (
170+ rule_id : Annotated [str , typer .Argument (help = "Rule id (e.g. github-token)." )],
171+ ) -> None :
172+ """Show details for a single rule."""
86173 show_rule (rule_id )
0 commit comments