Skip to content

Latest commit

 

History

History
194 lines (145 loc) · 6.21 KB

File metadata and controls

194 lines (145 loc) · 6.21 KB

Rekal CLI

Soul

Before making any design decision, read SOUL.md. It defines the two problems Rekal exists to solve and the seven beliefs that guide every choice. If a decision conflicts with the soul, the decision is wrong.

When working on a problem, consult Rekal's own memories first:

rekal "<describe the problem>"

The prior context for what you're working on may already exist.

Standing Rules

  • Keep this file up to date. Any change to commands, packages, files, or behavior must be reflected here. Update --help text when command behavior changes. Update docs/spec/command/ when a command spec changes. Stale docs are worse than no docs.
  • Consult SOUL.md before design decisions. Consult rekal before starting work on a problem.

Architecture

Single binary. Everything embedded — CLI, database engine, embedding model, compression dictionary.

  • CLI: Cobra (github.com/spf13/cobra)
  • Storage: DuckDB via github.com/marcboeker/go-duckdb (database/sql interface)
  • Compression: zstd via github.com/klauspost/compress with preset dictionary
  • IDs: ULID via github.com/oklog/ulid/v2
  • Embeddings: LSA (gonum) + Nomic (platform-specific builds)
  • Build: mise, go modules
  • Lint: golangci-lint v2 (2.8.0)
  • Language: Go 1.25.6

Two databases in .rekal/:

  • data.db — immutable source of truth. Append-only. Pushed to git.
  • index.db — local derived index. Rebuilt from data.db. Never pushed.

This split is a direct consequence of the soul: thin on the wire, rich on the machine.

Key Directories

Commands (cmd/rekal/)

  • main.go: Entry point

Core CLI (cmd/rekal/cli/)

  • root.go: Root command (recall is the default) + command registration
  • recall.go: Hybrid search — BM25 + LSA + Nomic ranking
  • checkpoint.go: Capture session after commit
  • push.go: Push data to remote branch
  • sync.go: Sync team context
  • sync_remote.go: Remote sync implementation
  • export.go: Encode checkpoints to wire format for push
  • import.go: Decode wire format during sync
  • init.go: Bootstrap Rekal in a git repo
  • clean.go: Remove Rekal setup — completely, no residue
  • index_cmd.go: Rebuild index DB from data DB
  • log.go: Show recent checkpoints
  • query.go: Raw SQL access
  • version.go: Version constant (set via ldflags)
  • errors.go: SilentError pattern for clean error output
  • preconditions.go: Shared checks (git repo, init done, index exists)

Packages (cmd/rekal/cli/)

  • codec/: Binary wire format — frame encoding/decoding, body, dictionary, preset zstd dictionary
  • session/: Claude Code .jsonl parsing — extract turns, tool calls, deduplicate
  • db/: DuckDB backend — open, close, schema, insert helpers, index population
  • lsa/: Latent Semantic Analysis embeddings
  • nomic/: Nomic-embed-text deep semantic embeddings (platform build tags)
  • skill/: Rekal Skill definition for Claude Code integration
  • versioncheck/: Auto-update notification
  • integration_test/: Integration tests (//go:build integration)

Docs (docs/)

  • DEVELOPMENT.md: Dev process, testing, CI/CD
  • git-transportation.md: Git transport layer design
  • db/: Database schema and design
  • spec/preconditions.md: Shared checks for all commands
  • spec/command/: One file per command — checkpoint, clean, index, init, log, push, query, recall, sync

Development

Running Tests

mise run test              # Unit tests only
mise run test:integration  # Integration tests only
mise run test:ci           # All tests (unit + integration) with race detection

Linting and Formatting

mise run fmt           # Format code (gofmt)
mise run lint          # Lint check (gofmt + golangci-lint)

Building

mise run build         # Build binary with version from git tag
mise run build:all     # Build for all platforms (snapshot)

Before Every Commit

mise run fmt && mise run lint && mise run test:ci

Test Organization

Unit tests (_test.go next to source, same package). Always use t.Parallel().

Integration tests (integration_test/, //go:build integration). Use TestEnv pattern — isolated temp git repos per test. Tests public API only. Cannot be parallelized (uses os.Chdir).

Code Patterns

Error Handling — SilentError

  • root.go sets SilenceErrors: true globally
  • Commands return NewSilentError(err) when they've already printed a user-friendly message
  • For normal errors, return the error directly
if err := EnsureGitRoot(); err != nil {
    cmd.SilenceUsage = true
    fmt.Fprintln(cmd.ErrOrStderr(), err)
    return NewSilentError(err)
}

Shared Preconditions

All commands except init and clean must call both:

  1. EnsureGitRoot() — verifies inside a git repo
  2. EnsureInitDone(gitRoot) — verifies .rekal/ exists

Command Structure

func newFooCmd() *cobra.Command {
    cmd := &cobra.Command{
        Use:   "foo",
        Short: "Short description",
        RunE: func(cmd *cobra.Command, args []string) error {
            cmd.SilenceUsage = true
            gitRoot, err := EnsureGitRoot()
            if err != nil {
                fmt.Fprintln(cmd.ErrOrStderr(), err)
                return NewSilentError(err)
            }
            if err := EnsureInitDone(gitRoot); err != nil {
                fmt.Fprintln(cmd.ErrOrStderr(), err)
                return NewSilentError(err)
            }
            // command logic
            return nil
        },
    }
    return cmd
}

CLI Output Voice

From the soul: short sentences, plain words, say what happened, say what to do, stop.

rekal: not a git repository (run this inside a project)
rekal: captured 3 sessions, 847 turns
rekal: no sessions match "JWT expiry" in src/auth/

No exclamation marks. No emoji. No "oops."

Go Code Style

  • Write lint-compliant Go code on the first attempt
  • Follow standard Go idioms: proper error handling, no unused variables/imports
  • Handle all errors explicitly
  • Reference .golangci.yaml for enabled linters (govet, errcheck, ineffassign, staticcheck, unused)

Release Process

  1. Ensure main is green (CI, Lint, License Check)
  2. Tag and push:
    git tag v0.x.y
    git push origin v0.x.y
  3. Release workflow validates then publishes via GoReleaser