Purpose
Find the user's photos in Jarvis's uploaded library or a connected device gallery. The media-library CLI covers uploaded photos only.
Device Photos
Search connected galleries for explicit gallery requests, or when uploaded or attached images are insufficient for a personal photo request.
- Discover. Call
device.list, thendevice.describe. Choose a reachable device advertisingphotos.searchincommands: user-named first, thenis_request_origin, then another phone. Clarify ambiguity. - Search. Invoke
photos.search, narrowing with its advertised filters; albums mean folders. Respect permission denial and retry after required setup succeeds. Follow pagination and report incomplete coverage rather than claiming no matches. - Upload. Search returns metadata. For viewing or comparison, upload a shortlist using the advertised commands. Wait for completion and account for partial failures; acceptance is not completion.
- Locate. After completion, run
/opt/hatch/bin/media-library recent --limit 20for paths/IDs. Match capture dates, filenames, and GPS against device results; usegetfor details and date/GPS searches for older or already-uploaded photos. Filenames or recency alone cannot prove identity. Read confidently matched images; report uncertainty rather than guessing paths.
Tooling
Use exec to run /opt/hatch/bin/media-library <subcommand> [options] for uploaded photos.
Subcommands:
stats— library overview (total count, date range, description status)search [--query "<terms>"] [--after YYYY-MM-DD] [--before YYYY-MM-DD] [--lat <f64>] [--long <f64>] [--radius <km>] [--has-gps] [--path <prefix>] [--limit N]— when--queryis present, FTS ranked by BM25; otherwise structured filtering sorted by taken daterecent [--limit N]— latest uploads by upload time (default 20, max 50)scan [--ids <id1,id2,...>] [--query "<terms>"] [--after YYYY-MM-DD] [--before YYYY-MM-DD] [--limit N]— richer summaries with full descriptions; requires at least one narrowing option (any of the flags above, including--limitalone)get <media_id>— full detail for one photo (JSON)
Output contract:
stats: plain text withTotal photos,Date range, and description status counts (ready,pending,failed,rejected).search: each result line:<media_id> | <path> | <date> | [<location> |] <summary_short>, with optionalmatch:snippet below.recent: same compact format assearch, without match snippets.scan: text blocks per item:media_id,path,date,location,summary,detail.get: JSON withmedia_id,rel_path,mime_type,size_bytes,taken_at_local,taken_at_local_date,gps_latitude,gps_longitude,location_text,location_json,exif_json,description_status,description(nested:summary_short,summary_full,people_text,activity_text,objects_text,ocr_text,location_hint_text).
The path field in results is relative to the home directory.
Operating Rules
- Workflow.
statson first use →searchorrecent→scanfor richer detail on candidates →getfor full info →readthe image only if the user wants to see it or the description is insufficient. Most queries can be answered from descriptions alone. - Stats interpretation. If total is 0, the uploaded library is empty or not set up yet — mention this briefly and check a connected device gallery, if available, for personal photo requests. If total is low (under ~20), note the library is small. If many are pending, text search will miss undescribed items — prefer
recentor date-filtered searches instead. - Opportunistic vs primary use. When the media library may help answer the question, do a quick probe —
statsplus asearchorrecent— and move on if nothing useful comes back. When the user is explicitly asking to find, show, identify, date, or locate a photo, treat the media library as a primary source and iterate harder with multiple searches,scan, andget. - Uploaded-library searches are cheap local PostgreSQL lookups (typically milliseconds). Don't hesitate to run many queries — try different terms, filters, and angles until you find what the user is looking for.
- FTS semantics. Bare terms are joined with implicit OR, ranked by BM25 (more matching terms = higher rank). AND/OR/NOT are supported. Terms are prefix-matched after punctuation stripping. Adding more OR terms makes results broader, not narrower — to narrow, use AND, date filters, or GPS filters instead.
- Start with the user's words, not guessed content. Broaden with synonyms, context from memory or conversation, or related terms only after direct queries come up short or return irrelevant results.
- Location. Geocoded city/county, state, and country names are in the search index —
search --query "Tokyo"works. For GPS proximity, callmap.geocodewith the place asquery, then use the returned coordinates withsearch --lat <lat> --long <lon> --radius <km>(scale:0.1for a spot,5for a neighborhood,50for a metro area). - People. Use physical descriptions ("woman in red dress"), not names.
- Memory. Cross-reference memory for dates and locations when the user mentions events or trips.
- When finding a specific photo, show what you found and confirm it's the right one.