Skip to content

Refactor: break up the cli god-package + code-smell cleanups#6

Merged
rekal-dev merged 6 commits into
mainfrom
claude/capabilities-scope-8uqk1g
Jul 5, 2026
Merged

Refactor: break up the cli god-package + code-smell cleanups#6
rekal-dev merged 6 commits into
mainfrom
claude/capabilities-scope-8uqk1g

Conversation

@rekal-dev

Copy link
Copy Markdown
Owner

Structural refactors and code-smell cleanups on top of v0.3.0. Behavior-preserving throughout — no logic changes, recall output byte-identical. CI + Lint green on the tip (c9288ec).

Package structure

cmd/rekal/cli was a 25-file god-package. Broke it into focused packages with a clean dependency DAG:

  • cli/search — the recall ranking engine (hybrid BM25+LSA+Nomic, signal weighting, conversation grouping, snippet extraction, LSA projection cache). recall.go shrank from ~1,050 lines to ~60 lines of command orchestration.
  • cli/transport — wire export/import + orphan-branch commit (was export.go/import.go/sync_remote.go).
  • cli/gitx — thin git plumbing (rev-parse, show, hash-object, config, rekal/<email> branch name). The shared lower layer that lets transport exist without a commands ↔ transport import cycle.
  • cli/ids — ULID generation.

cli now holds only command definitions + orchestration.

Code-smell cleanups

  • Centralized the git-repo + init-done precondition block that was copy-pasted across 7 command RunEs into RequireInitializedRepo (spec already called for "one central way").
  • Deduped SQL null-coalescing (db.NullIfEmpty/NullIfZero) and IN-clause placeholder building (sqlInClause).
  • Deduped the ULID generator closure (×3) into ids.NewULIDFunc() and the "rekal.body"/"dict.bin" literals (×7) into codec.BodyFilename/codec.DictFilename.

Docs

  • Updated CLAUDE.md to match the new package layout and fill prior gaps (scrub/, LSA cache).

🤖 Generated with Claude Code

https://claude.ai/code/session_01TBMA7dNfAQn86m59P64dsS


Generated by Claude Code

claude added 6 commits July 5, 2026 07:50
Every command except init and clean opened with the same ~10-line
block: EnsureGitRoot, print + wrap on failure, EnsureInitDone, print +
wrap on failure, plus SilenceUsage. It was copy-pasted across seven
RunE functions, and docs/spec/preconditions.md explicitly calls for
"one central way to check init is done; same message everywhere."

Extract RequireInitializedRepo(cmd) — it runs both checks, prints the
user-facing message, and returns a SilentError so callers collapse to
`gitRoot, err := RequireInitializedRepo(cmd); if err != nil { return err }`.
Net -24 lines and the shared behavior now lives in one place.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBMA7dNfAQn86m59P64dsS
Two small duplications had crept in:

- recall.go built the "$1,$2,...,$N" placeholder list and args slice
  inline in three batch loaders. Extract sqlInClause(sessionIDs).
- db.nullIfEmpty/nullIfZero and sync_remote's sqlNullIfEmpty/
  sqlNullIfZero were identical NULL-coalescing helpers in two packages.
  Export them from db (NullIfEmpty/NullIfZero) — the storage layer is
  where they belong — and have the raw INSERT in sync_remote reuse
  them, dropping the copies.

Behavior-preserving cleanup only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBMA7dNfAQn86m59P64dsS
The Packages list omitted scrub/ (secret redaction + path anonymization
that runs on every checkpoint before any DB write — security-relevant
and worth surfacing), and the Core CLI list omitted lsa_cache.go (the
LSA query-projection cache). Add both, and note the new
RequireInitializedRepo precondition helper.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBMA7dNfAQn86m59P64dsS
cmd/rekal/cli was a 25-file god-package; recall.go alone was ~1,050
lines mixing the cobra command with the whole hybrid-search engine.
Move the engine into a new cli/search package — hybrid BM25+LSA+Nomic
ranking, signal weighting, conversation grouping, snippet extraction,
the facet batch loaders, and the LSA query-projection cache
(lsa_cache.go -> search/projection.go).

recall.go shrinks to ~60 lines of command orchestration: open, migrate,
and auto-rebuild the index DB, call search.Run, marshal JSON. The
package exposes a small surface — Run, Filters, Result, SessionDetail,
Output, StoreLSAProjection — with everything else unexported. The three
affected callers (root, index, sync) switch to the search.* names, and
the recall/batch/projection tests move into the package. Output shape
and JSON tags are unchanged, so recall output is byte-identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBMA7dNfAQn86m59P64dsS
…ckage

Completes the cli package breakup. The command files kept doing three
unrelated jobs: cobra command wiring, the wire-format export/import
pipeline, and low-level git plumbing. Pull the latter two out:

- cli/transport: encode checkpoints to the orphan-branch wire format
  and decode them back (export.go + import.go + sync_remote.go, plus
  ensureOrphanBranch/commitWireFormat). Sits above codec, db, and gitx.
- cli/gitx: the thin git-plumbing helpers (rev-parse, show, hash-object,
  config, rekal/<email> branch name) that both the command layer and
  transport need. Putting them in a shared lower package is what lets
  transport exist without a commands<->transport import cycle.

cli now holds only command definitions and their orchestration. No
behavior change — pure code movement; the exported transport/gitx
surface is called from push/sync/init and the export regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBMA7dNfAQn86m59P64dsS
Two small smells surfaced while reviewing the split packages:

- The ULID generator closure (a shared math/rand entropy source plus a
  ulid.MustNew wrapper) was copy-pasted three times — once in
  checkpoint and twice in transport. Extract cli/ids.NewULIDFunc(),
  which returns the same reuse-one-generator closure the batch loops
  expect, and drop the now-unused math/rand and oklog/ulid imports from
  both callers.
- The wire filenames "rekal.body" and "dict.bin" were spelled out in
  seven places across init and transport. Give them names where the
  format is defined: codec.BodyFilename / codec.DictFilename.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TBMA7dNfAQn86m59P64dsS
@rekal-dev rekal-dev merged commit d0b4b9c into main Jul 5, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants