---
phase: 08-dork-engine
plan: 06
subsystem: cli
tags: [cobra, dorks, cli, sqlite, github, viper]

requires:
  - phase: 08-dork-engine
    provides: "Registry, Runner, GitHubExecutor, custom_dorks storage"
provides:
  - "keyhunter dorks list/info/export/run/add/delete command tree"
  - "Injectable newGitHubExecutor seam for cmd tests"
  - "GITHUB_TOKEN env var bound to viper.dorks.github.token"
affects: [phase-09-osint, phase-10-shodan, phase-17-scheduler]

tech-stack:
  added: []
  patterns:
    - "Package-level executor factory var as test injection seam"
    - "Embedded+custom dork merge via internal dorkRow helper"
    - "text/tabwriter for CLI table output (no lipgloss yet for dorks)"

key-files:
  created:
    - cmd/dorks.go
    - cmd/dorks_test.go
  modified:
    - cmd/stubs.go

key-decisions:
  - "Flag reuse: dorks list and dorks run share dorksFilterSource/Category vars since they never run concurrently — simplifies test reset"
  - "initDorksDB skips encryption key derivation: dorks CLI only touches plaintext custom_dorks, not the encrypted findings table"
  - "dorks run wraps ErrSourceNotImplemented with friendly 'coming Phase 9-16' message instead of bare error"
  - "newGitHubExecutor exposed as package var (not method) so tests can swap without touching dorksRunCmd internals"

patterns-established:
  - "Injectable executor factory: var newFooExecutor = func() Executor { ... } for mocking in cmd tests"
  - "resetDorksFlags helper called from setupDorksTest + between sequential rootCmd.Execute() calls"

requirements-completed: [DORK-03, DORK-04]

duration: ~35min
completed: 2026-04-05
---

# Phase 8 Plan 06: Dorks CLI Command Tree Summary

**Full Cobra command tree (list/info/export/run/add/delete) replacing the Phase-8 stub, wiring Registry + custom_dorks + GitHubExecutor through an injectable factory seam for testability.**

## Performance

- **Duration:** ~35 min
- **Tasks:** 2
- **Files modified:** 3 (1 created in commit 1, 1 created in commit 2, 1 edited)

## Accomplishments

- Replaced cmd/stubs.go dorks stub with a six-verb subcommand tree
- Merged embedded Registry dorks with user-authored custom_dorks in list + export (custom rows prefixed with `*`)
- Added --source/--category filtering to both list and run
- Wired dorks run to dorks.Runner with GitHub live execution, friendly ErrSourceNotImplemented messaging for other sources, and an ErrMissingAuth hint that points users at GITHUB_TOKEN
- Added validation to dorks add: source must be in dorks.ValidSources, category in dorks.ValidCategories, dork id must not collide with an embedded id
- Delete refuses embedded ids with a clear error
- Bound GITHUB_TOKEN -> dorks.github.token via viper.BindEnv in the dorks init()
- 11 end-to-end cmd tests covering every branch, passing against an in-memory SQLite temp DB

## Task Commits

1. **Task 1: list/info/export + stub removal** - `b7934ce` (feat)
2. **Task 2: run/add/delete + tests** - `c281c96` (feat)

## Files Created/Modified

- `cmd/dorks.go` - Full dorks Cobra command tree, flag declarations, initDorksDB helper, newGitHubExecutor injection seam, init() wiring
- `cmd/dorks_test.go` - 11 tests: list filtering, add persistence + marker, invalid source rejection, embedded id collision, embedded delete refusal, custom delete, shodan not-implemented, github missing-token hint, fake-executor run path, yaml export merge, info for both origins
- `cmd/stubs.go` - Removed dorksCmd stub declaration (kept notImplemented helper and other stubs)

## Decisions Made

- **Skip encryption in initDorksDB**: The dorks CLI only reads/writes `custom_dorks`, which stores plaintext metadata. Forcing users through the encryption-key derivation path that `openDBWithKey` uses would add a passphrase prompt for commands that don't need it. initDorksDB deliberately calls `storage.Open` directly.
- **tabwriter over lipgloss**: Phase 8 plan lists lipgloss as a "nice to have" for the dorks table; the current pkg/output table helpers are keyed to findings, not dorks. Used stdlib text/tabwriter to keep Phase 8 self-contained. A future refactor can swap to lipgloss alongside a shared Table type.
- **Package-level executor factory var**: `var newGitHubExecutor = func() dorks.Executor { ... }`. Tests swap it; production code reads it. Avoids forcing the Runner to accept a factory injection or the cmd struct to carry a builder field.
- **Shared filter flag vars between list and run**: `dorksFilterSource`/`dorksFilterCategory` are reused across subcommands. Cobra runs one subcommand per invocation, so sharing is safe; `resetDorksFlags` in the test helper guarantees clean state.

## Deviations from Plan

None significant. Minor adjustments made inline during implementation:

1. **[Inline adjustment] renderDork signature switched to `io.Writer`** after initial draft used a structural interface. Single-line fix before the first build.
2. **[Inline adjustment] sort key used `rows[i].Dork.Source`** not `rows[i].Source` — dorkRow wraps Dork rather than embedding it, so the field path goes through `.Dork`. Caught by `go build` on the first run and fixed immediately.

Both were typo-class fixes inside Task 1, pre-commit. Neither required new infrastructure or plan changes.

## Issues Encountered

None. All 11 tests passed on first run after the two trivial compile fixes above; no retries, no flakes.

## Self-Check: PASSED

Files verified to exist:
- `cmd/dorks.go` — FOUND
- `cmd/dorks_test.go` — FOUND

Commits verified in `git log`:
- `b7934ce` feat(08-06): add dorks list/info/export commands — FOUND
- `c281c96` feat(08-06): add dorks run/add/delete with injectable executor — FOUND

Build + tests:
- `go build ./...` — PASS
- `go test ./cmd/... ./pkg/dorks/... ./pkg/storage/...` — PASS
- `go test ./...` — PASS (entire workspace green)

## User Setup Required

None for tests. For live `keyhunter dorks run --source=github ...`, users must export `GITHUB_TOKEN` with `public_repo` scope, or run `keyhunter config set dorks.github.token <pat>`.

## Next Phase Readiness

- DORK-03 and DORK-04 requirements satisfied
- Plan 08-07 (guardrail/E2E tests) unblocked — the CLI surface it exercises now exists
- Phase 9-16 OSINT sources can plug into the dispatcher by registering executors with `dorks.Runner`; the friendly "coming Phase 9-16" message transparently disappears as each one lands

---
*Phase: 08-dork-engine*
*Plan: 06*
*Completed: 2026-04-05*
