Skip to main content

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}