{"owner":"spacedriveapp","repo":"spacedrive","hasSkills":true,"totalSkillsCount":32,"totalTokensCount":22099,"categories":["root-instruction","anthropic-skill","plugin-manifest","claude-rule"],"hasMcp":false,"mcpConfig":null,"found":["core/AGENTS.md","extensions/photos/manifest.json","extensions/photos/ui_manifest.json","extensions/README.md","extensions/photos/README.md",".tasks/Claude.md","AGENTS.md","extensions/test-extension/manifest.json","extensions/test-extension/README.md","packages/ts-client/README.md","packages/ts-client/package.json","packages/ts-client/tsconfig.json","packages/ts-client/USAGE.md","packages/swift-client/README.md","packages/interface/CLAUDE.md","packages/interface/package.json","packages/interface/SHARED-UI-STRATEGY.md","packages/interface/tsconfig.json","packages/config/app.tsconfig.json","packages/config/base.tsconfig.json","packages/config/package.json","packages/config/tsconfig.json","packages/assets/package.json","packages/assets/README.md","packages/interface/src/contributors.json","packages/assets/lottie/loading-pulse.json","packages/assets/sounds/README.md","packages/ts-client/tests/integration/README.md","packages/interface/src/Spacebot/VISION.md","packages/assets/svgs/ext/icons.json","packages/interface/src/components/modals/FileOperationModal.README.md","packages/interface/src/components/Inspector/variants/README.md"],"skills":{"core/AGENTS.md":"# AGENTS.md - Spacedrive Core v2\n\n## Build/Test Commands\n\n- `cargo build` - Build the project\n- `cargo test` - Run all tests\n- `cargo test <test_name>` - Run specific test (e.g., `cargo test library_test`)\n- `cargo clippy` - Lint code\n- `cargo fmt` - Format code\n- `cargo run --bin sd-cli -- <command>` - Run CLI (note: binary is `sd-cli`, not `spacedrive`)\n\n## Code Style\n\n- **Imports**: Group std, external crates, then local modules with blank lines between\n- **Formatting**: Use `cargo fmt` - tabs for indentation, snake_case for variables/functions. DO NOT use emojis at all.\n- **Types**: Explicit types preferred, use `Result<T, E>` for error handling with `thiserror`\n- **Naming**: snake_case for functions/variables, PascalCase for types, SCREAMING_SNAKE_CASE for constants\n- **Error Handling**: Use `Result` types, `thiserror` for custom errors, `anyhow` for application errors\n- **Async**: Use `async/await`, prefer `tokio` primitives, avoid blocking operations\n- **Resumable Jobs**: For long-running jobs that need to be resumable, store the job's state within the job's struct itself. Use `#[serde(skip)]` for fields that should not be persisted. For example, in a file copy job, the list of already copied files can be stored to allow the job to resume from where it left off.\n- **Documentation**: Use `//!` for module docs, `///` for public items, include examples\n- **Architecture**: Follow a Command Query Responsibility Segregation (CQRS) and Domain-Driven Design (DDD) pattern.\n  - **Domain**: Core data structures and business logic are located in `src/domain/`. These are the \"nouns\" of your system.\n  - **Operations**: State-changing commands (actions) and data-retrieving queries are located in `src/ops/`. These are the \"verbs\" of your system.\n  - **Actions**: Operations that modify the state of the application. They should be self-contained and transactional.\n  - **Queries**: Operations that retrieve data without modifying state. They should be efficient and optimized for reading.\n- **Feature Modules**: Each new feature should be implemented in its own module within the `src/ops/` directory. For example, a new \"share\" feature would live in `src/ops/files/share`. Each feature module should contain the following files where applicable:\n  - `action.rs`: The main logic for the state-changing operation.\n  - `input.rs`: Data structures for the action's input.\n  - `output.rs`: Data structures for the action's output.\n  - `job.rs`: If the action is long-running, the job implementation.\n- **Database**: Use SeaORM entities, async queries, proper error propagation\n- **Comments**: Minimal inline comments, focus on why not what, no TODO comments in production code\n\n## Daemon Architecture\n\nSpacedrive uses a **daemon-client architecture** where a single daemon process manages the core functionality and multiple client applications (CLI, GraphQL server, desktop app) connect to it via Unix domain sockets.\n\n> **For detailed daemon architecture documentation, see [/docs/core/daemon.md](/docs/core/daemon.md)**\n\n### **The `Wire` Trait**\n\nAll actions and queries must implement the `Wire` trait to enable type-safe client-daemon communication:\n\n```rust\npub trait Wire {\n    const METHOD: &'static str;\n}\n```\n\n### **Registration Macros**\n\nInstead of manually implementing `Wire`, use these registration macros that automatically:\n\n1. Implement the `Wire` trait with the correct method string\n2. Register the operation in the global registry using the `inventory` crate\n\n**For Queries:**\n\n```rust\ncrate::register_query!(NetworkStatusQuery, \"network.status\");\n// Generates method: \"query:network.status\"\n```\n\n**For Library Actions:**\n\n```rust\ncrate::register_library_action!(FileCopyAction, \"files.copy\");\n// Generates method: \"action:files.copy.input\"\n```\n\n**For Core Actions:**\n\n```rust\ncrate::register_core_action!(LibraryCreateAction, \"libraries.create\");\n// Generates method: \"action:libraries.create.input\"\n```\n\n### **Registry System**\n\n- **Location**: `core/src/ops/registry.rs`\n- **Mechanism**: Uses the `inventory` crate for compile-time registration\n- **Global Maps**: `QUERIES` and `ACTIONS` hashmaps populated at startup\n- **Handler Functions**: Generic handlers that decode payloads, execute operations, and encode results\n\n## Logging Standards\n\n- **Setup**: Use `tracing_subscriber::fmt()` with env filter for structured logging\n- **Macros**: Use `info!`, `warn!`, `error!`, `debug!` from `tracing` crate, not `println!`\n- **Job Context**: Use `ctx.log()` in jobs for job-specific logging with automatic job_id tagging\n- **Structured**: Include relevant context fields: `debug!(job_id = %self.id, \"message\")`\n- **Levels**: debug for detailed flow, info for user-relevant events, warn for recoverable issues, error for failures\n- **Format**: `tracing_subscriber::fmt().with_env_filter(env_filter).init()` in main/examples\n- **Environment**: Respect `RUST_LOG` env var, fallback to module-specific filters like `sd_core=info`\n\n## Documentation\n\n- **Core level docs**: Live in `/docs/core` - comprehensive architecture and implementation guides\n- **Core design docs**: Live in `/docs/core/design` - planning documents, RFCs, and design decisions\n- **Application level docs**: Live in `/docs`\n- **Code docs**: Use `///` for public APIs, `//!` for module overviews, include examples\n\n## Debug Instructions\n\n- You can view the logs of a job in the job_logs directory in the root of the data folder\n- When testing the CLI, after compiling you must first use the `restart` command to ensure the Spacedrive daemon is using the latest build.\n","extensions/photos/manifest.json":"{\n\t\"id\": \"com.spacedrive.photos\",\n\t\"name\": \"Photos\",\n\t\"version\": \"1.0.0\",\n\t\"description\": \"Advanced photo management with AI-powered face recognition, place identification, and intelligent organization\",\n\t\"author\": \"Spacedrive\",\n\t\"wasm_file\": \"photos.wasm\",\n\t\"min_core_version\": \"2.0.0\",\n\t\"required_features\": [\n\t\t\"exif_extraction\",\n\t\t\"ai_models\",\n\t\t\"semantic_search\"\n\t],\n\t\"permissions\": {\n\t\t\"read_records\": {\n\t\t\t\"glob\": \"**/*.{jpg,jpeg,png,heic,heif,raw,cr2,nef,dng,webp}\"\n\t\t},\n\t\t\"read_sidecars\": [\n\t\t\t\"exif\",\n\t\t\t\"thumbnail\"\n\t\t],\n\t\t\"write_sidecars\": [\n\t\t\t\"faces\",\n\t\t\t\"places\",\n\t\t\t\"scene\",\n\t\t\t\"aesthetics\"\n\t\t],\n\t\t\"write_tags\": true,\n\t\t\"write_custom_fields\": [\n\t\t\t\"photos\"\n\t\t],\n\t\t\"dispatch_jobs\": true,\n\t\t\"use_models\": [\n\t\t\t{\n\t\t\t\t\"category\": \"face_detection\",\n\t\t\t\t\"preference\": \"local\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"category\": \"scene_classification\",\n\t\t\t\t\"preference\": \"local\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"category\": \"llm\",\n\t\t\t\t\"preference\": \"local\"\n\t\t\t}\n\t\t]\n\t},\n\t\"models\": [\n\t\t{\n\t\t\t\"name\": \"photos_face_detection_v1\",\n\t\t\t\"category\": \"face_detection\",\n\t\t\t\"source\": {\n\t\t\t\t\"type\": \"download\",\n\t\t\t\t\"url\": \"https://models.spacedrive.com/photos/face_detection_v1.onnx\",\n\t\t\t\t\"sha256\": \"placeholder_hash\",\n\t\t\t\t\"size_mb\": 12\n\t\t\t},\n\t\t\t\"description\": \"RetinaFace model for face detection\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"photos_scene_v1\",\n\t\t\t\"category\": \"scene_classification\",\n\t\t\t\"source\": {\n\t\t\t\t\"type\": \"download\",\n\t\t\t\t\"url\": \"https://models.spacedrive.com/photos/scene_resnet50.onnx\",\n\t\t\t\t\"sha256\": \"placeholder_hash\",\n\t\t\t\t\"size_mb\": 95\n\t\t\t},\n\t\t\t\"description\": \"ResNet50 trained on Places365 dataset\"\n\t\t}\n\t],\n\t\"kinds\": [\n\t\t{\n\t\t\t\"name\": \"raw\",\n\t\t\t\"display_name\": \"RAW photo\",\n\t\t\t\"parent\": \"image\",\n\t\t\t\"extensions\": [\n\t\t\t\t\"cr2\",\n\t\t\t\t\"cr3\",\n\t\t\t\t\"nef\",\n\t\t\t\t\"arw\",\n\t\t\t\t\"dng\",\n\t\t\t\t\"raf\",\n\t\t\t\t\"orf\",\n\t\t\t\t\"rw2\",\n\t\t\t\t\"pef\",\n\t\t\t\t\"dcr\",\n\t\t\t\t\"srw\"\n\t\t\t],\n\t\t\t\"mime_types\": [\n\t\t\t\t\"image/x-canon-cr2\",\n\t\t\t\t\"image/x-canon-cr3\",\n\t\t\t\t\"image/x-nikon-nef\",\n\t\t\t\t\"image/x-sony-arw\",\n\t\t\t\t\"image/x-adobe-dng\",\n\t\t\t\t\"image/x-fuji-raf\",\n\t\t\t\t\"image/x-olympus-orf\",\n\t\t\t\t\"image/x-panasonic-rw2\",\n\t\t\t\t\"image/x-pentax-pef\",\n\t\t\t\t\"image/x-kodak-dcr\",\n\t\t\t\t\"image/x-samsung-srw\"\n\t\t\t],\n\t\t\t\"magic\": [\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"49 49 2A 00\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"4D 4D 00 2A\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"46 55 4A 49 46 49 4C 4D\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"49 49 52 4F\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"49 49 55 00\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"preview\": {\n\t\t\t\t\"renderer\": \"image\"\n\t\t\t}\n\t\t}\n\t]\n}\n","extensions/photos/ui_manifest.json":"{\n\t\"sidebar\": {\n\t\t\"section\": \"Photos\",\n\t\t\"icon\": \"assets/photos_icon.svg\",\n\t\t\"order\": 10,\n\t\t\"views\": [\n\t\t\t{\n\t\t\t\t\"id\": \"library\",\n\t\t\t\t\"title\": \"Library\",\n\t\t\t\t\"component\": \"photo_grid\",\n\t\t\t\t\"query\": \"list_all_photos\",\n\t\t\t\t\"default\": true\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"albums\",\n\t\t\t\t\"title\": \"Albums\",\n\t\t\t\t\"component\": \"album_grid\",\n\t\t\t\t\"query\": \"list_albums\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"people\",\n\t\t\t\t\"title\": \"People\",\n\t\t\t\t\"component\": \"person_cluster_grid\",\n\t\t\t\t\"query\": \"list_people\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"places\",\n\t\t\t\t\"title\": \"Places\",\n\t\t\t\t\"component\": \"map_view\",\n\t\t\t\t\"query\": \"list_places\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"moments\",\n\t\t\t\t\"title\": \"Moments\",\n\t\t\t\t\"component\": \"moment_timeline\",\n\t\t\t\t\"query\": \"list_moments\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"favorites\",\n\t\t\t\t\"title\": \"Favorites\",\n\t\t\t\t\"component\": \"photo_grid\",\n\t\t\t\t\"query\": \"tags LIKE '#favorite'\",\n\t\t\t\t\"icon\": \"heart\"\n\t\t\t}\n\t\t]\n\t},\n\t\"context_menu\": [\n\t\t{\n\t\t\t\"id\": \"add_to_album\",\n\t\t\t\"label\": \"Add to Album...\",\n\t\t\t\"icon\": \"plus_circle\",\n\t\t\t\"action\": \"add_to_album\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t],\n\t\t\t\"keyboard_shortcut\": \"cmd+shift+a\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"identify_person\",\n\t\t\t\"label\": \"This is...\",\n\t\t\t\"icon\": \"person\",\n\t\t\t\"action\": \"identify_person\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t],\n\t\t\t\"requires\": \"has_detected_faces\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"set_as_cover\",\n\t\t\t\"label\": \"Set as Album Cover\",\n\t\t\t\"icon\": \"star\",\n\t\t\t\"action\": \"set_album_cover\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t],\n\t\t\t\"context\": \"album_view\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"hide_photo\",\n\t\t\t\"label\": \"Hide\",\n\t\t\t\"icon\": \"eye_slash\",\n\t\t\t\"action\": \"hide_photo\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t]\n\t\t}\n\t],\n\t\"toolbar_actions\": [\n\t\t{\n\t\t\t\"id\": \"analyze_location\",\n\t\t\t\"label\": \"Analyze for Faces\",\n\t\t\t\"icon\": \"sparkles\",\n\t\t\t\"action\": \"analyze_photos_batch\",\n\t\t\t\"context\": \"location_view\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"identify_places\",\n\t\t\t\"label\": \"Identify Places\",\n\t\t\t\"icon\": \"map_pin\",\n\t\t\t\"action\": \"identify_places_in_location\",\n\t\t\t\"context\": \"location_view\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"create_moment\",\n\t\t\t\"label\": \"Create Moment\",\n\t\t\t\"icon\": \"calendar\",\n\t\t\t\"action\": \"create_moments_from_selection\"\n\t\t}\n\t],\n\t\"file_viewers\": [\n\t\t{\n\t\t\t\"id\": \"photo_viewer\",\n\t\t\t\"bundle\": \"ui/photo_viewer.js\",\n\t\t\t\"mime_types\": [\n\t\t\t\t\"image/jpeg\",\n\t\t\t\t\"image/png\",\n\t\t\t\t\"image/heic\",\n\t\t\t\t\"image/webp\"\n\t\t\t],\n\t\t\t\"component\": \"photo_viewer\",\n\t\t\t\"features\": {\n\t\t\t\t\"slideshow\": true,\n\t\t\t\t\"metadata_panel\": true,\n\t\t\t\t\"face_indicators\": true,\n\t\t\t\t\"related_photos\": true\n\t\t\t}\n\t\t}\n\t],\n\t\"search_filters\": [\n\t\t{\n\t\t\t\"id\": \"has_people\",\n\t\t\t\"label\": \"Has People\",\n\t\t\t\"type\": \"boolean\",\n\t\t\t\"query\": \"tags LIKE '#person:%'\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"location\",\n\t\t\t\"label\": \"Location\",\n\t\t\t\"type\": \"place_picker\",\n\t\t\t\"query_template\": \"tags LIKE '#place:{value}'\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"date_range\",\n\t\t\t\"label\": \"Date\",\n\t\t\t\"type\": \"date_range\",\n\t\t\t\"query_template\": \"exif.taken_at BETWEEN '{start}' AND '{end}'\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"camera\",\n\t\t\t\"label\": \"Camera\",\n\t\t\t\"type\": \"select\",\n\t\t\t\"options_query\": \"SELECT DISTINCT exif.camera_model FROM photos\",\n\t\t\t\"query_template\": \"exif.camera_model = '{value}'\"\n\t\t}\n\t]\n}\n","extensions/README.md":"# Spacedrive Official Extensions\n\nThis directory contains the extension SDK and official extensions for Spacedrive.\n\n## Structure\n\n```\nextensions/\n├── spacedrive-sdk/          # Core SDK library\n├── spacedrive-sdk-macros/   # Proc macros for beautiful API\n├── test-extension/          # Example extension with beautiful API\n└── finance/                 # (Future) First revenue-generating extension\n```\n\n## Quick Start\n\n### 1. Install WASM Target\n\n```bash\nrustup target add wasm32-unknown-unknown\n```\n\n### 2. Create Extension\n\n```bash\ncargo new --lib my-extension\ncd my-extension\n```\n\n**Cargo.toml:**\n```toml\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacedrive-sdk = { path = \"../spacedrive-sdk\" }\nserde = { version = \"1.0\", features = [\"derive\"] }\n```\n\n**src/lib.rs:**\n```rust\nuse spacedrive_sdk::prelude::*;\nuse spacedrive_sdk::{extension, job};\n\n#[extension(\n    id = \"my-extension\",\n    name = \"My Extension\",\n    version = \"0.1.0\"\n)]\nstruct MyExtension;\n\n#[derive(Serialize, Deserialize, Default)]\npub struct MyJobState {\n    pub counter: u32,\n}\n\n#[job]\nfn my_job(ctx: &JobContext, state: &mut MyJobState) -> Result<()> {\n    ctx.log(\"Job starting!\");\n\n    state.counter += 1;\n    ctx.report_progress(1.0, \"Done!\");\n\n    Ok(())\n}\n```\n\n### 3. Build\n\n```bash\ncargo build --target wasm32-unknown-unknown --release\ncp target/wasm32-unknown-unknown/release/my_extension.wasm .\n```\n\n### 4. Create manifest.json\n\n```json\n{\n  \"id\": \"my-extension\",\n  \"name\": \"My Extension\",\n  \"version\": \"0.1.0\",\n  \"wasm_file\": \"my_extension.wasm\",\n  \"permissions\": {\n    \"read_records\": { \"glob\": \"**/*.{jpg,png}\" },\n    \"read_sidecars\": [\"exif\"],\n    \"write_sidecars\": [\"digest\"],\n    \"use_models\": [{ \"category\": \"face_detection\", \"preference\": \"local\" }]\n  }\n}\n```\n\nThe host rejects a manifest with a field it does not know, so a typo in a\ngrant fails at load. `read_records` is the only way to see files; its optional\n`glob` is honored for its trailing extension list only (`*.jpg` or\n`**/*.{jpg,png}`); the directory part is ignored and a glob of another shape\nis refused at load. `write_sidecars` names the kinds\nthe extension may write; `use_models` the inference categories it may ask for.\n`write_tags`, `write_custom_fields` and `dispatch_jobs` are declared but have\nno host side yet.\n\n## The Beautiful API\n\n### Before Macros (Manual FFI):\n```rust\n#[no_mangle]\npub extern \"C\" fn execute_my_job(\n    ctx_ptr: u32, ctx_len: u32,\n    state_ptr: u32, state_len: u32\n) -> i32 {\n    let ctx_json = unsafe { /* 30 lines of pointer manipulation */ };\n    let mut state = /* 40 lines of deserialization */;\n    // ... business logic buried in boilerplate ...\n}\n```\n**180+ lines, lots of unsafe**\n\n### After Macros (Beautiful):\n```rust\n#[job]\nfn my_job(ctx: &JobContext, state: &mut MyJobState) -> Result<()> {\n    // Just write business logic!\n    ctx.log(\"Working...\");\n    state.counter += 1;\n    Ok(())\n}\n```\n**60-80 lines, zero unsafe, pure logic**\n\n## API Reference\n\n### Extension Container\n\n```rust\n#[extension(\n    id = \"finance\",\n    name = \"Spacedrive Finance\",\n    version = \"0.1.0\"\n)]\nstruct Finance;\n```\n\nGenerates:\n- `plugin_init()` export\n- `plugin_cleanup()` export\n- Metadata for manifest generation\n\n### Job Definition\n\n```rust\n#[job]\nfn email_scan(ctx: &JobContext, state: &mut EmailScanState) -> Result<()> {\n    // Progress reporting\n    ctx.report_progress(0.5, \"Half done\");\n\n    // Checkpointing\n    ctx.checkpoint(state)?;\n\n    // Interruption handling\n    if ctx.check_interrupt() {\n        return Err(Error::OperationFailed(\"Interrupted\".into()));\n    }\n\n    // Metrics\n    ctx.increment_items(1);\n    ctx.increment_bytes(1000);\n\n    // Warnings\n    ctx.add_warning(\"Non-fatal issue\");\n\n    // Full SDK access\n    let record = ctx.vdfs().get_record(uuid).await?;\n    let bytes = record.read().await?;\n\n    Ok(())\n}\n```\n\n### VDFS Operations\n\n```rust\n// Records of the library's sources, filtered by extension (read_records grant)\nlet photos = ctx.vdfs()\n    .query_records()\n    .with_extensions([\"jpg\", \"png\"])\n    .collect()\n    .await?;\n\n// One record and its bytes\nlet record = ctx.vdfs().get_record(uuid).await?;\nlet bytes = record.read().await?;\n\n// Sidecars are one JSON document per kind, keyed by content uuid\nlet content_uuid = record.content_uuid().expect(\"hashed\");\nif !ctx.sidecar_exists(content_uuid, \"digest\")? {\n    ctx.save_sidecar(content_uuid, \"digest\", &digest).await?;\n}\nlet digest: Digest = ctx.read_sidecar(content_uuid, \"digest\").await?;\n\n// Models declared with #[model] live in the extension's own store\nctx.vdfs().create_model(entry).await?;\nlet all: Vec<CatalogEntry> = ctx.vdfs().query_models().collect().await?;\n```\n\nTags and custom fields have no host side yet and return `Error::NotAvailable`.\n\n### AI Operations\n\nInference goes through `ctx.ai().infer(...)` behind the `use_models` grant.\nThe core has no provider yet, so every request answers `Error::NotAvailable`;\nhandle it the way `extensions/photos/src/jobs/analyze.rs` does, with one\nwarning and a skip, so the job completes and a later run with a provider\npicks the files up.\n\n```rust\n// Written against the API; answers NotAvailable until a provider exists\nlet faces = ctx.ai().from_registered(\"face_detection\").detect_faces(&bytes).await?;\n```\n\n### Credentials\n\n```rust\n// Store OAuth\nctx.credentials().store(\"gmail\", Credential::oauth2(\n    access_token,\n    Some(refresh_token),\n    3600,\n    vec![\"https://www.googleapis.com/auth/gmail.readonly\".into()]\n))?;\n\n// Get (auto-refreshes)\nlet cred = ctx.credentials().get(\"gmail\")?;\n```\n\n## Examples\n\nSee `extensions/test-extension/` for a complete working example.\n\n## Building\n\nAll extensions:\n```bash\ncd extensions/test-extension\ncargo build --target wasm32-unknown-unknown --release\n```\n\n## Documentation\n\n- **[SDK API Vision](../docs/EXTENSION_SDK_API_VISION.md)** - Future API improvements\n- **[Before/After Comparison](./BEFORE_AFTER_COMPARISON.md)** - See the transformation\n- **[WASM Architecture](../docs/core/design/WASM_ARCHITECTURE_FINAL.md)** - Technical details\n- **[Platform Revenue Model](../docs/PLATFORM_REVENUE_MODEL.md)** - Business case\n\n---\n\n**Extension development is now beautiful, safe, and productive. Start building!** \n","extensions/photos/README.md":"# Photos Extension for Spacedrive\n\nA comprehensive photo management extension that brings Apple Photos and Google Photos capabilities to Spacedrive.\n\n## Features\n\n### Face Recognition\n- **Automatic face detection** using RetinaFace model\n- **Face clustering** with DBSCAN algorithm\n- **Person identification** with name labeling\n- **Face-based search** (\"Show me photos of Alice\")\n- **Cross-device sync** of face clusters\n\n### Place Identification\n- **GPS extraction** from EXIF data\n- **Geographic clustering** (groups photos within 500m)\n- **Reverse geocoding** using AI\n- **Place-based search** (\"Photos from Paris\")\n- **Map view** of photo locations\n\n### Moments\n- **Automatic moment generation** (time + location clustering)\n- **AI-generated titles** (\"Summer in Paris\")\n- **Weekly memories** (scheduled agent task)\n- **Moment timeline** view\n\n### ️ Scene Understanding\n- **Scene classification** (ResNet50 on Places365)\n- **Smart tags** (#beach, #sunset, #food)\n- **Scene-based search** (\"Photos with sunsets\")\n- **Quality scoring** (aesthetic assessment)\n\n### Albums & Organization\n- **Manual albums** (user-created)\n- **Smart albums** (rule-based)\n- **Favorites** (special album)\n- **Hidden photos** (privacy)\n- **Shared albums** (multi-device)\n\n## Architecture\n\n### Models\n\n```rust\nPhoto       // Links to image file + EXIF + face/scene sidecars\nPerson      // Face cluster with name and embeddings\nPlace       // Geographic location with radius\nAlbum       // Collection of photos\nMoment      // Time/location-based photo group\n```\n\n### Agent Memory\n\n```rust\nPhotosMind {\n    history: TemporalMemory<PhotoEvent>,     // Analysis timeline\n    knowledge: AssociativeMemory<PhotoKnowledge>, // Face/place graph\n    plan: WorkingMemory<AnalysisPlan>,       // Pending work\n}\n```\n\n### Jobs\n\n- `analyze_photos_batch` - Face detection on photos\n- `identify_places_in_location` - Place clustering and naming\n- `analyze_scenes` - Scene classification\n- `create_moments` - Automatic moment generation\n- `cluster_faces_into_people` - Face clustering\n- `generate_face_tags` - Tag generation from sidecars\n\n### Actions\n\n- `create_album` - User creates album\n- `identify_person` - Name a face cluster\n- `remove_photo_from_album` - Organize albums\n- `hide_photo` - Privacy controls\n\n## Usage\n\n### Installation\n\n1. Install extension from Spacedrive Extension Store\n2. Grant permissions to specific photo locations:\n   - `/Users/alice/Photos`\n   - `/Volumes/External/Family Photos`\n3. Extension downloads AI models (~107MB total)\n4. User initiates analysis: \"Analyze for Faces\" button\n\n### Analysis Flow\n\n```\nUser enables Photos on \"/My Photos\"\n  ↓\nExtension dispatches analyze_photos_batch job\n  ↓\nFor each photo:\n  - Read EXIF (from Core)\n  - Detect faces → save to sidecar\n  - Classify scene → save to sidecar\n  ↓\nCluster faces into people\n  ↓\nGenerate tags from sidecars\n  ↓\nUser can search \"#person:alice\" or \"photos from beach\"\n```\n\n### Data Storage\n\n```\n~/.spacedrive/\n  └── models/\n      ├── face_detection/\n      │   └── photos_v1.onnx (12MB)\n      └── scene_classification/\n          └── resnet50.onnx (95MB)\n\n.sdlibrary/\n  ├── sidecars/\n  │   └── content/{ab}/{cd}/{uuid}/\n  │       └── extensions/com.spacedrive.photos/\n  │           ├── faces/default.json       # Face detection results\n  │           ├── scene/default.json       # Scene classification\n  │           └── aesthetics/default.json  # Quality score\n  └── extensions/com.spacedrive.photos/\n      └── data.db                           # Person, Place, Moment, Album, Photo rows\n```\n\n## SDK Features Demonstrated\n\n### Implemented in Example\n\n- `#[extension]` with permissions and dependencies\n- `#[model]` for Photo, Person, Place, Album, Moment\n- `#[agent]` with lifecycle hooks and event handlers\n- `#[agent_memory]` with enum-based Temporal/Associative memory\n- `#[job]` and `#[task]` for durable processing\n- `#[action]` with preview-execute pattern\n- `#[query]` for user searches\n- Extension-owned sidecars\n- Virtual models with persistence\n- User-scoped permissions\n- Model registration on install\n- AI with Jinja templates\n- Tag generation from sidecars\n- Custom memory query methods\n\n### What runs today\n\nUnder a daemon built with `--features wasm`, `analyze_photos` runs end to end\nover a library: it reads each photo through its record, asks for face\ndetection, and because the core has no inference provider the host answers\n`not_available`; the job warns once, skips, and completes, so a later run with\na detector picks the photos up. `core/tests/wasm_extension_test.rs` proves\nthis against a fixture library.\n\n### Not yet backed by the core\n\n- Inference. There is no provider for face detection, scene classification,\n  embeddings or language models, so `analyze_scenes`, `identify_places` and\n  `create_moments` have nothing to run on.\n- Tags, custom fields and `dispatch_jobs`.\n- Agents, actions and queries.\n- Model downloads named in `manifest.json`.\n- File-kind and preview registration.\n\n## Capabilities Compared\n\n### Apple Photos\n- Face recognition and clustering\n- Place identification\n- Memories\n- Albums (manual and smart)\n- Favorites and hidden\n- Search by person/place/scene\n- Map view\n- ️ Shared albums (requires P2P implementation)\n\n### Google Photos\n- Face grouping\n- Place detection\n- Search by content\n- Albums\n- Automatic creations (moments)\n- Scene/object detection\n- ️ Cloud backup (Spacedrive handles differently)\n\n### Spacedrive Photos Advantages\n- **100% local** - No cloud upload required\n- **Privacy-first** - Face data never leaves devices\n- **Multi-device sync** - Via P2P, not cloud\n- **Zero recurring cost** - No subscription needed\n- **User-scoped** - Only analyzes chosen locations\n- **Extensible** - Open SDK for customization\n\n## Building\n\n```bash\ncd extensions/photos\ncargo build --target wasm32-unknown-unknown --release\ncp target/wasm32-unknown-unknown/release/photos_extension.wasm ./photos.wasm\n```\n\n## License\n\nSame as Spacedrive Core\n\n",".tasks/Claude.md":"# Task Tracking Cheatsheet\n\nHi Claude, Claude here. Your owner has pointed you here with absolutely no context. This document is your prompt.\n\nThis directory tracks the complete development of Spacedrive. Tasks are organized into subdirectories:\n- `core/` - Backend/Rust tasks (CORE, JOB, INDEX, LSYNC, etc.)\n- `interface/` - Frontend/React tasks (UI, EXPL, SETS, MEDIA, etc.)\n- `mobile/` - Mobile-specific tasks (IOS, MACOS, AND)\n\nTasks are always kept up-to-date, though some may need updating based on recent work. Your job is to check git history for changes, validate completed tasks against the instructions below, and update their status accordingly.\n\n## Quick Start\n\nWhen asked to review and update tasks, follow this process:\n\n### 1. List Current Tasks\n\n```bash\ncargo run --bin task-validator -- list --sort-by status\n```\n\nThis shows all tasks grouped by status (Completed, Done, In Progress, To Do).\n\n### 2. Review Recent Commits\n\n```bash\ngit log --oneline -20\n```\n\nLook for feature implementations, major refactors, or completed work that might correspond to tasks.\n\n### 3. Cross-Reference Work with Tasks\n\nFor each recent feature/change:\n\n- **Read the task file** directly from `.tasks/{subdirectory}/TASK-ID-name.md`\n  - Core/backend work: `.tasks/core/`\n  - Interface work: `.tasks/interface/`\n  - Mobile work: `.tasks/mobile/`\n- **Read the acceptance criteria** carefully - this is your checklist\n- **Read all implementation files** mentioned in the task (they're usually listed in an \"Implementation Files\" section)\n- **Check core/tests/** for integration tests that validate the feature (for core tasks)\n- **Check packages/interface/src/** for interface implementations (for interface tasks)\n- Verify each acceptance criterion is actually met in the code\n\n### 4. Update Task Status\n\nWhen updating a task's status, edit the YAML front matter:\n\n```yaml\n---\nid: TASK-000\ntitle: Task Title\nstatus: Done # Changed from \"To Do\" or \"In Progress\"\nassignee: jamiepine\npriority: High\ntags: [core, feature]\nlast_updated: 2025-10-14 # Update this date\n---\n```\n\n**Status Levels:**\n\n- `To Do` - Not started\n- `In Progress` - Actively being worked on\n- `Done` - Complete and merged\n- `Completed` - Same as Done (legacy)\n\n### 5. Validate Changes\n\nBefore committing task updates, validate them:\n\n```bash\ncargo run --bin task-validator -- validate\n```\n\nThis checks that your YAML front matter matches the schema.\n\n## Task Status Guidelines\n\n### Mark as \"Done\" when:\n\n- All acceptance criteria are met\n- Code is merged to main\n- Tests pass (if applicable)\n- Feature is actually usable\n\n### Keep \"In Progress\" when:\n\n- ️ Partially implemented\n- ️ Core work done but rough edges remain\n- ️ Actively being iterated on\n\n### Keep \"To Do\" when:\n\n- Not started\n- Only design/planning done\n- Mentioned in docs but no code exists\n\n## Pro Tips\n\n1. **Read implementation files directly** - Open and read the actual source files mentioned in the task.\n\n2. **Always check core/tests/** for integration tests that validate the feature works end-to-end.\n\n3. **Check recent file changes:**\n\n   ```bash\n   git diff --name-only HEAD~10\n   ```\n\n4. **Search commits by keyword:**\n\n   ```bash\n   git log --oneline --grep=\"volume\\|cloud\" -i\n   ```\n\n5. **Filter tasks by criteria:**\n\n   ```bash\n   # Show only \"In Progress\" tasks\n   cargo run --bin task-validator -- list --status \"In Progress\"\n\n   # Show tasks by priority\n   cargo run --bin task-validator -- list --priority High\n   ```\n\n## Common Pitfalls\n\n**Don't mark tasks done if:**\n\n- Implementation exists but doesn't work\n- Only scaffolding/types are defined\n- Tests are failing\n- Feature is behind a feature flag and disabled\n\n**Do mark tasks done if:**\n\n- All acceptance criteria are genuinely met\n- Implementation is production-ready\n- Code demonstrates the feature working\n\n## Example Workflow\n\n```bash\n# 1. See current state\ncargo run --bin task-validator -- list --sort-by status\n\n# 2. Check what's been done recently\ngit log --oneline -15\n\n# 3. Found \"feat: implement cloud volume support\" commit\n#    Let's check CLOUD-003 task\n```\n\nNow use the Read tool to thoroughly verify:\n\n````\n# 4. Read the task file (note the subdirectory)\nRead .tasks/core/CLOUD-003-cloud-volume.md\n\n# 5. Read every implementation file listed in the task\nRead core/src/volume/backend/mod.rs\nRead core/src/volume/backend/cloud.rs\nRead core/src/ops/volumes/add_cloud/mod.rs\n... (read all mentioned files)\n\n# 6. Check for integration tests\nGlob core/tests/**/*.rs\nRead core/tests/test_cloud_volume.rs  # if it exists\n\n# 7. Verify each acceptance criterion:\n#    - [x] User can add S3 bucket? Check add_cloud action code\n#    - [x] Cloud volume can be indexed? Check indexer integration\n#    - [ ] Files can be copied to/from cloud? Search for copy implementation\n#\n# If ALL criteria met → mark \"Done\"\n# If SOME criteria met → keep \"In Progress\"\n# If implementation looks complete → Update status and last_updated date\n\n# 8. Validate before committing\n```bash\ncargo run --bin task-validator -- validate\n````\n\n## Task Schema Reference\n\nRequired fields in YAML front matter:\n\n- `id` - Unique identifier (e.g., CORE-001)\n- `title` - Human-readable title\n- `status` - One of: To Do, In Progress, Done, Completed\n- `assignee` - Who's working on it (or \"james\")\n- `priority` - Critical, High, Medium, or Low\n- `tags` - Array of relevant tags\n\nOptional fields:\n\n- `parent` - Parent epic/task ID\n- `whitepaper` - Reference to design docs\n- `last_updated` - ISO date of last update\n- `related_tasks` - Array of related task IDs\n\n---\n\n**Remember**: The task tracker is only useful if it's honest. Be rigorous about what \"Done\" means. When in doubt, leave it as \"In Progress\" and document what's left.\n\n— Past Claude, trying to help Future Claude (that's you!)\n","AGENTS.md":"# Spacedrive Core v2 Development Guide\n\n## Quick Start\n\n### Development Workflow\n\n1. Start daemon: `cargo run --bin sd-daemon`\n2. Make code changes\n3. Run tests: `cargo test`\n4. Rebuild and restart: `cargo run --bin sd-cli -- restart`\n5. Test via CLI: `cargo run --bin sd-cli -- <command>`\n\n### Common Commands\n\n```bash\ncargo build                              # Build the project\ncargo test                               # Run all tests\ncargo test <test_name>                   # Run specific test\ncargo clippy                             # Lint code\ncargo fmt                                # Format code\ncargo run --bin sd-cli -- <command>      # Run CLI (binary is sd-cli, not spacedrive)\n```\n\n### Common Mistakes\n\n- Running `spacedrive` instead of `sd-cli` (the binary name is `sd-cli`)\n- Forgetting to restart daemon after rebuilding\n- Using `println!` instead of `tracing` macros (`info!`, `debug!`, etc)\n- Implementing `Wire` manually instead of using `register_*` macros\n- Blocking the async runtime with synchronous I/O operations\n\n### Quick tips\n\n- On frontend apps, such as the interface in React, you must ALWAYS ensure type-safety based on the auto generated TypeScript types from `ts-client`. Never cast to as any or redefine backend types. our hooks are typesafe with correct input/output types, but sometimes you might need to access types directly from the `ts-client`.\n- If you have changed types on the backend that are public to the frontend (have `Type` derive), then you must regenerate the types using `cargo run --bin generate_typescript_types`\n- Read the `.mdx` files in /docs for context on any part of the app, they are kept up to date.\n\n### Current direction\n\n`docs/core/product-direction.mdx` defines the product boundary. Use it to decide\nwhether work belongs in Spacedrive before extending a subsystem.\n\nThe entries world is gone. The `entry` and `location` tables and everything\nhanging off them were dropped, and durable file state lives in the record table\nin `crates/store`; `docs/plans/2026-09-15-entries-final-drop.md` records how.\n`docs/plans/2026-08-22-source-convergence.md` is the register for making the\nnew world one thing: one store shape, one registry, one set of claims that\nmatch the code.\n\n- Spacedrive is a set of sources. A source has an origin, an ingest, and a\n  store; filesystem and adapter sources differ only in ingest. Anything that\n  makes those two diverge further is wrong.\n- New durable state belongs in a source store: the record table, or the\n  assertion half for anything no ingest can rebuild. `library.db` keeps only\n  the source registry, devices, volumes, spaces, and small shared state.\n- A source store has two halves. The generation is rebuildable for as long as\n  its origin answers; the assertion layer never is. Since the origin can stop\n  answering silently, no code path may assume a store can be thrown away and\n  rebuilt. `docs/core/design/source-durability.md` has the rest, including what\n  the assertion tables have to reserve for sync.\n- The `.tasks/` tree predates this work and is not its register. The status\n  table at the top of the execution plan remains the entries-teardown register.\n  Use `PROJECT_STATUS.md` as the short-term project-wide working context and\n  update it in the same commit as active work.\n\n## Architecture Overview\n\nSpacedrive uses daemon-client architecture. A single daemon process manages core functionality. Multiple clients (CLI, GraphQL server, desktop app) connect via Unix domain sockets.\n\n### CQRS and DDD Pattern\n\n- **Domain** (`src/domain/`): Core data structures and business logic (nouns)\n- **Operations** (`src/ops/`): Actions and queries (verbs)\n- **Actions**: State-changing operations (writes)\n- **Queries**: Data retrieval without state changes (reads)\n\n### Feature Module Structure\n\nEach feature lives in its own module under `src/ops/`. Example: `src/ops/files/share`\n\n```\nsrc/ops/files/share/\n├── action.rs      # State-changing logic\n├── input.rs       # Action input structures\n├── output.rs      # Action output structures\n└── job.rs         # Long-running job implementation (if needed)\n```\n\nComplete feature example:\n\n```rust\n// src/ops/files/share/input.rs\n#[derive(Debug, Serialize, Deserialize)]\npub struct ShareFileInput {\n    pub file_id: i32,\n    pub recipient: String,\n}\n\n// src/ops/files/share/output.rs\n#[derive(Debug, Serialize, Deserialize)]\npub struct ShareFileOutput {\n    pub share_id: String,\n    pub url: String,\n}\n\n// src/ops/files/share/action.rs\nuse super::{ShareFileInput, ShareFileOutput};\n\npub struct ShareFileAction;\n\ncrate::register_library_action!(ShareFileAction, \"files.share\");\n\nimpl Action for ShareFileAction {\n    type Input = ShareFileInput;\n    type Output = ShareFileOutput;\n\n    async fn run(input: Self::Input, ctx: &ActionContext) -> Result<Self::Output> {\n        // Implementation\n    }\n}\n```\n\n## Communication Architecture\n\nSpacedrive supports multiple communication patterns for different platforms and use cases.\n\n### Daemon-Client Communication (Tauri Desktop, CLI, Web)\n\nThe Tauri desktop app, CLI, and web interface connect to a daemon process via Unix domain sockets (or WebSockets for web). Communication uses JSON-RPC 2.0 with Wire method strings.\n\n**Registration Macros:**\n\nNever implement `Wire` manually. Use registration macros:\n\n```rust\n// Queries\ncrate::register_query!(NetworkStatusQuery, \"network.status\");\n// Generates: \"query:network.status\"\n\n// Library Actions\ncrate::register_library_action!(FileCopyAction, \"files.copy\");\n// Generates: \"action:files.copy.input\"\n\n// Core Actions\ncrate::register_core_action!(LibraryCreateAction, \"libraries.create\");\n// Generates: \"action:libraries.create.input\"\n```\n\n**Registry System:**\n\nThe `inventory` crate collects operations at compile time. When you use `register_core_query!`, `register_library_query!`, `register_core_action!` or `register_library_action!`, the operation automatically appears in the global `CORE_QUERIES`, `LIBRARY_QUERIES`, `CORE_ACTIONS` and `LIBRARY_ACTIONS` maps at startup. You never manually register operations.\n\nLocation: `core/src/infra/wire/registry.rs`\n\n### Tauri Desktop Development\n\nThe Tauri app (`apps/tauri/`) is the primary desktop application for Spacedrive. It connects to the daemon via the TypeScript client.\n\n**Development Workflow:**\n\n```bash\n# Install dependencies\nbun install\n\n# Run Tauri app in dev mode (auto-starts daemon)\ncd apps/tauri\nbun run tauri:dev\n\n# Build for production\nbun run tauri:build\n```\n\n**TypeScript Client:**\n\nThe TypeScript client (`packages/ts-client/`) is auto-generated from Rust types using Specta:\n\n```bash\n# Generate TypeScript types\ncargo run --bin generate_typescript_types\n```\n\n**Output:** `packages/ts-client/src/generated/types.ts`\n\n**Architecture:**\n\n```\nTauri App (React)\n    ↓\n@sd/ts-client (TypeScript)\n    ↓\nDaemon (Unix Socket / IPC)\n    ↓\nRpcServer (Rust)\n    ↓\nOperation Registry\n```\n\n### Native Prototypes (iOS, macOS)\n\n**Note:** iOS and macOS apps are experimental prototypes, not production apps.\n\nNative prototypes embed the core directly as a library via FFI rather than connecting to a daemon. These are located in `apps/ios/` and `apps/macos/` but are private and not documented for public use.\n\n**Swift Client Generation:**\n\nFor the prototypes, Swift types can be generated:\n\n```bash\ncargo run --bin generate_swift_types\n```\n\nOutput: `packages/swift-client/Sources/SpacedriveClient/`\n\n### Extension System (WASM)\n\nExtensions run as sandboxed WASM modules that interact with Spacedrive core via host functions. Extensions are distributed as compiled `.wasm` files.\n\n**Architecture:**\n\n```\nExtension.wasm (compiled Rust)\n    ↓\nspacedrive-sdk (Rust crate)\n    ↓\nHost Functions (FFI boundary)\n    ↓\nCore (VDFS, Jobs, AI, etc.)\n```\n\n**Key Components:**\n\n**SDK Location:** `crates/sdk/`\n\n- High-level Rust API abstracting FFI details\n- Procedural macros for extension definition\n- Type-safe job, model, and action builders\n\n**Extension Development:**\n\nExtensions use procedural macros to minimize boilerplate:\n\n```rust\nuse spacedrive_sdk::prelude::*;\n\n#[extension(\n    id = \"test-extension\",\n    name = \"Test Extension\",\n    version = \"0.1.0\",\n    jobs = [test_counter],\n)]\nstruct TestExtension;\n\n#[derive(Serialize, Deserialize, Default)]\npub struct CounterState {\n    pub current: u32,\n    pub target: u32,\n    pub processed: Vec<String>,\n}\n\n#[job(name = \"counter\")]\nfn test_counter(ctx: &JobContext, state: &mut CounterState) -> Result<()> {\n    ctx.log(&format!(\"Starting counter (current: {}, target: {})\",\n        state.current, state.target));\n\n    while state.current < state.target {\n        if ctx.check_interrupt_sync() {\n            ctx.checkpoint(state)?;\n            return Err(Error::OperationFailed(\"Interrupted\".into()));\n        }\n\n        state.current += 1;\n        ctx.report_progress(\n            state.current as f32 / state.target as f32,\n            &format!(\"Counted {}/{}\", state.current, state.target),\n        );\n\n        if state.current % 10 == 0 {\n            ctx.checkpoint(state)?;\n        }\n    }\n\n    Ok(())\n}\n```\n\n**Host Functions:**\n\nExtensions import minimal FFI functions:\n\n```rust\n#[link(wasm_import_module = \"spacedrive\")]\nextern \"C\" {\n    fn spacedrive_log(level: u32, msg_ptr: *const u8, msg_len: usize);\n    fn register_job(\n        job_name_ptr: *const u8,\n        job_name_len: u32,\n        export_fn_ptr: *const u8,\n        export_fn_len: u32,\n        resumable: u32,\n    ) -> i32;\n}\n```\n\n**Building Extensions:**\n\n```bash\n# From extension directory\ncargo build --target wasm32-unknown-unknown --release\n\n# Output: target/wasm32-unknown-unknown/release/extension_name.wasm\n```\n\n**Extension Capabilities:**\n\nExtensions can define:\n\n- Models: Data structures stored in `models` table (content-scoped, standalone, or entry-scoped)\n- Jobs: Long-running resumable operations\n- Actions: User-invoked operations with preview-commit workflow\n- Agents: Autonomous logic with memory and event handling\n- UI: Custom views via `ui_manifest.json`\n\n**Example Use Cases:**\n\n- Photos extension: Face detection, scene tagging, album organization\n- Finance extension: Receipt extraction, expense tracking\n- Research extension: Citation extraction, knowledge graphs\n\n**Key Benefits:**\n\n- Single `.wasm` file works on all platforms\n- True sandboxing (WASM isolation)\n- Resumable jobs with checkpointing\n- Type-safe API with procedural macros\n- No core modifications needed for new features\n\n**Documentation:**\n\n- `crates/sdk/API_REFERENCE.md` - SDK specification and API reference\n- `extensions/test-extension/` - Working example extension\n- `crates/sdk/` - SDK implementation\n- `crates/sdk-macros/` - SDK procedural macros\n\n**Status:** A daemon built with the `wasm` feature loads every extension under `<data dir>/extensions/` at startup and runs the jobs they register through the job manager, with checkpoints and resume (`core/src/infra/extension/README.md`). Only the job host functions exist; the rest of the SDK (VDFS, AI, models, tasks) has no host side yet.\n\n## Code Standards\n\n### Import Organization\n\nGroup imports with blank lines between groups:\n\n```rust\n// Standard library\nuse std::path::PathBuf;\nuse std::sync::Arc;\n\n// External crates\nuse serde::{Deserialize, Serialize};\nuse tokio::sync::RwLock;\n\n// Local modules\nuse crate::domain::library::Library;\nuse crate::ops::Action;\n```\n\n### Naming Conventions\n\n- Functions/variables: `snake_case`\n- Types: `PascalCase`\n- Constants: `SCREAMING_SNAKE_CASE`\n\n### Error Handling\n\nUse `Result<T, E>` for all fallible operations. Use `thiserror` for custom errors, `anyhow` for application errors.\n\n```rust\nuse thiserror::Error;\n\n#[derive(Error, Debug)]\npub enum ShareError {\n    #[error(\"File not found: {0}\")]\n    FileNotFound(i32),\n\n    #[error(\"Permission denied\")]\n    PermissionDenied,\n\n    #[error(\"Database error: {0}\")]\n    Database(#[from] sea_orm::DbErr),\n}\n\npub async fn share_file(id: i32) -> Result<String, ShareError> {\n    let file = find_file(id).await.ok_or(ShareError::FileNotFound(id))?;\n    // Implementation\n    Ok(share_url)\n}\n```\n\n### Async Code\n\n- Use `async/await` syntax\n- Prefer `tokio` primitives (`tokio::sync::RwLock`, `tokio::spawn`)\n- Avoid blocking operations (use `tokio::fs` not `std::fs`)\n- Use `tokio::task::spawn_blocking` for CPU-intensive work\n\n### Resumable Jobs\n\nStore job state within the job struct. Use `#[serde(skip)]` for non-persistent fields.\n\n```rust\n#[derive(Serialize, Deserialize)]\npub struct FileCopyJob {\n    pub source: PathBuf,\n    pub destination: PathBuf,\n    pub copied_files: Vec<PathBuf>,  // Persisted for resumability\n\n    #[serde(skip)]\n    pub progress_tx: Option<tokio::sync::mpsc::Sender<Progress>>,  // Not persisted\n}\n\nimpl Job for FileCopyJob {\n    async fn run(&mut self, ctx: &JobContext) -> Result<()> {\n        ctx.log().info(\"Starting file copy job\");\n\n        for file in &self.files_to_copy {\n            if self.copied_files.contains(file) {\n                continue;  // Skip already copied files on resume\n            }\n\n            copy_file(file).await?;\n            self.copied_files.push(file.clone());\n        }\n\n        Ok(())\n    }\n}\n```\n\n### Documentation\n\n**Core principle:** Explain WHY, not WHAT. Keep comments as short as possible. One sentence explaining rationale beats a paragraph restating code.\n\n**Module docs (`//!`):**\n- Add a title with `#` for the module name\n- Explain what the module does in plain language (not bullet points)\n- Include design rationale naturally in prose\n- Add runnable code examples showing usage\n\n````rust\n//! # File Sharing System\n//!\n//! `core::ops::files::share` provides temporary file sharing via signed URLs.\n//! Share links expire after 7 days by default to prevent indefinite access to\n//! private files. UUID v5 deterministic IDs ensure the same file generates\n//! consistent share URLs across devices without coordination.\n//!\n//! ## Example\n//! ```rust,no_run\n//! use spacedrive_core::ops::files::share::{ShareFileAction, ShareFileInput};\n//!\n//! let input = ShareFileInput { file_id: 123, recipient: \"user@example.com\" };\n//! let output = ShareFileAction::run(input, &ctx).await?;\n//! ```\n````\n\n**Function docs (`///`):**\n- First line: brief one-liner\n- Second paragraph: explain design rationale and why this exists\n- Document error handling philosophy when relevant\n- Explain non-obvious behavior and platform differences\n\n```rust\n/// Creates a share link with automatic expiration.\n///\n/// Share links use signed JWTs so the daemon can validate them without\n/// database lookups on every request. Expiration is enforced server-side\n/// to prevent timezone manipulation. Recipients without library access\n/// get read-only access to the specific file only.\n///\n/// Returns `ShareError::PermissionDenied` if the file is private and\n/// the recipient isn't a library member. The share is still created\n/// but marked inactive for audit logging.\npub async fn share_file(input: ShareFileInput) -> Result<ShareFileOutput>\n```\n\n**Inline comments:**\n- Delete comments that restate obvious code\n- Explain WHY for decisions, not WHAT the code does\n- Use one sentence when possible\n- Only expand for truly non-obvious consequences\n\n```rust\n// Good: explains WHY\n// Lowercase for case-insensitive search matching.\nlet ext = path.extension().map(|e| e.to_lowercase());\n\n// Bad: restates code\n// Extract file extension and convert to lowercase\nlet ext = path.extension().map(|e| e.to_lowercase());\n\n// Good: explains consequence\n// Keep the arena's UUID so a file keeps its identity when a source adopts it.\nlet uuid = arena.get_entry_uuid(path).unwrap_or_else(Uuid::now_v7);\n\n// Bad: verbose explanation of obvious behavior\n// UUID assignment strategy:\n// 1. First check if the arena has a UUID\n// 2. If not, generate a new one\nlet uuid = arena.get_entry_uuid(path).unwrap_or_else(Uuid::now_v7);\n```\n\n**Error handling comments:**\nExplain strategy and recovery, not just \"log and continue\".\n\n```rust\n// Good: explains recovery\n// Best-effort: continue with remaining moves, stale paths cleaned up on next reindex.\nErr(e) => ctx.log(format!(\"Failed to move: {}\", e)),\n\n// Bad: states the obvious\n// Log error but continue\nErr(e) => ctx.log(format!(\"Failed to move: {}\", e)),\n```\n\n**Platform-specific comments:**\nExplain consequences, not implementation blockers.\n\n```rust\n// Good: explains why and fallback\n#[cfg(windows)]\npub fn get_inode(_metadata: &std::fs::Metadata) -> Option<u64> {\n    // Windows file indices are unstable across reboots; fall back to path-only matching.\n    None\n}\n\n// Bad: over-explains implementation details\n#[cfg(windows)]\npub fn get_inode(_metadata: &std::fs::Metadata) -> Option<u64> {\n    // Windows doesn't have inodes.\n    // The method `file_index()` is unstable (issue #63010).\n    // Returning None is safe as the field is Optional.\n    None\n}\n```\n\n**Never use:**\n- Placeholder comments (\"for now\", \"TODO: extract this later\")\n- Markdown formatting (`**bold**`, `_italic_`) in code comments\n- ASCII diagrams (put those in `/docs/` if needed)\n- Section divider comments (`// ========== Section ==========`)\n- Comments explaining removed code during refactors\n\nTrack future work in GitHub issues, not code comments.\n\n### Formatting\n\nRun `cargo fmt` before committing. Tabs for indentation. No emojis.\n\n## Logging\n\n### Setup\n\nUse `tracing_subscriber` in main or examples:\n\n```rust\nuse tracing_subscriber::EnvFilter;\n\nfn main() {\n    tracing_subscriber::fmt()\n        .with_env_filter(\n            EnvFilter::try_from_default_env()\n                .unwrap_or_else(|_| EnvFilter::new(\"sd_core=info\"))\n        )\n        .init();\n}\n```\n\n## Writing Style\n\nThis applies to all documentation, code comments, and design documents.\n\nUse clear, simple language. Write short, impactful sentences. Use active voice. Focus on practical, actionable information.\n\nAddress the reader directly with \"you\" and \"your\". Support claims with data and examples when possible.\n\nAvoid these constructions:\n\n- Em dashes (use commas or periods)\n- \"Not only this, but also this\"\n- Metaphors and cliches\n- Generalizations\n- Setup language like \"in conclusion\"\n- Unnecessary adjectives and adverbs\n- Emojis, hashtags, markdown formatting in prose\n\nAvoid these words:\ncomprehensive, delve, utilize, harness, realm, tapestry, unlock, revolutionary, groundbreaking, remarkable, pivotal\n\n### Macros\n\nUse `tracing` macros, never `println!`:\n\n```rust\nuse tracing::{info, warn, error, debug};\n\ninfo!(\"Server started on port {}\", port);\ndebug!(file_id = %id, \"Processing file\");\nwarn!(error = %e, \"Retrying operation\");\nerror!(\"Failed to connect to database\");\n```\n\n### Job Logging\n\nUse `ctx.log()` in job implementations for automatic `job_id` tagging:\n\n```rust\nimpl Job for MyJob {\n    async fn run(&mut self, ctx: &JobContext) -> Result<()> {\n        ctx.log().info(\"Job started\");\n        ctx.log().debug!(progress = %self.progress, \"Processing\");\n        Ok(())\n    }\n}\n```\n\n### Log Levels\n\n- `debug`: Detailed flow for troubleshooting\n- `info`: User-relevant events (server start, job completion)\n- `warn`: Recoverable issues (retry, fallback)\n- `error`: Failures requiring attention\n\n### Environment Control\n\nUse `RUST_LOG` environment variable:\n\n```bash\nRUST_LOG=debug cargo run --bin sd-cli\nRUST_LOG=sd_core=trace cargo run\nRUST_LOG=sd_core::ops=debug cargo run\n```\n\n## Testing\n\n### Test Organization\n\n- Unit tests: Colocated in `#[cfg(test)]` modules\n- Integration tests: `tests/` directory at crate root\n\n```rust\n// src/ops/files/share/action.rs\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[tokio::test]\n    async fn test_share_file() {\n        let input = ShareFileInput {\n            file_id: 1,\n            recipient: \"test@example.com\".to_string(),\n        };\n\n        let output = share_file(input).await.unwrap();\n        assert!(!output.share_id.is_empty());\n    }\n}\n```\n\n### Running Tests\n\n```bash\ncargo test                    # All tests\ncargo test test_share_file    # Specific test\ncargo test --lib              # Library tests only\ncargo test -- --nocapture     # Show output\n```\n\n## Task Tracking\n\nRead `/PROJECT_STATUS.md` at the start of a session and update its Immediate\nregister when broad work starts, lands, becomes blocked, or changes owner. Keep\ndesign rationale and phase acceptance criteria in the associated plan. For\nanything still in the entries teardown, also update the status table at the top\nof `/docs/plans/2026-08-20-entries-teardown-execution.md` in the same commit.\n\n`/.tasks/` is frozen. It describes the entry-centric model that the teardown\nremoves, and was last updated in May. Do not add to it and do not run the\nvalidator against new work. The sections below document the retired system for\nhistorical reference only.\n\nSpacedrive uses a file-based task system in `/.tasks/` to track features, epics, and development work. All task files are version-controlled alongside the code.\n\n### When to Create Tasks\n\nCreate tasks for work that:\n\n- Introduces a new feature or capability\n- Refactors a significant system or module\n- Fixes a bug requiring architectural changes\n- Implements a whitepaper specification\n\nDo not create tasks for:\n\n- Routine code formatting or style fixes\n- Trivial bug fixes (single line changes)\n- Documentation updates to existing features\n- Dependency version bumps\n\n### Task Structure\n\nEach task is a Markdown file: `CATEGORY-###-title-slug.md`\n\n```yaml\n---\nid: CORE-042\ntitle: \"Implement file sharing API\"\nstatus: \"In Progress\"\nassignee: \"james\"\npriority: \"High\"\ntags: [\"core\", \"networking\"]\nwhitepaper: \"Section 4.2\" # And/or design_doc: DESIGN_DOC_NAME.md\n---\n\n## Description\nBrief overview of what needs to be done and why.\n\n## Implementation Steps\n- [ ] Create share action in src/ops/files/share\n- [ ] Add database schema for shares table\n- [ ] Implement expiration logic\n\n## Acceptance Criteria\n- Share links work across all platforms\n- Expired shares return 404\n- Tests cover edge cases\n```\n\n### Managing Tasks\n\n```bash\n# List your active tasks\ncargo run -p task-validator -- list --assignee \"yourname\" --status \"In Progress\"\n\n# List high priority tasks\ncargo run -p task-validator -- list --priority \"High\" --sort-by id\n\n# Validate before committing (automatic via git hook)\ncargo run -p task-validator -- validate\n```\n\n### Task Lifecycle\n\n1. Create task file in `/.tasks/` with `status: \"To Do\"`\n2. Update status to `\"In Progress\"` when you start work\n3. Complete implementation and tests\n4. Update status to `\"Done\"` and commit\n\nFull documentation: `/docs/core/task-tracking.mdx`\n\n## Debugging\n\n### Log Files\n\nJob logs live in the `job_logs` directory in the data folder root.\n\n### Daemon Restart\n\nAfter rebuilding, restart the daemon to use the latest code:\n\n```bash\ncargo build\ncargo run --bin sd-cli -- restart\n```\n\n### Verbose Logging\n\n```bash\nRUST_LOG=debug cargo run --bin sd-daemon\nRUST_LOG=sd_core::jobs=trace cargo run\n```\n\n## Documentation Locations\n\n- Core architecture: `/docs/core/`\n- Design docs and RFCs: `/docs/core/design/`\n- Application docs: `/docs/`\n- Task tracking: `/docs/core/task-tracking.mdx`\n","extensions/test-extension/manifest.json":"{\n\t\"id\": \"test-extension\",\n\t\"name\": \"Test Extension\",\n\t\"version\": \"0.1.0\",\n\t\"description\": \"Minimal extension demonstrating beautiful SDK API\",\n\t\"author\": \"Spacedrive Team\",\n\t\"homepage\": \"https://spacedrive.com\",\n\t\"wasm_file\": \"test_extension.wasm\",\n\t\"permissions\": {\n\t\t\"read_records\": {},\n\t\t\"write_sidecars\": [\n\t\t\t\"digest\"\n\t\t],\n\t\t\"write_tags\": true,\n\t\t\"write_custom_fields\": [\n\t\t\t\"test\"\n\t\t],\n\t\t\"dispatch_jobs\": true\n\t},\n\t\"kinds\": [\n\t\t{\n\t\t\t\"name\": \"fake\",\n\t\t\t\"display_name\": \"Fake file\",\n\t\t\t\"parent\": \"text\",\n\t\t\t\"extensions\": [\n\t\t\t\t\"fake\"\n\t\t\t],\n\t\t\t\"magic\": [\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"46 41 4B 45\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"preview\": {\n\t\t\t\t\"viewer\": \"fake_viewer\"\n\t\t\t}\n\t\t}\n\t]\n}\n","extensions/test-extension/README.md":"# Test Extension\n\n**The canonical example of Spacedrive extension development.**\n\nThis extension demonstrates the beautiful, macro-powered API that makes building extensions delightful.\n\n## Features\n\n**Zero Boilerplate** - Macros generate all FFI code\n**Type-Safe** - Full Rust type system\n**No Unsafe** - Safe by default\n**Clean API** - Just write business logic\n\n## Code\n\n**Complete extension in 76 lines:**\n\n```rust\nuse spacedrive_sdk::prelude::*;\nuse spacedrive_sdk::{extension, job};\n\n// Extension definition\n#[extension(\n    id = \"test-extension\",\n    name = \"Test Extension\",\n    version = \"0.1.0\"\n)]\nstruct TestExtension;\n\n// Job state\n#[derive(Serialize, Deserialize, Default)]\npub struct CounterState {\n    pub current: u32,\n    pub target: u32,\n}\n\n// Job implementation - THAT'S IT!\n#[job]\nfn test_counter(ctx: &JobContext, state: &mut CounterState) -> Result<()> {\n    while state.current < state.target {\n        ctx.check_interrupt()?;\n        state.current += 1;\n        ctx.report_progress(\n            state.current as f32 / state.target as f32,\n            &format!(\"Counted {}/{}\", state.current, state.target)\n        );\n        if state.current % 10 == 0 {\n            ctx.checkpoint(state)?;\n        }\n    }\n    Ok(())\n}\n```\n\n## What the Macros Generate\n\nThe `#[extension]` and `#[job]` macros automatically generate:\n\n- `plugin_init()` - Extension initialization\n- `plugin_cleanup()` - Extension cleanup\n- `execute_test_counter()` - FFI export with full state management\n- All pointer marshalling\n- Serialization/deserialization\n- Error handling\n- Progress tracking\n- Checkpoint management\n\n**~120 lines of boilerplate you don't write!**\n\n## Building\n\n```bash\ncargo build --target wasm32-unknown-unknown --release\n```\n\nOutput: `target/wasm32-unknown-unknown/release/test_extension.wasm` (~254KB)\n\n## Capabilities Demonstrated\n\n### Job System\n- Progress reporting (0-100%)\n- Checkpointing (resume after crash)\n- Interruption handling (pause/cancel)\n- Metrics tracking (items processed)\n- State persistence\n\n### API Ergonomics\n- Clean function signatures\n- `?` operator for error handling\n- No FFI knowledge required\n- No unsafe code\n- Just write Rust!\n\n## Testing\n\nOnce Core is running:\n```rust\n// Load extension\nplugin_manager.load_plugin(\"test-extension\").await?;\n\n// Dispatch job\nlet job_id = job_manager.dispatch_by_name(\n    \"test-extension:test_counter\",\n    json!({ \"target\": 100 })\n).await?;\n\n// Watch progress in logs:\n// INFO Counted 10/100 (10% complete)\n// INFO Counted 20/100 (20% complete)\n// ...\n// INFO ✓ Completed! Processed 100 items\n```\n\n## Comparison\n\n| Metric | Manual FFI | With Macros | Improvement |\n|--------|-----------|-------------|-------------|\n| Lines of Code | 181 | 76 | 58% less |\n| Unsafe Blocks | 4 | 0 | 100% safer |\n| Boilerplate | 120 lines | 10 lines | 92% less |\n| WASM Size | 252KB | 254KB | Same |\n| Readability | 5/10 | 10/10 | Much better |\n| Dev Time | 2-3 hours | 15 minutes | 10x faster |\n\n---\n\n**This is what all Spacedrive extensions should look like going forward!** \n","packages/ts-client/README.md":"# @spacedrive/client\n\nType-safe TypeScript client for the Spacedrive daemon, automatically generated from Rust core types using Specta.\n\n## Features\n\n- **Fully Type-Safe** - All types generated directly from Rust core\n- **Real-time Events** - Subscribe to daemon events with full type safety\n- **Unix Domain Sockets** - Direct communication with daemon\n- **Auto-Generated** - Types stay in sync with Rust core automatically\n- **Rich Documentation** - JSDoc comments from Rust code\n\n## Installation\n\n```bash\nnpm install @spacedrive/client\n```\n\n## Quick Start\n\n```typescript\nimport { SpacedriveClient } from '@spacedrive/client';\n\nconst client = new SpacedriveClient();\n\n// Test connection\nawait client.ping();\n\n// Get libraries (fully typed)\nconst libraries = await client.getLibraries();\nconsole.log('Libraries:', libraries);\n\n// Get jobs with status filtering\nconst jobs = await client.getJobs('running');\nconsole.log('Running jobs:', jobs);\n\n// Create a new library\nconst newLibrary = await client.createLibrary('My Photos', '/Users/me/Photos');\nconsole.log('Created library:', newLibrary);\n```\n\n## Event Subscription\n\n```typescript\n// Subscribe to specific event types\nawait client.subscribe(['JobStarted', 'JobProgress', 'JobCompleted']);\n\n// Listen for events (fully typed)\nclient.on('event', (event) => {\n  switch (event) {\n    case 'CoreStarted':\n      console.log('Daemon started');\n      break;\n    default:\n      if ('JobStarted' in event) {\n        console.log(`Job started: ${event.JobStarted.job_type} (${event.JobStarted.job_id})`);\n      } else if ('JobProgress' in event) {\n        console.log(`Job progress: ${event.JobProgress.progress * 100}%`);\n      } else if ('JobCompleted' in event) {\n        console.log(`Job completed: ${event.JobCompleted.job_type}`);\n        console.log('Output:', event.JobCompleted.output);\n      }\n      break;\n  }\n});\n\nclient.on('error', (error) => {\n  console.error('Client error:', error);\n});\n\nclient.on('disconnected', () => {\n  console.log('Disconnected from daemon');\n});\n```\n\n## Type Generation\n\nTypes are automatically generated from the Rust core using Specta. To regenerate types:\n\n```bash\nnpm run generate-types\n```\n\n## API Reference\n\n### Core Methods\n\n- `ping()` - Test daemon connectivity\n- `executeQuery<Q, R>(query: Q, method: string): Promise<R>` - Execute a query operation\n- `executeAction<A, R>(action: A, method: string): Promise<R>` - Execute an action operation\n- `subscribe(eventTypes?: string[]): Promise<void>` - Subscribe to daemon events\n\n### Convenience Methods\n\n- `getLibraries(includeStats?: boolean): Promise<LibraryInfo[]>` - Get all libraries\n- `createLibrary(name: string, path?: string): Promise<LibraryCreateOutput>` - Create a new library\n- `getJobs(status?: JobStatus): Promise<JobListOutput>` - Get job list with optional filtering\n\n### Event Types\n\nAll event types are fully typed TypeScript unions:\n\n- `Event` - Main event union type\n- `JobOutput` - Job completion output (adjacently tagged)\n- `JobStatus` - Job status enum (`\"queued\" | \"running\" | \"completed\" | ...`)\n- `FileOperation` - File operation types\n- `Progress` - Progress information with multiple formats\n\n## Architecture\n\nThis client uses the same architecture as the Swift client:\n\n1. **JSON API Layer** - Communicates via JSON instead of bincode for external clients\n2. **Unix Domain Sockets** - Direct, efficient communication with daemon\n3. **Type Generation** - Rust types → TypeScript via Specta\n4. **Event Streaming** - Real-time event subscription with line-delimited JSON\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Build the client\nnpm run build\n\n# Run tests\nnpm test\n\n# Watch mode for development\nnpm run dev\n```\n","packages/ts-client/package.json":"{\n\t\"name\": \"@sd/ts-client\",\n\t\"version\": \"0.1.0\",\n\t\"description\": \"Type-safe TypeScript client for Spacedrive daemon\",\n\t\"main\": \"dist/index.js\",\n\t\"types\": \"dist/index.d.ts\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./src/index.ts\",\n\t\t\t\"default\": \"./src/index.ts\"\n\t\t},\n\t\t\"./generated/types\": {\n\t\t\t\"types\": \"./src/generated/types.ts\",\n\t\t\t\"default\": \"./src/generated/types.ts\"\n\t\t},\n\t\t\"./src/generated/types\": {\n\t\t\t\"types\": \"./src/generated/types.ts\",\n\t\t\t\"default\": \"./src/generated/types.ts\"\n\t\t},\n\t\t\"./hooks\": {\n\t\t\t\"types\": \"./src/hooks/index.ts\",\n\t\t\t\"default\": \"./src/hooks/index.ts\"\n\t\t},\n\t\t\"./hooks/useClient\": {\n\t\t\t\"types\": \"./src/hooks/useClient.tsx\",\n\t\t\t\"default\": \"./src/hooks/useClient.tsx\"\n\t\t},\n\t\t\"./hooks/useNormalizedQuery\": {\n\t\t\t\"types\": \"./src/hooks/useNormalizedQuery.ts\",\n\t\t\t\"default\": \"./src/hooks/useNormalizedQuery.ts\"\n\t\t},\n\t\t\"./src/hooks/useClient\": {\n\t\t\t\"types\": \"./src/hooks/useClient.tsx\",\n\t\t\t\"default\": \"./src/hooks/useClient.tsx\"\n\t\t},\n\t\t\"./src/hooks/useNormalizedQuery\": {\n\t\t\t\"types\": \"./src/hooks/useNormalizedQuery.ts\",\n\t\t\t\"default\": \"./src/hooks/useNormalizedQuery.ts\"\n\t\t}\n\t},\n\t\"scripts\": {\n\t\t\"build\": \"tsc\",\n\t\t\"dev\": \"tsc --watch\",\n\t\t\"test\": \"jest\",\n\t\t\"generate-types\": \"cargo run --bin generate_typescript_types --manifest-path ../../core/Cargo.toml\"\n\t},\n\t\"keywords\": [\n\t\t\"spacedrive\",\n\t\t\"client\",\n\t\t\"typescript\",\n\t\t\"daemon\"\n\t],\n\t\"author\": \"Spacedrive\",\n\t\"license\": \"Apache-2.0\",\n\t\"peerDependencies\": {\n\t\t\"@tanstack/react-query\": \"^5.0.0\",\n\t\t\"react\": \"^18.0.0 || ^19.0.0\"\n\t},\n\t\"peerDependenciesMeta\": {\n\t\t\"@tanstack/react-query\": {\n\t\t\t\"optional\": true\n\t\t},\n\t\t\"react\": {\n\t\t\t\"optional\": true\n\t\t}\n\t},\n\t\"devDependencies\": {\n\t\t\"@happy-dom/global-registrator\": \"^15.11.0\",\n\t\t\"@testing-library/jest-dom\": \"^6.0.0\",\n\t\t\"@testing-library/react\": \"^16.0.0\",\n\t\t\"@types/jest\": \"^29.0.0\",\n\t\t\"@types/node\": \"^20.0.0\",\n\t\t\"happy-dom\": \"^15.11.0\",\n\t\t\"jest\": \"^29.0.0\",\n\t\t\"jest-environment-jsdom\": \"^29.0.0\",\n\t\t\"jsdom\": \"^27.2.0\",\n\t\t\"ts-jest\": \"^29.0.0\",\n\t\t\"typescript\": \"^5.0.0\"\n\t},\n\t\"dependencies\": {\n\t\t\"@types/ws\": \"^8.0.0\",\n\t\t\"@sd/assets\": \"workspace:*\",\n\t\t\"ws\": \"^8.0.0\",\n\t\t\"zustand\": \"^5.0.8\",\n\t\t\"ts-deepmerge\": \"^7.0.1\",\n\t\t\"tiny-invariant\": \"^1.3.3\",\n\t\t\"valibot\": \"^1.0.0\",\n\t\t\"type-fest\": \"^4.30.0\"\n\t},\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"src/**/*\"\n\t]\n}\n","packages/ts-client/tsconfig.json":"{\n\t\"compilerOptions\": {\n\t\t\"target\": \"ES2020\",\n\t\t\"module\": \"commonjs\",\n\t\t\"lib\": [\"ES2020\"],\n\t\t\"jsx\": \"react-jsx\",\n\t\t\"outDir\": \"./dist\",\n\t\t\"rootDir\": \"./src\",\n\t\t\"strict\": true,\n\t\t\"esModuleInterop\": true,\n\t\t\"skipLibCheck\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"declaration\": true,\n\t\t\"declarationMap\": true,\n\t\t\"sourceMap\": true,\n\t\t\"moduleResolution\": \"node\",\n\t\t\"allowSyntheticDefaultImports\": true,\n\t\t\"resolveJsonModule\": true,\n\t\t\"types\": [\"jest\", \"node\"]\n\t},\n\t\"include\": [\"src/**/*\"],\n\t\"exclude\": [\"node_modules\", \"dist\", \"**/*.test.ts\", \"**/*.test.tsx\", \"**/__tests__/**\"]\n}\n","packages/ts-client/USAGE.md":"# Ts-client Skill\n\nPath: `packages/ts-client/USAGE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/ts-client/USAGE.md)","packages/swift-client/README.md":"# swift-client Documentation\n\nPath: `packages/swift-client/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/swift-client/README.md)","packages/interface/CLAUDE.md":"# Interface Skill\n\nPath: `packages/interface/CLAUDE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/CLAUDE.md)","packages/interface/package.json":"# Interface Skill\n\nPath: `packages/interface/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/package.json)","packages/interface/SHARED-UI-STRATEGY.md":"# Interface Skill\n\nPath: `packages/interface/SHARED-UI-STRATEGY.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/SHARED-UI-STRATEGY.md)","packages/interface/tsconfig.json":"# Interface Skill\n\nPath: `packages/interface/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/tsconfig.json)","packages/config/app.tsconfig.json":"# Config Skill\n\nPath: `packages/config/app.tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/app.tsconfig.json)","packages/config/base.tsconfig.json":"# Config Skill\n\nPath: `packages/config/base.tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/base.tsconfig.json)","packages/config/package.json":"# Config Skill\n\nPath: `packages/config/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/package.json)","packages/config/tsconfig.json":"# Config Skill\n\nPath: `packages/config/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/tsconfig.json)","packages/assets/package.json":"# Assets Skill\n\nPath: `packages/assets/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/package.json)","packages/assets/README.md":"# assets Documentation\n\nPath: `packages/assets/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/README.md)","packages/interface/src/contributors.json":"# Src Skill\n\nPath: `packages/interface/src/contributors.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/contributors.json)","packages/assets/lottie/loading-pulse.json":"# Lottie Skill\n\nPath: `packages/assets/lottie/loading-pulse.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/lottie/loading-pulse.json)","packages/assets/sounds/README.md":"# sounds Documentation\n\nPath: `packages/assets/sounds/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/sounds/README.md)","packages/ts-client/tests/integration/README.md":"# integration Documentation\n\nPath: `packages/ts-client/tests/integration/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/ts-client/tests/integration/README.md)","packages/interface/src/Spacebot/VISION.md":"# Spacebot Skill\n\nPath: `packages/interface/src/Spacebot/VISION.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/Spacebot/VISION.md)","packages/assets/svgs/ext/icons.json":"# Ext Skill\n\nPath: `packages/assets/svgs/ext/icons.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/svgs/ext/icons.json)","packages/interface/src/components/modals/FileOperationModal.README.md":"# Modals Skill\n\nPath: `packages/interface/src/components/modals/FileOperationModal.README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/components/modals/FileOperationModal.README.md)","packages/interface/src/components/Inspector/variants/README.md":"# variants Documentation\n\nPath: `packages/interface/src/components/Inspector/variants/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/components/Inspector/variants/README.md)"},"files":{"core/AGENTS.md":"# AGENTS.md - Spacedrive Core v2\n\n## Build/Test Commands\n\n- `cargo build` - Build the project\n- `cargo test` - Run all tests\n- `cargo test <test_name>` - Run specific test (e.g., `cargo test library_test`)\n- `cargo clippy` - Lint code\n- `cargo fmt` - Format code\n- `cargo run --bin sd-cli -- <command>` - Run CLI (note: binary is `sd-cli`, not `spacedrive`)\n\n## Code Style\n\n- **Imports**: Group std, external crates, then local modules with blank lines between\n- **Formatting**: Use `cargo fmt` - tabs for indentation, snake_case for variables/functions. DO NOT use emojis at all.\n- **Types**: Explicit types preferred, use `Result<T, E>` for error handling with `thiserror`\n- **Naming**: snake_case for functions/variables, PascalCase for types, SCREAMING_SNAKE_CASE for constants\n- **Error Handling**: Use `Result` types, `thiserror` for custom errors, `anyhow` for application errors\n- **Async**: Use `async/await`, prefer `tokio` primitives, avoid blocking operations\n- **Resumable Jobs**: For long-running jobs that need to be resumable, store the job's state within the job's struct itself. Use `#[serde(skip)]` for fields that should not be persisted. For example, in a file copy job, the list of already copied files can be stored to allow the job to resume from where it left off.\n- **Documentation**: Use `//!` for module docs, `///` for public items, include examples\n- **Architecture**: Follow a Command Query Responsibility Segregation (CQRS) and Domain-Driven Design (DDD) pattern.\n  - **Domain**: Core data structures and business logic are located in `src/domain/`. These are the \"nouns\" of your system.\n  - **Operations**: State-changing commands (actions) and data-retrieving queries are located in `src/ops/`. These are the \"verbs\" of your system.\n  - **Actions**: Operations that modify the state of the application. They should be self-contained and transactional.\n  - **Queries**: Operations that retrieve data without modifying state. They should be efficient and optimized for reading.\n- **Feature Modules**: Each new feature should be implemented in its own module within the `src/ops/` directory. For example, a new \"share\" feature would live in `src/ops/files/share`. Each feature module should contain the following files where applicable:\n  - `action.rs`: The main logic for the state-changing operation.\n  - `input.rs`: Data structures for the action's input.\n  - `output.rs`: Data structures for the action's output.\n  - `job.rs`: If the action is long-running, the job implementation.\n- **Database**: Use SeaORM entities, async queries, proper error propagation\n- **Comments**: Minimal inline comments, focus on why not what, no TODO comments in production code\n\n## Daemon Architecture\n\nSpacedrive uses a **daemon-client architecture** where a single daemon process manages the core functionality and multiple client applications (CLI, GraphQL server, desktop app) connect to it via Unix domain sockets.\n\n> **For detailed daemon architecture documentation, see [/docs/core/daemon.md](/docs/core/daemon.md)**\n\n### **The `Wire` Trait**\n\nAll actions and queries must implement the `Wire` trait to enable type-safe client-daemon communication:\n\n```rust\npub trait Wire {\n    const METHOD: &'static str;\n}\n```\n\n### **Registration Macros**\n\nInstead of manually implementing `Wire`, use these registration macros that automatically:\n\n1. Implement the `Wire` trait with the correct method string\n2. Register the operation in the global registry using the `inventory` crate\n\n**For Queries:**\n\n```rust\ncrate::register_query!(NetworkStatusQuery, \"network.status\");\n// Generates method: \"query:network.status\"\n```\n\n**For Library Actions:**\n\n```rust\ncrate::register_library_action!(FileCopyAction, \"files.copy\");\n// Generates method: \"action:files.copy.input\"\n```\n\n**For Core Actions:**\n\n```rust\ncrate::register_core_action!(LibraryCreateAction, \"libraries.create\");\n// Generates method: \"action:libraries.create.input\"\n```\n\n### **Registry System**\n\n- **Location**: `core/src/ops/registry.rs`\n- **Mechanism**: Uses the `inventory` crate for compile-time registration\n- **Global Maps**: `QUERIES` and `ACTIONS` hashmaps populated at startup\n- **Handler Functions**: Generic handlers that decode payloads, execute operations, and encode results\n\n## Logging Standards\n\n- **Setup**: Use `tracing_subscriber::fmt()` with env filter for structured logging\n- **Macros**: Use `info!`, `warn!`, `error!`, `debug!` from `tracing` crate, not `println!`\n- **Job Context**: Use `ctx.log()` in jobs for job-specific logging with automatic job_id tagging\n- **Structured**: Include relevant context fields: `debug!(job_id = %self.id, \"message\")`\n- **Levels**: debug for detailed flow, info for user-relevant events, warn for recoverable issues, error for failures\n- **Format**: `tracing_subscriber::fmt().with_env_filter(env_filter).init()` in main/examples\n- **Environment**: Respect `RUST_LOG` env var, fallback to module-specific filters like `sd_core=info`\n\n## Documentation\n\n- **Core level docs**: Live in `/docs/core` - comprehensive architecture and implementation guides\n- **Core design docs**: Live in `/docs/core/design` - planning documents, RFCs, and design decisions\n- **Application level docs**: Live in `/docs`\n- **Code docs**: Use `///` for public APIs, `//!` for module overviews, include examples\n\n## Debug Instructions\n\n- You can view the logs of a job in the job_logs directory in the root of the data folder\n- When testing the CLI, after compiling you must first use the `restart` command to ensure the Spacedrive daemon is using the latest build.\n","extensions/photos/manifest.json":"{\n\t\"id\": \"com.spacedrive.photos\",\n\t\"name\": \"Photos\",\n\t\"version\": \"1.0.0\",\n\t\"description\": \"Advanced photo management with AI-powered face recognition, place identification, and intelligent organization\",\n\t\"author\": \"Spacedrive\",\n\t\"wasm_file\": \"photos.wasm\",\n\t\"min_core_version\": \"2.0.0\",\n\t\"required_features\": [\n\t\t\"exif_extraction\",\n\t\t\"ai_models\",\n\t\t\"semantic_search\"\n\t],\n\t\"permissions\": {\n\t\t\"read_records\": {\n\t\t\t\"glob\": \"**/*.{jpg,jpeg,png,heic,heif,raw,cr2,nef,dng,webp}\"\n\t\t},\n\t\t\"read_sidecars\": [\n\t\t\t\"exif\",\n\t\t\t\"thumbnail\"\n\t\t],\n\t\t\"write_sidecars\": [\n\t\t\t\"faces\",\n\t\t\t\"places\",\n\t\t\t\"scene\",\n\t\t\t\"aesthetics\"\n\t\t],\n\t\t\"write_tags\": true,\n\t\t\"write_custom_fields\": [\n\t\t\t\"photos\"\n\t\t],\n\t\t\"dispatch_jobs\": true,\n\t\t\"use_models\": [\n\t\t\t{\n\t\t\t\t\"category\": \"face_detection\",\n\t\t\t\t\"preference\": \"local\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"category\": \"scene_classification\",\n\t\t\t\t\"preference\": \"local\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"category\": \"llm\",\n\t\t\t\t\"preference\": \"local\"\n\t\t\t}\n\t\t]\n\t},\n\t\"models\": [\n\t\t{\n\t\t\t\"name\": \"photos_face_detection_v1\",\n\t\t\t\"category\": \"face_detection\",\n\t\t\t\"source\": {\n\t\t\t\t\"type\": \"download\",\n\t\t\t\t\"url\": \"https://models.spacedrive.com/photos/face_detection_v1.onnx\",\n\t\t\t\t\"sha256\": \"placeholder_hash\",\n\t\t\t\t\"size_mb\": 12\n\t\t\t},\n\t\t\t\"description\": \"RetinaFace model for face detection\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"photos_scene_v1\",\n\t\t\t\"category\": \"scene_classification\",\n\t\t\t\"source\": {\n\t\t\t\t\"type\": \"download\",\n\t\t\t\t\"url\": \"https://models.spacedrive.com/photos/scene_resnet50.onnx\",\n\t\t\t\t\"sha256\": \"placeholder_hash\",\n\t\t\t\t\"size_mb\": 95\n\t\t\t},\n\t\t\t\"description\": \"ResNet50 trained on Places365 dataset\"\n\t\t}\n\t],\n\t\"kinds\": [\n\t\t{\n\t\t\t\"name\": \"raw\",\n\t\t\t\"display_name\": \"RAW photo\",\n\t\t\t\"parent\": \"image\",\n\t\t\t\"extensions\": [\n\t\t\t\t\"cr2\",\n\t\t\t\t\"cr3\",\n\t\t\t\t\"nef\",\n\t\t\t\t\"arw\",\n\t\t\t\t\"dng\",\n\t\t\t\t\"raf\",\n\t\t\t\t\"orf\",\n\t\t\t\t\"rw2\",\n\t\t\t\t\"pef\",\n\t\t\t\t\"dcr\",\n\t\t\t\t\"srw\"\n\t\t\t],\n\t\t\t\"mime_types\": [\n\t\t\t\t\"image/x-canon-cr2\",\n\t\t\t\t\"image/x-canon-cr3\",\n\t\t\t\t\"image/x-nikon-nef\",\n\t\t\t\t\"image/x-sony-arw\",\n\t\t\t\t\"image/x-adobe-dng\",\n\t\t\t\t\"image/x-fuji-raf\",\n\t\t\t\t\"image/x-olympus-orf\",\n\t\t\t\t\"image/x-panasonic-rw2\",\n\t\t\t\t\"image/x-pentax-pef\",\n\t\t\t\t\"image/x-kodak-dcr\",\n\t\t\t\t\"image/x-samsung-srw\"\n\t\t\t],\n\t\t\t\"magic\": [\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"49 49 2A 00\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"4D 4D 00 2A\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"46 55 4A 49 46 49 4C 4D\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"49 49 52 4F\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"49 49 55 00\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"preview\": {\n\t\t\t\t\"renderer\": \"image\"\n\t\t\t}\n\t\t}\n\t]\n}\n","extensions/photos/ui_manifest.json":"{\n\t\"sidebar\": {\n\t\t\"section\": \"Photos\",\n\t\t\"icon\": \"assets/photos_icon.svg\",\n\t\t\"order\": 10,\n\t\t\"views\": [\n\t\t\t{\n\t\t\t\t\"id\": \"library\",\n\t\t\t\t\"title\": \"Library\",\n\t\t\t\t\"component\": \"photo_grid\",\n\t\t\t\t\"query\": \"list_all_photos\",\n\t\t\t\t\"default\": true\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"albums\",\n\t\t\t\t\"title\": \"Albums\",\n\t\t\t\t\"component\": \"album_grid\",\n\t\t\t\t\"query\": \"list_albums\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"people\",\n\t\t\t\t\"title\": \"People\",\n\t\t\t\t\"component\": \"person_cluster_grid\",\n\t\t\t\t\"query\": \"list_people\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"places\",\n\t\t\t\t\"title\": \"Places\",\n\t\t\t\t\"component\": \"map_view\",\n\t\t\t\t\"query\": \"list_places\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"moments\",\n\t\t\t\t\"title\": \"Moments\",\n\t\t\t\t\"component\": \"moment_timeline\",\n\t\t\t\t\"query\": \"list_moments\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"favorites\",\n\t\t\t\t\"title\": \"Favorites\",\n\t\t\t\t\"component\": \"photo_grid\",\n\t\t\t\t\"query\": \"tags LIKE '#favorite'\",\n\t\t\t\t\"icon\": \"heart\"\n\t\t\t}\n\t\t]\n\t},\n\t\"context_menu\": [\n\t\t{\n\t\t\t\"id\": \"add_to_album\",\n\t\t\t\"label\": \"Add to Album...\",\n\t\t\t\"icon\": \"plus_circle\",\n\t\t\t\"action\": \"add_to_album\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t],\n\t\t\t\"keyboard_shortcut\": \"cmd+shift+a\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"identify_person\",\n\t\t\t\"label\": \"This is...\",\n\t\t\t\"icon\": \"person\",\n\t\t\t\"action\": \"identify_person\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t],\n\t\t\t\"requires\": \"has_detected_faces\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"set_as_cover\",\n\t\t\t\"label\": \"Set as Album Cover\",\n\t\t\t\"icon\": \"star\",\n\t\t\t\"action\": \"set_album_cover\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t],\n\t\t\t\"context\": \"album_view\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"hide_photo\",\n\t\t\t\"label\": \"Hide\",\n\t\t\t\"icon\": \"eye_slash\",\n\t\t\t\"action\": \"hide_photo\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t]\n\t\t}\n\t],\n\t\"toolbar_actions\": [\n\t\t{\n\t\t\t\"id\": \"analyze_location\",\n\t\t\t\"label\": \"Analyze for Faces\",\n\t\t\t\"icon\": \"sparkles\",\n\t\t\t\"action\": \"analyze_photos_batch\",\n\t\t\t\"context\": \"location_view\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"identify_places\",\n\t\t\t\"label\": \"Identify Places\",\n\t\t\t\"icon\": \"map_pin\",\n\t\t\t\"action\": \"identify_places_in_location\",\n\t\t\t\"context\": \"location_view\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"create_moment\",\n\t\t\t\"label\": \"Create Moment\",\n\t\t\t\"icon\": \"calendar\",\n\t\t\t\"action\": \"create_moments_from_selection\"\n\t\t}\n\t],\n\t\"file_viewers\": [\n\t\t{\n\t\t\t\"id\": \"photo_viewer\",\n\t\t\t\"bundle\": \"ui/photo_viewer.js\",\n\t\t\t\"mime_types\": [\n\t\t\t\t\"image/jpeg\",\n\t\t\t\t\"image/png\",\n\t\t\t\t\"image/heic\",\n\t\t\t\t\"image/webp\"\n\t\t\t],\n\t\t\t\"component\": \"photo_viewer\",\n\t\t\t\"features\": {\n\t\t\t\t\"slideshow\": true,\n\t\t\t\t\"metadata_panel\": true,\n\t\t\t\t\"face_indicators\": true,\n\t\t\t\t\"related_photos\": true\n\t\t\t}\n\t\t}\n\t],\n\t\"search_filters\": [\n\t\t{\n\t\t\t\"id\": \"has_people\",\n\t\t\t\"label\": \"Has People\",\n\t\t\t\"type\": \"boolean\",\n\t\t\t\"query\": \"tags LIKE '#person:%'\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"location\",\n\t\t\t\"label\": \"Location\",\n\t\t\t\"type\": \"place_picker\",\n\t\t\t\"query_template\": \"tags LIKE '#place:{value}'\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"date_range\",\n\t\t\t\"label\": \"Date\",\n\t\t\t\"type\": \"date_range\",\n\t\t\t\"query_template\": \"exif.taken_at BETWEEN '{start}' AND '{end}'\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"camera\",\n\t\t\t\"label\": \"Camera\",\n\t\t\t\"type\": \"select\",\n\t\t\t\"options_query\": \"SELECT DISTINCT exif.camera_model FROM photos\",\n\t\t\t\"query_template\": \"exif.camera_model = '{value}'\"\n\t\t}\n\t]\n}\n","extensions/README.md":"# Spacedrive Official Extensions\n\nThis directory contains the extension SDK and official extensions for Spacedrive.\n\n## Structure\n\n```\nextensions/\n├── spacedrive-sdk/          # Core SDK library\n├── spacedrive-sdk-macros/   # Proc macros for beautiful API\n├── test-extension/          # Example extension with beautiful API\n└── finance/                 # (Future) First revenue-generating extension\n```\n\n## Quick Start\n\n### 1. Install WASM Target\n\n```bash\nrustup target add wasm32-unknown-unknown\n```\n\n### 2. Create Extension\n\n```bash\ncargo new --lib my-extension\ncd my-extension\n```\n\n**Cargo.toml:**\n```toml\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacedrive-sdk = { path = \"../spacedrive-sdk\" }\nserde = { version = \"1.0\", features = [\"derive\"] }\n```\n\n**src/lib.rs:**\n```rust\nuse spacedrive_sdk::prelude::*;\nuse spacedrive_sdk::{extension, job};\n\n#[extension(\n    id = \"my-extension\",\n    name = \"My Extension\",\n    version = \"0.1.0\"\n)]\nstruct MyExtension;\n\n#[derive(Serialize, Deserialize, Default)]\npub struct MyJobState {\n    pub counter: u32,\n}\n\n#[job]\nfn my_job(ctx: &JobContext, state: &mut MyJobState) -> Result<()> {\n    ctx.log(\"Job starting!\");\n\n    state.counter += 1;\n    ctx.report_progress(1.0, \"Done!\");\n\n    Ok(())\n}\n```\n\n### 3. Build\n\n```bash\ncargo build --target wasm32-unknown-unknown --release\ncp target/wasm32-unknown-unknown/release/my_extension.wasm .\n```\n\n### 4. Create manifest.json\n\n```json\n{\n  \"id\": \"my-extension\",\n  \"name\": \"My Extension\",\n  \"version\": \"0.1.0\",\n  \"wasm_file\": \"my_extension.wasm\",\n  \"permissions\": {\n    \"read_records\": { \"glob\": \"**/*.{jpg,png}\" },\n    \"read_sidecars\": [\"exif\"],\n    \"write_sidecars\": [\"digest\"],\n    \"use_models\": [{ \"category\": \"face_detection\", \"preference\": \"local\" }]\n  }\n}\n```\n\nThe host rejects a manifest with a field it does not know, so a typo in a\ngrant fails at load. `read_records` is the only way to see files; its optional\n`glob` is honored for its trailing extension list only (`*.jpg` or\n`**/*.{jpg,png}`); the directory part is ignored and a glob of another shape\nis refused at load. `write_sidecars` names the kinds\nthe extension may write; `use_models` the inference categories it may ask for.\n`write_tags`, `write_custom_fields` and `dispatch_jobs` are declared but have\nno host side yet.\n\n## The Beautiful API\n\n### Before Macros (Manual FFI):\n```rust\n#[no_mangle]\npub extern \"C\" fn execute_my_job(\n    ctx_ptr: u32, ctx_len: u32,\n    state_ptr: u32, state_len: u32\n) -> i32 {\n    let ctx_json = unsafe { /* 30 lines of pointer manipulation */ };\n    let mut state = /* 40 lines of deserialization */;\n    // ... business logic buried in boilerplate ...\n}\n```\n**180+ lines, lots of unsafe**\n\n### After Macros (Beautiful):\n```rust\n#[job]\nfn my_job(ctx: &JobContext, state: &mut MyJobState) -> Result<()> {\n    // Just write business logic!\n    ctx.log(\"Working...\");\n    state.counter += 1;\n    Ok(())\n}\n```\n**60-80 lines, zero unsafe, pure logic**\n\n## API Reference\n\n### Extension Container\n\n```rust\n#[extension(\n    id = \"finance\",\n    name = \"Spacedrive Finance\",\n    version = \"0.1.0\"\n)]\nstruct Finance;\n```\n\nGenerates:\n- `plugin_init()` export\n- `plugin_cleanup()` export\n- Metadata for manifest generation\n\n### Job Definition\n\n```rust\n#[job]\nfn email_scan(ctx: &JobContext, state: &mut EmailScanState) -> Result<()> {\n    // Progress reporting\n    ctx.report_progress(0.5, \"Half done\");\n\n    // Checkpointing\n    ctx.checkpoint(state)?;\n\n    // Interruption handling\n    if ctx.check_interrupt() {\n        return Err(Error::OperationFailed(\"Interrupted\".into()));\n    }\n\n    // Metrics\n    ctx.increment_items(1);\n    ctx.increment_bytes(1000);\n\n    // Warnings\n    ctx.add_warning(\"Non-fatal issue\");\n\n    // Full SDK access\n    let record = ctx.vdfs().get_record(uuid).await?;\n    let bytes = record.read().await?;\n\n    Ok(())\n}\n```\n\n### VDFS Operations\n\n```rust\n// Records of the library's sources, filtered by extension (read_records grant)\nlet photos = ctx.vdfs()\n    .query_records()\n    .with_extensions([\"jpg\", \"png\"])\n    .collect()\n    .await?;\n\n// One record and its bytes\nlet record = ctx.vdfs().get_record(uuid).await?;\nlet bytes = record.read().await?;\n\n// Sidecars are one JSON document per kind, keyed by content uuid\nlet content_uuid = record.content_uuid().expect(\"hashed\");\nif !ctx.sidecar_exists(content_uuid, \"digest\")? {\n    ctx.save_sidecar(content_uuid, \"digest\", &digest).await?;\n}\nlet digest: Digest = ctx.read_sidecar(content_uuid, \"digest\").await?;\n\n// Models declared with #[model] live in the extension's own store\nctx.vdfs().create_model(entry).await?;\nlet all: Vec<CatalogEntry> = ctx.vdfs().query_models().collect().await?;\n```\n\nTags and custom fields have no host side yet and return `Error::NotAvailable`.\n\n### AI Operations\n\nInference goes through `ctx.ai().infer(...)` behind the `use_models` grant.\nThe core has no provider yet, so every request answers `Error::NotAvailable`;\nhandle it the way `extensions/photos/src/jobs/analyze.rs` does, with one\nwarning and a skip, so the job completes and a later run with a provider\npicks the files up.\n\n```rust\n// Written against the API; answers NotAvailable until a provider exists\nlet faces = ctx.ai().from_registered(\"face_detection\").detect_faces(&bytes).await?;\n```\n\n### Credentials\n\n```rust\n// Store OAuth\nctx.credentials().store(\"gmail\", Credential::oauth2(\n    access_token,\n    Some(refresh_token),\n    3600,\n    vec![\"https://www.googleapis.com/auth/gmail.readonly\".into()]\n))?;\n\n// Get (auto-refreshes)\nlet cred = ctx.credentials().get(\"gmail\")?;\n```\n\n## Examples\n\nSee `extensions/test-extension/` for a complete working example.\n\n## Building\n\nAll extensions:\n```bash\ncd extensions/test-extension\ncargo build --target wasm32-unknown-unknown --release\n```\n\n## Documentation\n\n- **[SDK API Vision](../docs/EXTENSION_SDK_API_VISION.md)** - Future API improvements\n- **[Before/After Comparison](./BEFORE_AFTER_COMPARISON.md)** - See the transformation\n- **[WASM Architecture](../docs/core/design/WASM_ARCHITECTURE_FINAL.md)** - Technical details\n- **[Platform Revenue Model](../docs/PLATFORM_REVENUE_MODEL.md)** - Business case\n\n---\n\n**Extension development is now beautiful, safe, and productive. Start building!** \n","extensions/photos/README.md":"# Photos Extension for Spacedrive\n\nA comprehensive photo management extension that brings Apple Photos and Google Photos capabilities to Spacedrive.\n\n## Features\n\n### Face Recognition\n- **Automatic face detection** using RetinaFace model\n- **Face clustering** with DBSCAN algorithm\n- **Person identification** with name labeling\n- **Face-based search** (\"Show me photos of Alice\")\n- **Cross-device sync** of face clusters\n\n### Place Identification\n- **GPS extraction** from EXIF data\n- **Geographic clustering** (groups photos within 500m)\n- **Reverse geocoding** using AI\n- **Place-based search** (\"Photos from Paris\")\n- **Map view** of photo locations\n\n### Moments\n- **Automatic moment generation** (time + location clustering)\n- **AI-generated titles** (\"Summer in Paris\")\n- **Weekly memories** (scheduled agent task)\n- **Moment timeline** view\n\n### ️ Scene Understanding\n- **Scene classification** (ResNet50 on Places365)\n- **Smart tags** (#beach, #sunset, #food)\n- **Scene-based search** (\"Photos with sunsets\")\n- **Quality scoring** (aesthetic assessment)\n\n### Albums & Organization\n- **Manual albums** (user-created)\n- **Smart albums** (rule-based)\n- **Favorites** (special album)\n- **Hidden photos** (privacy)\n- **Shared albums** (multi-device)\n\n## Architecture\n\n### Models\n\n```rust\nPhoto       // Links to image file + EXIF + face/scene sidecars\nPerson      // Face cluster with name and embeddings\nPlace       // Geographic location with radius\nAlbum       // Collection of photos\nMoment      // Time/location-based photo group\n```\n\n### Agent Memory\n\n```rust\nPhotosMind {\n    history: TemporalMemory<PhotoEvent>,     // Analysis timeline\n    knowledge: AssociativeMemory<PhotoKnowledge>, // Face/place graph\n    plan: WorkingMemory<AnalysisPlan>,       // Pending work\n}\n```\n\n### Jobs\n\n- `analyze_photos_batch` - Face detection on photos\n- `identify_places_in_location` - Place clustering and naming\n- `analyze_scenes` - Scene classification\n- `create_moments` - Automatic moment generation\n- `cluster_faces_into_people` - Face clustering\n- `generate_face_tags` - Tag generation from sidecars\n\n### Actions\n\n- `create_album` - User creates album\n- `identify_person` - Name a face cluster\n- `remove_photo_from_album` - Organize albums\n- `hide_photo` - Privacy controls\n\n## Usage\n\n### Installation\n\n1. Install extension from Spacedrive Extension Store\n2. Grant permissions to specific photo locations:\n   - `/Users/alice/Photos`\n   - `/Volumes/External/Family Photos`\n3. Extension downloads AI models (~107MB total)\n4. User initiates analysis: \"Analyze for Faces\" button\n\n### Analysis Flow\n\n```\nUser enables Photos on \"/My Photos\"\n  ↓\nExtension dispatches analyze_photos_batch job\n  ↓\nFor each photo:\n  - Read EXIF (from Core)\n  - Detect faces → save to sidecar\n  - Classify scene → save to sidecar\n  ↓\nCluster faces into people\n  ↓\nGenerate tags from sidecars\n  ↓\nUser can search \"#person:alice\" or \"photos from beach\"\n```\n\n### Data Storage\n\n```\n~/.spacedrive/\n  └── models/\n      ├── face_detection/\n      │   └── photos_v1.onnx (12MB)\n      └── scene_classification/\n          └── resnet50.onnx (95MB)\n\n.sdlibrary/\n  ├── sidecars/\n  │   └── content/{ab}/{cd}/{uuid}/\n  │       └── extensions/com.spacedrive.photos/\n  │           ├── faces/default.json       # Face detection results\n  │           ├── scene/default.json       # Scene classification\n  │           └── aesthetics/default.json  # Quality score\n  └── extensions/com.spacedrive.photos/\n      └── data.db                           # Person, Place, Moment, Album, Photo rows\n```\n\n## SDK Features Demonstrated\n\n### Implemented in Example\n\n- `#[extension]` with permissions and dependencies\n- `#[model]` for Photo, Person, Place, Album, Moment\n- `#[agent]` with lifecycle hooks and event handlers\n- `#[agent_memory]` with enum-based Temporal/Associative memory\n- `#[job]` and `#[task]` for durable processing\n- `#[action]` with preview-execute pattern\n- `#[query]` for user searches\n- Extension-owned sidecars\n- Virtual models with persistence\n- User-scoped permissions\n- Model registration on install\n- AI with Jinja templates\n- Tag generation from sidecars\n- Custom memory query methods\n\n### What runs today\n\nUnder a daemon built with `--features wasm`, `analyze_photos` runs end to end\nover a library: it reads each photo through its record, asks for face\ndetection, and because the core has no inference provider the host answers\n`not_available`; the job warns once, skips, and completes, so a later run with\na detector picks the photos up. `core/tests/wasm_extension_test.rs` proves\nthis against a fixture library.\n\n### Not yet backed by the core\n\n- Inference. There is no provider for face detection, scene classification,\n  embeddings or language models, so `analyze_scenes`, `identify_places` and\n  `create_moments` have nothing to run on.\n- Tags, custom fields and `dispatch_jobs`.\n- Agents, actions and queries.\n- Model downloads named in `manifest.json`.\n- File-kind and preview registration.\n\n## Capabilities Compared\n\n### Apple Photos\n- Face recognition and clustering\n- Place identification\n- Memories\n- Albums (manual and smart)\n- Favorites and hidden\n- Search by person/place/scene\n- Map view\n- ️ Shared albums (requires P2P implementation)\n\n### Google Photos\n- Face grouping\n- Place detection\n- Search by content\n- Albums\n- Automatic creations (moments)\n- Scene/object detection\n- ️ Cloud backup (Spacedrive handles differently)\n\n### Spacedrive Photos Advantages\n- **100% local** - No cloud upload required\n- **Privacy-first** - Face data never leaves devices\n- **Multi-device sync** - Via P2P, not cloud\n- **Zero recurring cost** - No subscription needed\n- **User-scoped** - Only analyzes chosen locations\n- **Extensible** - Open SDK for customization\n\n## Building\n\n```bash\ncd extensions/photos\ncargo build --target wasm32-unknown-unknown --release\ncp target/wasm32-unknown-unknown/release/photos_extension.wasm ./photos.wasm\n```\n\n## License\n\nSame as Spacedrive Core\n\n",".tasks/Claude.md":"# Task Tracking Cheatsheet\n\nHi Claude, Claude here. Your owner has pointed you here with absolutely no context. This document is your prompt.\n\nThis directory tracks the complete development of Spacedrive. Tasks are organized into subdirectories:\n- `core/` - Backend/Rust tasks (CORE, JOB, INDEX, LSYNC, etc.)\n- `interface/` - Frontend/React tasks (UI, EXPL, SETS, MEDIA, etc.)\n- `mobile/` - Mobile-specific tasks (IOS, MACOS, AND)\n\nTasks are always kept up-to-date, though some may need updating based on recent work. Your job is to check git history for changes, validate completed tasks against the instructions below, and update their status accordingly.\n\n## Quick Start\n\nWhen asked to review and update tasks, follow this process:\n\n### 1. List Current Tasks\n\n```bash\ncargo run --bin task-validator -- list --sort-by status\n```\n\nThis shows all tasks grouped by status (Completed, Done, In Progress, To Do).\n\n### 2. Review Recent Commits\n\n```bash\ngit log --oneline -20\n```\n\nLook for feature implementations, major refactors, or completed work that might correspond to tasks.\n\n### 3. Cross-Reference Work with Tasks\n\nFor each recent feature/change:\n\n- **Read the task file** directly from `.tasks/{subdirectory}/TASK-ID-name.md`\n  - Core/backend work: `.tasks/core/`\n  - Interface work: `.tasks/interface/`\n  - Mobile work: `.tasks/mobile/`\n- **Read the acceptance criteria** carefully - this is your checklist\n- **Read all implementation files** mentioned in the task (they're usually listed in an \"Implementation Files\" section)\n- **Check core/tests/** for integration tests that validate the feature (for core tasks)\n- **Check packages/interface/src/** for interface implementations (for interface tasks)\n- Verify each acceptance criterion is actually met in the code\n\n### 4. Update Task Status\n\nWhen updating a task's status, edit the YAML front matter:\n\n```yaml\n---\nid: TASK-000\ntitle: Task Title\nstatus: Done # Changed from \"To Do\" or \"In Progress\"\nassignee: jamiepine\npriority: High\ntags: [core, feature]\nlast_updated: 2025-10-14 # Update this date\n---\n```\n\n**Status Levels:**\n\n- `To Do` - Not started\n- `In Progress` - Actively being worked on\n- `Done` - Complete and merged\n- `Completed` - Same as Done (legacy)\n\n### 5. Validate Changes\n\nBefore committing task updates, validate them:\n\n```bash\ncargo run --bin task-validator -- validate\n```\n\nThis checks that your YAML front matter matches the schema.\n\n## Task Status Guidelines\n\n### Mark as \"Done\" when:\n\n- All acceptance criteria are met\n- Code is merged to main\n- Tests pass (if applicable)\n- Feature is actually usable\n\n### Keep \"In Progress\" when:\n\n- ️ Partially implemented\n- ️ Core work done but rough edges remain\n- ️ Actively being iterated on\n\n### Keep \"To Do\" when:\n\n- Not started\n- Only design/planning done\n- Mentioned in docs but no code exists\n\n## Pro Tips\n\n1. **Read implementation files directly** - Open and read the actual source files mentioned in the task.\n\n2. **Always check core/tests/** for integration tests that validate the feature works end-to-end.\n\n3. **Check recent file changes:**\n\n   ```bash\n   git diff --name-only HEAD~10\n   ```\n\n4. **Search commits by keyword:**\n\n   ```bash\n   git log --oneline --grep=\"volume\\|cloud\" -i\n   ```\n\n5. **Filter tasks by criteria:**\n\n   ```bash\n   # Show only \"In Progress\" tasks\n   cargo run --bin task-validator -- list --status \"In Progress\"\n\n   # Show tasks by priority\n   cargo run --bin task-validator -- list --priority High\n   ```\n\n## Common Pitfalls\n\n**Don't mark tasks done if:**\n\n- Implementation exists but doesn't work\n- Only scaffolding/types are defined\n- Tests are failing\n- Feature is behind a feature flag and disabled\n\n**Do mark tasks done if:**\n\n- All acceptance criteria are genuinely met\n- Implementation is production-ready\n- Code demonstrates the feature working\n\n## Example Workflow\n\n```bash\n# 1. See current state\ncargo run --bin task-validator -- list --sort-by status\n\n# 2. Check what's been done recently\ngit log --oneline -15\n\n# 3. Found \"feat: implement cloud volume support\" commit\n#    Let's check CLOUD-003 task\n```\n\nNow use the Read tool to thoroughly verify:\n\n````\n# 4. Read the task file (note the subdirectory)\nRead .tasks/core/CLOUD-003-cloud-volume.md\n\n# 5. Read every implementation file listed in the task\nRead core/src/volume/backend/mod.rs\nRead core/src/volume/backend/cloud.rs\nRead core/src/ops/volumes/add_cloud/mod.rs\n... (read all mentioned files)\n\n# 6. Check for integration tests\nGlob core/tests/**/*.rs\nRead core/tests/test_cloud_volume.rs  # if it exists\n\n# 7. Verify each acceptance criterion:\n#    - [x] User can add S3 bucket? Check add_cloud action code\n#    - [x] Cloud volume can be indexed? Check indexer integration\n#    - [ ] Files can be copied to/from cloud? Search for copy implementation\n#\n# If ALL criteria met → mark \"Done\"\n# If SOME criteria met → keep \"In Progress\"\n# If implementation looks complete → Update status and last_updated date\n\n# 8. Validate before committing\n```bash\ncargo run --bin task-validator -- validate\n````\n\n## Task Schema Reference\n\nRequired fields in YAML front matter:\n\n- `id` - Unique identifier (e.g., CORE-001)\n- `title` - Human-readable title\n- `status` - One of: To Do, In Progress, Done, Completed\n- `assignee` - Who's working on it (or \"james\")\n- `priority` - Critical, High, Medium, or Low\n- `tags` - Array of relevant tags\n\nOptional fields:\n\n- `parent` - Parent epic/task ID\n- `whitepaper` - Reference to design docs\n- `last_updated` - ISO date of last update\n- `related_tasks` - Array of related task IDs\n\n---\n\n**Remember**: The task tracker is only useful if it's honest. Be rigorous about what \"Done\" means. When in doubt, leave it as \"In Progress\" and document what's left.\n\n— Past Claude, trying to help Future Claude (that's you!)\n","AGENTS.md":"# Spacedrive Core v2 Development Guide\n\n## Quick Start\n\n### Development Workflow\n\n1. Start daemon: `cargo run --bin sd-daemon`\n2. Make code changes\n3. Run tests: `cargo test`\n4. Rebuild and restart: `cargo run --bin sd-cli -- restart`\n5. Test via CLI: `cargo run --bin sd-cli -- <command>`\n\n### Common Commands\n\n```bash\ncargo build                              # Build the project\ncargo test                               # Run all tests\ncargo test <test_name>                   # Run specific test\ncargo clippy                             # Lint code\ncargo fmt                                # Format code\ncargo run --bin sd-cli -- <command>      # Run CLI (binary is sd-cli, not spacedrive)\n```\n\n### Common Mistakes\n\n- Running `spacedrive` instead of `sd-cli` (the binary name is `sd-cli`)\n- Forgetting to restart daemon after rebuilding\n- Using `println!` instead of `tracing` macros (`info!`, `debug!`, etc)\n- Implementing `Wire` manually instead of using `register_*` macros\n- Blocking the async runtime with synchronous I/O operations\n\n### Quick tips\n\n- On frontend apps, such as the interface in React, you must ALWAYS ensure type-safety based on the auto generated TypeScript types from `ts-client`. Never cast to as any or redefine backend types. our hooks are typesafe with correct input/output types, but sometimes you might need to access types directly from the `ts-client`.\n- If you have changed types on the backend that are public to the frontend (have `Type` derive), then you must regenerate the types using `cargo run --bin generate_typescript_types`\n- Read the `.mdx` files in /docs for context on any part of the app, they are kept up to date.\n\n### Current direction\n\n`docs/core/product-direction.mdx` defines the product boundary. Use it to decide\nwhether work belongs in Spacedrive before extending a subsystem.\n\nThe entries world is gone. The `entry` and `location` tables and everything\nhanging off them were dropped, and durable file state lives in the record table\nin `crates/store`; `docs/plans/2026-09-15-entries-final-drop.md` records how.\n`docs/plans/2026-08-22-source-convergence.md` is the register for making the\nnew world one thing: one store shape, one registry, one set of claims that\nmatch the code.\n\n- Spacedrive is a set of sources. A source has an origin, an ingest, and a\n  store; filesystem and adapter sources differ only in ingest. Anything that\n  makes those two diverge further is wrong.\n- New durable state belongs in a source store: the record table, or the\n  assertion half for anything no ingest can rebuild. `library.db` keeps only\n  the source registry, devices, volumes, spaces, and small shared state.\n- A source store has two halves. The generation is rebuildable for as long as\n  its origin answers; the assertion layer never is. Since the origin can stop\n  answering silently, no code path may assume a store can be thrown away and\n  rebuilt. `docs/core/design/source-durability.md` has the rest, including what\n  the assertion tables have to reserve for sync.\n- The `.tasks/` tree predates this work and is not its register. The status\n  table at the top of the execution plan remains the entries-teardown register.\n  Use `PROJECT_STATUS.md` as the short-term project-wide working context and\n  update it in the same commit as active work.\n\n## Architecture Overview\n\nSpacedrive uses daemon-client architecture. A single daemon process manages core functionality. Multiple clients (CLI, GraphQL server, desktop app) connect via Unix domain sockets.\n\n### CQRS and DDD Pattern\n\n- **Domain** (`src/domain/`): Core data structures and business logic (nouns)\n- **Operations** (`src/ops/`): Actions and queries (verbs)\n- **Actions**: State-changing operations (writes)\n- **Queries**: Data retrieval without state changes (reads)\n\n### Feature Module Structure\n\nEach feature lives in its own module under `src/ops/`. Example: `src/ops/files/share`\n\n```\nsrc/ops/files/share/\n├── action.rs      # State-changing logic\n├── input.rs       # Action input structures\n├── output.rs      # Action output structures\n└── job.rs         # Long-running job implementation (if needed)\n```\n\nComplete feature example:\n\n```rust\n// src/ops/files/share/input.rs\n#[derive(Debug, Serialize, Deserialize)]\npub struct ShareFileInput {\n    pub file_id: i32,\n    pub recipient: String,\n}\n\n// src/ops/files/share/output.rs\n#[derive(Debug, Serialize, Deserialize)]\npub struct ShareFileOutput {\n    pub share_id: String,\n    pub url: String,\n}\n\n// src/ops/files/share/action.rs\nuse super::{ShareFileInput, ShareFileOutput};\n\npub struct ShareFileAction;\n\ncrate::register_library_action!(ShareFileAction, \"files.share\");\n\nimpl Action for ShareFileAction {\n    type Input = ShareFileInput;\n    type Output = ShareFileOutput;\n\n    async fn run(input: Self::Input, ctx: &ActionContext) -> Result<Self::Output> {\n        // Implementation\n    }\n}\n```\n\n## Communication Architecture\n\nSpacedrive supports multiple communication patterns for different platforms and use cases.\n\n### Daemon-Client Communication (Tauri Desktop, CLI, Web)\n\nThe Tauri desktop app, CLI, and web interface connect to a daemon process via Unix domain sockets (or WebSockets for web). Communication uses JSON-RPC 2.0 with Wire method strings.\n\n**Registration Macros:**\n\nNever implement `Wire` manually. Use registration macros:\n\n```rust\n// Queries\ncrate::register_query!(NetworkStatusQuery, \"network.status\");\n// Generates: \"query:network.status\"\n\n// Library Actions\ncrate::register_library_action!(FileCopyAction, \"files.copy\");\n// Generates: \"action:files.copy.input\"\n\n// Core Actions\ncrate::register_core_action!(LibraryCreateAction, \"libraries.create\");\n// Generates: \"action:libraries.create.input\"\n```\n\n**Registry System:**\n\nThe `inventory` crate collects operations at compile time. When you use `register_core_query!`, `register_library_query!`, `register_core_action!` or `register_library_action!`, the operation automatically appears in the global `CORE_QUERIES`, `LIBRARY_QUERIES`, `CORE_ACTIONS` and `LIBRARY_ACTIONS` maps at startup. You never manually register operations.\n\nLocation: `core/src/infra/wire/registry.rs`\n\n### Tauri Desktop Development\n\nThe Tauri app (`apps/tauri/`) is the primary desktop application for Spacedrive. It connects to the daemon via the TypeScript client.\n\n**Development Workflow:**\n\n```bash\n# Install dependencies\nbun install\n\n# Run Tauri app in dev mode (auto-starts daemon)\ncd apps/tauri\nbun run tauri:dev\n\n# Build for production\nbun run tauri:build\n```\n\n**TypeScript Client:**\n\nThe TypeScript client (`packages/ts-client/`) is auto-generated from Rust types using Specta:\n\n```bash\n# Generate TypeScript types\ncargo run --bin generate_typescript_types\n```\n\n**Output:** `packages/ts-client/src/generated/types.ts`\n\n**Architecture:**\n\n```\nTauri App (React)\n    ↓\n@sd/ts-client (TypeScript)\n    ↓\nDaemon (Unix Socket / IPC)\n    ↓\nRpcServer (Rust)\n    ↓\nOperation Registry\n```\n\n### Native Prototypes (iOS, macOS)\n\n**Note:** iOS and macOS apps are experimental prototypes, not production apps.\n\nNative prototypes embed the core directly as a library via FFI rather than connecting to a daemon. These are located in `apps/ios/` and `apps/macos/` but are private and not documented for public use.\n\n**Swift Client Generation:**\n\nFor the prototypes, Swift types can be generated:\n\n```bash\ncargo run --bin generate_swift_types\n```\n\nOutput: `packages/swift-client/Sources/SpacedriveClient/`\n\n### Extension System (WASM)\n\nExtensions run as sandboxed WASM modules that interact with Spacedrive core via host functions. Extensions are distributed as compiled `.wasm` files.\n\n**Architecture:**\n\n```\nExtension.wasm (compiled Rust)\n    ↓\nspacedrive-sdk (Rust crate)\n    ↓\nHost Functions (FFI boundary)\n    ↓\nCore (VDFS, Jobs, AI, etc.)\n```\n\n**Key Components:**\n\n**SDK Location:** `crates/sdk/`\n\n- High-level Rust API abstracting FFI details\n- Procedural macros for extension definition\n- Type-safe job, model, and action builders\n\n**Extension Development:**\n\nExtensions use procedural macros to minimize boilerplate:\n\n```rust\nuse spacedrive_sdk::prelude::*;\n\n#[extension(\n    id = \"test-extension\",\n    name = \"Test Extension\",\n    version = \"0.1.0\",\n    jobs = [test_counter],\n)]\nstruct TestExtension;\n\n#[derive(Serialize, Deserialize, Default)]\npub struct CounterState {\n    pub current: u32,\n    pub target: u32,\n    pub processed: Vec<String>,\n}\n\n#[job(name = \"counter\")]\nfn test_counter(ctx: &JobContext, state: &mut CounterState) -> Result<()> {\n    ctx.log(&format!(\"Starting counter (current: {}, target: {})\",\n        state.current, state.target));\n\n    while state.current < state.target {\n        if ctx.check_interrupt_sync() {\n            ctx.checkpoint(state)?;\n            return Err(Error::OperationFailed(\"Interrupted\".into()));\n        }\n\n        state.current += 1;\n        ctx.report_progress(\n            state.current as f32 / state.target as f32,\n            &format!(\"Counted {}/{}\", state.current, state.target),\n        );\n\n        if state.current % 10 == 0 {\n            ctx.checkpoint(state)?;\n        }\n    }\n\n    Ok(())\n}\n```\n\n**Host Functions:**\n\nExtensions import minimal FFI functions:\n\n```rust\n#[link(wasm_import_module = \"spacedrive\")]\nextern \"C\" {\n    fn spacedrive_log(level: u32, msg_ptr: *const u8, msg_len: usize);\n    fn register_job(\n        job_name_ptr: *const u8,\n        job_name_len: u32,\n        export_fn_ptr: *const u8,\n        export_fn_len: u32,\n        resumable: u32,\n    ) -> i32;\n}\n```\n\n**Building Extensions:**\n\n```bash\n# From extension directory\ncargo build --target wasm32-unknown-unknown --release\n\n# Output: target/wasm32-unknown-unknown/release/extension_name.wasm\n```\n\n**Extension Capabilities:**\n\nExtensions can define:\n\n- Models: Data structures stored in `models` table (content-scoped, standalone, or entry-scoped)\n- Jobs: Long-running resumable operations\n- Actions: User-invoked operations with preview-commit workflow\n- Agents: Autonomous logic with memory and event handling\n- UI: Custom views via `ui_manifest.json`\n\n**Example Use Cases:**\n\n- Photos extension: Face detection, scene tagging, album organization\n- Finance extension: Receipt extraction, expense tracking\n- Research extension: Citation extraction, knowledge graphs\n\n**Key Benefits:**\n\n- Single `.wasm` file works on all platforms\n- True sandboxing (WASM isolation)\n- Resumable jobs with checkpointing\n- Type-safe API with procedural macros\n- No core modifications needed for new features\n\n**Documentation:**\n\n- `crates/sdk/API_REFERENCE.md` - SDK specification and API reference\n- `extensions/test-extension/` - Working example extension\n- `crates/sdk/` - SDK implementation\n- `crates/sdk-macros/` - SDK procedural macros\n\n**Status:** A daemon built with the `wasm` feature loads every extension under `<data dir>/extensions/` at startup and runs the jobs they register through the job manager, with checkpoints and resume (`core/src/infra/extension/README.md`). Only the job host functions exist; the rest of the SDK (VDFS, AI, models, tasks) has no host side yet.\n\n## Code Standards\n\n### Import Organization\n\nGroup imports with blank lines between groups:\n\n```rust\n// Standard library\nuse std::path::PathBuf;\nuse std::sync::Arc;\n\n// External crates\nuse serde::{Deserialize, Serialize};\nuse tokio::sync::RwLock;\n\n// Local modules\nuse crate::domain::library::Library;\nuse crate::ops::Action;\n```\n\n### Naming Conventions\n\n- Functions/variables: `snake_case`\n- Types: `PascalCase`\n- Constants: `SCREAMING_SNAKE_CASE`\n\n### Error Handling\n\nUse `Result<T, E>` for all fallible operations. Use `thiserror` for custom errors, `anyhow` for application errors.\n\n```rust\nuse thiserror::Error;\n\n#[derive(Error, Debug)]\npub enum ShareError {\n    #[error(\"File not found: {0}\")]\n    FileNotFound(i32),\n\n    #[error(\"Permission denied\")]\n    PermissionDenied,\n\n    #[error(\"Database error: {0}\")]\n    Database(#[from] sea_orm::DbErr),\n}\n\npub async fn share_file(id: i32) -> Result<String, ShareError> {\n    let file = find_file(id).await.ok_or(ShareError::FileNotFound(id))?;\n    // Implementation\n    Ok(share_url)\n}\n```\n\n### Async Code\n\n- Use `async/await` syntax\n- Prefer `tokio` primitives (`tokio::sync::RwLock`, `tokio::spawn`)\n- Avoid blocking operations (use `tokio::fs` not `std::fs`)\n- Use `tokio::task::spawn_blocking` for CPU-intensive work\n\n### Resumable Jobs\n\nStore job state within the job struct. Use `#[serde(skip)]` for non-persistent fields.\n\n```rust\n#[derive(Serialize, Deserialize)]\npub struct FileCopyJob {\n    pub source: PathBuf,\n    pub destination: PathBuf,\n    pub copied_files: Vec<PathBuf>,  // Persisted for resumability\n\n    #[serde(skip)]\n    pub progress_tx: Option<tokio::sync::mpsc::Sender<Progress>>,  // Not persisted\n}\n\nimpl Job for FileCopyJob {\n    async fn run(&mut self, ctx: &JobContext) -> Result<()> {\n        ctx.log().info(\"Starting file copy job\");\n\n        for file in &self.files_to_copy {\n            if self.copied_files.contains(file) {\n                continue;  // Skip already copied files on resume\n            }\n\n            copy_file(file).await?;\n            self.copied_files.push(file.clone());\n        }\n\n        Ok(())\n    }\n}\n```\n\n### Documentation\n\n**Core principle:** Explain WHY, not WHAT. Keep comments as short as possible. One sentence explaining rationale beats a paragraph restating code.\n\n**Module docs (`//!`):**\n- Add a title with `#` for the module name\n- Explain what the module does in plain language (not bullet points)\n- Include design rationale naturally in prose\n- Add runnable code examples showing usage\n\n````rust\n//! # File Sharing System\n//!\n//! `core::ops::files::share` provides temporary file sharing via signed URLs.\n//! Share links expire after 7 days by default to prevent indefinite access to\n//! private files. UUID v5 deterministic IDs ensure the same file generates\n//! consistent share URLs across devices without coordination.\n//!\n//! ## Example\n//! ```rust,no_run\n//! use spacedrive_core::ops::files::share::{ShareFileAction, ShareFileInput};\n//!\n//! let input = ShareFileInput { file_id: 123, recipient: \"user@example.com\" };\n//! let output = ShareFileAction::run(input, &ctx).await?;\n//! ```\n````\n\n**Function docs (`///`):**\n- First line: brief one-liner\n- Second paragraph: explain design rationale and why this exists\n- Document error handling philosophy when relevant\n- Explain non-obvious behavior and platform differences\n\n```rust\n/// Creates a share link with automatic expiration.\n///\n/// Share links use signed JWTs so the daemon can validate them without\n/// database lookups on every request. Expiration is enforced server-side\n/// to prevent timezone manipulation. Recipients without library access\n/// get read-only access to the specific file only.\n///\n/// Returns `ShareError::PermissionDenied` if the file is private and\n/// the recipient isn't a library member. The share is still created\n/// but marked inactive for audit logging.\npub async fn share_file(input: ShareFileInput) -> Result<ShareFileOutput>\n```\n\n**Inline comments:**\n- Delete comments that restate obvious code\n- Explain WHY for decisions, not WHAT the code does\n- Use one sentence when possible\n- Only expand for truly non-obvious consequences\n\n```rust\n// Good: explains WHY\n// Lowercase for case-insensitive search matching.\nlet ext = path.extension().map(|e| e.to_lowercase());\n\n// Bad: restates code\n// Extract file extension and convert to lowercase\nlet ext = path.extension().map(|e| e.to_lowercase());\n\n// Good: explains consequence\n// Keep the arena's UUID so a file keeps its identity when a source adopts it.\nlet uuid = arena.get_entry_uuid(path).unwrap_or_else(Uuid::now_v7);\n\n// Bad: verbose explanation of obvious behavior\n// UUID assignment strategy:\n// 1. First check if the arena has a UUID\n// 2. If not, generate a new one\nlet uuid = arena.get_entry_uuid(path).unwrap_or_else(Uuid::now_v7);\n```\n\n**Error handling comments:**\nExplain strategy and recovery, not just \"log and continue\".\n\n```rust\n// Good: explains recovery\n// Best-effort: continue with remaining moves, stale paths cleaned up on next reindex.\nErr(e) => ctx.log(format!(\"Failed to move: {}\", e)),\n\n// Bad: states the obvious\n// Log error but continue\nErr(e) => ctx.log(format!(\"Failed to move: {}\", e)),\n```\n\n**Platform-specific comments:**\nExplain consequences, not implementation blockers.\n\n```rust\n// Good: explains why and fallback\n#[cfg(windows)]\npub fn get_inode(_metadata: &std::fs::Metadata) -> Option<u64> {\n    // Windows file indices are unstable across reboots; fall back to path-only matching.\n    None\n}\n\n// Bad: over-explains implementation details\n#[cfg(windows)]\npub fn get_inode(_metadata: &std::fs::Metadata) -> Option<u64> {\n    // Windows doesn't have inodes.\n    // The method `file_index()` is unstable (issue #63010).\n    // Returning None is safe as the field is Optional.\n    None\n}\n```\n\n**Never use:**\n- Placeholder comments (\"for now\", \"TODO: extract this later\")\n- Markdown formatting (`**bold**`, `_italic_`) in code comments\n- ASCII diagrams (put those in `/docs/` if needed)\n- Section divider comments (`// ========== Section ==========`)\n- Comments explaining removed code during refactors\n\nTrack future work in GitHub issues, not code comments.\n\n### Formatting\n\nRun `cargo fmt` before committing. Tabs for indentation. No emojis.\n\n## Logging\n\n### Setup\n\nUse `tracing_subscriber` in main or examples:\n\n```rust\nuse tracing_subscriber::EnvFilter;\n\nfn main() {\n    tracing_subscriber::fmt()\n        .with_env_filter(\n            EnvFilter::try_from_default_env()\n                .unwrap_or_else(|_| EnvFilter::new(\"sd_core=info\"))\n        )\n        .init();\n}\n```\n\n## Writing Style\n\nThis applies to all documentation, code comments, and design documents.\n\nUse clear, simple language. Write short, impactful sentences. Use active voice. Focus on practical, actionable information.\n\nAddress the reader directly with \"you\" and \"your\". Support claims with data and examples when possible.\n\nAvoid these constructions:\n\n- Em dashes (use commas or periods)\n- \"Not only this, but also this\"\n- Metaphors and cliches\n- Generalizations\n- Setup language like \"in conclusion\"\n- Unnecessary adjectives and adverbs\n- Emojis, hashtags, markdown formatting in prose\n\nAvoid these words:\ncomprehensive, delve, utilize, harness, realm, tapestry, unlock, revolutionary, groundbreaking, remarkable, pivotal\n\n### Macros\n\nUse `tracing` macros, never `println!`:\n\n```rust\nuse tracing::{info, warn, error, debug};\n\ninfo!(\"Server started on port {}\", port);\ndebug!(file_id = %id, \"Processing file\");\nwarn!(error = %e, \"Retrying operation\");\nerror!(\"Failed to connect to database\");\n```\n\n### Job Logging\n\nUse `ctx.log()` in job implementations for automatic `job_id` tagging:\n\n```rust\nimpl Job for MyJob {\n    async fn run(&mut self, ctx: &JobContext) -> Result<()> {\n        ctx.log().info(\"Job started\");\n        ctx.log().debug!(progress = %self.progress, \"Processing\");\n        Ok(())\n    }\n}\n```\n\n### Log Levels\n\n- `debug`: Detailed flow for troubleshooting\n- `info`: User-relevant events (server start, job completion)\n- `warn`: Recoverable issues (retry, fallback)\n- `error`: Failures requiring attention\n\n### Environment Control\n\nUse `RUST_LOG` environment variable:\n\n```bash\nRUST_LOG=debug cargo run --bin sd-cli\nRUST_LOG=sd_core=trace cargo run\nRUST_LOG=sd_core::ops=debug cargo run\n```\n\n## Testing\n\n### Test Organization\n\n- Unit tests: Colocated in `#[cfg(test)]` modules\n- Integration tests: `tests/` directory at crate root\n\n```rust\n// src/ops/files/share/action.rs\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[tokio::test]\n    async fn test_share_file() {\n        let input = ShareFileInput {\n            file_id: 1,\n            recipient: \"test@example.com\".to_string(),\n        };\n\n        let output = share_file(input).await.unwrap();\n        assert!(!output.share_id.is_empty());\n    }\n}\n```\n\n### Running Tests\n\n```bash\ncargo test                    # All tests\ncargo test test_share_file    # Specific test\ncargo test --lib              # Library tests only\ncargo test -- --nocapture     # Show output\n```\n\n## Task Tracking\n\nRead `/PROJECT_STATUS.md` at the start of a session and update its Immediate\nregister when broad work starts, lands, becomes blocked, or changes owner. Keep\ndesign rationale and phase acceptance criteria in the associated plan. For\nanything still in the entries teardown, also update the status table at the top\nof `/docs/plans/2026-08-20-entries-teardown-execution.md` in the same commit.\n\n`/.tasks/` is frozen. It describes the entry-centric model that the teardown\nremoves, and was last updated in May. Do not add to it and do not run the\nvalidator against new work. The sections below document the retired system for\nhistorical reference only.\n\nSpacedrive uses a file-based task system in `/.tasks/` to track features, epics, and development work. All task files are version-controlled alongside the code.\n\n### When to Create Tasks\n\nCreate tasks for work that:\n\n- Introduces a new feature or capability\n- Refactors a significant system or module\n- Fixes a bug requiring architectural changes\n- Implements a whitepaper specification\n\nDo not create tasks for:\n\n- Routine code formatting or style fixes\n- Trivial bug fixes (single line changes)\n- Documentation updates to existing features\n- Dependency version bumps\n\n### Task Structure\n\nEach task is a Markdown file: `CATEGORY-###-title-slug.md`\n\n```yaml\n---\nid: CORE-042\ntitle: \"Implement file sharing API\"\nstatus: \"In Progress\"\nassignee: \"james\"\npriority: \"High\"\ntags: [\"core\", \"networking\"]\nwhitepaper: \"Section 4.2\" # And/or design_doc: DESIGN_DOC_NAME.md\n---\n\n## Description\nBrief overview of what needs to be done and why.\n\n## Implementation Steps\n- [ ] Create share action in src/ops/files/share\n- [ ] Add database schema for shares table\n- [ ] Implement expiration logic\n\n## Acceptance Criteria\n- Share links work across all platforms\n- Expired shares return 404\n- Tests cover edge cases\n```\n\n### Managing Tasks\n\n```bash\n# List your active tasks\ncargo run -p task-validator -- list --assignee \"yourname\" --status \"In Progress\"\n\n# List high priority tasks\ncargo run -p task-validator -- list --priority \"High\" --sort-by id\n\n# Validate before committing (automatic via git hook)\ncargo run -p task-validator -- validate\n```\n\n### Task Lifecycle\n\n1. Create task file in `/.tasks/` with `status: \"To Do\"`\n2. Update status to `\"In Progress\"` when you start work\n3. Complete implementation and tests\n4. Update status to `\"Done\"` and commit\n\nFull documentation: `/docs/core/task-tracking.mdx`\n\n## Debugging\n\n### Log Files\n\nJob logs live in the `job_logs` directory in the data folder root.\n\n### Daemon Restart\n\nAfter rebuilding, restart the daemon to use the latest code:\n\n```bash\ncargo build\ncargo run --bin sd-cli -- restart\n```\n\n### Verbose Logging\n\n```bash\nRUST_LOG=debug cargo run --bin sd-daemon\nRUST_LOG=sd_core::jobs=trace cargo run\n```\n\n## Documentation Locations\n\n- Core architecture: `/docs/core/`\n- Design docs and RFCs: `/docs/core/design/`\n- Application docs: `/docs/`\n- Task tracking: `/docs/core/task-tracking.mdx`\n","extensions/test-extension/manifest.json":"{\n\t\"id\": \"test-extension\",\n\t\"name\": \"Test Extension\",\n\t\"version\": \"0.1.0\",\n\t\"description\": \"Minimal extension demonstrating beautiful SDK API\",\n\t\"author\": \"Spacedrive Team\",\n\t\"homepage\": \"https://spacedrive.com\",\n\t\"wasm_file\": \"test_extension.wasm\",\n\t\"permissions\": {\n\t\t\"read_records\": {},\n\t\t\"write_sidecars\": [\n\t\t\t\"digest\"\n\t\t],\n\t\t\"write_tags\": true,\n\t\t\"write_custom_fields\": [\n\t\t\t\"test\"\n\t\t],\n\t\t\"dispatch_jobs\": true\n\t},\n\t\"kinds\": [\n\t\t{\n\t\t\t\"name\": \"fake\",\n\t\t\t\"display_name\": \"Fake file\",\n\t\t\t\"parent\": \"text\",\n\t\t\t\"extensions\": [\n\t\t\t\t\"fake\"\n\t\t\t],\n\t\t\t\"magic\": [\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"46 41 4B 45\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"preview\": {\n\t\t\t\t\"viewer\": \"fake_viewer\"\n\t\t\t}\n\t\t}\n\t]\n}\n","extensions/test-extension/README.md":"# Test Extension\n\n**The canonical example of Spacedrive extension development.**\n\nThis extension demonstrates the beautiful, macro-powered API that makes building extensions delightful.\n\n## Features\n\n**Zero Boilerplate** - Macros generate all FFI code\n**Type-Safe** - Full Rust type system\n**No Unsafe** - Safe by default\n**Clean API** - Just write business logic\n\n## Code\n\n**Complete extension in 76 lines:**\n\n```rust\nuse spacedrive_sdk::prelude::*;\nuse spacedrive_sdk::{extension, job};\n\n// Extension definition\n#[extension(\n    id = \"test-extension\",\n    name = \"Test Extension\",\n    version = \"0.1.0\"\n)]\nstruct TestExtension;\n\n// Job state\n#[derive(Serialize, Deserialize, Default)]\npub struct CounterState {\n    pub current: u32,\n    pub target: u32,\n}\n\n// Job implementation - THAT'S IT!\n#[job]\nfn test_counter(ctx: &JobContext, state: &mut CounterState) -> Result<()> {\n    while state.current < state.target {\n        ctx.check_interrupt()?;\n        state.current += 1;\n        ctx.report_progress(\n            state.current as f32 / state.target as f32,\n            &format!(\"Counted {}/{}\", state.current, state.target)\n        );\n        if state.current % 10 == 0 {\n            ctx.checkpoint(state)?;\n        }\n    }\n    Ok(())\n}\n```\n\n## What the Macros Generate\n\nThe `#[extension]` and `#[job]` macros automatically generate:\n\n- `plugin_init()` - Extension initialization\n- `plugin_cleanup()` - Extension cleanup\n- `execute_test_counter()` - FFI export with full state management\n- All pointer marshalling\n- Serialization/deserialization\n- Error handling\n- Progress tracking\n- Checkpoint management\n\n**~120 lines of boilerplate you don't write!**\n\n## Building\n\n```bash\ncargo build --target wasm32-unknown-unknown --release\n```\n\nOutput: `target/wasm32-unknown-unknown/release/test_extension.wasm` (~254KB)\n\n## Capabilities Demonstrated\n\n### Job System\n- Progress reporting (0-100%)\n- Checkpointing (resume after crash)\n- Interruption handling (pause/cancel)\n- Metrics tracking (items processed)\n- State persistence\n\n### API Ergonomics\n- Clean function signatures\n- `?` operator for error handling\n- No FFI knowledge required\n- No unsafe code\n- Just write Rust!\n\n## Testing\n\nOnce Core is running:\n```rust\n// Load extension\nplugin_manager.load_plugin(\"test-extension\").await?;\n\n// Dispatch job\nlet job_id = job_manager.dispatch_by_name(\n    \"test-extension:test_counter\",\n    json!({ \"target\": 100 })\n).await?;\n\n// Watch progress in logs:\n// INFO Counted 10/100 (10% complete)\n// INFO Counted 20/100 (20% complete)\n// ...\n// INFO ✓ Completed! Processed 100 items\n```\n\n## Comparison\n\n| Metric | Manual FFI | With Macros | Improvement |\n|--------|-----------|-------------|-------------|\n| Lines of Code | 181 | 76 | 58% less |\n| Unsafe Blocks | 4 | 0 | 100% safer |\n| Boilerplate | 120 lines | 10 lines | 92% less |\n| WASM Size | 252KB | 254KB | Same |\n| Readability | 5/10 | 10/10 | Much better |\n| Dev Time | 2-3 hours | 15 minutes | 10x faster |\n\n---\n\n**This is what all Spacedrive extensions should look like going forward!** \n","packages/ts-client/README.md":"# @spacedrive/client\n\nType-safe TypeScript client for the Spacedrive daemon, automatically generated from Rust core types using Specta.\n\n## Features\n\n- **Fully Type-Safe** - All types generated directly from Rust core\n- **Real-time Events** - Subscribe to daemon events with full type safety\n- **Unix Domain Sockets** - Direct communication with daemon\n- **Auto-Generated** - Types stay in sync with Rust core automatically\n- **Rich Documentation** - JSDoc comments from Rust code\n\n## Installation\n\n```bash\nnpm install @spacedrive/client\n```\n\n## Quick Start\n\n```typescript\nimport { SpacedriveClient } from '@spacedrive/client';\n\nconst client = new SpacedriveClient();\n\n// Test connection\nawait client.ping();\n\n// Get libraries (fully typed)\nconst libraries = await client.getLibraries();\nconsole.log('Libraries:', libraries);\n\n// Get jobs with status filtering\nconst jobs = await client.getJobs('running');\nconsole.log('Running jobs:', jobs);\n\n// Create a new library\nconst newLibrary = await client.createLibrary('My Photos', '/Users/me/Photos');\nconsole.log('Created library:', newLibrary);\n```\n\n## Event Subscription\n\n```typescript\n// Subscribe to specific event types\nawait client.subscribe(['JobStarted', 'JobProgress', 'JobCompleted']);\n\n// Listen for events (fully typed)\nclient.on('event', (event) => {\n  switch (event) {\n    case 'CoreStarted':\n      console.log('Daemon started');\n      break;\n    default:\n      if ('JobStarted' in event) {\n        console.log(`Job started: ${event.JobStarted.job_type} (${event.JobStarted.job_id})`);\n      } else if ('JobProgress' in event) {\n        console.log(`Job progress: ${event.JobProgress.progress * 100}%`);\n      } else if ('JobCompleted' in event) {\n        console.log(`Job completed: ${event.JobCompleted.job_type}`);\n        console.log('Output:', event.JobCompleted.output);\n      }\n      break;\n  }\n});\n\nclient.on('error', (error) => {\n  console.error('Client error:', error);\n});\n\nclient.on('disconnected', () => {\n  console.log('Disconnected from daemon');\n});\n```\n\n## Type Generation\n\nTypes are automatically generated from the Rust core using Specta. To regenerate types:\n\n```bash\nnpm run generate-types\n```\n\n## API Reference\n\n### Core Methods\n\n- `ping()` - Test daemon connectivity\n- `executeQuery<Q, R>(query: Q, method: string): Promise<R>` - Execute a query operation\n- `executeAction<A, R>(action: A, method: string): Promise<R>` - Execute an action operation\n- `subscribe(eventTypes?: string[]): Promise<void>` - Subscribe to daemon events\n\n### Convenience Methods\n\n- `getLibraries(includeStats?: boolean): Promise<LibraryInfo[]>` - Get all libraries\n- `createLibrary(name: string, path?: string): Promise<LibraryCreateOutput>` - Create a new library\n- `getJobs(status?: JobStatus): Promise<JobListOutput>` - Get job list with optional filtering\n\n### Event Types\n\nAll event types are fully typed TypeScript unions:\n\n- `Event` - Main event union type\n- `JobOutput` - Job completion output (adjacently tagged)\n- `JobStatus` - Job status enum (`\"queued\" | \"running\" | \"completed\" | ...`)\n- `FileOperation` - File operation types\n- `Progress` - Progress information with multiple formats\n\n## Architecture\n\nThis client uses the same architecture as the Swift client:\n\n1. **JSON API Layer** - Communicates via JSON instead of bincode for external clients\n2. **Unix Domain Sockets** - Direct, efficient communication with daemon\n3. **Type Generation** - Rust types → TypeScript via Specta\n4. **Event Streaming** - Real-time event subscription with line-delimited JSON\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Build the client\nnpm run build\n\n# Run tests\nnpm test\n\n# Watch mode for development\nnpm run dev\n```\n","packages/ts-client/package.json":"{\n\t\"name\": \"@sd/ts-client\",\n\t\"version\": \"0.1.0\",\n\t\"description\": \"Type-safe TypeScript client for Spacedrive daemon\",\n\t\"main\": \"dist/index.js\",\n\t\"types\": \"dist/index.d.ts\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./src/index.ts\",\n\t\t\t\"default\": \"./src/index.ts\"\n\t\t},\n\t\t\"./generated/types\": {\n\t\t\t\"types\": \"./src/generated/types.ts\",\n\t\t\t\"default\": \"./src/generated/types.ts\"\n\t\t},\n\t\t\"./src/generated/types\": {\n\t\t\t\"types\": \"./src/generated/types.ts\",\n\t\t\t\"default\": \"./src/generated/types.ts\"\n\t\t},\n\t\t\"./hooks\": {\n\t\t\t\"types\": \"./src/hooks/index.ts\",\n\t\t\t\"default\": \"./src/hooks/index.ts\"\n\t\t},\n\t\t\"./hooks/useClient\": {\n\t\t\t\"types\": \"./src/hooks/useClient.tsx\",\n\t\t\t\"default\": \"./src/hooks/useClient.tsx\"\n\t\t},\n\t\t\"./hooks/useNormalizedQuery\": {\n\t\t\t\"types\": \"./src/hooks/useNormalizedQuery.ts\",\n\t\t\t\"default\": \"./src/hooks/useNormalizedQuery.ts\"\n\t\t},\n\t\t\"./src/hooks/useClient\": {\n\t\t\t\"types\": \"./src/hooks/useClient.tsx\",\n\t\t\t\"default\": \"./src/hooks/useClient.tsx\"\n\t\t},\n\t\t\"./src/hooks/useNormalizedQuery\": {\n\t\t\t\"types\": \"./src/hooks/useNormalizedQuery.ts\",\n\t\t\t\"default\": \"./src/hooks/useNormalizedQuery.ts\"\n\t\t}\n\t},\n\t\"scripts\": {\n\t\t\"build\": \"tsc\",\n\t\t\"dev\": \"tsc --watch\",\n\t\t\"test\": \"jest\",\n\t\t\"generate-types\": \"cargo run --bin generate_typescript_types --manifest-path ../../core/Cargo.toml\"\n\t},\n\t\"keywords\": [\n\t\t\"spacedrive\",\n\t\t\"client\",\n\t\t\"typescript\",\n\t\t\"daemon\"\n\t],\n\t\"author\": \"Spacedrive\",\n\t\"license\": \"Apache-2.0\",\n\t\"peerDependencies\": {\n\t\t\"@tanstack/react-query\": \"^5.0.0\",\n\t\t\"react\": \"^18.0.0 || ^19.0.0\"\n\t},\n\t\"peerDependenciesMeta\": {\n\t\t\"@tanstack/react-query\": {\n\t\t\t\"optional\": true\n\t\t},\n\t\t\"react\": {\n\t\t\t\"optional\": true\n\t\t}\n\t},\n\t\"devDependencies\": {\n\t\t\"@happy-dom/global-registrator\": \"^15.11.0\",\n\t\t\"@testing-library/jest-dom\": \"^6.0.0\",\n\t\t\"@testing-library/react\": \"^16.0.0\",\n\t\t\"@types/jest\": \"^29.0.0\",\n\t\t\"@types/node\": \"^20.0.0\",\n\t\t\"happy-dom\": \"^15.11.0\",\n\t\t\"jest\": \"^29.0.0\",\n\t\t\"jest-environment-jsdom\": \"^29.0.0\",\n\t\t\"jsdom\": \"^27.2.0\",\n\t\t\"ts-jest\": \"^29.0.0\",\n\t\t\"typescript\": \"^5.0.0\"\n\t},\n\t\"dependencies\": {\n\t\t\"@types/ws\": \"^8.0.0\",\n\t\t\"@sd/assets\": \"workspace:*\",\n\t\t\"ws\": \"^8.0.0\",\n\t\t\"zustand\": \"^5.0.8\",\n\t\t\"ts-deepmerge\": \"^7.0.1\",\n\t\t\"tiny-invariant\": \"^1.3.3\",\n\t\t\"valibot\": \"^1.0.0\",\n\t\t\"type-fest\": \"^4.30.0\"\n\t},\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"src/**/*\"\n\t]\n}\n","packages/ts-client/tsconfig.json":"{\n\t\"compilerOptions\": {\n\t\t\"target\": \"ES2020\",\n\t\t\"module\": \"commonjs\",\n\t\t\"lib\": [\"ES2020\"],\n\t\t\"jsx\": \"react-jsx\",\n\t\t\"outDir\": \"./dist\",\n\t\t\"rootDir\": \"./src\",\n\t\t\"strict\": true,\n\t\t\"esModuleInterop\": true,\n\t\t\"skipLibCheck\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"declaration\": true,\n\t\t\"declarationMap\": true,\n\t\t\"sourceMap\": true,\n\t\t\"moduleResolution\": \"node\",\n\t\t\"allowSyntheticDefaultImports\": true,\n\t\t\"resolveJsonModule\": true,\n\t\t\"types\": [\"jest\", \"node\"]\n\t},\n\t\"include\": [\"src/**/*\"],\n\t\"exclude\": [\"node_modules\", \"dist\", \"**/*.test.ts\", \"**/*.test.tsx\", \"**/__tests__/**\"]\n}\n","packages/ts-client/USAGE.md":"# Ts-client Skill\n\nPath: `packages/ts-client/USAGE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/ts-client/USAGE.md)","packages/swift-client/README.md":"# swift-client Documentation\n\nPath: `packages/swift-client/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/swift-client/README.md)","packages/interface/CLAUDE.md":"# Interface Skill\n\nPath: `packages/interface/CLAUDE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/CLAUDE.md)","packages/interface/package.json":"# Interface Skill\n\nPath: `packages/interface/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/package.json)","packages/interface/SHARED-UI-STRATEGY.md":"# Interface Skill\n\nPath: `packages/interface/SHARED-UI-STRATEGY.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/SHARED-UI-STRATEGY.md)","packages/interface/tsconfig.json":"# Interface Skill\n\nPath: `packages/interface/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/tsconfig.json)","packages/config/app.tsconfig.json":"# Config Skill\n\nPath: `packages/config/app.tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/app.tsconfig.json)","packages/config/base.tsconfig.json":"# Config Skill\n\nPath: `packages/config/base.tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/base.tsconfig.json)","packages/config/package.json":"# Config Skill\n\nPath: `packages/config/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/package.json)","packages/config/tsconfig.json":"# Config Skill\n\nPath: `packages/config/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/tsconfig.json)","packages/assets/package.json":"# Assets Skill\n\nPath: `packages/assets/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/package.json)","packages/assets/README.md":"# assets Documentation\n\nPath: `packages/assets/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/README.md)","packages/interface/src/contributors.json":"# Src Skill\n\nPath: `packages/interface/src/contributors.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/contributors.json)","packages/assets/lottie/loading-pulse.json":"# Lottie Skill\n\nPath: `packages/assets/lottie/loading-pulse.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/lottie/loading-pulse.json)","packages/assets/sounds/README.md":"# sounds Documentation\n\nPath: `packages/assets/sounds/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/sounds/README.md)","packages/ts-client/tests/integration/README.md":"# integration Documentation\n\nPath: `packages/ts-client/tests/integration/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/ts-client/tests/integration/README.md)","packages/interface/src/Spacebot/VISION.md":"# Spacebot Skill\n\nPath: `packages/interface/src/Spacebot/VISION.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/Spacebot/VISION.md)","packages/assets/svgs/ext/icons.json":"# Ext Skill\n\nPath: `packages/assets/svgs/ext/icons.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/svgs/ext/icons.json)","packages/interface/src/components/modals/FileOperationModal.README.md":"# Modals Skill\n\nPath: `packages/interface/src/components/modals/FileOperationModal.README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/components/modals/FileOperationModal.README.md)","packages/interface/src/components/Inspector/variants/README.md":"# variants Documentation\n\nPath: `packages/interface/src/components/Inspector/variants/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/components/Inspector/variants/README.md)"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Spacedrive Core v2 Development Guide\n\n## Quick Start\n\n### Development Workflow\n\n1. Start daemon: `cargo run --bin sd-daemon`\n2. Make code changes\n3. Run tests: `cargo test`\n4. Rebuild and restart: `cargo run --bin sd-cli -- restart`\n5. Test via CLI: `cargo run --bin sd-cli -- <command>`\n\n### Common Commands\n\n```bash\ncargo build                              # Build the project\ncargo test                               # Run all tests\ncargo test <test_name>                   # Run specific test\ncargo clippy                             # Lint code\ncargo fmt                                # Format code\ncargo run --bin sd-cli -- <command>      # Run CLI (binary is sd-cli, not spacedrive)\n```\n\n### Common Mistakes\n\n- Running `spacedrive` instead of `sd-cli` (the binary name is `sd-cli`)\n- Forgetting to restart daemon after rebuilding\n- Using `println!` instead of `tracing` macros (`info!`, `debug!`, etc)\n- Implementing `Wire` manually instead of using `register_*` macros\n- Blocking the async runtime with synchronous I/O operations\n\n### Quick tips\n\n- On frontend apps, such as the interface in React, you must ALWAYS ensure type-safety based on the auto generated TypeScript types from `ts-client`. Never cast to as any or redefine backend types. our hooks are typesafe with correct input/output types, but sometimes you might need to access types directly from the `ts-client`.\n- If you have changed types on the backend that are public to the frontend (have `Type` derive), then you must regenerate the types using `cargo run --bin generate_typescript_types`\n- Read the `.mdx` files in /docs for context on any part of the app, they are kept up to date.\n\n### Current direction\n\n`docs/core/product-direction.mdx` defines the product boundary. Use it to decide\nwhether work belongs in Spacedrive before extending a subsystem.\n\nThe entries world is gone. The `entry` and `location` tables and everything\nhanging off them were dropped, and durable file state lives in the record table\nin `crates/store`; `docs/plans/2026-09-15-entries-final-drop.md` records how.\n`docs/plans/2026-08-22-source-convergence.md` is the register for making the\nnew world one thing: one store shape, one registry, one set of claims that\nmatch the code.\n\n- Spacedrive is a set of sources. A source has an origin, an ingest, and a\n  store; filesystem and adapter sources differ only in ingest. Anything that\n  makes those two diverge further is wrong.\n- New durable state belongs in a source store: the record table, or the\n  assertion half for anything no ingest can rebuild. `library.db` keeps only\n  the source registry, devices, volumes, spaces, and small shared state.\n- A source store has two halves. The generation is rebuildable for as long as\n  its origin answers; the assertion layer never is. Since the origin can stop\n  answering silently, no code path may assume a store can be thrown away and\n  rebuilt. `docs/core/design/source-durability.md` has the rest, including what\n  the assertion tables have to reserve for sync.\n- The `.tasks/` tree predates this work and is not its register. The status\n  table at the top of the execution plan remains the entries-teardown register.\n  Use `PROJECT_STATUS.md` as the short-term project-wide working context and\n  update it in the same commit as active work.\n\n## Architecture Overview\n\nSpacedrive uses daemon-client architecture. A single daemon process manages core functionality. Multiple clients (CLI, GraphQL server, desktop app) connect via Unix domain sockets.\n\n### CQRS and DDD Pattern\n\n- **Domain** (`src/domain/`): Core data structures and business logic (nouns)\n- **Operations** (`src/ops/`): Actions and queries (verbs)\n- **Actions**: State-changing operations (writes)\n- **Queries**: Data retrieval without state changes (reads)\n\n### Feature Module Structure\n\nEach feature lives in its own module under `src/ops/`. Example: `src/ops/files/share`\n\n```\nsrc/ops/files/share/\n├── action.rs      # State-changing logic\n├── input.rs       # Action input structures\n├── output.rs      # Action output structures\n└── job.rs         # Long-running job implementation (if needed)\n```\n\nComplete feature example:\n\n```rust\n// src/ops/files/share/input.rs\n#[derive(Debug, Serialize, Deserialize)]\npub struct ShareFileInput {\n    pub file_id: i32,\n    pub recipient: String,\n}\n\n// src/ops/files/share/output.rs\n#[derive(Debug, Serialize, Deserialize)]\npub struct ShareFileOutput {\n    pub share_id: String,\n    pub url: String,\n}\n\n// src/ops/files/share/action.rs\nuse super::{ShareFileInput, ShareFileOutput};\n\npub struct ShareFileAction;\n\ncrate::register_library_action!(ShareFileAction, \"files.share\");\n\nimpl Action for ShareFileAction {\n    type Input = ShareFileInput;\n    type Output = ShareFileOutput;\n\n    async fn run(input: Self::Input, ctx: &ActionContext) -> Result<Self::Output> {\n        // Implementation\n    }\n}\n```\n\n## Communication Architecture\n\nSpacedrive supports multiple communication patterns for different platforms and use cases.\n\n### Daemon-Client Communication (Tauri Desktop, CLI, Web)\n\nThe Tauri desktop app, CLI, and web interface connect to a daemon process via Unix domain sockets (or WebSockets for web). Communication uses JSON-RPC 2.0 with Wire method strings.\n\n**Registration Macros:**\n\nNever implement `Wire` manually. Use registration macros:\n\n```rust\n// Queries\ncrate::register_query!(NetworkStatusQuery, \"network.status\");\n// Generates: \"query:network.status\"\n\n// Library Actions\ncrate::register_library_action!(FileCopyAction, \"files.copy\");\n// Generates: \"action:files.copy.input\"\n\n// Core Actions\ncrate::register_core_action!(LibraryCreateAction, \"libraries.create\");\n// Generates: \"action:libraries.create.input\"\n```\n\n**Registry System:**\n\nThe `inventory` crate collects operations at compile time. When you use `register_core_query!`, `register_library_query!`, `register_core_action!` or `register_library_action!`, the operation automatically appears in the global `CORE_QUERIES`, `LIBRARY_QUERIES`, `CORE_ACTIONS` and `LIBRARY_ACTIONS` maps at startup. You never manually register operations.\n\nLocation: `core/src/infra/wire/registry.rs`\n\n### Tauri Desktop Development\n\nThe Tauri app (`apps/tauri/`) is the primary desktop application for Spacedrive. It connects to the daemon via the TypeScript client.\n\n**Development Workflow:**\n\n```bash\n# Install dependencies\nbun install\n\n# Run Tauri app in dev mode (auto-starts daemon)\ncd apps/tauri\nbun run tauri:dev\n\n# Build for production\nbun run tauri:build\n```\n\n**TypeScript Client:**\n\nThe TypeScript client (`packages/ts-client/`) is auto-generated from Rust types using Specta:\n\n```bash\n# Generate TypeScript types\ncargo run --bin generate_typescript_types\n```\n\n**Output:** `packages/ts-client/src/generated/types.ts`\n\n**Architecture:**\n\n```\nTauri App (React)\n    ↓\n@sd/ts-client (TypeScript)\n    ↓\nDaemon (Unix Socket / IPC)\n    ↓\nRpcServer (Rust)\n    ↓\nOperation Registry\n```\n\n### Native Prototypes (iOS, macOS)\n\n**Note:** iOS and macOS apps are experimental prototypes, not production apps.\n\nNative prototypes embed the core directly as a library via FFI rather than connecting to a daemon. These are located in `apps/ios/` and `apps/macos/` but are private and not documented for public use.\n\n**Swift Client Generation:**\n\nFor the prototypes, Swift types can be generated:\n\n```bash\ncargo run --bin generate_swift_types\n```\n\nOutput: `packages/swift-client/Sources/SpacedriveClient/`\n\n### Extension System (WASM)\n\nExtensions run as sandboxed WASM modules that interact with Spacedrive core via host functions. Extensions are distributed as compiled `.wasm` files.\n\n**Architecture:**\n\n```\nExtension.wasm (compiled Rust)\n    ↓\nspacedrive-sdk (Rust crate)\n    ↓\nHost Functions (FFI boundary)\n    ↓\nCore (VDFS, Jobs, AI, etc.)\n```\n\n**Key Components:**\n\n**SDK Location:** `crates/sdk/`\n\n- High-level Rust API abstracting FFI details\n- Procedural macros for extension definition\n- Type-safe job, model, and action builders\n\n**Extension Development:**\n\nExtensions use procedural macros to minimize boilerplate:\n\n```rust\nuse spacedrive_sdk::prelude::*;\n\n#[extension(\n    id = \"test-extension\",\n    name = \"Test Extension\",\n    version = \"0.1.0\",\n    jobs = [test_counter],\n)]\nstruct TestExtension;\n\n#[derive(Serialize, Deserialize, Default)]\npub struct CounterState {\n    pub current: u32,\n    pub target: u32,\n    pub processed: Vec<String>,\n}\n\n#[job(name = \"counter\")]\nfn test_counter(ctx: &JobContext, state: &mut CounterState) -> Result<()> {\n    ctx.log(&format!(\"Starting counter (current: {}, target: {})\",\n        state.current, state.target));\n\n    while state.current < state.target {\n        if ctx.check_interrupt_sync() {\n            ctx.checkpoint(state)?;\n            return Err(Error::OperationFailed(\"Interrupted\".into()));\n        }\n\n        state.current += 1;\n        ctx.report_progress(\n            state.current as f32 / state.target as f32,\n            &format!(\"Counted {}/{}\", state.current, state.target),\n        );\n\n        if state.current % 10 == 0 {\n            ctx.checkpoint(state)?;\n        }\n    }\n\n    Ok(())\n}\n```\n\n**Host Functions:**\n\nExtensions import minimal FFI functions:\n\n```rust\n#[link(wasm_import_module = \"spacedrive\")]\nextern \"C\" {\n    fn spacedrive_log(level: u32, msg_ptr: *const u8, msg_len: usize);\n    fn register_job(\n        job_name_ptr: *const u8,\n        job_name_len: u32,\n        export_fn_ptr: *const u8,\n        export_fn_len: u32,\n        resumable: u32,\n    ) -> i32;\n}\n```\n\n**Building Extensions:**\n\n```bash\n# From extension directory\ncargo build --target wasm32-unknown-unknown --release\n\n# Output: target/wasm32-unknown-unknown/release/extension_name.wasm\n```\n\n**Extension Capabilities:**\n\nExtensions can define:\n\n- Models: Data structures stored in `models` table (content-scoped, standalone, or entry-scoped)\n- Jobs: Long-running resumable operations\n- Actions: User-invoked operations with preview-commit workflow\n- Agents: Autonomous logic with memory and event handling\n- UI: Custom views via `ui_manifest.json`\n\n**Example Use Cases:**\n\n- Photos extension: Face detection, scene tagging, album organization\n- Finance extension: Receipt extraction, expense tracking\n- Research extension: Citation extraction, knowledge graphs\n\n**Key Benefits:**\n\n- Single `.wasm` file works on all platforms\n- True sandboxing (WASM isolation)\n- Resumable jobs with checkpointing\n- Type-safe API with procedural macros\n- No core modifications needed for new features\n\n**Documentation:**\n\n- `crates/sdk/API_REFERENCE.md` - SDK specification and API reference\n- `extensions/test-extension/` - Working example extension\n- `crates/sdk/` - SDK implementation\n- `crates/sdk-macros/` - SDK procedural macros\n\n**Status:** A daemon built with the `wasm` feature loads every extension under `<data dir>/extensions/` at startup and runs the jobs they register through the job manager, with checkpoints and resume (`core/src/infra/extension/README.md`). Only the job host functions exist; the rest of the SDK (VDFS, AI, models, tasks) has no host side yet.\n\n## Code Standards\n\n### Import Organization\n\nGroup imports with blank lines between groups:\n\n```rust\n// Standard library\nuse std::path::PathBuf;\nuse std::sync::Arc;\n\n// External crates\nuse serde::{Deserialize, Serialize};\nuse tokio::sync::RwLock;\n\n// Local modules\nuse crate::domain::library::Library;\nuse crate::ops::Action;\n```\n\n### Naming Conventions\n\n- Functions/variables: `snake_case`\n- Types: `PascalCase`\n- Constants: `SCREAMING_SNAKE_CASE`\n\n### Error Handling\n\nUse `Result<T, E>` for all fallible operations. Use `thiserror` for custom errors, `anyhow` for application errors.\n\n```rust\nuse thiserror::Error;\n\n#[derive(Error, Debug)]\npub enum ShareError {\n    #[error(\"File not found: {0}\")]\n    FileNotFound(i32),\n\n    #[error(\"Permission denied\")]\n    PermissionDenied,\n\n    #[error(\"Database error: {0}\")]\n    Database(#[from] sea_orm::DbErr),\n}\n\npub async fn share_file(id: i32) -> Result<String, ShareError> {\n    let file = find_file(id).await.ok_or(ShareError::FileNotFound(id))?;\n    // Implementation\n    Ok(share_url)\n}\n```\n\n### Async Code\n\n- Use `async/await` syntax\n- Prefer `tokio` primitives (`tokio::sync::RwLock`, `tokio::spawn`)\n- Avoid blocking operations (use `tokio::fs` not `std::fs`)\n- Use `tokio::task::spawn_blocking` for CPU-intensive work\n\n### Resumable Jobs\n\nStore job state within the job struct. Use `#[serde(skip)]` for non-persistent fields.\n\n```rust\n#[derive(Serialize, Deserialize)]\npub struct FileCopyJob {\n    pub source: PathBuf,\n    pub destination: PathBuf,\n    pub copied_files: Vec<PathBuf>,  // Persisted for resumability\n\n    #[serde(skip)]\n    pub progress_tx: Option<tokio::sync::mpsc::Sender<Progress>>,  // Not persisted\n}\n\nimpl Job for FileCopyJob {\n    async fn run(&mut self, ctx: &JobContext) -> Result<()> {\n        ctx.log().info(\"Starting file copy job\");\n\n        for file in &self.files_to_copy {\n            if self.copied_files.contains(file) {\n                continue;  // Skip already copied files on resume\n            }\n\n            copy_file(file).await?;\n            self.copied_files.push(file.clone());\n        }\n\n        Ok(())\n    }\n}\n```\n\n### Documentation\n\n**Core principle:** Explain WHY, not WHAT. Keep comments as short as possible. One sentence explaining rationale beats a paragraph restating code.\n\n**Module docs (`//!`):**\n- Add a title with `#` for the module name\n- Explain what the module does in plain language (not bullet points)\n- Include design rationale naturally in prose\n- Add runnable code examples showing usage\n\n````rust\n//! # File Sharing System\n//!\n//! `core::ops::files::share` provides temporary file sharing via signed URLs.\n//! Share links expire after 7 days by default to prevent indefinite access to\n//! private files. UUID v5 deterministic IDs ensure the same file generates\n//! consistent share URLs across devices without coordination.\n//!\n//! ## Example\n//! ```rust,no_run\n//! use spacedrive_core::ops::files::share::{ShareFileAction, ShareFileInput};\n//!\n//! let input = ShareFileInput { file_id: 123, recipient: \"user@example.com\" };\n//! let output = ShareFileAction::run(input, &ctx).await?;\n//! ```\n````\n\n**Function docs (`///`):**\n- First line: brief one-liner\n- Second paragraph: explain design rationale and why this exists\n- Document error handling philosophy when relevant\n- Explain non-obvious behavior and platform differences\n\n```rust\n/// Creates a share link with automatic expiration.\n///\n/// Share links use signed JWTs so the daemon can validate them without\n/// database lookups on every request. Expiration is enforced server-side\n/// to prevent timezone manipulation. Recipients without library access\n/// get read-only access to the specific file only.\n///\n/// Returns `ShareError::PermissionDenied` if the file is private and\n/// the recipient isn't a library member. The share is still created\n/// but marked inactive for audit logging.\npub async fn share_file(input: ShareFileInput) -> Result<ShareFileOutput>\n```\n\n**Inline comments:**\n- Delete comments that restate obvious code\n- Explain WHY for decisions, not WHAT the code does\n- Use one sentence when possible\n- Only expand for truly non-obvious consequences\n\n```rust\n// Good: explains WHY\n// Lowercase for case-insensitive search matching.\nlet ext = path.extension().map(|e| e.to_lowercase());\n\n// Bad: restates code\n// Extract file extension and convert to lowercase\nlet ext = path.extension().map(|e| e.to_lowercase());\n\n// Good: explains consequence\n// Keep the arena's UUID so a file keeps its identity when a source adopts it.\nlet uuid = arena.get_entry_uuid(path).unwrap_or_else(Uuid::now_v7);\n\n// Bad: verbose explanation of obvious behavior\n// UUID assignment strategy:\n// 1. First check if the arena has a UUID\n// 2. If not, generate a new one\nlet uuid = arena.get_entry_uuid(path).unwrap_or_else(Uuid::now_v7);\n```\n\n**Error handling comments:**\nExplain strategy and recovery, not just \"log and continue\".\n\n```rust\n// Good: explains recovery\n// Best-effort: continue with remaining moves, stale paths cleaned up on next reindex.\nErr(e) => ctx.log(format!(\"Failed to move: {}\", e)),\n\n// Bad: states the obvious\n// Log error but continue\nErr(e) => ctx.log(format!(\"Failed to move: {}\", e)),\n```\n\n**Platform-specific comments:**\nExplain consequences, not implementation blockers.\n\n```rust\n// Good: explains why and fallback\n#[cfg(windows)]\npub fn get_inode(_metadata: &std::fs::Metadata) -> Option<u64> {\n    // Windows file indices are unstable across reboots; fall back to path-only matching.\n    None\n}\n\n// Bad: over-explains implementation details\n#[cfg(windows)]\npub fn get_inode(_metadata: &std::fs::Metadata) -> Option<u64> {\n    // Windows doesn't have inodes.\n    // The method `file_index()` is unstable (issue #63010).\n    // Returning None is safe as the field is Optional.\n    None\n}\n```\n\n**Never use:**\n- Placeholder comments (\"for now\", \"TODO: extract this later\")\n- Markdown formatting (`**bold**`, `_italic_`) in code comments\n- ASCII diagrams (put those in `/docs/` if needed)\n- Section divider comments (`// ========== Section ==========`)\n- Comments explaining removed code during refactors\n\nTrack future work in GitHub issues, not code comments.\n\n### Formatting\n\nRun `cargo fmt` before committing. Tabs for indentation. No emojis.\n\n## Logging\n\n### Setup\n\nUse `tracing_subscriber` in main or examples:\n\n```rust\nuse tracing_subscriber::EnvFilter;\n\nfn main() {\n    tracing_subscriber::fmt()\n        .with_env_filter(\n            EnvFilter::try_from_default_env()\n                .unwrap_or_else(|_| EnvFilter::new(\"sd_core=info\"))\n        )\n        .init();\n}\n```\n\n## Writing Style\n\nThis applies to all documentation, code comments, and design documents.\n\nUse clear, simple language. Write short, impactful sentences. Use active voice. Focus on practical, actionable information.\n\nAddress the reader directly with \"you\" and \"your\". Support claims with data and examples when possible.\n\nAvoid these constructions:\n\n- Em dashes (use commas or periods)\n- \"Not only this, but also this\"\n- Metaphors and cliches\n- Generalizations\n- Setup language like \"in conclusion\"\n- Unnecessary adjectives and adverbs\n- Emojis, hashtags, markdown formatting in prose\n\nAvoid these words:\ncomprehensive, delve, utilize, harness, realm, tapestry, unlock, revolutionary, groundbreaking, remarkable, pivotal\n\n### Macros\n\nUse `tracing` macros, never `println!`:\n\n```rust\nuse tracing::{info, warn, error, debug};\n\ninfo!(\"Server started on port {}\", port);\ndebug!(file_id = %id, \"Processing file\");\nwarn!(error = %e, \"Retrying operation\");\nerror!(\"Failed to connect to database\");\n```\n\n### Job Logging\n\nUse `ctx.log()` in job implementations for automatic `job_id` tagging:\n\n```rust\nimpl Job for MyJob {\n    async fn run(&mut self, ctx: &JobContext) -> Result<()> {\n        ctx.log().info(\"Job started\");\n        ctx.log().debug!(progress = %self.progress, \"Processing\");\n        Ok(())\n    }\n}\n```\n\n### Log Levels\n\n- `debug`: Detailed flow for troubleshooting\n- `info`: User-relevant events (server start, job completion)\n- `warn`: Recoverable issues (retry, fallback)\n- `error`: Failures requiring attention\n\n### Environment Control\n\nUse `RUST_LOG` environment variable:\n\n```bash\nRUST_LOG=debug cargo run --bin sd-cli\nRUST_LOG=sd_core=trace cargo run\nRUST_LOG=sd_core::ops=debug cargo run\n```\n\n## Testing\n\n### Test Organization\n\n- Unit tests: Colocated in `#[cfg(test)]` modules\n- Integration tests: `tests/` directory at crate root\n\n```rust\n// src/ops/files/share/action.rs\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[tokio::test]\n    async fn test_share_file() {\n        let input = ShareFileInput {\n            file_id: 1,\n            recipient: \"test@example.com\".to_string(),\n        };\n\n        let output = share_file(input).await.unwrap();\n        assert!(!output.share_id.is_empty());\n    }\n}\n```\n\n### Running Tests\n\n```bash\ncargo test                    # All tests\ncargo test test_share_file    # Specific test\ncargo test --lib              # Library tests only\ncargo test -- --nocapture     # Show output\n```\n\n## Task Tracking\n\nRead `/PROJECT_STATUS.md` at the start of a session and update its Immediate\nregister when broad work starts, lands, becomes blocked, or changes owner. Keep\ndesign rationale and phase acceptance criteria in the associated plan. For\nanything still in the entries teardown, also update the status table at the top\nof `/docs/plans/2026-08-20-entries-teardown-execution.md` in the same commit.\n\n`/.tasks/` is frozen. It describes the entry-centric model that the teardown\nremoves, and was last updated in May. Do not add to it and do not run the\nvalidator against new work. The sections below document the retired system for\nhistorical reference only.\n\nSpacedrive uses a file-based task system in `/.tasks/` to track features, epics, and development work. All task files are version-controlled alongside the code.\n\n### When to Create Tasks\n\nCreate tasks for work that:\n\n- Introduces a new feature or capability\n- Refactors a significant system or module\n- Fixes a bug requiring architectural changes\n- Implements a whitepaper specification\n\nDo not create tasks for:\n\n- Routine code formatting or style fixes\n- Trivial bug fixes (single line changes)\n- Documentation updates to existing features\n- Dependency version bumps\n\n### Task Structure\n\nEach task is a Markdown file: `CATEGORY-###-title-slug.md`\n\n```yaml\n---\nid: CORE-042\ntitle: \"Implement file sharing API\"\nstatus: \"In Progress\"\nassignee: \"james\"\npriority: \"High\"\ntags: [\"core\", \"networking\"]\nwhitepaper: \"Section 4.2\" # And/or design_doc: DESIGN_DOC_NAME.md\n---\n\n## Description\nBrief overview of what needs to be done and why.\n\n## Implementation Steps\n- [ ] Create share action in src/ops/files/share\n- [ ] Add database schema for shares table\n- [ ] Implement expiration logic\n\n## Acceptance Criteria\n- Share links work across all platforms\n- Expired shares return 404\n- Tests cover edge cases\n```\n\n### Managing Tasks\n\n```bash\n# List your active tasks\ncargo run -p task-validator -- list --assignee \"yourname\" --status \"In Progress\"\n\n# List high priority tasks\ncargo run -p task-validator -- list --priority \"High\" --sort-by id\n\n# Validate before committing (automatic via git hook)\ncargo run -p task-validator -- validate\n```\n\n### Task Lifecycle\n\n1. Create task file in `/.tasks/` with `status: \"To Do\"`\n2. Update status to `\"In Progress\"` when you start work\n3. Complete implementation and tests\n4. Update status to `\"Done\"` and commit\n\nFull documentation: `/docs/core/task-tracking.mdx`\n\n## Debugging\n\n### Log Files\n\nJob logs live in the `job_logs` directory in the data folder root.\n\n### Daemon Restart\n\nAfter rebuilding, restart the daemon to use the latest code:\n\n```bash\ncargo build\ncargo run --bin sd-cli -- restart\n```\n\n### Verbose Logging\n\n```bash\nRUST_LOG=debug cargo run --bin sd-daemon\nRUST_LOG=sd_core::jobs=trace cargo run\n```\n\n## Documentation Locations\n\n- Core architecture: `/docs/core/`\n- Design docs and RFCs: `/docs/core/design/`\n- Application docs: `/docs/`\n- Task tracking: `/docs/core/task-tracking.mdx`\n","isInternal":false,"tokens":5462,"sizeBytes":23079},{"name":"AGENTS.md","path":"core/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/core/AGENTS.md","title":"core - AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# AGENTS.md - Spacedrive Core v2\n\n## Build/Test Commands\n\n- `cargo build` - Build the project\n- `cargo test` - Run all tests\n- `cargo test <test_name>` - Run specific test (e.g., `cargo test library_test`)\n- `cargo clippy` - Lint code\n- `cargo fmt` - Format code\n- `cargo run --bin sd-cli -- <command>` - Run CLI (note: binary is `sd-cli`, not `spacedrive`)\n\n## Code Style\n\n- **Imports**: Group std, external crates, then local modules with blank lines between\n- **Formatting**: Use `cargo fmt` - tabs for indentation, snake_case for variables/functions. DO NOT use emojis at all.\n- **Types**: Explicit types preferred, use `Result<T, E>` for error handling with `thiserror`\n- **Naming**: snake_case for functions/variables, PascalCase for types, SCREAMING_SNAKE_CASE for constants\n- **Error Handling**: Use `Result` types, `thiserror` for custom errors, `anyhow` for application errors\n- **Async**: Use `async/await`, prefer `tokio` primitives, avoid blocking operations\n- **Resumable Jobs**: For long-running jobs that need to be resumable, store the job's state within the job's struct itself. Use `#[serde(skip)]` for fields that should not be persisted. For example, in a file copy job, the list of already copied files can be stored to allow the job to resume from where it left off.\n- **Documentation**: Use `//!` for module docs, `///` for public items, include examples\n- **Architecture**: Follow a Command Query Responsibility Segregation (CQRS) and Domain-Driven Design (DDD) pattern.\n  - **Domain**: Core data structures and business logic are located in `src/domain/`. These are the \"nouns\" of your system.\n  - **Operations**: State-changing commands (actions) and data-retrieving queries are located in `src/ops/`. These are the \"verbs\" of your system.\n  - **Actions**: Operations that modify the state of the application. They should be self-contained and transactional.\n  - **Queries**: Operations that retrieve data without modifying state. They should be efficient and optimized for reading.\n- **Feature Modules**: Each new feature should be implemented in its own module within the `src/ops/` directory. For example, a new \"share\" feature would live in `src/ops/files/share`. Each feature module should contain the following files where applicable:\n  - `action.rs`: The main logic for the state-changing operation.\n  - `input.rs`: Data structures for the action's input.\n  - `output.rs`: Data structures for the action's output.\n  - `job.rs`: If the action is long-running, the job implementation.\n- **Database**: Use SeaORM entities, async queries, proper error propagation\n- **Comments**: Minimal inline comments, focus on why not what, no TODO comments in production code\n\n## Daemon Architecture\n\nSpacedrive uses a **daemon-client architecture** where a single daemon process manages the core functionality and multiple client applications (CLI, GraphQL server, desktop app) connect to it via Unix domain sockets.\n\n> **For detailed daemon architecture documentation, see [/docs/core/daemon.md](/docs/core/daemon.md)**\n\n### **The `Wire` Trait**\n\nAll actions and queries must implement the `Wire` trait to enable type-safe client-daemon communication:\n\n```rust\npub trait Wire {\n    const METHOD: &'static str;\n}\n```\n\n### **Registration Macros**\n\nInstead of manually implementing `Wire`, use these registration macros that automatically:\n\n1. Implement the `Wire` trait with the correct method string\n2. Register the operation in the global registry using the `inventory` crate\n\n**For Queries:**\n\n```rust\ncrate::register_query!(NetworkStatusQuery, \"network.status\");\n// Generates method: \"query:network.status\"\n```\n\n**For Library Actions:**\n\n```rust\ncrate::register_library_action!(FileCopyAction, \"files.copy\");\n// Generates method: \"action:files.copy.input\"\n```\n\n**For Core Actions:**\n\n```rust\ncrate::register_core_action!(LibraryCreateAction, \"libraries.create\");\n// Generates method: \"action:libraries.create.input\"\n```\n\n### **Registry System**\n\n- **Location**: `core/src/ops/registry.rs`\n- **Mechanism**: Uses the `inventory` crate for compile-time registration\n- **Global Maps**: `QUERIES` and `ACTIONS` hashmaps populated at startup\n- **Handler Functions**: Generic handlers that decode payloads, execute operations, and encode results\n\n## Logging Standards\n\n- **Setup**: Use `tracing_subscriber::fmt()` with env filter for structured logging\n- **Macros**: Use `info!`, `warn!`, `error!`, `debug!` from `tracing` crate, not `println!`\n- **Job Context**: Use `ctx.log()` in jobs for job-specific logging with automatic job_id tagging\n- **Structured**: Include relevant context fields: `debug!(job_id = %self.id, \"message\")`\n- **Levels**: debug for detailed flow, info for user-relevant events, warn for recoverable issues, error for failures\n- **Format**: `tracing_subscriber::fmt().with_env_filter(env_filter).init()` in main/examples\n- **Environment**: Respect `RUST_LOG` env var, fallback to module-specific filters like `sd_core=info`\n\n## Documentation\n\n- **Core level docs**: Live in `/docs/core` - comprehensive architecture and implementation guides\n- **Core design docs**: Live in `/docs/core/design` - planning documents, RFCs, and design decisions\n- **Application level docs**: Live in `/docs`\n- **Code docs**: Use `///` for public APIs, `//!` for module overviews, include examples\n\n## Debug Instructions\n\n- You can view the logs of a job in the job_logs directory in the root of the data folder\n- When testing the CLI, after compiling you must first use the `restart` command to ensure the Spacedrive daemon is using the latest build.\n","isInternal":false,"tokens":1298,"sizeBytes":5571},{"name":"manifest.json","path":"extensions/photos/manifest.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/extensions/photos/manifest.json","title":"Photos","category":"anthropic-skill","format":"json","content":"{\n\t\"id\": \"com.spacedrive.photos\",\n\t\"name\": \"Photos\",\n\t\"version\": \"1.0.0\",\n\t\"description\": \"Advanced photo management with AI-powered face recognition, place identification, and intelligent organization\",\n\t\"author\": \"Spacedrive\",\n\t\"wasm_file\": \"photos.wasm\",\n\t\"min_core_version\": \"2.0.0\",\n\t\"required_features\": [\n\t\t\"exif_extraction\",\n\t\t\"ai_models\",\n\t\t\"semantic_search\"\n\t],\n\t\"permissions\": {\n\t\t\"read_records\": {\n\t\t\t\"glob\": \"**/*.{jpg,jpeg,png,heic,heif,raw,cr2,nef,dng,webp}\"\n\t\t},\n\t\t\"read_sidecars\": [\n\t\t\t\"exif\",\n\t\t\t\"thumbnail\"\n\t\t],\n\t\t\"write_sidecars\": [\n\t\t\t\"faces\",\n\t\t\t\"places\",\n\t\t\t\"scene\",\n\t\t\t\"aesthetics\"\n\t\t],\n\t\t\"write_tags\": true,\n\t\t\"write_custom_fields\": [\n\t\t\t\"photos\"\n\t\t],\n\t\t\"dispatch_jobs\": true,\n\t\t\"use_models\": [\n\t\t\t{\n\t\t\t\t\"category\": \"face_detection\",\n\t\t\t\t\"preference\": \"local\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"category\": \"scene_classification\",\n\t\t\t\t\"preference\": \"local\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"category\": \"llm\",\n\t\t\t\t\"preference\": \"local\"\n\t\t\t}\n\t\t]\n\t},\n\t\"models\": [\n\t\t{\n\t\t\t\"name\": \"photos_face_detection_v1\",\n\t\t\t\"category\": \"face_detection\",\n\t\t\t\"source\": {\n\t\t\t\t\"type\": \"download\",\n\t\t\t\t\"url\": \"https://models.spacedrive.com/photos/face_detection_v1.onnx\",\n\t\t\t\t\"sha256\": \"placeholder_hash\",\n\t\t\t\t\"size_mb\": 12\n\t\t\t},\n\t\t\t\"description\": \"RetinaFace model for face detection\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"photos_scene_v1\",\n\t\t\t\"category\": \"scene_classification\",\n\t\t\t\"source\": {\n\t\t\t\t\"type\": \"download\",\n\t\t\t\t\"url\": \"https://models.spacedrive.com/photos/scene_resnet50.onnx\",\n\t\t\t\t\"sha256\": \"placeholder_hash\",\n\t\t\t\t\"size_mb\": 95\n\t\t\t},\n\t\t\t\"description\": \"ResNet50 trained on Places365 dataset\"\n\t\t}\n\t],\n\t\"kinds\": [\n\t\t{\n\t\t\t\"name\": \"raw\",\n\t\t\t\"display_name\": \"RAW photo\",\n\t\t\t\"parent\": \"image\",\n\t\t\t\"extensions\": [\n\t\t\t\t\"cr2\",\n\t\t\t\t\"cr3\",\n\t\t\t\t\"nef\",\n\t\t\t\t\"arw\",\n\t\t\t\t\"dng\",\n\t\t\t\t\"raf\",\n\t\t\t\t\"orf\",\n\t\t\t\t\"rw2\",\n\t\t\t\t\"pef\",\n\t\t\t\t\"dcr\",\n\t\t\t\t\"srw\"\n\t\t\t],\n\t\t\t\"mime_types\": [\n\t\t\t\t\"image/x-canon-cr2\",\n\t\t\t\t\"image/x-canon-cr3\",\n\t\t\t\t\"image/x-nikon-nef\",\n\t\t\t\t\"image/x-sony-arw\",\n\t\t\t\t\"image/x-adobe-dng\",\n\t\t\t\t\"image/x-fuji-raf\",\n\t\t\t\t\"image/x-olympus-orf\",\n\t\t\t\t\"image/x-panasonic-rw2\",\n\t\t\t\t\"image/x-pentax-pef\",\n\t\t\t\t\"image/x-kodak-dcr\",\n\t\t\t\t\"image/x-samsung-srw\"\n\t\t\t],\n\t\t\t\"magic\": [\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"49 49 2A 00\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"4D 4D 00 2A\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"46 55 4A 49 46 49 4C 4D\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"49 49 52 4F\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"49 49 55 00\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"preview\": {\n\t\t\t\t\"renderer\": \"image\"\n\t\t\t}\n\t\t}\n\t]\n}\n","frontmatter":{"id":"com.spacedrive.photos","name":"Photos","version":"1.0.0","description":"Advanced photo management with AI-powered face recognition, place identification, and intelligent organization","author":"Spacedrive","wasm_file":"photos.wasm","min_core_version":"2.0.0","required_features":["exif_extraction","ai_models","semantic_search"],"permissions":{"read_records":{"glob":"**/*.{jpg,jpeg,png,heic,heif,raw,cr2,nef,dng,webp}"},"read_sidecars":["exif","thumbnail"],"write_sidecars":["faces","places","scene","aesthetics"],"write_tags":true,"write_custom_fields":["photos"],"dispatch_jobs":true,"use_models":[{"category":"face_detection","preference":"local"},{"category":"scene_classification","preference":"local"},{"category":"llm","preference":"local"}]},"models":[{"name":"photos_face_detection_v1","category":"face_detection","source":{"type":"download","url":"https://models.spacedrive.com/photos/face_detection_v1.onnx","sha256":"placeholder_hash","size_mb":12},"description":"RetinaFace model for face detection"},{"name":"photos_scene_v1","category":"scene_classification","source":{"type":"download","url":"https://models.spacedrive.com/photos/scene_resnet50.onnx","sha256":"placeholder_hash","size_mb":95},"description":"ResNet50 trained on Places365 dataset"}],"kinds":[{"name":"raw","display_name":"RAW photo","parent":"image","extensions":["cr2","cr3","nef","arw","dng","raf","orf","rw2","pef","dcr","srw"],"mime_types":["image/x-canon-cr2","image/x-canon-cr3","image/x-nikon-nef","image/x-sony-arw","image/x-adobe-dng","image/x-fuji-raf","image/x-olympus-orf","image/x-panasonic-rw2","image/x-pentax-pef","image/x-kodak-dcr","image/x-samsung-srw"],"magic":[{"pattern":"49 49 2A 00","offset":0},{"pattern":"4D 4D 00 2A","offset":0},{"pattern":"46 55 4A 49 46 49 4C 4D","offset":0},{"pattern":"49 49 52 4F","offset":0},{"pattern":"49 49 55 00","offset":0}],"preview":{"renderer":"image"}}]},"isInternal":false,"tokens":940,"sizeBytes":2492},{"name":"ui_manifest.json","path":"extensions/photos/ui_manifest.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/extensions/photos/ui_manifest.json","title":"Photos Skill","category":"anthropic-skill","format":"json","content":"{\n\t\"sidebar\": {\n\t\t\"section\": \"Photos\",\n\t\t\"icon\": \"assets/photos_icon.svg\",\n\t\t\"order\": 10,\n\t\t\"views\": [\n\t\t\t{\n\t\t\t\t\"id\": \"library\",\n\t\t\t\t\"title\": \"Library\",\n\t\t\t\t\"component\": \"photo_grid\",\n\t\t\t\t\"query\": \"list_all_photos\",\n\t\t\t\t\"default\": true\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"albums\",\n\t\t\t\t\"title\": \"Albums\",\n\t\t\t\t\"component\": \"album_grid\",\n\t\t\t\t\"query\": \"list_albums\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"people\",\n\t\t\t\t\"title\": \"People\",\n\t\t\t\t\"component\": \"person_cluster_grid\",\n\t\t\t\t\"query\": \"list_people\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"places\",\n\t\t\t\t\"title\": \"Places\",\n\t\t\t\t\"component\": \"map_view\",\n\t\t\t\t\"query\": \"list_places\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"moments\",\n\t\t\t\t\"title\": \"Moments\",\n\t\t\t\t\"component\": \"moment_timeline\",\n\t\t\t\t\"query\": \"list_moments\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"favorites\",\n\t\t\t\t\"title\": \"Favorites\",\n\t\t\t\t\"component\": \"photo_grid\",\n\t\t\t\t\"query\": \"tags LIKE '#favorite'\",\n\t\t\t\t\"icon\": \"heart\"\n\t\t\t}\n\t\t]\n\t},\n\t\"context_menu\": [\n\t\t{\n\t\t\t\"id\": \"add_to_album\",\n\t\t\t\"label\": \"Add to Album...\",\n\t\t\t\"icon\": \"plus_circle\",\n\t\t\t\"action\": \"add_to_album\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t],\n\t\t\t\"keyboard_shortcut\": \"cmd+shift+a\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"identify_person\",\n\t\t\t\"label\": \"This is...\",\n\t\t\t\"icon\": \"person\",\n\t\t\t\"action\": \"identify_person\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t],\n\t\t\t\"requires\": \"has_detected_faces\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"set_as_cover\",\n\t\t\t\"label\": \"Set as Album Cover\",\n\t\t\t\"icon\": \"star\",\n\t\t\t\"action\": \"set_album_cover\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t],\n\t\t\t\"context\": \"album_view\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"hide_photo\",\n\t\t\t\"label\": \"Hide\",\n\t\t\t\"icon\": \"eye_slash\",\n\t\t\t\"action\": \"hide_photo\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t]\n\t\t}\n\t],\n\t\"toolbar_actions\": [\n\t\t{\n\t\t\t\"id\": \"analyze_location\",\n\t\t\t\"label\": \"Analyze for Faces\",\n\t\t\t\"icon\": \"sparkles\",\n\t\t\t\"action\": \"analyze_photos_batch\",\n\t\t\t\"context\": \"location_view\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"identify_places\",\n\t\t\t\"label\": \"Identify Places\",\n\t\t\t\"icon\": \"map_pin\",\n\t\t\t\"action\": \"identify_places_in_location\",\n\t\t\t\"context\": \"location_view\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"create_moment\",\n\t\t\t\"label\": \"Create Moment\",\n\t\t\t\"icon\": \"calendar\",\n\t\t\t\"action\": \"create_moments_from_selection\"\n\t\t}\n\t],\n\t\"file_viewers\": [\n\t\t{\n\t\t\t\"id\": \"photo_viewer\",\n\t\t\t\"bundle\": \"ui/photo_viewer.js\",\n\t\t\t\"mime_types\": [\n\t\t\t\t\"image/jpeg\",\n\t\t\t\t\"image/png\",\n\t\t\t\t\"image/heic\",\n\t\t\t\t\"image/webp\"\n\t\t\t],\n\t\t\t\"component\": \"photo_viewer\",\n\t\t\t\"features\": {\n\t\t\t\t\"slideshow\": true,\n\t\t\t\t\"metadata_panel\": true,\n\t\t\t\t\"face_indicators\": true,\n\t\t\t\t\"related_photos\": true\n\t\t\t}\n\t\t}\n\t],\n\t\"search_filters\": [\n\t\t{\n\t\t\t\"id\": \"has_people\",\n\t\t\t\"label\": \"Has People\",\n\t\t\t\"type\": \"boolean\",\n\t\t\t\"query\": \"tags LIKE '#person:%'\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"location\",\n\t\t\t\"label\": \"Location\",\n\t\t\t\"type\": \"place_picker\",\n\t\t\t\"query_template\": \"tags LIKE '#place:{value}'\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"date_range\",\n\t\t\t\"label\": \"Date\",\n\t\t\t\"type\": \"date_range\",\n\t\t\t\"query_template\": \"exif.taken_at BETWEEN '{start}' AND '{end}'\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"camera\",\n\t\t\t\"label\": \"Camera\",\n\t\t\t\"type\": \"select\",\n\t\t\t\"options_query\": \"SELECT DISTINCT exif.camera_model FROM photos\",\n\t\t\t\"query_template\": \"exif.camera_model = '{value}'\"\n\t\t}\n\t]\n}\n","frontmatter":{"sidebar":{"section":"Photos","icon":"assets/photos_icon.svg","order":10,"views":[{"id":"library","title":"Library","component":"photo_grid","query":"list_all_photos","default":true},{"id":"albums","title":"Albums","component":"album_grid","query":"list_albums"},{"id":"people","title":"People","component":"person_cluster_grid","query":"list_people"},{"id":"places","title":"Places","component":"map_view","query":"list_places"},{"id":"moments","title":"Moments","component":"moment_timeline","query":"list_moments"},{"id":"favorites","title":"Favorites","component":"photo_grid","query":"tags LIKE '#favorite'","icon":"heart"}]},"context_menu":[{"id":"add_to_album","label":"Add to Album...","icon":"plus_circle","action":"add_to_album","applies_to":["image/*"],"keyboard_shortcut":"cmd+shift+a"},{"id":"identify_person","label":"This is...","icon":"person","action":"identify_person","applies_to":["image/*"],"requires":"has_detected_faces"},{"id":"set_as_cover","label":"Set as Album Cover","icon":"star","action":"set_album_cover","applies_to":["image/*"],"context":"album_view"},{"id":"hide_photo","label":"Hide","icon":"eye_slash","action":"hide_photo","applies_to":["image/*"]}],"toolbar_actions":[{"id":"analyze_location","label":"Analyze for Faces","icon":"sparkles","action":"analyze_photos_batch","context":"location_view"},{"id":"identify_places","label":"Identify Places","icon":"map_pin","action":"identify_places_in_location","context":"location_view"},{"id":"create_moment","label":"Create Moment","icon":"calendar","action":"create_moments_from_selection"}],"file_viewers":[{"id":"photo_viewer","bundle":"ui/photo_viewer.js","mime_types":["image/jpeg","image/png","image/heic","image/webp"],"component":"photo_viewer","features":{"slideshow":true,"metadata_panel":true,"face_indicators":true,"related_photos":true}}],"search_filters":[{"id":"has_people","label":"Has People","type":"boolean","query":"tags LIKE '#person:%'"},{"id":"location","label":"Location","type":"place_picker","query_template":"tags LIKE '#place:{value}'"},{"id":"date_range","label":"Date","type":"date_range","query_template":"exif.taken_at BETWEEN '{start}' AND '{end}'"},{"id":"camera","label":"Camera","type":"select","options_query":"SELECT DISTINCT exif.camera_model FROM photos","query_template":"exif.camera_model = '{value}'"}]},"isInternal":false,"tokens":1044,"sizeBytes":3035},{"name":"manifest.json","path":"extensions/test-extension/manifest.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/extensions/test-extension/manifest.json","title":"Test Extension","category":"anthropic-skill","format":"json","content":"{\n\t\"id\": \"test-extension\",\n\t\"name\": \"Test Extension\",\n\t\"version\": \"0.1.0\",\n\t\"description\": \"Minimal extension demonstrating beautiful SDK API\",\n\t\"author\": \"Spacedrive Team\",\n\t\"homepage\": \"https://spacedrive.com\",\n\t\"wasm_file\": \"test_extension.wasm\",\n\t\"permissions\": {\n\t\t\"read_records\": {},\n\t\t\"write_sidecars\": [\n\t\t\t\"digest\"\n\t\t],\n\t\t\"write_tags\": true,\n\t\t\"write_custom_fields\": [\n\t\t\t\"test\"\n\t\t],\n\t\t\"dispatch_jobs\": true\n\t},\n\t\"kinds\": [\n\t\t{\n\t\t\t\"name\": \"fake\",\n\t\t\t\"display_name\": \"Fake file\",\n\t\t\t\"parent\": \"text\",\n\t\t\t\"extensions\": [\n\t\t\t\t\"fake\"\n\t\t\t],\n\t\t\t\"magic\": [\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"46 41 4B 45\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"preview\": {\n\t\t\t\t\"viewer\": \"fake_viewer\"\n\t\t\t}\n\t\t}\n\t]\n}\n","frontmatter":{"id":"test-extension","name":"Test Extension","version":"0.1.0","description":"Minimal extension demonstrating beautiful SDK API","author":"Spacedrive Team","homepage":"https://spacedrive.com","wasm_file":"test_extension.wasm","permissions":{"read_records":{},"write_sidecars":["digest"],"write_tags":true,"write_custom_fields":["test"],"dispatch_jobs":true},"kinds":[{"name":"fake","display_name":"Fake file","parent":"text","extensions":["fake"],"magic":[{"pattern":"46 41 4B 45","offset":0}],"preview":{"viewer":"fake_viewer"}}]},"isInternal":false,"tokens":240,"sizeBytes":683},{"name":"loading-pulse.json","path":"packages/assets/lottie/loading-pulse.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/lottie/loading-pulse.json","title":"Lottie Skill","category":"anthropic-skill","format":"json","content":"# Lottie Skill\n\nPath: `packages/assets/lottie/loading-pulse.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/lottie/loading-pulse.json)","tokens":300,"sizeBytes":202},{"name":"package.json","path":"packages/assets/package.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/package.json","title":"Assets Skill","category":"anthropic-skill","format":"json","content":"# Assets Skill\n\nPath: `packages/assets/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/package.json)","tokens":300,"sizeBytes":176},{"name":"icons.json","path":"packages/assets/svgs/ext/icons.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/svgs/ext/icons.json","title":"Ext Skill","category":"anthropic-skill","format":"json","content":"# Ext Skill\n\nPath: `packages/assets/svgs/ext/icons.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/svgs/ext/icons.json)","tokens":300,"sizeBytes":187},{"name":"app.tsconfig.json","path":"packages/config/app.tsconfig.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/app.tsconfig.json","title":"Config Skill","category":"anthropic-skill","format":"json","content":"# Config Skill\n\nPath: `packages/config/app.tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/app.tsconfig.json)","tokens":300,"sizeBytes":186},{"name":"base.tsconfig.json","path":"packages/config/base.tsconfig.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/base.tsconfig.json","title":"Config Skill","category":"anthropic-skill","format":"json","content":"# Config Skill\n\nPath: `packages/config/base.tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/base.tsconfig.json)","tokens":300,"sizeBytes":188},{"name":"package.json","path":"packages/config/package.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/package.json","title":"Config Skill","category":"anthropic-skill","format":"json","content":"# Config Skill\n\nPath: `packages/config/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/package.json)","tokens":300,"sizeBytes":176},{"name":"tsconfig.json","path":"packages/config/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/tsconfig.json","title":"Config Skill","category":"anthropic-skill","format":"json","content":"# Config Skill\n\nPath: `packages/config/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/tsconfig.json)","tokens":300,"sizeBytes":178},{"name":"CLAUDE.md","path":"packages/interface/CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/CLAUDE.md","title":"Interface Skill","category":"anthropic-skill","format":"markdown","content":"# Interface Skill\n\nPath: `packages/interface/CLAUDE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/CLAUDE.md)","tokens":300,"sizeBytes":179},{"name":"package.json","path":"packages/interface/package.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/package.json","title":"Interface Skill","category":"anthropic-skill","format":"json","content":"# Interface Skill\n\nPath: `packages/interface/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/package.json)","tokens":300,"sizeBytes":185},{"name":"SHARED-UI-STRATEGY.md","path":"packages/interface/SHARED-UI-STRATEGY.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/SHARED-UI-STRATEGY.md","title":"Interface Skill","category":"anthropic-skill","format":"markdown","content":"# Interface Skill\n\nPath: `packages/interface/SHARED-UI-STRATEGY.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/SHARED-UI-STRATEGY.md)","tokens":300,"sizeBytes":203},{"name":"FileOperationModal.README.md","path":"packages/interface/src/components/modals/FileOperationModal.README.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/components/modals/FileOperationModal.README.md","title":"Modals Skill","category":"anthropic-skill","format":"markdown","content":"# Modals Skill\n\nPath: `packages/interface/src/components/modals/FileOperationModal.README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/components/modals/FileOperationModal.README.md)","tokens":300,"sizeBytes":258},{"name":"contributors.json","path":"packages/interface/src/contributors.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/contributors.json","title":"Src Skill","category":"anthropic-skill","format":"json","content":"# Src Skill\n\nPath: `packages/interface/src/contributors.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/contributors.json)","tokens":300,"sizeBytes":197},{"name":"VISION.md","path":"packages/interface/src/Spacebot/VISION.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/Spacebot/VISION.md","title":"Spacebot Skill","category":"anthropic-skill","format":"markdown","content":"# Spacebot Skill\n\nPath: `packages/interface/src/Spacebot/VISION.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/Spacebot/VISION.md)","tokens":300,"sizeBytes":204},{"name":"tsconfig.json","path":"packages/interface/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/tsconfig.json","title":"Interface Skill","category":"anthropic-skill","format":"json","content":"# Interface Skill\n\nPath: `packages/interface/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/tsconfig.json)","tokens":300,"sizeBytes":187},{"name":"package.json","path":"packages/ts-client/package.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/ts-client/package.json","title":"@sd/ts-client","category":"anthropic-skill","format":"json","content":"{\n\t\"name\": \"@sd/ts-client\",\n\t\"version\": \"0.1.0\",\n\t\"description\": \"Type-safe TypeScript client for Spacedrive daemon\",\n\t\"main\": \"dist/index.js\",\n\t\"types\": \"dist/index.d.ts\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./src/index.ts\",\n\t\t\t\"default\": \"./src/index.ts\"\n\t\t},\n\t\t\"./generated/types\": {\n\t\t\t\"types\": \"./src/generated/types.ts\",\n\t\t\t\"default\": \"./src/generated/types.ts\"\n\t\t},\n\t\t\"./src/generated/types\": {\n\t\t\t\"types\": \"./src/generated/types.ts\",\n\t\t\t\"default\": \"./src/generated/types.ts\"\n\t\t},\n\t\t\"./hooks\": {\n\t\t\t\"types\": \"./src/hooks/index.ts\",\n\t\t\t\"default\": \"./src/hooks/index.ts\"\n\t\t},\n\t\t\"./hooks/useClient\": {\n\t\t\t\"types\": \"./src/hooks/useClient.tsx\",\n\t\t\t\"default\": \"./src/hooks/useClient.tsx\"\n\t\t},\n\t\t\"./hooks/useNormalizedQuery\": {\n\t\t\t\"types\": \"./src/hooks/useNormalizedQuery.ts\",\n\t\t\t\"default\": \"./src/hooks/useNormalizedQuery.ts\"\n\t\t},\n\t\t\"./src/hooks/useClient\": {\n\t\t\t\"types\": \"./src/hooks/useClient.tsx\",\n\t\t\t\"default\": \"./src/hooks/useClient.tsx\"\n\t\t},\n\t\t\"./src/hooks/useNormalizedQuery\": {\n\t\t\t\"types\": \"./src/hooks/useNormalizedQuery.ts\",\n\t\t\t\"default\": \"./src/hooks/useNormalizedQuery.ts\"\n\t\t}\n\t},\n\t\"scripts\": {\n\t\t\"build\": \"tsc\",\n\t\t\"dev\": \"tsc --watch\",\n\t\t\"test\": \"jest\",\n\t\t\"generate-types\": \"cargo run --bin generate_typescript_types --manifest-path ../../core/Cargo.toml\"\n\t},\n\t\"keywords\": [\n\t\t\"spacedrive\",\n\t\t\"client\",\n\t\t\"typescript\",\n\t\t\"daemon\"\n\t],\n\t\"author\": \"Spacedrive\",\n\t\"license\": \"Apache-2.0\",\n\t\"peerDependencies\": {\n\t\t\"@tanstack/react-query\": \"^5.0.0\",\n\t\t\"react\": \"^18.0.0 || ^19.0.0\"\n\t},\n\t\"peerDependenciesMeta\": {\n\t\t\"@tanstack/react-query\": {\n\t\t\t\"optional\": true\n\t\t},\n\t\t\"react\": {\n\t\t\t\"optional\": true\n\t\t}\n\t},\n\t\"devDependencies\": {\n\t\t\"@happy-dom/global-registrator\": \"^15.11.0\",\n\t\t\"@testing-library/jest-dom\": \"^6.0.0\",\n\t\t\"@testing-library/react\": \"^16.0.0\",\n\t\t\"@types/jest\": \"^29.0.0\",\n\t\t\"@types/node\": \"^20.0.0\",\n\t\t\"happy-dom\": \"^15.11.0\",\n\t\t\"jest\": \"^29.0.0\",\n\t\t\"jest-environment-jsdom\": \"^29.0.0\",\n\t\t\"jsdom\": \"^27.2.0\",\n\t\t\"ts-jest\": \"^29.0.0\",\n\t\t\"typescript\": \"^5.0.0\"\n\t},\n\t\"dependencies\": {\n\t\t\"@types/ws\": \"^8.0.0\",\n\t\t\"@sd/assets\": \"workspace:*\",\n\t\t\"ws\": \"^8.0.0\",\n\t\t\"zustand\": \"^5.0.8\",\n\t\t\"ts-deepmerge\": \"^7.0.1\",\n\t\t\"tiny-invariant\": \"^1.3.3\",\n\t\t\"valibot\": \"^1.0.0\",\n\t\t\"type-fest\": \"^4.30.0\"\n\t},\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"src/**/*\"\n\t]\n}\n","frontmatter":{"name":"@sd/ts-client","version":"0.1.0","description":"Type-safe TypeScript client for Spacedrive daemon","main":"dist/index.js","types":"dist/index.d.ts","exports":{".":{"types":"./src/index.ts","default":"./src/index.ts"},"./generated/types":{"types":"./src/generated/types.ts","default":"./src/generated/types.ts"},"./src/generated/types":{"types":"./src/generated/types.ts","default":"./src/generated/types.ts"},"./hooks":{"types":"./src/hooks/index.ts","default":"./src/hooks/index.ts"},"./hooks/useClient":{"types":"./src/hooks/useClient.tsx","default":"./src/hooks/useClient.tsx"},"./hooks/useNormalizedQuery":{"types":"./src/hooks/useNormalizedQuery.ts","default":"./src/hooks/useNormalizedQuery.ts"},"./src/hooks/useClient":{"types":"./src/hooks/useClient.tsx","default":"./src/hooks/useClient.tsx"},"./src/hooks/useNormalizedQuery":{"types":"./src/hooks/useNormalizedQuery.ts","default":"./src/hooks/useNormalizedQuery.ts"}},"scripts":{"build":"tsc","dev":"tsc --watch","test":"jest","generate-types":"cargo run --bin generate_typescript_types --manifest-path ../../core/Cargo.toml"},"keywords":["spacedrive","client","typescript","daemon"],"author":"Spacedrive","license":"Apache-2.0","peerDependencies":{"@tanstack/react-query":"^5.0.0","react":"^18.0.0 || ^19.0.0"},"peerDependenciesMeta":{"@tanstack/react-query":{"optional":true},"react":{"optional":true}},"devDependencies":{"@happy-dom/global-registrator":"^15.11.0","@testing-library/jest-dom":"^6.0.0","@testing-library/react":"^16.0.0","@types/jest":"^29.0.0","@types/node":"^20.0.0","happy-dom":"^15.11.0","jest":"^29.0.0","jest-environment-jsdom":"^29.0.0","jsdom":"^27.2.0","ts-jest":"^29.0.0","typescript":"^5.0.0"},"dependencies":{"@types/ws":"^8.0.0","@sd/assets":"workspace:*","ws":"^8.0.0","zustand":"^5.0.8","ts-deepmerge":"^7.0.1","tiny-invariant":"^1.3.3","valibot":"^1.0.0","type-fest":"^4.30.0"},"files":["dist/**/*","src/**/*"]},"isInternal":false,"tokens":818,"sizeBytes":2255},{"name":"tsconfig.json","path":"packages/ts-client/tsconfig.json","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/ts-client/tsconfig.json","title":"Ts-client Skill","category":"anthropic-skill","format":"json","content":"{\n\t\"compilerOptions\": {\n\t\t\"target\": \"ES2020\",\n\t\t\"module\": \"commonjs\",\n\t\t\"lib\": [\"ES2020\"],\n\t\t\"jsx\": \"react-jsx\",\n\t\t\"outDir\": \"./dist\",\n\t\t\"rootDir\": \"./src\",\n\t\t\"strict\": true,\n\t\t\"esModuleInterop\": true,\n\t\t\"skipLibCheck\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"declaration\": true,\n\t\t\"declarationMap\": true,\n\t\t\"sourceMap\": true,\n\t\t\"moduleResolution\": \"node\",\n\t\t\"allowSyntheticDefaultImports\": true,\n\t\t\"resolveJsonModule\": true,\n\t\t\"types\": [\"jest\", \"node\"]\n\t},\n\t\"include\": [\"src/**/*\"],\n\t\"exclude\": [\"node_modules\", \"dist\", \"**/*.test.ts\", \"**/*.test.tsx\", \"**/__tests__/**\"]\n}\n","frontmatter":{"compilerOptions":{"target":"ES2020","module":"commonjs","lib":["ES2020"],"jsx":"react-jsx","outDir":"./dist","rootDir":"./src","strict":true,"esModuleInterop":true,"skipLibCheck":true,"forceConsistentCasingInFileNames":true,"declaration":true,"declarationMap":true,"sourceMap":true,"moduleResolution":"node","allowSyntheticDefaultImports":true,"resolveJsonModule":true,"types":["jest","node"]},"include":["src/**/*"],"exclude":["node_modules","dist","**/*.test.ts","**/*.test.tsx","**/__tests__/**"]},"isInternal":false,"tokens":205,"sizeBytes":588},{"name":"USAGE.md","path":"packages/ts-client/USAGE.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/ts-client/USAGE.md","title":"Ts-client Skill","category":"anthropic-skill","format":"markdown","content":"# Ts-client Skill\n\nPath: `packages/ts-client/USAGE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/ts-client/USAGE.md)","tokens":300,"sizeBytes":177},{"name":"Claude.md","path":".tasks/Claude.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/.tasks/Claude.md","title":".tasks - Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# Task Tracking Cheatsheet\n\nHi Claude, Claude here. Your owner has pointed you here with absolutely no context. This document is your prompt.\n\nThis directory tracks the complete development of Spacedrive. Tasks are organized into subdirectories:\n- `core/` - Backend/Rust tasks (CORE, JOB, INDEX, LSYNC, etc.)\n- `interface/` - Frontend/React tasks (UI, EXPL, SETS, MEDIA, etc.)\n- `mobile/` - Mobile-specific tasks (IOS, MACOS, AND)\n\nTasks are always kept up-to-date, though some may need updating based on recent work. Your job is to check git history for changes, validate completed tasks against the instructions below, and update their status accordingly.\n\n## Quick Start\n\nWhen asked to review and update tasks, follow this process:\n\n### 1. List Current Tasks\n\n```bash\ncargo run --bin task-validator -- list --sort-by status\n```\n\nThis shows all tasks grouped by status (Completed, Done, In Progress, To Do).\n\n### 2. Review Recent Commits\n\n```bash\ngit log --oneline -20\n```\n\nLook for feature implementations, major refactors, or completed work that might correspond to tasks.\n\n### 3. Cross-Reference Work with Tasks\n\nFor each recent feature/change:\n\n- **Read the task file** directly from `.tasks/{subdirectory}/TASK-ID-name.md`\n  - Core/backend work: `.tasks/core/`\n  - Interface work: `.tasks/interface/`\n  - Mobile work: `.tasks/mobile/`\n- **Read the acceptance criteria** carefully - this is your checklist\n- **Read all implementation files** mentioned in the task (they're usually listed in an \"Implementation Files\" section)\n- **Check core/tests/** for integration tests that validate the feature (for core tasks)\n- **Check packages/interface/src/** for interface implementations (for interface tasks)\n- Verify each acceptance criterion is actually met in the code\n\n### 4. Update Task Status\n\nWhen updating a task's status, edit the YAML front matter:\n\n```yaml\n---\nid: TASK-000\ntitle: Task Title\nstatus: Done # Changed from \"To Do\" or \"In Progress\"\nassignee: jamiepine\npriority: High\ntags: [core, feature]\nlast_updated: 2025-10-14 # Update this date\n---\n```\n\n**Status Levels:**\n\n- `To Do` - Not started\n- `In Progress` - Actively being worked on\n- `Done` - Complete and merged\n- `Completed` - Same as Done (legacy)\n\n### 5. Validate Changes\n\nBefore committing task updates, validate them:\n\n```bash\ncargo run --bin task-validator -- validate\n```\n\nThis checks that your YAML front matter matches the schema.\n\n## Task Status Guidelines\n\n### Mark as \"Done\" when:\n\n- All acceptance criteria are met\n- Code is merged to main\n- Tests pass (if applicable)\n- Feature is actually usable\n\n### Keep \"In Progress\" when:\n\n- ️ Partially implemented\n- ️ Core work done but rough edges remain\n- ️ Actively being iterated on\n\n### Keep \"To Do\" when:\n\n- Not started\n- Only design/planning done\n- Mentioned in docs but no code exists\n\n## Pro Tips\n\n1. **Read implementation files directly** - Open and read the actual source files mentioned in the task.\n\n2. **Always check core/tests/** for integration tests that validate the feature works end-to-end.\n\n3. **Check recent file changes:**\n\n   ```bash\n   git diff --name-only HEAD~10\n   ```\n\n4. **Search commits by keyword:**\n\n   ```bash\n   git log --oneline --grep=\"volume\\|cloud\" -i\n   ```\n\n5. **Filter tasks by criteria:**\n\n   ```bash\n   # Show only \"In Progress\" tasks\n   cargo run --bin task-validator -- list --status \"In Progress\"\n\n   # Show tasks by priority\n   cargo run --bin task-validator -- list --priority High\n   ```\n\n## Common Pitfalls\n\n**Don't mark tasks done if:**\n\n- Implementation exists but doesn't work\n- Only scaffolding/types are defined\n- Tests are failing\n- Feature is behind a feature flag and disabled\n\n**Do mark tasks done if:**\n\n- All acceptance criteria are genuinely met\n- Implementation is production-ready\n- Code demonstrates the feature working\n\n## Example Workflow\n\n```bash\n# 1. See current state\ncargo run --bin task-validator -- list --sort-by status\n\n# 2. Check what's been done recently\ngit log --oneline -15\n\n# 3. Found \"feat: implement cloud volume support\" commit\n#    Let's check CLOUD-003 task\n```\n\nNow use the Read tool to thoroughly verify:\n\n````\n# 4. Read the task file (note the subdirectory)\nRead .tasks/core/CLOUD-003-cloud-volume.md\n\n# 5. Read every implementation file listed in the task\nRead core/src/volume/backend/mod.rs\nRead core/src/volume/backend/cloud.rs\nRead core/src/ops/volumes/add_cloud/mod.rs\n... (read all mentioned files)\n\n# 6. Check for integration tests\nGlob core/tests/**/*.rs\nRead core/tests/test_cloud_volume.rs  # if it exists\n\n# 7. Verify each acceptance criterion:\n#    - [x] User can add S3 bucket? Check add_cloud action code\n#    - [x] Cloud volume can be indexed? Check indexer integration\n#    - [ ] Files can be copied to/from cloud? Search for copy implementation\n#\n# If ALL criteria met → mark \"Done\"\n# If SOME criteria met → keep \"In Progress\"\n# If implementation looks complete → Update status and last_updated date\n\n# 8. Validate before committing\n```bash\ncargo run --bin task-validator -- validate\n````\n\n## Task Schema Reference\n\nRequired fields in YAML front matter:\n\n- `id` - Unique identifier (e.g., CORE-001)\n- `title` - Human-readable title\n- `status` - One of: To Do, In Progress, Done, Completed\n- `assignee` - Who's working on it (or \"james\")\n- `priority` - Critical, High, Medium, or Low\n- `tags` - Array of relevant tags\n\nOptional fields:\n\n- `parent` - Parent epic/task ID\n- `whitepaper` - Reference to design docs\n- `last_updated` - ISO date of last update\n- `related_tasks` - Array of related task IDs\n\n---\n\n**Remember**: The task tracker is only useful if it's honest. Be rigorous about what \"Done\" means. When in doubt, leave it as \"In Progress\" and document what's left.\n\n— Past Claude, trying to help Future Claude (that's you!)\n","isInternal":false,"tokens":1430,"sizeBytes":5778},{"name":"README.md","path":"extensions/photos/README.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/extensions/photos/README.md","title":"photos Documentation","category":"plugin-manifest","format":"markdown","content":"# Photos Extension for Spacedrive\n\nA comprehensive photo management extension that brings Apple Photos and Google Photos capabilities to Spacedrive.\n\n## Features\n\n### Face Recognition\n- **Automatic face detection** using RetinaFace model\n- **Face clustering** with DBSCAN algorithm\n- **Person identification** with name labeling\n- **Face-based search** (\"Show me photos of Alice\")\n- **Cross-device sync** of face clusters\n\n### Place Identification\n- **GPS extraction** from EXIF data\n- **Geographic clustering** (groups photos within 500m)\n- **Reverse geocoding** using AI\n- **Place-based search** (\"Photos from Paris\")\n- **Map view** of photo locations\n\n### Moments\n- **Automatic moment generation** (time + location clustering)\n- **AI-generated titles** (\"Summer in Paris\")\n- **Weekly memories** (scheduled agent task)\n- **Moment timeline** view\n\n### ️ Scene Understanding\n- **Scene classification** (ResNet50 on Places365)\n- **Smart tags** (#beach, #sunset, #food)\n- **Scene-based search** (\"Photos with sunsets\")\n- **Quality scoring** (aesthetic assessment)\n\n### Albums & Organization\n- **Manual albums** (user-created)\n- **Smart albums** (rule-based)\n- **Favorites** (special album)\n- **Hidden photos** (privacy)\n- **Shared albums** (multi-device)\n\n## Architecture\n\n### Models\n\n```rust\nPhoto       // Links to image file + EXIF + face/scene sidecars\nPerson      // Face cluster with name and embeddings\nPlace       // Geographic location with radius\nAlbum       // Collection of photos\nMoment      // Time/location-based photo group\n```\n\n### Agent Memory\n\n```rust\nPhotosMind {\n    history: TemporalMemory<PhotoEvent>,     // Analysis timeline\n    knowledge: AssociativeMemory<PhotoKnowledge>, // Face/place graph\n    plan: WorkingMemory<AnalysisPlan>,       // Pending work\n}\n```\n\n### Jobs\n\n- `analyze_photos_batch` - Face detection on photos\n- `identify_places_in_location` - Place clustering and naming\n- `analyze_scenes` - Scene classification\n- `create_moments` - Automatic moment generation\n- `cluster_faces_into_people` - Face clustering\n- `generate_face_tags` - Tag generation from sidecars\n\n### Actions\n\n- `create_album` - User creates album\n- `identify_person` - Name a face cluster\n- `remove_photo_from_album` - Organize albums\n- `hide_photo` - Privacy controls\n\n## Usage\n\n### Installation\n\n1. Install extension from Spacedrive Extension Store\n2. Grant permissions to specific photo locations:\n   - `/Users/alice/Photos`\n   - `/Volumes/External/Family Photos`\n3. Extension downloads AI models (~107MB total)\n4. User initiates analysis: \"Analyze for Faces\" button\n\n### Analysis Flow\n\n```\nUser enables Photos on \"/My Photos\"\n  ↓\nExtension dispatches analyze_photos_batch job\n  ↓\nFor each photo:\n  - Read EXIF (from Core)\n  - Detect faces → save to sidecar\n  - Classify scene → save to sidecar\n  ↓\nCluster faces into people\n  ↓\nGenerate tags from sidecars\n  ↓\nUser can search \"#person:alice\" or \"photos from beach\"\n```\n\n### Data Storage\n\n```\n~/.spacedrive/\n  └── models/\n      ├── face_detection/\n      │   └── photos_v1.onnx (12MB)\n      └── scene_classification/\n          └── resnet50.onnx (95MB)\n\n.sdlibrary/\n  ├── sidecars/\n  │   └── content/{ab}/{cd}/{uuid}/\n  │       └── extensions/com.spacedrive.photos/\n  │           ├── faces/default.json       # Face detection results\n  │           ├── scene/default.json       # Scene classification\n  │           └── aesthetics/default.json  # Quality score\n  └── extensions/com.spacedrive.photos/\n      └── data.db                           # Person, Place, Moment, Album, Photo rows\n```\n\n## SDK Features Demonstrated\n\n### Implemented in Example\n\n- `#[extension]` with permissions and dependencies\n- `#[model]` for Photo, Person, Place, Album, Moment\n- `#[agent]` with lifecycle hooks and event handlers\n- `#[agent_memory]` with enum-based Temporal/Associative memory\n- `#[job]` and `#[task]` for durable processing\n- `#[action]` with preview-execute pattern\n- `#[query]` for user searches\n- Extension-owned sidecars\n- Virtual models with persistence\n- User-scoped permissions\n- Model registration on install\n- AI with Jinja templates\n- Tag generation from sidecars\n- Custom memory query methods\n\n### What runs today\n\nUnder a daemon built with `--features wasm`, `analyze_photos` runs end to end\nover a library: it reads each photo through its record, asks for face\ndetection, and because the core has no inference provider the host answers\n`not_available`; the job warns once, skips, and completes, so a later run with\na detector picks the photos up. `core/tests/wasm_extension_test.rs` proves\nthis against a fixture library.\n\n### Not yet backed by the core\n\n- Inference. There is no provider for face detection, scene classification,\n  embeddings or language models, so `analyze_scenes`, `identify_places` and\n  `create_moments` have nothing to run on.\n- Tags, custom fields and `dispatch_jobs`.\n- Agents, actions and queries.\n- Model downloads named in `manifest.json`.\n- File-kind and preview registration.\n\n## Capabilities Compared\n\n### Apple Photos\n- Face recognition and clustering\n- Place identification\n- Memories\n- Albums (manual and smart)\n- Favorites and hidden\n- Search by person/place/scene\n- Map view\n- ️ Shared albums (requires P2P implementation)\n\n### Google Photos\n- Face grouping\n- Place detection\n- Search by content\n- Albums\n- Automatic creations (moments)\n- Scene/object detection\n- ️ Cloud backup (Spacedrive handles differently)\n\n### Spacedrive Photos Advantages\n- **100% local** - No cloud upload required\n- **Privacy-first** - Face data never leaves devices\n- **Multi-device sync** - Via P2P, not cloud\n- **Zero recurring cost** - No subscription needed\n- **User-scoped** - Only analyzes chosen locations\n- **Extensible** - Open SDK for customization\n\n## Building\n\n```bash\ncd extensions/photos\ncargo build --target wasm32-unknown-unknown --release\ncp target/wasm32-unknown-unknown/release/photos_extension.wasm ./photos.wasm\n```\n\n## License\n\nSame as Spacedrive Core\n\n","isInternal":false,"tokens":1439,"sizeBytes":6064},{"name":"README.md","path":"extensions/README.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/extensions/README.md","title":"extensions Documentation","category":"plugin-manifest","format":"markdown","content":"# Spacedrive Official Extensions\n\nThis directory contains the extension SDK and official extensions for Spacedrive.\n\n## Structure\n\n```\nextensions/\n├── spacedrive-sdk/          # Core SDK library\n├── spacedrive-sdk-macros/   # Proc macros for beautiful API\n├── test-extension/          # Example extension with beautiful API\n└── finance/                 # (Future) First revenue-generating extension\n```\n\n## Quick Start\n\n### 1. Install WASM Target\n\n```bash\nrustup target add wasm32-unknown-unknown\n```\n\n### 2. Create Extension\n\n```bash\ncargo new --lib my-extension\ncd my-extension\n```\n\n**Cargo.toml:**\n```toml\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacedrive-sdk = { path = \"../spacedrive-sdk\" }\nserde = { version = \"1.0\", features = [\"derive\"] }\n```\n\n**src/lib.rs:**\n```rust\nuse spacedrive_sdk::prelude::*;\nuse spacedrive_sdk::{extension, job};\n\n#[extension(\n    id = \"my-extension\",\n    name = \"My Extension\",\n    version = \"0.1.0\"\n)]\nstruct MyExtension;\n\n#[derive(Serialize, Deserialize, Default)]\npub struct MyJobState {\n    pub counter: u32,\n}\n\n#[job]\nfn my_job(ctx: &JobContext, state: &mut MyJobState) -> Result<()> {\n    ctx.log(\"Job starting!\");\n\n    state.counter += 1;\n    ctx.report_progress(1.0, \"Done!\");\n\n    Ok(())\n}\n```\n\n### 3. Build\n\n```bash\ncargo build --target wasm32-unknown-unknown --release\ncp target/wasm32-unknown-unknown/release/my_extension.wasm .\n```\n\n### 4. Create manifest.json\n\n```json\n{\n  \"id\": \"my-extension\",\n  \"name\": \"My Extension\",\n  \"version\": \"0.1.0\",\n  \"wasm_file\": \"my_extension.wasm\",\n  \"permissions\": {\n    \"read_records\": { \"glob\": \"**/*.{jpg,png}\" },\n    \"read_sidecars\": [\"exif\"],\n    \"write_sidecars\": [\"digest\"],\n    \"use_models\": [{ \"category\": \"face_detection\", \"preference\": \"local\" }]\n  }\n}\n```\n\nThe host rejects a manifest with a field it does not know, so a typo in a\ngrant fails at load. `read_records` is the only way to see files; its optional\n`glob` is honored for its trailing extension list only (`*.jpg` or\n`**/*.{jpg,png}`); the directory part is ignored and a glob of another shape\nis refused at load. `write_sidecars` names the kinds\nthe extension may write; `use_models` the inference categories it may ask for.\n`write_tags`, `write_custom_fields` and `dispatch_jobs` are declared but have\nno host side yet.\n\n## The Beautiful API\n\n### Before Macros (Manual FFI):\n```rust\n#[no_mangle]\npub extern \"C\" fn execute_my_job(\n    ctx_ptr: u32, ctx_len: u32,\n    state_ptr: u32, state_len: u32\n) -> i32 {\n    let ctx_json = unsafe { /* 30 lines of pointer manipulation */ };\n    let mut state = /* 40 lines of deserialization */;\n    // ... business logic buried in boilerplate ...\n}\n```\n**180+ lines, lots of unsafe**\n\n### After Macros (Beautiful):\n```rust\n#[job]\nfn my_job(ctx: &JobContext, state: &mut MyJobState) -> Result<()> {\n    // Just write business logic!\n    ctx.log(\"Working...\");\n    state.counter += 1;\n    Ok(())\n}\n```\n**60-80 lines, zero unsafe, pure logic**\n\n## API Reference\n\n### Extension Container\n\n```rust\n#[extension(\n    id = \"finance\",\n    name = \"Spacedrive Finance\",\n    version = \"0.1.0\"\n)]\nstruct Finance;\n```\n\nGenerates:\n- `plugin_init()` export\n- `plugin_cleanup()` export\n- Metadata for manifest generation\n\n### Job Definition\n\n```rust\n#[job]\nfn email_scan(ctx: &JobContext, state: &mut EmailScanState) -> Result<()> {\n    // Progress reporting\n    ctx.report_progress(0.5, \"Half done\");\n\n    // Checkpointing\n    ctx.checkpoint(state)?;\n\n    // Interruption handling\n    if ctx.check_interrupt() {\n        return Err(Error::OperationFailed(\"Interrupted\".into()));\n    }\n\n    // Metrics\n    ctx.increment_items(1);\n    ctx.increment_bytes(1000);\n\n    // Warnings\n    ctx.add_warning(\"Non-fatal issue\");\n\n    // Full SDK access\n    let record = ctx.vdfs().get_record(uuid).await?;\n    let bytes = record.read().await?;\n\n    Ok(())\n}\n```\n\n### VDFS Operations\n\n```rust\n// Records of the library's sources, filtered by extension (read_records grant)\nlet photos = ctx.vdfs()\n    .query_records()\n    .with_extensions([\"jpg\", \"png\"])\n    .collect()\n    .await?;\n\n// One record and its bytes\nlet record = ctx.vdfs().get_record(uuid).await?;\nlet bytes = record.read().await?;\n\n// Sidecars are one JSON document per kind, keyed by content uuid\nlet content_uuid = record.content_uuid().expect(\"hashed\");\nif !ctx.sidecar_exists(content_uuid, \"digest\")? {\n    ctx.save_sidecar(content_uuid, \"digest\", &digest).await?;\n}\nlet digest: Digest = ctx.read_sidecar(content_uuid, \"digest\").await?;\n\n// Models declared with #[model] live in the extension's own store\nctx.vdfs().create_model(entry).await?;\nlet all: Vec<CatalogEntry> = ctx.vdfs().query_models().collect().await?;\n```\n\nTags and custom fields have no host side yet and return `Error::NotAvailable`.\n\n### AI Operations\n\nInference goes through `ctx.ai().infer(...)` behind the `use_models` grant.\nThe core has no provider yet, so every request answers `Error::NotAvailable`;\nhandle it the way `extensions/photos/src/jobs/analyze.rs` does, with one\nwarning and a skip, so the job completes and a later run with a provider\npicks the files up.\n\n```rust\n// Written against the API; answers NotAvailable until a provider exists\nlet faces = ctx.ai().from_registered(\"face_detection\").detect_faces(&bytes).await?;\n```\n\n### Credentials\n\n```rust\n// Store OAuth\nctx.credentials().store(\"gmail\", Credential::oauth2(\n    access_token,\n    Some(refresh_token),\n    3600,\n    vec![\"https://www.googleapis.com/auth/gmail.readonly\".into()]\n))?;\n\n// Get (auto-refreshes)\nlet cred = ctx.credentials().get(\"gmail\")?;\n```\n\n## Examples\n\nSee `extensions/test-extension/` for a complete working example.\n\n## Building\n\nAll extensions:\n```bash\ncd extensions/test-extension\ncargo build --target wasm32-unknown-unknown --release\n```\n\n## Documentation\n\n- **[SDK API Vision](../docs/EXTENSION_SDK_API_VISION.md)** - Future API improvements\n- **[Before/After Comparison](./BEFORE_AFTER_COMPARISON.md)** - See the transformation\n- **[WASM Architecture](../docs/core/design/WASM_ARCHITECTURE_FINAL.md)** - Technical details\n- **[Platform Revenue Model](../docs/PLATFORM_REVENUE_MODEL.md)** - Business case\n\n---\n\n**Extension development is now beautiful, safe, and productive. Start building!** \n","isInternal":false,"tokens":1610,"sizeBytes":6223},{"name":"README.md","path":"extensions/test-extension/README.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/extensions/test-extension/README.md","title":"test-extension Documentation","category":"plugin-manifest","format":"markdown","content":"# Test Extension\n\n**The canonical example of Spacedrive extension development.**\n\nThis extension demonstrates the beautiful, macro-powered API that makes building extensions delightful.\n\n## Features\n\n**Zero Boilerplate** - Macros generate all FFI code\n**Type-Safe** - Full Rust type system\n**No Unsafe** - Safe by default\n**Clean API** - Just write business logic\n\n## Code\n\n**Complete extension in 76 lines:**\n\n```rust\nuse spacedrive_sdk::prelude::*;\nuse spacedrive_sdk::{extension, job};\n\n// Extension definition\n#[extension(\n    id = \"test-extension\",\n    name = \"Test Extension\",\n    version = \"0.1.0\"\n)]\nstruct TestExtension;\n\n// Job state\n#[derive(Serialize, Deserialize, Default)]\npub struct CounterState {\n    pub current: u32,\n    pub target: u32,\n}\n\n// Job implementation - THAT'S IT!\n#[job]\nfn test_counter(ctx: &JobContext, state: &mut CounterState) -> Result<()> {\n    while state.current < state.target {\n        ctx.check_interrupt()?;\n        state.current += 1;\n        ctx.report_progress(\n            state.current as f32 / state.target as f32,\n            &format!(\"Counted {}/{}\", state.current, state.target)\n        );\n        if state.current % 10 == 0 {\n            ctx.checkpoint(state)?;\n        }\n    }\n    Ok(())\n}\n```\n\n## What the Macros Generate\n\nThe `#[extension]` and `#[job]` macros automatically generate:\n\n- `plugin_init()` - Extension initialization\n- `plugin_cleanup()` - Extension cleanup\n- `execute_test_counter()` - FFI export with full state management\n- All pointer marshalling\n- Serialization/deserialization\n- Error handling\n- Progress tracking\n- Checkpoint management\n\n**~120 lines of boilerplate you don't write!**\n\n## Building\n\n```bash\ncargo build --target wasm32-unknown-unknown --release\n```\n\nOutput: `target/wasm32-unknown-unknown/release/test_extension.wasm` (~254KB)\n\n## Capabilities Demonstrated\n\n### Job System\n- Progress reporting (0-100%)\n- Checkpointing (resume after crash)\n- Interruption handling (pause/cancel)\n- Metrics tracking (items processed)\n- State persistence\n\n### API Ergonomics\n- Clean function signatures\n- `?` operator for error handling\n- No FFI knowledge required\n- No unsafe code\n- Just write Rust!\n\n## Testing\n\nOnce Core is running:\n```rust\n// Load extension\nplugin_manager.load_plugin(\"test-extension\").await?;\n\n// Dispatch job\nlet job_id = job_manager.dispatch_by_name(\n    \"test-extension:test_counter\",\n    json!({ \"target\": 100 })\n).await?;\n\n// Watch progress in logs:\n// INFO Counted 10/100 (10% complete)\n// INFO Counted 20/100 (20% complete)\n// ...\n// INFO ✓ Completed! Processed 100 items\n```\n\n## Comparison\n\n| Metric | Manual FFI | With Macros | Improvement |\n|--------|-----------|-------------|-------------|\n| Lines of Code | 181 | 76 | 58% less |\n| Unsafe Blocks | 4 | 0 | 100% safer |\n| Boilerplate | 120 lines | 10 lines | 92% less |\n| WASM Size | 252KB | 254KB | Same |\n| Readability | 5/10 | 10/10 | Much better |\n| Dev Time | 2-3 hours | 15 minutes | 10x faster |\n\n---\n\n**This is what all Spacedrive extensions should look like going forward!** \n","isInternal":false,"tokens":759,"sizeBytes":3043},{"name":"README.md","path":"packages/assets/README.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/README.md","title":"assets Documentation","category":"plugin-manifest","format":"markdown","content":"# assets Documentation\n\nPath: `packages/assets/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/README.md)","tokens":300,"sizeBytes":178},{"name":"README.md","path":"packages/assets/sounds/README.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/sounds/README.md","title":"sounds Documentation","category":"plugin-manifest","format":"markdown","content":"# sounds Documentation\n\nPath: `packages/assets/sounds/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/sounds/README.md)","tokens":300,"sizeBytes":192},{"name":"README.md","path":"packages/interface/src/components/Inspector/variants/README.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/components/Inspector/variants/README.md","title":"variants Documentation","category":"plugin-manifest","format":"markdown","content":"# variants Documentation\n\nPath: `packages/interface/src/components/Inspector/variants/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/components/Inspector/variants/README.md)","tokens":300,"sizeBytes":254},{"name":"README.md","path":"packages/swift-client/README.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/swift-client/README.md","title":"swift-client Documentation","category":"plugin-manifest","format":"markdown","content":"# swift-client Documentation\n\nPath: `packages/swift-client/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/swift-client/README.md)","tokens":300,"sizeBytes":196},{"name":"README.md","path":"packages/ts-client/README.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/ts-client/README.md","title":"ts-client Documentation","category":"plugin-manifest","format":"markdown","content":"# @spacedrive/client\n\nType-safe TypeScript client for the Spacedrive daemon, automatically generated from Rust core types using Specta.\n\n## Features\n\n- **Fully Type-Safe** - All types generated directly from Rust core\n- **Real-time Events** - Subscribe to daemon events with full type safety\n- **Unix Domain Sockets** - Direct communication with daemon\n- **Auto-Generated** - Types stay in sync with Rust core automatically\n- **Rich Documentation** - JSDoc comments from Rust code\n\n## Installation\n\n```bash\nnpm install @spacedrive/client\n```\n\n## Quick Start\n\n```typescript\nimport { SpacedriveClient } from '@spacedrive/client';\n\nconst client = new SpacedriveClient();\n\n// Test connection\nawait client.ping();\n\n// Get libraries (fully typed)\nconst libraries = await client.getLibraries();\nconsole.log('Libraries:', libraries);\n\n// Get jobs with status filtering\nconst jobs = await client.getJobs('running');\nconsole.log('Running jobs:', jobs);\n\n// Create a new library\nconst newLibrary = await client.createLibrary('My Photos', '/Users/me/Photos');\nconsole.log('Created library:', newLibrary);\n```\n\n## Event Subscription\n\n```typescript\n// Subscribe to specific event types\nawait client.subscribe(['JobStarted', 'JobProgress', 'JobCompleted']);\n\n// Listen for events (fully typed)\nclient.on('event', (event) => {\n  switch (event) {\n    case 'CoreStarted':\n      console.log('Daemon started');\n      break;\n    default:\n      if ('JobStarted' in event) {\n        console.log(`Job started: ${event.JobStarted.job_type} (${event.JobStarted.job_id})`);\n      } else if ('JobProgress' in event) {\n        console.log(`Job progress: ${event.JobProgress.progress * 100}%`);\n      } else if ('JobCompleted' in event) {\n        console.log(`Job completed: ${event.JobCompleted.job_type}`);\n        console.log('Output:', event.JobCompleted.output);\n      }\n      break;\n  }\n});\n\nclient.on('error', (error) => {\n  console.error('Client error:', error);\n});\n\nclient.on('disconnected', () => {\n  console.log('Disconnected from daemon');\n});\n```\n\n## Type Generation\n\nTypes are automatically generated from the Rust core using Specta. To regenerate types:\n\n```bash\nnpm run generate-types\n```\n\n## API Reference\n\n### Core Methods\n\n- `ping()` - Test daemon connectivity\n- `executeQuery<Q, R>(query: Q, method: string): Promise<R>` - Execute a query operation\n- `executeAction<A, R>(action: A, method: string): Promise<R>` - Execute an action operation\n- `subscribe(eventTypes?: string[]): Promise<void>` - Subscribe to daemon events\n\n### Convenience Methods\n\n- `getLibraries(includeStats?: boolean): Promise<LibraryInfo[]>` - Get all libraries\n- `createLibrary(name: string, path?: string): Promise<LibraryCreateOutput>` - Create a new library\n- `getJobs(status?: JobStatus): Promise<JobListOutput>` - Get job list with optional filtering\n\n### Event Types\n\nAll event types are fully typed TypeScript unions:\n\n- `Event` - Main event union type\n- `JobOutput` - Job completion output (adjacently tagged)\n- `JobStatus` - Job status enum (`\"queued\" | \"running\" | \"completed\" | ...`)\n- `FileOperation` - File operation types\n- `Progress` - Progress information with multiple formats\n\n## Architecture\n\nThis client uses the same architecture as the Swift client:\n\n1. **JSON API Layer** - Communicates via JSON instead of bincode for external clients\n2. **Unix Domain Sockets** - Direct, efficient communication with daemon\n3. **Type Generation** - Rust types → TypeScript via Specta\n4. **Event Streaming** - Real-time event subscription with line-delimited JSON\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Build the client\nnpm run build\n\n# Run tests\nnpm test\n\n# Watch mode for development\nnpm run dev\n```\n","isInternal":false,"tokens":854,"sizeBytes":3699},{"name":"README.md","path":"packages/ts-client/tests/integration/README.md","rawUrl":"https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/ts-client/tests/integration/README.md","title":"integration Documentation","category":"plugin-manifest","format":"markdown","content":"# integration Documentation\n\nPath: `packages/ts-client/tests/integration/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/ts-client/tests/integration/README.md)","tokens":300,"sizeBytes":225}],"systemPromptSnippet":"<yakaai_skills repo=\"spacedriveapp/spacedrive\">\n<!-- File: AGENTS.md (Tokens: ~5462 | Category: root-instruction) -->\n# Spacedrive Core v2 Development Guide\n\n## Quick Start\n\n### Development Workflow\n\n1. Start daemon: `cargo run --bin sd-daemon`\n2. Make code changes\n3. Run tests: `cargo test`\n4. Rebuild and restart: `cargo run --bin sd-cli -- restart`\n5. Test via CLI: `cargo run --bin sd-cli -- <command>`\n\n### Common Commands\n\n```bash\ncargo build                              # Build the project\ncargo test                               # Run all tests\ncargo test <test_name>                   # Run specific test\ncargo clippy                             # Lint code\ncargo fmt                                # Format code\ncargo run --bin sd-cli -- <command>      # Run CLI (binary is sd-cli, not spacedrive)\n```\n\n### Common Mistakes\n\n- Running `spacedrive` instead of `sd-cli` (the binary name is `sd-cli`)\n- Forgetting to restart daemon after rebuilding\n- Using `println!` instead of `tracing` macros (`info!`, `debug!`, etc)\n- Implementing `Wire` manually instead of using `register_*` macros\n- Blocking the async runtime with synchronous I/O operations\n\n### Quick tips\n\n- On frontend apps, such as the interface in React, you must ALWAYS ensure type-safety based on the auto generated TypeScript types from `ts-client`. Never cast to as any or redefine backend types. our hooks are typesafe with correct input/output types, but sometimes you might need to access types directly from the `ts-client`.\n- If you have changed types on the backend that are public to the frontend (have `Type` derive), then you must regenerate the types using `cargo run --bin generate_typescript_types`\n- Read the `.mdx` files in /docs for context on any part of the app, they are kept up to date.\n\n### Current direction\n\n`docs/core/product-direction.mdx` defines the product boundary. Use it to decide\nwhether work belongs in Spacedrive before extending a subsystem.\n\nThe entries world is gone. The `entry` and `location` tables and everything\nhanging off them were dropped, and durable file state lives in the record table\nin `crates/store`; `docs/plans/2026-09-15-entries-final-drop.md` records how.\n`docs/plans/2026-08-22-source-convergence.md` is the register for making the\nnew world one thing: one store shape, one registry, one set of claims that\nmatch the code.\n\n- Spacedrive is a set of sources. A source has an origin, an ingest, and a\n  store; filesystem and adapter sources differ only in ingest. Anything that\n  makes those two diverge further is wrong.\n- New durable state belongs in a source store: the record table, or the\n  assertion half for anything no ingest can rebuild. `library.db` keeps only\n  the source registry, devices, volumes, spaces, and small shared state.\n- A source store has two halves. The generation is rebuildable for as long as\n  its origin answers; the assertion layer never is. Since the origin can stop\n  answering silently, no code path may assume a store can be thrown away and\n  rebuilt. `docs/core/design/source-durability.md` has the rest, including what\n  the assertion tables have to reserve for sync.\n- The `.tasks/` tree predates this work and is not its register. The status\n  table at the top of the execution plan remains the entries-teardown register.\n  Use `PROJECT_STATUS.md` as the short-term project-wide working context and\n  update it in the same commit as active work.\n\n## Architecture Overview\n\nSpacedrive uses daemon-client architecture. A single daemon process manages core functionality. Multiple clients (CLI, GraphQL server, desktop app) connect via Unix domain sockets.\n\n### CQRS and DDD Pattern\n\n- **Domain** (`src/domain/`): Core data structures and business logic (nouns)\n- **Operations** (`src/ops/`): Actions and queries (verbs)\n- **Actions**: State-changing operations (writes)\n- **Queries**: Data retrieval without state changes (reads)\n\n### Feature Module Structure\n\nEach feature lives in its own module under `src/ops/`. Example: `src/ops/files/share`\n\n```\nsrc/ops/files/share/\n├── action.rs      # State-changing logic\n├── input.rs       # Action input structures\n├── output.rs      # Action output structures\n└── job.rs         # Long-running job implementation (if needed)\n```\n\nComplete feature example:\n\n```rust\n// src/ops/files/share/input.rs\n#[derive(Debug, Serialize, Deserialize)]\npub struct ShareFileInput {\n    pub file_id: i32,\n    pub recipient: String,\n}\n\n// src/ops/files/share/output.rs\n#[derive(Debug, Serialize, Deserialize)]\npub struct ShareFileOutput {\n    pub share_id: String,\n    pub url: String,\n}\n\n// src/ops/files/share/action.rs\nuse super::{ShareFileInput, ShareFileOutput};\n\npub struct ShareFileAction;\n\ncrate::register_library_action!(ShareFileAction, \"files.share\");\n\nimpl Action for ShareFileAction {\n    type Input = ShareFileInput;\n    type Output = ShareFileOutput;\n\n    async fn run(input: Self::Input, ctx: &ActionContext) -> Result<Self::Output> {\n        // Implementation\n    }\n}\n```\n\n## Communication Architecture\n\nSpacedrive supports multiple communication patterns for different platforms and use cases.\n\n### Daemon-Client Communication (Tauri Desktop, CLI, Web)\n\nThe Tauri desktop app, CLI, and web interface connect to a daemon process via Unix domain sockets (or WebSockets for web). Communication uses JSON-RPC 2.0 with Wire method strings.\n\n**Registration Macros:**\n\nNever implement `Wire` manually. Use registration macros:\n\n```rust\n// Queries\ncrate::register_query!(NetworkStatusQuery, \"network.status\");\n// Generates: \"query:network.status\"\n\n// Library Actions\ncrate::register_library_action!(FileCopyAction, \"files.copy\");\n// Generates: \"action:files.copy.input\"\n\n// Core Actions\ncrate::register_core_action!(LibraryCreateAction, \"libraries.create\");\n// Generates: \"action:libraries.create.input\"\n```\n\n**Registry System:**\n\nThe `inventory` crate collects operations at compile time. When you use `register_core_query!`, `register_library_query!`, `register_core_action!` or `register_library_action!`, the operation automatically appears in the global `CORE_QUERIES`, `LIBRARY_QUERIES`, `CORE_ACTIONS` and `LIBRARY_ACTIONS` maps at startup. You never manually register operations.\n\nLocation: `core/src/infra/wire/registry.rs`\n\n### Tauri Desktop Development\n\nThe Tauri app (`apps/tauri/`) is the primary desktop application for Spacedrive. It connects to the daemon via the TypeScript client.\n\n**Development Workflow:**\n\n```bash\n# Install dependencies\nbun install\n\n# Run Tauri app in dev mode (auto-starts daemon)\ncd apps/tauri\nbun run tauri:dev\n\n# Build for production\nbun run tauri:build\n```\n\n**TypeScript Client:**\n\nThe TypeScript client (`packages/ts-client/`) is auto-generated from Rust types using Specta:\n\n```bash\n# Generate TypeScript types\ncargo run --bin generate_typescript_types\n```\n\n**Output:** `packages/ts-client/src/generated/types.ts`\n\n**Architecture:**\n\n```\nTauri App (React)\n    ↓\n@sd/ts-client (TypeScript)\n    ↓\nDaemon (Unix Socket / IPC)\n    ↓\nRpcServer (Rust)\n    ↓\nOperation Registry\n```\n\n### Native Prototypes (iOS, macOS)\n\n**Note:** iOS and macOS apps are experimental prototypes, not production apps.\n\nNative prototypes embed the core directly as a library via FFI rather than connecting to a daemon. These are located in `apps/ios/` and `apps/macos/` but are private and not documented for public use.\n\n**Swift Client Generation:**\n\nFor the prototypes, Swift types can be generated:\n\n```bash\ncargo run --bin generate_swift_types\n```\n\nOutput: `packages/swift-client/Sources/SpacedriveClient/`\n\n### Extension System (WASM)\n\nExtensions run as sandboxed WASM modules that interact with Spacedrive core via host functions. Extensions are distributed as compiled `.wasm` files.\n\n**Architecture:**\n\n```\nExtension.wasm (compiled Rust)\n    ↓\nspacedrive-sdk (Rust crate)\n    ↓\nHost Functions (FFI boundary)\n    ↓\nCore (VDFS, Jobs, AI, etc.)\n```\n\n**Key Components:**\n\n**SDK Location:** `crates/sdk/`\n\n- High-level Rust API abstracting FFI details\n- Procedural macros for extension definition\n- Type-safe job, model, and action builders\n\n**Extension Development:**\n\nExtensions use procedural macros to minimize boilerplate:\n\n```rust\nuse spacedrive_sdk::prelude::*;\n\n#[extension(\n    id = \"test-extension\",\n    name = \"Test Extension\",\n    version = \"0.1.0\",\n    jobs = [test_counter],\n)]\nstruct TestExtension;\n\n#[derive(Serialize, Deserialize, Default)]\npub struct CounterState {\n    pub current: u32,\n    pub target: u32,\n    pub processed: Vec<String>,\n}\n\n#[job(name = \"counter\")]\nfn test_counter(ctx: &JobContext, state: &mut CounterState) -> Result<()> {\n    ctx.log(&format!(\"Starting counter (current: {}, target: {})\",\n        state.current, state.target));\n\n    while state.current < state.target {\n        if ctx.check_interrupt_sync() {\n            ctx.checkpoint(state)?;\n            return Err(Error::OperationFailed(\"Interrupted\".into()));\n        }\n\n        state.current += 1;\n        ctx.report_progress(\n            state.current as f32 / state.target as f32,\n            &format!(\"Counted {}/{}\", state.current, state.target),\n        );\n\n        if state.current % 10 == 0 {\n            ctx.checkpoint(state)?;\n        }\n    }\n\n    Ok(())\n}\n```\n\n**Host Functions:**\n\nExtensions import minimal FFI functions:\n\n```rust\n#[link(wasm_import_module = \"spacedrive\")]\nextern \"C\" {\n    fn spacedrive_log(level: u32, msg_ptr: *const u8, msg_len: usize);\n    fn register_job(\n        job_name_ptr: *const u8,\n        job_name_len: u32,\n        export_fn_ptr: *const u8,\n        export_fn_len: u32,\n        resumable: u32,\n    ) -> i32;\n}\n```\n\n**Building Extensions:**\n\n```bash\n# From extension directory\ncargo build --target wasm32-unknown-unknown --release\n\n# Output: target/wasm32-unknown-unknown/release/extension_name.wasm\n```\n\n**Extension Capabilities:**\n\nExtensions can define:\n\n- Models: Data structures stored in `models` table (content-scoped, standalone, or entry-scoped)\n- Jobs: Long-running resumable operations\n- Actions: User-invoked operations with preview-commit workflow\n- Agents: Autonomous logic with memory and event handling\n- UI: Custom views via `ui_manifest.json`\n\n**Example Use Cases:**\n\n- Photos extension: Face detection, scene tagging, album organization\n- Finance extension: Receipt extraction, expense tracking\n- Research extension: Citation extraction, knowledge graphs\n\n**Key Benefits:**\n\n- Single `.wasm` file works on all platforms\n- True sandboxing (WASM isolation)\n- Resumable jobs with checkpointing\n- Type-safe API with procedural macros\n- No core modifications needed for new features\n\n**Documentation:**\n\n- `crates/sdk/API_REFERENCE.md` - SDK specification and API reference\n- `extensions/test-extension/` - Working example extension\n- `crates/sdk/` - SDK implementation\n- `crates/sdk-macros/` - SDK procedural macros\n\n**Status:** A daemon built with the `wasm` feature loads every extension under `<data dir>/extensions/` at startup and runs the jobs they register through the job manager, with checkpoints and resume (`core/src/infra/extension/README.md`). Only the job host functions exist; the rest of the SDK (VDFS, AI, models, tasks) has no host side yet.\n\n## Code Standards\n\n### Import Organization\n\nGroup imports with blank lines between groups:\n\n```rust\n// Standard library\nuse std::path::PathBuf;\nuse std::sync::Arc;\n\n// External crates\nuse serde::{Deserialize, Serialize};\nuse tokio::sync::RwLock;\n\n// Local modules\nuse crate::domain::library::Library;\nuse crate::ops::Action;\n```\n\n### Naming Conventions\n\n- Functions/variables: `snake_case`\n- Types: `PascalCase`\n- Constants: `SCREAMING_SNAKE_CASE`\n\n### Error Handling\n\nUse `Result<T, E>` for all fallible operations. Use `thiserror` for custom errors, `anyhow` for application errors.\n\n```rust\nuse thiserror::Error;\n\n#[derive(Error, Debug)]\npub enum ShareError {\n    #[error(\"File not found: {0}\")]\n    FileNotFound(i32),\n\n    #[error(\"Permission denied\")]\n    PermissionDenied,\n\n    #[error(\"Database error: {0}\")]\n    Database(#[from] sea_orm::DbErr),\n}\n\npub async fn share_file(id: i32) -> Result<String, ShareError> {\n    let file = find_file(id).await.ok_or(ShareError::FileNotFound(id))?;\n    // Implementation\n    Ok(share_url)\n}\n```\n\n### Async Code\n\n- Use `async/await` syntax\n- Prefer `tokio` primitives (`tokio::sync::RwLock`, `tokio::spawn`)\n- Avoid blocking operations (use `tokio::fs` not `std::fs`)\n- Use `tokio::task::spawn_blocking` for CPU-intensive work\n\n### Resumable Jobs\n\nStore job state within the job struct. Use `#[serde(skip)]` for non-persistent fields.\n\n```rust\n#[derive(Serialize, Deserialize)]\npub struct FileCopyJob {\n    pub source: PathBuf,\n    pub destination: PathBuf,\n    pub copied_files: Vec<PathBuf>,  // Persisted for resumability\n\n    #[serde(skip)]\n    pub progress_tx: Option<tokio::sync::mpsc::Sender<Progress>>,  // Not persisted\n}\n\nimpl Job for FileCopyJob {\n    async fn run(&mut self, ctx: &JobContext) -> Result<()> {\n        ctx.log().info(\"Starting file copy job\");\n\n        for file in &self.files_to_copy {\n            if self.copied_files.contains(file) {\n                continue;  // Skip already copied files on resume\n            }\n\n            copy_file(file).await?;\n            self.copied_files.push(file.clone());\n        }\n\n        Ok(())\n    }\n}\n```\n\n### Documentation\n\n**Core principle:** Explain WHY, not WHAT. Keep comments as short as possible. One sentence explaining rationale beats a paragraph restating code.\n\n**Module docs (`//!`):**\n- Add a title with `#` for the module name\n- Explain what the module does in plain language (not bullet points)\n- Include design rationale naturally in prose\n- Add runnable code examples showing usage\n\n````rust\n//! # File Sharing System\n//!\n//! `core::ops::files::share` provides temporary file sharing via signed URLs.\n//! Share links expire after 7 days by default to prevent indefinite access to\n//! private files. UUID v5 deterministic IDs ensure the same file generates\n//! consistent share URLs across devices without coordination.\n//!\n//! ## Example\n//! ```rust,no_run\n//! use spacedrive_core::ops::files::share::{ShareFileAction, ShareFileInput};\n//!\n//! let input = ShareFileInput { file_id: 123, recipient: \"user@example.com\" };\n//! let output = ShareFileAction::run(input, &ctx).await?;\n//! ```\n````\n\n**Function docs (`///`):**\n- First line: brief one-liner\n- Second paragraph: explain design rationale and why this exists\n- Document error handling philosophy when relevant\n- Explain non-obvious behavior and platform differences\n\n```rust\n/// Creates a share link with automatic expiration.\n///\n/// Share links use signed JWTs so the daemon can validate them without\n/// database lookups on every request. Expiration is enforced server-side\n/// to prevent timezone manipulation. Recipients without library access\n/// get read-only access to the specific file only.\n///\n/// Returns `ShareError::PermissionDenied` if the file is private and\n/// the recipient isn't a library member. The share is still created\n/// but marked inactive for audit logging.\npub async fn share_file(input: ShareFileInput) -> Result<ShareFileOutput>\n```\n\n**Inline comments:**\n- Delete comments that restate obvious code\n- Explain WHY for decisions, not WHAT the code does\n- Use one sentence when possible\n- Only expand for truly non-obvious consequences\n\n```rust\n// Good: explains WHY\n// Lowercase for case-insensitive search matching.\nlet ext = path.extension().map(|e| e.to_lowercase());\n\n// Bad: restates code\n// Extract file extension and convert to lowercase\nlet ext = path.extension().map(|e| e.to_lowercase());\n\n// Good: explains consequence\n// Keep the arena's UUID so a file keeps its identity when a source adopts it.\nlet uuid = arena.get_entry_uuid(path).unwrap_or_else(Uuid::now_v7);\n\n// Bad: verbose explanation of obvious behavior\n// UUID assignment strategy:\n// 1. First check if the arena has a UUID\n// 2. If not, generate a new one\nlet uuid = arena.get_entry_uuid(path).unwrap_or_else(Uuid::now_v7);\n```\n\n**Error handling comments:**\nExplain strategy and recovery, not just \"log and continue\".\n\n```rust\n// Good: explains recovery\n// Best-effort: continue with remaining moves, stale paths cleaned up on next reindex.\nErr(e) => ctx.log(format!(\"Failed to move: {}\", e)),\n\n// Bad: states the obvious\n// Log error but continue\nErr(e) => ctx.log(format!(\"Failed to move: {}\", e)),\n```\n\n**Platform-specific comments:**\nExplain consequences, not implementation blockers.\n\n```rust\n// Good: explains why and fallback\n#[cfg(windows)]\npub fn get_inode(_metadata: &std::fs::Metadata) -> Option<u64> {\n    // Windows file indices are unstable across reboots; fall back to path-only matching.\n    None\n}\n\n// Bad: over-explains implementation details\n#[cfg(windows)]\npub fn get_inode(_metadata: &std::fs::Metadata) -> Option<u64> {\n    // Windows doesn't have inodes.\n    // The method `file_index()` is unstable (issue #63010).\n    // Returning None is safe as the field is Optional.\n    None\n}\n```\n\n**Never use:**\n- Placeholder comments (\"for now\", \"TODO: extract this later\")\n- Markdown formatting (`**bold**`, `_italic_`) in code comments\n- ASCII diagrams (put those in `/docs/` if needed)\n- Section divider comments (`// ========== Section ==========`)\n- Comments explaining removed code during refactors\n\nTrack future work in GitHub issues, not code comments.\n\n### Formatting\n\nRun `cargo fmt` before committing. Tabs for indentation. No emojis.\n\n## Logging\n\n### Setup\n\nUse `tracing_subscriber` in main or examples:\n\n```rust\nuse tracing_subscriber::EnvFilter;\n\nfn main() {\n    tracing_subscriber::fmt()\n        .with_env_filter(\n            EnvFilter::try_from_default_env()\n                .unwrap_or_else(|_| EnvFilter::new(\"sd_core=info\"))\n        )\n        .init();\n}\n```\n\n## Writing Style\n\nThis applies to all documentation, code comments, and design documents.\n\nUse clear, simple language. Write short, impactful sentences. Use active voice. Focus on practical, actionable information.\n\nAddress the reader directly with \"you\" and \"your\". Support claims with data and examples when possible.\n\nAvoid these constructions:\n\n- Em dashes (use commas or periods)\n- \"Not only this, but also this\"\n- Metaphors and cliches\n- Generalizations\n- Setup language like \"in conclusion\"\n- Unnecessary adjectives and adverbs\n- Emojis, hashtags, markdown formatting in prose\n\nAvoid these words:\ncomprehensive, delve, utilize, harness, realm, tapestry, unlock, revolutionary, groundbreaking, remarkable, pivotal\n\n### Macros\n\nUse `tracing` macros, never `println!`:\n\n```rust\nuse tracing::{info, warn, error, debug};\n\ninfo!(\"Server started on port {}\", port);\ndebug!(file_id = %id, \"Processing file\");\nwarn!(error = %e, \"Retrying operation\");\nerror!(\"Failed to connect to database\");\n```\n\n### Job Logging\n\nUse `ctx.log()` in job implementations for automatic `job_id` tagging:\n\n```rust\nimpl Job for MyJob {\n    async fn run(&mut self, ctx: &JobContext) -> Result<()> {\n        ctx.log().info(\"Job started\");\n        ctx.log().debug!(progress = %self.progress, \"Processing\");\n        Ok(())\n    }\n}\n```\n\n### Log Levels\n\n- `debug`: Detailed flow for troubleshooting\n- `info`: User-relevant events (server start, job completion)\n- `warn`: Recoverable issues (retry, fallback)\n- `error`: Failures requiring attention\n\n### Environment Control\n\nUse `RUST_LOG` environment variable:\n\n```bash\nRUST_LOG=debug cargo run --bin sd-cli\nRUST_LOG=sd_core=trace cargo run\nRUST_LOG=sd_core::ops=debug cargo run\n```\n\n## Testing\n\n### Test Organization\n\n- Unit tests: Colocated in `#[cfg(test)]` modules\n- Integration tests: `tests/` directory at crate root\n\n```rust\n// src/ops/files/share/action.rs\n\n#[cfg(test)]\nmod tests {\n    use super::*;\n\n    #[tokio::test]\n    async fn test_share_file() {\n        let input = ShareFileInput {\n            file_id: 1,\n            recipient: \"test@example.com\".to_string(),\n        };\n\n        let output = share_file(input).await.unwrap();\n        assert!(!output.share_id.is_empty());\n    }\n}\n```\n\n### Running Tests\n\n```bash\ncargo test                    # All tests\ncargo test test_share_file    # Specific test\ncargo test --lib              # Library tests only\ncargo test -- --nocapture     # Show output\n```\n\n## Task Tracking\n\nRead `/PROJECT_STATUS.md` at the start of a session and update its Immediate\nregister when broad work starts, lands, becomes blocked, or changes owner. Keep\ndesign rationale and phase acceptance criteria in the associated plan. For\nanything still in the entries teardown, also update the status table at the top\nof `/docs/plans/2026-08-20-entries-teardown-execution.md` in the same commit.\n\n`/.tasks/` is frozen. It describes the entry-centric model that the teardown\nremoves, and was last updated in May. Do not add to it and do not run the\nvalidator against new work. The sections below document the retired system for\nhistorical reference only.\n\nSpacedrive uses a file-based task system in `/.tasks/` to track features, epics, and development work. All task files are version-controlled alongside the code.\n\n### When to Create Tasks\n\nCreate tasks for work that:\n\n- Introduces a new feature or capability\n- Refactors a significant system or module\n- Fixes a bug requiring architectural changes\n- Implements a whitepaper specification\n\nDo not create tasks for:\n\n- Routine code formatting or style fixes\n- Trivial bug fixes (single line changes)\n- Documentation updates to existing features\n- Dependency version bumps\n\n### Task Structure\n\nEach task is a Markdown file: `CATEGORY-###-title-slug.md`\n\n```yaml\n---\nid: CORE-042\ntitle: \"Implement file sharing API\"\nstatus: \"In Progress\"\nassignee: \"james\"\npriority: \"High\"\ntags: [\"core\", \"networking\"]\nwhitepaper: \"Section 4.2\" # And/or design_doc: DESIGN_DOC_NAME.md\n---\n\n## Description\nBrief overview of what needs to be done and why.\n\n## Implementation Steps\n- [ ] Create share action in src/ops/files/share\n- [ ] Add database schema for shares table\n- [ ] Implement expiration logic\n\n## Acceptance Criteria\n- Share links work across all platforms\n- Expired shares return 404\n- Tests cover edge cases\n```\n\n### Managing Tasks\n\n```bash\n# List your active tasks\ncargo run -p task-validator -- list --assignee \"yourname\" --status \"In Progress\"\n\n# List high priority tasks\ncargo run -p task-validator -- list --priority \"High\" --sort-by id\n\n# Validate before committing (automatic via git hook)\ncargo run -p task-validator -- validate\n```\n\n### Task Lifecycle\n\n1. Create task file in `/.tasks/` with `status: \"To Do\"`\n2. Update status to `\"In Progress\"` when you start work\n3. Complete implementation and tests\n4. Update status to `\"Done\"` and commit\n\nFull documentation: `/docs/core/task-tracking.mdx`\n\n## Debugging\n\n### Log Files\n\nJob logs live in the `job_logs` directory in the data folder root.\n\n### Daemon Restart\n\nAfter rebuilding, restart the daemon to use the latest code:\n\n```bash\ncargo build\ncargo run --bin sd-cli -- restart\n```\n\n### Verbose Logging\n\n```bash\nRUST_LOG=debug cargo run --bin sd-daemon\nRUST_LOG=sd_core::jobs=trace cargo run\n```\n\n## Documentation Locations\n\n- Core architecture: `/docs/core/`\n- Design docs and RFCs: `/docs/core/design/`\n- Application docs: `/docs/`\n- Task tracking: `/docs/core/task-tracking.mdx`\n\n<!-- File: core/AGENTS.md (Tokens: ~1298 | Category: root-instruction) -->\n# AGENTS.md - Spacedrive Core v2\n\n## Build/Test Commands\n\n- `cargo build` - Build the project\n- `cargo test` - Run all tests\n- `cargo test <test_name>` - Run specific test (e.g., `cargo test library_test`)\n- `cargo clippy` - Lint code\n- `cargo fmt` - Format code\n- `cargo run --bin sd-cli -- <command>` - Run CLI (note: binary is `sd-cli`, not `spacedrive`)\n\n## Code Style\n\n- **Imports**: Group std, external crates, then local modules with blank lines between\n- **Formatting**: Use `cargo fmt` - tabs for indentation, snake_case for variables/functions. DO NOT use emojis at all.\n- **Types**: Explicit types preferred, use `Result<T, E>` for error handling with `thiserror`\n- **Naming**: snake_case for functions/variables, PascalCase for types, SCREAMING_SNAKE_CASE for constants\n- **Error Handling**: Use `Result` types, `thiserror` for custom errors, `anyhow` for application errors\n- **Async**: Use `async/await`, prefer `tokio` primitives, avoid blocking operations\n- **Resumable Jobs**: For long-running jobs that need to be resumable, store the job's state within the job's struct itself. Use `#[serde(skip)]` for fields that should not be persisted. For example, in a file copy job, the list of already copied files can be stored to allow the job to resume from where it left off.\n- **Documentation**: Use `//!` for module docs, `///` for public items, include examples\n- **Architecture**: Follow a Command Query Responsibility Segregation (CQRS) and Domain-Driven Design (DDD) pattern.\n  - **Domain**: Core data structures and business logic are located in `src/domain/`. These are the \"nouns\" of your system.\n  - **Operations**: State-changing commands (actions) and data-retrieving queries are located in `src/ops/`. These are the \"verbs\" of your system.\n  - **Actions**: Operations that modify the state of the application. They should be self-contained and transactional.\n  - **Queries**: Operations that retrieve data without modifying state. They should be efficient and optimized for reading.\n- **Feature Modules**: Each new feature should be implemented in its own module within the `src/ops/` directory. For example, a new \"share\" feature would live in `src/ops/files/share`. Each feature module should contain the following files where applicable:\n  - `action.rs`: The main logic for the state-changing operation.\n  - `input.rs`: Data structures for the action's input.\n  - `output.rs`: Data structures for the action's output.\n  - `job.rs`: If the action is long-running, the job implementation.\n- **Database**: Use SeaORM entities, async queries, proper error propagation\n- **Comments**: Minimal inline comments, focus on why not what, no TODO comments in production code\n\n## Daemon Architecture\n\nSpacedrive uses a **daemon-client architecture** where a single daemon process manages the core functionality and multiple client applications (CLI, GraphQL server, desktop app) connect to it via Unix domain sockets.\n\n> **For detailed daemon architecture documentation, see [/docs/core/daemon.md](/docs/core/daemon.md)**\n\n### **The `Wire` Trait**\n\nAll actions and queries must implement the `Wire` trait to enable type-safe client-daemon communication:\n\n```rust\npub trait Wire {\n    const METHOD: &'static str;\n}\n```\n\n### **Registration Macros**\n\nInstead of manually implementing `Wire`, use these registration macros that automatically:\n\n1. Implement the `Wire` trait with the correct method string\n2. Register the operation in the global registry using the `inventory` crate\n\n**For Queries:**\n\n```rust\ncrate::register_query!(NetworkStatusQuery, \"network.status\");\n// Generates method: \"query:network.status\"\n```\n\n**For Library Actions:**\n\n```rust\ncrate::register_library_action!(FileCopyAction, \"files.copy\");\n// Generates method: \"action:files.copy.input\"\n```\n\n**For Core Actions:**\n\n```rust\ncrate::register_core_action!(LibraryCreateAction, \"libraries.create\");\n// Generates method: \"action:libraries.create.input\"\n```\n\n### **Registry System**\n\n- **Location**: `core/src/ops/registry.rs`\n- **Mechanism**: Uses the `inventory` crate for compile-time registration\n- **Global Maps**: `QUERIES` and `ACTIONS` hashmaps populated at startup\n- **Handler Functions**: Generic handlers that decode payloads, execute operations, and encode results\n\n## Logging Standards\n\n- **Setup**: Use `tracing_subscriber::fmt()` with env filter for structured logging\n- **Macros**: Use `info!`, `warn!`, `error!`, `debug!` from `tracing` crate, not `println!`\n- **Job Context**: Use `ctx.log()` in jobs for job-specific logging with automatic job_id tagging\n- **Structured**: Include relevant context fields: `debug!(job_id = %self.id, \"message\")`\n- **Levels**: debug for detailed flow, info for user-relevant events, warn for recoverable issues, error for failures\n- **Format**: `tracing_subscriber::fmt().with_env_filter(env_filter).init()` in main/examples\n- **Environment**: Respect `RUST_LOG` env var, fallback to module-specific filters like `sd_core=info`\n\n## Documentation\n\n- **Core level docs**: Live in `/docs/core` - comprehensive architecture and implementation guides\n- **Core design docs**: Live in `/docs/core/design` - planning documents, RFCs, and design decisions\n- **Application level docs**: Live in `/docs`\n- **Code docs**: Use `///` for public APIs, `//!` for module overviews, include examples\n\n## Debug Instructions\n\n- You can view the logs of a job in the job_logs directory in the root of the data folder\n- When testing the CLI, after compiling you must first use the `restart` command to ensure the Spacedrive daemon is using the latest build.\n\n<!-- File: extensions/photos/manifest.json (Tokens: ~940 | Category: anthropic-skill) -->\n{\n\t\"id\": \"com.spacedrive.photos\",\n\t\"name\": \"Photos\",\n\t\"version\": \"1.0.0\",\n\t\"description\": \"Advanced photo management with AI-powered face recognition, place identification, and intelligent organization\",\n\t\"author\": \"Spacedrive\",\n\t\"wasm_file\": \"photos.wasm\",\n\t\"min_core_version\": \"2.0.0\",\n\t\"required_features\": [\n\t\t\"exif_extraction\",\n\t\t\"ai_models\",\n\t\t\"semantic_search\"\n\t],\n\t\"permissions\": {\n\t\t\"read_records\": {\n\t\t\t\"glob\": \"**/*.{jpg,jpeg,png,heic,heif,raw,cr2,nef,dng,webp}\"\n\t\t},\n\t\t\"read_sidecars\": [\n\t\t\t\"exif\",\n\t\t\t\"thumbnail\"\n\t\t],\n\t\t\"write_sidecars\": [\n\t\t\t\"faces\",\n\t\t\t\"places\",\n\t\t\t\"scene\",\n\t\t\t\"aesthetics\"\n\t\t],\n\t\t\"write_tags\": true,\n\t\t\"write_custom_fields\": [\n\t\t\t\"photos\"\n\t\t],\n\t\t\"dispatch_jobs\": true,\n\t\t\"use_models\": [\n\t\t\t{\n\t\t\t\t\"category\": \"face_detection\",\n\t\t\t\t\"preference\": \"local\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"category\": \"scene_classification\",\n\t\t\t\t\"preference\": \"local\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"category\": \"llm\",\n\t\t\t\t\"preference\": \"local\"\n\t\t\t}\n\t\t]\n\t},\n\t\"models\": [\n\t\t{\n\t\t\t\"name\": \"photos_face_detection_v1\",\n\t\t\t\"category\": \"face_detection\",\n\t\t\t\"source\": {\n\t\t\t\t\"type\": \"download\",\n\t\t\t\t\"url\": \"https://models.spacedrive.com/photos/face_detection_v1.onnx\",\n\t\t\t\t\"sha256\": \"placeholder_hash\",\n\t\t\t\t\"size_mb\": 12\n\t\t\t},\n\t\t\t\"description\": \"RetinaFace model for face detection\"\n\t\t},\n\t\t{\n\t\t\t\"name\": \"photos_scene_v1\",\n\t\t\t\"category\": \"scene_classification\",\n\t\t\t\"source\": {\n\t\t\t\t\"type\": \"download\",\n\t\t\t\t\"url\": \"https://models.spacedrive.com/photos/scene_resnet50.onnx\",\n\t\t\t\t\"sha256\": \"placeholder_hash\",\n\t\t\t\t\"size_mb\": 95\n\t\t\t},\n\t\t\t\"description\": \"ResNet50 trained on Places365 dataset\"\n\t\t}\n\t],\n\t\"kinds\": [\n\t\t{\n\t\t\t\"name\": \"raw\",\n\t\t\t\"display_name\": \"RAW photo\",\n\t\t\t\"parent\": \"image\",\n\t\t\t\"extensions\": [\n\t\t\t\t\"cr2\",\n\t\t\t\t\"cr3\",\n\t\t\t\t\"nef\",\n\t\t\t\t\"arw\",\n\t\t\t\t\"dng\",\n\t\t\t\t\"raf\",\n\t\t\t\t\"orf\",\n\t\t\t\t\"rw2\",\n\t\t\t\t\"pef\",\n\t\t\t\t\"dcr\",\n\t\t\t\t\"srw\"\n\t\t\t],\n\t\t\t\"mime_types\": [\n\t\t\t\t\"image/x-canon-cr2\",\n\t\t\t\t\"image/x-canon-cr3\",\n\t\t\t\t\"image/x-nikon-nef\",\n\t\t\t\t\"image/x-sony-arw\",\n\t\t\t\t\"image/x-adobe-dng\",\n\t\t\t\t\"image/x-fuji-raf\",\n\t\t\t\t\"image/x-olympus-orf\",\n\t\t\t\t\"image/x-panasonic-rw2\",\n\t\t\t\t\"image/x-pentax-pef\",\n\t\t\t\t\"image/x-kodak-dcr\",\n\t\t\t\t\"image/x-samsung-srw\"\n\t\t\t],\n\t\t\t\"magic\": [\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"49 49 2A 00\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"4D 4D 00 2A\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"46 55 4A 49 46 49 4C 4D\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"49 49 52 4F\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t},\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"49 49 55 00\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"preview\": {\n\t\t\t\t\"renderer\": \"image\"\n\t\t\t}\n\t\t}\n\t]\n}\n\n<!-- File: extensions/photos/ui_manifest.json (Tokens: ~1044 | Category: anthropic-skill) -->\n{\n\t\"sidebar\": {\n\t\t\"section\": \"Photos\",\n\t\t\"icon\": \"assets/photos_icon.svg\",\n\t\t\"order\": 10,\n\t\t\"views\": [\n\t\t\t{\n\t\t\t\t\"id\": \"library\",\n\t\t\t\t\"title\": \"Library\",\n\t\t\t\t\"component\": \"photo_grid\",\n\t\t\t\t\"query\": \"list_all_photos\",\n\t\t\t\t\"default\": true\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"albums\",\n\t\t\t\t\"title\": \"Albums\",\n\t\t\t\t\"component\": \"album_grid\",\n\t\t\t\t\"query\": \"list_albums\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"people\",\n\t\t\t\t\"title\": \"People\",\n\t\t\t\t\"component\": \"person_cluster_grid\",\n\t\t\t\t\"query\": \"list_people\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"places\",\n\t\t\t\t\"title\": \"Places\",\n\t\t\t\t\"component\": \"map_view\",\n\t\t\t\t\"query\": \"list_places\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"moments\",\n\t\t\t\t\"title\": \"Moments\",\n\t\t\t\t\"component\": \"moment_timeline\",\n\t\t\t\t\"query\": \"list_moments\"\n\t\t\t},\n\t\t\t{\n\t\t\t\t\"id\": \"favorites\",\n\t\t\t\t\"title\": \"Favorites\",\n\t\t\t\t\"component\": \"photo_grid\",\n\t\t\t\t\"query\": \"tags LIKE '#favorite'\",\n\t\t\t\t\"icon\": \"heart\"\n\t\t\t}\n\t\t]\n\t},\n\t\"context_menu\": [\n\t\t{\n\t\t\t\"id\": \"add_to_album\",\n\t\t\t\"label\": \"Add to Album...\",\n\t\t\t\"icon\": \"plus_circle\",\n\t\t\t\"action\": \"add_to_album\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t],\n\t\t\t\"keyboard_shortcut\": \"cmd+shift+a\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"identify_person\",\n\t\t\t\"label\": \"This is...\",\n\t\t\t\"icon\": \"person\",\n\t\t\t\"action\": \"identify_person\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t],\n\t\t\t\"requires\": \"has_detected_faces\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"set_as_cover\",\n\t\t\t\"label\": \"Set as Album Cover\",\n\t\t\t\"icon\": \"star\",\n\t\t\t\"action\": \"set_album_cover\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t],\n\t\t\t\"context\": \"album_view\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"hide_photo\",\n\t\t\t\"label\": \"Hide\",\n\t\t\t\"icon\": \"eye_slash\",\n\t\t\t\"action\": \"hide_photo\",\n\t\t\t\"applies_to\": [\n\t\t\t\t\"image/*\"\n\t\t\t]\n\t\t}\n\t],\n\t\"toolbar_actions\": [\n\t\t{\n\t\t\t\"id\": \"analyze_location\",\n\t\t\t\"label\": \"Analyze for Faces\",\n\t\t\t\"icon\": \"sparkles\",\n\t\t\t\"action\": \"analyze_photos_batch\",\n\t\t\t\"context\": \"location_view\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"identify_places\",\n\t\t\t\"label\": \"Identify Places\",\n\t\t\t\"icon\": \"map_pin\",\n\t\t\t\"action\": \"identify_places_in_location\",\n\t\t\t\"context\": \"location_view\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"create_moment\",\n\t\t\t\"label\": \"Create Moment\",\n\t\t\t\"icon\": \"calendar\",\n\t\t\t\"action\": \"create_moments_from_selection\"\n\t\t}\n\t],\n\t\"file_viewers\": [\n\t\t{\n\t\t\t\"id\": \"photo_viewer\",\n\t\t\t\"bundle\": \"ui/photo_viewer.js\",\n\t\t\t\"mime_types\": [\n\t\t\t\t\"image/jpeg\",\n\t\t\t\t\"image/png\",\n\t\t\t\t\"image/heic\",\n\t\t\t\t\"image/webp\"\n\t\t\t],\n\t\t\t\"component\": \"photo_viewer\",\n\t\t\t\"features\": {\n\t\t\t\t\"slideshow\": true,\n\t\t\t\t\"metadata_panel\": true,\n\t\t\t\t\"face_indicators\": true,\n\t\t\t\t\"related_photos\": true\n\t\t\t}\n\t\t}\n\t],\n\t\"search_filters\": [\n\t\t{\n\t\t\t\"id\": \"has_people\",\n\t\t\t\"label\": \"Has People\",\n\t\t\t\"type\": \"boolean\",\n\t\t\t\"query\": \"tags LIKE '#person:%'\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"location\",\n\t\t\t\"label\": \"Location\",\n\t\t\t\"type\": \"place_picker\",\n\t\t\t\"query_template\": \"tags LIKE '#place:{value}'\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"date_range\",\n\t\t\t\"label\": \"Date\",\n\t\t\t\"type\": \"date_range\",\n\t\t\t\"query_template\": \"exif.taken_at BETWEEN '{start}' AND '{end}'\"\n\t\t},\n\t\t{\n\t\t\t\"id\": \"camera\",\n\t\t\t\"label\": \"Camera\",\n\t\t\t\"type\": \"select\",\n\t\t\t\"options_query\": \"SELECT DISTINCT exif.camera_model FROM photos\",\n\t\t\t\"query_template\": \"exif.camera_model = '{value}'\"\n\t\t}\n\t]\n}\n\n<!-- File: extensions/test-extension/manifest.json (Tokens: ~240 | Category: anthropic-skill) -->\n{\n\t\"id\": \"test-extension\",\n\t\"name\": \"Test Extension\",\n\t\"version\": \"0.1.0\",\n\t\"description\": \"Minimal extension demonstrating beautiful SDK API\",\n\t\"author\": \"Spacedrive Team\",\n\t\"homepage\": \"https://spacedrive.com\",\n\t\"wasm_file\": \"test_extension.wasm\",\n\t\"permissions\": {\n\t\t\"read_records\": {},\n\t\t\"write_sidecars\": [\n\t\t\t\"digest\"\n\t\t],\n\t\t\"write_tags\": true,\n\t\t\"write_custom_fields\": [\n\t\t\t\"test\"\n\t\t],\n\t\t\"dispatch_jobs\": true\n\t},\n\t\"kinds\": [\n\t\t{\n\t\t\t\"name\": \"fake\",\n\t\t\t\"display_name\": \"Fake file\",\n\t\t\t\"parent\": \"text\",\n\t\t\t\"extensions\": [\n\t\t\t\t\"fake\"\n\t\t\t],\n\t\t\t\"magic\": [\n\t\t\t\t{\n\t\t\t\t\t\"pattern\": \"46 41 4B 45\",\n\t\t\t\t\t\"offset\": 0\n\t\t\t\t}\n\t\t\t],\n\t\t\t\"preview\": {\n\t\t\t\t\"viewer\": \"fake_viewer\"\n\t\t\t}\n\t\t}\n\t]\n}\n\n<!-- File: packages/assets/lottie/loading-pulse.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Lottie Skill\n\nPath: `packages/assets/lottie/loading-pulse.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/lottie/loading-pulse.json)\n\n<!-- File: packages/assets/package.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Assets Skill\n\nPath: `packages/assets/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/package.json)\n\n<!-- File: packages/assets/svgs/ext/icons.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Ext Skill\n\nPath: `packages/assets/svgs/ext/icons.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/svgs/ext/icons.json)\n\n<!-- File: packages/config/app.tsconfig.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Config Skill\n\nPath: `packages/config/app.tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/app.tsconfig.json)\n\n<!-- File: packages/config/base.tsconfig.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Config Skill\n\nPath: `packages/config/base.tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/base.tsconfig.json)\n\n<!-- File: packages/config/package.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Config Skill\n\nPath: `packages/config/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/package.json)\n\n<!-- File: packages/config/tsconfig.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Config Skill\n\nPath: `packages/config/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/config/tsconfig.json)\n\n<!-- File: packages/interface/CLAUDE.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Interface Skill\n\nPath: `packages/interface/CLAUDE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/CLAUDE.md)\n\n<!-- File: packages/interface/package.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Interface Skill\n\nPath: `packages/interface/package.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/package.json)\n\n<!-- File: packages/interface/SHARED-UI-STRATEGY.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Interface Skill\n\nPath: `packages/interface/SHARED-UI-STRATEGY.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/SHARED-UI-STRATEGY.md)\n\n<!-- File: packages/interface/src/components/modals/FileOperationModal.README.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Modals Skill\n\nPath: `packages/interface/src/components/modals/FileOperationModal.README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/components/modals/FileOperationModal.README.md)\n\n<!-- File: packages/interface/src/contributors.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Src Skill\n\nPath: `packages/interface/src/contributors.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/contributors.json)\n\n<!-- File: packages/interface/src/Spacebot/VISION.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Spacebot Skill\n\nPath: `packages/interface/src/Spacebot/VISION.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/Spacebot/VISION.md)\n\n<!-- File: packages/interface/tsconfig.json (Tokens: ~300 | Category: anthropic-skill) -->\n# Interface Skill\n\nPath: `packages/interface/tsconfig.json`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/tsconfig.json)\n\n<!-- File: packages/ts-client/package.json (Tokens: ~818 | Category: anthropic-skill) -->\n{\n\t\"name\": \"@sd/ts-client\",\n\t\"version\": \"0.1.0\",\n\t\"description\": \"Type-safe TypeScript client for Spacedrive daemon\",\n\t\"main\": \"dist/index.js\",\n\t\"types\": \"dist/index.d.ts\",\n\t\"exports\": {\n\t\t\".\": {\n\t\t\t\"types\": \"./src/index.ts\",\n\t\t\t\"default\": \"./src/index.ts\"\n\t\t},\n\t\t\"./generated/types\": {\n\t\t\t\"types\": \"./src/generated/types.ts\",\n\t\t\t\"default\": \"./src/generated/types.ts\"\n\t\t},\n\t\t\"./src/generated/types\": {\n\t\t\t\"types\": \"./src/generated/types.ts\",\n\t\t\t\"default\": \"./src/generated/types.ts\"\n\t\t},\n\t\t\"./hooks\": {\n\t\t\t\"types\": \"./src/hooks/index.ts\",\n\t\t\t\"default\": \"./src/hooks/index.ts\"\n\t\t},\n\t\t\"./hooks/useClient\": {\n\t\t\t\"types\": \"./src/hooks/useClient.tsx\",\n\t\t\t\"default\": \"./src/hooks/useClient.tsx\"\n\t\t},\n\t\t\"./hooks/useNormalizedQuery\": {\n\t\t\t\"types\": \"./src/hooks/useNormalizedQuery.ts\",\n\t\t\t\"default\": \"./src/hooks/useNormalizedQuery.ts\"\n\t\t},\n\t\t\"./src/hooks/useClient\": {\n\t\t\t\"types\": \"./src/hooks/useClient.tsx\",\n\t\t\t\"default\": \"./src/hooks/useClient.tsx\"\n\t\t},\n\t\t\"./src/hooks/useNormalizedQuery\": {\n\t\t\t\"types\": \"./src/hooks/useNormalizedQuery.ts\",\n\t\t\t\"default\": \"./src/hooks/useNormalizedQuery.ts\"\n\t\t}\n\t},\n\t\"scripts\": {\n\t\t\"build\": \"tsc\",\n\t\t\"dev\": \"tsc --watch\",\n\t\t\"test\": \"jest\",\n\t\t\"generate-types\": \"cargo run --bin generate_typescript_types --manifest-path ../../core/Cargo.toml\"\n\t},\n\t\"keywords\": [\n\t\t\"spacedrive\",\n\t\t\"client\",\n\t\t\"typescript\",\n\t\t\"daemon\"\n\t],\n\t\"author\": \"Spacedrive\",\n\t\"license\": \"Apache-2.0\",\n\t\"peerDependencies\": {\n\t\t\"@tanstack/react-query\": \"^5.0.0\",\n\t\t\"react\": \"^18.0.0 || ^19.0.0\"\n\t},\n\t\"peerDependenciesMeta\": {\n\t\t\"@tanstack/react-query\": {\n\t\t\t\"optional\": true\n\t\t},\n\t\t\"react\": {\n\t\t\t\"optional\": true\n\t\t}\n\t},\n\t\"devDependencies\": {\n\t\t\"@happy-dom/global-registrator\": \"^15.11.0\",\n\t\t\"@testing-library/jest-dom\": \"^6.0.0\",\n\t\t\"@testing-library/react\": \"^16.0.0\",\n\t\t\"@types/jest\": \"^29.0.0\",\n\t\t\"@types/node\": \"^20.0.0\",\n\t\t\"happy-dom\": \"^15.11.0\",\n\t\t\"jest\": \"^29.0.0\",\n\t\t\"jest-environment-jsdom\": \"^29.0.0\",\n\t\t\"jsdom\": \"^27.2.0\",\n\t\t\"ts-jest\": \"^29.0.0\",\n\t\t\"typescript\": \"^5.0.0\"\n\t},\n\t\"dependencies\": {\n\t\t\"@types/ws\": \"^8.0.0\",\n\t\t\"@sd/assets\": \"workspace:*\",\n\t\t\"ws\": \"^8.0.0\",\n\t\t\"zustand\": \"^5.0.8\",\n\t\t\"ts-deepmerge\": \"^7.0.1\",\n\t\t\"tiny-invariant\": \"^1.3.3\",\n\t\t\"valibot\": \"^1.0.0\",\n\t\t\"type-fest\": \"^4.30.0\"\n\t},\n\t\"files\": [\n\t\t\"dist/**/*\",\n\t\t\"src/**/*\"\n\t]\n}\n\n<!-- File: packages/ts-client/tsconfig.json (Tokens: ~205 | Category: anthropic-skill) -->\n{\n\t\"compilerOptions\": {\n\t\t\"target\": \"ES2020\",\n\t\t\"module\": \"commonjs\",\n\t\t\"lib\": [\"ES2020\"],\n\t\t\"jsx\": \"react-jsx\",\n\t\t\"outDir\": \"./dist\",\n\t\t\"rootDir\": \"./src\",\n\t\t\"strict\": true,\n\t\t\"esModuleInterop\": true,\n\t\t\"skipLibCheck\": true,\n\t\t\"forceConsistentCasingInFileNames\": true,\n\t\t\"declaration\": true,\n\t\t\"declarationMap\": true,\n\t\t\"sourceMap\": true,\n\t\t\"moduleResolution\": \"node\",\n\t\t\"allowSyntheticDefaultImports\": true,\n\t\t\"resolveJsonModule\": true,\n\t\t\"types\": [\"jest\", \"node\"]\n\t},\n\t\"include\": [\"src/**/*\"],\n\t\"exclude\": [\"node_modules\", \"dist\", \"**/*.test.ts\", \"**/*.test.tsx\", \"**/__tests__/**\"]\n}\n\n<!-- File: packages/ts-client/USAGE.md (Tokens: ~300 | Category: anthropic-skill) -->\n# Ts-client Skill\n\nPath: `packages/ts-client/USAGE.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/ts-client/USAGE.md)\n\n<!-- File: .tasks/Claude.md (Tokens: ~1430 | Category: claude-rule) -->\n# Task Tracking Cheatsheet\n\nHi Claude, Claude here. Your owner has pointed you here with absolutely no context. This document is your prompt.\n\nThis directory tracks the complete development of Spacedrive. Tasks are organized into subdirectories:\n- `core/` - Backend/Rust tasks (CORE, JOB, INDEX, LSYNC, etc.)\n- `interface/` - Frontend/React tasks (UI, EXPL, SETS, MEDIA, etc.)\n- `mobile/` - Mobile-specific tasks (IOS, MACOS, AND)\n\nTasks are always kept up-to-date, though some may need updating based on recent work. Your job is to check git history for changes, validate completed tasks against the instructions below, and update their status accordingly.\n\n## Quick Start\n\nWhen asked to review and update tasks, follow this process:\n\n### 1. List Current Tasks\n\n```bash\ncargo run --bin task-validator -- list --sort-by status\n```\n\nThis shows all tasks grouped by status (Completed, Done, In Progress, To Do).\n\n### 2. Review Recent Commits\n\n```bash\ngit log --oneline -20\n```\n\nLook for feature implementations, major refactors, or completed work that might correspond to tasks.\n\n### 3. Cross-Reference Work with Tasks\n\nFor each recent feature/change:\n\n- **Read the task file** directly from `.tasks/{subdirectory}/TASK-ID-name.md`\n  - Core/backend work: `.tasks/core/`\n  - Interface work: `.tasks/interface/`\n  - Mobile work: `.tasks/mobile/`\n- **Read the acceptance criteria** carefully - this is your checklist\n- **Read all implementation files** mentioned in the task (they're usually listed in an \"Implementation Files\" section)\n- **Check core/tests/** for integration tests that validate the feature (for core tasks)\n- **Check packages/interface/src/** for interface implementations (for interface tasks)\n- Verify each acceptance criterion is actually met in the code\n\n### 4. Update Task Status\n\nWhen updating a task's status, edit the YAML front matter:\n\n```yaml\n---\nid: TASK-000\ntitle: Task Title\nstatus: Done # Changed from \"To Do\" or \"In Progress\"\nassignee: jamiepine\npriority: High\ntags: [core, feature]\nlast_updated: 2025-10-14 # Update this date\n---\n```\n\n**Status Levels:**\n\n- `To Do` - Not started\n- `In Progress` - Actively being worked on\n- `Done` - Complete and merged\n- `Completed` - Same as Done (legacy)\n\n### 5. Validate Changes\n\nBefore committing task updates, validate them:\n\n```bash\ncargo run --bin task-validator -- validate\n```\n\nThis checks that your YAML front matter matches the schema.\n\n## Task Status Guidelines\n\n### Mark as \"Done\" when:\n\n- All acceptance criteria are met\n- Code is merged to main\n- Tests pass (if applicable)\n- Feature is actually usable\n\n### Keep \"In Progress\" when:\n\n- ️ Partially implemented\n- ️ Core work done but rough edges remain\n- ️ Actively being iterated on\n\n### Keep \"To Do\" when:\n\n- Not started\n- Only design/planning done\n- Mentioned in docs but no code exists\n\n## Pro Tips\n\n1. **Read implementation files directly** - Open and read the actual source files mentioned in the task.\n\n2. **Always check core/tests/** for integration tests that validate the feature works end-to-end.\n\n3. **Check recent file changes:**\n\n   ```bash\n   git diff --name-only HEAD~10\n   ```\n\n4. **Search commits by keyword:**\n\n   ```bash\n   git log --oneline --grep=\"volume\\|cloud\" -i\n   ```\n\n5. **Filter tasks by criteria:**\n\n   ```bash\n   # Show only \"In Progress\" tasks\n   cargo run --bin task-validator -- list --status \"In Progress\"\n\n   # Show tasks by priority\n   cargo run --bin task-validator -- list --priority High\n   ```\n\n## Common Pitfalls\n\n**Don't mark tasks done if:**\n\n- Implementation exists but doesn't work\n- Only scaffolding/types are defined\n- Tests are failing\n- Feature is behind a feature flag and disabled\n\n**Do mark tasks done if:**\n\n- All acceptance criteria are genuinely met\n- Implementation is production-ready\n- Code demonstrates the feature working\n\n## Example Workflow\n\n```bash\n# 1. See current state\ncargo run --bin task-validator -- list --sort-by status\n\n# 2. Check what's been done recently\ngit log --oneline -15\n\n# 3. Found \"feat: implement cloud volume support\" commit\n#    Let's check CLOUD-003 task\n```\n\nNow use the Read tool to thoroughly verify:\n\n````\n# 4. Read the task file (note the subdirectory)\nRead .tasks/core/CLOUD-003-cloud-volume.md\n\n# 5. Read every implementation file listed in the task\nRead core/src/volume/backend/mod.rs\nRead core/src/volume/backend/cloud.rs\nRead core/src/ops/volumes/add_cloud/mod.rs\n... (read all mentioned files)\n\n# 6. Check for integration tests\nGlob core/tests/**/*.rs\nRead core/tests/test_cloud_volume.rs  # if it exists\n\n# 7. Verify each acceptance criterion:\n#    - [x] User can add S3 bucket? Check add_cloud action code\n#    - [x] Cloud volume can be indexed? Check indexer integration\n#    - [ ] Files can be copied to/from cloud? Search for copy implementation\n#\n# If ALL criteria met → mark \"Done\"\n# If SOME criteria met → keep \"In Progress\"\n# If implementation looks complete → Update status and last_updated date\n\n# 8. Validate before committing\n```bash\ncargo run --bin task-validator -- validate\n````\n\n## Task Schema Reference\n\nRequired fields in YAML front matter:\n\n- `id` - Unique identifier (e.g., CORE-001)\n- `title` - Human-readable title\n- `status` - One of: To Do, In Progress, Done, Completed\n- `assignee` - Who's working on it (or \"james\")\n- `priority` - Critical, High, Medium, or Low\n- `tags` - Array of relevant tags\n\nOptional fields:\n\n- `parent` - Parent epic/task ID\n- `whitepaper` - Reference to design docs\n- `last_updated` - ISO date of last update\n- `related_tasks` - Array of related task IDs\n\n---\n\n**Remember**: The task tracker is only useful if it's honest. Be rigorous about what \"Done\" means. When in doubt, leave it as \"In Progress\" and document what's left.\n\n— Past Claude, trying to help Future Claude (that's you!)\n\n<!-- File: extensions/photos/README.md (Tokens: ~1439 | Category: plugin-manifest) -->\n# Photos Extension for Spacedrive\n\nA comprehensive photo management extension that brings Apple Photos and Google Photos capabilities to Spacedrive.\n\n## Features\n\n### Face Recognition\n- **Automatic face detection** using RetinaFace model\n- **Face clustering** with DBSCAN algorithm\n- **Person identification** with name labeling\n- **Face-based search** (\"Show me photos of Alice\")\n- **Cross-device sync** of face clusters\n\n### Place Identification\n- **GPS extraction** from EXIF data\n- **Geographic clustering** (groups photos within 500m)\n- **Reverse geocoding** using AI\n- **Place-based search** (\"Photos from Paris\")\n- **Map view** of photo locations\n\n### Moments\n- **Automatic moment generation** (time + location clustering)\n- **AI-generated titles** (\"Summer in Paris\")\n- **Weekly memories** (scheduled agent task)\n- **Moment timeline** view\n\n### ️ Scene Understanding\n- **Scene classification** (ResNet50 on Places365)\n- **Smart tags** (#beach, #sunset, #food)\n- **Scene-based search** (\"Photos with sunsets\")\n- **Quality scoring** (aesthetic assessment)\n\n### Albums & Organization\n- **Manual albums** (user-created)\n- **Smart albums** (rule-based)\n- **Favorites** (special album)\n- **Hidden photos** (privacy)\n- **Shared albums** (multi-device)\n\n## Architecture\n\n### Models\n\n```rust\nPhoto       // Links to image file + EXIF + face/scene sidecars\nPerson      // Face cluster with name and embeddings\nPlace       // Geographic location with radius\nAlbum       // Collection of photos\nMoment      // Time/location-based photo group\n```\n\n### Agent Memory\n\n```rust\nPhotosMind {\n    history: TemporalMemory<PhotoEvent>,     // Analysis timeline\n    knowledge: AssociativeMemory<PhotoKnowledge>, // Face/place graph\n    plan: WorkingMemory<AnalysisPlan>,       // Pending work\n}\n```\n\n### Jobs\n\n- `analyze_photos_batch` - Face detection on photos\n- `identify_places_in_location` - Place clustering and naming\n- `analyze_scenes` - Scene classification\n- `create_moments` - Automatic moment generation\n- `cluster_faces_into_people` - Face clustering\n- `generate_face_tags` - Tag generation from sidecars\n\n### Actions\n\n- `create_album` - User creates album\n- `identify_person` - Name a face cluster\n- `remove_photo_from_album` - Organize albums\n- `hide_photo` - Privacy controls\n\n## Usage\n\n### Installation\n\n1. Install extension from Spacedrive Extension Store\n2. Grant permissions to specific photo locations:\n   - `/Users/alice/Photos`\n   - `/Volumes/External/Family Photos`\n3. Extension downloads AI models (~107MB total)\n4. User initiates analysis: \"Analyze for Faces\" button\n\n### Analysis Flow\n\n```\nUser enables Photos on \"/My Photos\"\n  ↓\nExtension dispatches analyze_photos_batch job\n  ↓\nFor each photo:\n  - Read EXIF (from Core)\n  - Detect faces → save to sidecar\n  - Classify scene → save to sidecar\n  ↓\nCluster faces into people\n  ↓\nGenerate tags from sidecars\n  ↓\nUser can search \"#person:alice\" or \"photos from beach\"\n```\n\n### Data Storage\n\n```\n~/.spacedrive/\n  └── models/\n      ├── face_detection/\n      │   └── photos_v1.onnx (12MB)\n      └── scene_classification/\n          └── resnet50.onnx (95MB)\n\n.sdlibrary/\n  ├── sidecars/\n  │   └── content/{ab}/{cd}/{uuid}/\n  │       └── extensions/com.spacedrive.photos/\n  │           ├── faces/default.json       # Face detection results\n  │           ├── scene/default.json       # Scene classification\n  │           └── aesthetics/default.json  # Quality score\n  └── extensions/com.spacedrive.photos/\n      └── data.db                           # Person, Place, Moment, Album, Photo rows\n```\n\n## SDK Features Demonstrated\n\n### Implemented in Example\n\n- `#[extension]` with permissions and dependencies\n- `#[model]` for Photo, Person, Place, Album, Moment\n- `#[agent]` with lifecycle hooks and event handlers\n- `#[agent_memory]` with enum-based Temporal/Associative memory\n- `#[job]` and `#[task]` for durable processing\n- `#[action]` with preview-execute pattern\n- `#[query]` for user searches\n- Extension-owned sidecars\n- Virtual models with persistence\n- User-scoped permissions\n- Model registration on install\n- AI with Jinja templates\n- Tag generation from sidecars\n- Custom memory query methods\n\n### What runs today\n\nUnder a daemon built with `--features wasm`, `analyze_photos` runs end to end\nover a library: it reads each photo through its record, asks for face\ndetection, and because the core has no inference provider the host answers\n`not_available`; the job warns once, skips, and completes, so a later run with\na detector picks the photos up. `core/tests/wasm_extension_test.rs` proves\nthis against a fixture library.\n\n### Not yet backed by the core\n\n- Inference. There is no provider for face detection, scene classification,\n  embeddings or language models, so `analyze_scenes`, `identify_places` and\n  `create_moments` have nothing to run on.\n- Tags, custom fields and `dispatch_jobs`.\n- Agents, actions and queries.\n- Model downloads named in `manifest.json`.\n- File-kind and preview registration.\n\n## Capabilities Compared\n\n### Apple Photos\n- Face recognition and clustering\n- Place identification\n- Memories\n- Albums (manual and smart)\n- Favorites and hidden\n- Search by person/place/scene\n- Map view\n- ️ Shared albums (requires P2P implementation)\n\n### Google Photos\n- Face grouping\n- Place detection\n- Search by content\n- Albums\n- Automatic creations (moments)\n- Scene/object detection\n- ️ Cloud backup (Spacedrive handles differently)\n\n### Spacedrive Photos Advantages\n- **100% local** - No cloud upload required\n- **Privacy-first** - Face data never leaves devices\n- **Multi-device sync** - Via P2P, not cloud\n- **Zero recurring cost** - No subscription needed\n- **User-scoped** - Only analyzes chosen locations\n- **Extensible** - Open SDK for customization\n\n## Building\n\n```bash\ncd extensions/photos\ncargo build --target wasm32-unknown-unknown --release\ncp target/wasm32-unknown-unknown/release/photos_extension.wasm ./photos.wasm\n```\n\n## License\n\nSame as Spacedrive Core\n\n<!-- File: extensions/README.md (Tokens: ~1610 | Category: plugin-manifest) -->\n# Spacedrive Official Extensions\n\nThis directory contains the extension SDK and official extensions for Spacedrive.\n\n## Structure\n\n```\nextensions/\n├── spacedrive-sdk/          # Core SDK library\n├── spacedrive-sdk-macros/   # Proc macros for beautiful API\n├── test-extension/          # Example extension with beautiful API\n└── finance/                 # (Future) First revenue-generating extension\n```\n\n## Quick Start\n\n### 1. Install WASM Target\n\n```bash\nrustup target add wasm32-unknown-unknown\n```\n\n### 2. Create Extension\n\n```bash\ncargo new --lib my-extension\ncd my-extension\n```\n\n**Cargo.toml:**\n```toml\n[lib]\ncrate-type = [\"cdylib\"]\n\n[dependencies]\nspacedrive-sdk = { path = \"../spacedrive-sdk\" }\nserde = { version = \"1.0\", features = [\"derive\"] }\n```\n\n**src/lib.rs:**\n```rust\nuse spacedrive_sdk::prelude::*;\nuse spacedrive_sdk::{extension, job};\n\n#[extension(\n    id = \"my-extension\",\n    name = \"My Extension\",\n    version = \"0.1.0\"\n)]\nstruct MyExtension;\n\n#[derive(Serialize, Deserialize, Default)]\npub struct MyJobState {\n    pub counter: u32,\n}\n\n#[job]\nfn my_job(ctx: &JobContext, state: &mut MyJobState) -> Result<()> {\n    ctx.log(\"Job starting!\");\n\n    state.counter += 1;\n    ctx.report_progress(1.0, \"Done!\");\n\n    Ok(())\n}\n```\n\n### 3. Build\n\n```bash\ncargo build --target wasm32-unknown-unknown --release\ncp target/wasm32-unknown-unknown/release/my_extension.wasm .\n```\n\n### 4. Create manifest.json\n\n```json\n{\n  \"id\": \"my-extension\",\n  \"name\": \"My Extension\",\n  \"version\": \"0.1.0\",\n  \"wasm_file\": \"my_extension.wasm\",\n  \"permissions\": {\n    \"read_records\": { \"glob\": \"**/*.{jpg,png}\" },\n    \"read_sidecars\": [\"exif\"],\n    \"write_sidecars\": [\"digest\"],\n    \"use_models\": [{ \"category\": \"face_detection\", \"preference\": \"local\" }]\n  }\n}\n```\n\nThe host rejects a manifest with a field it does not know, so a typo in a\ngrant fails at load. `read_records` is the only way to see files; its optional\n`glob` is honored for its trailing extension list only (`*.jpg` or\n`**/*.{jpg,png}`); the directory part is ignored and a glob of another shape\nis refused at load. `write_sidecars` names the kinds\nthe extension may write; `use_models` the inference categories it may ask for.\n`write_tags`, `write_custom_fields` and `dispatch_jobs` are declared but have\nno host side yet.\n\n## The Beautiful API\n\n### Before Macros (Manual FFI):\n```rust\n#[no_mangle]\npub extern \"C\" fn execute_my_job(\n    ctx_ptr: u32, ctx_len: u32,\n    state_ptr: u32, state_len: u32\n) -> i32 {\n    let ctx_json = unsafe { /* 30 lines of pointer manipulation */ };\n    let mut state = /* 40 lines of deserialization */;\n    // ... business logic buried in boilerplate ...\n}\n```\n**180+ lines, lots of unsafe**\n\n### After Macros (Beautiful):\n```rust\n#[job]\nfn my_job(ctx: &JobContext, state: &mut MyJobState) -> Result<()> {\n    // Just write business logic!\n    ctx.log(\"Working...\");\n    state.counter += 1;\n    Ok(())\n}\n```\n**60-80 lines, zero unsafe, pure logic**\n\n## API Reference\n\n### Extension Container\n\n```rust\n#[extension(\n    id = \"finance\",\n    name = \"Spacedrive Finance\",\n    version = \"0.1.0\"\n)]\nstruct Finance;\n```\n\nGenerates:\n- `plugin_init()` export\n- `plugin_cleanup()` export\n- Metadata for manifest generation\n\n### Job Definition\n\n```rust\n#[job]\nfn email_scan(ctx: &JobContext, state: &mut EmailScanState) -> Result<()> {\n    // Progress reporting\n    ctx.report_progress(0.5, \"Half done\");\n\n    // Checkpointing\n    ctx.checkpoint(state)?;\n\n    // Interruption handling\n    if ctx.check_interrupt() {\n        return Err(Error::OperationFailed(\"Interrupted\".into()));\n    }\n\n    // Metrics\n    ctx.increment_items(1);\n    ctx.increment_bytes(1000);\n\n    // Warnings\n    ctx.add_warning(\"Non-fatal issue\");\n\n    // Full SDK access\n    let record = ctx.vdfs().get_record(uuid).await?;\n    let bytes = record.read().await?;\n\n    Ok(())\n}\n```\n\n### VDFS Operations\n\n```rust\n// Records of the library's sources, filtered by extension (read_records grant)\nlet photos = ctx.vdfs()\n    .query_records()\n    .with_extensions([\"jpg\", \"png\"])\n    .collect()\n    .await?;\n\n// One record and its bytes\nlet record = ctx.vdfs().get_record(uuid).await?;\nlet bytes = record.read().await?;\n\n// Sidecars are one JSON document per kind, keyed by content uuid\nlet content_uuid = record.content_uuid().expect(\"hashed\");\nif !ctx.sidecar_exists(content_uuid, \"digest\")? {\n    ctx.save_sidecar(content_uuid, \"digest\", &digest).await?;\n}\nlet digest: Digest = ctx.read_sidecar(content_uuid, \"digest\").await?;\n\n// Models declared with #[model] live in the extension's own store\nctx.vdfs().create_model(entry).await?;\nlet all: Vec<CatalogEntry> = ctx.vdfs().query_models().collect().await?;\n```\n\nTags and custom fields have no host side yet and return `Error::NotAvailable`.\n\n### AI Operations\n\nInference goes through `ctx.ai().infer(...)` behind the `use_models` grant.\nThe core has no provider yet, so every request answers `Error::NotAvailable`;\nhandle it the way `extensions/photos/src/jobs/analyze.rs` does, with one\nwarning and a skip, so the job completes and a later run with a provider\npicks the files up.\n\n```rust\n// Written against the API; answers NotAvailable until a provider exists\nlet faces = ctx.ai().from_registered(\"face_detection\").detect_faces(&bytes).await?;\n```\n\n### Credentials\n\n```rust\n// Store OAuth\nctx.credentials().store(\"gmail\", Credential::oauth2(\n    access_token,\n    Some(refresh_token),\n    3600,\n    vec![\"https://www.googleapis.com/auth/gmail.readonly\".into()]\n))?;\n\n// Get (auto-refreshes)\nlet cred = ctx.credentials().get(\"gmail\")?;\n```\n\n## Examples\n\nSee `extensions/test-extension/` for a complete working example.\n\n## Building\n\nAll extensions:\n```bash\ncd extensions/test-extension\ncargo build --target wasm32-unknown-unknown --release\n```\n\n## Documentation\n\n- **[SDK API Vision](../docs/EXTENSION_SDK_API_VISION.md)** - Future API improvements\n- **[Before/After Comparison](./BEFORE_AFTER_COMPARISON.md)** - See the transformation\n- **[WASM Architecture](../docs/core/design/WASM_ARCHITECTURE_FINAL.md)** - Technical details\n- **[Platform Revenue Model](../docs/PLATFORM_REVENUE_MODEL.md)** - Business case\n\n---\n\n**Extension development is now beautiful, safe, and productive. Start building!**\n\n<!-- File: extensions/test-extension/README.md (Tokens: ~759 | Category: plugin-manifest) -->\n# Test Extension\n\n**The canonical example of Spacedrive extension development.**\n\nThis extension demonstrates the beautiful, macro-powered API that makes building extensions delightful.\n\n## Features\n\n**Zero Boilerplate** - Macros generate all FFI code\n**Type-Safe** - Full Rust type system\n**No Unsafe** - Safe by default\n**Clean API** - Just write business logic\n\n## Code\n\n**Complete extension in 76 lines:**\n\n```rust\nuse spacedrive_sdk::prelude::*;\nuse spacedrive_sdk::{extension, job};\n\n// Extension definition\n#[extension(\n    id = \"test-extension\",\n    name = \"Test Extension\",\n    version = \"0.1.0\"\n)]\nstruct TestExtension;\n\n// Job state\n#[derive(Serialize, Deserialize, Default)]\npub struct CounterState {\n    pub current: u32,\n    pub target: u32,\n}\n\n// Job implementation - THAT'S IT!\n#[job]\nfn test_counter(ctx: &JobContext, state: &mut CounterState) -> Result<()> {\n    while state.current < state.target {\n        ctx.check_interrupt()?;\n        state.current += 1;\n        ctx.report_progress(\n            state.current as f32 / state.target as f32,\n            &format!(\"Counted {}/{}\", state.current, state.target)\n        );\n        if state.current % 10 == 0 {\n            ctx.checkpoint(state)?;\n        }\n    }\n    Ok(())\n}\n```\n\n## What the Macros Generate\n\nThe `#[extension]` and `#[job]` macros automatically generate:\n\n- `plugin_init()` - Extension initialization\n- `plugin_cleanup()` - Extension cleanup\n- `execute_test_counter()` - FFI export with full state management\n- All pointer marshalling\n- Serialization/deserialization\n- Error handling\n- Progress tracking\n- Checkpoint management\n\n**~120 lines of boilerplate you don't write!**\n\n## Building\n\n```bash\ncargo build --target wasm32-unknown-unknown --release\n```\n\nOutput: `target/wasm32-unknown-unknown/release/test_extension.wasm` (~254KB)\n\n## Capabilities Demonstrated\n\n### Job System\n- Progress reporting (0-100%)\n- Checkpointing (resume after crash)\n- Interruption handling (pause/cancel)\n- Metrics tracking (items processed)\n- State persistence\n\n### API Ergonomics\n- Clean function signatures\n- `?` operator for error handling\n- No FFI knowledge required\n- No unsafe code\n- Just write Rust!\n\n## Testing\n\nOnce Core is running:\n```rust\n// Load extension\nplugin_manager.load_plugin(\"test-extension\").await?;\n\n// Dispatch job\nlet job_id = job_manager.dispatch_by_name(\n    \"test-extension:test_counter\",\n    json!({ \"target\": 100 })\n).await?;\n\n// Watch progress in logs:\n// INFO Counted 10/100 (10% complete)\n// INFO Counted 20/100 (20% complete)\n// ...\n// INFO ✓ Completed! Processed 100 items\n```\n\n## Comparison\n\n| Metric | Manual FFI | With Macros | Improvement |\n|--------|-----------|-------------|-------------|\n| Lines of Code | 181 | 76 | 58% less |\n| Unsafe Blocks | 4 | 0 | 100% safer |\n| Boilerplate | 120 lines | 10 lines | 92% less |\n| WASM Size | 252KB | 254KB | Same |\n| Readability | 5/10 | 10/10 | Much better |\n| Dev Time | 2-3 hours | 15 minutes | 10x faster |\n\n---\n\n**This is what all Spacedrive extensions should look like going forward!**\n\n<!-- File: packages/assets/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# assets Documentation\n\nPath: `packages/assets/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/README.md)\n\n<!-- File: packages/assets/sounds/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# sounds Documentation\n\nPath: `packages/assets/sounds/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/assets/sounds/README.md)\n\n<!-- File: packages/interface/src/components/Inspector/variants/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# variants Documentation\n\nPath: `packages/interface/src/components/Inspector/variants/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/interface/src/components/Inspector/variants/README.md)\n\n<!-- File: packages/swift-client/README.md (Tokens: ~300 | Category: plugin-manifest) -->\n# swift-client Documentation\n\nPath: `packages/swift-client/README.md`\n\n[View Raw Content on GitHub](https://raw.githubusercontent.com/spacedriveapp/spacedrive/HEAD/packages/swift-client/README.md)\n\n<!-- ... +2 more modular skills available via /api/skills/spacedriveapp/spacedrive?path={path} -->\n</yakaai_skills>"}