mirror of
https://github.com/pbakaus/impeccable.git
synced 2026-09-16 16:16:32 +03:00
Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
94455d886c | ||
|
|
8a97f6cd1d | ||
|
|
8b22d258b4 |
@@ -140,25 +140,35 @@ fn locale_compare(a: &str, b: &str) -> std::cmp::Ordering {
|
||||
fn check(io: &mut Io) -> R<()> {
|
||||
let (sys, _) = ctx(io);
|
||||
let root = sys.find_project_root();
|
||||
if sys.is_already_installed(&root, None).is_none() {
|
||||
// A home-rooted tree is either the user-level install or a project
|
||||
// install under ~. Pick one scope so leftover dirs on the other layout
|
||||
// cannot make a current copy look stale (#824). Inferred scope walks
|
||||
// both, which is what produced the false "Updates available".
|
||||
let scope = if sys.is_home_dir(&root) {
|
||||
if sys.is_already_installed(&root, Some(Scope::User)).is_some() {
|
||||
Some(Scope::User)
|
||||
} else {
|
||||
Some(Scope::Project)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
if sys.is_already_installed(&root, scope).is_none() {
|
||||
out(io, "Impeccable is not installed in this project.");
|
||||
out(io, "Run `npx impeccable install` to install.");
|
||||
return Err(Flow::Exit(0));
|
||||
}
|
||||
let providers = sys.find_installed_providers(&root, None);
|
||||
let providers = sys.find_impeccable_providers(&root, scope);
|
||||
out(io, "Checking for updates...\n");
|
||||
let result = (|| -> Result<bool, String> {
|
||||
let bundle_dir = bundle::download_and_extract_bundle(&sys)?;
|
||||
// JS: agentScope 'user' for a home-rooted checkout (d2a9efb9), so
|
||||
// check() judges agent freshness against the user agent dirs.
|
||||
let agent_scope = if sys.is_home_dir(&root) { Some(Scope::User) } else { None };
|
||||
let up_to_date = bundle::is_up_to_date(&sys, &root, &providers, &bundle_dir, None, agent_scope)?;
|
||||
let up_to_date = bundle::is_up_to_date(&sys, &root, &providers, &bundle_dir, scope, scope)?;
|
||||
util::rm_rf(&bundle_dir);
|
||||
Ok(up_to_date)
|
||||
})();
|
||||
match result {
|
||||
Ok(true) => {
|
||||
let v = sys.get_skills_version(&root, None);
|
||||
let v = sys.get_skills_version(&root, scope);
|
||||
out(io, &format!("Skills are up to date{}.", version_suffix(&v)));
|
||||
}
|
||||
Ok(false) => {
|
||||
|
||||
@@ -460,6 +460,133 @@ fn check_accepts_current_copilot_user_agents_in_home_rooted_checkout() {
|
||||
std::fs::remove_dir_all(&root).ok();
|
||||
}
|
||||
|
||||
// ─── check vs update scope parity (#824) ─────────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn check_ignores_unrelated_harness_skill_in_home_rooted_tree() {
|
||||
let root = temp_root("check-824-extra-harness");
|
||||
let home = format!("{root}/home");
|
||||
let tmpdir = format!("{root}/tmp");
|
||||
for d in [&home, &tmpdir] {
|
||||
std::fs::create_dir_all(d).unwrap();
|
||||
}
|
||||
std::fs::create_dir_all(format!("{home}/.git")).unwrap();
|
||||
let bundle_root = create_fake_universal_bundle(&home, &[".claude", ".cursor"]);
|
||||
let env = base_env(&home, &tmpdir, &bundle_root);
|
||||
|
||||
let r = run_cli(
|
||||
&["install", "-y", "--scope=global", "--no-hooks", "--providers=claude"],
|
||||
&home,
|
||||
&env,
|
||||
);
|
||||
assert_eq!(r.code, 0, "{}\n{}", r.stdout, r.stderr);
|
||||
write(
|
||||
&format!("{home}/.cursor/skills/other/SKILL.md"),
|
||||
"---\nname: other\n---\nUnrelated skill.\n",
|
||||
);
|
||||
|
||||
let r = run_cli(&["check"], &home, &env);
|
||||
assert!(r.stdout.contains("Skills are up to date"), "{}\n{}", r.stdout, r.stderr);
|
||||
assert!(!r.stdout.contains("Updates available"), "{}", r.stdout);
|
||||
|
||||
let r = run_cli(&["update", "--global", "-y", "--no-hooks"], &home, &env);
|
||||
assert!(r.stdout.contains("Skills are up to date"), "{}\n{}", r.stdout, r.stderr);
|
||||
std::fs::remove_dir_all(&root).ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_ignores_leftover_pi_project_layout_in_home_rooted_tree() {
|
||||
let root = temp_root("check-824-pi-leftover");
|
||||
let home = format!("{root}/home");
|
||||
let tmpdir = format!("{root}/tmp");
|
||||
for d in [&home, &tmpdir] {
|
||||
std::fs::create_dir_all(d).unwrap();
|
||||
}
|
||||
std::fs::create_dir_all(format!("{home}/.git")).unwrap();
|
||||
let bundle_root = create_fake_universal_bundle(&home, &[".pi"]);
|
||||
let env = base_env(&home, &tmpdir, &bundle_root);
|
||||
|
||||
let r = run_cli(
|
||||
&["install", "-y", "--scope=global", "--no-hooks", "--providers=pi"],
|
||||
&home,
|
||||
&env,
|
||||
);
|
||||
assert_eq!(r.code, 0, "{}\n{}", r.stdout, r.stderr);
|
||||
assert!(std::path::Path::new(&format!("{home}/.pi/agent/skills/impeccable/SKILL.md")).exists());
|
||||
write(
|
||||
&format!("{home}/.pi/skills/other/SKILL.md"),
|
||||
"---\nname: other\n---\nLeftover project-layout skill.\n",
|
||||
);
|
||||
|
||||
let r = run_cli(&["check"], &home, &env);
|
||||
assert!(r.stdout.contains("Skills are up to date"), "{}\n{}", r.stdout, r.stderr);
|
||||
assert!(!r.stdout.contains("Updates available"), "{}", r.stdout);
|
||||
|
||||
let r = run_cli(&["update", "--global", "-y", "--no-hooks"], &home, &env);
|
||||
assert!(r.stdout.contains("Skills are up to date"), "{}\n{}", r.stdout, r.stderr);
|
||||
std::fs::remove_dir_all(&root).ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_sees_home_rooted_project_scope_pi_install() {
|
||||
let root = temp_root("check-824-pi-project");
|
||||
let home = format!("{root}/home");
|
||||
let tmpdir = format!("{root}/tmp");
|
||||
for d in [&home, &tmpdir] {
|
||||
std::fs::create_dir_all(d).unwrap();
|
||||
}
|
||||
std::fs::create_dir_all(format!("{home}/.git")).unwrap();
|
||||
let bundle_root = create_fake_universal_bundle(&home, &[".pi"]);
|
||||
let env = base_env(&home, &tmpdir, &bundle_root);
|
||||
|
||||
let r = run_cli(
|
||||
&["install", "-y", "--scope=project", "--no-hooks", "--providers=pi"],
|
||||
&home,
|
||||
&env,
|
||||
);
|
||||
assert_eq!(r.code, 0, "{}\n{}", r.stdout, r.stderr);
|
||||
assert!(std::path::Path::new(&format!("{home}/.pi/skills/impeccable/SKILL.md")).exists());
|
||||
assert!(!std::path::Path::new(&format!("{home}/.pi/agent/skills/impeccable/SKILL.md")).exists());
|
||||
write(
|
||||
&format!("{home}/.pi/agent/skills/other/SKILL.md"),
|
||||
"---\nname: other\n---\nLeftover user-layout skill.\n",
|
||||
);
|
||||
|
||||
let r = run_cli(&["check"], &home, &env);
|
||||
assert!(!r.stdout.contains("not installed"), "{}\n{}", r.stdout, r.stderr);
|
||||
assert!(r.stdout.contains("Skills are up to date"), "{}\n{}", r.stdout, r.stderr);
|
||||
assert!(!r.stdout.contains("Updates available"), "{}", r.stdout);
|
||||
std::fs::remove_dir_all(&root).ok();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn check_reports_stale_impeccable_in_home_rooted_tree() {
|
||||
let root = temp_root("check-824-stale");
|
||||
let home = format!("{root}/home");
|
||||
let tmpdir = format!("{root}/tmp");
|
||||
for d in [&home, &tmpdir] {
|
||||
std::fs::create_dir_all(d).unwrap();
|
||||
}
|
||||
std::fs::create_dir_all(format!("{home}/.git")).unwrap();
|
||||
let bundle_root = create_fake_universal_bundle(&home, &[".claude"]);
|
||||
let env = base_env(&home, &tmpdir, &bundle_root);
|
||||
|
||||
let r = run_cli(
|
||||
&["install", "-y", "--scope=global", "--no-hooks", "--providers=claude"],
|
||||
&home,
|
||||
&env,
|
||||
);
|
||||
assert_eq!(r.code, 0, "{}\n{}", r.stdout, r.stderr);
|
||||
write(
|
||||
&format!("{home}/.claude/skills/impeccable/SKILL.md"),
|
||||
"---\nname: impeccable\nversion: 0.0.0-stale\n---\n\nStale copy.\n",
|
||||
);
|
||||
|
||||
let r = run_cli(&["check"], &home, &env);
|
||||
assert!(r.stdout.contains("Updates available"), "{}\n{}", r.stdout, r.stderr);
|
||||
std::fs::remove_dir_all(&root).ok();
|
||||
}
|
||||
|
||||
// ─── inferred agent update scope (d2a9efb9) ──────────────────────────────────
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user