use std::path::Path;
pub use crate::RunOptions;
pub mod actions {
pub use crate::actions::{Dispatch, Interface, Offer, available, list, offered, perform};
}
pub mod person {
pub use crate::person::{Stream, TIMEOUT, open_terminal, run, sandboxed, shown, speccoder};
}
pub mod copy {
pub use crate::copy::block;
pub use crate::doctor::bug_report;
}
pub mod approvals {
pub use crate::approvals::{Answer, Approver, Presentation, Who, list};
}
pub mod progress {
pub use crate::progress::{Event, Progress, read_live};
}
pub mod status {
pub use crate::status::report;
}
pub mod shape {
pub use crate::shape::{FIELDS, findings, judge, shape};
}
pub mod ask {
pub use crate::ask::chips::{ChipRef, resolve as chip};
pub use crate::ask::proposal::revalidate as proposal;
pub use crate::ask::{Request, changed_since, conversations, new_id, stop, thread, turn};
}
pub mod explore {
pub use crate::explore::{list, session};
}
pub mod next {
pub use crate::next::attach;
}
pub mod review {
pub use crate::comments::list as comments;
pub use crate::review::{readiness, report};
}
pub mod planning {
pub use crate::planning_chain::{similar_command, similar_to, suggest};
}
pub mod new_project {
pub use crate::new_project::{Creation, creation, first_feature, plan, undo_offer};
pub use speccoder_core::new_project::{NewChoices, Stack, slug};
}
pub mod welcome {
pub use crate::welcome::{
Recent, RecentView, default_parent, folders, forget, is_project, remember, tilde, views,
};
}
pub mod sample {
pub use crate::sample::{available, copy, home, missing_tool};
}
pub mod inbox {
pub use crate::inbox::inbox;
}
pub mod bug {
pub use crate::bug::show;
}
pub mod evidence {
pub use crate::evidence::{account_of, banner, rows, statements};
}
pub mod tour {
use std::path::Path;
use speccoder_core::{CommandData, CommandResult, CommandStatus};
#[must_use]
pub fn of_task(root: &Path, feature: Option<&str>, task: &str) -> CommandResult {
let selected = || match crate::inspect(root, None).data() {
Some(CommandData::Inspect(snap)) => {
snap.feature.as_ref().map(|f| f.feature.name.clone())
}
_ => None,
};
let Some(feature) = feature.map(str::to_owned).or_else(selected) else {
return CommandResult::new(
"tour",
CommandStatus::SelectionNeeded,
"no feature selected",
);
};
let branch = format!("speccoder/{feature}");
let head = if crate::git::Git::at(root)
.line(&[
"rev-parse",
"--verify",
"--quiet",
&format!("refs/heads/{branch}"),
])
.is_ok_and(|l| !l.trim().is_empty())
{
branch
} else {
"HEAD".to_owned()
};
crate::tour::command(
root,
&feature,
task,
&crate::attest::branch::Options {
head,
..crate::attest::branch::Options::default()
},
"tour",
)
}
}
pub mod documents {
pub use crate::documents::{of_feature, render};
}
pub mod doctor {
pub use crate::doctor::{agents, run};
}
pub mod settings {
pub use crate::approvals::coverage::show as coverage;
pub use crate::settings::{apply, commit, show};
}
pub mod controls {
pub use crate::controls::{SkillCost, cost, skill_cost};
}
pub mod code {
pub use crate::code::{changes, file, tree, worktree_file, worktree_view};
}
pub mod init {
pub use crate::init::{commit_written, finish, prepare, preview, root_of};
}
pub mod current {
pub use crate::current::list;
}
pub mod handoff {
pub use crate::handoff::{PrOptions, pr};
}
pub mod worktree {
pub use crate::worktree::state_dir;
}
pub mod github {
pub use crate::github::{
Ctx, RepoEntry, description, issue, issues, login_with_token, pr_for, reach, refused, repo,
repositories, started, status, valid_slug,
};
pub mod auth {
pub use crate::github::auth::{Pending, client_id, start, wait};
}
pub mod http {
pub use crate::github::http::oauth;
}
}
#[derive(Debug, Clone, Copy)]
pub struct FormatWords {
pub name: &'static str,
pub unit: &'static str,
}
#[must_use]
pub fn format_words(root: &Path) -> FormatWords {
let format = crate::format::at(root);
FormatWords {
name: format.wording().name,
unit: format.messages().unit,
}
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct PlanningWords {
pub creates: Option<String>,
pub toward: Vec<String>,
pub checks: Option<String>,
pub revises: Option<String>,
}
#[must_use]
pub fn planning_words(root: &Path) -> PlanningWords {
let p = crate::format::at(root).planning();
let full = |s: &str| format!("{}{s}", p.prefix);
let creates = p.creates.first().map(|c| full(c));
PlanningWords {
toward: (p.toward_tasks)(false)
.iter()
.map(|s| full(s))
.filter(|s| Some(s) != creates.as_ref())
.collect(),
creates,
checks: p.checks.map(full),
revises: p.revises.map(full),
}
}
#[must_use]
pub fn create_skill(root: &Path) -> Option<String> {
let planning = crate::format::at(root).planning();
planning
.creates
.first()
.map(|c| format!("{}{c}", planning.prefix))
}
#[must_use]
pub fn commit_patch(root: &Path, commit: &str) -> String {
commit_patch_of(root, commit)
}
fn reachable(git: &crate::git::Git, id: &str) -> bool {
if git
.succeeds(&["merge-base", "--is-ancestor", id, "HEAD"])
.unwrap_or(false)
{
return true;
}
git.line(&[
"for-each-ref",
"--count=1",
"--format=%(refname)",
"--contains",
id,
"refs/heads",
"refs/speccoder/attempts",
])
.is_ok_and(|l| !l.trim().is_empty())
}
fn commit_patch_of(root: &Path, commit: &str) -> String {
let git = crate::git::Git::at(root);
let Ok(id) = git.line(&[
"rev-parse",
"--verify",
"-q",
&format!("{commit}^{{commit}}"),
]) else {
return String::new();
};
if !reachable(&git, &id) {
return String::new();
}
git.run(&["show", "--no-color", "--patch", "--format=%h %s%n%n%b", &id])
.map(|b| String::from_utf8_lossy(&b).into_owned())
.unwrap_or_default()
}