From immich-photo-manager
Analyzes photo timeline month-by-month to find gaps, failed imports, or missing backups. Use after multi-source import to verify coverage.
How this skill is triggered — by the user, by Claude, or both
Slash command
/immich-photo-manager:timeline-gapsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Before doing ANYTHING else in this skill, call `ping` on the Immich MCP server.**
Before doing ANYTHING else in this skill, call ping on the Immich MCP server.
ping succeeds → proceed with the skill normally.ping fails or the MCP tools are not available → STOP. Do not continue. Tell the user:❌ Immich is not connected. This plugin needs a running Immich MCP server to work.
Run /setup-immich-photo-manager to configure your Immich connection. You'll need:
- Your Immich server URL (e.g.,
http://192.168.1.100:2283)- An Immich API key (how to create one)
- The MCP server configured (see /setup-immich-photo-manager)
Nothing in this plugin will work until the connection is configured.
Do NOT skip this check. Do NOT try to run any other tool first. Always ping, always block if it fails.
Analyze the photo timeline month by month to detect gaps, anomalies, and coverage issues across import sources. Helps users discover missing imports, backup failures, or periods where photos only exist in one ecosystem.
Generate a complete month-by-month matrix across all sources:
WITH months AS (
SELECT generate_series(
date_trunc('month', min("localDateTime")),
date_trunc('month', max("localDateTime")),
'1 month'::interval
) as month
FROM asset WHERE "deletedAt" IS NULL
),
source_counts AS (
SELECT
date_trunc('month', "localDateTime") as month,
CASE
WHEN "originalPath" LIKE '%Apple%' THEN 'Apple'
WHEN "originalPath" LIKE '%Google%' THEN 'Google'
ELSE 'Other'
END as source,
count(*) as cnt
FROM asset WHERE "deletedAt" IS NULL
GROUP BY 1, 2
)
SELECT
m.month,
coalesce(sum(cnt) FILTER (WHERE source = 'Apple'), 0) as apple,
coalesce(sum(cnt) FILTER (WHERE source = 'Google'), 0) as google,
coalesce(sum(cnt) FILTER (WHERE source = 'Other'), 0) as other,
coalesce(sum(cnt), 0) as total
FROM months m
LEFT JOIN source_counts sc ON m.month = sc.month
GROUP BY m.month
ORDER BY m.month;
Apply classification rules:
| Classification | Rule | Action |
|---|---|---|
| EMPTY | 0 photos total | Flag as critical gap |
| SPARSE | <10 photos AND user averaged >50/month that year | Flag as suspicious |
| SINGLE-SOURCE | One source has >90% of photos | Note dependency risk |
| NORMAL | Above thresholds | No action needed |
# Classification logic
avg_monthly = total_photos / total_months
for month in timeline:
if month.total == 0:
month.status = 'EMPTY'
elif month.total < max(10, avg_monthly * 0.1):
month.status = 'SPARSE'
elif month.dominant_source_pct > 0.9 and len(sources) > 1:
month.status = 'SINGLE_SOURCE'
else:
month.status = 'NORMAL'
Look for systematic issues:
-- Consecutive empty months (indicates a bulk import failure)
-- Alternating source dominance (normal for dual-ecosystem users)
-- Sudden drops in a source (might indicate sync stopped)
-- Recent months with much lower counts (import not yet complete?)
TIMELINE ANALYSIS
══════════════════════════════════════
Coverage: Jan 2014 → Mar 2026 (147 months)
GAPS FOUND
Empty months: 3
- Aug 2015: 0 photos (Apple: 0, Google: 0)
- Feb 2016: 0 photos (Apple: 0, Google: 0)
- Nov 2019: 0 photos (Apple: 0, Google: 0)
Sparse months: 7
- Jan 2015: 4 photos (avg for 2015: 89/month)
- Mar 2017: 2 photos (avg for 2017: 112/month)
...
SOURCE COVERAGE
Apple Photos: Jan 2016 → Mar 2026 (dominates 2016, 2018, 2024-2026)
Google Photos: Mar 2014 → Dec 2023 (dominates 2017, 2019-2023)
Single-source months: 48 (32%)
Apple-only: 28 months
Google-only: 20 months
YEAR OVERVIEW
2014: ████░░░░░░░░ 142 photos (Google only)
2015: ████████░░░░ 891 photos (sparse in Aug)
2016: ██████████░░ 1,204 photos (Apple dominant)
...
RECOMMENDATIONS
1. Investigate Aug 2015, Feb 2016, Nov 2019 — check backups
2. Google Photos ends Dec 2023 — intentional or missed import?
3. 7 sparse months may indicate partial imports — cross-check with phone backups
If the user wants an HTML output, generate an interactive timeline using a heatmap grid:
For users with multiple import sources, check if gaps in one source are covered by another:
-- Months where Apple has photos but Google doesn't
SELECT date_trunc('month', "localDateTime") as month, count(*)
FROM asset
WHERE "deletedAt" IS NULL AND "originalPath" LIKE '%Apple%'
AND date_trunc('month', "localDateTime") NOT IN (
SELECT DISTINCT date_trunc('month', "localDateTime")
FROM asset WHERE "deletedAt" IS NULL AND "originalPath" LIKE '%Google%'
)
GROUP BY 1 ORDER BY 1;
This answers: "If I delete all Google photos, which months would I lose coverage for?"
localDateTime (not createdAt) for accurate chronological analysisnpx claudepluginhub drolosoft/immich-photo-manager --plugin immich-photo-managerRuns a comprehensive health check on an Immich photo library — asset counts, storage usage, metadata completeness, orphaned files, and quality indicators.
Builds forensic super-timelines with Plaso (log2timeline) by correlating events across file systems, logs, and artifacts into a unified chronological view for incident investigations.
Builds forensic super-timelines with Plaso (log2timeline) by correlating events across file systems, logs, and artifacts into a unified chronological view for incident investigations.