linkedin-studio/commands/import.md
Kjell Tore Guttormsen a413279c58 fix(linkedin-studio): S14 harden import — drop baseline/metadata over-promise, prose now matches CLI
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
2026-06-18 20:56:47 +02:00

295 lines
12 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
name: linkedin:import
description: |
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".
allowed-tools:
- Bash
- Read
- Glob
- Write
- AskUserQuestion
---
# 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:
```bash
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:
```bash
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:
```bash
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:
```bash
node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/quick-import.mjs
```
This will:
1. Open `linkedin.com/analytics/creator/content/` in your browser
2. Watch ~/Downloads for new CSV files
3. Auto-copy detected files to the exports directory
After the script completes, continue to Step 4.
**Option B: Manual export**
1. Go to [linkedin.com/analytics/creator/content/](https://linkedin.com/analytics/creator/content/)
2. Click the **"Export"** button (top right)
3. LinkedIn will download a CSV file
4. Move it to: `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/exports/`
```bash
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:
1. **List the files** with details (name, size, date)
2. **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):
```bash
cd "${CLAUDE_PLUGIN_ROOT}/scripts/analytics" && npm install --silent
```
Once the user selects, run the import CLI:
```bash
"${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 a `Warning:` line)
- `Date range:` — earliest to latest post in the batch
- `Batch ID:` and `Saved to: posts/<file>` — the batch file written
- `Saves entered:` — only when the CSV carried a `Saves` column (manual entry)
- An anomaly block — either `Immediate alerts detected:` with 🔴/⚠️/ spike/drop lines, or `No 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:
1. Read `expertise_areas` from `~/.claude/linkedin-studio.local.md`
2. Call `trends` for impressions and engagement_rate over the last 4 weeks:
```bash
"${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
```
3. Produce the Content Pillar Performance, Format Performance, and
Day-of-Week Performance tables, plus exactly 3 actionable recommendations
4. 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:
```bash
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:
1. **Check the CSV format** - LinkedIn sometimes changes export format
2. **Verify the file path** - Ensure the file is in `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/analytics/exports/`
3. **Check file permissions** - The CLI needs read access
4. **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 export
- `Permission denied`: Check file permissions with `ls -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>.json` is written, one per import — existing batch files are never overwritten; `loadAllPosts` deduplicates 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_date` and `last_import_week` are updated in the state file (`~/.claude/linkedin-studio.local.md`, see Step 6b)