use std::collections::HashSet;
use std::path::PathBuf;
use std::sync::Arc;
use super::session::TaskSource;
#[derive(Debug, Clone)]
pub struct PlannedTask {
pub source: TaskSource,
pub source_name: String,
pub goal: String,
pub mount_paths: Vec<PathBuf>,
}
pub struct WorkPlanner {
skills: Arc<crate::skill::SkillManager>,
projects: Option<Arc<crate::project::ProjectManager>>,
mounts: Option<Arc<crate::mount::MountManager>>,
}
impl WorkPlanner {
pub fn new(
skills: Arc<crate::skill::SkillManager>,
projects: Option<Arc<crate::project::ProjectManager>>,
mounts: Option<Arc<crate::mount::MountManager>>,
) -> Self {
Self {
skills,
projects,
mounts,
}
}
pub async fn next_task(&self, done_goals: &HashSet<String>) -> Option<PlannedTask> {
for entry in self.skills.list_skills().await {
let autonomous = entry
.metadata
.as_ref()
.map(|m| m.autonomous)
.unwrap_or(false);
if !autonomous {
continue;
}
let goal = format!(
"[Skill: {}] {}. Perform this skill's routine work autonomously and \
summarize the outcome. Do not make destructive changes or run deploys.",
entry.skill.name, entry.skill.description
);
if done_goals.contains(&goal) {
continue;
}
return Some(PlannedTask {
source: TaskSource::Skill,
source_name: entry.skill.name.clone(),
goal,
mount_paths: Vec::new(),
});
}
if let Some(pm) = &self.projects {
for proj in pm.list_projects() {
if proj.mount_ids.is_empty() {
continue;
}
let mount_paths = self.resolve_mount_paths(&proj.mount_ids);
if mount_paths.is_empty() {
continue;
}
let goal = format!(
"[Project: {}] Review the recent state of this project: summarize open \
work, recent changes, and any TODOs/FIXMEs. Read-only — do not modify \
files or run deploys.",
proj.name
);
if done_goals.contains(&goal) {
continue;
}
return Some(PlannedTask {
source: TaskSource::Project,
source_name: proj.name.clone(),
goal,
mount_paths,
});
}
}
None
}
fn resolve_mount_paths(&self, mount_ids: &[crate::mount::MountId]) -> Vec<PathBuf> {
let Some(mm) = &self.mounts else {
return Vec::new();
};
let mut paths = Vec::new();
for mount in mm.get_mounts_ordered(mount_ids) {
for p in &mount.paths {
if !paths.contains(p) {
paths.push(p.clone());
}
}
}
paths
}
}