# rtk-registry

[RTK (Rust Token Killer)](https://github.com/TokenFleet-AI/rtk) 项目的命令重写引擎库。

将 shell 命令重写为 RTK 优化格式，实现 token 高效的 LLM 过滤。支持 200+ 命令，涵盖 git、cargo、npm/pnpm、go、python、docker 等生态。

## 快速开始

在 `Cargo.toml` 中添加依赖：

```toml
[dependencies]
rtk-registry = "1.0"
```

## 使用示例

### 重写命令

```rust
use rtk_registry::rewrite_command;

let rewritten = rewrite_command("git status", &[], &[]);
assert_eq!(rewritten, Some("rtk git status".to_string()));

// 不支持的命令返回 None
let result = rewrite_command("htop", &[], &[]);
assert_eq!(result, None);

// 已有 rtk 前缀的命令原样返回
let result = rewrite_command("rtk git log", &[], &[]);
assert_eq!(result, Some("rtk git log".to_string()));
```

### 复合命令

```rust
use rtk_registry::rewrite_command;

// 链式命令逐段重写
let result = rewrite_command("git status && cargo test", &[], &[]);
assert_eq!(result, Some("rtk git status && rtk cargo test".to_string()));

// 管道命令也可以
let result = rewrite_command("git diff | head -20", &[], &[]);
assert_eq!(result, Some("rtk git diff | head -20".to_string()));
```

### 检查 RTK 是否已安装

```rust
use rtk_registry::{is_rtk_installed, RtkInstallStatus};

match is_rtk_installed() {
    RtkInstallStatus::Installed { path } => {
        println!("rtk 已安装在: {}", path.display());
    }
    RtkInstallStatus::NotInstalled { install_hints } => {
        eprintln!("rtk 未安装，请执行以下命令之一:");
        for cmd in &install_hints.install_commands {
            eprintln!("  {}", cmd);
        }
        eprintln!("仓库地址: {}", install_hints.repository);
        eprintln!("文档: {}", install_hints.docs_url);
    }
}
```

### 要求 RTK 必须安装（未安装时报错）

```rust
use rtk_registry::require_rtk_installed;

let rtk_path = require_rtk_installed().map_err(|hints| {
    format!(
        "rtk 未安装。请通过以下方式安装: {}",
        hints.install_commands.join(" 或 ")
    )
})?;

println!("rtk 路径: {}", rtk_path.display());
```

### 排除命令和透明前缀

```rust
use rtk_registry::rewrite_command;

// 排除特定命令不被重写
let excluded = vec!["git push".to_string()];
let result = rewrite_command("git push origin main", &excluded, &[]);
assert_eq!(result, None);

// 透明前缀（包装命令），重写会保留前缀
let prefixes = vec!["docker exec mycontainer".to_string()];
let result = rewrite_command("docker exec mycontainer git status", &[], &prefixes);
assert_eq!(result, Some("docker exec mycontainer rtk git status".to_string()));
```

### 命令分类

```rust
use rtk_registry::{classify_command, Classification};

match classify_command("git status") {
    Classification::Supported { rtk_equivalent, category, estimated_savings_pct, .. } => {
        println!("RTK 等价命令: {}", rtk_equivalent);
        println!("分类: {}", category);
        println!("预估节省: {:.0}%", estimated_savings_pct);
    }
    Classification::Unsupported { base_command } => {
        println!("无 RTK 支持: {}", base_command);
    }
    Classification::Ignored => {
        println!("命令应原样传递");
    }
}
```

### 访问规则表

```rust
use rtk_registry::RULES;

println!("RTK 支持 {} 个命令模式", RULES.len());

for rule in RULES.iter().take(5) {
    println!("{} → {} (节省 {:.0}%)",
        rule.pattern,
        rule.rtk_cmd,
        rule.savings_pct
    );
}
```

### 安全：检测不可信的 shell 结构

```rust
use rtk_registry::contains_unattestable_construct;

// 危险：可重写命令中包含命令替换
assert!(contains_unattestable_construct("git status `whoami`"));
assert!(contains_unattestable_construct("git log --pretty=$(rm -rf ~)"));

// 安全：无替换，简单变量展开没问题
assert!(!contains_unattestable_construct("git status"));
assert!(!contains_unattestable_construct("echo $HOME"));

// 安全：fd 重定向 (2>&1) 和 /dev/null 是例外
assert!(!contains_unattestable_construct("cargo test 2>&1"));
assert!(!contains_unattestable_construct("cmd > /dev/null"));
```

### 分割命令用于权限校验

```rust
use rtk_registry::split_for_permissions;

// 按操作符、管道、换行、后台 &、子 shell 分割
let segments = split_for_permissions("git status && cargo build");
assert_eq!(segments, vec!["git status", "cargo build"]);

// 在重定向处截断
let segments = split_for_permissions("git log > /tmp/out");
assert_eq!(segments, vec!["git log"]);
```

## 公共 API

| 函数/类型 | 描述 |
|-----------|------|
| `rewrite_command(cmd, excluded, transparent_prefixes) -> Option<String>` | 重写命令为 RTK 等价形式 |
| `rewrite_command_with_config(cmd, excluded, transparent_prefixes, config) -> Option<String>` | 带自定义回调的重写 |
| `classify_command(cmd) -> Classification` | 分类命令是否有 RTK 支持 |
| `contains_unattestable_construct(cmd) -> bool` | 检测不可自证的 shell 结构（反引号、`$()`、`<()`) |
| `split_for_permissions(cmd) -> Vec<&str>` | 按段分割命令用于权限校验 |
| `is_rtk_installed() -> RtkInstallStatus` | 检查 rtk 二进制是否在 PATH 中 |
| `require_rtk_installed() -> Result<PathBuf, InstallHints>` | 要求已安装，未安装时返回安装提示 |
| `has_heredoc(cmd) -> bool` | 检测命令中的 heredoc（不会重写） |
| `split_command_chain(cmd) -> Vec<&str>` | 按操作符分割复合命令 |
| `category_avg_tokens(category, subcmd) -> usize` | 分类/子命令的平均 token 数 |
| `cmd_has_rtk_disabled_prefix(cmd) -> bool` | 检查命令是否有 `RTK_DISABLED=` 环境前缀 |
| `prefix_contains_rtk_disabled(prefix) -> bool` | 检查前缀部分是否包含 `RTK_DISABLED=` |
| `strip_disabled_prefix(cmd) -> (&str, &str)` | 剥离 `RTK_DISABLED=` 前缀，返回 (前缀, 剩余) |
| `RewriteConfig` | 配置结构体，含可选的 `on_warning: Option<fn(&str)>` 回调 |
| `Classification` | 枚举：`Supported`、`Unsupported`、`Ignored` |
| `RtkRule` | 规则结构体，含 `pattern`、`rtk_cmd`、`category`、`savings_pct` 字段 |
| `RtkStatus` | 枚举：`Existing`、`Passthrough`、`NotSupported` |
| `IGNORED_EXACT: &[&str]` | RTK 不应重写的精确命令列表 |
| `IGNORED_PREFIXES: &[&str]` | RTK 不应重写的命令前缀列表 |
| `RULES: &[RtkRule]` | 静态重写规则表（74 条模式） |

## 支持的生态系统

| 生态 | 命令 | 预估节省 |
|------|------|----------|
| Git | git, gh, gt, diff | 80%+ |
| Rust | cargo, cargo test | 90%+ |
| JS/TS | npm, pnpm, vitest, jest, next, playwright, prisma | 70%+ |
| Python | ruff, pytest, mypy, pip | 75%+ |
| Go | go, golangci-lint | 65%+ |
| 容器 | docker, kubectl | 60%+ |
| 系统 | ls, tree, find, grep, env | 70%+ |
| Ruby | rake, rspec, rubocop | 80%+ |
| .NET | dotnet, binlog, trx | 60%+ |

## 许可证

MIT
