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
14 changes: 10 additions & 4 deletions docs/usage/FEATURES.md
Original file line number Diff line number Diff line change
Expand Up @@ -1175,12 +1175,18 @@ rtk init -g --uninstall # Desinstaller

### `rtk rewrite` -- Recriture de commande

Commande interne utilisee par le hook. Imprime la commande reecrite sur stdout (exit 0) ou sort avec exit 1 si aucun equivalent RTK n'existe.
Commande interne utilisee par le hook. Le code de sortie preserve la decision de permission :

- `0` : imprime une commande reecrite explicitement autorisee
- `1` : aucun equivalent RTK, sans sortie
- `2` : une regle de refus correspond, sans sortie
- `3` : imprime une commande reecrite qui necessite encore une confirmation (regle ask ou comportement par defaut)

Les integrations doivent donc lire stdout pour les codes `0` et `3`, tout en conservant la demande de confirmation pour le code `3`.

```bash
rtk rewrite "git status" # -> "rtk git status" (exit 0)
rtk rewrite "terraform plan" # -> (exit 1, pas de recriture)
rtk rewrite "rtk git status" # -> "rtk git status" (exit 0, inchange)
rtk rewrite "git status" # -> "rtk git status" (exit 3 par defaut)
rtk rewrite "echo hello" # -> (exit 1, pas de recriture)
```

### `rtk verify` -- Verification d'integrite
Expand Down
31 changes: 27 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -837,11 +837,13 @@ enum Commands {

/// Rewrite a raw command to its RTK equivalent (single source of truth for hooks)
///
/// Exits 0 and prints the rewritten command if supported.
/// Exits 1 with no output if the command has no RTK equivalent.
/// Exit 0: prints a rewrite that an explicit allow rule matched.
/// Exit 1: no RTK equivalent; prints nothing.
/// Exit 2: a deny rule matched; prints nothing.
/// Exit 3: prints a rewrite that still requires user approval (ask or default).
///
/// Used by Claude Code, Gemini CLI, and other LLM hooks:
/// REWRITTEN=$(rtk rewrite "$CMD") || exit 0
/// Integrators should consume stdout for exits 0 and 3, while preserving the
/// approval requirement for exit 3. Other exits should pass through unchanged.
Rewrite {
/// Raw command to rewrite (e.g. "git status", "cargo test && git push")
/// Accepts multiple args: `rtk rewrite ls -al` is equivalent to `rtk rewrite "ls -al"`
Expand Down Expand Up @@ -3386,6 +3388,27 @@ mod tests {
}
}

#[test]
fn test_rewrite_help_documents_exit_code_contract() {
use clap::CommandFactory;

let mut command = Cli::command();
let help = command
.find_subcommand_mut("rewrite")
.expect("rewrite subcommand should exist")
.render_long_help()
.to_string();

for exit_code in 0..=3 {
assert!(
help.contains(&format!("Exit {exit_code}:")),
"rewrite help should document exit {exit_code}:\n{help}"
);
}
assert!(help.contains("consume stdout for exits 0 and 3"));
assert!(!help.contains("|| exit 0"));
}

#[test]
fn test_merge_filters_with_no_args() {
let filters = vec![];
Expand Down