spool-memory 0.2.3

Local-first developer memory system — persistent, structured knowledge for AI coding tools
Documentation
use crate::config::{AppConfig, ProjectConfig};
use crate::domain::{MatchedModule, MatchedScene, RouteInput};

pub fn match_modules(project: &ProjectConfig, input: &RouteInput) -> Vec<MatchedModule> {
    project
        .modules
        .iter()
        .filter_map(|module| {
            let mut reasons = Vec::new();
            for prefix in &module.path_prefixes {
                if input.files.iter().any(|file| file.starts_with(prefix)) {
                    reasons.push(format!("file matched path_prefix {prefix}"));
                }
            }
            for keyword in &module.keywords {
                if input.task.contains(keyword) {
                    reasons.push(format!("task matched keyword {keyword}"));
                }
            }
            if reasons.is_empty() {
                None
            } else {
                Some(MatchedModule {
                    id: module.id.clone(),
                    reasons,
                })
            }
        })
        .collect()
}

pub fn match_scenes(config: &AppConfig, input: &RouteInput) -> Vec<MatchedScene> {
    config
        .scenes
        .iter()
        .filter_map(|scene| {
            let reasons: Vec<String> = scene
                .keywords
                .iter()
                .filter(|keyword| input.task.contains(keyword.as_str()))
                .map(|keyword| format!("task matched keyword {keyword}"))
                .collect();
            if reasons.is_empty() {
                None
            } else {
                Some(MatchedScene {
                    id: scene.id.clone(),
                    reasons,
                    preferred_notes: scene.preferred_notes.clone(),
                })
            }
        })
        .collect()
}