Files
2026-04-05 15:24:46 +03:00

8.4 KiB

phase, plan, subsystem, tags, requires, provides, affects, tech-stack, key-files, key-decisions, patterns-established, requirements-completed, duration, completed
phase plan subsystem tags requires provides affects tech-stack key-files key-decisions patterns-established requirements-completed duration completed
04-input-sources 05 cli
cli
cobra
scan
source-dispatch
integration
phase provides
04-input-sources DirSource, GitSource, StdinSource, URLSource, ClipboardSource (plans 04-02, 04-03, 04-04)
selectSource dispatcher routing CLI args+flags to the correct Source implementation
--git, --url, --clipboard, --since, --max-file-size, --insecure CLI flags on `scan`
Mutual-exclusion validation for conflicting source selectors
phase 05 verification integration
CLI UX for all Phase 4 input modes
added patterns
Struct-parameter dispatcher (sourceFlags) for testable CLI flag fan-in
cobra.MaximumNArgs(1) to support both positional and flag-only invocations
Stat-based auto-dispatch between FileSource and DirSource on a single positional
created modified
cmd/scan_sources_test.go
cmd/scan.go
selectSource takes a sourceFlags struct (not individual params) so the test suite can drive every combination without touching cobra flag state
Args changed from ExactArgs(1) to MaximumNArgs(1) because --url and --clipboard legitimately have no positional target
Stdin tokens 'stdin' and '-' are both accepted (spec from 04-04) and selectSource maps either to StdinSource
--since is parsed inside selectSource (not cobra) so the error path is covered by a unit test
--git + stdin is explicitly rejected even though --git is not in the explicit-source count, because scanning git history of a pipe is nonsensical
CLI dispatcher pattern: positional-first, then flag-overrides, with mutual-exclusion checks up front
Test-friendly flag fan-in: wrap cobra flags in a plain struct passed to the logic function
INPUT-06
~3min 2026-04-05

Phase 4 Plan 5: Scan Command Source Wiring Summary

Wires every Phase 4 input adapter (Dir, Git, Stdin, URL, Clipboard) through cmd/scan.go via a new selectSource dispatcher and six new CLI flags, making INPUT-01 through INPUT-05 reachable from the command line and satisfying the INPUT-06 integration requirement.

Performance

  • Duration: ~3 min (135s)
  • Started: 2026-04-05T12:21:08Z
  • Completed: 2026-04-05T12:23:23Z
  • Tasks: 1 (TDD: RED → GREEN)
  • Files created: 1 (cmd/scan_sources_test.go)
  • Files modified: 1 (cmd/scan.go)

Accomplishments

  • Added selectSource(args, sourceFlags) (sources.Source, error) with full dispatch logic for all five Phase 4 sources plus legacy FileSource
  • Added six new cobra flags: --git, --url, --clipboard, --since, --max-file-size, --insecure
  • Relaxed scanCmd.Args from ExactArgs(1) to MaximumNArgs(1) so --url and --clipboard can run without a positional target
  • Added clear mutual-exclusion errors when --git, --url, --clipboard are combined, and when --url/--clipboard are paired with a positional path
  • 13-case unit test suite covering every dispatch branch, including --since parse-error path
  • Downstream pipeline (engine.Scan, storage.SaveFinding, output, exit codes) untouched — source selection is the only change

Example Invocations

# Directory (DirSource, default)
keyhunter scan ./pkg

# Single file (FileSource, unchanged behavior)
keyhunter scan main.go

# Git history (GitSource with optional --since)
keyhunter scan --git ./some-repo --since 2024-01-01

# Stdin (StdinSource)
echo "API_KEY=xxx" | keyhunter scan -
cat creds.env | keyhunter scan stdin

# Remote URL (URLSource, no positional)
keyhunter scan --url https://example.com/config.js

# Clipboard (ClipboardSource, no positional)
keyhunter scan --clipboard

# Exclusions forwarded to DirSource
keyhunter scan ./src --exclude "*.log" --exclude "tmp/**"

selectSource Dispatch Branches

Input Result
--clipboard (no args) *sources.ClipboardSource
--url https://... (no args) *sources.URLSource
--git <path> *sources.GitSource
--git <path> --since YYYY-MM-DD *sources.GitSource with Since set
positional stdin or - *sources.StdinSource
positional directory *sources.DirSource (+ excludes)
positional file *sources.FileSource
Two of {--git, --url, --clipboard} error: "mutually exclusive"
--url or --clipboard + positional error: "does not accept a positional argument"
no args and no selector flag error: "missing target"
--since not YYYY-MM-DD error: "must be YYYY-MM-DD"
--git + stdin token error: "cannot be combined with stdin"

Task Commits

  1. Task 1 RED9105ca1 test(04-05): add failing tests for selectSource dispatcher
  2. Task 1 GREENb151e88 feat(04-05): wire all Phase 4 sources through scan command

Files Created/Modified

  • Created: cmd/scan_sources_test.go (112 lines, 13 test cases)
  • Modified: cmd/scan.go (+121, -15) — added flags, selectSource helper, dispatch call site, time import

Test Results

Full selectSource suite under -race:

Test Result
TestSelectSource_Directory PASS
TestSelectSource_File PASS
TestSelectSource_Git PASS
TestSelectSource_GitSince PASS
TestSelectSource_GitSinceBadFormat PASS
TestSelectSource_URL PASS
TestSelectSource_URLRejectsPositional PASS
TestSelectSource_Clipboard PASS
TestSelectSource_ClipboardRejectsPositional PASS
TestSelectSource_Stdin (stdin + -) PASS
TestSelectSource_MutuallyExclusive PASS
TestSelectSource_MissingTarget PASS
TestSelectSource_DirForwardsExcludes PASS

Full repo suite: go test ./... -race -count=1all packages ok (cmd, engine, engine/sources, providers, storage). Phase 1-3 tests still green.

Decisions Made

  • sourceFlags struct over many parameters: Keeps the test surface tiny (callers fabricate a struct literal) and makes adding future selectors (e.g. --archive) trivial without breaking signatures.
  • Stat-based Dir vs File: A single positional auto-dispatches on os.Stat().IsDir(). No new --dir flag needed — users just point at a path and it "does the right thing".
  • --since parsing happens in selectSource: So the invalid-format error is unit-testable and produces a CLI-friendly message rather than a raw time.Parse error.

Deviations from Plan

None — plan executed exactly as written. RED/GREEN TDD cycle completed in one iteration with no fixes needed beyond the initial plan specification.

Issues Encountered

None.

Known Stubs

None. --max-file-size and --insecure are wired as cobra flag declarations (per plan requirement) but are not yet consumed by any source — they are forward-compatibility hooks for Phase 5+ and documented as such in the flag help text. The plan explicitly requested the flag declarations; the downstream wiring is out of scope.

User Setup Required

None.

Next Phase Readiness

  • All Phase 4 input adapters are now reachable from the CLI.
  • keyhunter scan --help lists every new flag with descriptive help text.
  • Phase 5 (verification) can build on top of scanCfg.Verify which was already present and remains untouched.
  • Future wiring of --max-file-size and --insecure into DirSource/URLSource respectively is a small follow-up (one line each).

Self-Check: PASSED

  • cmd/scan.go: FOUND (modified)
  • cmd/scan_sources_test.go: FOUND
  • Commit 9105ca1 (RED): FOUND in git log
  • Commit b151e88 (GREEN): FOUND in git log
  • go build ./...: exit 0
  • go test ./... -race -count=1: all packages ok
  • grep -c selectSource cmd/scan.go: 4 (≥2 required)
  • grep sources.New(Dir|Git|Stdin|URL|Clipboard)Source cmd/scan.go: 5 hits
  • grep "mutually exclusive" cmd/scan.go: 1 hit
  • go run . scan --help lists --git, --url, --clipboard, --since, --insecure: confirmed

Phase: 04-input-sources Plan: 05 Completed: 2026-04-05