//! Scan all repos in a project for skills. use crate::AppService; use crate::error::AppError; use models::repos::repo::Entity as RepoEntity; use models::repos::repo::Column as RCol; use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; use uuid::Uuid; use super::scanner; impl AppService { /// Scan all repositories in a project and sync skills from `.claude/skills/` directories. /// /// This is called automatically during `git hook sync` and can also be triggered /// manually via `POST /api/projects/{name}/skills/scan`. /// /// - Discovers skills in each repo's `.claude/skills/` folder /// - Upserts repo-sourced skills (source = "repo") /// - Removes skills whose source repo no longer has the file /// - Manual skills (source = "manual") are never affected pub async fn skill_scan_repos( &self, project_uid: Uuid, _caller_uid: Uuid, ) -> Result { // Collect all repo IDs for this project let repos: Vec<_> = RepoEntity::find() .filter(RCol::Project.eq(project_uid)) .all(&self.db) .await?; let mut total_created = 0i64; let mut total_updated = 0i64; let mut total_removed = 0i64; let mut total_discovered = 0i64; for repo in repos { let result = scanner::scan_and_sync_skills(&self.db, project_uid, &repo).await?; total_created += result.created; total_updated += result.updated; total_removed += result.removed; total_discovered += result.discovered; } Ok(scanner::ScanSyncResult { discovered: total_discovered, created: total_created, updated: total_updated, removed: total_removed, }) } }