scud/lib.rs
1//! # SCUD - Fast, DAG-driven Task Manager
2//!
3//! SCUD (Simple, Concurrent, Unified, Directed) is a task management system designed
4//! for AI-driven development workflows. It provides both a CLI tool and a library
5//! for managing tasks organized in directed acyclic graphs (DAGs).
6//!
7//! ## Features
8//!
9//! - **DAG-based execution**: Tasks have dependencies that form a directed acyclic graph,
10//! ensuring work proceeds in the correct order
11//! - **Multi-phase support**: Organize tasks into phases (tags) for different project areas
12//! - **File locking**: Safe concurrent access with atomic read/write operations
13//! - **SCG format**: Token-efficient text format for task storage
14//! - **AI integration**: Parse PRDs and expand complex tasks using LLM providers
15//!
16//! ## Library Usage
17//!
18//! SCUD can be used as a library to programmatically manage tasks:
19//!
20//! ```no_run
21//! use scud::storage::Storage;
22//! use scud::models::{Phase, Task, TaskStatus};
23//!
24//! // Initialize storage (uses current directory by default)
25//! let storage = Storage::new(None);
26//!
27//! // Load all phases (task groups by tag)
28//! let phases = storage.load_tasks().expect("Failed to load tasks");
29//!
30//! // Load a specific phase by tag
31//! let auth_phase = storage.load_group("auth").expect("Phase not found");
32//!
33//! // Find the next available task (dependencies met, status pending)
34//! if let Some(next_task) = auth_phase.find_next_task() {
35//! println!("Next task: {} - {}", next_task.id, next_task.title);
36//! }
37//!
38//! // Get phase statistics
39//! let stats = auth_phase.get_stats();
40//! println!("Progress: {}/{} done", stats.done, stats.total);
41//! ```
42//!
43//! ## Storage Format
44//!
45//! Tasks are stored in `.scud/tasks/tasks.scg` using the SCG (SCUD Graph) format.
46//! Multiple phases are separated by `---` delimiters:
47//!
48//! ```text
49//! @phase auth
50//!
51//! [1] Implement login endpoint
52//! status: pending
53//! complexity: 5
54//! deps: 2, 3
55//!
56//! ---
57//!
58//! @phase api
59//!
60//! [1] Create REST framework
61//! status: done
62//! ```
63//!
64//! ## CLI Usage
65//!
66//! The `scud` binary provides commands for task management:
67//!
68//! - `scud init` - Initialize a new SCUD project
69//! - `scud list` - List tasks in the active phase
70//! - `scud next` - Find and optionally claim the next available task
71//! - `scud show <id>` - Display task details
72//! - `scud set-status <id> <status>` - Update task status
73//! - `scud waves` - Show tasks organized by execution waves
74//! - `scud stats` - Display completion statistics
75//!
76//! ## Task Generation Pipeline
77//!
78//! SCUD provides a multi-phase pipeline for generating tasks from PRD documents.
79//! This can be invoked via the CLI (`scud generate`) or programmatically:
80//!
81//! ```no_run
82//! use scud::commands::generate::{generate, GenerateOptions};
83//! use std::path::PathBuf;
84//!
85//! #[tokio::main]
86//! async fn main() -> anyhow::Result<()> {
87//! // Create options with required fields
88//! let mut options = GenerateOptions::new(
89//! PathBuf::from("docs/prd.md"),
90//! "my-feature".to_string(),
91//! );
92//!
93//! // Customize the pipeline
94//! options.num_tasks = 15; // Generate up to 15 tasks
95//! options.verbose = true; // Show detailed output
96//! options.no_expand = false; // Run expansion phase
97//! options.no_check_deps = false; // Run dependency validation
98//!
99//! // Run the pipeline: parse → expand → check-deps
100//! generate(options).await?;
101//! Ok(())
102//! }
103//! ```
104//!
105//! The pipeline consists of three phases:
106//!
107//! 1. **Parse**: Convert a PRD document into initial tasks using AI
108//! 2. **Expand**: Break down complex tasks into subtasks
109//! 3. **Check Dependencies**: Validate and fix task dependencies
110//!
111//! Each phase can be skipped using `no_expand` and `no_check_deps` options.
112
113/// Unified execution backend for AI agent tasks.
114///
115/// Provides the [`AgentBackend`] trait that abstracts over different execution modes
116/// (direct API, CLI subprocess, simulated) behind a single interface.
117pub mod backend;
118
119/// Attractor Mode — DOT-based AI workflow graph execution.
120///
121/// Define multi-step AI pipelines as DOT graphs where nodes represent tasks
122/// and edges represent transitions with optional conditions.
123pub mod attractor;
124
125/// Agent definitions for model routing.
126///
127/// Allows tasks to specify which AI harness and model should run them.
128/// Agent definitions are loaded from `.scud/agents/<name>.toml` files.
129///
130/// # Example
131///
132/// ```toml
133/// # .scud/agents/reviewer.toml
134/// [agent]
135/// name = "reviewer"
136/// description = "Code review agent using smarter model"
137///
138/// [model]
139/// harness = "claude"
140/// model = "opus"
141/// ```
142pub mod agents;
143
144/// Failure attribution using git blame.
145///
146/// Maps validation errors to specific tasks by parsing error output for
147/// file:line references and using git blame to find which commits changed
148/// those lines. Task IDs are extracted from commit message prefixes like `[TASK-ID]`.
149///
150/// # Example
151///
152/// ```no_run
153/// use scud::attribution::{attribute_failure, AttributionConfidence};
154/// use std::path::Path;
155///
156/// let working_dir = Path::new(".");
157/// let wave_tasks = vec!["auth:1".to_string(), "auth:2".to_string()];
158///
159/// let attribution = attribute_failure(
160/// working_dir,
161/// "error: src/main.rs:42: undefined variable",
162/// "",
163/// &wave_tasks,
164/// None,
165/// ).unwrap();
166///
167/// match attribution.confidence {
168/// AttributionConfidence::High => println!("Clear responsible: {:?}", attribution.responsible_tasks),
169/// AttributionConfidence::Medium => println!("Multiple suspects: {:?}", attribution.responsible_tasks),
170/// AttributionConfidence::Low => println!("Cannot determine - all tasks suspect"),
171/// }
172/// ```
173pub mod attribution;
174
175/// Backpressure validation for maintaining code quality during automated execution.
176///
177/// Runs programmatic validation (build, test, lint) after task completion.
178/// See [`backpressure::run_validation`] for the main entry point.
179pub mod backpressure;
180
181/// SQLite database for event logging, transcript storage, and session history.
182///
183/// Provides queryable storage for swarm events, agent transcripts, and session
184/// metadata. Uses WAL mode for concurrent access during swarm execution.
185pub mod db;
186
187/// Background transcript watcher for real-time import of Claude Code JSONL files.
188pub mod transcript_watcher;
189
190/// CLI command implementations.
191///
192/// This module contains the implementation of all SCUD CLI commands including
193/// task listing, status updates, AI-powered parsing, and more. Each submodule
194/// corresponds to a CLI subcommand.
195///
196/// Key submodules:
197/// - `ai` - AI-powered commands (parse PRD, expand tasks, analyze complexity)
198/// - `generate` - Multi-phase task generation pipeline (parse → expand → check-deps)
199/// - `list` - List tasks with filtering
200/// - `next` - Find next available task
201/// - `set_status` - Update task status
202/// - `waves` - Display execution waves
203/// - `stats` - Show completion statistics
204/// - `spawn` - Parallel task execution
205/// - `swarm` - Wave-based parallel execution with backpressure
206///
207/// ## Generate Pipeline
208///
209/// The [`commands::generate`] module provides a complete pipeline for task generation:
210///
211/// ```no_run
212/// use scud::commands::generate::{generate, GenerateOptions};
213/// use std::path::PathBuf;
214///
215/// # async fn example() -> anyhow::Result<()> {
216/// let options = GenerateOptions::new(
217/// PathBuf::from("prd.md"),
218/// "feature".to_string(),
219/// );
220/// generate(options).await?;
221/// # Ok(())
222/// # }
223/// ```
224pub mod commands;
225
226/// Configuration management for SCUD projects.
227///
228/// Handles loading and saving of `.scud/config.toml` which stores:
229/// - LLM provider settings (provider, model, API endpoints)
230/// - Model tiers (smart/fast models for different task types)
231/// - Token limits and other provider-specific settings
232///
233/// # Example
234///
235/// ```no_run
236/// use scud::config::Config;
237/// use std::path::Path;
238///
239/// let config = Config::load(Path::new(".scud/config.toml"))
240/// .unwrap_or_default();
241/// println!("Using provider: {}", config.llm.provider);
242/// ```
243pub mod config;
244
245/// Dynamic extension loading and execution.
246///
247/// Provides infrastructure for loading and running extensions that can
248/// extend SCUD functionality with custom tools and commands.
249pub mod extensions;
250
251/// Task graph serialization formats.
252///
253/// Provides parsers and serializers for the SCG (SCUD Graph) format,
254/// a token-efficient text format designed for AI context windows.
255///
256/// # Format Overview
257///
258/// ```text
259/// @phase my-project
260///
261/// [1] First task
262/// status: pending
263/// complexity: 3
264///
265/// [2] Second task
266/// status: done
267/// deps: 1
268/// ```
269///
270/// Key exports:
271/// - [`formats::parse_scg`] - Parse SCG text into a Phase
272/// - [`formats::serialize_scg`] - Serialize a Phase to SCG text
273/// - [`formats::Format`] - Enum of supported formats (SCG, JSON)
274pub mod formats;
275
276/// LLM client and prompt management.
277///
278/// Provides integration with various LLM providers for AI-powered features:
279/// - PRD parsing to generate initial task lists
280/// - Task expansion into subtasks
281/// - Complexity analysis and dependency detection
282///
283/// Supported providers:
284/// - `claude-cli` - Anthropic Claude via CLI
285/// - `anthropic` - Direct Anthropic API
286/// - `xai` - xAI/Grok API
287/// - `openai` - OpenAI API
288/// - `openrouter` - OpenRouter API
289/// - `codex` - OpenAI Codex CLI
290pub mod llm;
291
292/// OpenCode Server integration for agent orchestration.
293///
294/// Provides HTTP client and SSE event streaming for OpenCode Server mode,
295/// enabling structured communication with agents instead of CLI subprocess spawning.
296///
297/// # Architecture
298///
299/// Instead of spawning one CLI process per agent, this module communicates with
300/// a single OpenCode server that manages multiple sessions:
301///
302/// ```text
303/// SCUD Swarm ──HTTP──► OpenCode Server
304/// ◄─SSE── real-time events (tool calls, output, completion)
305/// ```
306///
307/// # Benefits
308///
309/// - Lower overhead (single server vs N processes)
310/// - Structured events (tool calls, text deltas, completion)
311/// - Graceful cancellation via HTTP API
312/// - Real-time visibility into agent activity
313///
314/// # Example
315///
316/// ```no_run
317/// use scud::opencode::{OpenCodeManager, global_manager};
318///
319/// #[tokio::main]
320/// async fn main() -> anyhow::Result<()> {
321/// let manager = global_manager();
322/// manager.ensure_running().await?;
323///
324/// let session = manager.client().create_session("Task 1").await?;
325/// manager.client().send_message(&session.id, "Do something", None).await?;
326///
327/// Ok(())
328/// }
329/// ```
330pub mod opencode;
331
332/// JSON RPC IPC server for subagent communication.
333///
334/// Provides a JSON RPC 2.0 protocol for inter-process communication between
335/// SCUD and external orchestrators. The server reads requests from stdin
336/// and emits events/responses to stdout.
337///
338/// ## Protocol
339///
340/// Requests (stdin):
341/// ```json
342/// {"jsonrpc": "2.0", "method": "spawn", "params": {"task_id": "1", "prompt": "..."}, "id": 1}
343/// {"jsonrpc": "2.0", "method": "ping", "id": 2}
344/// {"jsonrpc": "2.0", "method": "shutdown", "id": 3}
345/// ```
346///
347/// Responses/Events (stdout):
348/// ```json
349/// {"jsonrpc": "2.0", "result": {"status": "ok"}, "id": 1}
350/// {"jsonrpc": "2.0", "method": "agent.started", "params": {"task_id": "1"}}
351/// {"jsonrpc": "2.0", "method": "agent.completed", "params": {"task_id": "1", "success": true}}
352/// ```
353///
354/// ## Usage
355///
356/// ```no_run
357/// use scud::rpc::{RpcServer, RpcServerConfig};
358///
359/// #[tokio::main]
360/// async fn main() -> anyhow::Result<()> {
361/// let config = RpcServerConfig::default();
362/// let mut server = RpcServer::new(config);
363/// server.run().await
364/// }
365/// ```
366pub mod rpc;
367
368/// Core data models for tasks and phases.
369///
370/// This module defines the fundamental types used throughout SCUD:
371///
372/// - [`models::Task`] - Individual work items with status, complexity, dependencies
373/// - [`models::Phase`] - A collection of related tasks (identified by a tag)
374/// - [`models::TaskStatus`] - Task lifecycle states (Pending, InProgress, Done, etc.)
375/// - [`models::Priority`] - Task priority levels (Critical, High, Medium, Low)
376/// - [`models::IdFormat`] - ID generation strategy (Sequential or UUID)
377///
378/// # Example
379///
380/// ```
381/// use scud::models::{Task, TaskStatus, Phase};
382///
383/// let mut phase = Phase::new("my-feature".to_string());
384///
385/// let mut task = Task::new(
386/// "1".to_string(),
387/// "Implement feature".to_string(),
388/// "Add the new functionality".to_string(),
389/// );
390/// task.complexity = 5;
391/// task.dependencies = vec!["setup:1".to_string()]; // Cross-phase dependency
392///
393/// phase.add_task(task);
394///
395/// // Find tasks ready to work on
396/// if let Some(next) = phase.find_next_task() {
397/// println!("Ready: {}", next.title);
398/// }
399/// ```
400pub mod models;
401
402/// File-based task storage with locking.
403///
404/// Manages reading and writing of task data to the filesystem with:
405/// - File locking for safe concurrent access
406/// - Caching of active phase selection
407/// - Atomic read-modify-write operations
408///
409/// The storage layer handles:
410/// - `.scud/tasks/tasks.scg` - Main task storage
411/// - `.scud/active-tag` - Currently selected phase
412/// - `.scud/config.toml` - Project configuration
413/// - `.scud/guidance/*.md` - AI context files
414///
415/// # Example
416///
417/// ```no_run
418/// use scud::storage::Storage;
419/// use scud::models::TaskStatus;
420///
421/// let storage = Storage::new(None); // Use current directory
422///
423/// // Load and modify a phase atomically
424/// let mut phase = storage.load_group("my-phase").unwrap();
425/// if let Some(task) = phase.get_task_mut("1") {
426/// task.set_status(TaskStatus::Done);
427/// }
428/// storage.update_group("my-phase", &phase).unwrap();
429/// ```
430pub mod storage;
431
432/// Sync SCUD tasks to Claude Code's Tasks format.
433///
434/// Claude Code has a built-in Tasks feature that agents can access via
435/// `TaskList`, `TaskUpdate`, `TaskCreate` tools. By syncing SCUD tasks
436/// to `~/.claude/tasks/`, agents can see the full task list and dependencies.
437///
438/// # Example
439///
440/// ```no_run
441/// use scud::sync::claude_tasks;
442/// use scud::models::phase::Phase;
443///
444/// let phase = Phase::new("auth".to_string());
445/// // ... add tasks ...
446///
447/// // Sync to Claude Tasks format
448/// let task_file = claude_tasks::sync_phase(&phase, "auth").unwrap();
449///
450/// // Get the task list ID for environment variable
451/// let list_id = claude_tasks::task_list_id("auth");
452/// // Set CLAUDE_CODE_TASK_LIST_ID={list_id} when spawning agents
453/// ```
454pub mod sync;
455
456/// Swarm execution mode - how agents are spawned during swarm execution.
457///
458/// # Modes
459///
460/// - **Tmux**: Uses tmux for agent management. Requires tmux to be installed.
461/// Provides live visibility through tmux windows, useful for debugging.
462///
463/// - **Extensions**: Uses extension-based subprocesses with no tmux dependency.
464/// Agents run as direct child processes. Better for CI/CD environments or
465/// systems without tmux.
466///
467/// - **Server**: Uses OpenCode Server mode for agent orchestration. Provides
468/// structured events (tool calls, text deltas), graceful cancellation, and
469/// lower per-agent overhead. Recommended for production use.
470///
471/// # Example
472///
473/// ```
474/// use scud::SwarmMode;
475///
476/// let mode = SwarmMode::Server;
477/// assert_eq!(mode.to_string(), "server");
478/// ```
479#[derive(Clone, Debug, Default, clap::ValueEnum)]
480pub enum SwarmMode {
481 /// Use tmux for agent management (default, requires tmux installed)
482 #[default]
483 Tmux,
484 /// Use extension-based subprocesses (no tmux dependency)
485 Extensions,
486 /// Use OpenCode Server for agent orchestration (recommended)
487 Server,
488 /// Beads-style continuous execution (no wave batching)
489 /// Tasks execute immediately when dependencies are met
490 Beads,
491 /// Headless streaming mode (no tmux, captures JSON events)
492 Headless,
493}
494
495impl std::fmt::Display for SwarmMode {
496 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
497 match self {
498 SwarmMode::Tmux => write!(f, "tmux"),
499 SwarmMode::Extensions => write!(f, "extensions"),
500 SwarmMode::Server => write!(f, "server"),
501 SwarmMode::Beads => write!(f, "beads"),
502 SwarmMode::Headless => write!(f, "headless"),
503 }
504 }
505}
506
507#[cfg(test)]
508mod swarm_mode_tests {
509 use super::*;
510
511 #[test]
512 fn test_swarm_mode_default() {
513 let mode: SwarmMode = Default::default();
514 assert!(matches!(mode, SwarmMode::Tmux));
515 }
516
517 #[test]
518 fn test_swarm_mode_display() {
519 assert_eq!(SwarmMode::Tmux.to_string(), "tmux");
520 assert_eq!(SwarmMode::Extensions.to_string(), "extensions");
521 assert_eq!(SwarmMode::Server.to_string(), "server");
522 assert_eq!(SwarmMode::Beads.to_string(), "beads");
523 }
524
525 #[test]
526 fn test_swarm_mode_clone() {
527 let mode = SwarmMode::Extensions;
528 let cloned = mode.clone();
529 assert!(matches!(cloned, SwarmMode::Extensions));
530 }
531}
532
533/// Returns a greeting message.
534///
535/// # Example
536///
537/// ```
538/// assert_eq!(scud::hello(), "Hello, World!");
539/// ```
540pub fn hello() -> &'static str {
541 "Hello, World!"
542}