storkit: merge 433_story_setup_wizard_interviews_user_on_bare_projects_with_no_existing_code

This commit is contained in:
dave
2026-03-29 00:42:57 +00:00
parent a70a06a5fb
commit fec417cb16
3 changed files with 215 additions and 23 deletions
+1 -1
View File
@@ -180,7 +180,7 @@ pub fn commands() -> &'static [BotCommand] {
},
BotCommand {
name: "setup",
description: "Show setup wizard progress; or `setup confirm` / `setup skip` / `setup retry` to drive the wizard from chat",
description: "Show setup wizard progress; or `setup generate` / `setup confirm` / `setup skip` / `setup retry` to drive the wizard from chat",
handler: setup::handle_setup,
},
]
+75 -2
View File
@@ -9,7 +9,9 @@
//! - `setup retry` — discard staged content and reset the current step
use super::CommandContext;
use crate::http::mcp::wizard_tools::{is_script_step, step_output_path, write_if_missing};
use crate::http::mcp::wizard_tools::{
generation_hint, is_script_step, step_output_path, write_if_missing,
};
use crate::io::wizard::{format_wizard_state, StepStatus, WizardState};
pub(super) fn handle_setup(ctx: &CommandContext) -> Option<String> {
@@ -17,15 +19,45 @@ pub(super) fn handle_setup(ctx: &CommandContext) -> Option<String> {
match sub.as_str() {
"" => Some(wizard_status_reply(ctx)),
"generate" => Some(wizard_generate_reply(ctx)),
"confirm" => Some(wizard_confirm_reply(ctx)),
"skip" => Some(wizard_skip_reply(ctx)),
"retry" => Some(wizard_retry_reply(ctx)),
_ => Some(format!(
"Unknown sub-command `{sub}`. Usage: `setup`, `setup confirm`, `setup skip`, `setup retry`."
"Unknown sub-command `{sub}`. Usage: `setup`, `setup generate`, `setup confirm`, `setup skip`, `setup retry`."
)),
}
}
/// Mark the current step as generating and return the generation hint.
///
/// This mirrors `wizard_generate` (with no content) from the MCP tools, making
/// the interview flow accessible from chat transports (Matrix, Slack, WhatsApp).
fn wizard_generate_reply(ctx: &CommandContext) -> String {
let root = ctx.project_root;
let mut state = match WizardState::load(root) {
Some(s) => s,
None => return "No wizard active.".to_string(),
};
if state.completed {
return "Wizard is already complete.".to_string();
}
let idx = state.current_step_index();
let step = state.steps[idx].step;
state.set_step_status(step, StepStatus::Generating, None);
if let Err(e) = state.save(root) {
return format!("Failed to save wizard state: {e}");
}
let hint = generation_hint(step, root);
format!(
"Step '{}' marked as generating.\n\n{hint}\n\nOnce you have the content, stage it via the API and then run `setup confirm` to write it to disk.",
step.label()
)
}
/// Compose a status reply for the `setup` command (no args).
fn wizard_status_reply(ctx: &CommandContext) -> String {
match WizardState::load(ctx.project_root) {
@@ -263,4 +295,45 @@ mod tests {
assert!(result.contains("Unknown sub-command"));
assert!(result.contains("Usage"));
}
#[test]
fn setup_generate_marks_generating_and_returns_hint() {
let dir = TempDir::new().unwrap();
std::fs::create_dir_all(dir.path().join(".storkit")).unwrap();
WizardState::init_if_missing(dir.path());
let agents = Arc::new(crate::agents::AgentPool::new_test(4006));
let rooms = Arc::new(Mutex::new(HashSet::new()));
let ctx = make_ctx("generate", dir.path(), &agents, &rooms);
let result = handle_setup(&ctx).unwrap();
assert!(result.contains("generating"));
let state = WizardState::load(dir.path()).unwrap();
assert_eq!(
state.steps[1].status,
crate::io::wizard::StepStatus::Generating
);
}
#[test]
fn setup_generate_bare_project_asks_user() {
let dir = TempDir::new().unwrap();
// Bare project — only scaffolding files
std::fs::create_dir_all(dir.path().join(".storkit")).unwrap();
WizardState::init_if_missing(dir.path());
let agents = Arc::new(crate::agents::AgentPool::new_test(4007));
let rooms = Arc::new(Mutex::new(HashSet::new()));
let ctx = make_ctx("generate", dir.path(), &agents, &rooms);
let result = handle_setup(&ctx).unwrap();
assert!(result.contains("bare project"));
assert!(result.contains("Ask the user"));
}
#[test]
fn setup_generate_no_wizard_returns_error() {
let dir = TempDir::new().unwrap();
let agents = Arc::new(crate::agents::AgentPool::new_test(4008));
let rooms = Arc::new(Mutex::new(HashSet::new()));
let ctx = make_ctx("generate", dir.path(), &agents, &rooms);
let result = handle_setup(&ctx).unwrap();
assert!(result.contains("No wizard active"));
}
}