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/// Agent definitions for model routing.
114///
115/// Allows tasks to specify which AI harness and model should run them.
116/// Agent definitions are loaded from `.scud/agents/<name>.toml` files.
117///
118/// # Example
119///
120/// ```toml
121/// # .scud/agents/reviewer.toml
122/// [agent]
123/// name = "reviewer"
124/// description = "Code review agent using smarter model"
125///
126/// [model]
127/// harness = "claude"
128/// model = "opus"
129/// ```
130pub mod agents;
131
132/// Failure attribution using git blame.
133///
134/// Maps validation errors to specific tasks by parsing error output for
135/// file:line references and using git blame to find which commits changed
136/// those lines. Task IDs are extracted from commit message prefixes like `[TASK-ID]`.
137///
138/// # Example
139///
140/// ```no_run
141/// use scud::attribution::{attribute_failure, AttributionConfidence};
142/// use std::path::Path;
143///
144/// let working_dir = Path::new(".");
145/// let wave_tasks = vec!["auth:1".to_string(), "auth:2".to_string()];
146///
147/// let attribution = attribute_failure(
148///     working_dir,
149///     "error: src/main.rs:42: undefined variable",
150///     "",
151///     &wave_tasks,
152///     None,
153/// ).unwrap();
154///
155/// match attribution.confidence {
156///     AttributionConfidence::High => println!("Clear responsible: {:?}", attribution.responsible_tasks),
157///     AttributionConfidence::Medium => println!("Multiple suspects: {:?}", attribution.responsible_tasks),
158///     AttributionConfidence::Low => println!("Cannot determine - all tasks suspect"),
159/// }
160/// ```
161pub mod attribution;
162
163/// Backpressure validation for maintaining code quality during automated execution.
164///
165/// Runs programmatic validation (build, test, lint) after task completion.
166/// See [`backpressure::run_validation`] for the main entry point.
167pub mod backpressure;
168
169/// CLI command implementations.
170///
171/// This module contains the implementation of all SCUD CLI commands including
172/// task listing, status updates, AI-powered parsing, and more. Each submodule
173/// corresponds to a CLI subcommand.
174///
175/// Key submodules:
176/// - `ai` - AI-powered commands (parse PRD, expand tasks, analyze complexity)
177/// - `generate` - Multi-phase task generation pipeline (parse → expand → check-deps)
178/// - `list` - List tasks with filtering
179/// - `next` - Find next available task
180/// - `set_status` - Update task status
181/// - `waves` - Display execution waves
182/// - `stats` - Show completion statistics
183/// - `spawn` - Parallel task execution
184/// - `swarm` - Wave-based parallel execution with backpressure
185///
186/// ## Generate Pipeline
187///
188/// The [`commands::generate`] module provides a complete pipeline for task generation:
189///
190/// ```no_run
191/// use scud::commands::generate::{generate, GenerateOptions};
192/// use std::path::PathBuf;
193///
194/// # async fn example() -> anyhow::Result<()> {
195/// let options = GenerateOptions::new(
196///     PathBuf::from("prd.md"),
197///     "feature".to_string(),
198/// );
199/// generate(options).await?;
200/// # Ok(())
201/// # }
202/// ```
203pub mod commands;
204
205/// Configuration management for SCUD projects.
206///
207/// Handles loading and saving of `.scud/config.toml` which stores:
208/// - LLM provider settings (provider, model, API endpoints)
209/// - Model tiers (smart/fast models for different task types)
210/// - Token limits and other provider-specific settings
211///
212/// # Example
213///
214/// ```no_run
215/// use scud::config::Config;
216/// use std::path::Path;
217///
218/// let config = Config::load(Path::new(".scud/config.toml"))
219///     .unwrap_or_default();
220/// println!("Using provider: {}", config.llm.provider);
221/// ```
222pub mod config;
223
224/// Dynamic extension loading and execution.
225///
226/// Provides infrastructure for loading and running extensions that can
227/// extend SCUD functionality with custom tools and commands.
228pub mod extensions;
229
230/// Task graph serialization formats.
231///
232/// Provides parsers and serializers for the SCG (SCUD Graph) format,
233/// a token-efficient text format designed for AI context windows.
234///
235/// # Format Overview
236///
237/// ```text
238/// @phase my-project
239///
240/// [1] First task
241/// status: pending
242/// complexity: 3
243///
244/// [2] Second task
245/// status: done
246/// deps: 1
247/// ```
248///
249/// Key exports:
250/// - [`formats::parse_scg`] - Parse SCG text into a Phase
251/// - [`formats::serialize_scg`] - Serialize a Phase to SCG text
252/// - [`formats::Format`] - Enum of supported formats (SCG, JSON)
253pub mod formats;
254
255/// LLM client and prompt management.
256///
257/// Provides integration with various LLM providers for AI-powered features:
258/// - PRD parsing to generate initial task lists
259/// - Task expansion into subtasks
260/// - Complexity analysis and dependency detection
261///
262/// Supported providers:
263/// - `claude-cli` - Anthropic Claude via CLI
264/// - `anthropic` - Direct Anthropic API
265/// - `xai` - xAI/Grok API
266/// - `openai` - OpenAI API
267/// - `openrouter` - OpenRouter API
268/// - `codex` - OpenAI Codex CLI
269pub mod llm;
270
271/// OpenCode Server integration for agent orchestration.
272///
273/// Provides HTTP client and SSE event streaming for OpenCode Server mode,
274/// enabling structured communication with agents instead of CLI subprocess spawning.
275///
276/// # Architecture
277///
278/// Instead of spawning one CLI process per agent, this module communicates with
279/// a single OpenCode server that manages multiple sessions:
280///
281/// ```text
282/// SCUD Swarm ──HTTP──► OpenCode Server
283///                 ◄─SSE── real-time events (tool calls, output, completion)
284/// ```
285///
286/// # Benefits
287///
288/// - Lower overhead (single server vs N processes)
289/// - Structured events (tool calls, text deltas, completion)
290/// - Graceful cancellation via HTTP API
291/// - Real-time visibility into agent activity
292///
293/// # Example
294///
295/// ```no_run
296/// use scud::opencode::{OpenCodeManager, global_manager};
297///
298/// #[tokio::main]
299/// async fn main() -> anyhow::Result<()> {
300///     let manager = global_manager();
301///     manager.ensure_running().await?;
302///
303///     let session = manager.client().create_session("Task 1").await?;
304///     manager.client().send_message(&session.id, "Do something", None).await?;
305///
306///     Ok(())
307/// }
308/// ```
309pub mod opencode;
310
311/// JSON RPC IPC server for subagent communication.
312///
313/// Provides a JSON RPC 2.0 protocol for inter-process communication between
314/// SCUD and external orchestrators. The server reads requests from stdin
315/// and emits events/responses to stdout.
316///
317/// ## Protocol
318///
319/// Requests (stdin):
320/// ```json
321/// {"jsonrpc": "2.0", "method": "spawn", "params": {"task_id": "1", "prompt": "..."}, "id": 1}
322/// {"jsonrpc": "2.0", "method": "ping", "id": 2}
323/// {"jsonrpc": "2.0", "method": "shutdown", "id": 3}
324/// ```
325///
326/// Responses/Events (stdout):
327/// ```json
328/// {"jsonrpc": "2.0", "result": {"status": "ok"}, "id": 1}
329/// {"jsonrpc": "2.0", "method": "agent.started", "params": {"task_id": "1"}}
330/// {"jsonrpc": "2.0", "method": "agent.completed", "params": {"task_id": "1", "success": true}}
331/// ```
332///
333/// ## Usage
334///
335/// ```no_run
336/// use scud::rpc::{RpcServer, RpcServerConfig};
337///
338/// #[tokio::main]
339/// async fn main() -> anyhow::Result<()> {
340///     let config = RpcServerConfig::default();
341///     let mut server = RpcServer::new(config);
342///     server.run().await
343/// }
344/// ```
345pub mod rpc;
346
347/// Core data models for tasks and phases.
348///
349/// This module defines the fundamental types used throughout SCUD:
350///
351/// - [`models::Task`] - Individual work items with status, complexity, dependencies
352/// - [`models::Phase`] - A collection of related tasks (identified by a tag)
353/// - [`models::TaskStatus`] - Task lifecycle states (Pending, InProgress, Done, etc.)
354/// - [`models::Priority`] - Task priority levels (Critical, High, Medium, Low)
355/// - [`models::IdFormat`] - ID generation strategy (Sequential or UUID)
356///
357/// # Example
358///
359/// ```
360/// use scud::models::{Task, TaskStatus, Phase};
361///
362/// let mut phase = Phase::new("my-feature".to_string());
363///
364/// let mut task = Task::new(
365///     "1".to_string(),
366///     "Implement feature".to_string(),
367///     "Add the new functionality".to_string(),
368/// );
369/// task.complexity = 5;
370/// task.dependencies = vec!["setup:1".to_string()]; // Cross-phase dependency
371///
372/// phase.add_task(task);
373///
374/// // Find tasks ready to work on
375/// if let Some(next) = phase.find_next_task() {
376///     println!("Ready: {}", next.title);
377/// }
378/// ```
379pub mod models;
380
381/// File-based task storage with locking.
382///
383/// Manages reading and writing of task data to the filesystem with:
384/// - File locking for safe concurrent access
385/// - Caching of active phase selection
386/// - Atomic read-modify-write operations
387///
388/// The storage layer handles:
389/// - `.scud/tasks/tasks.scg` - Main task storage
390/// - `.scud/active-tag` - Currently selected phase
391/// - `.scud/config.toml` - Project configuration
392/// - `.scud/guidance/*.md` - AI context files
393///
394/// # Example
395///
396/// ```no_run
397/// use scud::storage::Storage;
398/// use scud::models::TaskStatus;
399///
400/// let storage = Storage::new(None); // Use current directory
401///
402/// // Load and modify a phase atomically
403/// let mut phase = storage.load_group("my-phase").unwrap();
404/// if let Some(task) = phase.get_task_mut("1") {
405///     task.set_status(TaskStatus::Done);
406/// }
407/// storage.update_group("my-phase", &phase).unwrap();
408/// ```
409pub mod storage;
410
411/// Sync SCUD tasks to Claude Code's Tasks format.
412///
413/// Claude Code has a built-in Tasks feature that agents can access via
414/// `TaskList`, `TaskUpdate`, `TaskCreate` tools. By syncing SCUD tasks
415/// to `~/.claude/tasks/`, agents can see the full task list and dependencies.
416///
417/// # Example
418///
419/// ```no_run
420/// use scud::sync::claude_tasks;
421/// use scud::models::phase::Phase;
422///
423/// let phase = Phase::new("auth".to_string());
424/// // ... add tasks ...
425///
426/// // Sync to Claude Tasks format
427/// let task_file = claude_tasks::sync_phase(&phase, "auth").unwrap();
428///
429/// // Get the task list ID for environment variable
430/// let list_id = claude_tasks::task_list_id("auth");
431/// // Set CLAUDE_CODE_TASK_LIST_ID={list_id} when spawning agents
432/// ```
433pub mod sync;
434
435/// Swarm execution mode - how agents are spawned during swarm execution.
436///
437/// # Modes
438///
439/// - **Tmux**: Uses tmux for agent management. Requires tmux to be installed.
440///   Provides live visibility through tmux windows, useful for debugging.
441///
442/// - **Extensions**: Uses extension-based subprocesses with no tmux dependency.
443///   Agents run as direct child processes. Better for CI/CD environments or
444///   systems without tmux.
445///
446/// - **Server**: Uses OpenCode Server mode for agent orchestration. Provides
447///   structured events (tool calls, text deltas), graceful cancellation, and
448///   lower per-agent overhead. Recommended for production use.
449///
450/// # Example
451///
452/// ```
453/// use scud::SwarmMode;
454///
455/// let mode = SwarmMode::Server;
456/// assert_eq!(mode.to_string(), "server");
457/// ```
458#[derive(Clone, Debug, Default, clap::ValueEnum)]
459pub enum SwarmMode {
460    /// Use tmux for agent management (default, requires tmux installed)
461    #[default]
462    Tmux,
463    /// Use extension-based subprocesses (no tmux dependency)
464    Extensions,
465    /// Use OpenCode Server for agent orchestration (recommended)
466    Server,
467}
468
469impl std::fmt::Display for SwarmMode {
470    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
471        match self {
472            SwarmMode::Tmux => write!(f, "tmux"),
473            SwarmMode::Extensions => write!(f, "extensions"),
474            SwarmMode::Server => write!(f, "server"),
475        }
476    }
477}
478
479#[cfg(test)]
480mod swarm_mode_tests {
481    use super::*;
482
483    #[test]
484    fn test_swarm_mode_default() {
485        let mode: SwarmMode = Default::default();
486        assert!(matches!(mode, SwarmMode::Tmux));
487    }
488
489    #[test]
490    fn test_swarm_mode_display() {
491        assert_eq!(SwarmMode::Tmux.to_string(), "tmux");
492        assert_eq!(SwarmMode::Extensions.to_string(), "extensions");
493        assert_eq!(SwarmMode::Server.to_string(), "server");
494    }
495
496    #[test]
497    fn test_swarm_mode_clone() {
498        let mode = SwarmMode::Extensions;
499        let cloned = mode.clone();
500        assert!(matches!(cloned, SwarmMode::Extensions));
501    }
502}
503
504/// Returns a greeting message.
505///
506/// # Example
507///
508/// ```
509/// assert_eq!(scud::hello(), "Hello, World!");
510/// ```
511pub fn hello() -> &'static str {
512    "Hello, World!"
513}