diff --git a/server/src/agents.rs b/server/src/agents.rs index 7861bf9..325630e 100644 --- a/server/src/agents.rs +++ b/server/src/agents.rs @@ -249,11 +249,21 @@ impl AgentPool { } } + // Read story content from the project root (the worktree may not have + // .story_kit/work/) and inject it into the agent prompt so the coder + // knows what to implement. + let story_content = read_story_content(project_root, story_id); + // Spawn the agent process let wt_path_str = wt_info.path.to_string_lossy().to_string(); let (command, args, mut prompt) = config.render_agent_args(&wt_path_str, story_id, Some(&resolved_name), Some(&wt_info.base_branch))?; + // Prepend story content so the agent sees the requirements first. + if let Some(content) = story_content { + prompt = format!("## Story Requirements\n\n{content}\n\n---\n\n{prompt}"); + } + // Append resume context if this is a restart with failure information. if let Some(ctx) = resume_context { prompt.push_str(ctx); @@ -1359,6 +1369,20 @@ fn item_type_from_id(item_id: &str) -> &'static str { } /// Return the source directory path for a work item (always work/1_upcoming/). +/// Read story/bug content from any pipeline stage directory. +/// Returns the file contents if found, or None if the file doesn't exist anywhere. +fn read_story_content(project_root: &Path, story_id: &str) -> Option { + let sk = project_root.join(".story_kit").join("work"); + let filename = format!("{story_id}.md"); + for stage in &["2_current", "1_upcoming", "3_qa", "4_merge"] { + let path = sk.join(stage).join(&filename); + if let Ok(content) = std::fs::read_to_string(&path) { + return Some(content); + } + } + None +} + fn item_source_dir(project_root: &Path, _item_id: &str) -> PathBuf { project_root.join(".story_kit").join("work").join("1_upcoming") }