Skip to content

Commit da3c432

Browse files
authored
Merge pull request #1399 from rtk-ai/fix/windows-go-binary-hook
fix(hooks): windows use 'rtk hook claude' no fallback
2 parents ca4c59c + 13a73dd commit da3c432

1 file changed

Lines changed: 132 additions & 25 deletions

File tree

src/hooks/init.rs

Lines changed: 132 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -872,20 +872,6 @@ fn hook_already_present(root: &serde_json::Value, hook_command: &str) -> bool {
872872
}
873873

874874
/// Default mode: hook + slim RTK.md + @RTK.md reference
875-
#[cfg(not(unix))]
876-
fn run_default_mode(
877-
_global: bool,
878-
_patch_mode: PatchMode,
879-
_verbose: u8,
880-
_install_opencode: bool,
881-
) -> Result<()> {
882-
eprintln!("[warn] Hook-based mode requires Unix (macOS/Linux).");
883-
eprintln!(" Windows: use --claude-md mode for full injection.");
884-
eprintln!(" Falling back to --claude-md mode.");
885-
run_claude_md_mode(_global, _verbose, _install_opencode)
886-
}
887-
888-
#[cfg(unix)]
889875
fn run_default_mode(
890876
global: bool,
891877
patch_mode: PatchMode,
@@ -1127,17 +1113,6 @@ fn generate_global_filters_template(verbose: u8) -> Result<()> {
11271113
}
11281114

11291115
/// Hook-only mode: just the hook, no RTK.md
1130-
#[cfg(not(unix))]
1131-
fn run_hook_only_mode(
1132-
_global: bool,
1133-
_patch_mode: PatchMode,
1134-
_verbose: u8,
1135-
_install_opencode: bool,
1136-
) -> Result<()> {
1137-
anyhow::bail!("Hook install requires Unix (macOS/Linux). Use WSL or --claude-md mode.")
1138-
}
1139-
1140-
#[cfg(unix)]
11411116
fn run_hook_only_mode(
11421117
global: bool,
11431118
patch_mode: PatchMode,
@@ -1752,6 +1727,9 @@ fn resolve_home_subdir(subdir: &str) -> Result<PathBuf> {
17521727
}
17531728

17541729
fn resolve_claude_dir() -> Result<PathBuf> {
1730+
if let Ok(dir) = std::env::var("RTK_CLAUDE_DIR") {
1731+
return Ok(PathBuf::from(dir));
1732+
}
17551733
resolve_home_subdir(CLAUDE_DIR)
17561734
}
17571735

@@ -3668,4 +3646,133 @@ More notes
36683646
assert_eq!(arr.len(), 1);
36693647
assert_eq!(arr[0]["command"].as_str().unwrap(), CURSOR_HOOK_COMMAND);
36703648
}
3649+
3650+
use std::sync::Mutex;
3651+
static CLAUDE_DIR_LOCK: Mutex<()> = Mutex::new(());
3652+
3653+
fn with_claude_dir_override<F: FnOnce(&Path)>(tmp: &TempDir, f: F) {
3654+
let _guard = CLAUDE_DIR_LOCK.lock().unwrap_or_else(|e| e.into_inner());
3655+
let claude_dir = tmp.path().join(CLAUDE_DIR);
3656+
fs::create_dir_all(&claude_dir).unwrap();
3657+
3658+
let orig = std::env::var_os("RTK_CLAUDE_DIR");
3659+
std::env::set_var("RTK_CLAUDE_DIR", &claude_dir);
3660+
f(&claude_dir);
3661+
match orig {
3662+
Some(v) => std::env::set_var("RTK_CLAUDE_DIR", v),
3663+
None => std::env::remove_var("RTK_CLAUDE_DIR"),
3664+
}
3665+
}
3666+
3667+
#[test]
3668+
fn test_global_default_mode_creates_artifacts() {
3669+
let tmp = TempDir::new().unwrap();
3670+
with_claude_dir_override(&tmp, |claude_dir| {
3671+
run_default_mode(true, PatchMode::Auto, 0, false).unwrap();
3672+
3673+
assert!(claude_dir.join(RTK_MD).exists(), "RTK.md must be created");
3674+
assert!(
3675+
claude_dir.join(CLAUDE_MD).exists(),
3676+
"CLAUDE.md must be created"
3677+
);
3678+
3679+
let settings = claude_dir.join(SETTINGS_JSON);
3680+
assert!(settings.exists(), "settings.json must be created");
3681+
let content = fs::read_to_string(&settings).unwrap();
3682+
assert!(
3683+
content.contains(CLAUDE_HOOK_COMMAND),
3684+
"settings.json must contain hook command"
3685+
);
3686+
});
3687+
}
3688+
3689+
#[test]
3690+
fn test_global_uninstall_removes_artifacts() {
3691+
let tmp = TempDir::new().unwrap();
3692+
with_claude_dir_override(&tmp, |claude_dir| {
3693+
run_default_mode(true, PatchMode::Auto, 0, false).unwrap();
3694+
uninstall(true, false, false, false, 0).unwrap();
3695+
3696+
assert!(!claude_dir.join(RTK_MD).exists(), "RTK.md must be removed");
3697+
let settings_content =
3698+
fs::read_to_string(claude_dir.join(SETTINGS_JSON)).unwrap_or_default();
3699+
assert!(
3700+
!settings_content.contains(CLAUDE_HOOK_COMMAND),
3701+
"hook entry must be removed from settings.json"
3702+
);
3703+
});
3704+
}
3705+
3706+
#[test]
3707+
fn test_global_default_mode_idempotent() {
3708+
let tmp = TempDir::new().unwrap();
3709+
with_claude_dir_override(&tmp, |claude_dir| {
3710+
run_default_mode(true, PatchMode::Auto, 0, false).unwrap();
3711+
run_default_mode(true, PatchMode::Auto, 0, false).unwrap();
3712+
3713+
let settings = fs::read_to_string(claude_dir.join(SETTINGS_JSON)).unwrap();
3714+
let count = settings.matches(CLAUDE_HOOK_COMMAND).count();
3715+
assert_eq!(count, 1, "hook command must appear exactly once");
3716+
});
3717+
}
3718+
3719+
#[test]
3720+
fn test_upgrade_from_claude_md_to_hook_mode() {
3721+
let tmp = TempDir::new().unwrap();
3722+
with_claude_dir_override(&tmp, |claude_dir| {
3723+
run_claude_md_mode(true, 0, false).unwrap();
3724+
let claude_md_content = fs::read_to_string(claude_dir.join(CLAUDE_MD)).unwrap();
3725+
assert!(
3726+
claude_md_content.contains("<!-- rtk-instructions"),
3727+
"pre-condition: old block must exist"
3728+
);
3729+
3730+
run_default_mode(true, PatchMode::Auto, 0, false).unwrap();
3731+
3732+
assert!(claude_dir.join(RTK_MD).exists(), "RTK.md must be created");
3733+
let settings = fs::read_to_string(claude_dir.join(SETTINGS_JSON)).unwrap();
3734+
assert!(
3735+
settings.contains(CLAUDE_HOOK_COMMAND),
3736+
"hook must be in settings.json after upgrade"
3737+
);
3738+
});
3739+
}
3740+
3741+
#[test]
3742+
fn test_local_init_no_hook() {
3743+
let tmp = TempDir::new().unwrap();
3744+
let cwd = std::env::current_dir().unwrap();
3745+
std::env::set_current_dir(tmp.path()).unwrap();
3746+
3747+
let result = run_default_mode(false, PatchMode::Auto, 0, false);
3748+
std::env::set_current_dir(&cwd).unwrap();
3749+
3750+
result.unwrap();
3751+
assert!(
3752+
tmp.path().join(CLAUDE_MD).exists(),
3753+
"local CLAUDE.md must be created"
3754+
);
3755+
assert!(
3756+
!tmp.path().join(SETTINGS_JSON).exists(),
3757+
"settings.json must not be created for local init"
3758+
);
3759+
}
3760+
3761+
#[test]
3762+
fn test_global_hook_only_mode_creates_settings() {
3763+
let tmp = TempDir::new().unwrap();
3764+
with_claude_dir_override(&tmp, |claude_dir| {
3765+
run_hook_only_mode(true, PatchMode::Auto, 0, false).unwrap();
3766+
3767+
assert!(
3768+
!claude_dir.join(RTK_MD).exists(),
3769+
"RTK.md must NOT be created in hook-only mode"
3770+
);
3771+
let settings = fs::read_to_string(claude_dir.join(SETTINGS_JSON)).unwrap();
3772+
assert!(
3773+
settings.contains(CLAUDE_HOOK_COMMAND),
3774+
"settings.json must contain hook command"
3775+
);
3776+
});
3777+
}
36713778
}

0 commit comments

Comments
 (0)