Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .claude/hooks/rtk-rewrite.sh
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,54 @@ elif echo "$MATCH_CMD" | grep -qE '^mypy([[:space:]]|$)'; then
elif echo "$MATCH_CMD" | grep -qE '^python[[:space:]]+-m[[:space:]]+mypy([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^python -m mypy/rtk mypy/')"

# --- Ruby tooling ---
elif echo "$MATCH_CMD" | grep -qE '^bundle[[:space:]]+(list|outdated|install|update)([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^bundle /rtk bundle /')"
elif echo "$MATCH_CMD" | grep -qE '^bundle[[:space:]]+exec[[:space:]]+rubocop([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^bundle exec rubocop/rtk rubocop/')"
elif echo "$MATCH_CMD" | grep -qE '^rubocop([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^rubocop/rtk rubocop/')"
elif echo "$MATCH_CMD" | grep -qE '^bundle[[:space:]]+exec[[:space:]]+rspec([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^bundle exec rspec/rtk rspec/')"
elif echo "$MATCH_CMD" | grep -qE '^bin/rspec([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's|^bin/rspec|rtk rspec|')"
elif echo "$MATCH_CMD" | grep -qE '^rspec([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^rspec/rtk rspec/')"
elif echo "$MATCH_CMD" | grep -qE '^bundle[[:space:]]+exec[[:space:]]+rails[[:space:]]+spec([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^bundle exec rails spec/rtk rspec/')"
elif echo "$MATCH_CMD" | grep -qE '^(bundle[[:space:]]+exec[[:space:]]+)?rails[[:space:]]+test([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed -E 's/^(bundle exec )?rails test/rtk rails test/')"
elif echo "$MATCH_CMD" | grep -qE '^bin/rails[[:space:]]+test([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's|^bin/rails test|rtk rails test|')"
elif echo "$MATCH_CMD" | grep -qE '^(bundle[[:space:]]+exec[[:space:]]+)?rails[[:space:]]+routes([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed -E 's/^(bundle exec )?rails routes/rtk rails routes/')"
elif echo "$MATCH_CMD" | grep -qE '^(bundle[[:space:]]+exec[[:space:]]+)?rails[[:space:]]+db:migrate:status([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed -E 's/^(bundle exec )?rails db:migrate:status/rtk rails db:migrate:status/')"
elif echo "$MATCH_CMD" | grep -qE '^(bundle[[:space:]]+exec[[:space:]]+)?rails[[:space:]]+db:migrate([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed -E 's/^(bundle exec )?rails db:migrate/rtk rails db:migrate/')"
elif echo "$MATCH_CMD" | grep -qE '^(bundle[[:space:]]+exec[[:space:]]+)?rails[[:space:]]+db:rollback([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed -E 's/^(bundle exec )?rails db:rollback/rtk rails db:rollback/')"
elif echo "$MATCH_CMD" | grep -qE '^(bundle[[:space:]]+exec[[:space:]]+)?rails[[:space:]]+(generate|g)([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed -E 's/^(bundle exec )?rails (generate|g)/rtk rails generate/')"
elif echo "$MATCH_CMD" | grep -qE '^bin/rails[[:space:]]+routes([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's|^bin/rails routes|rtk rails routes|')"
elif echo "$MATCH_CMD" | grep -qE '^bin/rails[[:space:]]+db:migrate:status([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's|^bin/rails db:migrate:status|rtk rails db:migrate:status|')"
elif echo "$MATCH_CMD" | grep -qE '^bin/rails[[:space:]]+db:migrate([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's|^bin/rails db:migrate|rtk rails db:migrate|')"
elif echo "$MATCH_CMD" | grep -qE '^bin/rails[[:space:]]+db:rollback([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's|^bin/rails db:rollback|rtk rails db:rollback|')"
elif echo "$MATCH_CMD" | grep -qE '^bin/rails[[:space:]]+(generate|g)([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed -E 's|^bin/rails (generate\|g)|rtk rails generate|')"
elif echo "$MATCH_CMD" | grep -qE '^rake[[:space:]]+routes([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^rake routes/rtk rails routes/')"
elif echo "$MATCH_CMD" | grep -qE '^rake[[:space:]]+db:migrate:status([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^rake db:migrate:status/rtk rails db:migrate:status/')"
elif echo "$MATCH_CMD" | grep -qE '^rake[[:space:]]+db:migrate([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^rake db:migrate/rtk rails db:migrate/')"
elif echo "$MATCH_CMD" | grep -qE '^rake[[:space:]]+db:rollback([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^rake db:rollback/rtk rails db:rollback/')"

# --- Go tooling ---
elif echo "$MATCH_CMD" | grep -qE '^go[[:space:]]+test([[:space:]]|$)'; then
REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^go test/rtk go test/')"
Expand Down
21 changes: 19 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,11 @@ PYTHON ruff_cmd.rs ruff check/format 80%+ ✓
GO go_cmd.rs go test/build/vet 75-90% ✓
golangci_cmd.rs golangci-lint 85% ✓

RUBY rspec_cmd.rs rspec 60%+ ✓
rubocop_cmd.rs rubocop 60%+ ✓
bundle_cmd.rs bundle list/outdated/install/update 10-30% ✓
rails_cmd.rs rails test/routes/db 40-50%+ ✓

NETWORK wget_cmd.rs wget 85-95% ✓

DEPENDENCIES deps.rs deps 80-90% ✓
Expand All @@ -288,16 +293,17 @@ SHARED utils.rs Helpers N/A ✓
tee.rs Full output recovery N/A ✓
```

**Total: 50 modules** (32 command modules + 18 infrastructure modules)
**Total: 54 modules** (36 command modules + 18 infrastructure modules)

### Module Count Breakdown

- **Command Modules**: 31 (directly exposed to users)
- **Command Modules**: 36 (directly exposed to users)
- **Infrastructure Modules**: 18 (utils, filter, tracking, tee, config, init, gain, etc.)
- **Git Commands**: 7 operations (status, diff, log, add, commit, push, branch/checkout)
- **JS/TS Tooling**: 8 modules (modern frontend/fullstack development)
- **Python Tooling**: 3 modules (ruff, pytest, pip)
- **Go Tooling**: 2 modules (go test/build/vet, golangci-lint)
- **Ruby Tooling**: 4 modules (rspec, rubocop, bundle, rails)

---

Expand Down Expand Up @@ -463,6 +469,17 @@ Commands::Pip { args } Build { args },
└─ pip_cmd.rs ├─ go_cmd.rs (sub-enum router)
└─ golangci_cmd.rs

Commands::Rspec { args } Commands::Rails { command }
Commands::Rubocop { args } │
Commands::Bundle { args } ├─ rails_cmd.rs (sub-enum router)
│ │ ├─ Test { args }
├─ rspec_cmd.rs │ ├─ Routes { args }
├─ rubocop_cmd.rs │ ├─ DbMigrate { args }
└─ bundle_cmd.rs (subcommand router) │ ├─ DbMigrateStatus { args }
│ ├─ DbRollback { args }
│ ├─ Generate { args }
│ └─ Other(Vec<OsString>)

Mirrors: lint, prettier Mirrors: git, cargo
```

Expand Down
16 changes: 15 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,12 @@ rtk gain --history | grep proxy
| pip_cmd.rs | pip/uv package manager | JSON parsing, auto-detect uv (70-85% reduction) |
| go_cmd.rs | Go commands | NDJSON for test, text for build/vet (80-90% reduction) |
| golangci_cmd.rs | golangci-lint | JSON parsing, group by rule (85% reduction) |
| rspec_cmd.rs | RSpec test runner | JSON parsing, failures only (60%+ JSON, 30%+ text fallback) |
| rubocop_cmd.rs | RuboCop linter | JSON parsing, group by cop name (60%+ reduction) |
| bundle_cmd.rs | Bundler package manager | Text parsing for list/outdated/install/update (10-30% reduction) |
| rails_cmd.rs | Rails commands | Sub-enum: test/routes/db:migrate (40-50%+ reduction) |
| tee.rs | Full output recovery | Save raw output to file on failure, print hint for LLM re-read |
| utils.rs | Shared utilities | Package manager detection, common formatting |
| utils.rs | Shared utilities | Package manager detection, ruby_exec, common formatting |
| discover/ | Claude Code history analysis | Scan JSONL sessions, classify commands, report missed savings |

## Performance Constraints
Expand Down Expand Up @@ -392,6 +396,16 @@ pub fn execute_with_filter(cmd: &str, args: &[&str]) -> Result<()> {
- **Architecture**: Standalone Python commands (mirror lint/prettier), Go sub-enum (mirror git/cargo)
- **Patterns**: JSON for structured output (ruff check, golangci-lint, pip), NDJSON streaming (go test), text state machine (pytest), text filters (go build/vet, ruff format)

### Ruby on Rails Support (2026-02-28)
- **Ruby Commands**: 4 modules covering the full Rails development workflow
- `rtk rspec`: RSpec test runner with JSON parsing (`--format json`), text fallback (60%+ JSON, 30%+ text fallback)
- `rtk rubocop`: RuboCop linter with JSON parsing, group by cop name/severity (60%+ reduction)
- `rtk bundle list/outdated/install/update`: Bundler package manager with subcommand dispatch (10-30% reduction)
- `rtk rails test/routes/db:migrate/db:migrate:status/db:rollback/generate`: Rails sub-enum with minitest parser, route grouping, migration summary (40-50%+ reduction)
- **Shared Infrastructure**: `ruby_exec()` in utils.rs auto-detects `bundle exec` when Gemfile exists
- **Architecture**: Standalone commands (rspec, rubocop, bundle) + sub-enum (rails, mirrors go_cmd.rs pattern)
- **Hook Integration**: Rewrites `rspec`, `rubocop`, `bundle list/outdated/install/update`, `rails test/routes/db:migrate/db:migrate:status/db:rollback/generate`, `bundle exec`/`bin/` variants, and `rake routes/db:migrate/db:migrate:status/db:rollback` variants

## Testing Strategy

### TDD Workflow (mandatory)
Expand Down
37 changes: 37 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,12 @@ rtk pytest # Python tests (failures only, 90% reduction)
rtk pip list # Python packages (auto-detect uv, 70% reduction)
rtk go test # Go tests (NDJSON, 90% reduction)
rtk golangci-lint run # Go linting (JSON, 85% reduction)
rtk rspec # RSpec tests (JSON, 60%+ reduction)
rtk rubocop # RuboCop linting (JSON, 60%+ reduction)
rtk bundle list # Bundler packages (compact format)
rtk rails test # Rails minitest (failures only, 50%+ reduction)
rtk rails routes # Routes grouped by controller (50%+ reduction)
rtk rails db:migrate # Migration summary (40%+ reduction)
```

### Data & Analytics
Expand Down Expand Up @@ -284,6 +290,32 @@ rtk go vet # Vet issues (75% reduction)
rtk golangci-lint run # JSON grouped by rule (85% reduction)
```

### Ruby on Rails Stack
```bash
# Testing
rtk rspec # RSpec tests (JSON parser, 60%+ reduction)
rtk rspec spec/models/ # Run specific directory
rtk rails test # Minitest (state machine parser, 50%+ reduction)
rtk rails test test/models/ # Run specific directory

# Linting
rtk rubocop # RuboCop (JSON, group by cop, 60%+ reduction)
rtk rubocop -A # Auto-correct with summary

# Package Management
rtk bundle list # Gem list with counts (10%+ reduction)
rtk bundle outdated # Outdated gems with version transitions (30%+ reduction)
rtk bundle install # Install summary (new/updated gems only)
rtk bundle update # Update summary (same filter as install)

# Rails
rtk rails routes # Routes grouped by controller (50%+ reduction)
rtk rails db:migrate # Migration summary (40%+ reduction)
rtk rails db:migrate:status # Pending migration status
rtk rails db:rollback # Rollback summary
rtk rails generate model User # Generator summary (created files)
```

## Examples

### Standard vs rtk
Expand Down Expand Up @@ -629,6 +661,11 @@ The hook is included in this repository at `.claude/hooks/rtk-rewrite.sh`. To us
| `kubectl get/logs` | `rtk kubectl ...` |
| `curl` | `rtk curl` |
| `pnpm list/ls/outdated` | `rtk pnpm ...` |
| `rspec/bundle exec rspec/bin/rspec` | `rtk rspec ...` |
| `rubocop/bundle exec rubocop` | `rtk rubocop ...` |
| `bundle list/outdated/install/update` | `rtk bundle ...` |
| `rails test/routes/db:migrate/...` | `rtk rails ...` |
| `rake routes/db:migrate` | `rtk rails ...` |

Commands already using `rtk`, heredocs (`<<`), and unrecognized commands pass through unchanged.

Expand Down
32 changes: 31 additions & 1 deletion scripts/test-all.sh
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,37 @@ else
skip "golangci-lint not installed"
fi

# ── 29. Global flags ────────────────────────────────
# ── 29. Ruby (conditional) ──────────────────────────

section "Ruby (conditional)"

if command -v rspec &>/dev/null; then
assert_help "rtk rspec" rtk rspec --help
else
skip "rspec not installed"
fi

if command -v rubocop &>/dev/null; then
assert_help "rtk rubocop" rtk rubocop --help
else
skip "rubocop not installed"
fi

if command -v bundle &>/dev/null; then
assert_help "rtk bundle" rtk bundle --help
else
skip "bundler not installed"
fi

if command -v rails &>/dev/null; then
assert_help "rtk rails" rtk rails --help
assert_help "rtk rails test" rtk rails test -h
assert_help "rtk rails routes" rtk rails routes -h
else
skip "rails not installed"
fi

# ── 30. Global flags ────────────────────────────────

section "Global flags"

Expand Down
Loading