apple-health-analyst
Analyze Apple Health export ZIP. Run local prepare to generate structured insights, then produce either a health report or a training report with long-term context.
git clone https://github.com/RuochenLyu/apple-health-analyst
T=$(mktemp -d) && git clone --depth=1 https://github.com/RuochenLyu/apple-health-analyst "$T" && mkdir -p ~/.claude/skills && cp -r "$T/.agents/skills/apple-health-analyst" ~/.claude/skills/ruochenlyu-apple-health-analyst-apple-health-analyst && rm -rf "$T"
.agents/skills/apple-health-analyst/SKILL.mdApple Health Advisor
Use this skill when a user wants to analyze an Apple Health export ZIP. The ZIP is too large to fit directly into context, so the skill uses a local CLI pipeline to parse and structure the data first.
This is one skill that ships two complementary reports:
- Default (recommended): generate BOTH reports — health report + training report, rendered into the same
folder and cross-linked via an in-page link in the topbar.output/
runs once, thenprepare
runs twice.render - Explicit health-only: user says "只要健康报告" / "health report only" / similar → skip training render.
- Explicit training-only: user says "只要运动报告" / "training report only" / names a sport exclusively → skip health render.
Language Detection
Detect the user's language from their message:
- If the user writes in Chinese, use
--lang zh - For all other languages, use
--lang en
The narrative language must match the language declared in
insights.json:
- Health report:
narrativeContext.language - Training report:
training.narrativeContext.language
Intent Routing
Default: generate both health + training reports. Only drop one when the user is explicit.
- Default (both) examples:
Analyze my Apple Health export帮我分析 Apple Health 导出Generate a report from my Apple Health export
- Health-only examples (skip training render):
Only generate the health report只要健康报告不用生成运动报告
- Training-only examples (skip health render):
Only generate the training report只要运动报告
(user is only asking about a specific sport)重点分析拳击训练状态分析跑步和骑行训练趋势
Ambiguous-but-lean-training keywords still default to both reports (the training report alone is rarely enough context). The keywords below only matter as hints — they do NOT suppress the health report unless the user also says "只" / "only":
,training
,workout
,运动
,训练专项- named sports such as
,boxing
,running
,cycling
,walking
,hiking
,strength training
,拳击
,跑步
,骑行力量训练
Your Role
Two roles share the same pipeline:
- Health management advisor:
- Integrate sleep, recovery, activity, and body metrics into an overall health view
- Prioritize cross-metric reasoning over metric-by-metric reporting
- Training status advisor:
- Judge load, recovery support, consistency, and sport-specific trends
- Give actionable training-management advice without pretending to be Garmin or a coach writing a periodized plan
Workflow
- Confirm the input is an official Apple Health export ZIP and that the main XML has
as its root node.HealthData - Run local
once with the correctprepare
, producing--lang
andsummary.json
.insights.json - Read
, thensummary.json
.insights.json - Decide which reports to produce (default = both unless the user is explicit; see Intent Routing).
- For each selected report, write the narrative JSON:
- health:
report.llm.json - training:
training.report.llm.json
- health:
- Run
for each selected report:render- health: default
render - training:
render --type training
- health: default
- Both HTML reports share the same
folder. The topbar carries a cross-link between them, so the user can jump back and forth. File names are fixed (output/
↔report.html
) — do not rename.training.report.html
insights.json
Keys You Must Use
insights.jsonShared
| Key | What it contains |
|---|---|
| , , , , |
| Recent 30d, baseline 90d, trailing 180d, all-time context |
| Health chart groups |
| Cross-metric health reasoning |
| Health risks with evidence |
| Significant changes |
| Missing or sparse data warnings |
| Device/source reliability signals |
Health report
| Key | What it contains |
|---|---|
| Sleep duration, stages, timing, regularity |
| RHR, HRV, blood oxygen, respiratory rate, VO2 max |
| Active energy, exercise minutes, stand hours, workouts |
| Weight, body fat % |
| Cycle analysis if present |
| Health-report audience, goal, schema version, boundaries |
Training report
| Key | What it contains |
|---|---|
| Training state, readiness, recent load, recovery support, primary sport |
| CTL / ATL / TSB snapshot + 30-day & 90-day CTL deltas (null when < 28 days of data or < 6 workouts) |
| Top sports (dormant ones filtered, configurable via , default 5) with recent/baseline/trailing/all-time windows, recovery-after-workout, consistency, tags |
| (CTL/ATL monthly curve), , and charts |
| Training-report audience, goal, schema version, boundaries |
Commands
Default flow — prepare once, render twice so the folder contains both report sets. Pass
--with-cross-link to both render calls so the topbar/footer cross-link lights up; omit it on single-report runs to avoid a dead link to a file that will not exist.
# 1. Prepare once (shared by both reports) # Optional: --top-sports N to cap the training-report sport list (default 5) npx apple-health-analyst prepare /path/to/export.zip --lang en --out ./output # 2. Health render (fixed file name: report.html) npx apple-health-analyst render \ --insights ./output/insights.json \ --narrative ./output/report.llm.json \ --with-cross-link \ --out ./output # 3. Training render (fixed file name: training.report.html) npx apple-health-analyst render \ --type training \ --insights ./output/insights.json \ --narrative ./output/training.report.llm.json \ --with-cross-link \ --out ./output
The two HTML files auto-link to each other via the topbar and footer only when
is set on both renders. Always write both into the same --with-cross-link
--out directory to keep the cross-links working.
Single-report mode: if the user is explicit about only wanting the health or the training report (see Intent Routing), run
render once without --with-cross-link — otherwise the lone HTML will point at a companion file that never gets generated.
Health Narrative Framework
Use the existing health schema in references/report-llm-json.md.
Prioritize:
crossMetric.compositeAssessmentcrossMetric.sleepRecoveryLinkcrossMetric.sleepConsistencycrossMetric.activityRecoveryBalancecrossMetric.recoveryCoherencecrossMetric.patterns
andriskFlagsnotableChanges
Health writing rules:
- Every conclusion must cite concrete values or dates from
orsummary.jsoninsights.json
must be cross-metric, not single-metric triviakey_findings
must specify time, frequency, or numeric targetsactions_next_2_weeks
must be data-driven and specificquestions_for_doctor
Training Narrative Framework
Use the training schema in references/training-report-llm-json.md.
Prioritize:
andtraining.summary.trainingStatetraining.summary.readiness
— the CTL / ATL / TSB snapshot (primary load signal)training.summary.trainingLoad
andtraining.summary.loadTrend
(legacy 30d-vs-90d views, use as corroboration)training.summary.recoverySupport
in descending importancetraining.sports[]training.charts[]
and missing metric coveragedataGaps[]
Training writing rules:
- Use neutral wording inspired by public training-status concepts (CTL / ATL / TSB), not branded Garmin claims
- When
is non-null, lead with CTL direction + TSB value; citetraining.summary.trainingLoad
andctlDelta30dPct
rather than the legacy 30-day-vs-90-day numbersctlDelta90dPct - If
is null, fall back totrainingLoad
and say so explicitly (e.g. "数据覆盖不足 28 天,暂以 30 天对比为准")loadTrend - Sport sections must focus on the actual top sports in
training.sports[] - Only discuss heart rate or distance when the structured data includes those metrics
- Recommendations are for training management and health monitoring, not race plans or diagnosis
Required Reading Before Writing Narrative
summary.jsoninsights.json- references/report-llm-json.md for health mode
- references/training-report-llm-json.md for training mode
- references/safety-boundaries.md
- references/analysis-framework.md
Constraints
- Only reference facts from
andsummary.jsoninsights.json - Do not fabricate sport metrics, chart IDs, or medical risks
- Provide health management and training adjustment advice, not diagnoses or treatment plans
- If a module is
, say so plainlyinsufficient_data - Do not generate final HTML directly; write the narrative JSON first, then run
render
Error Handling
- ZIP format error: if
cannot find theprepare
XML, verify the user provided the official Apple Health export ZIP. The main XML filename is not fixed and may be localized (for exampleHealthData
) or appear as mojibake.导出.xml
/export_cda.xml
is auxiliary only and should not be used as the main analysis input.ClinicalDocument - Out of memory: large ZIPs may need
and--from--to - Health narrative validation failure: verify
matches schema v2report.llm.json - Training narrative validation failure: verify
matches schema v1 and only references existing sport/chart IDstraining.report.llm.json - npm cache EPERM: use
npm_config_cache=./.npm-cache - Sandbox/policy rejection: do not chain destructive commands with
/prepare
; create directories separately if neededrender
Output Files
Always produced by
prepare:
summary.jsoninsights.json
Health render (file names are fixed; do not rename):
report.llm.jsonreport.mdreport.html
Training render (file names are fixed; do not rename):
training.report.llm.jsontraining.report.mdtraining.report.html
The two HTML reports cross-link via the topbar and footer using relative paths (
./report.html ↔ ./training.report.html). Keep both in the same output/ directory for the links to work.