rename .story_kit directory to .storkit and update all references
Renames the config directory and updates 514 references across 42 Rust source files, plus CLAUDE.md, .gitignore, Makefile, script/release, and .mcp.json files. All 1205 tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,7 +9,7 @@ const KEY_LAST_PROJECT: &str = "last_project_path";
|
||||
const KEY_SELECTED_MODEL: &str = "selected_model";
|
||||
const KEY_KNOWN_PROJECTS: &str = "known_projects";
|
||||
|
||||
const STORY_KIT_README: &str = include_str!("../../../.story_kit/README.md");
|
||||
const STORY_KIT_README: &str = include_str!("../../../.storkit/README.md");
|
||||
|
||||
const STORY_KIT_CONTEXT: &str = "<!-- story-kit:scaffold-template -->\n\
|
||||
# Project Context\n\
|
||||
@@ -57,7 +57,7 @@ The permission system validates the entire command string, and chained commands
|
||||
won't match allow rules like `Bash(git *)`. Use separate Bash calls instead — \
|
||||
parallel calls work fine.\n\
|
||||
\n\
|
||||
Read .story_kit/README.md to see our dev process.\n";
|
||||
Read .storkit/README.md to see our dev process.\n";
|
||||
|
||||
const STORY_KIT_CLAUDE_SETTINGS: &str = r#"{
|
||||
"permissions": {
|
||||
@@ -110,7 +110,7 @@ role = "Full-stack engineer. Implements features across all components."
|
||||
model = "sonnet"
|
||||
max_turns = 50
|
||||
max_budget_usd = 5.00
|
||||
prompt = "You are working in a git worktree on story {{story_id}}. Read CLAUDE.md first, then .story_kit/README.md to understand the dev process. Follow the workflow through implementation and verification. The worktree and feature branch already exist - do not create them. Check .mcp.json for MCP tools. Do NOT accept the story or merge - commit your work and stop.\n\nIMPORTANT: Commit all your work before your process exits. The server will automatically run acceptance gates when your process exits."
|
||||
prompt = "You are working in a git worktree on story {{story_id}}. Read CLAUDE.md first, then .storkit/README.md to understand the dev process. Follow the workflow through implementation and verification. The worktree and feature branch already exist - do not create them. Check .mcp.json for MCP tools. Do NOT accept the story or merge - commit your work and stop.\n\nIMPORTANT: Commit all your work before your process exits. The server will automatically run acceptance gates when your process exits."
|
||||
system_prompt = "You are a full-stack engineer working autonomously in a git worktree. Follow the Story-Driven Test Workflow strictly. Commit all your work before finishing. Do not accept stories, move them to archived, or merge to master."
|
||||
|
||||
[[agent]]
|
||||
@@ -238,11 +238,11 @@ pub fn resolve_cli_path(cwd: &Path, path_arg: &str) -> PathBuf {
|
||||
}
|
||||
|
||||
/// Walk from `start` up through parent directories, returning the first
|
||||
/// directory that contains a `.story_kit/` subdirectory, or `None`.
|
||||
/// directory that contains a `.storkit/` subdirectory, or `None`.
|
||||
pub fn find_story_kit_root(start: &Path) -> Option<PathBuf> {
|
||||
let mut current = start.to_path_buf();
|
||||
loop {
|
||||
if current.join(".story_kit").is_dir() {
|
||||
if current.join(".storkit").is_dir() {
|
||||
return Some(current);
|
||||
}
|
||||
if !current.pop() {
|
||||
@@ -316,12 +316,12 @@ fn write_script_if_missing(path: &Path, content: &str) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Write (or idempotently update) `.story_kit/.gitignore` with Story Kit–specific
|
||||
/// ignore patterns for files that live inside the `.story_kit/` directory.
|
||||
/// Patterns are relative to `.story_kit/` as git resolves `.gitignore` files
|
||||
/// Write (or idempotently update) `.storkit/.gitignore` with Story Kit–specific
|
||||
/// ignore patterns for files that live inside the `.storkit/` directory.
|
||||
/// Patterns are relative to `.storkit/` as git resolves `.gitignore` files
|
||||
/// relative to the directory that contains them.
|
||||
fn write_story_kit_gitignore(root: &Path) -> Result<(), String> {
|
||||
// Entries that belong inside .story_kit/.gitignore (relative to .story_kit/).
|
||||
// Entries that belong inside .storkit/.gitignore (relative to .storkit/).
|
||||
let entries = [
|
||||
"bot.toml",
|
||||
"matrix_store/",
|
||||
@@ -331,10 +331,10 @@ fn write_story_kit_gitignore(root: &Path) -> Result<(), String> {
|
||||
"coverage/",
|
||||
];
|
||||
|
||||
let gitignore_path = root.join(".story_kit").join(".gitignore");
|
||||
let gitignore_path = root.join(".storkit").join(".gitignore");
|
||||
let existing = if gitignore_path.exists() {
|
||||
fs::read_to_string(&gitignore_path)
|
||||
.map_err(|e| format!("Failed to read .story_kit/.gitignore: {}", e))?
|
||||
.map_err(|e| format!("Failed to read .storkit/.gitignore: {}", e))?
|
||||
} else {
|
||||
String::new()
|
||||
};
|
||||
@@ -359,17 +359,17 @@ fn write_story_kit_gitignore(root: &Path) -> Result<(), String> {
|
||||
}
|
||||
|
||||
fs::write(&gitignore_path, new_content)
|
||||
.map_err(|e| format!("Failed to write .story_kit/.gitignore: {}", e))?;
|
||||
.map_err(|e| format!("Failed to write .storkit/.gitignore: {}", e))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Append root-level Story Kit entries to the project `.gitignore`.
|
||||
/// Only `store.json` and `.story_kit_port` remain here because they live at
|
||||
/// Only `store.json` and `.storkit_port` remain here because they live at
|
||||
/// the project root and git does not support `../` patterns in `.gitignore`
|
||||
/// files, so they cannot be expressed in `.story_kit/.gitignore`.
|
||||
/// files, so they cannot be expressed in `.storkit/.gitignore`.
|
||||
fn append_root_gitignore_entries(root: &Path) -> Result<(), String> {
|
||||
let entries = [".story_kit_port", "store.json"];
|
||||
let entries = [".storkit_port", "store.json"];
|
||||
|
||||
let gitignore_path = root.join(".gitignore");
|
||||
let existing = if gitignore_path.exists() {
|
||||
@@ -405,7 +405,7 @@ fn append_root_gitignore_entries(root: &Path) -> Result<(), String> {
|
||||
}
|
||||
|
||||
fn scaffold_story_kit(root: &Path) -> Result<(), String> {
|
||||
let story_kit_root = root.join(".story_kit");
|
||||
let story_kit_root = root.join(".storkit");
|
||||
let specs_root = story_kit_root.join("specs");
|
||||
let tech_root = specs_root.join("tech");
|
||||
let functional_root = specs_root.join("functional");
|
||||
@@ -464,7 +464,7 @@ fn scaffold_story_kit(root: &Path) -> Result<(), String> {
|
||||
}
|
||||
|
||||
let add_output = std::process::Command::new("git")
|
||||
.args(["add", ".story_kit", "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))?;
|
||||
@@ -505,7 +505,7 @@ async fn ensure_project_root_with_story_kit(path: PathBuf) -> Result<(), String>
|
||||
fs::create_dir_all(&path)
|
||||
.map_err(|e| format!("Failed to create project directory: {}", e))?;
|
||||
}
|
||||
if !path.join(".story_kit").is_dir() {
|
||||
if !path.join(".storkit").is_dir() {
|
||||
scaffold_story_kit(&path)?;
|
||||
}
|
||||
Ok(())
|
||||
@@ -1032,7 +1032,7 @@ mod tests {
|
||||
#[test]
|
||||
fn find_story_kit_root_returns_cwd_when_story_kit_in_cwd() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
std::fs::create_dir_all(tmp.path().join(".story_kit")).unwrap();
|
||||
std::fs::create_dir_all(tmp.path().join(".storkit")).unwrap();
|
||||
|
||||
let result = find_story_kit_root(tmp.path());
|
||||
assert_eq!(result, Some(tmp.path().to_path_buf()));
|
||||
@@ -1041,7 +1041,7 @@ mod tests {
|
||||
#[test]
|
||||
fn find_story_kit_root_returns_parent_when_story_kit_in_parent() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
std::fs::create_dir_all(tmp.path().join(".story_kit")).unwrap();
|
||||
std::fs::create_dir_all(tmp.path().join(".storkit")).unwrap();
|
||||
let child = tmp.path().join("subdir").join("nested");
|
||||
std::fs::create_dir_all(&child).unwrap();
|
||||
|
||||
@@ -1060,9 +1060,9 @@ mod tests {
|
||||
#[test]
|
||||
fn find_story_kit_root_prefers_nearest_ancestor() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
std::fs::create_dir_all(tmp.path().join(".story_kit")).unwrap();
|
||||
std::fs::create_dir_all(tmp.path().join(".storkit")).unwrap();
|
||||
let child = tmp.path().join("inner");
|
||||
std::fs::create_dir_all(child.join(".story_kit")).unwrap();
|
||||
std::fs::create_dir_all(child.join(".storkit")).unwrap();
|
||||
|
||||
let result = find_story_kit_root(&child);
|
||||
assert_eq!(result, Some(child));
|
||||
@@ -1075,12 +1075,12 @@ mod tests {
|
||||
let dir = tempdir().unwrap();
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
assert!(dir.path().join(".story_kit/README.md").exists());
|
||||
assert!(dir.path().join(".story_kit/project.toml").exists());
|
||||
assert!(dir.path().join(".story_kit/specs/00_CONTEXT.md").exists());
|
||||
assert!(dir.path().join(".story_kit/specs/tech/STACK.md").exists());
|
||||
assert!(dir.path().join(".storkit/README.md").exists());
|
||||
assert!(dir.path().join(".storkit/project.toml").exists());
|
||||
assert!(dir.path().join(".storkit/specs/00_CONTEXT.md").exists());
|
||||
assert!(dir.path().join(".storkit/specs/tech/STACK.md").exists());
|
||||
// Old stories/ dirs should NOT be created
|
||||
assert!(!dir.path().join(".story_kit/stories").exists());
|
||||
assert!(!dir.path().join(".storkit/stories").exists());
|
||||
assert!(dir.path().join("script/test").exists());
|
||||
}
|
||||
|
||||
@@ -1091,7 +1091,7 @@ mod tests {
|
||||
|
||||
let stages = ["1_backlog", "2_current", "3_qa", "4_merge", "5_done", "6_archived"];
|
||||
for stage in &stages {
|
||||
let path = dir.path().join(".story_kit/work").join(stage);
|
||||
let path = dir.path().join(".storkit/work").join(stage);
|
||||
assert!(path.is_dir(), "work/{} should be a directory", stage);
|
||||
assert!(
|
||||
path.join(".gitkeep").exists(),
|
||||
@@ -1107,7 +1107,7 @@ mod tests {
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let content =
|
||||
fs::read_to_string(dir.path().join(".story_kit/project.toml")).unwrap();
|
||||
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\""));
|
||||
@@ -1121,7 +1121,7 @@ mod tests {
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let content =
|
||||
fs::read_to_string(dir.path().join(".story_kit/specs/00_CONTEXT.md")).unwrap();
|
||||
fs::read_to_string(dir.path().join(".storkit/specs/00_CONTEXT.md")).unwrap();
|
||||
assert!(content.contains("<!-- story-kit:scaffold-template -->"));
|
||||
assert!(content.contains("## High-Level Goal"));
|
||||
assert!(content.contains("## Core Features"));
|
||||
@@ -1138,7 +1138,7 @@ mod tests {
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let content =
|
||||
fs::read_to_string(dir.path().join(".story_kit/specs/tech/STACK.md")).unwrap();
|
||||
fs::read_to_string(dir.path().join(".storkit/specs/tech/STACK.md")).unwrap();
|
||||
assert!(content.contains("<!-- story-kit:scaffold-template -->"));
|
||||
assert!(content.contains("## Core Stack"));
|
||||
assert!(content.contains("## Coding Standards"));
|
||||
@@ -1169,7 +1169,7 @@ mod tests {
|
||||
#[test]
|
||||
fn scaffold_story_kit_does_not_overwrite_existing() {
|
||||
let dir = tempdir().unwrap();
|
||||
let readme = dir.path().join(".story_kit/README.md");
|
||||
let readme = dir.path().join(".storkit/README.md");
|
||||
fs::create_dir_all(readme.parent().unwrap()).unwrap();
|
||||
fs::write(&readme, "custom content").unwrap();
|
||||
|
||||
@@ -1184,24 +1184,24 @@ mod tests {
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let readme_content =
|
||||
fs::read_to_string(dir.path().join(".story_kit/README.md")).unwrap();
|
||||
fs::read_to_string(dir.path().join(".storkit/README.md")).unwrap();
|
||||
let toml_content =
|
||||
fs::read_to_string(dir.path().join(".story_kit/project.toml")).unwrap();
|
||||
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();
|
||||
|
||||
assert_eq!(
|
||||
fs::read_to_string(dir.path().join(".story_kit/README.md")).unwrap(),
|
||||
fs::read_to_string(dir.path().join(".storkit/README.md")).unwrap(),
|
||||
readme_content
|
||||
);
|
||||
assert_eq!(
|
||||
fs::read_to_string(dir.path().join(".story_kit/project.toml")).unwrap(),
|
||||
fs::read_to_string(dir.path().join(".storkit/project.toml")).unwrap(),
|
||||
toml_content
|
||||
);
|
||||
|
||||
let story_kit_gitignore =
|
||||
fs::read_to_string(dir.path().join(".story_kit/.gitignore")).unwrap();
|
||||
fs::read_to_string(dir.path().join(".storkit/.gitignore")).unwrap();
|
||||
let count = story_kit_gitignore
|
||||
.lines()
|
||||
.filter(|l| l.trim() == "worktrees/")
|
||||
@@ -1209,7 +1209,7 @@ mod tests {
|
||||
assert_eq!(
|
||||
count,
|
||||
1,
|
||||
".story_kit/.gitignore should not have duplicate entries"
|
||||
".storkit/.gitignore should not have duplicate entries"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1260,32 +1260,32 @@ mod tests {
|
||||
let dir = tempdir().unwrap();
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
// .story_kit/.gitignore must contain relative patterns for files under .story_kit/
|
||||
// .storkit/.gitignore must contain relative patterns for files under .storkit/
|
||||
let sk_content =
|
||||
fs::read_to_string(dir.path().join(".story_kit/.gitignore")).unwrap();
|
||||
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 .story_kit/ prefixed paths
|
||||
assert!(!sk_content.contains(".story_kit/"));
|
||||
// Must NOT contain absolute .storkit/ prefixed paths
|
||||
assert!(!sk_content.contains(".storkit/"));
|
||||
|
||||
// Root .gitignore must contain root-level story-kit entries
|
||||
let root_content = fs::read_to_string(dir.path().join(".gitignore")).unwrap();
|
||||
assert!(root_content.contains(".story_kit_port"));
|
||||
assert!(root_content.contains(".storkit_port"));
|
||||
assert!(root_content.contains("store.json"));
|
||||
// Root .gitignore must NOT contain .story_kit/ sub-directory patterns
|
||||
assert!(!root_content.contains(".story_kit/worktrees/"));
|
||||
assert!(!root_content.contains(".story_kit/merge_workspace/"));
|
||||
assert!(!root_content.contains(".story_kit/coverage/"));
|
||||
// Root .gitignore must NOT contain .storkit/ sub-directory patterns
|
||||
assert!(!root_content.contains(".storkit/worktrees/"));
|
||||
assert!(!root_content.contains(".storkit/merge_workspace/"));
|
||||
assert!(!root_content.contains(".storkit/coverage/"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn scaffold_story_kit_gitignore_does_not_duplicate_existing_entries() {
|
||||
let dir = tempdir().unwrap();
|
||||
// Pre-create .story_kit dir and .gitignore with some entries already present
|
||||
fs::create_dir_all(dir.path().join(".story_kit")).unwrap();
|
||||
// Pre-create .storkit dir and .gitignore with some entries already present
|
||||
fs::create_dir_all(dir.path().join(".storkit")).unwrap();
|
||||
fs::write(
|
||||
dir.path().join(".story_kit/.gitignore"),
|
||||
dir.path().join(".storkit/.gitignore"),
|
||||
"worktrees/\ncoverage/\n",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1293,7 +1293,7 @@ mod tests {
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let content =
|
||||
fs::read_to_string(dir.path().join(".story_kit/.gitignore")).unwrap();
|
||||
fs::read_to_string(dir.path().join(".storkit/.gitignore")).unwrap();
|
||||
let worktrees_count = content
|
||||
.lines()
|
||||
.filter(|l| l.trim() == "worktrees/")
|
||||
@@ -1324,8 +1324,8 @@ mod tests {
|
||||
"CLAUDE.md should contain the scaffold sentinel"
|
||||
);
|
||||
assert!(
|
||||
content.contains("Read .story_kit/README.md"),
|
||||
"CLAUDE.md should include directive to read .story_kit/README.md"
|
||||
content.contains("Read .storkit/README.md"),
|
||||
"CLAUDE.md should include directive to read .storkit/README.md"
|
||||
);
|
||||
assert!(
|
||||
content.contains("Never chain shell commands"),
|
||||
@@ -1366,15 +1366,15 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// .story_kit/ should have been created automatically
|
||||
assert!(project_dir.join(".story_kit").is_dir());
|
||||
// .storkit/ should have been created automatically
|
||||
assert!(project_dir.join(".storkit").is_dir());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn open_project_does_not_overwrite_existing_story_kit() {
|
||||
let dir = tempdir().unwrap();
|
||||
let project_dir = dir.path().join("myproject");
|
||||
let sk_dir = project_dir.join(".story_kit");
|
||||
let sk_dir = project_dir.join(".storkit");
|
||||
fs::create_dir_all(&sk_dir).unwrap();
|
||||
let readme = sk_dir.join("README.md");
|
||||
fs::write(&readme, "custom content").unwrap();
|
||||
@@ -1389,7 +1389,7 @@ mod tests {
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Existing .story_kit/ content should not be overwritten
|
||||
// Existing .storkit/ content should not be overwritten
|
||||
assert_eq!(fs::read_to_string(&readme).unwrap(), "custom content");
|
||||
}
|
||||
|
||||
@@ -1570,7 +1570,7 @@ mod tests {
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let content =
|
||||
fs::read_to_string(dir.path().join(".story_kit/project.toml")).unwrap();
|
||||
fs::read_to_string(dir.path().join(".storkit/project.toml")).unwrap();
|
||||
assert!(
|
||||
content.contains("[[component]]"),
|
||||
"project.toml should contain a component entry"
|
||||
@@ -1591,7 +1591,7 @@ mod tests {
|
||||
scaffold_story_kit(dir.path()).unwrap();
|
||||
|
||||
let content =
|
||||
fs::read_to_string(dir.path().join(".story_kit/project.toml")).unwrap();
|
||||
fs::read_to_string(dir.path().join(".storkit/project.toml")).unwrap();
|
||||
assert!(
|
||||
content.contains("[[component]]"),
|
||||
"project.toml should always have at least one component"
|
||||
@@ -1606,7 +1606,7 @@ mod tests {
|
||||
#[test]
|
||||
fn scaffold_does_not_overwrite_existing_project_toml_with_components() {
|
||||
let dir = tempdir().unwrap();
|
||||
let sk_dir = dir.path().join(".story_kit");
|
||||
let sk_dir = dir.path().join(".storkit");
|
||||
fs::create_dir_all(&sk_dir).unwrap();
|
||||
let existing = "[[component]]\nname = \"custom\"\npath = \".\"\nsetup = [\"make build\"]\n";
|
||||
fs::write(sk_dir.join("project.toml"), existing).unwrap();
|
||||
|
||||
Reference in New Issue
Block a user