Skip to main content

driven/
lib.rs

1//! # Driven
2//!
3//! Professional AI-assisted development orchestrator with binary-first architecture.
4//!
5//! Driven brings structure, consistency, and intelligence to AI-powered coding workflows
6//! by combining template-driven approaches with methodical frameworks, reimagined in Rust
7//! with DX's binary-first philosophy for unparalleled performance.
8//!
9//! ## Features
10//!
11//! - **Universal Rule Format**: One source of truth, convert to any editor
12//! - **Binary-First Storage**: Using DX serializer principles for 70%+ size reduction
13//! - **Context Intelligence**: Deep project analysis for AI guidance
14//! - **Professional Templates**: Battle-tested patterns for AI agents
15//! - **Zero-Parse Loading**: Instant rule loading with memory-mapped binaries
16//! - **DX Binary Dawn**: SIMD scanning, XOR patching, Ed25519 signing
17//!
18//! ## Quick Start
19//!
20//! ```rust,ignore
21//! use driven::{DrivenConfig, RuleSet};
22//!
23//! // Load existing rules
24//! let rules = RuleSet::load(".cursorrules")?;
25//!
26//! // Convert to different formats
27//! rules.emit_copilot(".github/copilot-instructions.md")?;
28//! rules.emit_windsurf(".windsurfrules")?;
29//!
30//! // Or use binary format for maximum efficiency
31//! rules.save_binary(".driven/rules.drv")?;
32//! ```
33//!
34//! ## Architecture
35//!
36//! Driven is organized into several key modules:
37//!
38//! - [`format`]: Binary rule format (.drv) encoding/decoding
39//! - [`parser`]: Universal parser for all editor rule formats
40//! - [`emitter`]: Target format generators for each editor
41//! - [`templates`]: Template system with built-in library
42//! - [`context`]: AI context intelligence and project analysis
43//! - [`sync`]: Multi-editor synchronization
44//! - [`validation`]: Rule validation and linting
45//!
46//! ## DX Binary Dawn Modules
47//!
48//! - [`binary`]: DX ∞ infinity format, zero-copy schemas, SIMD tokenizer
49//! - [`fusion`]: Pre-compiled templates, hot cache, speculative loading
50//! - [`streaming`]: HTIP delivery, XOR patching, ETag negotiation
51//! - [`security`]: Ed25519 signing, capability manifest, sandbox
52//! - [`state`]: Dirty-bit tracking, shared rules, atomic sync
53
54// Note: Some fields are for API completeness and future use
55
56// Core modules
57pub mod agents;
58pub mod config_validation;
59pub mod context;
60pub mod dcp;
61pub mod dx_integration;
62pub mod emitter;
63pub mod error;
64pub mod format;
65pub mod generator_integration;
66pub mod hooks;
67pub mod integration;
68pub mod modules;
69pub mod parser;
70pub mod scale;
71pub mod steering;
72pub mod strategy;
73pub mod sync;
74pub mod sysinfo;
75pub mod templates;
76pub mod validation;
77pub mod workflows;
78
79// DX Binary Dawn modules
80pub mod binary;
81pub mod fusion;
82pub mod security;
83pub mod state;
84pub mod streaming;
85
86#[cfg(feature = "cli")]
87pub mod cli;
88
89// Re-export core types
90pub use context::{ProjectAnalyzer, ProjectContext};
91pub use emitter::Emitter;
92pub use format::{DrvDecoder, DrvEncoder, DrvHeader, RuleCategory};
93pub use parser::{ParsedRule, Parser, UnifiedRule};
94pub use sync::SyncEngine;
95pub use templates::{Template, TemplateRegistry};
96pub use validation::{Linter, ValidationResult};
97
98// Re-export DX Binary Dawn types
99pub use binary::{
100    Blake3Checksum, InfinityHeader, InfinityRule, MappedRule, SimdTokenizer, StringId, StringTable,
101    StringTableBuilder,
102};
103pub use fusion::{BinaryCache, FusionModule, HotCache, SpeculativeLoader};
104pub use security::{Capability, CapabilityManifest, Ed25519Signer, IntegrityGuard, Sandbox};
105pub use state::{AtomicSync, DirtyBits, RuleSnapshot, SharedRules, SnapshotManager};
106pub use streaming::{ChunkStreamer, ETagNegotiator, HtipDelivery, XorPatcher};
107
108// Re-export DX integration
109pub use dx_integration::DxSerializable;
110pub use dx_integration::{
111    DxDocumentable, DxMarkdownConfig, DxMarkdownFormat, rules_to_dx_markdown,
112};
113pub use dx_integration::{LegacyConverter, LegacyFormat, LegacySerializable};
114
115// Re-export DCP integration
116pub use dcp::{ConnectionState, DcpClient, DcpConfig, ToolDefinition};
117
118// Re-export generator integration
119pub use generator_integration::{
120    DrivenTemplate, DrivenTemplateProvider, DrivenTemplateType, GenerateParams, GeneratedFile,
121    GeneratorBridge, TemplateCategory, TemplateInfo,
122};
123
124// Re-export cross-crate integration
125pub use integration::{
126    BinaryFormatChecker, CrossCrateHeader, DrivenDcpBridge, DrivenTool, DrivenToolCategory,
127    HookMessage, HookMessenger, SourceCrate, SpecScaffoldResult, SpecScaffolder,
128    ValidationResult as IntegrationValidationResult,
129};
130
131// Re-export error handling
132pub use error::{EnhancedError, EnhancedResult, ErrorContext};
133
134// Re-export config validation
135pub use config_validation::{
136    ConfigValidator, ValidationError, ValidationReport, ValidationWarning, validate_config,
137};
138
139// Re-export hooks system
140pub use hooks::{
141    AgentHook, BuildEvent, GitOp, HookAction, HookCondition, HookContext, HookEngine,
142    HookExecutionResult, HookTrigger, TestFilter, TestResult, TestStatus,
143};
144
145// Re-export steering system
146pub use steering::{AgentContext, FileReference, SteeringEngine, SteeringInclusion, SteeringRule};
147
148// Re-export strategy system
149pub use strategy::{
150    ClaimId, ClaimStatus, ClaimToken, CommandEvidence, CommandProof, CommandStatus, FileProof,
151    GitCheckoutKind, LaneClaim, LaneId, LanePassAssignment, LanePassAssignmentStatus,
152    LanePassConfig, LanePassStatePaths, LanePassStore, NextPassHandoff, OutcomeProof, PassNumber,
153    PassOutcome, ProofReceipt, ReceiptFormat, SubagentDelegation, VerificationClass, WorkerId,
154    WorkerIdentity, WorkerKind, WorktreeCreationDecision, WorktreeIsolationMode,
155    WorktreeIsolationPlan, WorktreeMetadata, detect_worktree_metadata, plan_worktree_isolation,
156};
157
158// Re-export sysinfo system
159pub use sysinfo::{
160    BuildToolInfo, GitInfo, LanguageInfo, OsInfo, PackageManagerInfo, ProjectInfo, ProjectType,
161    ShellInfo, SystemInfo, SystemInfoCache, SystemInfoProvider, TestFrameworkInfo,
162};
163
164// Re-export agents system
165pub use agents::{Agent, AgentPersona, AgentRegistry, DelegationRequest, DelegationResult};
166
167// Re-export workflows system
168pub use workflows::{
169    StepResult, Workflow, WorkflowBranch, WorkflowEngine, WorkflowPhase, WorkflowProgress,
170    WorkflowSession, WorkflowStep,
171};
172
173// Re-export scale system
174pub use scale::{
175    ComplexityAnalyzer, DependencyAnalyzer, FileSizeAnalyzer, HistoryAnalyzer,
176    ProjectContext as ScaleProjectContext, ProjectScale, ScaleAnalyzer, ScaleDetector,
177    ScaleRecommendation, TeamSizeAnalyzer,
178};
179
180// Re-export modules system
181pub use modules::{Module, ModuleDependency, ModuleManager, ModuleStatus};
182
183/// Configuration for Driven
184#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
185pub struct DrivenConfig {
186    /// Version of the configuration format
187    pub version: String,
188    /// Default editor to target
189    pub default_editor: Editor,
190    /// Enabled editors for sync
191    pub editors: EditorConfig,
192    /// Template configuration
193    pub templates: TemplateConfig,
194    /// Sync settings
195    pub sync: SyncConfig,
196    /// Context analysis settings
197    pub context: ContextConfig,
198}
199
200impl Default for DrivenConfig {
201    fn default() -> Self {
202        Self {
203            version: "1.0".to_string(),
204            default_editor: Editor::Cursor,
205            editors: EditorConfig::default(),
206            templates: TemplateConfig::default(),
207            sync: SyncConfig::default(),
208            context: ContextConfig::default(),
209        }
210    }
211}
212
213/// Supported AI code editors
214#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)]
215#[serde(rename_all = "lowercase")]
216pub enum Editor {
217    /// Cursor AI editor
218    Cursor,
219    /// GitHub Copilot in VS Code
220    Copilot,
221    /// Windsurf (Codeium)
222    Windsurf,
223    /// Claude Code (Anthropic)
224    Claude,
225    /// Aider CLI
226    Aider,
227    /// Cline extension
228    Cline,
229}
230
231impl std::fmt::Display for Editor {
232    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
233        write!(f, "{}", self.display_name())
234    }
235}
236
237impl Editor {
238    /// Get the default rule file path for this editor
239    pub fn rule_path(&self) -> &'static str {
240        match self {
241            Editor::Cursor => ".cursorrules",
242            Editor::Copilot => ".github/copilot-instructions.md",
243            Editor::Windsurf => ".windsurfrules",
244            Editor::Claude => ".claude/settings.json",
245            Editor::Aider => ".aider.conf.yml",
246            Editor::Cline => ".cline/settings.json",
247        }
248    }
249
250    /// Get a human-readable name for this editor
251    pub fn display_name(&self) -> &'static str {
252        match self {
253            Editor::Cursor => "Cursor",
254            Editor::Copilot => "GitHub Copilot",
255            Editor::Windsurf => "Windsurf",
256            Editor::Claude => "Claude Code",
257            Editor::Aider => "Aider",
258            Editor::Cline => "Cline",
259        }
260    }
261}
262
263/// Editor enablement configuration
264#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
265pub struct EditorConfig {
266    pub cursor: bool,
267    pub copilot: bool,
268    pub windsurf: bool,
269    pub claude: bool,
270    pub aider: bool,
271    pub cline: bool,
272}
273
274impl Default for EditorConfig {
275    fn default() -> Self {
276        Self {
277            cursor: true,
278            copilot: true,
279            windsurf: false,
280            claude: true,
281            aider: false,
282            cline: false,
283        }
284    }
285}
286
287/// Template configuration
288#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)]
289pub struct TemplateConfig {
290    /// Active persona templates
291    pub personas: Vec<String>,
292    /// Project type template
293    pub project: Option<String>,
294    /// Active standard templates
295    pub standards: Vec<String>,
296    /// Active workflow template
297    pub workflow: Option<String>,
298}
299
300/// Synchronization configuration
301#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
302pub struct SyncConfig {
303    /// Watch for file changes
304    pub watch: bool,
305    /// Automatically convert on change
306    pub auto_convert: bool,
307    /// Source of truth file
308    pub source_of_truth: String,
309}
310
311impl Default for SyncConfig {
312    fn default() -> Self {
313        Self {
314            watch: true,
315            auto_convert: true,
316            source_of_truth: ".driven/rules.drv".to_string(),
317        }
318    }
319}
320
321/// Context analysis configuration
322#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
323pub struct ContextConfig {
324    /// Patterns to include in analysis
325    pub include: Vec<String>,
326    /// Patterns to exclude from analysis
327    pub exclude: Vec<String>,
328    /// Path to binary index
329    pub index_path: String,
330}
331
332impl Default for ContextConfig {
333    fn default() -> Self {
334        Self {
335            include: vec!["src/**".to_string(), "crates/**".to_string()],
336            exclude: vec!["target/**".to_string(), "node_modules/**".to_string()],
337            index_path: ".driven/index.drv".to_string(),
338        }
339    }
340}
341
342/// A complete rule set that can be converted between formats
343#[derive(Debug, Clone, Default)]
344pub struct RuleSet {
345    /// The unified rules
346    pub rules: Vec<UnifiedRule>,
347    /// Source file path (if loaded from file)
348    pub source: Option<std::path::PathBuf>,
349}
350
351impl RuleSet {
352    /// Create an empty rule set
353    pub fn new() -> Self {
354        Self::default()
355    }
356
357    /// Load rules from any supported format
358    pub fn load(path: impl AsRef<std::path::Path>) -> Result<Self> {
359        let path = path.as_ref();
360        let parser = Parser::detect(path)?;
361        let rules = parser.parse_file(path)?;
362        Ok(Self {
363            rules,
364            source: Some(path.to_path_buf()),
365        })
366    }
367
368    /// Load rules from binary format (.drv)
369    pub fn load_binary(path: impl AsRef<std::path::Path>) -> Result<Self> {
370        let data = std::fs::read(path.as_ref())?;
371        let decoder = DrvDecoder::new(&data)?;
372        let rules = decoder.decode_all()?;
373        Ok(Self {
374            rules,
375            source: Some(path.as_ref().to_path_buf()),
376        })
377    }
378
379    /// Save rules to binary format (.drv)
380    pub fn save_binary(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
381        let encoder = DrvEncoder::new();
382        let data = encoder.encode(&self.rules)?;
383        std::fs::write(path, data)?;
384        Ok(())
385    }
386
387    /// Emit rules to a specific editor format
388    pub fn emit(&self, editor: Editor, path: impl AsRef<std::path::Path>) -> Result<()> {
389        let emitter = Emitter::for_editor(editor);
390        emitter.emit(&self.rules, path)?;
391        Ok(())
392    }
393
394    /// Emit rules to Cursor format
395    pub fn emit_cursor(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
396        self.emit(Editor::Cursor, path)
397    }
398
399    /// Emit rules to Copilot format
400    pub fn emit_copilot(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
401        self.emit(Editor::Copilot, path)
402    }
403
404    /// Emit rules to Windsurf format
405    pub fn emit_windsurf(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
406        self.emit(Editor::Windsurf, path)
407    }
408
409    /// Get the number of rules
410    pub fn len(&self) -> usize {
411        self.rules.len()
412    }
413
414    /// Check if the rule set is empty
415    pub fn is_empty(&self) -> bool {
416        self.rules.is_empty()
417    }
418
419    /// Get rules as unified format
420    pub fn as_unified(&self) -> Vec<UnifiedRule> {
421        self.rules.clone()
422    }
423}
424
425/// Main error type for Driven
426#[derive(Debug, thiserror::Error)]
427pub enum DrivenError {
428    #[error("IO error: {0}")]
429    Io(std::io::Error),
430
431    #[error("Parse error: {0}")]
432    Parse(String),
433
434    #[error("Format error: {0}")]
435    Format(String),
436
437    #[error("Invalid binary format: {0}")]
438    InvalidBinary(String),
439
440    #[error("Template error: {0}")]
441    Template(String),
442
443    #[error("Template not found: {0}")]
444    TemplateNotFound(String),
445
446    #[error("Validation error: {0}")]
447    Validation(String),
448
449    #[error("Sync error: {0}")]
450    Sync(String),
451
452    #[error("Config error: {0}")]
453    Config(String),
454
455    #[error("Security error: {0}")]
456    Security(String),
457
458    #[error("Context error: {0}")]
459    Context(String),
460
461    #[error("CLI error: {0}")]
462    Cli(String),
463
464    #[error("Unsupported format: {0}")]
465    UnsupportedFormat(String),
466}
467
468/// Result type alias for Driven operations
469pub type Result<T> = std::result::Result<T, DrivenError>;
470
471impl From<std::io::Error> for DrivenError {
472    fn from(err: std::io::Error) -> Self {
473        DrivenError::Io(err)
474    }
475}
476
477#[cfg(test)]
478mod tests {
479    use super::*;
480
481    #[test]
482    fn test_default_config() {
483        let config = DrivenConfig::default();
484        assert_eq!(config.version, "1.0");
485        assert_eq!(config.default_editor, Editor::Cursor);
486        assert!(config.editors.cursor);
487        assert!(config.editors.copilot);
488    }
489
490    #[test]
491    fn test_editor_paths() {
492        assert_eq!(Editor::Cursor.rule_path(), ".cursorrules");
493        assert_eq!(
494            Editor::Copilot.rule_path(),
495            ".github/copilot-instructions.md"
496        );
497        assert_eq!(Editor::Windsurf.rule_path(), ".windsurfrules");
498    }
499
500    #[test]
501    fn test_empty_ruleset() {
502        let rules = RuleSet::new();
503        assert!(rules.rules.is_empty());
504        assert!(rules.source.is_none());
505    }
506}