feat: implement company pipeline orchestration with user commands for full, quick, and skip modes

This commit is contained in:
asepharyana
2026-07-13 05:33:04 +07:00
parent a6eed9e574
commit 2310c2df7f
9 changed files with 269 additions and 94 deletions
+43 -28
View File
@@ -185,7 +185,11 @@ pub fn run_company_pipeline_quick(
Ok(build_executive_summary(user_request, &results, &all_findings, quick_divisions))
}
/// Build a consolidated executive summary from pipeline results.
/// Build a compressed executive summary from pipeline results.
///
/// Keeps output brief to save context window space — just division verdicts
/// and key findings, not full outputs. Full results are accessible to the
/// CEO via the notes/findings that were archived during execution.
fn build_executive_summary(
request: &str,
results: &[String],
@@ -193,41 +197,23 @@ fn build_executive_summary(
divisions: &[division::Division],
) -> String {
let mut summary = String::new();
summary.push_str(&format!("# Company Pipeline — Executive Summary\n\n"));
summary.push_str(&format!("**Request**: {}\n\n", request));
summary.push_str("## Division Results\n\n");
summary.push_str(&format!("Pipeline for: {}\n", request));
for (i, div) in divisions.iter().enumerate() {
let result_summary = results.get(i)
let verdict = results.get(i)
.map(|r| {
let first_line = r.lines().next().unwrap_or(r);
if first_line.len() > 120 {
format!("{}...", &first_line[..117])
} else {
first_line.to_string()
}
r.lines().next().unwrap_or(r)
.chars().take(100).collect::<String>()
})
.unwrap_or_else(|| "No output".to_string());
.unwrap_or_else(|| "".to_string());
summary.push_str(&format!("### {} Division\n", div.name));
summary.push_str(&format!("- Role: {}\n", div.description));
summary.push_str(&format!("- Result: {}\n\n", result_summary));
summary.push_str(&format!(" {}: {}\n", div.name, verdict));
}
if !findings.is_empty() {
summary.push_str("## Cross-Division Findings\n\n");
for (i, f) in findings.iter().enumerate() {
summary.push_str(&format!("{}. {}\n", i + 1, f));
}
summary.push_str("\n");
summary.push_str(&format!(" Notes: {} cross-division finding(s)\n", findings.len()));
}
summary.push_str("---\n");
summary.push_str(&format!(
"Pipeline completed: {} division(s) executed.\n",
divisions.len(),
));
summary
}
@@ -239,14 +225,43 @@ fn build_executive_summary(
///
/// Used by the auto-CEO pipeline trigger in run_agent_turn to decide
/// whether to delegate to the full company pipeline or handle directly.
///
/// Heuristics:
/// - Very short requests (< 10 chars) are never complex.
/// - Negative keywords (simple/trivial/typo/quick) skip the pipeline.
/// - Positive keywords (refactor/api/implement/architecture) trigger it.
/// - Multi-line or multi-sentence requests are more likely complex.
pub fn is_complex_request(request: &str) -> bool {
let trimmed = request.trim();
// Very short requests are never complex
if trimmed.len() < 10 {
return false;
}
// Single-line simple update patterns
let lower = trimmed.to_lowercase();
let negative_keywords = [
"simple", "trivial", "typo", "just a", "only a", "minor",
"quick", "tiny", "small fix", "rename", "nitpick",
"cosmetic", "formatting", "spelling", "grammar",
"bump", "version bump", "update comment",
];
if negative_keywords.iter().any(|k| lower.contains(k)) {
return false;
}
// Multi-line/multi-sentence → likely complex
let sentences = trimmed.split(|c| c == '.' || c == '!' || c == '?')
.filter(|s| !s.trim().is_empty())
.count();
if sentences >= 3 {
return true;
}
// Positive complexity keywords
let complexity_keywords = [
"refactor", "redesign", "architecture", "feature", "implement",
"migrate", "restructure", "rewrite", "new module", "new component",
"scaffold", "multi", "multiple files", "api", "endpoint",
"integration", "system", "workflow", "pipeline", "database",
"authentication", "authorization",
"authentication", "authorization", "full stack",
];
let lower = request.to_lowercase();
complexity_keywords.iter().any(|k| lower.contains(k))
}