subcog/lib.rs
1//! # Subcog
2//!
3//! A persistent memory system for AI coding assistants.
4//! Persistent memory system for AI coding assistants.
5//!
6//! Subcog captures decisions, learnings, and context from coding sessions
7//! and surfaces them when relevant through semantic search.
8//!
9//! ## Features
10//!
11//! - Single-binary distribution (<100MB, <10ms cold start)
12//! - Three-layer storage architecture (Persistence, Index, Vector)
13//! - Pluggable backends (SQLite+usearch, PostgreSQL+pgvector)
14//! - MCP server integration for AI agent interoperability
15//! - Claude Code hooks for seamless IDE integration
16//! - Semantic search with hybrid vector + BM25 ranking
17//!
18//! ## Example
19//!
20//! ```rust,ignore
21//! use subcog::{CaptureService, CaptureRequest, Namespace};
22//!
23//! let service = CaptureService::new(config)?;
24//! let result = service.capture(CaptureRequest {
25//! namespace: Namespace::Decisions,
26//! content: "Use PostgreSQL for primary storage".to_string(),
27//! ..Default::default()
28//! })?;
29//! ```
30
31#![deny(clippy::all)]
32#![warn(clippy::pedantic)]
33#![warn(clippy::nursery)]
34#![warn(missing_docs)]
35#![forbid(unsafe_code)]
36// multiple_crate_versions is inherently crate-level (detects duplicate transitive dependencies).
37// Cannot be moved to function level. Current duplicates: fastembed→ort transitive deps.
38#![allow(clippy::multiple_crate_versions)]
39
40use thiserror::Error as ThisError;
41
42// Module declarations
43pub mod cli;
44pub mod config;
45pub mod context;
46pub mod embedding;
47pub mod gc;
48pub mod git;
49pub mod hooks;
50pub mod io;
51pub mod llm;
52pub mod mcp;
53pub mod models;
54pub mod observability;
55pub mod rendering;
56pub mod security;
57pub mod services;
58pub mod storage;
59pub mod webhooks;
60
61// Re-exports for convenience
62pub use config::{FeatureFlags, OperationTimeoutConfig, OperationType, SubcogConfig};
63pub use embedding::Embedder;
64pub use llm::LlmProvider;
65pub use models::{
66 CaptureRequest, CaptureResult, DetailLevel, Domain, Memory, MemoryId, MemoryStatus, Namespace,
67 SearchFilter, SearchMode, SearchResult,
68};
69pub use services::{
70 CaptureService, ConsolidationService, ContextBuilderService, RecallService, SyncService,
71};
72pub use storage::{CompositeStorage, IndexBackend, PersistenceBackend, VectorBackend};
73
74/// Error type for subcog operations.
75///
76/// Uses `thiserror` for automatic `Display` and `Error` trait implementations.
77///
78/// # Error Variant Triggers
79///
80/// | Variant | Raised When |
81/// |---------|-------------|
82/// | `InvalidInput` | Missing required parameters, malformed JSON, invalid namespace names |
83/// | `OperationFailed` | I/O errors, git operations fail, database queries fail |
84/// | `ContentBlocked` | Secret patterns detected (API keys, tokens), PII detected |
85/// | `NotImplemented` | Calling unfinished features (e.g., PostgreSQL consolidation) |
86/// | `FeatureNotEnabled` | Using features requiring compile-time flags |
87/// | `Unauthorized` | Invalid/missing JWT token in MCP HTTP transport |
88#[derive(Debug, ThisError)]
89pub enum Error {
90 /// Invalid input was provided.
91 ///
92 /// Raised when:
93 /// - Required parameters are missing (e.g., empty content in capture)
94 /// - JSON deserialization fails in MCP tool handlers
95 /// - Invalid namespace string is provided
96 /// - Prompt template has invalid variable syntax
97 /// - Search query is empty or malformed
98 #[error("invalid input: {0}")]
99 InvalidInput(String),
100
101 /// An operation failed.
102 ///
103 /// Raised when:
104 /// - `SQLite` database operations fail
105 /// - Filesystem I/O errors occur
106 /// - Index backend is not configured
107 /// - Service container initialization fails
108 #[error("operation '{operation}' failed: {cause}")]
109 OperationFailed {
110 /// The operation that failed.
111 operation: String,
112 /// The underlying cause.
113 cause: String,
114 },
115
116 /// Content was blocked due to security concerns.
117 ///
118 /// Raised when:
119 /// - Secret detection finds API keys, tokens, or credentials
120 /// - PII patterns are detected (configurable)
121 /// - Content fails security policy checks
122 ///
123 /// See `security::secrets` for pattern definitions.
124 #[error("content blocked: {reason}")]
125 ContentBlocked {
126 /// The reason the content was blocked.
127 reason: String,
128 },
129
130 /// Feature not yet implemented.
131 ///
132 /// Raised when:
133 /// - PostgreSQL consolidation is called
134 /// - Redis consolidation is called
135 /// - Other stub implementations are invoked
136 #[error("not implemented: {0}")]
137 NotImplemented(String),
138
139 /// Feature not enabled (requires feature flag).
140 ///
141 /// Raised when:
142 /// - Optional Cargo features are not compiled in
143 /// - Currently unused but reserved for future gated features
144 #[error("feature not enabled: {0} (compile with --features {0})")]
145 FeatureNotEnabled(String),
146
147 /// Authentication failed (SEC-H1).
148 ///
149 /// Raised when:
150 /// - JWT token is missing in HTTP transport requests
151 /// - JWT token is expired or invalid
152 /// - JWT signature verification fails
153 /// - Insufficient entropy in JWT secret
154 #[error("unauthorized: {0}")]
155 Unauthorized(String),
156}
157
158/// Result type alias for subcog operations.
159pub type Result<T> = std::result::Result<T, Error>;
160
161/// Returns the current Unix timestamp in seconds.
162///
163/// This is a centralized utility function to avoid duplicate implementations
164/// across the codebase (CQ-H1). Uses `SystemTime::now()` with fallback to 0
165/// if the system clock is before the Unix epoch.
166///
167/// # Examples
168///
169/// ```rust
170/// use subcog::current_timestamp;
171///
172/// let ts = current_timestamp();
173/// assert!(ts > 0); // Should be a reasonable Unix timestamp
174/// ```
175#[must_use]
176pub fn current_timestamp() -> u64 {
177 use std::time::{SystemTime, UNIX_EPOCH};
178 SystemTime::now()
179 .duration_since(UNIX_EPOCH)
180 .map(|d| d.as_secs())
181 .unwrap_or(0)
182}
183
184// Placeholder functions (add, divide) and Config struct removed in code review cleanup.
185// Use SubcogConfig for configuration instead.
186
187#[cfg(test)]
188mod tests {
189 use super::*;
190
191 #[test]
192 fn test_error_display() {
193 let err = Error::InvalidInput("test error".to_string());
194 assert_eq!(err.to_string(), "invalid input: test error");
195
196 let err = Error::OperationFailed {
197 operation: "test".to_string(),
198 cause: "failed".to_string(),
199 };
200 assert_eq!(err.to_string(), "operation 'test' failed: failed");
201
202 let err = Error::ContentBlocked {
203 reason: "secrets detected".to_string(),
204 };
205 assert_eq!(err.to_string(), "content blocked: secrets detected");
206 }
207}