speccoder_runtime/
quick.rs1use std::path::Path;
9
10use speccoder_core::{CommandResult, CommandStatus};
11
12#[must_use]
14pub fn change(
15 path: &Path,
16 request: &str,
17 checks: &[String],
18 run: bool,
19 via: speccoder_core::actions::Via,
20 options: &crate::RunOptions,
21 asked: Option<(&str, &Path)>,
22) -> CommandResult {
23 let format = crate::format::at(path);
24 let Some(root) = format.detect(path) else {
25 return CommandResult::new(
26 "do",
27 CommandStatus::NotAProject,
28 &(format.messages().not_a_project)(path),
29 )
30 .with_code("NO-PROJECT");
31 };
32 let rec = match format.record_change(&root, request, checks) {
33 Ok(r) => r,
34 Err(e) => {
35 return CommandResult::new("do", CommandStatus::UsageError, &e).with_code("USAGE");
36 }
37 };
38 let approved = crate::review::decide(
40 &root,
41 Some(&rec.feature),
42 std::slice::from_ref(&rec.requirement),
43 "approve",
44 None,
45 via,
46 );
47 if approved.status() != CommandStatus::Ok {
48 return CommandResult::carried("do", &approved);
49 }
50 if let Some((conversation, state)) = asked {
52 let _ = crate::ladder::record_origin(
53 &crate::worktree::project_state_dir(state, &root),
54 &speccoder_core::ladder::subject(&rec.feature, &rec.requirement),
55 vec![speccoder_core::ladder::asked(
56 conversation,
57 crate::bug::confirm::now(),
58 )],
59 );
60 }
61 let files = crate::hosting::uncommitted(&root, Some(&rec.feature));
63 if !files.is_empty() {
64 let committed = crate::hosting::commit_planning_as(
65 &root,
66 Some(&rec.feature),
67 &files,
68 Some(&format!("quick change: {}", rec.title)),
69 );
70 if committed.status() != CommandStatus::Ok {
71 return CommandResult::carried("do", &committed);
72 }
73 }
74 let said = format!(
75 "recorded \"{}\" as {} and {} in {}",
76 rec.title, rec.requirement, rec.task, rec.feature
77 );
78 if !run {
79 return CommandResult::ok(
80 "do",
81 &format!(
82 "{said}; run it: speccoder run --feature {} --task {}",
83 rec.feature, rec.task
84 ),
85 )
86 .with_next(vec![speccoder_core::next::NextStep {
87 command: format!(
88 "speccoder run --feature {} --task {}",
89 rec.feature, rec.task
90 ),
91 why: "the change, judged on evidence".to_owned(),
92 }]);
93 }
94 let result = crate::run(&root, Some(&rec.feature), &rec.task, options);
95 let status = result.status();
96 let message = format!("{said}; {}", result.message());
97 result.reworded(status, &message)
98}