mirror of
https://github.com/rtk-ai/rtk.git
synced 2026-09-19 07:33:17 +08:00
8fae5b067d
* fix: P1 exit codes, grep regex perf, SQLite WAL (#631) * fix: P1 exit codes, grep regex perf, SQLite concurrency Exit code propagation (same pattern as existing modules): - wget_cmd: run() and run_stdout() now exit on failure - container: docker_logs, kubectl_pods/services/logs now check status before parsing JSON (was showing "No pods found" on error) - pnpm_cmd: replace bail!() with eprint + process::exit in run_list and run_install Performance: - grep_cmd: compile context regex once before loop instead of per-line in clean_line() (was N compilations per grep call) Data integrity: - tracking: add PRAGMA journal_mode=WAL and busy_timeout=5000 to prevent SQLite corruption with concurrent Claude Code instances Signed-off-by: Patrick <patrick@rtk.ai> Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * fix: address review findings on P1 fixes - tracking: WAL pragma non-fatal (NFS/read-only compat) - wget: forward raw stderr on failure, track raw==raw (no fake savings) - container: remove stderr shadow in docker_logs, add empty-stderr guard on all 4 new exit code paths for consistency with prisma pattern Signed-off-by: Patrick <patrick@rtk.ai> Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> --------- Signed-off-by: Patrick <patrick@rtk.ai> Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * fix: raise output caps for P0 bugs (#617, #618, #620) (#630) * fix: raise output caps for grep, git status, and parser fallback (#617, #618, #620) - grep: per-file match cap 10 → 25, global max 50 → 200 - git status: file list caps 5/5/3 → 15/15/10 - parser fallback: truncate 500 → 2000 chars across all modules These P0 bugs caused LLM retry loops when RTK returned less signal than the raw command, making RTK worse than not using it. Fixes #617, #618, #620 Signed-off-by: Patrick <patrick@rtk.ai> Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * fix: update README example and add truncation tests for modified/untracked - parser/README.md: update example from 500 → 2000 to match code - git.rs: add test_format_status_modified_truncation (cap 15) - git.rs: add test_format_status_untracked_truncation (cap 10) Signed-off-by: Patrick <patrick@rtk.ai> Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * refactor: extract output caps into [limits] config section Move hardcoded caps into config.toml so users can tune them: [limits] grep_max_results = 200 # global grep match limit grep_max_per_file = 25 # per-file match limit status_max_files = 15 # staged/modified file list cap status_max_untracked = 10 # untracked file list cap passthrough_max_chars = 2000 # parser fallback truncation All 8 modules now read from config::limits() instead of hardcoded values. Defaults unchanged from previous commit. Signed-off-by: Patrick <patrick@rtk.ai> Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> --------- Signed-off-by: Patrick <patrick@rtk.ai> Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * feat(.claude): add /rtk-triage skill — cross-analysis of PRs and issues (#662) * feat(.claude): add /rtk-triage skill — orchestrated PR+issue cross-analysis New skill that runs issue-triage + pr-triage in parallel then produces a cross-analysis layer that neither skill can do individually: - Double coverage detection: identifies when 2+ PRs target the same issue (via body scan + file overlap), recommends which to keep/close - Security gap detection: for security review issues, maps each finding to a PR (or flags it as uncovered) - P0/P1 bugs without PR: groups by pattern to suggest sprint batching - Our dirty PRs: identifies probable cause (conflict with sibling PR, needs rebase, missing linked issue) Output is saved automatically to claudedocs/RTK-YYYY-MM-DD.md. Usage: /rtk-triage (French, auto-save) /rtk-triage en (English output) Signed-off-by: Florian Bruniaux <florian@bel-etage.com> Signed-off-by: Florian BRUNIAUX <florian@bruniaux.com> * docs(architecture): update module count to 66 Sync ARCHITECTURE.md with current main.rs state. Previous count (60) was stale since several modules were added (dotnet_cmd, dotnet_format_report, dotnet_trx, npm_cmd, gt_cmd, etc.). Signed-off-by: Florian Bruniaux <florian@bel-etage.com> Signed-off-by: Florian BRUNIAUX <florian@bruniaux.com> --------- Signed-off-by: Florian Bruniaux <florian@bel-etage.com> Signed-off-by: Florian BRUNIAUX <florian@bruniaux.com> * fix: subcommand routing drops unrecognized subcommands (#600) (#601) - git stash: pass unknown subcommands (save, branch, clear) through instead of silently falling back to git stash push - git branch: add --show-current, --set-upstream-to, --format, --sort to flag detection so they don't get overridden by -a injection - pip: replace bail!() with passthrough for unknown subcommands (freeze, download, wheel, etc.) Fixes #600 Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * fix: resolve cargo fmt + 54 clippy warnings blocking CI (#663) cargo fmt diffs in config.rs, git.rs, playwright_cmd.rs were failing the fmt CI check, which cascaded to block clippy/test/security on PRs #632, #635, #638. Also fixes all clippy warnings: dead code annotations, iterator simplifications, assert patterns, and unnecessary allocations. Signed-off-by: Patrick Szymkowiak <patrick@rtk-ai.app> Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * fix: discover absolute paths + git global options (#485, #163) (#518) * fix: discover classifies absolute paths like /usr/bin/grep (#485) Normalize absolute binary paths before classification: /usr/bin/grep → grep, /bin/ls → ls, /usr/local/bin/git → git Adds strip_absolute_path() helper + 5 tests. Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * fix: discover and rewrite support git global options -C, --no-pager, etc. (#163) Strip git global options (-C <path>, -c <key=val>, --git-dir, --work-tree, --no-pager, --no-optional-locks, --bare, --literal-pathspecs) before classification so git -C /tmp status is recognized as rtk git. Rewrite preserves global options: git -C /tmp status → rtk git -C /tmp status Adds GIT_GLOBAL_OPT lazy_static regex + strip_git_global_opts() helper + 6 tests. Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> --------- Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * fix: prevent double `--` separator in cargo clippy with -p flags (#519) When running `rtk cargo clippy -p my-crate -- -D warnings`, Clap with `trailing_var_arg = true` preserves the `--` in parsed args when flags precede it. `restore_double_dash()` then added a second `--`, producing `cargo clippy -p my-crate -- -- -D warnings`. This caused rustc to interpret `-D` as a filename instead of a lint flag. Fix: skip restoration when args already contain `--` (Clap preserved it). Fixes #496 Signed-off-by: Ousama Ben Younes <benyounes.ousama@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * ci: add PR template + target branch check (#521) - PR template reminds contributors to target develop - CI workflow labels PRs targeting master with 'wrong-base' and posts a comment - Excludes develop→master PRs (maintainer releases) Signed-off-by: Patrick <patrick@rtk-ai.com> Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * fix: prevent rtk read from corrupting JSON/YAML/data files (#522) Add Language::Data variant for data formats (JSON, YAML, TOML, XML, CSV, etc.) with empty comment patterns to prevent comment stripping. AggressiveFilter falls back to MinimalFilter for data files. Fixes #464 Signed-off-by: Ousama Ben Younes <benyounes.ousama@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> * fix: skip rewriting find/fd in pipes to preserve xargs compatibility (#439) (#563) rtk find outputs a grouped format incompatible with pipe consumers like xargs, grep, wc, sort. Skip rewrite when find/fd is followed by a pipe, preserving native one-per-line output. Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * fix: add hint when git diff is truncated + fix --no-compact passthrough (#427) (#564) When compact_diff truncates output, append a hint line so Claude knows how to get the full diff: [full diff: rtk git diff --no-compact] Also fix --no-compact flag being passed to git (causing usage error) and remove decorative emoji from compact_diff output. Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * fix: propagate exit codes in git diff, status+args, commit, and branch (#632) 4 P1 bugs where git exit codes were swallowed: - git diff: failure silently printed empty stat output - git status (with args): failure was filtered instead of propagated - git commit: failure printed "FAILED" but returned Ok(()) breaking pre-commit hooks - git branch (list mode): failure was silently ignored All now follow the established pattern: eprint stderr, track raw==raw, process::exit(code). Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * feat: add 5 new TOML filters (ollama, nx, gradle, spring-boot, jira) (#635) * feat: add 5 new TOML built-in filters (ollama, nx, gradle, spring-boot, jira) New filters for commands not covered by Rust modules: - ollama: strip ANSI spinners, keep final text response (#624) - nx: strip Nx monorepo noise, keep build results (#444) - gradle/gradlew: strip UP-TO-DATE tasks, keep build summary (#147) - spring-boot: strip banner and verbose logs, keep startup/errors (#147) - jira: strip blanks, truncate wide columns (#524) All 5 filters pass inline tests via rtk verify (123/123). Updated builtin filter count: 47 -> 52. Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * feat: add 5 more TOML filters (turbo, mise, just, task, yadm) New filters for task runners and git wrapper: - turbo: strip cache/Tasks/Duration noise, keep task output (#531) - mise: strip install/download progress, keep task results (#607) - just: strip blanks and recipe headers, keep output (#607) - task: strip task headers and up-to-date lines, keep results (#607) - yadm: strip hint lines, compact git-like output (#567) All verified with fake binaries through catch-all TOML engine. 137/137 TOML tests pass, 934 Rust tests pass. Updated builtin filter count: 52 -> 57. Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> --------- Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> * fix: replace emojis with plain text in git status output (#603) (#638) Git status output used emojis (📌, 📝, ❓, ✅, ⚠️) that confuse non-Claude LLMs (GPT, etc.) causing retry loops. Replace with plain text labels (branch:, modified:, staged:, untracked:, conflicts:). Also add "clean — nothing to commit" when working tree is clean, so LLMs understand the repo state without ambiguity. Before: 📌 master After: branch: master clean — nothing to commit Fixes #603 Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> --------- Signed-off-by: Patrick <patrick@rtk.ai> Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu> Signed-off-by: Florian Bruniaux <florian@bel-etage.com> Signed-off-by: Florian BRUNIAUX <florian@bruniaux.com> Signed-off-by: Patrick Szymkowiak <patrick@rtk-ai.app> Signed-off-by: Ousama Ben Younes <benyounes.ousama@gmail.com> Signed-off-by: Patrick <patrick@rtk-ai.com> Co-authored-by: Florian BRUNIAUX <florian@bruniaux.com> Co-authored-by: Ben Younes <benyounes.ousama@gmail.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
221 lines
5.5 KiB
Rust
221 lines
5.5 KiB
Rust
use anyhow::Result;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::path::PathBuf;
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
|
pub struct Config {
|
|
#[serde(default)]
|
|
pub tracking: TrackingConfig,
|
|
#[serde(default)]
|
|
pub display: DisplayConfig,
|
|
#[serde(default)]
|
|
pub filters: FilterConfig,
|
|
#[serde(default)]
|
|
pub tee: crate::tee::TeeConfig,
|
|
#[serde(default)]
|
|
pub telemetry: TelemetryConfig,
|
|
#[serde(default)]
|
|
pub hooks: HooksConfig,
|
|
#[serde(default)]
|
|
pub limits: LimitsConfig,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize, Default)]
|
|
pub struct HooksConfig {
|
|
/// Commands to exclude from auto-rewrite (e.g. ["curl", "playwright"]).
|
|
/// Survives `rtk init -g` re-runs since config.toml is user-owned.
|
|
#[serde(default)]
|
|
pub exclude_commands: Vec<String>,
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct TrackingConfig {
|
|
pub enabled: bool,
|
|
pub history_days: u32,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub database_path: Option<PathBuf>,
|
|
}
|
|
|
|
impl Default for TrackingConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
enabled: true,
|
|
history_days: 90,
|
|
database_path: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct DisplayConfig {
|
|
pub colors: bool,
|
|
pub emoji: bool,
|
|
pub max_width: usize,
|
|
}
|
|
|
|
impl Default for DisplayConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
colors: true,
|
|
emoji: true,
|
|
max_width: 120,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct FilterConfig {
|
|
pub ignore_dirs: Vec<String>,
|
|
pub ignore_files: Vec<String>,
|
|
}
|
|
|
|
impl Default for FilterConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
ignore_dirs: vec![
|
|
".git".into(),
|
|
"node_modules".into(),
|
|
"target".into(),
|
|
"__pycache__".into(),
|
|
".venv".into(),
|
|
"vendor".into(),
|
|
],
|
|
ignore_files: vec!["*.lock".into(), "*.min.js".into(), "*.min.css".into()],
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct TelemetryConfig {
|
|
pub enabled: bool,
|
|
}
|
|
|
|
impl Default for TelemetryConfig {
|
|
fn default() -> Self {
|
|
Self { enabled: true }
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
pub struct LimitsConfig {
|
|
/// Max total grep results to show (default: 200)
|
|
pub grep_max_results: usize,
|
|
/// Max matches per file in grep output (default: 25)
|
|
pub grep_max_per_file: usize,
|
|
/// Max staged/modified files shown in git status (default: 15)
|
|
pub status_max_files: usize,
|
|
/// Max untracked files shown in git status (default: 10)
|
|
pub status_max_untracked: usize,
|
|
/// Max chars for parser passthrough fallback (default: 2000)
|
|
pub passthrough_max_chars: usize,
|
|
}
|
|
|
|
impl Default for LimitsConfig {
|
|
fn default() -> Self {
|
|
Self {
|
|
grep_max_results: 200,
|
|
grep_max_per_file: 25,
|
|
status_max_files: 15,
|
|
status_max_untracked: 10,
|
|
passthrough_max_chars: 2000,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Get limits config. Falls back to defaults if config can't be loaded.
|
|
pub fn limits() -> LimitsConfig {
|
|
Config::load().map(|c| c.limits).unwrap_or_default()
|
|
}
|
|
|
|
/// Check if telemetry is enabled in config. Returns None if config can't be loaded.
|
|
pub fn telemetry_enabled() -> Option<bool> {
|
|
Config::load().ok().map(|c| c.telemetry.enabled)
|
|
}
|
|
|
|
impl Config {
|
|
pub fn load() -> Result<Self> {
|
|
let path = get_config_path()?;
|
|
|
|
if path.exists() {
|
|
let content = std::fs::read_to_string(&path)?;
|
|
let config: Config = toml::from_str(&content)?;
|
|
Ok(config)
|
|
} else {
|
|
Ok(Config::default())
|
|
}
|
|
}
|
|
|
|
pub fn save(&self) -> Result<()> {
|
|
let path = get_config_path()?;
|
|
|
|
if let Some(parent) = path.parent() {
|
|
std::fs::create_dir_all(parent)?;
|
|
}
|
|
|
|
let content = toml::to_string_pretty(self)?;
|
|
std::fs::write(&path, content)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn create_default() -> Result<PathBuf> {
|
|
let config = Config::default();
|
|
config.save()?;
|
|
get_config_path()
|
|
}
|
|
}
|
|
|
|
fn get_config_path() -> Result<PathBuf> {
|
|
let config_dir = dirs::config_dir().unwrap_or_else(|| PathBuf::from("."));
|
|
Ok(config_dir.join("rtk").join("config.toml"))
|
|
}
|
|
|
|
pub fn show_config() -> Result<()> {
|
|
let path = get_config_path()?;
|
|
println!("Config: {}", path.display());
|
|
println!();
|
|
|
|
if path.exists() {
|
|
let config = Config::load()?;
|
|
println!("{}", toml::to_string_pretty(&config)?);
|
|
} else {
|
|
println!("(default config, file not created)");
|
|
println!();
|
|
let config = Config::default();
|
|
println!("{}", toml::to_string_pretty(&config)?);
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_hooks_config_deserialize() {
|
|
let toml = r#"
|
|
[hooks]
|
|
exclude_commands = ["curl", "gh"]
|
|
"#;
|
|
let config: Config = toml::from_str(toml).expect("valid toml");
|
|
assert_eq!(config.hooks.exclude_commands, vec!["curl", "gh"]);
|
|
}
|
|
|
|
#[test]
|
|
fn test_hooks_config_default_empty() {
|
|
let config = Config::default();
|
|
assert!(config.hooks.exclude_commands.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_config_without_hooks_section_is_valid() {
|
|
let toml = r#"
|
|
[tracking]
|
|
enabled = true
|
|
history_days = 90
|
|
"#;
|
|
let config: Config = toml::from_str(toml).expect("valid toml");
|
|
assert!(config.hooks.exclude_commands.is_empty());
|
|
}
|
|
}
|