huskies: rename project from storkit to huskies
Rename all references from storkit to huskies across the codebase: - .storkit/ directory → .huskies/ - Binary name, Cargo package name, Docker image references - Server code, frontend code, config files, scripts - Fix script/test to build frontend before cargo clippy/test so merge worktrees have frontend/dist available for RustEmbed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
+16
-16
@@ -158,7 +158,7 @@ struct AllTokenUsageResponse {
|
||||
/// response so the agents panel is not cluttered with old completed items on
|
||||
/// frontend startup.
|
||||
pub fn story_is_archived(project_root: &path::Path, story_id: &str) -> bool {
|
||||
let work = project_root.join(".storkit").join("work");
|
||||
let work = project_root.join(".huskies").join("work");
|
||||
let filename = format!("{story_id}.md");
|
||||
work.join("5_done").join(&filename).exists() || work.join("6_archived").join(&filename).exists()
|
||||
}
|
||||
@@ -307,7 +307,7 @@ impl AgentsApi {
|
||||
))
|
||||
}
|
||||
|
||||
/// Create a git worktree for a story under .storkit/worktrees/{story_id}.
|
||||
/// Create a git worktree for a story under .huskies/worktrees/{story_id}.
|
||||
#[oai(path = "/agents/worktrees", method = "post")]
|
||||
async fn create_worktree(
|
||||
&self,
|
||||
@@ -334,7 +334,7 @@ impl AgentsApi {
|
||||
}))
|
||||
}
|
||||
|
||||
/// List all worktrees under .storkit/worktrees/.
|
||||
/// List all worktrees under .huskies/worktrees/.
|
||||
#[oai(path = "/agents/worktrees", method = "get")]
|
||||
async fn list_worktrees(&self) -> OpenApiResult<Json<Vec<WorktreeListEntry>>> {
|
||||
let project_root = self
|
||||
@@ -380,7 +380,7 @@ impl AgentsApi {
|
||||
("6_archived", "archived"),
|
||||
];
|
||||
|
||||
let work_dir = project_root.join(".storkit").join("work");
|
||||
let work_dir = project_root.join(".huskies").join("work");
|
||||
let filename = format!("{}.md", story_id.0);
|
||||
|
||||
for (stage_dir, stage_name) in &stages {
|
||||
@@ -593,7 +593,7 @@ mod tests {
|
||||
fn make_work_dirs(tmp: &TempDir) -> path::PathBuf {
|
||||
let root = tmp.path().to_path_buf();
|
||||
for stage in &["5_done", "6_archived"] {
|
||||
std::fs::create_dir_all(root.join(".storkit").join("work").join(stage)).unwrap();
|
||||
std::fs::create_dir_all(root.join(".huskies").join("work").join(stage)).unwrap();
|
||||
}
|
||||
root
|
||||
}
|
||||
@@ -610,7 +610,7 @@ mod tests {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let root = make_work_dirs(&tmp);
|
||||
std::fs::write(
|
||||
root.join(".storkit/work/5_done/79_story_foo.md"),
|
||||
root.join(".huskies/work/5_done/79_story_foo.md"),
|
||||
"---\nname: test\n---\n",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -622,7 +622,7 @@ mod tests {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let root = make_work_dirs(&tmp);
|
||||
std::fs::write(
|
||||
root.join(".storkit/work/6_archived/79_story_foo.md"),
|
||||
root.join(".huskies/work/6_archived/79_story_foo.md"),
|
||||
"---\nname: test\n---\n",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -636,7 +636,7 @@ mod tests {
|
||||
|
||||
// Place an archived story file in 6_archived
|
||||
std::fs::write(
|
||||
root.join(".storkit/work/6_archived/79_story_archived.md"),
|
||||
root.join(".huskies/work/6_archived/79_story_archived.md"),
|
||||
"---\nname: archived story\n---\n",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -679,7 +679,7 @@ mod tests {
|
||||
}
|
||||
|
||||
fn make_project_toml(root: &path::Path, content: &str) {
|
||||
let sk_dir = root.join(".storkit");
|
||||
let sk_dir = root.join(".huskies");
|
||||
std::fs::create_dir_all(&sk_dir).unwrap();
|
||||
std::fs::write(sk_dir.join("project.toml"), content).unwrap();
|
||||
}
|
||||
@@ -801,7 +801,7 @@ allowed_tools = ["Read", "Bash"]
|
||||
#[tokio::test]
|
||||
async fn list_worktrees_returns_entries_from_dir() {
|
||||
let tmp = TempDir::new().unwrap();
|
||||
let worktrees_dir = tmp.path().join(".storkit").join("worktrees");
|
||||
let worktrees_dir = tmp.path().join(".huskies").join("worktrees");
|
||||
std::fs::create_dir_all(worktrees_dir.join("42_story_foo")).unwrap();
|
||||
std::fs::create_dir_all(worktrees_dir.join("43_story_bar")).unwrap();
|
||||
|
||||
@@ -894,7 +894,7 @@ allowed_tools = ["Read", "Bash"]
|
||||
// --- get_work_item_content tests ---
|
||||
|
||||
fn make_stage_dir(root: &path::Path, stage: &str) {
|
||||
std::fs::create_dir_all(root.join(".storkit").join("work").join(stage)).unwrap();
|
||||
std::fs::create_dir_all(root.join(".huskies").join("work").join(stage)).unwrap();
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
@@ -903,7 +903,7 @@ allowed_tools = ["Read", "Bash"]
|
||||
let root = tmp.path();
|
||||
make_stage_dir(root, "1_backlog");
|
||||
std::fs::write(
|
||||
root.join(".storkit/work/1_backlog/42_story_foo.md"),
|
||||
root.join(".huskies/work/1_backlog/42_story_foo.md"),
|
||||
"---\nname: \"Foo Story\"\n---\n\n# Story 42: Foo Story\n\nSome content.",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -925,7 +925,7 @@ allowed_tools = ["Read", "Bash"]
|
||||
let root = tmp.path();
|
||||
make_stage_dir(root, "2_current");
|
||||
std::fs::write(
|
||||
root.join(".storkit/work/2_current/43_story_bar.md"),
|
||||
root.join(".huskies/work/2_current/43_story_bar.md"),
|
||||
"---\nname: \"Bar Story\"\n---\n\nBar content.",
|
||||
)
|
||||
.unwrap();
|
||||
@@ -1174,7 +1174,7 @@ allowed_tools = ["Read", "Bash"]
|
||||
let root = tmp.path().to_path_buf();
|
||||
// Create work dirs including 2_current for the story file.
|
||||
for stage in &["1_backlog", "2_current", "5_done", "6_archived"] {
|
||||
std::fs::create_dir_all(root.join(".storkit").join("work").join(stage)).unwrap();
|
||||
std::fs::create_dir_all(root.join(".huskies").join("work").join(stage)).unwrap();
|
||||
}
|
||||
|
||||
// Write a story file with persisted test results.
|
||||
@@ -1185,10 +1185,10 @@ name: "Test story"
|
||||
|
||||
## Test Results
|
||||
|
||||
<!-- storkit-test-results: {"unit":[{"name":"from_file","status":"pass","details":null}],"integration":[]} -->
|
||||
<!-- huskies-test-results: {"unit":[{"name":"from_file","status":"pass","details":null}],"integration":[]} -->
|
||||
"#;
|
||||
std::fs::write(
|
||||
root.join(".storkit/work/2_current/42_story_foo.md"),
|
||||
root.join(".huskies/work/2_current/42_story_foo.md"),
|
||||
story_content,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -74,7 +74,7 @@ impl AppContext {
|
||||
pub fn new_test(project_root: std::path::PathBuf) -> Self {
|
||||
let state = SessionState::default();
|
||||
*state.project_root.lock().unwrap() = Some(project_root.clone());
|
||||
let store_path = project_root.join(".storkit_store.json");
|
||||
let store_path = project_root.join(".huskies_store.json");
|
||||
let (watcher_tx, _) = broadcast::channel(64);
|
||||
let (reconciliation_tx, _) = broadcast::channel(64);
|
||||
let (perm_tx, perm_rx) = mpsc::unbounded_channel();
|
||||
|
||||
@@ -42,11 +42,11 @@ pub(super) async fn tool_start_agent(args: &Value, ctx: &AppContext) -> Result<S
|
||||
|
||||
/// Try to read the overall line coverage percentage from the llvm-cov JSON report.
|
||||
///
|
||||
/// Expects the file at `{project_root}/.storkit/coverage/server.json`.
|
||||
/// Expects the file at `{project_root}/.huskies/coverage/server.json`.
|
||||
/// Returns `None` if the file is absent, unreadable, or cannot be parsed.
|
||||
pub(super) fn read_coverage_percent_from_json(project_root: &std::path::Path) -> Option<f64> {
|
||||
let path = project_root
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("coverage")
|
||||
.join("server.json");
|
||||
let contents = std::fs::read_to_string(&path).ok()?;
|
||||
@@ -485,7 +485,7 @@ mod tests {
|
||||
// Config has only a supervisor — start_agent without agent_name should
|
||||
// refuse rather than silently assigning supervisor.
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let sk = tmp.path().join(".storkit");
|
||||
let sk = tmp.path().join(".huskies");
|
||||
std::fs::create_dir_all(&sk).unwrap();
|
||||
std::fs::write(
|
||||
sk.join("project.toml"),
|
||||
@@ -513,7 +513,7 @@ stage = "other"
|
||||
// missing git repo / worktree, but the error must NOT be about
|
||||
// "No coder agent configured".
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let sk = tmp.path().join(".storkit");
|
||||
let sk = tmp.path().join(".huskies");
|
||||
std::fs::create_dir_all(&sk).unwrap();
|
||||
std::fs::write(
|
||||
sk.join("project.toml"),
|
||||
@@ -709,7 +709,7 @@ stage = "coder"
|
||||
fn read_coverage_percent_from_json_parses_llvm_cov_format() {
|
||||
use std::fs;
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let cov_dir = tmp.path().join(".storkit/coverage");
|
||||
let cov_dir = tmp.path().join(".huskies/coverage");
|
||||
fs::create_dir_all(&cov_dir).unwrap();
|
||||
let json_content = r#"{"data":[{"totals":{"lines":{"count":100,"covered":78,"percent":78.0}}}]}"#;
|
||||
fs::write(cov_dir.join("server.json"), json_content).unwrap();
|
||||
|
||||
@@ -47,7 +47,7 @@ pub(super) async fn tool_rebuild_and_restart(ctx: &AppContext) -> Result<String,
|
||||
///
|
||||
/// - `Edit` / `Write` / `Read` / `Grep` / `Glob` etc. → just the tool name
|
||||
/// - `Bash` → `Bash(first_word *)` derived from the `command` field in `tool_input`
|
||||
/// - `mcp__*` → the full tool name (e.g. `mcp__storkit__create_story`)
|
||||
/// - `mcp__*` → the full tool name (e.g. `mcp__huskies__create_story`)
|
||||
fn generate_permission_rule(tool_name: &str, tool_input: &Value) -> String {
|
||||
if tool_name == "Bash" {
|
||||
// Extract command from tool_input.command and use first word as prefix
|
||||
@@ -109,8 +109,8 @@ pub(super) fn add_permission_rule(
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Also check for wildcard coverage: if "mcp__storkit__*" exists, don't add
|
||||
// a more specific "mcp__storkit__create_story".
|
||||
// Also check for wildcard coverage: if "mcp__huskies__*" exists, don't add
|
||||
// a more specific "mcp__huskies__create_story".
|
||||
let dominated = allow.iter().any(|existing| {
|
||||
if let Some(pat) = existing.as_str()
|
||||
&& let Some(prefix) = pat.strip_suffix('*')
|
||||
@@ -470,8 +470,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn generate_rule_for_mcp_tool() {
|
||||
let rule = generate_permission_rule("mcp__storkit__create_story", &json!({"name": "foo"}));
|
||||
assert_eq!(rule, "mcp__storkit__create_story");
|
||||
let rule = generate_permission_rule("mcp__huskies__create_story", &json!({"name": "foo"}));
|
||||
assert_eq!(rule, "mcp__huskies__create_story");
|
||||
}
|
||||
|
||||
// ── Settings.json writing tests ──────────────────────────────
|
||||
@@ -507,17 +507,17 @@ mod tests {
|
||||
fs::create_dir_all(&claude_dir).unwrap();
|
||||
fs::write(
|
||||
claude_dir.join("settings.json"),
|
||||
r#"{"permissions":{"allow":["mcp__storkit__*"]}}"#,
|
||||
r#"{"permissions":{"allow":["mcp__huskies__*"]}}"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
add_permission_rule(tmp.path(), "mcp__storkit__create_story").unwrap();
|
||||
add_permission_rule(tmp.path(), "mcp__huskies__create_story").unwrap();
|
||||
|
||||
let content = fs::read_to_string(claude_dir.join("settings.json")).unwrap();
|
||||
let settings: Value = serde_json::from_str(&content).unwrap();
|
||||
let allow = settings["permissions"]["allow"].as_array().unwrap();
|
||||
assert_eq!(allow.len(), 1);
|
||||
assert_eq!(allow[0], "mcp__storkit__*");
|
||||
assert_eq!(allow[0], "mcp__huskies__*");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -548,7 +548,7 @@ mod tests {
|
||||
fs::create_dir_all(&claude_dir).unwrap();
|
||||
fs::write(
|
||||
claude_dir.join("settings.json"),
|
||||
r#"{"permissions":{"allow":["Edit"]},"enabledMcpjsonServers":["storkit"]}"#,
|
||||
r#"{"permissions":{"allow":["Edit"]},"enabledMcpjsonServers":["huskies"]}"#,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -558,7 +558,7 @@ mod tests {
|
||||
let settings: Value = serde_json::from_str(&content).unwrap();
|
||||
let servers = settings["enabledMcpjsonServers"].as_array().unwrap();
|
||||
assert_eq!(servers.len(), 1);
|
||||
assert_eq!(servers[0], "storkit");
|
||||
assert_eq!(servers[0], "huskies");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -598,9 +598,9 @@ mod tests {
|
||||
// binary, otherwise cargo build outputs to a different target dir and
|
||||
// current_exe() still points at the old binary.
|
||||
let build_args: Vec<&str> = if cfg!(debug_assertions) {
|
||||
vec!["build", "-p", "storkit"]
|
||||
vec!["build", "-p", "huskies"]
|
||||
} else {
|
||||
vec!["build", "--release", "-p", "storkit"]
|
||||
vec!["build", "--release", "-p", "huskies"]
|
||||
};
|
||||
|
||||
// Tests always run in debug mode, so --release must NOT be present.
|
||||
@@ -652,7 +652,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let root = tmp.path();
|
||||
// Seed project root in state so get_project_root works
|
||||
let backlog = root.join(".storkit/work/1_backlog");
|
||||
let backlog = root.join(".huskies/work/1_backlog");
|
||||
fs::create_dir_all(&backlog).unwrap();
|
||||
fs::write(backlog.join("1_story_test.md"), "---\nname: Test\n---\n").unwrap();
|
||||
let ctx = test_ctx(root);
|
||||
@@ -668,8 +668,8 @@ mod tests {
|
||||
fn tool_move_story_moves_from_backlog_to_current() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let root = tmp.path();
|
||||
let backlog = root.join(".storkit/work/1_backlog");
|
||||
let current = root.join(".storkit/work/2_current");
|
||||
let backlog = root.join(".huskies/work/1_backlog");
|
||||
let current = root.join(".huskies/work/2_current");
|
||||
fs::create_dir_all(&backlog).unwrap();
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::write(backlog.join("5_story_test.md"), "---\nname: Test\n---\n").unwrap();
|
||||
@@ -693,8 +693,8 @@ mod tests {
|
||||
fn tool_move_story_moves_from_current_to_backlog() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let root = tmp.path();
|
||||
let current = root.join(".storkit/work/2_current");
|
||||
let backlog = root.join(".storkit/work/1_backlog");
|
||||
let current = root.join(".huskies/work/2_current");
|
||||
let backlog = root.join(".huskies/work/1_backlog");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::create_dir_all(&backlog).unwrap();
|
||||
fs::write(current.join("6_story_back.md"), "---\nname: Back\n---\n").unwrap();
|
||||
@@ -717,7 +717,7 @@ mod tests {
|
||||
fn tool_move_story_idempotent_when_already_in_target() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let root = tmp.path();
|
||||
let current = root.join(".storkit/work/2_current");
|
||||
let current = root.join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::write(current.join("7_story_idem.md"), "---\nname: Idem\n---\n").unwrap();
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use serde_json::{json, Value};
|
||||
use std::path::PathBuf;
|
||||
|
||||
/// Validates that `worktree_path` exists and is inside the project's
|
||||
/// `.storkit/worktrees/` directory. Returns the canonicalized path.
|
||||
/// `.huskies/worktrees/` directory. Returns the canonicalized path.
|
||||
fn validate_worktree_path(worktree_path: &str, ctx: &AppContext) -> Result<PathBuf, String> {
|
||||
let wd = PathBuf::from(worktree_path);
|
||||
|
||||
@@ -17,7 +17,7 @@ fn validate_worktree_path(worktree_path: &str, ctx: &AppContext) -> Result<PathB
|
||||
}
|
||||
|
||||
let project_root = ctx.agents.get_project_root(&ctx.state)?;
|
||||
let worktrees_root = project_root.join(".storkit").join("worktrees");
|
||||
let worktrees_root = project_root.join(".huskies").join("worktrees");
|
||||
|
||||
let canonical_wd = wd
|
||||
.canonicalize()
|
||||
@@ -33,7 +33,7 @@ fn validate_worktree_path(worktree_path: &str, ctx: &AppContext) -> Result<PathB
|
||||
|
||||
if !canonical_wd.starts_with(&canonical_wt) {
|
||||
return Err(format!(
|
||||
"worktree_path must be inside .storkit/worktrees/. Got: {worktree_path}"
|
||||
"worktree_path must be inside .huskies/worktrees/. Got: {worktree_path}"
|
||||
));
|
||||
}
|
||||
|
||||
@@ -312,7 +312,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let story_wt = tmp
|
||||
.path()
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("worktrees")
|
||||
.join("42_test_story");
|
||||
std::fs::create_dir_all(&story_wt).unwrap();
|
||||
@@ -361,12 +361,12 @@ mod tests {
|
||||
#[test]
|
||||
fn validate_rejects_path_outside_worktrees() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let wt_dir = tmp.path().join(".storkit").join("worktrees");
|
||||
let wt_dir = tmp.path().join(".huskies").join("worktrees");
|
||||
std::fs::create_dir_all(&wt_dir).unwrap();
|
||||
let ctx = test_ctx(tmp.path());
|
||||
let result = validate_worktree_path(tmp.path().to_str().unwrap(), &ctx);
|
||||
assert!(result.is_err());
|
||||
assert!(result.unwrap_err().contains("inside .storkit/worktrees"));
|
||||
assert!(result.unwrap_err().contains("inside .huskies/worktrees"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -374,7 +374,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let story_wt = tmp
|
||||
.path()
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("worktrees")
|
||||
.join("42_test_story");
|
||||
std::fs::create_dir_all(&story_wt).unwrap();
|
||||
|
||||
@@ -138,7 +138,7 @@ pub(super) fn tool_report_merge_failure(args: &Value, ctx: &AppContext) -> Resul
|
||||
// survives server restarts and is visible in the web UI.
|
||||
if let Ok(project_root) = ctx.state.get_project_root() {
|
||||
let story_file = project_root
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("work")
|
||||
.join("4_merge")
|
||||
.join(format!("{story_id}.md"));
|
||||
@@ -243,7 +243,7 @@ mod tests {
|
||||
async fn tool_move_story_to_merge_moves_file() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
setup_git_repo_in(tmp.path());
|
||||
let current_dir = tmp.path().join(".storkit/work/2_current");
|
||||
let current_dir = tmp.path().join(".huskies/work/2_current");
|
||||
std::fs::create_dir_all(¤t_dir).unwrap();
|
||||
let story_file = current_dir.join("24_story_test.md");
|
||||
std::fs::write(&story_file, "---\nname: Test\n---\n").unwrap();
|
||||
@@ -264,7 +264,7 @@ mod tests {
|
||||
// File should have been moved regardless of agent start outcome
|
||||
assert!(!story_file.exists(), "2_current file should be gone");
|
||||
assert!(
|
||||
tmp.path().join(".storkit/work/4_merge/24_story_test.md").exists(),
|
||||
tmp.path().join(".huskies/work/4_merge/24_story_test.md").exists(),
|
||||
"4_merge file should exist"
|
||||
);
|
||||
// Result is either Ok (agent started) or Err (agent failed - acceptable in tests)
|
||||
|
||||
+19
-19
@@ -308,7 +308,7 @@ fn handle_initialize(id: Option<Value>, params: &Value) -> JsonRpcResponse {
|
||||
"tools": {}
|
||||
},
|
||||
"serverInfo": {
|
||||
"name": "storkit",
|
||||
"name": "huskies",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
}),
|
||||
@@ -537,7 +537,7 @@ fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
|
||||
},
|
||||
{
|
||||
"name": "create_worktree",
|
||||
"description": "Create a git worktree for a story under .storkit/worktrees/{story_id} with deterministic naming. Writes .mcp.json and runs component setup. Returns the worktree path.",
|
||||
"description": "Create a git worktree for a story under .huskies/worktrees/{story_id} with deterministic naming. Writes .mcp.json and runs component setup. Returns the worktree path.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -551,7 +551,7 @@ fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
|
||||
},
|
||||
{
|
||||
"name": "list_worktrees",
|
||||
"description": "List all worktrees under .storkit/worktrees/ for the current project.",
|
||||
"description": "List all worktrees under .huskies/worktrees/ for the current project.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {}
|
||||
@@ -670,7 +670,7 @@ fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
|
||||
},
|
||||
{
|
||||
"name": "create_spike",
|
||||
"description": "Create a spike file in .storkit/work/1_backlog/ with a deterministic filename and YAML front matter. Returns the spike_id.",
|
||||
"description": "Create a spike file in .huskies/work/1_backlog/ with a deterministic filename and YAML front matter. Returns the spike_id.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -894,7 +894,7 @@ fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
|
||||
},
|
||||
{
|
||||
"name": "launch_qa_app",
|
||||
"description": "Launch the app from a story's worktree for manual QA testing. Automatically assigns a free port, writes it to .storkit_port, and starts the backend server. Only one QA app instance runs at a time.",
|
||||
"description": "Launch the app from a story's worktree for manual QA testing. Automatically assigns a free port, writes it to .huskies_port, and starts the backend server. Only one QA app instance runs at a time.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1023,7 +1023,7 @@ fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
|
||||
},
|
||||
{
|
||||
"name": "run_command",
|
||||
"description": "Execute a shell command in an agent's worktree directory. The working_dir must be inside .storkit/worktrees/. Returns stdout, stderr, exit_code, and timed_out. Supports SSE streaming (send Accept: text/event-stream) for long-running commands. Dangerous commands (rm -rf /, sudo, etc.) are blocked.",
|
||||
"description": "Execute a shell command in an agent's worktree directory. The working_dir must be inside .huskies/worktrees/. Returns stdout, stderr, exit_code, and timed_out. Supports SSE streaming (send Accept: text/event-stream) for long-running commands. Dangerous commands (rm -rf /, sudo, etc.) are blocked.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -1033,7 +1033,7 @@ fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
|
||||
},
|
||||
"working_dir": {
|
||||
"type": "string",
|
||||
"description": "Absolute path to the worktree directory to run the command in. Must be inside .storkit/worktrees/."
|
||||
"description": "Absolute path to the worktree directory to run the command in. Must be inside .huskies/worktrees/."
|
||||
},
|
||||
"timeout": {
|
||||
"type": "integer",
|
||||
@@ -1045,13 +1045,13 @@ fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
|
||||
},
|
||||
{
|
||||
"name": "git_status",
|
||||
"description": "Return the working tree status of an agent's worktree (staged, unstaged, and untracked files). The worktree_path must be inside .storkit/worktrees/. Push and remote operations are not available.",
|
||||
"description": "Return the working tree status of an agent's worktree (staged, unstaged, and untracked files). The worktree_path must be inside .huskies/worktrees/. Push and remote operations are not available.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"worktree_path": {
|
||||
"type": "string",
|
||||
"description": "Absolute path to the worktree directory. Must be inside .storkit/worktrees/."
|
||||
"description": "Absolute path to the worktree directory. Must be inside .huskies/worktrees/."
|
||||
}
|
||||
},
|
||||
"required": ["worktree_path"]
|
||||
@@ -1059,13 +1059,13 @@ fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
|
||||
},
|
||||
{
|
||||
"name": "git_diff",
|
||||
"description": "Return diff output for an agent's worktree. Supports unstaged (default), staged, or a commit range. The worktree_path must be inside .storkit/worktrees/.",
|
||||
"description": "Return diff output for an agent's worktree. Supports unstaged (default), staged, or a commit range. The worktree_path must be inside .huskies/worktrees/.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"worktree_path": {
|
||||
"type": "string",
|
||||
"description": "Absolute path to the worktree directory. Must be inside .storkit/worktrees/."
|
||||
"description": "Absolute path to the worktree directory. Must be inside .huskies/worktrees/."
|
||||
},
|
||||
"staged": {
|
||||
"type": "boolean",
|
||||
@@ -1081,13 +1081,13 @@ fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
|
||||
},
|
||||
{
|
||||
"name": "git_add",
|
||||
"description": "Stage files by path in an agent's worktree. The worktree_path must be inside .storkit/worktrees/.",
|
||||
"description": "Stage files by path in an agent's worktree. The worktree_path must be inside .huskies/worktrees/.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"worktree_path": {
|
||||
"type": "string",
|
||||
"description": "Absolute path to the worktree directory. Must be inside .storkit/worktrees/."
|
||||
"description": "Absolute path to the worktree directory. Must be inside .huskies/worktrees/."
|
||||
},
|
||||
"paths": {
|
||||
"type": "array",
|
||||
@@ -1100,13 +1100,13 @@ fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
|
||||
},
|
||||
{
|
||||
"name": "git_commit",
|
||||
"description": "Commit staged changes in an agent's worktree with the given message. The worktree_path must be inside .storkit/worktrees/. Push and remote operations are not available.",
|
||||
"description": "Commit staged changes in an agent's worktree with the given message. The worktree_path must be inside .huskies/worktrees/. Push and remote operations are not available.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"worktree_path": {
|
||||
"type": "string",
|
||||
"description": "Absolute path to the worktree directory. Must be inside .storkit/worktrees/."
|
||||
"description": "Absolute path to the worktree directory. Must be inside .huskies/worktrees/."
|
||||
},
|
||||
"message": {
|
||||
"type": "string",
|
||||
@@ -1118,13 +1118,13 @@ fn handle_tools_list(id: Option<Value>) -> JsonRpcResponse {
|
||||
},
|
||||
{
|
||||
"name": "git_log",
|
||||
"description": "Return commit history for an agent's worktree with configurable count and format. The worktree_path must be inside .storkit/worktrees/.",
|
||||
"description": "Return commit history for an agent's worktree with configurable count and format. The worktree_path must be inside .huskies/worktrees/.",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"worktree_path": {
|
||||
"type": "string",
|
||||
"description": "Absolute path to the worktree directory. Must be inside .storkit/worktrees/."
|
||||
"description": "Absolute path to the worktree directory. Must be inside .huskies/worktrees/."
|
||||
},
|
||||
"count": {
|
||||
"type": "integer",
|
||||
@@ -1363,7 +1363,7 @@ mod tests {
|
||||
let result = resp.result.unwrap();
|
||||
assert_eq!(result["protocolVersion"], "2025-03-26");
|
||||
assert!(result["capabilities"]["tools"].is_object());
|
||||
assert_eq!(result["serverInfo"]["name"], "storkit");
|
||||
assert_eq!(result["serverInfo"]["name"], "huskies");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -1638,7 +1638,7 @@ mod tests {
|
||||
)
|
||||
.await;
|
||||
assert_eq!(body["result"]["protocolVersion"], "2025-03-26");
|
||||
assert_eq!(body["result"]["serverInfo"]["name"], "storkit");
|
||||
assert_eq!(body["result"]["serverInfo"]["name"], "huskies");
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
||||
@@ -48,7 +48,7 @@ pub(super) async fn tool_approve_qa(args: &Value, ctx: &AppContext) -> Result<St
|
||||
|
||||
// Clear review_hold before moving
|
||||
let qa_path = project_root
|
||||
.join(".storkit/work/3_qa")
|
||||
.join(".huskies/work/3_qa")
|
||||
.join(format!("{story_id}.md"));
|
||||
if qa_path.exists() {
|
||||
let _ = crate::io::story_metadata::clear_front_matter_field(&qa_path, "review_hold");
|
||||
@@ -92,7 +92,7 @@ pub(super) async fn tool_reject_qa(args: &Value, ctx: &AppContext) -> Result<Str
|
||||
|
||||
// Restart the coder agent with rejection context
|
||||
let story_path = project_root
|
||||
.join(".storkit/work/2_current")
|
||||
.join(".huskies/work/2_current")
|
||||
.join(format!("{story_id}.md"));
|
||||
let agent_name = if story_path.exists() {
|
||||
let contents = std::fs::read_to_string(&story_path).unwrap_or_default();
|
||||
@@ -152,15 +152,15 @@ pub(super) async fn tool_launch_qa_app(args: &Value, ctx: &AppContext) -> Result
|
||||
// Find a free port starting from 3100
|
||||
let port = find_free_port(3100);
|
||||
|
||||
// Write .storkit_port so the frontend dev server knows where to connect
|
||||
let port_file = wt_path.join(".storkit_port");
|
||||
// Write .huskies_port so the frontend dev server knows where to connect
|
||||
let port_file = wt_path.join(".huskies_port");
|
||||
std::fs::write(&port_file, port.to_string())
|
||||
.map_err(|e| format!("Failed to write .storkit_port: {e}"))?;
|
||||
.map_err(|e| format!("Failed to write .huskies_port: {e}"))?;
|
||||
|
||||
// Launch the server from the worktree
|
||||
let child = std::process::Command::new("cargo")
|
||||
.args(["run"])
|
||||
.env("STORKIT_PORT", port.to_string())
|
||||
.env("HUSKIES_PORT", port.to_string())
|
||||
.current_dir(&wt_path)
|
||||
.stdout(std::process::Stdio::null())
|
||||
.stderr(std::process::Stdio::null())
|
||||
|
||||
@@ -59,7 +59,7 @@ fn is_dangerous(command: &str) -> Option<String> {
|
||||
}
|
||||
|
||||
/// Validates that `working_dir` exists and is inside the project's
|
||||
/// `.storkit/worktrees/` directory. Returns the canonicalized path.
|
||||
/// `.huskies/worktrees/` directory. Returns the canonicalized path.
|
||||
fn validate_working_dir(working_dir: &str, ctx: &AppContext) -> Result<PathBuf, String> {
|
||||
let wd = PathBuf::from(working_dir);
|
||||
|
||||
@@ -71,7 +71,7 @@ fn validate_working_dir(working_dir: &str, ctx: &AppContext) -> Result<PathBuf,
|
||||
}
|
||||
|
||||
let project_root = ctx.agents.get_project_root(&ctx.state)?;
|
||||
let worktrees_root = project_root.join(".storkit").join("worktrees");
|
||||
let worktrees_root = project_root.join(".huskies").join("worktrees");
|
||||
|
||||
let canonical_wd = wd
|
||||
.canonicalize()
|
||||
@@ -88,7 +88,7 @@ fn validate_working_dir(working_dir: &str, ctx: &AppContext) -> Result<PathBuf,
|
||||
|
||||
if !canonical_wd.starts_with(&canonical_wt) {
|
||||
return Err(format!(
|
||||
"working_dir must be inside .storkit/worktrees/. Got: {working_dir}"
|
||||
"working_dir must be inside .huskies/worktrees/. Got: {working_dir}"
|
||||
));
|
||||
}
|
||||
|
||||
@@ -406,14 +406,14 @@ mod tests {
|
||||
fn validate_working_dir_rejects_path_outside_worktrees() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
// Create the worktrees dir so it exists
|
||||
let wt_dir = tmp.path().join(".storkit").join("worktrees");
|
||||
let wt_dir = tmp.path().join(".huskies").join("worktrees");
|
||||
std::fs::create_dir_all(&wt_dir).unwrap();
|
||||
let ctx = test_ctx(tmp.path());
|
||||
// Try to use /tmp (outside worktrees)
|
||||
let result = validate_working_dir(tmp.path().to_str().unwrap(), &ctx);
|
||||
assert!(result.is_err());
|
||||
assert!(
|
||||
result.unwrap_err().contains("inside .storkit/worktrees"),
|
||||
result.unwrap_err().contains("inside .huskies/worktrees"),
|
||||
"expected sandbox error"
|
||||
);
|
||||
}
|
||||
@@ -423,7 +423,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let story_wt = tmp
|
||||
.path()
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("worktrees")
|
||||
.join("42_test_story");
|
||||
std::fs::create_dir_all(&story_wt).unwrap();
|
||||
@@ -477,7 +477,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn tool_run_command_rejects_path_outside_worktrees() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let wt_dir = tmp.path().join(".storkit").join("worktrees");
|
||||
let wt_dir = tmp.path().join(".huskies").join("worktrees");
|
||||
std::fs::create_dir_all(&wt_dir).unwrap();
|
||||
let ctx = test_ctx(tmp.path());
|
||||
let result = tool_run_command(
|
||||
@@ -500,7 +500,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let story_wt = tmp
|
||||
.path()
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("worktrees")
|
||||
.join("42_test");
|
||||
std::fs::create_dir_all(&story_wt).unwrap();
|
||||
@@ -528,7 +528,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let story_wt = tmp
|
||||
.path()
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("worktrees")
|
||||
.join("43_test");
|
||||
std::fs::create_dir_all(&story_wt).unwrap();
|
||||
@@ -554,7 +554,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let story_wt = tmp
|
||||
.path()
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("worktrees")
|
||||
.join("44_test");
|
||||
std::fs::create_dir_all(&story_wt).unwrap();
|
||||
@@ -580,7 +580,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let story_wt = tmp
|
||||
.path()
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("worktrees")
|
||||
.join("45_test");
|
||||
std::fs::create_dir_all(&story_wt).unwrap();
|
||||
|
||||
@@ -30,10 +30,10 @@ fn parse_ac_items(contents: &str) -> Vec<(String, bool)> {
|
||||
items
|
||||
}
|
||||
|
||||
/// Find the most recent log file for any agent under `.storkit/logs/{story_id}/`.
|
||||
/// Find the most recent log file for any agent under `.huskies/logs/{story_id}/`.
|
||||
fn find_most_recent_log(project_root: &Path, story_id: &str) -> Option<PathBuf> {
|
||||
let dir = project_root
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("logs")
|
||||
.join(story_id);
|
||||
|
||||
@@ -157,7 +157,7 @@ pub(super) async fn tool_status(args: &Value, ctx: &AppContext) -> Result<String
|
||||
.ok_or("Missing required argument: story_id")?;
|
||||
|
||||
let root = ctx.state.get_project_root()?;
|
||||
let current_dir = root.join(".storkit").join("work").join("2_current");
|
||||
let current_dir = root.join(".huskies").join("work").join("2_current");
|
||||
let filepath = current_dir.join(format!("{story_id}.md"));
|
||||
|
||||
if !filepath.exists() {
|
||||
@@ -206,7 +206,7 @@ pub(super) async fn tool_status(args: &Value, ctx: &AppContext) -> Result<String
|
||||
.collect();
|
||||
|
||||
// --- Worktree ---
|
||||
let worktree_path = root.join(".storkit").join("worktrees").join(story_id);
|
||||
let worktree_path = root.join(".huskies").join("worktrees").join(story_id);
|
||||
let (_, worktree_info) = if worktree_path.is_dir() {
|
||||
let branch = git_branch(&worktree_path).await;
|
||||
(
|
||||
@@ -300,7 +300,7 @@ mod tests {
|
||||
let tmp = tempdir().unwrap();
|
||||
let log_dir = tmp
|
||||
.path()
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("logs")
|
||||
.join("42_story_foo");
|
||||
fs::create_dir_all(&log_dir).unwrap();
|
||||
@@ -336,7 +336,7 @@ mod tests {
|
||||
let tmp = tempdir().unwrap();
|
||||
let current_dir = tmp
|
||||
.path()
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("work")
|
||||
.join("2_current");
|
||||
fs::create_dir_all(¤t_dir).unwrap();
|
||||
|
||||
@@ -130,7 +130,7 @@ pub(super) fn tool_get_story_todos(args: &Value, ctx: &AppContext) -> Result<Str
|
||||
.ok_or("Missing required argument: story_id")?;
|
||||
|
||||
let root = ctx.state.get_project_root()?;
|
||||
let current_dir = root.join(".storkit").join("work").join("2_current");
|
||||
let current_dir = root.join(".huskies").join("work").join("2_current");
|
||||
let filepath = current_dir.join(format!("{story_id}.md"));
|
||||
|
||||
if !filepath.exists() {
|
||||
@@ -448,7 +448,7 @@ pub(super) async fn tool_delete_story(args: &Value, ctx: &AppContext) -> Result<
|
||||
}
|
||||
|
||||
// 4. Find and delete the story file from any pipeline stage
|
||||
let sk = project_root.join(".storkit").join("work");
|
||||
let sk = project_root.join(".huskies").join("work");
|
||||
let stage_dirs = [
|
||||
"1_backlog",
|
||||
"2_current",
|
||||
@@ -667,7 +667,7 @@ mod tests {
|
||||
("4_merge", "40_story_merge", "Merge Story"),
|
||||
("5_done", "50_story_done", "Done Story"),
|
||||
] {
|
||||
let dir = root.join(".storkit/work").join(stage);
|
||||
let dir = root.join(".huskies/work").join(stage);
|
||||
std::fs::create_dir_all(&dir).unwrap();
|
||||
std::fs::write(
|
||||
dir.join(format!("{id}.md")),
|
||||
@@ -705,7 +705,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let root = tmp.path();
|
||||
|
||||
let current = root.join(".storkit/work/2_current");
|
||||
let current = root.join(".huskies/work/2_current");
|
||||
std::fs::create_dir_all(¤t).unwrap();
|
||||
std::fs::write(
|
||||
current.join("20_story_active.md"),
|
||||
@@ -745,7 +745,7 @@ mod tests {
|
||||
#[test]
|
||||
fn tool_get_story_todos_returns_unchecked() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current_dir = tmp.path().join(".storkit").join("work").join("2_current");
|
||||
let current_dir = tmp.path().join(".huskies").join("work").join("2_current");
|
||||
fs::create_dir_all(¤t_dir).unwrap();
|
||||
fs::write(
|
||||
current_dir.join("1_test.md"),
|
||||
@@ -839,8 +839,8 @@ mod tests {
|
||||
"create_bug description should reference work/1_backlog/, got: {desc}"
|
||||
);
|
||||
assert!(
|
||||
!desc.contains(".storkit/bugs"),
|
||||
"create_bug description should not reference nonexistent .storkit/bugs/, got: {desc}"
|
||||
!desc.contains(".huskies/bugs"),
|
||||
"create_bug description should not reference nonexistent .huskies/bugs/, got: {desc}"
|
||||
);
|
||||
let required = t["inputSchema"]["required"].as_array().unwrap();
|
||||
let req_names: Vec<&str> = required.iter().map(|v| v.as_str().unwrap()).collect();
|
||||
@@ -865,8 +865,8 @@ mod tests {
|
||||
"list_bugs description should reference work/1_backlog/, got: {desc}"
|
||||
);
|
||||
assert!(
|
||||
!desc.contains(".storkit/bugs"),
|
||||
"list_bugs description should not reference nonexistent .storkit/bugs/, got: {desc}"
|
||||
!desc.contains(".huskies/bugs"),
|
||||
"list_bugs description should not reference nonexistent .huskies/bugs/, got: {desc}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -880,8 +880,8 @@ mod tests {
|
||||
let t = tool.unwrap();
|
||||
let desc = t["description"].as_str().unwrap();
|
||||
assert!(
|
||||
!desc.contains(".storkit/bugs"),
|
||||
"close_bug description should not reference nonexistent .storkit/bugs/, got: {desc}"
|
||||
!desc.contains(".huskies/bugs"),
|
||||
"close_bug description should not reference nonexistent .huskies/bugs/, got: {desc}"
|
||||
);
|
||||
assert!(
|
||||
desc.contains("work/5_done/"),
|
||||
@@ -947,7 +947,7 @@ mod tests {
|
||||
assert!(result.contains("1_bug_login_crash"));
|
||||
let bug_file = tmp
|
||||
.path()
|
||||
.join(".storkit/work/1_backlog/1_bug_login_crash.md");
|
||||
.join(".huskies/work/1_backlog/1_bug_login_crash.md");
|
||||
assert!(bug_file.exists());
|
||||
}
|
||||
|
||||
@@ -963,7 +963,7 @@ mod tests {
|
||||
#[test]
|
||||
fn tool_list_bugs_returns_open_bugs() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let backlog_dir = tmp.path().join(".storkit/work/1_backlog");
|
||||
let backlog_dir = tmp.path().join(".huskies/work/1_backlog");
|
||||
std::fs::create_dir_all(&backlog_dir).unwrap();
|
||||
std::fs::write(backlog_dir.join("1_bug_crash.md"), "# Bug 1: App Crash\n").unwrap();
|
||||
std::fs::write(
|
||||
@@ -995,7 +995,7 @@ mod tests {
|
||||
fn tool_close_bug_moves_to_archive() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
setup_git_repo_in(tmp.path());
|
||||
let backlog_dir = tmp.path().join(".storkit/work/1_backlog");
|
||||
let backlog_dir = tmp.path().join(".huskies/work/1_backlog");
|
||||
std::fs::create_dir_all(&backlog_dir).unwrap();
|
||||
let bug_file = backlog_dir.join("1_bug_crash.md");
|
||||
std::fs::write(&bug_file, "# Bug 1: Crash\n").unwrap();
|
||||
@@ -1017,7 +1017,7 @@ mod tests {
|
||||
assert!(!bug_file.exists());
|
||||
assert!(
|
||||
tmp.path()
|
||||
.join(".storkit/work/5_done/1_bug_crash.md")
|
||||
.join(".huskies/work/5_done/1_bug_crash.md")
|
||||
.exists()
|
||||
);
|
||||
}
|
||||
@@ -1070,7 +1070,7 @@ mod tests {
|
||||
assert!(result.contains("1_spike_compare_encoders"));
|
||||
let spike_file = tmp
|
||||
.path()
|
||||
.join(".storkit/work/1_backlog/1_spike_compare_encoders.md");
|
||||
.join(".huskies/work/1_backlog/1_spike_compare_encoders.md");
|
||||
assert!(spike_file.exists());
|
||||
let contents = std::fs::read_to_string(&spike_file).unwrap();
|
||||
assert!(contents.starts_with("---\nname: \"Compare Encoders\"\n---"));
|
||||
@@ -1087,7 +1087,7 @@ mod tests {
|
||||
|
||||
let spike_file = tmp
|
||||
.path()
|
||||
.join(".storkit/work/1_backlog/1_spike_my_spike.md");
|
||||
.join(".huskies/work/1_backlog/1_spike_my_spike.md");
|
||||
assert!(spike_file.exists());
|
||||
let contents = std::fs::read_to_string(&spike_file).unwrap();
|
||||
assert!(contents.starts_with("---\nname: \"My Spike\"\n---"));
|
||||
@@ -1130,7 +1130,7 @@ mod tests {
|
||||
#[test]
|
||||
fn tool_validate_stories_with_valid_story() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current_dir = tmp.path().join(".storkit").join("work").join("2_current");
|
||||
let current_dir = tmp.path().join(".huskies").join("work").join("2_current");
|
||||
fs::create_dir_all(¤t_dir).unwrap();
|
||||
fs::write(
|
||||
current_dir.join("1_test.md"),
|
||||
@@ -1147,7 +1147,7 @@ mod tests {
|
||||
#[test]
|
||||
fn tool_validate_stories_with_invalid_front_matter() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current_dir = tmp.path().join(".storkit").join("work").join("2_current");
|
||||
let current_dir = tmp.path().join(".huskies").join("work").join("2_current");
|
||||
fs::create_dir_all(¤t_dir).unwrap();
|
||||
fs::write(current_dir.join("1_test.md"), "## No front matter at all\n").unwrap();
|
||||
let ctx = test_ctx(tmp.path());
|
||||
@@ -1160,7 +1160,7 @@ mod tests {
|
||||
#[test]
|
||||
fn record_tests_persists_to_story_file() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::write(
|
||||
current.join("1_story_persist.md"),
|
||||
@@ -1185,7 +1185,7 @@ mod tests {
|
||||
"file should have Test Results section"
|
||||
);
|
||||
assert!(
|
||||
contents.contains("storkit-test-results:"),
|
||||
contents.contains("huskies-test-results:"),
|
||||
"file should have JSON marker"
|
||||
);
|
||||
assert!(contents.contains("u1"), "file should contain test name");
|
||||
@@ -1194,11 +1194,11 @@ mod tests {
|
||||
#[test]
|
||||
fn ensure_acceptance_reads_from_file_when_not_in_memory() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
|
||||
// Write a story file with a pre-populated Test Results section (simulating a restart)
|
||||
let story_content = "---\nname: Persist\n---\n# Story\n\n## Test Results\n\n<!-- storkit-test-results: {\"unit\":[{\"name\":\"u1\",\"status\":\"pass\",\"details\":null}],\"integration\":[{\"name\":\"i1\",\"status\":\"pass\",\"details\":null}]} -->\n";
|
||||
let story_content = "---\nname: Persist\n---\n# Story\n\n## Test Results\n\n<!-- huskies-test-results: {\"unit\":[{\"name\":\"u1\",\"status\":\"pass\",\"details\":null}],\"integration\":[{\"name\":\"i1\",\"status\":\"pass\",\"details\":null}]} -->\n";
|
||||
fs::write(current.join("2_story_file_only.md"), story_content).unwrap();
|
||||
|
||||
// Use a fresh context (empty in-memory state, simulating a restart)
|
||||
@@ -1217,10 +1217,10 @@ mod tests {
|
||||
#[test]
|
||||
fn ensure_acceptance_file_with_failures_still_blocks() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
|
||||
let story_content = "---\nname: Fail\n---\n# Story\n\n## Test Results\n\n<!-- storkit-test-results: {\"unit\":[{\"name\":\"u1\",\"status\":\"fail\",\"details\":\"error\"}],\"integration\":[]} -->\n";
|
||||
let story_content = "---\nname: Fail\n---\n# Story\n\n## Test Results\n\n<!-- huskies-test-results: {\"unit\":[{\"name\":\"u1\",\"status\":\"fail\",\"details\":\"error\"}],\"integration\":[]} -->\n";
|
||||
fs::write(current.join("3_story_fail.md"), story_content).unwrap();
|
||||
|
||||
let ctx = test_ctx(tmp.path());
|
||||
@@ -1254,7 +1254,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn tool_delete_story_deletes_file_from_backlog() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let backlog = tmp.path().join(".storkit/work/1_backlog");
|
||||
let backlog = tmp.path().join(".huskies/work/1_backlog");
|
||||
fs::create_dir_all(&backlog).unwrap();
|
||||
let story_file = backlog.join("10_story_cleanup.md");
|
||||
fs::write(&story_file, "---\nname: Cleanup\n---\n").unwrap();
|
||||
@@ -1268,7 +1268,7 @@ mod tests {
|
||||
#[tokio::test]
|
||||
async fn tool_delete_story_deletes_file_from_current() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let story_file = current.join("11_story_active.md");
|
||||
fs::write(&story_file, "---\nname: Active\n---\n").unwrap();
|
||||
@@ -1328,7 +1328,7 @@ mod tests {
|
||||
.unwrap();
|
||||
|
||||
// Create story file in current/ so move_story_to_done would work.
|
||||
let current_dir = tmp.path().join(".storkit/work/2_current");
|
||||
let current_dir = tmp.path().join(".huskies/work/2_current");
|
||||
std::fs::create_dir_all(¤t_dir).unwrap();
|
||||
std::fs::write(
|
||||
current_dir.join("50_story_test.md"),
|
||||
@@ -1356,7 +1356,7 @@ mod tests {
|
||||
setup_git_repo_in(tmp.path());
|
||||
|
||||
// Create story file in current/ (no feature branch).
|
||||
let current_dir = tmp.path().join(".storkit/work/2_current");
|
||||
let current_dir = tmp.path().join(".huskies/work/2_current");
|
||||
std::fs::create_dir_all(¤t_dir).unwrap();
|
||||
std::fs::write(
|
||||
current_dir.join("51_story_no_branch.md"),
|
||||
@@ -1394,7 +1394,7 @@ mod tests {
|
||||
fn tool_check_criterion_marks_unchecked_item() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
setup_git_repo_in(tmp.path());
|
||||
let current_dir = tmp.path().join(".storkit").join("work").join("2_current");
|
||||
let current_dir = tmp.path().join(".huskies").join("work").join("2_current");
|
||||
fs::create_dir_all(¤t_dir).unwrap();
|
||||
fs::write(
|
||||
current_dir.join("1_test.md"),
|
||||
|
||||
@@ -22,19 +22,19 @@ use std::path::Path;
|
||||
/// Return the filesystem path (relative to `project_root`) for a step's output.
|
||||
///
|
||||
/// Returns `None` for `Scaffold` since that step has no single output file — it
|
||||
/// creates the full `.storkit/` directory structure and is handled by
|
||||
/// `storkit init` before the server starts.
|
||||
/// creates the full `.huskies/` directory structure and is handled by
|
||||
/// `huskies init` before the server starts.
|
||||
pub(crate) fn step_output_path(project_root: &Path, step: WizardStep) -> Option<std::path::PathBuf> {
|
||||
match step {
|
||||
WizardStep::Context => Some(
|
||||
project_root
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("specs")
|
||||
.join("00_CONTEXT.md"),
|
||||
),
|
||||
WizardStep::Stack => Some(
|
||||
project_root
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("specs")
|
||||
.join("tech")
|
||||
.join("STACK.md"),
|
||||
@@ -99,7 +99,7 @@ fn step_slug(step: WizardStep) -> String {
|
||||
pub(super) fn tool_wizard_status(ctx: &AppContext) -> Result<String, String> {
|
||||
let root = ctx.state.get_project_root()?;
|
||||
let state =
|
||||
WizardState::load(&root).ok_or("No wizard active. Run `storkit init` to begin setup.")?;
|
||||
WizardState::load(&root).ok_or("No wizard active. Run `huskies init` to begin setup.")?;
|
||||
Ok(format_wizard_state(&state))
|
||||
}
|
||||
|
||||
@@ -161,7 +161,7 @@ pub(crate) fn is_bare_project(project_root: &Path) -> bool {
|
||||
.filter_map(|e| e.ok())
|
||||
.map(|e| e.file_name().to_string_lossy().to_string())
|
||||
.collect();
|
||||
// A bare project only has storkit scaffolding and no real code
|
||||
// A bare project only has huskies scaffolding and no real code
|
||||
names.iter().all(|n| {
|
||||
n.starts_with('.')
|
||||
|| n == "CLAUDE.md"
|
||||
@@ -182,13 +182,13 @@ pub(crate) fn generation_hint(step: WizardStep, project_root: &Path) -> String {
|
||||
if bare {
|
||||
"This is a bare project with no existing code. Ask the user what they want \
|
||||
to build — the project's purpose, goals, target users, and key features. \
|
||||
Then generate `.storkit/specs/00_CONTEXT.md` from their answers covering:\n\
|
||||
Then generate `.huskies/specs/00_CONTEXT.md` from their answers covering:\n\
|
||||
- High-level goal of the project\n\
|
||||
- Core features\n\
|
||||
- Domain concepts and entities\n\
|
||||
- Glossary of abbreviations and technical terms".to_string()
|
||||
} else {
|
||||
"Read the project source tree and generate a `.storkit/specs/00_CONTEXT.md` describing:\n\
|
||||
"Read the project source tree and generate a `.huskies/specs/00_CONTEXT.md` describing:\n\
|
||||
- High-level goal of the project\n\
|
||||
- Core features\n\
|
||||
- Domain concepts and entities\n\
|
||||
@@ -198,14 +198,14 @@ pub(crate) fn generation_hint(step: WizardStep, project_root: &Path) -> String {
|
||||
WizardStep::Stack => {
|
||||
if bare {
|
||||
"This is a bare project with no existing code. Ask the user what language, \
|
||||
frameworks, and tools they plan to use. Then generate `.storkit/specs/tech/STACK.md` \
|
||||
frameworks, and tools they plan to use. Then generate `.huskies/specs/tech/STACK.md` \
|
||||
from their answers covering:\n\
|
||||
- Language, frameworks, and runtimes\n\
|
||||
- Coding standards and linting rules\n\
|
||||
- Quality gates (commands that must pass before merging)\n\
|
||||
- Approved libraries and their purpose".to_string()
|
||||
} else {
|
||||
"Read the project source tree and generate a `.storkit/specs/tech/STACK.md` describing:\n\
|
||||
"Read the project source tree and generate a `.huskies/specs/tech/STACK.md` describing:\n\
|
||||
- Language, frameworks, and runtimes\n\
|
||||
- Coding standards and linting rules\n\
|
||||
- Quality gates (commands that must pass before merging)\n\
|
||||
@@ -262,7 +262,7 @@ pub(crate) fn generation_hint(step: WizardStep, project_root: &Path) -> String {
|
||||
"Generate a `script/test_coverage` shell script (#!/usr/bin/env bash, set -euo pipefail) that generates a test coverage report (e.g. `cargo llvm-cov nextest` or `npm run coverage`).".to_string()
|
||||
}
|
||||
}
|
||||
WizardStep::Scaffold => "Scaffold step is handled automatically by `storkit init`.".to_string(),
|
||||
WizardStep::Scaffold => "Scaffold step is handled automatically by `huskies init`.".to_string(),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -391,7 +391,7 @@ mod tests {
|
||||
|
||||
fn setup(dir: &TempDir) -> AppContext {
|
||||
let root = dir.path().to_path_buf();
|
||||
std::fs::create_dir_all(root.join(".storkit")).unwrap();
|
||||
std::fs::create_dir_all(root.join(".huskies")).unwrap();
|
||||
WizardState::init_if_missing(&root);
|
||||
AppContext::new_test(root)
|
||||
}
|
||||
@@ -408,7 +408,7 @@ mod tests {
|
||||
#[test]
|
||||
fn wizard_status_no_wizard_returns_error() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".storkit")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".huskies")).unwrap();
|
||||
let ctx = AppContext::new_test(dir.path().to_path_buf());
|
||||
assert!(tool_wizard_status(&ctx).is_err());
|
||||
}
|
||||
@@ -453,7 +453,7 @@ mod tests {
|
||||
// File should now exist.
|
||||
let context_path = dir
|
||||
.path()
|
||||
.join(".storkit")
|
||||
.join(".huskies")
|
||||
.join("specs")
|
||||
.join("00_CONTEXT.md");
|
||||
assert!(context_path.exists());
|
||||
@@ -472,7 +472,7 @@ mod tests {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let ctx = setup(&dir);
|
||||
// Pre-create the specs directory and file.
|
||||
let specs_dir = dir.path().join(".storkit").join("specs");
|
||||
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();
|
||||
@@ -550,7 +550,7 @@ mod tests {
|
||||
#[test]
|
||||
fn is_bare_project_detects_scaffold_only_dir() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".storkit")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".huskies")).unwrap();
|
||||
std::fs::write(dir.path().join("CLAUDE.md"), "# Claude").unwrap();
|
||||
std::fs::write(dir.path().join("README.md"), "# Readme").unwrap();
|
||||
std::fs::create_dir_all(dir.path().join("script")).unwrap();
|
||||
@@ -560,7 +560,7 @@ mod tests {
|
||||
#[test]
|
||||
fn is_bare_project_false_when_source_files_exist() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".storkit")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".huskies")).unwrap();
|
||||
std::fs::write(dir.path().join("Cargo.toml"), "[package]").unwrap();
|
||||
assert!(!is_bare_project(dir.path()));
|
||||
}
|
||||
@@ -576,7 +576,7 @@ mod tests {
|
||||
fn generation_hint_bare_context_asks_user() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
// Bare project — only scaffolding
|
||||
std::fs::create_dir_all(dir.path().join(".storkit")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".huskies")).unwrap();
|
||||
let hint = generation_hint(WizardStep::Context, dir.path());
|
||||
assert!(hint.contains("bare project"));
|
||||
assert!(hint.contains("Ask the user"));
|
||||
@@ -585,7 +585,7 @@ mod tests {
|
||||
#[test]
|
||||
fn generation_hint_bare_stack_asks_user() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".storkit")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".huskies")).unwrap();
|
||||
let hint = generation_hint(WizardStep::Stack, dir.path());
|
||||
assert!(hint.contains("bare project"));
|
||||
assert!(hint.contains("Ask the user"));
|
||||
@@ -594,7 +594,7 @@ mod tests {
|
||||
#[test]
|
||||
fn generation_hint_bare_test_script_references_stack() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".storkit")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".huskies")).unwrap();
|
||||
let hint = generation_hint(WizardStep::TestScript, dir.path());
|
||||
assert!(hint.contains("bare project"));
|
||||
assert!(hint.contains("STACK.md"));
|
||||
@@ -603,7 +603,7 @@ mod tests {
|
||||
#[test]
|
||||
fn generation_hint_bare_release_script_references_stack() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".storkit")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".huskies")).unwrap();
|
||||
let hint = generation_hint(WizardStep::ReleaseScript, dir.path());
|
||||
assert!(hint.contains("bare project"));
|
||||
assert!(hint.contains("STACK.md"));
|
||||
@@ -612,7 +612,7 @@ mod tests {
|
||||
#[test]
|
||||
fn generation_hint_bare_test_coverage_references_stack() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".storkit")).unwrap();
|
||||
std::fs::create_dir_all(dir.path().join(".huskies")).unwrap();
|
||||
let hint = generation_hint(WizardStep::TestCoverage, dir.path());
|
||||
assert!(hint.contains("bare project"));
|
||||
assert!(hint.contains("STACK.md"));
|
||||
|
||||
@@ -47,11 +47,11 @@ pub fn parse_port(value: Option<String>) -> u16 {
|
||||
}
|
||||
|
||||
pub fn resolve_port() -> u16 {
|
||||
parse_port(std::env::var("STORKIT_PORT").ok())
|
||||
parse_port(std::env::var("HUSKIES_PORT").ok())
|
||||
}
|
||||
|
||||
pub fn write_port_file(dir: &Path, port: u16) -> Option<PathBuf> {
|
||||
let path = dir.join(".storkit_port");
|
||||
let path = dir.join(".huskies_port");
|
||||
std::fs::write(&path, port.to_string()).ok()?;
|
||||
Some(path)
|
||||
}
|
||||
@@ -156,7 +156,7 @@ pub fn build_openapi_service(ctx: Arc<AppContext>) -> (ApiService, ApiService) {
|
||||
);
|
||||
|
||||
let api_service =
|
||||
OpenApiService::new(api, "Storkit API", "1.0").server("http://127.0.0.1:3001/api");
|
||||
OpenApiService::new(api, "Huskies API", "1.0").server("http://127.0.0.1:3001/api");
|
||||
|
||||
let docs_api = (
|
||||
ProjectApi { ctx: ctx.clone() },
|
||||
@@ -172,7 +172,7 @@ pub fn build_openapi_service(ctx: Arc<AppContext>) -> (ApiService, ApiService) {
|
||||
);
|
||||
|
||||
let docs_service =
|
||||
OpenApiService::new(docs_api, "Storkit API", "1.0").server("http://127.0.0.1:3001/api");
|
||||
OpenApiService::new(docs_api, "Huskies API", "1.0").server("http://127.0.0.1:3001/api");
|
||||
|
||||
(api_service, docs_service)
|
||||
}
|
||||
@@ -221,7 +221,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn resolve_port_returns_a_valid_port() {
|
||||
// Exercises the resolve_port code path (reads STORKIT_PORT env var or defaults).
|
||||
// Exercises the resolve_port code path (reads HUSKIES_PORT env var or defaults).
|
||||
let port = resolve_port();
|
||||
assert!(port > 0);
|
||||
}
|
||||
|
||||
@@ -289,7 +289,7 @@ pub async fn oauth_callback(
|
||||
html_response(
|
||||
StatusCode::OK,
|
||||
"Authenticated!",
|
||||
"Claude OAuth login successful. You can close this tab and return to Storkit.",
|
||||
"Claude OAuth login successful. You can close this tab and return to Huskies.",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ mod tests {
|
||||
#[test]
|
||||
fn editor_command_survives_reload() {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let store_path = dir.path().join(".storkit_store.json");
|
||||
let store_path = dir.path().join(".huskies_store.json");
|
||||
|
||||
{
|
||||
let ctx = AppContext::new_test(dir.path().to_path_buf());
|
||||
|
||||
@@ -168,7 +168,7 @@ mod tests {
|
||||
fn setup() -> (TempDir, TestClient<impl poem::Endpoint>) {
|
||||
let dir = TempDir::new().unwrap();
|
||||
let root = dir.path().to_path_buf();
|
||||
std::fs::create_dir_all(root.join(".storkit")).unwrap();
|
||||
std::fs::create_dir_all(root.join(".huskies")).unwrap();
|
||||
|
||||
let ctx = Arc::new(AppContext::new_test(root.clone()));
|
||||
let api = WizardApi { ctx };
|
||||
|
||||
@@ -24,7 +24,7 @@ pub fn create_bug_file(
|
||||
}
|
||||
|
||||
let filename = format!("{bug_number}_bug_{slug}.md");
|
||||
let bugs_dir = root.join(".storkit").join("work").join("1_backlog");
|
||||
let bugs_dir = root.join(".huskies").join("work").join("1_backlog");
|
||||
fs::create_dir_all(&bugs_dir)
|
||||
.map_err(|e| format!("Failed to create backlog directory: {e}"))?;
|
||||
|
||||
@@ -88,7 +88,7 @@ pub fn create_spike_file(
|
||||
}
|
||||
|
||||
let filename = format!("{spike_number}_spike_{slug}.md");
|
||||
let backlog_dir = root.join(".storkit").join("work").join("1_backlog");
|
||||
let backlog_dir = root.join(".huskies").join("work").join("1_backlog");
|
||||
fs::create_dir_all(&backlog_dir)
|
||||
.map_err(|e| format!("Failed to create backlog directory: {e}"))?;
|
||||
|
||||
@@ -151,7 +151,7 @@ pub fn create_refactor_file(
|
||||
}
|
||||
|
||||
let filename = format!("{refactor_number}_refactor_{slug}.md");
|
||||
let backlog_dir = root.join(".storkit").join("work").join("1_backlog");
|
||||
let backlog_dir = root.join(".huskies").join("work").join("1_backlog");
|
||||
fs::create_dir_all(&backlog_dir)
|
||||
.map_err(|e| format!("Failed to create backlog directory: {e}"))?;
|
||||
|
||||
@@ -227,7 +227,7 @@ fn extract_bug_name(path: &Path) -> Option<String> {
|
||||
///
|
||||
/// Returns a sorted list of `(bug_id, name)` pairs.
|
||||
pub fn list_bug_files(root: &Path) -> Result<Vec<(String, String)>, String> {
|
||||
let backlog_dir = root.join(".storkit").join("work").join("1_backlog");
|
||||
let backlog_dir = root.join(".huskies").join("work").join("1_backlog");
|
||||
if !backlog_dir.exists() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
@@ -277,7 +277,7 @@ fn is_refactor_item(stem: &str) -> bool {
|
||||
///
|
||||
/// Returns a sorted list of `(refactor_id, name)` pairs.
|
||||
pub fn list_refactor_files(root: &Path) -> Result<Vec<(String, String)>, String> {
|
||||
let backlog_dir = root.join(".storkit").join("work").join("1_backlog");
|
||||
let backlog_dir = root.join(".huskies").join("work").join("1_backlog");
|
||||
if !backlog_dir.exists() {
|
||||
return Ok(Vec::new());
|
||||
}
|
||||
@@ -357,7 +357,7 @@ mod tests {
|
||||
#[test]
|
||||
fn next_item_number_increments_from_existing_bugs() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let backlog = tmp.path().join(".storkit/work/1_backlog");
|
||||
let backlog = tmp.path().join(".huskies/work/1_backlog");
|
||||
fs::create_dir_all(&backlog).unwrap();
|
||||
fs::write(backlog.join("1_bug_crash.md"), "").unwrap();
|
||||
fs::write(backlog.join("3_bug_another.md"), "").unwrap();
|
||||
@@ -367,8 +367,8 @@ mod tests {
|
||||
#[test]
|
||||
fn next_item_number_scans_archived_too() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let backlog = tmp.path().join(".storkit/work/1_backlog");
|
||||
let archived = tmp.path().join(".storkit/work/5_done");
|
||||
let backlog = tmp.path().join(".huskies/work/1_backlog");
|
||||
let archived = tmp.path().join(".huskies/work/5_done");
|
||||
fs::create_dir_all(&backlog).unwrap();
|
||||
fs::create_dir_all(&archived).unwrap();
|
||||
fs::write(archived.join("5_bug_old.md"), "").unwrap();
|
||||
@@ -385,8 +385,8 @@ mod tests {
|
||||
#[test]
|
||||
fn list_bug_files_excludes_archive_subdir() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let backlog_dir = tmp.path().join(".storkit/work/1_backlog");
|
||||
let archived_dir = tmp.path().join(".storkit/work/5_done");
|
||||
let backlog_dir = tmp.path().join(".huskies/work/1_backlog");
|
||||
let archived_dir = tmp.path().join(".huskies/work/5_done");
|
||||
fs::create_dir_all(&backlog_dir).unwrap();
|
||||
fs::create_dir_all(&archived_dir).unwrap();
|
||||
fs::write(backlog_dir.join("1_bug_open.md"), "# Bug 1: Open Bug\n").unwrap();
|
||||
@@ -401,7 +401,7 @@ mod tests {
|
||||
#[test]
|
||||
fn list_bug_files_sorted_by_id() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let backlog_dir = tmp.path().join(".storkit/work/1_backlog");
|
||||
let backlog_dir = tmp.path().join(".huskies/work/1_backlog");
|
||||
fs::create_dir_all(&backlog_dir).unwrap();
|
||||
fs::write(backlog_dir.join("3_bug_third.md"), "# Bug 3: Third\n").unwrap();
|
||||
fs::write(backlog_dir.join("1_bug_first.md"), "# Bug 1: First\n").unwrap();
|
||||
@@ -443,7 +443,7 @@ mod tests {
|
||||
|
||||
let filepath = tmp
|
||||
.path()
|
||||
.join(".storkit/work/1_backlog/1_bug_login_crash.md");
|
||||
.join(".huskies/work/1_backlog/1_bug_login_crash.md");
|
||||
assert!(filepath.exists());
|
||||
let contents = fs::read_to_string(&filepath).unwrap();
|
||||
assert!(
|
||||
@@ -487,7 +487,7 @@ mod tests {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let filepath = tmp.path().join(".storkit/work/1_backlog/1_bug_some_bug.md");
|
||||
let filepath = tmp.path().join(".huskies/work/1_backlog/1_bug_some_bug.md");
|
||||
let contents = fs::read_to_string(&filepath).unwrap();
|
||||
assert!(
|
||||
contents.starts_with("---\nname: \"Some Bug\"\n---"),
|
||||
@@ -509,7 +509,7 @@ mod tests {
|
||||
|
||||
let filepath = tmp
|
||||
.path()
|
||||
.join(".storkit/work/1_backlog/1_spike_filesystem_watcher_architecture.md");
|
||||
.join(".huskies/work/1_backlog/1_spike_filesystem_watcher_architecture.md");
|
||||
assert!(filepath.exists());
|
||||
let contents = fs::read_to_string(&filepath).unwrap();
|
||||
assert!(
|
||||
@@ -533,7 +533,7 @@ mod tests {
|
||||
create_spike_file(tmp.path(), "FS Watcher Spike", Some(description)).unwrap();
|
||||
|
||||
let filepath =
|
||||
tmp.path().join(".storkit/work/1_backlog/1_spike_fs_watcher_spike.md");
|
||||
tmp.path().join(".huskies/work/1_backlog/1_spike_fs_watcher_spike.md");
|
||||
let contents = fs::read_to_string(&filepath).unwrap();
|
||||
assert!(contents.contains(description));
|
||||
}
|
||||
@@ -543,7 +543,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
create_spike_file(tmp.path(), "My Spike", None).unwrap();
|
||||
|
||||
let filepath = tmp.path().join(".storkit/work/1_backlog/1_spike_my_spike.md");
|
||||
let filepath = tmp.path().join(".huskies/work/1_backlog/1_spike_my_spike.md");
|
||||
let contents = fs::read_to_string(&filepath).unwrap();
|
||||
// Should have placeholder TBD in Question section
|
||||
assert!(contents.contains("## Question\n\n- TBD\n"));
|
||||
@@ -564,7 +564,7 @@ mod tests {
|
||||
let result = create_spike_file(tmp.path(), name, None);
|
||||
assert!(result.is_ok(), "create_spike_file failed: {result:?}");
|
||||
|
||||
let backlog = tmp.path().join(".storkit/work/1_backlog");
|
||||
let backlog = tmp.path().join(".huskies/work/1_backlog");
|
||||
let spike_id = result.unwrap();
|
||||
let filename = format!("{spike_id}.md");
|
||||
let contents = fs::read_to_string(backlog.join(&filename)).unwrap();
|
||||
@@ -576,7 +576,7 @@ mod tests {
|
||||
#[test]
|
||||
fn create_spike_file_increments_from_existing_items() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let backlog = tmp.path().join(".storkit/work/1_backlog");
|
||||
let backlog = tmp.path().join(".huskies/work/1_backlog");
|
||||
fs::create_dir_all(&backlog).unwrap();
|
||||
fs::write(backlog.join("5_story_existing.md"), "").unwrap();
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ fn load_stage_items(
|
||||
agent_map: &HashMap<String, AgentAssignment>,
|
||||
) -> Result<Vec<UpcomingStory>, String> {
|
||||
let root = ctx.state.get_project_root()?;
|
||||
let dir = root.join(".storkit").join("work").join(stage_dir);
|
||||
let dir = root.join(".huskies").join("work").join(stage_dir);
|
||||
|
||||
if !dir.exists() {
|
||||
return Ok(Vec::new());
|
||||
@@ -166,8 +166,8 @@ pub fn validate_story_dirs(
|
||||
|
||||
// Directories to validate: work/2_current/ + work/1_backlog/
|
||||
let dirs_to_validate: Vec<PathBuf> = vec![
|
||||
root.join(".storkit").join("work").join("2_current"),
|
||||
root.join(".storkit").join("work").join("1_backlog"),
|
||||
root.join(".huskies").join("work").join("2_current"),
|
||||
root.join(".huskies").join("work").join("1_backlog"),
|
||||
];
|
||||
|
||||
for dir in &dirs_to_validate {
|
||||
@@ -230,7 +230,7 @@ pub fn validate_story_dirs(
|
||||
/// Searches in priority order: 2_current, 1_backlog, 3_qa, 4_merge, 5_done, 6_archived.
|
||||
pub(super) fn find_story_file(project_root: &Path, story_id: &str) -> Result<PathBuf, String> {
|
||||
let filename = format!("{story_id}.md");
|
||||
let sk = project_root.join(".storkit").join("work");
|
||||
let sk = project_root.join(".huskies").join("work");
|
||||
for stage in &["2_current", "1_backlog", "3_qa", "4_merge", "5_done", "6_archived"] {
|
||||
let path = sk.join(stage).join(&filename);
|
||||
if path.exists() {
|
||||
@@ -370,7 +370,7 @@ pub(super) fn slugify_name(name: &str) -> String {
|
||||
|
||||
/// Scan all `work/` subdirectories for the highest item number across all types (stories, bugs, spikes).
|
||||
pub(super) fn next_item_number(root: &std::path::Path) -> Result<u32, String> {
|
||||
let work_base = root.join(".storkit").join("work");
|
||||
let work_base = root.join(".huskies").join("work");
|
||||
let mut max_num: u32 = 0;
|
||||
|
||||
for subdir in &["1_backlog", "2_current", "3_qa", "4_merge", "5_done", "6_archived"] {
|
||||
@@ -413,7 +413,7 @@ mod tests {
|
||||
("4_merge", "40_story_merge"),
|
||||
("5_done", "50_story_done"),
|
||||
] {
|
||||
let dir = root.join(".storkit").join("work").join(stage);
|
||||
let dir = root.join(".huskies").join("work").join(stage);
|
||||
fs::create_dir_all(&dir).unwrap();
|
||||
fs::write(
|
||||
dir.join(format!("{id}.md")),
|
||||
@@ -445,7 +445,7 @@ mod tests {
|
||||
fn load_upcoming_returns_empty_when_no_dir() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let root = tmp.path().to_path_buf();
|
||||
// No .storkit directory at all
|
||||
// No .huskies directory at all
|
||||
let ctx = crate::http::context::AppContext::new_test(root);
|
||||
let result = load_upcoming_stories(&ctx).unwrap();
|
||||
assert!(result.is_empty());
|
||||
@@ -456,7 +456,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let root = tmp.path().to_path_buf();
|
||||
|
||||
let current = root.join(".storkit/work/2_current");
|
||||
let current = root.join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::write(
|
||||
current.join("10_story_test.md"),
|
||||
@@ -482,7 +482,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let root = tmp.path().to_path_buf();
|
||||
|
||||
let current = root.join(".storkit/work/2_current");
|
||||
let current = root.join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::write(
|
||||
current.join("11_story_done.md"),
|
||||
@@ -507,7 +507,7 @@ mod tests {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let root = tmp.path().to_path_buf();
|
||||
|
||||
let current = root.join(".storkit/work/2_current");
|
||||
let current = root.join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::write(
|
||||
current.join("12_story_pending.md"),
|
||||
@@ -529,7 +529,7 @@ mod tests {
|
||||
#[test]
|
||||
fn load_upcoming_parses_metadata() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let backlog = tmp.path().join(".storkit/work/1_backlog");
|
||||
let backlog = tmp.path().join(".huskies/work/1_backlog");
|
||||
fs::create_dir_all(&backlog).unwrap();
|
||||
fs::write(
|
||||
backlog.join("31_story_view_upcoming.md"),
|
||||
@@ -554,7 +554,7 @@ mod tests {
|
||||
#[test]
|
||||
fn load_upcoming_skips_non_md_files() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let backlog = tmp.path().join(".storkit/work/1_backlog");
|
||||
let backlog = tmp.path().join(".huskies/work/1_backlog");
|
||||
fs::create_dir_all(&backlog).unwrap();
|
||||
fs::write(backlog.join(".gitkeep"), "").unwrap();
|
||||
fs::write(
|
||||
@@ -572,8 +572,8 @@ mod tests {
|
||||
#[test]
|
||||
fn validate_story_dirs_valid_files() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let backlog = tmp.path().join(".storkit/work/1_backlog");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
let backlog = tmp.path().join(".huskies/work/1_backlog");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::create_dir_all(&backlog).unwrap();
|
||||
fs::write(
|
||||
@@ -596,7 +596,7 @@ mod tests {
|
||||
#[test]
|
||||
fn validate_story_dirs_missing_front_matter() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::write(current.join("28_story_todos.md"), "# No front matter\n").unwrap();
|
||||
|
||||
@@ -609,7 +609,7 @@ mod tests {
|
||||
#[test]
|
||||
fn validate_story_dirs_missing_required_fields() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::write(current.join("28_story_todos.md"), "---\n---\n# Story\n").unwrap();
|
||||
|
||||
@@ -667,7 +667,7 @@ mod tests {
|
||||
#[test]
|
||||
fn next_item_number_empty_dirs() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let base = tmp.path().join(".storkit/work/1_backlog");
|
||||
let base = tmp.path().join(".huskies/work/1_backlog");
|
||||
fs::create_dir_all(&base).unwrap();
|
||||
assert_eq!(next_item_number(tmp.path()).unwrap(), 1);
|
||||
}
|
||||
@@ -675,9 +675,9 @@ mod tests {
|
||||
#[test]
|
||||
fn next_item_number_scans_all_dirs() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let backlog = tmp.path().join(".storkit/work/1_backlog");
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let archived = tmp.path().join(".storkit/work/5_done");
|
||||
let backlog = tmp.path().join(".huskies/work/1_backlog");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
let archived = tmp.path().join(".huskies/work/5_done");
|
||||
fs::create_dir_all(&backlog).unwrap();
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::create_dir_all(&archived).unwrap();
|
||||
@@ -690,7 +690,7 @@ mod tests {
|
||||
#[test]
|
||||
fn next_item_number_no_work_dirs() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
// No .storkit at all
|
||||
// No .huskies at all
|
||||
assert_eq!(next_item_number(tmp.path()).unwrap(), 1);
|
||||
}
|
||||
|
||||
@@ -699,8 +699,8 @@ mod tests {
|
||||
#[test]
|
||||
fn find_story_file_searches_current_then_backlog() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let backlog = tmp.path().join(".storkit/work/1_backlog");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
let backlog = tmp.path().join(".huskies/work/1_backlog");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::create_dir_all(&backlog).unwrap();
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ pub fn create_story_file(
|
||||
}
|
||||
|
||||
let filename = format!("{story_number}_story_{slug}.md");
|
||||
let backlog_dir = root.join(".storkit").join("work").join("1_backlog");
|
||||
let backlog_dir = root.join(".huskies").join("work").join("1_backlog");
|
||||
fs::create_dir_all(&backlog_dir)
|
||||
.map_err(|e| format!("Failed to create backlog directory: {e}"))?;
|
||||
|
||||
@@ -281,7 +281,7 @@ mod tests {
|
||||
#[test]
|
||||
fn create_story_writes_correct_content() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let backlog = tmp.path().join(".storkit/work/1_backlog");
|
||||
let backlog = tmp.path().join(".huskies/work/1_backlog");
|
||||
fs::create_dir_all(&backlog).unwrap();
|
||||
fs::write(backlog.join("36_story_existing.md"), "").unwrap();
|
||||
|
||||
@@ -324,7 +324,7 @@ mod tests {
|
||||
let result = create_story_file(tmp.path(), name, None, None, false);
|
||||
assert!(result.is_ok(), "create_story_file failed: {result:?}");
|
||||
|
||||
let backlog = tmp.path().join(".storkit/work/1_backlog");
|
||||
let backlog = tmp.path().join(".huskies/work/1_backlog");
|
||||
let story_id = result.unwrap();
|
||||
let filename = format!("{story_id}.md");
|
||||
let contents = fs::read_to_string(backlog.join(&filename)).unwrap();
|
||||
@@ -336,7 +336,7 @@ mod tests {
|
||||
#[test]
|
||||
fn create_story_rejects_duplicate() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let backlog = tmp.path().join(".storkit/work/1_backlog");
|
||||
let backlog = tmp.path().join(".huskies/work/1_backlog");
|
||||
fs::create_dir_all(&backlog).unwrap();
|
||||
|
||||
let filepath = backlog.join("1_story_my_feature.md");
|
||||
@@ -352,7 +352,7 @@ mod tests {
|
||||
fn check_criterion_marks_first_unchecked() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
setup_git_repo(tmp.path());
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let filepath = current.join("1_test.md");
|
||||
fs::write(&filepath, story_with_criteria(3)).unwrap();
|
||||
@@ -379,7 +379,7 @@ mod tests {
|
||||
fn check_criterion_marks_second_unchecked() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
setup_git_repo(tmp.path());
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let filepath = current.join("2_test.md");
|
||||
fs::write(&filepath, story_with_criteria(3)).unwrap();
|
||||
@@ -406,7 +406,7 @@ mod tests {
|
||||
fn check_criterion_out_of_range_returns_error() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
setup_git_repo(tmp.path());
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let filepath = current.join("3_test.md");
|
||||
fs::write(&filepath, story_with_criteria(2)).unwrap();
|
||||
@@ -440,7 +440,7 @@ mod tests {
|
||||
#[test]
|
||||
fn add_criterion_appends_after_last_criterion() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let filepath = current.join("10_test.md");
|
||||
fs::write(&filepath, story_with_ac_section(&["First", "Second"])).unwrap();
|
||||
@@ -460,7 +460,7 @@ mod tests {
|
||||
#[test]
|
||||
fn add_criterion_to_empty_section() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let filepath = current.join("11_test.md");
|
||||
let content = "---\nname: Test\n---\n\n## Acceptance Criteria\n\n## Out of Scope\n\n- N/A\n";
|
||||
@@ -475,7 +475,7 @@ mod tests {
|
||||
#[test]
|
||||
fn add_criterion_missing_section_returns_error() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let filepath = current.join("12_test.md");
|
||||
fs::write(&filepath, "---\nname: Test\n---\n\nNo AC section here.\n").unwrap();
|
||||
@@ -490,7 +490,7 @@ mod tests {
|
||||
#[test]
|
||||
fn update_story_replaces_user_story_section() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let filepath = current.join("20_test.md");
|
||||
let content = "---\nname: T\n---\n\n## User Story\n\nOld text\n\n## Acceptance Criteria\n\n- [ ] AC\n";
|
||||
@@ -507,7 +507,7 @@ mod tests {
|
||||
#[test]
|
||||
fn update_story_replaces_description_section() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let filepath = current.join("21_test.md");
|
||||
let content = "---\nname: T\n---\n\n## Description\n\nOld description\n\n## Acceptance Criteria\n\n- [ ] AC\n";
|
||||
@@ -523,7 +523,7 @@ mod tests {
|
||||
#[test]
|
||||
fn update_story_no_args_returns_error() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::write(current.join("22_test.md"), "---\nname: T\n---\n").unwrap();
|
||||
|
||||
@@ -535,7 +535,7 @@ mod tests {
|
||||
#[test]
|
||||
fn update_story_missing_section_returns_error() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::write(
|
||||
current.join("23_test.md"),
|
||||
@@ -551,7 +551,7 @@ mod tests {
|
||||
#[test]
|
||||
fn update_story_sets_agent_front_matter_field() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let filepath = current.join("24_test.md");
|
||||
fs::write(&filepath, "---\nname: T\n---\n\n## User Story\n\nSome story\n").unwrap();
|
||||
@@ -568,7 +568,7 @@ mod tests {
|
||||
#[test]
|
||||
fn update_story_sets_arbitrary_front_matter_fields() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let filepath = current.join("25_test.md");
|
||||
fs::write(&filepath, "---\nname: T\n---\n\n## User Story\n\nSome story\n").unwrap();
|
||||
@@ -587,7 +587,7 @@ mod tests {
|
||||
#[test]
|
||||
fn update_story_front_matter_only_no_section_required() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
// File without a User Story section — front matter update should succeed
|
||||
let filepath = current.join("26_test.md");
|
||||
@@ -605,7 +605,7 @@ mod tests {
|
||||
#[test]
|
||||
fn update_story_bool_front_matter_written_unquoted() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let filepath = current.join("27_test.md");
|
||||
fs::write(&filepath, "---\nname: T\n---\n\nNo sections.\n").unwrap();
|
||||
@@ -622,7 +622,7 @@ mod tests {
|
||||
#[test]
|
||||
fn update_story_integer_front_matter_written_unquoted() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let filepath = current.join("28_test.md");
|
||||
fs::write(&filepath, "---\nname: T\n---\n\nNo sections.\n").unwrap();
|
||||
@@ -639,7 +639,7 @@ mod tests {
|
||||
#[test]
|
||||
fn update_story_bool_front_matter_parseable_after_write() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let filepath = current.join("29_test.md");
|
||||
fs::write(&filepath, "---\nname: My Story\n---\n\nNo sections.\n").unwrap();
|
||||
|
||||
@@ -5,7 +5,7 @@ use std::path::Path;
|
||||
|
||||
use super::{find_story_file, replace_or_append_section};
|
||||
|
||||
const TEST_RESULTS_MARKER: &str = "<!-- storkit-test-results:";
|
||||
const TEST_RESULTS_MARKER: &str = "<!-- huskies-test-results:";
|
||||
|
||||
/// Write (or overwrite) the `## Test Results` section in a story file.
|
||||
///
|
||||
@@ -160,7 +160,7 @@ mod tests {
|
||||
#[test]
|
||||
fn write_and_read_test_results_roundtrip() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::write(
|
||||
current.join("1_story_test.md"),
|
||||
@@ -186,7 +186,7 @@ mod tests {
|
||||
#[test]
|
||||
fn write_test_results_creates_readable_section() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let story_path = current.join("2_story_check.md");
|
||||
fs::write(
|
||||
@@ -203,7 +203,7 @@ mod tests {
|
||||
assert!(contents.contains("✅ unit-pass"));
|
||||
assert!(contents.contains("❌ unit-fail"));
|
||||
assert!(contents.contains("assertion failed"));
|
||||
assert!(contents.contains("storkit-test-results:"));
|
||||
assert!(contents.contains("huskies-test-results:"));
|
||||
// Original content still present
|
||||
assert!(contents.contains("## Acceptance Criteria"));
|
||||
}
|
||||
@@ -211,12 +211,12 @@ mod tests {
|
||||
#[test]
|
||||
fn write_test_results_overwrites_existing_section() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
let story_path = current.join("3_story_overwrite.md");
|
||||
fs::write(
|
||||
&story_path,
|
||||
"---\nname: Overwrite\n---\n# Story\n\n## Test Results\n\n<!-- storkit-test-results: {} -->\n\n### Unit Tests (0 passed, 0 failed)\n\n*No unit tests recorded.*\n",
|
||||
"---\nname: Overwrite\n---\n# Story\n\n## Test Results\n\n<!-- huskies-test-results: {} -->\n\n### Unit Tests (0 passed, 0 failed)\n\n*No unit tests recorded.*\n",
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@@ -233,7 +233,7 @@ mod tests {
|
||||
#[test]
|
||||
fn read_test_results_returns_none_when_no_section() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::write(
|
||||
current.join("4_story_empty.md"),
|
||||
@@ -255,7 +255,7 @@ mod tests {
|
||||
#[test]
|
||||
fn write_test_results_finds_story_in_any_stage() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let qa_dir = tmp.path().join(".storkit/work/3_qa");
|
||||
let qa_dir = tmp.path().join(".huskies/work/3_qa");
|
||||
fs::create_dir_all(&qa_dir).unwrap();
|
||||
fs::write(
|
||||
qa_dir.join("5_story_qa.md"),
|
||||
@@ -280,7 +280,7 @@ mod tests {
|
||||
#[test]
|
||||
fn write_coverage_baseline_to_story_file_updates_front_matter() {
|
||||
let tmp = tempfile::tempdir().unwrap();
|
||||
let current = tmp.path().join(".storkit/work/2_current");
|
||||
let current = tmp.path().join(".huskies/work/2_current");
|
||||
fs::create_dir_all(¤t).unwrap();
|
||||
fs::write(
|
||||
current.join("6_story_cov.md"),
|
||||
|
||||
@@ -64,8 +64,8 @@ pub struct WizardStepInfo {
|
||||
/// - `token` streams partial model output.
|
||||
/// - `update` pushes the updated message history.
|
||||
/// - `error` reports a request or processing failure.
|
||||
/// - `work_item_changed` notifies that a `.storkit/work/` file changed.
|
||||
/// - `agent_config_changed` notifies that `.storkit/project.toml` was modified.
|
||||
/// - `work_item_changed` notifies that a `.huskies/work/` file changed.
|
||||
/// - `agent_config_changed` notifies that `.huskies/project.toml` was modified.
|
||||
enum WsResponse {
|
||||
Token {
|
||||
content: String,
|
||||
@@ -97,7 +97,7 @@ enum WsResponse {
|
||||
merge: Vec<crate::http::workflow::UpcomingStory>,
|
||||
done: Vec<crate::http::workflow::UpcomingStory>,
|
||||
},
|
||||
/// `.storkit/project.toml` was modified; the frontend should re-fetch the
|
||||
/// `.huskies/project.toml` was modified; the frontend should re-fetch the
|
||||
/// agent roster. Does NOT trigger a pipeline state refresh.
|
||||
AgentConfigChanged,
|
||||
/// An agent's state changed (started, stopped, completed, etc.).
|
||||
@@ -773,14 +773,14 @@ mod tests {
|
||||
stage: "2_current".to_string(),
|
||||
item_id: "42_story_foo".to_string(),
|
||||
action: "start".to_string(),
|
||||
commit_msg: "storkit: start 42_story_foo".to_string(),
|
||||
commit_msg: "huskies: start 42_story_foo".to_string(),
|
||||
};
|
||||
let json = serde_json::to_value(&resp).unwrap();
|
||||
assert_eq!(json["type"], "work_item_changed");
|
||||
assert_eq!(json["stage"], "2_current");
|
||||
assert_eq!(json["item_id"], "42_story_foo");
|
||||
assert_eq!(json["action"], "start");
|
||||
assert_eq!(json["commit_msg"], "storkit: start 42_story_foo");
|
||||
assert_eq!(json["commit_msg"], "huskies: start 42_story_foo");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -901,7 +901,7 @@ mod tests {
|
||||
stage: "2_current".to_string(),
|
||||
item_id: "42_story_foo".to_string(),
|
||||
action: "start".to_string(),
|
||||
commit_msg: "storkit: start 42_story_foo".to_string(),
|
||||
commit_msg: "huskies: start 42_story_foo".to_string(),
|
||||
from_stage: None,
|
||||
};
|
||||
let ws_msg: Option<WsResponse> = evt.into();
|
||||
@@ -1166,7 +1166,7 @@ mod tests {
|
||||
|
||||
// Create minimal pipeline dirs so load_pipeline_state succeeds.
|
||||
for stage in &["1_backlog", "2_current", "3_qa", "4_merge"] {
|
||||
std::fs::create_dir_all(root.join(".storkit").join("work").join(stage)).unwrap();
|
||||
std::fs::create_dir_all(root.join(".huskies").join("work").join(stage)).unwrap();
|
||||
}
|
||||
|
||||
let ctx = Arc::new(AppContext::new_test(root));
|
||||
@@ -1381,7 +1381,7 @@ mod tests {
|
||||
stage: "2_current".to_string(),
|
||||
item_id: "99_story_test".to_string(),
|
||||
action: "start".to_string(),
|
||||
commit_msg: "storkit: start 99_story_test".to_string(),
|
||||
commit_msg: "huskies: start 99_story_test".to_string(),
|
||||
from_stage: None,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
Reference in New Issue
Block a user