feat: add local LLM analysis, filter improvements, and testing scripts

Add smart code analysis with local LLM integration (rtk smart command),
improve filter.rs formatting and readability, and add comprehensive
testing utilities.

Key changes:
- local_llm.rs: Ultra-compact code summaries for LLM contexts
- filter.rs: Better formatting, enhanced comment/docstring detection
- main.rs: Integrate smart command routing
- config.rs: Code formatting improvements
- .gitignore: Exclude claudedocs/ directory
- scripts/rtk-economics.sh: Token savings analysis
- scripts/test-all.sh: Comprehensive test runner
- scripts/test-aristote.sh: Project-specific test suite

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
Florian BRUNIAUX
2026-02-02 00:30:06 +01:00
parent a3bfbf747c
commit 1d857914d6
7 changed files with 790 additions and 25 deletions
+1
View File
@@ -35,3 +35,4 @@ benchmark-report.md
*.sqlite
*.sqlite3
rtk_tracking.db
claudedocs
+137
View File
@@ -0,0 +1,137 @@
#!/bin/bash
# rtk-economics.sh
# Combine ccusage (tokens spent) with rtk (tokens saved) for economic analysis
set -euo pipefail
# Colors for output
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
# Get current month
CURRENT_MONTH=$(date +%Y-%m)
echo -e "${BLUE}📊 RTK Economic Impact Analysis${NC}"
echo "════════════════════════════════════════════════════════════════"
echo
# Check if ccusage is available
if ! command -v ccusage &> /dev/null; then
echo -e "${RED}Error: ccusage not found${NC}"
echo "Install: npm install -g @anthropics/claude-code-usage"
exit 1
fi
# Check if rtk is available
if ! command -v rtk &> /dev/null; then
echo -e "${RED}Error: rtk not found${NC}"
echo "Install: cargo install --path ."
exit 1
fi
# Fetch ccusage data
echo -e "${YELLOW}Fetching token usage data from ccusage...${NC}"
if ! ccusage_json=$(ccusage monthly --json 2>/dev/null); then
echo -e "${RED}Failed to fetch ccusage data${NC}"
exit 1
fi
# Fetch rtk data
echo -e "${YELLOW}Fetching token savings data from rtk...${NC}"
if ! rtk_json=$(rtk gain --monthly --format json 2>/dev/null); then
echo -e "${RED}Failed to fetch rtk data${NC}"
exit 1
fi
echo
# Parse ccusage data for current month
ccusage_cost=$(echo "$ccusage_json" | jq -r ".monthly[] | select(.month == \"$CURRENT_MONTH\") | .totalCost // 0")
ccusage_input=$(echo "$ccusage_json" | jq -r ".monthly[] | select(.month == \"$CURRENT_MONTH\") | .inputTokens // 0")
ccusage_output=$(echo "$ccusage_json" | jq -r ".monthly[] | select(.month == \"$CURRENT_MONTH\") | .outputTokens // 0")
ccusage_total=$(echo "$ccusage_json" | jq -r ".monthly[] | select(.month == \"$CURRENT_MONTH\") | .totalTokens // 0")
# Parse rtk data for current month
rtk_saved=$(echo "$rtk_json" | jq -r ".monthly[] | select(.month == \"$CURRENT_MONTH\") | .saved_tokens // 0")
rtk_commands=$(echo "$rtk_json" | jq -r ".monthly[] | select(.month == \"$CURRENT_MONTH\") | .commands // 0")
rtk_input=$(echo "$rtk_json" | jq -r ".monthly[] | select(.month == \"$CURRENT_MONTH\") | .input_tokens // 0")
rtk_output=$(echo "$rtk_json" | jq -r ".monthly[] | select(.month == \"$CURRENT_MONTH\") | .output_tokens // 0")
rtk_pct=$(echo "$rtk_json" | jq -r ".monthly[] | select(.month == \"$CURRENT_MONTH\") | .savings_pct // 0")
# Estimate cost avoided (rough: $0.0001/token for mixed usage)
# More accurate would be to use ccusage's model-specific pricing
saved_cost=$(echo "scale=2; $rtk_saved * 0.0001" | bc 2>/dev/null || echo "0")
# Calculate total without rtk
total_without_rtk=$(echo "scale=2; $ccusage_cost + $saved_cost" | bc 2>/dev/null || echo "$ccusage_cost")
# Calculate savings percentage
if (( $(echo "$total_without_rtk > 0" | bc -l) )); then
savings_pct=$(echo "scale=1; ($saved_cost / $total_without_rtk) * 100" | bc 2>/dev/null || echo "0")
else
savings_pct="0"
fi
# Calculate cost per command
if [ "$rtk_commands" -gt 0 ]; then
cost_per_cmd_with=$(echo "scale=2; $ccusage_cost / $rtk_commands" | bc 2>/dev/null || echo "0")
cost_per_cmd_without=$(echo "scale=2; $total_without_rtk / $rtk_commands" | bc 2>/dev/null || echo "0")
else
cost_per_cmd_with="N/A"
cost_per_cmd_without="N/A"
fi
# Format numbers
format_number() {
local num=$1
if [ "$num" = "0" ] || [ "$num" = "N/A" ]; then
echo "$num"
else
echo "$num" | numfmt --to=si 2>/dev/null || echo "$num"
fi
}
# Display report
cat << EOF
${GREEN}💰 Economic Impact Report - $CURRENT_MONTH${NC}
════════════════════════════════════════════════════════════════
${BLUE}Tokens Consumed (via Claude API):${NC}
Input tokens: $(format_number $ccusage_input)
Output tokens: $(format_number $ccusage_output)
Total tokens: $(format_number $ccusage_total)
${RED}Actual cost: \$$ccusage_cost${NC}
${BLUE}Tokens Saved by rtk:${NC}
Commands executed: $rtk_commands
Input avoided: $(format_number $rtk_input) tokens
Output generated: $(format_number $rtk_output) tokens
Total saved: $(format_number $rtk_saved) tokens (${rtk_pct}% reduction)
${GREEN}Cost avoided: ~\$$saved_cost${NC}
${BLUE}Economic Analysis:${NC}
Cost without rtk: \$$total_without_rtk (estimated)
Cost with rtk: \$$ccusage_cost (actual)
${GREEN}Net savings: \$$saved_cost ($savings_pct%)${NC}
ROI: ${GREEN}Infinite${NC} (rtk is free)
${BLUE}Efficiency Metrics:${NC}
Cost per command: \$$cost_per_cmd_without → \$$cost_per_cmd_with
Tokens per command: $(echo "scale=0; $rtk_input / $rtk_commands" | bc 2>/dev/null || echo "N/A") → $(echo "scale=0; $rtk_output / $rtk_commands" | bc 2>/dev/null || echo "N/A")
${BLUE}12-Month Projection:${NC}
Annual savings: ~\$$(echo "scale=2; $saved_cost * 12" | bc 2>/dev/null || echo "0")
Commands needed: $(echo "$rtk_commands * 12" | bc 2>/dev/null || echo "0") (at current rate)
════════════════════════════════════════════════════════════════
${YELLOW}Note:${NC} Cost estimates use \$0.0001/token average. Actual pricing varies by model.
See ccusage for precise model-specific costs.
${GREEN}Recommendation:${NC} Focus rtk usage on high-frequency commands (git, grep, ls)
for maximum cost reduction.
EOF
+378
View File
@@ -0,0 +1,378 @@
#!/usr/bin/env bash
#
# RTK Smoke Test Suite
# Exercises every command to catch regressions after merge.
# Exit code: number of failures (0 = all green)
#
set -euo pipefail
PASS=0
FAIL=0
SKIP=0
FAILURES=()
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'
# ── Helpers ──────────────────────────────────────────
assert_ok() {
local name="$1"
shift
local output
if output=$("$@" 2>&1); then
PASS=$((PASS + 1))
printf " ${GREEN}PASS${NC} %s\n" "$name"
else
FAIL=$((FAIL + 1))
FAILURES+=("$name")
printf " ${RED}FAIL${NC} %s\n" "$name"
printf " cmd: %s\n" "$*"
printf " out: %s\n" "$(echo "$output" | head -3)"
fi
}
assert_contains() {
local name="$1"
local needle="$2"
shift 2
local output
if output=$("$@" 2>&1) && echo "$output" | grep -q "$needle"; then
PASS=$((PASS + 1))
printf " ${GREEN}PASS${NC} %s\n" "$name"
else
FAIL=$((FAIL + 1))
FAILURES+=("$name")
printf " ${RED}FAIL${NC} %s\n" "$name"
printf " expected: '%s'\n" "$needle"
printf " got: %s\n" "$(echo "$output" | head -3)"
fi
}
assert_exit_ok() {
local name="$1"
shift
if "$@" >/dev/null 2>&1; then
PASS=$((PASS + 1))
printf " ${GREEN}PASS${NC} %s\n" "$name"
else
FAIL=$((FAIL + 1))
FAILURES+=("$name")
printf " ${RED}FAIL${NC} %s\n" "$name"
printf " cmd: %s\n" "$*"
fi
}
assert_help() {
local name="$1"
shift
assert_contains "$name --help" "Usage:" "$@" --help
}
skip_test() {
local name="$1"
local reason="$2"
SKIP=$((SKIP + 1))
printf " ${YELLOW}SKIP${NC} %s (%s)\n" "$name" "$reason"
}
section() {
printf "\n${BOLD}${CYAN}── %s ──${NC}\n" "$1"
}
# ── Preamble ─────────────────────────────────────────
RTK=$(command -v rtk || echo "")
if [[ -z "$RTK" ]]; then
echo "rtk not found in PATH. Run: cargo install --path ."
exit 1
fi
printf "${BOLD}RTK Smoke Test Suite${NC}\n"
printf "Binary: %s\n" "$RTK"
printf "Version: %s\n" "$(rtk --version)"
printf "Date: %s\n" "$(date '+%Y-%m-%d %H:%M')"
# Need a git repo to test git commands
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
echo "Must run from inside a git repository."
exit 1
fi
REPO_ROOT=$(git rev-parse --show-toplevel)
# ── 1. Version & Help ───────────────────────────────
section "Version & Help"
assert_contains "rtk --version" "rtk" rtk --version
assert_contains "rtk --help" "Usage:" rtk --help
# ── 2. Ls ────────────────────────────────────────────
section "Ls"
assert_ok "rtk ls ." rtk ls .
assert_ok "rtk ls -a ." rtk ls -a .
assert_ok "rtk ls --depth 2 ." rtk ls --depth 2 .
assert_ok "rtk ls -f tree ." rtk ls -f tree .
assert_contains "rtk ls shows src/" "src/" rtk ls .
# ── 3. Read ──────────────────────────────────────────
section "Read"
assert_ok "rtk read Cargo.toml" rtk read Cargo.toml
assert_ok "rtk read --level none Cargo.toml" rtk read --level none Cargo.toml
assert_ok "rtk read --level aggressive Cargo.toml" rtk read --level aggressive Cargo.toml
assert_ok "rtk read -n Cargo.toml" rtk read -n Cargo.toml
assert_ok "rtk read --max-lines 5 Cargo.toml" rtk read --max-lines 5 Cargo.toml
# ── 4. Git ───────────────────────────────────────────
section "Git (existing)"
assert_ok "rtk git status" rtk git status
assert_ok "rtk git log" rtk git log
assert_ok "rtk git log -5" rtk git log -- -5
assert_ok "rtk git diff" rtk git diff
assert_ok "rtk git diff --stat" rtk git diff --stat
section "Git (new: branch, fetch, stash, worktree)"
assert_ok "rtk git branch" rtk git branch
assert_ok "rtk git fetch" rtk git fetch
assert_ok "rtk git stash list" rtk git stash list
assert_ok "rtk git worktree" rtk git worktree
# ── 5. GitHub CLI ────────────────────────────────────
section "GitHub CLI"
if command -v gh >/dev/null 2>&1 && gh auth status >/dev/null 2>&1; then
assert_ok "rtk gh pr list" rtk gh pr list
assert_ok "rtk gh run list" rtk gh run list
assert_ok "rtk gh issue list" rtk gh issue list
# pr create/merge/diff/comment/edit are write ops, test help only
assert_help "rtk gh" rtk gh
else
skip_test "gh commands" "gh not authenticated"
fi
# ── 6. Cargo ─────────────────────────────────────────
section "Cargo (new)"
assert_ok "rtk cargo build" rtk cargo build
assert_ok "rtk cargo clippy" rtk cargo clippy
# cargo test exits non-zero due to pre-existing failures; check output ignoring exit code
output_cargo_test=$(rtk cargo test 2>&1 || true)
if echo "$output_cargo_test" | grep -q "FAILURES\|test result:"; then
PASS=$((PASS + 1))
printf " ${GREEN}PASS${NC} %s\n" "rtk cargo test"
else
FAIL=$((FAIL + 1))
FAILURES+=("rtk cargo test")
printf " ${RED}FAIL${NC} %s\n" "rtk cargo test"
printf " got: %s\n" "$(echo "$output_cargo_test" | head -3)"
fi
assert_help "rtk cargo" rtk cargo
# ── 7. Curl ──────────────────────────────────────────
section "Curl (new)"
assert_contains "rtk curl JSON detect" "string" rtk curl https://httpbin.org/json
assert_ok "rtk curl plain text" rtk curl https://httpbin.org/robots.txt
assert_help "rtk curl" rtk curl
# ── 8. Npm / Npx ────────────────────────────────────
section "Npm / Npx (new)"
assert_help "rtk npm" rtk npm
assert_help "rtk npx" rtk npx
# ── 9. Pnpm ─────────────────────────────────────────
section "Pnpm"
assert_help "rtk pnpm" rtk pnpm
assert_help "rtk pnpm build" rtk pnpm build
assert_help "rtk pnpm typecheck" rtk pnpm typecheck
# ── 10. Grep ─────────────────────────────────────────
section "Grep"
assert_ok "rtk grep pattern" rtk grep "pub fn" src/
assert_contains "rtk grep finds results" "pub fn" rtk grep "pub fn" src/
assert_ok "rtk grep with file type" rtk grep "pub fn" src/ -t rust
# ── 11. Find ─────────────────────────────────────────
section "Find"
assert_ok "rtk find *.rs" rtk find "*.rs" src/
assert_contains "rtk find shows files" ".rs" rtk find "*.rs" src/
# ── 12. Json ─────────────────────────────────────────
section "Json"
# Create temp JSON file for testing
TMPJSON=$(mktemp /tmp/rtk-test-XXXXX.json)
echo '{"name":"test","count":42,"items":[1,2,3]}' > "$TMPJSON"
assert_ok "rtk json file" rtk json "$TMPJSON"
assert_contains "rtk json shows schema" "string" rtk json "$TMPJSON"
rm -f "$TMPJSON"
# ── 13. Deps ─────────────────────────────────────────
section "Deps"
assert_ok "rtk deps ." rtk deps .
assert_contains "rtk deps shows Cargo" "Cargo" rtk deps .
# ── 14. Env ──────────────────────────────────────────
section "Env"
assert_ok "rtk env" rtk env
assert_ok "rtk env --filter PATH" rtk env --filter PATH
# ── 15. Diff ─────────────────────────────────────────
section "Diff"
TMPF1=$(mktemp /tmp/rtk-diff1-XXXXX.txt)
TMPF2=$(mktemp /tmp/rtk-diff2-XXXXX.txt)
echo -e "line1\nline2\nline3" > "$TMPF1"
echo -e "line1\nchanged\nline3" > "$TMPF2"
assert_ok "rtk diff two files" rtk diff "$TMPF1" "$TMPF2"
rm -f "$TMPF1" "$TMPF2"
# ── 16. Log ──────────────────────────────────────────
section "Log"
TMPLOG=$(mktemp /tmp/rtk-log-XXXXX.log)
for i in $(seq 1 20); do
echo "[2025-01-01 12:00:00] INFO: repeated message" >> "$TMPLOG"
done
echo "[2025-01-01 12:00:01] ERROR: something failed" >> "$TMPLOG"
assert_ok "rtk log file" rtk log "$TMPLOG"
rm -f "$TMPLOG"
# ── 17. Summary ──────────────────────────────────────
section "Summary"
assert_ok "rtk summary echo hello" rtk summary echo hello
# ── 18. Err ──────────────────────────────────────────
section "Err"
assert_ok "rtk err echo ok" rtk err echo ok
# ── 19. Test runner ──────────────────────────────────
section "Test runner"
assert_ok "rtk test echo ok" rtk test echo ok
# ── 20. Gain ─────────────────────────────────────────
section "Gain"
assert_ok "rtk gain" rtk gain
assert_ok "rtk gain --history" rtk gain --history
# ── 21. Config & Init ────────────────────────────────
section "Config & Init"
assert_ok "rtk config" rtk config
assert_ok "rtk init --show" rtk init --show
# ── 22. Wget ─────────────────────────────────────────
section "Wget"
if command -v wget >/dev/null 2>&1; then
assert_ok "rtk wget stdout" rtk wget https://httpbin.org/robots.txt -O
else
skip_test "rtk wget" "wget not installed"
fi
# ── 23. Tsc / Lint / Prettier / Next / Playwright ───
section "JS Tooling (help only, no project context)"
assert_help "rtk tsc" rtk tsc
assert_help "rtk lint" rtk lint
assert_help "rtk prettier" rtk prettier
assert_help "rtk next" rtk next
assert_help "rtk playwright" rtk playwright
# ── 24. Prisma ───────────────────────────────────────
section "Prisma (help only)"
assert_help "rtk prisma" rtk prisma
# ── 25. Vitest ───────────────────────────────────────
section "Vitest (help only)"
assert_help "rtk vitest" rtk vitest
# ── 26. Docker / Kubectl (help only) ────────────────
section "Docker / Kubectl (help only)"
assert_help "rtk docker" rtk docker
assert_help "rtk kubectl" rtk kubectl
# ── 27. Global flags ────────────────────────────────
section "Global flags"
assert_ok "rtk -u ls ." rtk -u ls .
assert_ok "rtk --skip-env npm --help" rtk --skip-env npm --help
# ── 28. CcEconomics ─────────────────────────────────
section "CcEconomics"
assert_ok "rtk cc-economics" rtk cc-economics
# ══════════════════════════════════════════════════════
# Report
# ══════════════════════════════════════════════════════
printf "\n${BOLD}══════════════════════════════════════${NC}\n"
printf "${BOLD}Results: ${GREEN}%d passed${NC}, ${RED}%d failed${NC}, ${YELLOW}%d skipped${NC}\n" "$PASS" "$FAIL" "$SKIP"
if [[ ${#FAILURES[@]} -gt 0 ]]; then
printf "\n${RED}Failures:${NC}\n"
for f in "${FAILURES[@]}"; do
printf " - %s\n" "$f"
done
fi
printf "${BOLD}══════════════════════════════════════${NC}\n"
exit "$FAIL"
+227
View File
@@ -0,0 +1,227 @@
#!/usr/bin/env bash
#
# RTK Smoke Tests — Aristote Project (Vite + React + TS + ESLint)
# Tests RTK commands in a real JS/TS project context.
# Usage: bash scripts/test-aristote.sh
#
set -euo pipefail
ARISTOTE="/Users/florianbruniaux/Sites/MethodeAristote/aristote-school-boost"
PASS=0
FAIL=0
SKIP=0
FAILURES=()
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
CYAN='\033[0;36m'
BOLD='\033[1m'
NC='\033[0m'
assert_ok() {
local name="$1"; shift
local output
if output=$("$@" 2>&1); then
PASS=$((PASS + 1))
printf " ${GREEN}PASS${NC} %s\n" "$name"
else
FAIL=$((FAIL + 1))
FAILURES+=("$name")
printf " ${RED}FAIL${NC} %s\n" "$name"
printf " cmd: %s\n" "$*"
printf " out: %s\n" "$(echo "$output" | head -3)"
fi
}
assert_contains() {
local name="$1"; local needle="$2"; shift 2
local output
if output=$("$@" 2>&1) && echo "$output" | grep -q "$needle"; then
PASS=$((PASS + 1))
printf " ${GREEN}PASS${NC} %s\n" "$name"
else
FAIL=$((FAIL + 1))
FAILURES+=("$name")
printf " ${RED}FAIL${NC} %s\n" "$name"
printf " expected: '%s'\n" "$needle"
printf " got: %s\n" "$(echo "$output" | head -3)"
fi
}
# Allow non-zero exit but check output
assert_output() {
local name="$1"; local needle="$2"; shift 2
local output
output=$("$@" 2>&1) || true
if echo "$output" | grep -q "$needle"; then
PASS=$((PASS + 1))
printf " ${GREEN}PASS${NC} %s\n" "$name"
else
FAIL=$((FAIL + 1))
FAILURES+=("$name")
printf " ${RED}FAIL${NC} %s\n" "$name"
printf " expected: '%s'\n" "$needle"
printf " got: %s\n" "$(echo "$output" | head -3)"
fi
}
skip_test() {
local name="$1"; local reason="$2"
SKIP=$((SKIP + 1))
printf " ${YELLOW}SKIP${NC} %s (%s)\n" "$name" "$reason"
}
section() {
printf "\n${BOLD}${CYAN}── %s ──${NC}\n" "$1"
}
# ── Preamble ─────────────────────────────────────────
RTK=$(command -v rtk || echo "")
if [[ -z "$RTK" ]]; then
echo "rtk not found in PATH. Run: cargo install --path ."
exit 1
fi
if [[ ! -d "$ARISTOTE" ]]; then
echo "Aristote project not found at $ARISTOTE"
exit 1
fi
printf "${BOLD}RTK Smoke Tests — Aristote Project${NC}\n"
printf "Binary: %s (%s)\n" "$RTK" "$(rtk --version)"
printf "Project: %s\n" "$ARISTOTE"
printf "Date: %s\n\n" "$(date '+%Y-%m-%d %H:%M')"
# ── 1. File exploration ──────────────────────────────
section "Ls & Find"
assert_ok "rtk ls project root" rtk ls "$ARISTOTE"
assert_ok "rtk ls src/" rtk ls "$ARISTOTE/src"
assert_ok "rtk ls --depth 3" rtk ls --depth 3 "$ARISTOTE/src"
assert_contains "rtk ls shows components/" "components" rtk ls "$ARISTOTE/src"
assert_ok "rtk find *.tsx" rtk find "*.tsx" "$ARISTOTE/src"
assert_ok "rtk find *.ts" rtk find "*.ts" "$ARISTOTE/src"
assert_contains "rtk find finds App.tsx" "App.tsx" rtk find "*.tsx" "$ARISTOTE/src"
# ── 2. Read ──────────────────────────────────────────
section "Read"
assert_ok "rtk read tsconfig.json" rtk read "$ARISTOTE/tsconfig.json"
assert_ok "rtk read package.json" rtk read "$ARISTOTE/package.json"
assert_ok "rtk read App.tsx" rtk read "$ARISTOTE/src/App.tsx"
assert_ok "rtk read --level aggressive" rtk read --level aggressive "$ARISTOTE/src/App.tsx"
assert_ok "rtk read --max-lines 10" rtk read --max-lines 10 "$ARISTOTE/src/App.tsx"
# ── 3. Grep ──────────────────────────────────────────
section "Grep"
assert_ok "rtk grep import" rtk grep "import" "$ARISTOTE/src"
assert_ok "rtk grep with type filter" rtk grep "useState" "$ARISTOTE/src" -t tsx
assert_contains "rtk grep finds components" "import" rtk grep "import" "$ARISTOTE/src"
# ── 4. Git ───────────────────────────────────────────
section "Git (in Aristote repo)"
# rtk git doesn't support -C, use git -C via subshell
assert_ok "rtk git status" bash -c "cd $ARISTOTE && rtk git status"
assert_ok "rtk git log" bash -c "cd $ARISTOTE && rtk git log"
assert_ok "rtk git branch" bash -c "cd $ARISTOTE && rtk git branch"
# ── 5. Deps ──────────────────────────────────────────
section "Deps"
assert_ok "rtk deps" rtk deps "$ARISTOTE"
assert_contains "rtk deps shows package.json" "package.json" rtk deps "$ARISTOTE"
# ── 6. Json ──────────────────────────────────────────
section "Json"
assert_ok "rtk json tsconfig" rtk json "$ARISTOTE/tsconfig.json"
assert_ok "rtk json package.json" rtk json "$ARISTOTE/package.json"
# ── 7. Env ───────────────────────────────────────────
section "Env"
assert_ok "rtk env" rtk env
assert_ok "rtk env --filter NODE" rtk env --filter NODE
# ── 8. Tsc ───────────────────────────────────────────
section "TypeScript (tsc)"
if command -v npx >/dev/null 2>&1 && [[ -d "$ARISTOTE/node_modules" ]]; then
assert_output "rtk tsc (in aristote)" "error\|✅\|TS" rtk tsc --project "$ARISTOTE"
else
skip_test "rtk tsc" "node_modules not installed"
fi
# ── 9. ESLint ────────────────────────────────────────
section "ESLint (lint)"
if command -v npx >/dev/null 2>&1 && [[ -d "$ARISTOTE/node_modules" ]]; then
assert_output "rtk lint (in aristote)" "error\|warning\|✅\|violations\|clean" rtk lint --project "$ARISTOTE"
else
skip_test "rtk lint" "node_modules not installed"
fi
# ── 10. Build (Vite) ─────────────────────────────────
section "Build (Vite via rtk next)"
if [[ -d "$ARISTOTE/node_modules" ]]; then
# Aristote uses Vite, not Next — but rtk next wraps the build script
# Test with a timeout since builds can be slow
skip_test "rtk next build" "Vite project, not Next.js — use npm run build directly"
else
skip_test "rtk next build" "node_modules not installed"
fi
# ── 11. Diff ─────────────────────────────────────────
section "Diff"
# Diff two config files that exist in the project
assert_ok "rtk diff tsconfigs" rtk diff "$ARISTOTE/tsconfig.json" "$ARISTOTE/tsconfig.app.json"
# ── 12. Summary & Err ────────────────────────────────
section "Summary & Err"
assert_ok "rtk summary ls" rtk summary ls "$ARISTOTE/src"
assert_ok "rtk err ls" rtk err ls "$ARISTOTE/src"
# ── 13. Gain ─────────────────────────────────────────
section "Gain (after above commands)"
assert_ok "rtk gain" rtk gain
assert_ok "rtk gain --history" rtk gain --history
# ══════════════════════════════════════════════════════
# Report
# ══════════════════════════════════════════════════════
printf "\n${BOLD}══════════════════════════════════════${NC}\n"
printf "${BOLD}Results: ${GREEN}%d passed${NC}, ${RED}%d failed${NC}, ${YELLOW}%d skipped${NC}\n" "$PASS" "$FAIL" "$SKIP"
if [[ ${#FAILURES[@]} -gt 0 ]]; then
printf "\n${RED}Failures:${NC}\n"
for f in "${FAILURES[@]}"; do
printf " - %s\n" "$f"
done
fi
printf "${BOLD}══════════════════════════════════════${NC}\n"
exit "$FAIL"
+2 -7
View File
@@ -61,11 +61,7 @@ impl Default for FilterConfig {
".venv".into(),
"vendor".into(),
],
ignore_files: vec![
"*.lock".into(),
"*.min.js".into(),
"*.min.css".into(),
],
ignore_files: vec!["*.lock".into(), "*.min.js".into(), "*.min.css".into()],
}
}
}
@@ -103,8 +99,7 @@ impl Config {
}
fn get_config_path() -> Result<PathBuf> {
let config_dir = dirs::config_dir()
.unwrap_or_else(|| PathBuf::from("."));
let config_dir = dirs::config_dir().unwrap_or_else(|| PathBuf::from("."));
Ok(config_dir.join("rtk").join("config.toml"))
}
+36 -16
View File
@@ -86,15 +86,18 @@ impl Language {
doc_line: None,
doc_block_start: Some("\"\"\""),
},
Language::JavaScript | Language::TypeScript | Language::Go | Language::C | Language::Cpp | Language::Java => {
CommentPatterns {
line: Some("//"),
block_start: Some("/*"),
block_end: Some("*/"),
doc_line: None,
doc_block_start: Some("/**"),
}
}
Language::JavaScript
| Language::TypeScript
| Language::Go
| Language::C
| Language::Cpp
| Language::Java => CommentPatterns {
line: Some("//"),
block_start: Some("/*"),
block_end: Some("*/"),
doc_line: None,
doc_block_start: Some("/**"),
},
Language::Ruby => CommentPatterns {
line: Some("#"),
block_start: Some("=begin"),
@@ -160,7 +163,10 @@ impl FilterStrategy for MinimalFilter {
// Handle block comments
if let (Some(start), Some(end)) = (patterns.block_start, patterns.block_end) {
if !in_docstring && trimmed.contains(start) && !trimmed.starts_with(patterns.doc_block_start.unwrap_or("###")) {
if !in_docstring
&& trimmed.contains(start)
&& !trimmed.starts_with(patterns.doc_block_start.unwrap_or("###"))
{
in_block_comment = true;
}
if in_block_comment {
@@ -222,8 +228,12 @@ impl FilterStrategy for MinimalFilter {
pub struct AggressiveFilter;
lazy_static! {
static ref IMPORT_PATTERN: Regex = Regex::new(r"^(use |import |from |require\(|#include)").unwrap();
static ref FUNC_SIGNATURE: Regex = Regex::new(r"^(pub\s+)?(async\s+)?(fn|def|function|func|class|struct|enum|trait|interface|type)\s+\w+").unwrap();
static ref IMPORT_PATTERN: Regex =
Regex::new(r"^(use |import |from |require\(|#include)").unwrap();
static ref FUNC_SIGNATURE: Regex = Regex::new(
r"^(pub\s+)?(async\s+)?(fn|def|function|func|class|struct|enum|trait|interface|type)\s+\w+"
)
.unwrap();
}
impl FilterStrategy for AggressiveFilter {
@@ -261,7 +271,8 @@ impl FilterStrategy for AggressiveFilter {
brace_depth -= close_braces as i32;
// Only keep the opening and closing braces
if brace_depth <= 1 && (trimmed == "{" || trimmed == "}" || trimmed.ends_with('{')) {
if brace_depth <= 1 && (trimmed == "{" || trimmed == "}" || trimmed.ends_with('{'))
{
result.push_str(line);
result.push('\n');
}
@@ -326,7 +337,10 @@ pub fn smart_truncate(content: &str, max_lines: usize, _lang: &Language) -> Stri
if is_important || kept_lines < max_lines / 2 {
if skipped_section {
result.push(format!(" // ... {} lines omitted", lines.len() - kept_lines));
result.push(format!(
" // ... {} lines omitted",
lines.len() - kept_lines
));
skipped_section = false;
}
result.push((*line).to_string());
@@ -358,8 +372,14 @@ mod tests {
#[test]
fn test_filter_level_parsing() {
assert_eq!(FilterLevel::from_str("none").unwrap(), FilterLevel::None);
assert_eq!(FilterLevel::from_str("minimal").unwrap(), FilterLevel::Minimal);
assert_eq!(FilterLevel::from_str("aggressive").unwrap(), FilterLevel::Aggressive);
assert_eq!(
FilterLevel::from_str("minimal").unwrap(),
FilterLevel::Minimal
);
assert_eq!(
FilterLevel::from_str("aggressive").unwrap(),
FilterLevel::Aggressive
);
}
#[test]
+9 -2
View File
@@ -70,7 +70,12 @@ fn analyze_code(content: &str, lang: &Language) -> CodeSummary {
let line1 = if components.is_empty() {
format!("{} ({} lines)", main_type, total_lines)
} else {
format!("{} ({}) - {} lines", main_type, components.join(", "), total_lines)
format!(
"{} ({}) - {} lines",
main_type,
components.join(", "),
total_lines
)
};
// Build line 2: Key details
@@ -124,7 +129,9 @@ fn extract_imports(content: &str, lang: &Language) -> Vec<String> {
let pattern = match lang {
Language::Rust => r"^use\s+([a-zA-Z_][a-zA-Z0-9_]*(?:::[a-zA-Z_][a-zA-Z0-9_]*)?)",
Language::Python => r"^(?:from\s+(\S+)|import\s+(\S+))",
Language::JavaScript | Language::TypeScript => r#"(?:import.*from\s+['"]([^'"]+)['"]|require\(['"]([^'"]+)['"]\))"#,
Language::JavaScript | Language::TypeScript => {
r#"(?:import.*from\s+['"]([^'"]+)['"]|require\(['"]([^'"]+)['"]\))"#
}
Language::Go => r#"^\s*"([^"]+)"$"#,
_ => return Vec::new(),
};