Skip to content
Open
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
2 changes: 1 addition & 1 deletion ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ SHARED utils.rs Helpers N/A ✓
tee.rs Full output recovery N/A ✓
```

**Total: 67 modules** (45 command modules + 22 infrastructure modules)
**Total: 70 modules** (48 command modules + 22 infrastructure modules)

### Module Count Breakdown

Expand Down
146 changes: 146 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,18 @@ rtk gain # Should show token savings stats (NOT "command not found")

If `rtk gain` fails, you have the wrong package installed.

## Changelog Fragments

Every PR needs a fragment in `changelog/fragments/{PR}-{slug}.yml`. Do NOT edit CHANGELOG.md directly.

```bash
pnpm changelog:add # Interactive — creates the fragment file
pnpm changelog:validate <file> # Validate a specific fragment
pnpm changelog:assemble --version x.y.z # Release time only — assembles into CHANGELOG.md
```

Fragment format documented in `changelog/schema.yml`. Required fields: `pr`, `type`, `scope`, `title`.

## Development Commands

> **Note**: If rtk is installed, prefer `rtk <cmd>` over raw commands for token-optimized output.
Expand Down Expand Up @@ -615,3 +627,137 @@ rtk newcmd args
# 7. Document
# Update README.md, CHANGELOG.md, this file
```

<!-- rtk-instructions v2 -->
# RTK (Rust Token Killer) - Token-Optimized Commands

## Golden Rule

**Always prefix commands with `rtk`**. If RTK has a dedicated filter, it uses it. If not, it passes through unchanged. This means RTK is always safe to use.

**Important**: Even in command chains with `&&`, use `rtk`:
```bash
# ❌ Wrong
git add . && git commit -m "msg" && git push

# ✅ Correct
rtk git add . && rtk git commit -m "msg" && rtk git push
```

## RTK Commands by Workflow

### Build & Compile (80-90% savings)
```bash
rtk cargo build # Cargo build output
rtk cargo check # Cargo check output
rtk cargo clippy # Clippy warnings grouped by file (80%)
rtk tsc # TypeScript errors grouped by file/code (83%)
rtk lint # ESLint/Biome violations grouped (84%)
rtk prettier --check # Files needing format only (70%)
rtk next build # Next.js build with route metrics (87%)
```

### Test (90-99% savings)
```bash
rtk cargo test # Cargo test failures only (90%)
rtk vitest run # Vitest failures only (99.5%)
rtk playwright test # Playwright failures only (94%)
rtk test <cmd> # Generic test wrapper - failures only
```

### Git (59-80% savings)
```bash
rtk git status # Compact status
rtk git log # Compact log (works with all git flags)
rtk git diff # Compact diff (80%)
rtk git show # Compact show (80%)
rtk git add # Ultra-compact confirmations (59%)
rtk git commit # Ultra-compact confirmations (59%)
rtk git push # Ultra-compact confirmations
rtk git pull # Ultra-compact confirmations
rtk git branch # Compact branch list
rtk git fetch # Compact fetch
rtk git stash # Compact stash
rtk git worktree # Compact worktree
```

Note: Git passthrough works for ALL subcommands, even those not explicitly listed.

### GitHub (26-87% savings)
```bash
rtk gh pr view <num> # Compact PR view (87%)
rtk gh pr checks # Compact PR checks (79%)
rtk gh run list # Compact workflow runs (82%)
rtk gh issue list # Compact issue list (80%)
rtk gh api # Compact API responses (26%)
```

### JavaScript/TypeScript Tooling (70-90% savings)
```bash
rtk pnpm list # Compact dependency tree (70%)
rtk pnpm outdated # Compact outdated packages (80%)
rtk pnpm install # Compact install output (90%)
rtk npm run <script> # Compact npm script output
rtk npx <cmd> # Compact npx command output
rtk prisma # Prisma without ASCII art (88%)
```

### Files & Search (60-75% savings)
```bash
rtk ls <path> # Tree format, compact (65%)
rtk read <file> # Code reading with filtering (60%)
rtk grep <pattern> # Search grouped by file (75%)
rtk find <pattern> # Find grouped by directory (70%)
```

### Analysis & Debug (70-90% savings)
```bash
rtk err <cmd> # Filter errors only from any command
rtk log <file> # Deduplicated logs with counts
rtk json <file> # JSON structure without values
rtk deps # Dependency overview
rtk env # Environment variables compact
rtk summary <cmd> # Smart summary of command output
rtk diff # Ultra-compact diffs
```

### Infrastructure (85% savings)
```bash
rtk docker ps # Compact container list
rtk docker images # Compact image list
rtk docker logs <c> # Deduplicated logs
rtk kubectl get # Compact resource list
rtk kubectl logs # Deduplicated pod logs
```

### Network (65-70% savings)
```bash
rtk curl <url> # Compact HTTP responses (70%)
rtk wget <url> # Compact download output (65%)
```

### Meta Commands
```bash
rtk gain # View token savings statistics
rtk gain --history # View command history with savings
rtk discover # Analyze Claude Code sessions for missed RTK usage
rtk proxy <cmd> # Run command without filtering (for debugging)
rtk init # Add RTK instructions to CLAUDE.md
rtk init --global # Add RTK to ~/.claude/CLAUDE.md
```

## Token Savings Overview

| Category | Commands | Typical Savings |
|----------|----------|-----------------|
| Tests | vitest, playwright, cargo test | 90-99% |
| Build | next, tsc, lint, prettier | 70-87% |
| Git | status, log, diff, add, commit | 59-80% |
| GitHub | gh pr, gh run, gh issue | 26-87% |
| Package Managers | pnpm, npm, npx | 70-90% |
| Files | ls, read, grep, find | 60-75% |
| Infrastructure | docker, kubectl | 85% |
| Network | curl, wget | 65-70% |

Overall average: **60-90% token reduction** on common development operations.
<!-- /rtk-instructions -->
43 changes: 43 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,49 @@ chore(proxy): remove-deprecated-flags

---

## Changelog Fragments

Each PR needs a changelog fragment so CHANGELOG.md stays conflict-free. Nobody touches CHANGELOG.md directly — the release script assembles it from the fragments.

### Workflow

**Create a fragment when opening a PR:**

```bash
pnpm changelog:add
# Fill in PR number, type, scope, title — a file is created at changelog/fragments/<PR>-<slug>.yml
# Edit the description field, then commit the fragment with the PR
git add changelog/fragments/<PR>-<slug>.yml
git commit -s -m "docs(changelog): add fragment for PR #<PR>"
```

**CI validates the fragment automatically** (`pnpm changelog:validate <file>`). Fix any errors before merging.

**At release time**, the maintainer runs:

```bash
pnpm changelog:assemble --version x.y.z [--dry-run]
```

This inserts the new version block into CHANGELOG.md and archives the fragments.

### Bypass labels

Some PRs don't need a fragment. Add one of these labels on GitHub to skip the check:

| Label | When to use |
|-------|-------------|
| `skip-changelog` | Truly internal change with no user impact |
| `dependencies` | Dependency bumps |
| `release` | The release commit itself |
| `chore: deps` | Automated dependency updates |

### Fragment format

See `changelog/schema.yml` for the full field reference. Required fields: `pr`, `type`, `scope`, `title`. Optional: `description`, `breaking`, `migration`, `scripts`.

---

## Pull Request Process

### Scope Rules
Expand Down
Empty file added changelog/fragments/.gitkeep
Empty file.
21 changes: 21 additions & 0 deletions changelog/schema.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# RTK Changelog Fragment Schema
# One file per PR — never edit manually, use `pnpm changelog:add`
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

schema.yml says "never edit manually", but the documented workflow (and the generated fragment template) requires contributors to edit the description field before committing. Adjust this wording to avoid telling contributors to avoid the exact manual edit they must do.

Suggested change
# One file per PR — never edit manually, use `pnpm changelog:add`
# One file per PR — create via `pnpm changelog:add`, then edit fields as needed

Copilot uses AI. Check for mistakes.

# Required fields
pr: 123 # PR number (must match filename prefix)
type: feat # feat | fix | perf | refactor | security | docs | chore
scope: "hook" # Functional scope (free text)
title: "Short title < 80 chars" # Will appear in CHANGELOG.md

# Optional fields
description: |
User-facing impact in 1-2 sentences (Markdown).
breaking: false # true → appears in Breaking Changes section
migration: false # true → shows ⚠️ Migration DB warning
scripts: [] # Post-deploy commands (array of strings)
# Example:
# scripts:
# - "node scripts/migrate-data.js --execute"

# Naming rule: {PR_NUMBER}-{slug-kebab-case}.yml
# The PR number in the filename MUST match the `pr` field.
100 changes: 100 additions & 0 deletions changelog/scripts/add.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
#!/usr/bin/env tsx
/**
* changelog:add — Interactive CLI to create a changelog fragment
* Usage: pnpm changelog:add
*/
import * as readline from "readline";
import * as fs from "fs";
import * as path from "path";

const TYPES = ["feat", "fix", "perf", "refactor", "security", "docs", "chore"] as const;
type FragmentType = (typeof TYPES)[number];

const FRAGMENTS_DIR = path.resolve(process.cwd(), "changelog/fragments");

function slugify(text: string, maxLen = 40): string {
return text
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.toLowerCase()
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "")
.slice(0, maxLen);
}

function ask(rl: readline.Interface, question: string): Promise<string> {
return new Promise((resolve) => rl.question(question, resolve));
}

async function main() {
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });

console.log("\n📝 Create a new changelog fragment\n");

const prStr = await ask(rl, "PR number: ");
const pr = parseInt(prStr.trim(), 10);
if (isNaN(pr) || pr <= 0) {
console.error("❌ Invalid PR number");
process.exit(1);
}

console.log(`Types: ${TYPES.join(" | ")}`);
const typeInput = (await ask(rl, "Type: ")).trim() as FragmentType;
if (!TYPES.includes(typeInput)) {
console.error(`❌ Invalid type. Must be one of: ${TYPES.join(", ")}`);
process.exit(1);
}

const scope = (await ask(rl, "Scope (e.g. hook, git, permissions): ")).trim();
if (!scope) {
console.error("❌ Scope is required");
process.exit(1);
}

const title = (await ask(rl, "Title (< 80 chars): ")).trim();
if (!title) {
console.error("❌ Title is required");
process.exit(1);
}
if (title.length > 80) {
console.error(`❌ Title too long: ${title.length} chars (max 80)`);
process.exit(1);
}

rl.close();

const slug = slugify(title);
const filename = `${pr}-${slug}.yml`;
const filepath = path.join(FRAGMENTS_DIR, filename);

if (fs.existsSync(filepath)) {
console.error(`❌ File already exists: changelog/fragments/${filename}`);
process.exit(1);
}

const content = [
`pr: ${pr}`,
`type: ${typeInput}`,
`scope: "${scope}"`,
`title: "${title}"`,
`description: |`,
` TODO: describe the user-facing impact in 1-2 sentences.`,
`breaking: false`,
`migration: false`,
`scripts: []`,
].join("\n") + "\n";
Comment on lines +75 to +85
Copy link

Copilot AI Mar 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fragment generator writes scope and title into YAML using double quotes without escaping. If a user enters a quote, backslash, newline, or : it can produce invalid YAML, causing validate/assemble to fail later. Prefer generating YAML via the yaml library (stringify) or at least properly escape/quote scalar values.

Copilot uses AI. Check for mistakes.

fs.mkdirSync(FRAGMENTS_DIR, { recursive: true });
fs.writeFileSync(filepath, content, "utf8");

console.log(`\n✅ Created: changelog/fragments/${filename}`);
console.log("\nNext steps:");
console.log(` 1. Edit the description in changelog/fragments/${filename}`);
console.log(` 2. git add changelog/fragments/${filename}`);
console.log(` 3. git commit -s -m "docs(changelog): add fragment for PR #${pr}"`);
}

main().catch((e) => {
console.error(e);
process.exit(1);
});
Loading
Loading