huskies: merge 586_bug_wizard_skips_context_and_stack_generation_when_files_already_exist_from_scaffold

This commit is contained in:
dave
2026-04-15 23:48:14 +00:00
parent 4a1c6b4cfa
commit 4553d7215a
2 changed files with 45 additions and 8 deletions
+44 -7
View File
@@ -56,18 +56,27 @@ pub(crate) fn is_script_step(step: WizardStep) -> bool {
)
}
/// Write `content` to `path` only when the file does not already exist.
/// Write `content` to `path`, skipping if the file already exists with real
/// (non-template) content.
///
/// Existing files (including `CLAUDE.md`) are never overwritten — the wizard
/// appends or skips per the acceptance criteria. For script steps the file is
/// also made executable after writing.
/// Scaffold template files (those containing [`TEMPLATE_SENTINEL`]) are treated
/// as placeholders and will be overwritten with the wizard-generated content.
/// Files with real user content are never overwritten. For script steps the
/// file is also made executable after writing.
pub(crate) fn write_if_missing(
path: &Path,
content: &str,
executable: bool,
) -> Result<bool, String> {
use crate::io::onboarding::TEMPLATE_SENTINEL;
if path.exists() {
return Ok(false); // already present — skip silently
// Overwrite scaffold template placeholders; preserve real user content.
let is_template = std::fs::read_to_string(path)
.map(|s| s.contains(TEMPLATE_SENTINEL))
.unwrap_or(false);
if !is_template {
return Ok(false); // real content already present — skip
}
}
if let Some(parent) = path.parent() {
fs::create_dir_all(parent)
@@ -473,13 +482,13 @@ mod tests {
fn wizard_confirm_does_not_overwrite_existing_file() {
let dir = TempDir::new().unwrap();
let ctx = setup(&dir);
// Pre-create the specs directory and file.
// Pre-create the specs directory and file with real (non-template) content.
let specs_dir = dir.path().join(".huskies").join("specs");
std::fs::create_dir_all(&specs_dir).unwrap();
let context_path = specs_dir.join("00_CONTEXT.md");
std::fs::write(&context_path, "original content").unwrap();
// Stage and confirm — existing file should NOT be overwritten.
// Stage and confirm — existing real file should NOT be overwritten.
tool_wizard_generate(&serde_json::json!({"content": "new content"}), &ctx).unwrap();
let result = tool_wizard_confirm(&ctx).unwrap();
assert!(result.contains("already exists"));
@@ -489,6 +498,34 @@ mod tests {
);
}
#[test]
fn wizard_confirm_overwrites_scaffold_template_file() {
let dir = TempDir::new().unwrap();
let ctx = setup(&dir);
// Pre-create the file with scaffold template placeholder content.
let specs_dir = dir.path().join(".huskies").join("specs");
std::fs::create_dir_all(&specs_dir).unwrap();
let context_path = specs_dir.join("00_CONTEXT.md");
std::fs::write(
&context_path,
"<!-- huskies:scaffold-template -->\n# Project Context\n\nTODO: Describe...",
)
.unwrap();
// Stage and confirm — template placeholder should be overwritten with generated content.
tool_wizard_generate(
&serde_json::json!({"content": "# My Real Project\n\nThis is a real project."}),
&ctx,
)
.unwrap();
let result = tool_wizard_confirm(&ctx).unwrap();
assert!(result.contains("confirmed"));
assert_eq!(
std::fs::read_to_string(&context_path).unwrap(),
"# My Real Project\n\nThis is a real project."
);
}
#[test]
fn wizard_skip_advances_wizard() {
let dir = TempDir::new().unwrap();
+1 -1
View File
@@ -5,7 +5,7 @@ use std::path::Path;
/// Only untouched templates contain this marker — real project content
/// will never include it, so it avoids false positives when the project
/// itself is an "Agentic AI Code Assistant".
const TEMPLATE_SENTINEL: &str = "<!-- huskies:scaffold-template -->";
pub(crate) const TEMPLATE_SENTINEL: &str = "<!-- huskies:scaffold-template -->";
/// Marker found in the default `script/test` scaffold output.
const TEMPLATE_MARKER_SCRIPT: &str = "No tests configured";