import.md repeatedly promised baseline comparison, rolling 4-week averages, and baselines.json/metadata.json the importer "creates" — but no code anywhere writes either file; detectAlerts flags intra-batch std-dev from the batch's own mean, not a stored baseline. Step 5b's `cat baselines.json` was always empty, so the command fell forever to "first import — baselines will be established" on every import. Rewrote 5 sections (Step 5, 5b, 7, State Tracking, Reference Files) to the truth: real CLI fields + YYYY-MM-DD-<shortid> filename, honest intra-batch surfacing, cross-week analysis deferred to /linkedin:report. Verified with a grounded run against a throwaway fixture. Axes a/b/c/d all PASS post-fix; lint 81/0/0, counts 29/19 unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016qgzo6rxthw7KuxHjn5vyE
12 KiB
| name | description | allowed-tools | |||||
|---|---|---|---|---|---|---|---|
| linkedin:import | Import a LinkedIn analytics CSV export into the structured analytics system. Parses CSV, converts to JSON, detects anomalies, and prepares data for trend analysis. Now with auto-detect from ~/Downloads, quick-import browser helper, and analytics-to-strategy feedback loop. Use when the user wants to import analytics data from LinkedIn. Triggers on: "import analytics", "import CSV", "upload analytics", "parse LinkedIn data", "add analytics export", "import my LinkedIn data". |
|
LinkedIn Analytics Import Workflow
You are a LinkedIn analytics data import assistant. Guide the user through importing their LinkedIn analytics CSV export with minimal friction.
Reference
For data format details and directory structure, see assets/analytics/README.md.
Why CSV (as of 2026-05). Post-level analytics via LinkedIn's API is partner-gated (vetted Community Management app + verified org + Page) and not self-serve for a personal profile, so the CSV export is the practical floor. Saves are visible in native post analytics (count-only) but have no self-serve API pull; dwell is internal-only for organic posts. See the README boundaries.
Step 1: Check for CSV Files in Exports Directory
First, check if any CSV files exist in the exports directory:
ls -lh ${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/exports/*.csv 2>/dev/null || echo "No CSV files found"
If files found: Skip to Step 3.
Step 1b: Auto-Detect from ~/Downloads
If no files in exports directory, scan ~/Downloads/ for recent LinkedIn CSV files:
find ~/Downloads -maxdepth 1 -name "*.csv" -mtime -14 -type f 2>/dev/null | sort -t/ -k$(echo ~/Downloads/x | tr '/' '\n' | wc -l) | head -10
Filter results for LinkedIn-looking files (filenames containing 'linkedin', 'analytics', 'content', 'export', or any CSV modified in the last 24 hours).
If matching files found, present them using AskUserQuestion:
Options:
- Import specific file — Select one of the detected files
- Import all — Import all matching CSV files
- Quick-import — Open LinkedIn Analytics in browser and auto-detect download
- Skip — Show manual instructions instead
On file selection, copy the file to the exports directory:
cp "<selected-file>" ${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/exports/
Then continue to Step 4.
Step 2: If No Files Found Anywhere
If no CSV files exist in exports or ~/Downloads, offer two options:
Option A: Quick-import (recommended)
Run the quick-import helper that opens LinkedIn Analytics in the browser and watches for the download:
node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/quick-import.mjs
This will:
- Open
linkedin.com/analytics/creator/content/in your browser - Watch ~/Downloads for new CSV files
- Auto-copy detected files to the exports directory
After the script completes, continue to Step 4.
Option B: Manual export
- Go to linkedin.com/analytics/creator/content/
- Click the "Export" button (top right)
- LinkedIn will download a CSV file
- Move it to:
${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/exports/
mv ~/Downloads/linkedin_analytics_export*.csv ${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/exports/
Once done, run /linkedin:import again.
Step 3: Select Files to Import
If CSV files exist in the exports directory:
- List the files with details (name, size, date)
- Ask the user which file to import using AskUserQuestion:
Options:
- Latest — Import the most recent file only
- All — Import all CSV files
- Select — Choose a specific file
- Cancel — Exit import
Step 4: Run Import
The import CLI runs under tsx and depends on csv-parse. Both live in the
gitignored scripts/analytics/node_modules/, so on a fresh clone they are
absent and the CLI would crash with ERR_MODULE_NOT_FOUND. Install them once
first (idempotent — a fast no-op when already present):
cd "${CLAUDE_PLUGIN_ROOT}/scripts/analytics" && npm install --silent
Once the user selects, run the import CLI:
"${CLAUDE_PLUGIN_ROOT}/scripts/analytics/node_modules/.bin/tsx" "${CLAUDE_PLUGIN_ROOT}/scripts/analytics/src/cli.ts" import <filename>
If importing multiple files, run the command for each file sequentially.
Step 5: Capture and Present Results
The CLI prints (see cli.ts handleImport):
Posts imported:— count of valid rows (rows with an empty title or an unparseable date are skipped, each with aWarning:line)Date range:— earliest to latest post in the batchBatch ID:andSaved to: posts/<file>— the batch file writtenSaves entered:— only when the CSV carried aSavescolumn (manual entry)- An anomaly block — either
Immediate alerts detected:with 🔴/⚠️/ℹ️ spike/drop lines, orNo anomalies detected in imported data.
Surface the CLI's output to the user — for example:
Import successful!
─────────────────────────────────────
Posts imported: 42
Date range: 2025-12-01 to 2026-01-29
Saved to: posts/2025-12-01-batch-a1b2c3d4.json
Saves entered: 1,204 across 18 post(s) (manual)
Immediate alerts detected:
─────────────────────────────────────
ℹ️ [INFO] Post "AI agents are eating..." has unusually high impressions: 21,400 (2.4 std deviations above mean)
The saved file is named posts/YYYY-MM-DD-<shortid>.json (the batch's earliest post date + a short batch id), not by ISO week.
Step 5b: Surface the Anomalies the Importer Detected
The import CLI runs intra-batch anomaly detection during Step 4 (detectAlerts
in cli.ts): for the just-imported batch it flags any post whose impressions
deviate sharply — by standard deviation — from that batch's own mean, printing
either Immediate alerts detected: (🔴/⚠️/ℹ️ spike/drop lines) or
No anomalies detected in imported data.
Surface those lines as-is, and state the scope honestly: a flagged post stands out among the posts in this export, not against a stored historical baseline — the importer keeps no baseline file. Cross-week comparison is Step 6's job.
Present as:
### Import Summary — <batch date range>
X posts imported (Y skipped: empty title or unparseable date)
#### Standout in this batch
ℹ️ "[hook text...]" — 21,400 impressions (2.4 std dev above this batch's mean)
⚠️ "[hook text...]" — 180 impressions (2.1 std dev below this batch's mean)
(or: "No standout deviations within this batch.")
Cross-week analysis — best day of week, format performance, week-over-week trend,
rolling averages — is not computed here; it is produced by /linkedin:report
(Step 6), which reads the full post history via the trends/heatmap CLI. Defer
that analysis to Step 6 rather than restating it.
Step 6: Analytics-to-Strategy Feedback Loop
After successful import, the analysis fan-out (pillar performance, format
performance, day-of-week heatmap, actionable recommendations) is delegated
to /linkedin:report — both commands consume the same trends CLI from
scripts/analytics/, and keeping a second analysis pipeline here drifted
out of sync with report.md.
Step 6a: Run the report
Invoke the report generator and surface its output inline:
Run /linkedin:report (period: 4w)
/linkedin:report will:
- Read
expertise_areasfrom~/.claude/linkedin-studio.local.md - Call
trendsfor impressions and engagement_rate over the last 4 weeks:"${CLAUDE_PLUGIN_ROOT}/scripts/analytics/node_modules/.bin/tsx" "${CLAUDE_PLUGIN_ROOT}/scripts/analytics/src/cli.ts" trends --period 4w --metric impressions "${CLAUDE_PLUGIN_ROOT}/scripts/analytics/node_modules/.bin/tsx" "${CLAUDE_PLUGIN_ROOT}/scripts/analytics/src/cli.ts" trends --period 4w --metric engagement_rate - Produce the Content Pillar Performance, Format Performance, and Day-of-Week Performance tables, plus exactly 3 actionable recommendations
- Return its summary back to this import flow
If /linkedin:report is unavailable (analytics dir empty, tsx missing),
fall back to a one-line status: "Import complete — run /linkedin:report
manually when analytics are ready."
Step 6b: Update State with Import Date
After successful import and analysis, update the state file:
Read ~/.claude/linkedin-studio.local.md
Set last_import_date to today (YYYY-MM-DD)
Set last_import_week to current ISO week (YYYY-WXX)
Write the updated state file
Step 7: Next Steps
Present next steps using AskUserQuestion based on the analysis results:
If the report's trend is down (impressions or engagement trending DOWN):
- "Run /linkedin:report for full weekly breakdown"
- "Run content audit to review strategy"
- "Analyze your top post to understand what worked"
If the report's trend is up (impressions or engagement trending UP):
- "Run /linkedin:report for the full numbers"
- "Create more content in your top format"
- "Draft your next post while insights are fresh"
If first import:
- "Run /linkedin:report for your first performance report"
- "Import 2-3 more weeks for trend analysis"
- "Tip: Export weekly every Monday for best tracking"
If mixed results:
- "Run /linkedin:report for complete breakdown"
- "Review trend analysis for diverging metrics"
- "Check which formats and topics drove results"
Present using AskUserQuestion with the top 3 most relevant suggestions.
Step 8: Demographics Sync Suggestion
After completing the import workflow, check if ${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/audience-insights/demographics.md still has placeholder data:
grep -c '\[Industry name\]\|\[Function\]\|\[Country\]\|\[X\]%' ${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/audience-insights/demographics.md 2>/dev/null
If placeholder count is > 10 (still mostly unfilled), suggest:
"While you're in LinkedIn Analytics exporting CSV data, you can also capture your audience demographics. Run /linkedin:setup and choose option 5 (Demographics) to fill in your audience insights with real data."
Error Handling
If the import fails:
- Check the CSV format - LinkedIn sometimes changes export format
- Verify the file path - Ensure the file is in
${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/exports/ - Check file permissions - The CLI needs read access
- Show the error message and suggest solutions
Common errors:
File not found: Check the filename (case-sensitive)Invalid CSV format: Verify this is a LinkedIn analytics exportPermission denied: Check file permissions withls -l
Reference Files
The import system creates:
${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/posts/YYYY-MM-DD-<shortid>.json- one JSON batch per import (earliest post date + short batch id)
Weekly and monthly report files (under weekly-reports/ and monthly-reports/) are created separately by /linkedin:report, not by import.
State Tracking
After import:
- A new batch file
posts/YYYY-MM-DD-<shortid>.jsonis written, one per import — existing batch files are never overwritten;loadAllPostsdeduplicates by post id at read time (latest import wins) - Intra-batch spike/drop alerts are computed and surfaced (std deviation from the batch's own mean — no persisted baseline)
last_import_dateandlast_import_weekare updated in the state file (~/.claude/linkedin-studio.local.md, see Step 6b)