1pub 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
79pub mod binary;
81pub mod fusion;
82pub mod security;
83pub mod state;
84pub mod streaming;
85
86#[cfg(feature = "cli")]
87pub mod cli;
88
89pub 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
98pub 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
108pub 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
115pub use dcp::{ConnectionState, DcpClient, DcpConfig, ToolDefinition};
117
118pub use generator_integration::{
120 DrivenTemplate, DrivenTemplateProvider, DrivenTemplateType, GenerateParams, GeneratedFile,
121 GeneratorBridge, TemplateCategory, TemplateInfo,
122};
123
124pub use integration::{
126 BinaryFormatChecker, CrossCrateHeader, DrivenDcpBridge, DrivenTool, DrivenToolCategory,
127 HookMessage, HookMessenger, SourceCrate, SpecScaffoldResult, SpecScaffolder,
128 ValidationResult as IntegrationValidationResult,
129};
130
131pub use error::{EnhancedError, EnhancedResult, ErrorContext};
133
134pub use config_validation::{
136 ConfigValidator, ValidationError, ValidationReport, ValidationWarning, validate_config,
137};
138
139pub use hooks::{
141 AgentHook, BuildEvent, GitOp, HookAction, HookCondition, HookContext, HookEngine,
142 HookExecutionResult, HookTrigger, TestFilter, TestResult, TestStatus,
143};
144
145pub use steering::{AgentContext, FileReference, SteeringEngine, SteeringInclusion, SteeringRule};
147
148pub 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
158pub use sysinfo::{
160 BuildToolInfo, GitInfo, LanguageInfo, OsInfo, PackageManagerInfo, ProjectInfo, ProjectType,
161 ShellInfo, SystemInfo, SystemInfoCache, SystemInfoProvider, TestFrameworkInfo,
162};
163
164pub use agents::{Agent, AgentPersona, AgentRegistry, DelegationRequest, DelegationResult};
166
167pub use workflows::{
169 StepResult, Workflow, WorkflowBranch, WorkflowEngine, WorkflowPhase, WorkflowProgress,
170 WorkflowSession, WorkflowStep,
171};
172
173pub use scale::{
175 ComplexityAnalyzer, DependencyAnalyzer, FileSizeAnalyzer, HistoryAnalyzer,
176 ProjectContext as ScaleProjectContext, ProjectScale, ScaleAnalyzer, ScaleDetector,
177 ScaleRecommendation, TeamSizeAnalyzer,
178};
179
180pub use modules::{Module, ModuleDependency, ModuleManager, ModuleStatus};
182
183#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
185pub struct DrivenConfig {
186 pub version: String,
188 pub default_editor: Editor,
190 pub editors: EditorConfig,
192 pub templates: TemplateConfig,
194 pub sync: SyncConfig,
196 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#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, serde::Deserialize, serde::Serialize)]
215#[serde(rename_all = "lowercase")]
216pub enum Editor {
217 Cursor,
219 Copilot,
221 Windsurf,
223 Claude,
225 Aider,
227 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 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 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#[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#[derive(Debug, Clone, Default, serde::Deserialize, serde::Serialize)]
289pub struct TemplateConfig {
290 pub personas: Vec<String>,
292 pub project: Option<String>,
294 pub standards: Vec<String>,
296 pub workflow: Option<String>,
298}
299
300#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
302pub struct SyncConfig {
303 pub watch: bool,
305 pub auto_convert: bool,
307 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#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
323pub struct ContextConfig {
324 pub include: Vec<String>,
326 pub exclude: Vec<String>,
328 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#[derive(Debug, Clone, Default)]
344pub struct RuleSet {
345 pub rules: Vec<UnifiedRule>,
347 pub source: Option<std::path::PathBuf>,
349}
350
351impl RuleSet {
352 pub fn new() -> Self {
354 Self::default()
355 }
356
357 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 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 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 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 pub fn emit_cursor(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
396 self.emit(Editor::Cursor, path)
397 }
398
399 pub fn emit_copilot(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
401 self.emit(Editor::Copilot, path)
402 }
403
404 pub fn emit_windsurf(&self, path: impl AsRef<std::path::Path>) -> Result<()> {
406 self.emit(Editor::Windsurf, path)
407 }
408
409 pub fn len(&self) -> usize {
411 self.rules.len()
412 }
413
414 pub fn is_empty(&self) -> bool {
416 self.rules.is_empty()
417 }
418
419 pub fn as_unified(&self) -> Vec<UnifiedRule> {
421 self.rules.clone()
422 }
423}
424
425#[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
468pub 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}