The great storkit name conversion
This commit is contained in:
+88
-85
@@ -11,7 +11,7 @@ const KEY_KNOWN_PROJECTS: &str = "known_projects";
|
||||
|
||||
const STORY_KIT_README: &str = include_str!("../../../.storkit/README.md");
|
||||
|
||||
const STORY_KIT_CONTEXT: &str = "<!-- story-kit:scaffold-template -->\n\
|
||||
const STORY_KIT_CONTEXT: &str = "<!-- storkit:scaffold-template -->\n\
|
||||
# Project Context\n\
|
||||
\n\
|
||||
## High-Level Goal\n\
|
||||
@@ -30,7 +30,7 @@ TODO: Define the key domain concepts and entities.\n\
|
||||
\n\
|
||||
TODO: Define abbreviations and technical terms.\n";
|
||||
|
||||
const STORY_KIT_STACK: &str = "<!-- story-kit:scaffold-template -->\n\
|
||||
const STORY_KIT_STACK: &str = "<!-- storkit:scaffold-template -->\n\
|
||||
# Tech Stack & Constraints\n\
|
||||
\n\
|
||||
## Core Stack\n\
|
||||
@@ -51,7 +51,7 @@ TODO: List approved libraries and their purpose.\n";
|
||||
|
||||
const STORY_KIT_SCRIPT_TEST: &str = "#!/usr/bin/env bash\nset -euo pipefail\n\n# Add your project's test commands here.\n# Story Kit agents invoke this script as the canonical test runner.\n# Exit 0 on success, non-zero on failure.\necho \"No tests configured\"\n";
|
||||
|
||||
const STORY_KIT_CLAUDE_MD: &str = "<!-- story-kit:scaffold-template -->\n\
|
||||
const STORY_KIT_CLAUDE_MD: &str = "<!-- storkit:scaffold-template -->\n\
|
||||
Never chain shell commands with `&&`, `||`, or `;` in a single Bash call. \
|
||||
The permission system validates the entire command string, and chained commands \
|
||||
won't match allow rules like `Bash(git *)`. Use separate Bash calls instead — \
|
||||
@@ -90,7 +90,7 @@ const STORY_KIT_CLAUDE_SETTINGS: &str = r#"{
|
||||
"Bash(./script/test:*)",
|
||||
"Edit",
|
||||
"Write",
|
||||
"mcp__story-kit__*"
|
||||
"mcp__storkit__*"
|
||||
]
|
||||
},
|
||||
"enabledMcpjsonServers": [
|
||||
@@ -422,8 +422,7 @@ fn scaffold_story_kit(root: &Path) -> Result<(), String> {
|
||||
];
|
||||
for stage in &work_stages {
|
||||
let dir = story_kit_root.join("work").join(stage);
|
||||
fs::create_dir_all(&dir)
|
||||
.map_err(|e| format!("Failed to create work/{}: {}", stage, e))?;
|
||||
fs::create_dir_all(&dir).map_err(|e| format!("Failed to create work/{}: {}", stage, e))?;
|
||||
write_file_if_missing(&dir.join(".gitkeep"), "")?;
|
||||
}
|
||||
|
||||
@@ -464,7 +463,14 @@ fn scaffold_story_kit(root: &Path) -> Result<(), String> {
|
||||
}
|
||||
|
||||
let add_output = std::process::Command::new("git")
|
||||
.args(["add", ".storkit", "script", ".gitignore", "CLAUDE.md", ".claude"])
|
||||
.args([
|
||||
"add",
|
||||
".storkit",
|
||||
"script",
|
||||
".gitignore",
|
||||
"CLAUDE.md",
|
||||
".claude",
|
||||
])
|
||||
.current_dir(root)
|
||||
.output()
|
||||
.map_err(|e| format!("Failed to run git add: {}", e))?;
|
||||
@@ -478,7 +484,7 @@ fn scaffold_story_kit(root: &Path) -> Result<(), String> {
|
||||
let commit_output = std::process::Command::new("git")
|
||||
.args([
|
||||
"-c",
|
||||
"user.email=story-kit@localhost",
|
||||
"user.email=storkit@localhost",
|
||||
"-c",
|
||||
"user.name=Story Kit",
|
||||
"commit",
|
||||
@@ -526,7 +532,10 @@ pub async fn open_project(
|
||||
|
||||
{
|
||||
// TRACE:MERGE-DEBUG — remove once root cause is found
|
||||
crate::slog!("[MERGE-DEBUG] open_project: setting project_root to {:?}", p);
|
||||
crate::slog!(
|
||||
"[MERGE-DEBUG] open_project: setting project_root to {:?}",
|
||||
p
|
||||
);
|
||||
let mut root = state.project_root.lock().map_err(|e| e.to_string())?;
|
||||
*root = Some(p);
|
||||
}
|
||||
@@ -807,12 +816,7 @@ mod tests {
|
||||
let store = make_store(&dir);
|
||||
let state = SessionState::default();
|
||||
|
||||
let result = open_project(
|
||||
project_dir.to_string_lossy().to_string(),
|
||||
&state,
|
||||
&store,
|
||||
)
|
||||
.await;
|
||||
let result = open_project(project_dir.to_string_lossy().to_string(), &state, &store).await;
|
||||
|
||||
assert!(result.is_ok());
|
||||
let root = state.get_project_root().unwrap();
|
||||
@@ -831,13 +835,9 @@ mod tests {
|
||||
let store = make_store(&dir);
|
||||
let state = SessionState::default();
|
||||
|
||||
open_project(
|
||||
project_dir.to_string_lossy().to_string(),
|
||||
&state,
|
||||
&store,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
open_project(project_dir.to_string_lossy().to_string(), &state, &store)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mcp_path = project_dir.join(".mcp.json");
|
||||
assert!(
|
||||
@@ -898,13 +898,9 @@ mod tests {
|
||||
let store = make_store(&dir);
|
||||
let state = SessionState::default();
|
||||
|
||||
open_project(
|
||||
project_dir.to_string_lossy().to_string(),
|
||||
&state,
|
||||
&store,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
open_project(project_dir.to_string_lossy().to_string(), &state, &store)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let projects = get_known_projects(&store).unwrap();
|
||||
assert_eq!(projects.len(), 1);
|
||||
@@ -978,7 +974,9 @@ mod tests {
|
||||
let dir = tempdir().unwrap();
|
||||
let file = dir.path().join("sub").join("output.txt");
|
||||
|
||||
write_file_impl(file.clone(), "content".to_string()).await.unwrap();
|
||||
write_file_impl(file.clone(), "content".to_string())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(fs::read_to_string(&file).unwrap(), "content");
|
||||
}
|
||||
@@ -1089,7 +1087,14 @@ mod tests {
|
||||
let dir = tempdir().unwrap();
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let stages = ["1_backlog", "2_current", "3_qa", "4_merge", "5_done", "6_archived"];
|
||||
let stages = [
|
||||
"1_backlog",
|
||||
"2_current",
|
||||
"3_qa",
|
||||
"4_merge",
|
||||
"5_done",
|
||||
"6_archived",
|
||||
];
|
||||
for stage in &stages {
|
||||
let path = dir.path().join(".storkit/work").join(stage);
|
||||
assert!(path.is_dir(), "work/{} should be a directory", stage);
|
||||
@@ -1106,8 +1111,7 @@ mod tests {
|
||||
let dir = tempdir().unwrap();
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let content =
|
||||
fs::read_to_string(dir.path().join(".storkit/project.toml")).unwrap();
|
||||
let content = fs::read_to_string(dir.path().join(".storkit/project.toml")).unwrap();
|
||||
assert!(content.contains("[[agent]]"));
|
||||
assert!(content.contains("stage = \"coder\""));
|
||||
assert!(content.contains("stage = \"qa\""));
|
||||
@@ -1120,9 +1124,8 @@ mod tests {
|
||||
let dir = tempdir().unwrap();
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let content =
|
||||
fs::read_to_string(dir.path().join(".storkit/specs/00_CONTEXT.md")).unwrap();
|
||||
assert!(content.contains("<!-- story-kit:scaffold-template -->"));
|
||||
let content = fs::read_to_string(dir.path().join(".storkit/specs/00_CONTEXT.md")).unwrap();
|
||||
assert!(content.contains("<!-- storkit:scaffold-template -->"));
|
||||
assert!(content.contains("## High-Level Goal"));
|
||||
assert!(content.contains("## Core Features"));
|
||||
assert!(content.contains("## Domain Definition"));
|
||||
@@ -1137,9 +1140,8 @@ mod tests {
|
||||
let dir = tempdir().unwrap();
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let content =
|
||||
fs::read_to_string(dir.path().join(".storkit/specs/tech/STACK.md")).unwrap();
|
||||
assert!(content.contains("<!-- story-kit:scaffold-template -->"));
|
||||
let content = fs::read_to_string(dir.path().join(".storkit/specs/tech/STACK.md")).unwrap();
|
||||
assert!(content.contains("<!-- storkit:scaffold-template -->"));
|
||||
assert!(content.contains("## Core Stack"));
|
||||
assert!(content.contains("## Coding Standards"));
|
||||
assert!(content.contains("## Quality Gates"));
|
||||
@@ -1183,10 +1185,8 @@ mod tests {
|
||||
let dir = tempdir().unwrap();
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let readme_content =
|
||||
fs::read_to_string(dir.path().join(".storkit/README.md")).unwrap();
|
||||
let toml_content =
|
||||
fs::read_to_string(dir.path().join(".storkit/project.toml")).unwrap();
|
||||
let readme_content = fs::read_to_string(dir.path().join(".storkit/README.md")).unwrap();
|
||||
let toml_content = fs::read_to_string(dir.path().join(".storkit/project.toml")).unwrap();
|
||||
|
||||
// Run again — must not change content or add duplicate .gitignore entries
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
@@ -1207,8 +1207,7 @@ mod tests {
|
||||
.filter(|l| l.trim() == "worktrees/")
|
||||
.count();
|
||||
assert_eq!(
|
||||
count,
|
||||
1,
|
||||
count, 1,
|
||||
".storkit/.gitignore should not have duplicate entries"
|
||||
);
|
||||
}
|
||||
@@ -1249,8 +1248,7 @@ mod tests {
|
||||
let log = String::from_utf8_lossy(&log_output.stdout);
|
||||
let commit_count = log.lines().count();
|
||||
assert_eq!(
|
||||
commit_count,
|
||||
1,
|
||||
commit_count, 1,
|
||||
"scaffold should not create a commit in an existing git repo"
|
||||
);
|
||||
}
|
||||
@@ -1261,15 +1259,14 @@ mod tests {
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
// .storkit/.gitignore must contain relative patterns for files under .storkit/
|
||||
let sk_content =
|
||||
fs::read_to_string(dir.path().join(".storkit/.gitignore")).unwrap();
|
||||
let sk_content = fs::read_to_string(dir.path().join(".storkit/.gitignore")).unwrap();
|
||||
assert!(sk_content.contains("worktrees/"));
|
||||
assert!(sk_content.contains("merge_workspace/"));
|
||||
assert!(sk_content.contains("coverage/"));
|
||||
// Must NOT contain absolute .storkit/ prefixed paths
|
||||
assert!(!sk_content.contains(".storkit/"));
|
||||
|
||||
// Root .gitignore must contain root-level story-kit entries
|
||||
// Root .gitignore must contain root-level storkit entries
|
||||
let root_content = fs::read_to_string(dir.path().join(".gitignore")).unwrap();
|
||||
assert!(root_content.contains(".storkit_port"));
|
||||
assert!(root_content.contains("store.json"));
|
||||
@@ -1292,17 +1289,10 @@ mod tests {
|
||||
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let content =
|
||||
fs::read_to_string(dir.path().join(".storkit/.gitignore")).unwrap();
|
||||
let worktrees_count = content
|
||||
.lines()
|
||||
.filter(|l| l.trim() == "worktrees/")
|
||||
.count();
|
||||
let content = fs::read_to_string(dir.path().join(".storkit/.gitignore")).unwrap();
|
||||
let worktrees_count = content.lines().filter(|l| l.trim() == "worktrees/").count();
|
||||
assert_eq!(worktrees_count, 1, "worktrees/ should not be duplicated");
|
||||
let coverage_count = content
|
||||
.lines()
|
||||
.filter(|l| l.trim() == "coverage/")
|
||||
.count();
|
||||
let coverage_count = content.lines().filter(|l| l.trim() == "coverage/").count();
|
||||
assert_eq!(coverage_count, 1, "coverage/ should not be duplicated");
|
||||
// The missing entry must have been added
|
||||
assert!(content.contains("merge_workspace/"));
|
||||
@@ -1316,11 +1306,14 @@ mod tests {
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let claude_md = dir.path().join("CLAUDE.md");
|
||||
assert!(claude_md.exists(), "CLAUDE.md should be created at project root");
|
||||
assert!(
|
||||
claude_md.exists(),
|
||||
"CLAUDE.md should be created at project root"
|
||||
);
|
||||
|
||||
let content = fs::read_to_string(&claude_md).unwrap();
|
||||
assert!(
|
||||
content.contains("<!-- story-kit:scaffold-template -->"),
|
||||
content.contains("<!-- storkit:scaffold-template -->"),
|
||||
"CLAUDE.md should contain the scaffold sentinel"
|
||||
);
|
||||
assert!(
|
||||
@@ -1358,13 +1351,9 @@ mod tests {
|
||||
let store = make_store(&dir);
|
||||
let state = SessionState::default();
|
||||
|
||||
open_project(
|
||||
project_dir.to_string_lossy().to_string(),
|
||||
&state,
|
||||
&store,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
open_project(project_dir.to_string_lossy().to_string(), &state, &store)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// .storkit/ should have been created automatically
|
||||
assert!(project_dir.join(".storkit").is_dir());
|
||||
@@ -1381,13 +1370,9 @@ mod tests {
|
||||
let store = make_store(&dir);
|
||||
let state = SessionState::default();
|
||||
|
||||
open_project(
|
||||
project_dir.to_string_lossy().to_string(),
|
||||
&state,
|
||||
&store,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
open_project(project_dir.to_string_lossy().to_string(), &state, &store)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Existing .storkit/ content should not be overwritten
|
||||
assert_eq!(fs::read_to_string(&readme).unwrap(), "custom content");
|
||||
@@ -1451,7 +1436,11 @@ mod tests {
|
||||
#[test]
|
||||
fn detect_cargo_toml_generates_rust_component() {
|
||||
let dir = tempdir().unwrap();
|
||||
fs::write(dir.path().join("Cargo.toml"), "[package]\nname = \"test\"\n").unwrap();
|
||||
fs::write(
|
||||
dir.path().join("Cargo.toml"),
|
||||
"[package]\nname = \"test\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let toml = detect_components_toml(dir.path());
|
||||
assert!(toml.contains("name = \"server\""));
|
||||
@@ -1482,7 +1471,11 @@ mod tests {
|
||||
#[test]
|
||||
fn detect_pyproject_toml_generates_python_component() {
|
||||
let dir = tempdir().unwrap();
|
||||
fs::write(dir.path().join("pyproject.toml"), "[project]\nname = \"test\"\n").unwrap();
|
||||
fs::write(
|
||||
dir.path().join("pyproject.toml"),
|
||||
"[project]\nname = \"test\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let toml = detect_components_toml(dir.path());
|
||||
assert!(toml.contains("name = \"python\""));
|
||||
@@ -1512,7 +1505,11 @@ mod tests {
|
||||
#[test]
|
||||
fn detect_gemfile_generates_ruby_component() {
|
||||
let dir = tempdir().unwrap();
|
||||
fs::write(dir.path().join("Gemfile"), "source \"https://rubygems.org\"\n").unwrap();
|
||||
fs::write(
|
||||
dir.path().join("Gemfile"),
|
||||
"source \"https://rubygems.org\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let toml = detect_components_toml(dir.path());
|
||||
assert!(toml.contains("name = \"ruby\""));
|
||||
@@ -1522,7 +1519,11 @@ mod tests {
|
||||
#[test]
|
||||
fn detect_multiple_markers_generates_multiple_components() {
|
||||
let dir = tempdir().unwrap();
|
||||
fs::write(dir.path().join("Cargo.toml"), "[package]\nname = \"server\"\n").unwrap();
|
||||
fs::write(
|
||||
dir.path().join("Cargo.toml"),
|
||||
"[package]\nname = \"server\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
fs::write(dir.path().join("package.json"), "{}").unwrap();
|
||||
|
||||
let toml = detect_components_toml(dir.path());
|
||||
@@ -1565,12 +1566,15 @@ mod tests {
|
||||
fn scaffold_project_toml_contains_detected_components() {
|
||||
let dir = tempdir().unwrap();
|
||||
// Place a Cargo.toml in the project root before scaffolding
|
||||
fs::write(dir.path().join("Cargo.toml"), "[package]\nname = \"myapp\"\n").unwrap();
|
||||
fs::write(
|
||||
dir.path().join("Cargo.toml"),
|
||||
"[package]\nname = \"myapp\"\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let content =
|
||||
fs::read_to_string(dir.path().join(".storkit/project.toml")).unwrap();
|
||||
let content = fs::read_to_string(dir.path().join(".storkit/project.toml")).unwrap();
|
||||
assert!(
|
||||
content.contains("[[component]]"),
|
||||
"project.toml should contain a component entry"
|
||||
@@ -1590,8 +1594,7 @@ mod tests {
|
||||
let dir = tempdir().unwrap();
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let content =
|
||||
fs::read_to_string(dir.path().join(".storkit/project.toml")).unwrap();
|
||||
let content = fs::read_to_string(dir.path().join(".storkit/project.toml")).unwrap();
|
||||
assert!(
|
||||
content.contains("[[component]]"),
|
||||
"project.toml should always have at least one component"
|
||||
|
||||
Reference in New Issue
Block a user