src/core/README.md
See also docs/contributing/TECHNICAL.md for the full architecture overview
Domain-agnostic building blocks with no knowledge of any specific command, hook, or agent. If a module references "git", "cargo", "claude", or any external tool by name, it does not belong here. Core is a leaf in the dependency graph — it is consumed by all other components but imports from none of them.
Owns: configuration loading, token tracking persistence, TOML filter engine, tee output recovery, display formatting, telemetry, and shared utilities.
Does not own: command-specific filtering logic (that's cmds/), hook lifecycle management (that's src/hooks/), or analytics dashboards (that's analytics/).
Core infrastructure shared by all RTK command modules. Every filter, tracker, and command handler depends on these modules. No inward dependencies — leaf in the dependency graph (no circular imports possible).
The TOML DSL applies 8 stages in order:
unless field prevents swallowing errors)Three-tier filter lookup (first match wins):
.rtk/filters.toml (project-local, requires rtk trust)~/.config/rtk/filters.toml (user-global)build.rs at compile timeCREATE TABLE commands (
id INTEGER PRIMARY KEY,
timestamp TEXT, -- UTC ISO8601
original_cmd TEXT, -- "ls -la"
rtk_cmd TEXT, -- "rtk ls"
project_path TEXT, -- cwd (for project-scoped stats)
input_tokens INTEGER, -- estimated from raw output
output_tokens INTEGER, -- estimated from filtered output
saved_tokens INTEGER, -- input - output
savings_pct REAL, -- (saved / input) * 100
exec_time_ms INTEGER -- elapsed milliseconds
);
CREATE TABLE parse_failures (
id INTEGER PRIMARY KEY,
timestamp TEXT,
raw_command TEXT,
error_message TEXT,
fallback_succeeded INTEGER -- 1=yes, 0=no
);
Project-scoped queries use GLOB patterns (not LIKE) to avoid _/% wildcard issues in paths.
[tracking]
enabled = true
history_days = 90
database_path = "/custom/path/to/tracking.db" # Optional
[display]
colors = true
emoji = true
max_width = 120
[tee]
enabled = true
mode = "failures" # failures | always | never
max_files = 20
max_file_size = 1048576
directory = "/custom/tee/dir"
[telemetry]
enabled = true
[hooks]
exclude_commands = ["curl", "playwright"] # Never auto-rewrite these
[limits]
grep_max_results = 200
grep_max_per_file = 25
status_max_files = 15
status_max_untracked = 10
passthrough_max_chars = 2000
Key functions available to all command modules:
| Function | Purpose |
|---|---|
truncate(s, max) | Truncate string with ... suffix |
strip_ansi(text) | Remove ANSI escape/color codes |
resolved_command(name) | Find command in PATH, returns Command |
tool_exists(name) | Check if a CLI tool is available |
detect_package_manager() | Detect pnpm/yarn/npm from lockfiles |
package_manager_exec(tool) | Build Command using detected package manager |
ruby_exec(tool) | Auto-detect bundle exec when Gemfile exists |
count_tokens(text) | Estimate tokens: ceil(chars / 4.0) |
Core provides infrastructure that cmds/ and other components consume. These contracts define expected usage.
TimedExecution)Consumers must call timer.track() on all code paths — success, failure, and fallback. Calling std::process::exit() before track() loses metrics. The raw string passed to track() should include both stdout and stderr to produce accurate savings percentages.
tee_and_hint)Consumers that parse structured output (JSON, NDJSON, state machines) should call tee::tee_and_hint() to save raw output for LLM recovery on failure. Tee must be called before std::process::exit().
For truncation recovery on success (e.g., list truncated at 20 items), use tee::force_tee_hint() which bypasses the tee mode check and writes regardless of exit code. This ensures LLMs always have a [full output: ...] recovery path instead of burning tokens working around missing data.
Place new infrastructure code here if it meets all of these criteria: (1) it has no dependencies on command modules or hooks, (2) it is used by two or more other modules, and (3) it provides a general-purpose utility rather than command-specific logic. Follow the existing pattern of lazy-initialized resources (lazy_static! for regex, on-demand config loading) to preserve the <10ms startup target. Add #[cfg(test)] mod tests with unit tests in the same file.