diff --git a/agents/trend-spotter.md b/agents/trend-spotter.md index ff9fb5c..a04179d 100644 --- a/agents/trend-spotter.md +++ b/agents/trend-spotter.md @@ -41,7 +41,7 @@ Before scanning, load the user's content pillars and expertise areas: - Extract: 5 core expertise areas, target audience, voice preferences - If file does not exist, ask the user for their 5 content pillars before proceeding -2. **Read voice samples:** `${CLAUDE_PLUGIN_ROOT}/assets/voice-samples/` (glob for .md files) +2. **Read voice samples:** `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/` (glob for .md files) - Understand their typical angle and tone 3. **Check recent posts:** `${CLAUDE_PLUGIN_ROOT}/assets/analytics/posts/` (if available) diff --git a/agents/video-scripter.md b/agents/video-scripter.md index 639e6d4..49cb9ac 100644 --- a/agents/video-scripter.md +++ b/agents/video-scripter.md @@ -34,7 +34,7 @@ ${CLAUDE_PLUGIN_ROOT}/references/video-strategy-guide.md → Script ${CLAUDE_PLUGIN_ROOT}/references/linkedin-formats.md → Video specs, algorithm data, technical requirements ${CLAUDE_PLUGIN_ROOT}/references/engagement-frameworks.md → Hook types, CTAs, story structures ${CLAUDE_PLUGIN_ROOT}/references/thought-leadership-angles.md → 8 universal angles -${CLAUDE_PLUGIN_ROOT}/assets/voice-samples/ → User's authentic voice (ALWAYS read before scripting) +${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/ → User's authentic voice (ALWAYS read before scripting) ${CLAUDE_PLUGIN_ROOT}/assets/examples/high-engagement-posts.md → Successful content patterns ~/.claude/linkedin-studio.local.md → User state, recent topics, streak ``` @@ -150,7 +150,7 @@ When converting an existing text post to video: After drafting the script: -1. Read `assets/voice-samples/` to match the user's natural speech patterns +1. Read `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/` to match the user's natural speech patterns 2. Check for: - **Sentence length** — match their natural rhythm - **Vocabulary level** — match their word choices diff --git a/agents/voice-scrubber.md b/agents/voice-scrubber.md index c0bbca0..40a204d 100644 --- a/agents/voice-scrubber.md +++ b/agents/voice-scrubber.md @@ -52,7 +52,7 @@ This is the single most important rule of this agent. - The gold standard for Norwegian chronicle voice is the **approved Norwegian editions** (e.g. the series' approved Del 1 + Del 2). The caller passes the path(s); read them as the corpus before scrubbing. -- **Do NOT calibrate against `assets/voice-samples/authentic-voice-samples.md`.** +- **Do NOT calibrate against `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md`.** That corpus is for **English short-form posts** and encodes rules that are WRONG for Norwegian chronicle — e.g. it forbids the em-dash, which the author *does* use in long-form Norwegian. Using it as the gold standard would actively @@ -111,7 +111,7 @@ overwrite identity-level voice. After scrubbing, append what you learned to a drift log so the agent gets sharper each edition: -- Write to `${CLAUDE_PLUGIN_ROOT}/assets/voice-samples/chronicle-voice-drift-log.md` +- Write to `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/chronicle-voice-drift-log.md` (create if absent). One dated entry per run: which tells recurred, which voice traits the draft drifted on, and any newly-confirmed gold-standard pattern. - Do **not** rewrite the general voice profile (`config/user-profile.local.md`) — diff --git a/agents/voice-trainer.md b/agents/voice-trainer.md index cb5f7c3..8a08cd8 100644 --- a/agents/voice-trainer.md +++ b/agents/voice-trainer.md @@ -133,7 +133,7 @@ Architecture: [prose/sectioned/framework] ### Analysis Process -1. **Gather** — Read all files in `${CLAUDE_PLUGIN_ROOT}/assets/voice-samples/`, existing profile from `config/user-profile.local.md`, and template from `config/user-profile.template.md` +1. **Gather** — Read all files in `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/`, existing profile from `config/user-profile.local.md`, and template from `config/user-profile.template.md` 2. **Analyze** — Apply all six dimensions to each sample. Note dates for temporal analysis. Flag inconsistent samples as outliers or evolution. 3. **Synthesize** — Patterns in 70%+ of samples = core traits. 40-70% = situational traits (note context). <40% = experimental traits. Track temporal trends. 4. **Build** — Compile into Voice Profile Document format. Include confidence levels (high/medium/low) and concrete examples for every trait. @@ -323,7 +323,7 @@ Fixes: [specific corrections with baseline examples] ## References Read these files for context and methodology: -- `${CLAUDE_PLUGIN_ROOT}/assets/voice-samples/` — Source samples for analysis +- `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/` — Source samples for analysis - `${CLAUDE_PLUGIN_ROOT}/config/user-profile.template.md` — Profile structure template - `${CLAUDE_PLUGIN_ROOT}/config/user-profile.local.md` — Current voice profile (if exists) - `${CLAUDE_PLUGIN_ROOT}/references/ai-content-framework.md` — AI content anti-patterns and quality checklist diff --git a/commands/carousel.md b/commands/carousel.md index 1d1376a..69bc5d8 100644 --- a/commands/carousel.md +++ b/commands/carousel.md @@ -24,7 +24,7 @@ You are a LinkedIn carousel content specialist. Create high-engagement carousel ## Step 0: Load Context - Read `~/.claude/linkedin-studio.local.md` for posting state and expertise areas -- Read `assets/voice-samples/authentic-voice-samples.md` for voice profile +- Read `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` for voice profile - Check recent posts to avoid topic repetition ## Step 1: Choose Template diff --git a/commands/first-post.md b/commands/first-post.md index 5a21f04..221b9ba 100644 --- a/commands/first-post.md +++ b/commands/first-post.md @@ -24,7 +24,7 @@ The first post doesn't need to be perfect. It needs to EXIST. Every day without ## Step 0: Load Context Read `~/.claude/linkedin-studio.local.md` for current state. -Read `assets/voice-samples/authentic-voice-samples.md` for voice profile (if it exists). +Read `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` for voice profile (if it exists). Check: If `first_post_date` is already set, this user has posted before. Suggest `/linkedin:post` or `/linkedin:quick` instead, and explain this command is for true first-timers. @@ -44,7 +44,7 @@ Total: ~10 minutes. Let's go. ## Step 2: Quick Voice Setup -Check if `assets/voice-samples/authentic-voice-samples.md` has substantive content (more than just the template headers). +Check if `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` has substantive content (more than just the template headers). **If voice profile exists:** Say "I already have your voice profile. Let's use it." Skip to Step 3. diff --git a/commands/firsthour.md b/commands/firsthour.md index 086f2ab..4697da6 100644 --- a/commands/firsthour.md +++ b/commands/firsthour.md @@ -25,7 +25,7 @@ worked plan: who to engage, what to say, and exactly when — persisted to state ## Step 0: Load Context - Read `~/.claude/linkedin-studio.local.md` for posting state (streak, weekly progress, recent posts, follower phase). -- Read `assets/voice-samples/authentic-voice-samples.md` so every draft comment is in the user's voice. +- Read `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` so every draft comment is in the user's voice. - Note the user's growth phase (follower count) — it sets daily comment volume and target split. ## Step 1: Identify the Post @@ -113,6 +113,6 @@ delayed spike) with the post-feedback monitor — invoke it via `Task` with ## Reference Files -- `assets/voice-samples/authentic-voice-samples.md` — voice matching for the draft comments +- `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` — voice matching for the draft comments - `references/engagement-frameworks.md` — hook types, CEA, engagement hierarchy - `references/algorithm-signals-reference.md` — first-hour weighting, signal order, timing data diff --git a/commands/newsletter.md b/commands/newsletter.md index 40b68e2..53d71eb 100644 --- a/commands/newsletter.md +++ b/commands/newsletter.md @@ -170,8 +170,8 @@ the edition left off before doing anything. Step 2. Do not confuse `/STATE.md` (this edition's production state) with the plugin's own `STATE.md` / `docs/BUILD-HANDOVER.local.md` (which govern building the plugin itself). -4. **Read the voice profile** — `assets/voice-samples/authentic-voice-samples.md` - and anything else under `assets/voice-samples/`. Long-form must match the +4. **Read the voice profile** — `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` + and anything else under `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/`. Long-form must match the author's voice; this is the reference for every drafting and review phase. 5. **Resolve the active personas (per-artifact).** Personas are configured **per edition**, not from one fixed global file. Resolve the set for @@ -541,7 +541,7 @@ Typically ~20–30 % of the edition's final length. **Procedure:** -1. **Re-read the voice profile** (`assets/voice-samples/`) before writing a +1. **Re-read the voice profile** (`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/`) before writing a single sentence — this is the existing LTL rule and it is not optional for long-form. Voice match starts at the spine, not at expansion. @@ -635,7 +635,7 @@ turning-points the spine already named. **Procedure:** -1. **Re-read the voice profile** (`assets/voice-samples/`) before expanding — +1. **Re-read the voice profile** (`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/`) before expanding — the voice was set at the spine; do not lose it in expansion. 2. **Expand section by section, against the spine.** Each section's paragraph @@ -724,7 +724,7 @@ linkedin-studio:voice-scrubber`, from THIS command layer in the foreground (principle 4). Pass it the draft path AND the paths to the **approved Norwegian editions** as the gold standard (e.g. earlier parts' locked `linkedin/NN/POST.html` or their approved `NN-utkast.md`). **Do NOT** point it at -`assets/voice-samples/authentic-voice-samples.md` — that corpus is English +`${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` — that corpus is English short-form and forbids the em-dash; using it as the gold standard would degrade the Norwegian chronicle voice. The scrubber runs two passes: Pass 1 strips AI-tells (objective — «la meg være ærlig», reflex rule-of-three, em-dash-spam, @@ -1587,7 +1587,7 @@ the honest decision surface; it sells nothing. - `${CLAUDE_PLUGIN_ROOT}/commands/headless-review.md` — the Step 6.5 cold review package as a standalone command (run in a fresh session for maximum isolation) - `${CLAUDE_PLUGIN_ROOT}/commands/pivot.md` — re-opens the pipeline after a late pivot so Steps 5–6.5 re-run on the changed version before lock - `${CLAUDE_PLUGIN_ROOT}/commands/react.md` — multi-source synthesis discipline (reused in Step 2) -- `${CLAUDE_PLUGIN_ROOT}/assets/voice-samples/authentic-voice-samples.md` — voice matching +- `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` — voice matching - `${CLAUDE_PLUGIN_ROOT}/references/longform-quality-rules.md` — canonical long-form rules (Steps 2.5, 3a, 3b, 4–9 all reference) - `${CLAUDE_PLUGIN_ROOT}/render/build-linkedin.mjs` — POST.html delivery; reads `linkedin/NN/cover.png` + credit/caption (Step 8) - `${CLAUDE_PLUGIN_ROOT}/render/build-html.mjs` — annotatable review renderer (Step 7) diff --git a/commands/onboarding.md b/commands/onboarding.md index bca300f..8ba0e4b 100644 --- a/commands/onboarding.md +++ b/commands/onboarding.md @@ -140,7 +140,7 @@ Use AskUserQuestion: 4. "Paste a paragraph you've written that sounds like YOU (email, doc, anything)" 5. "Any words or phrases you'd NEVER use?" -Save the responses to `assets/voice-samples/authentic-voice-samples.md`. **If the +Save the responses to `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md`. **If the file is the shipped placeholder** (it contains ``), **REPLACE it entirely** with the profile built from the answers — the `` sentinel must NOT remain, or the voice score stays at @@ -196,7 +196,7 @@ Then ask: "Give me a sentence or two about what you have in mind." If expertise ### 3.2 — Write the post (3-line formula) -Draft the post using the voice profile from Phase 2 (or the existing `assets/voice-samples/` profile): +Draft the post using the voice profile from Phase 2 (or the existing `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/` profile): - **Line 1 — Hook (110-140 chars):** specific to their experience, no generic opening - **Line 2 — Context (1-3 sentences):** the what and why, kept tight - **Line 3 — Insight + question:** their takeaway, ending on a genuine question that invites comments diff --git a/commands/pipeline.md b/commands/pipeline.md index 885b1fe..99a2426 100644 --- a/commands/pipeline.md +++ b/commands/pipeline.md @@ -26,7 +26,7 @@ You are a LinkedIn content pipeline orchestrator. Guide the user through the com Load persistent state and personalization: - Read `~/.claude/linkedin-studio.local.md` for posting state - Read `${CLAUDE_PLUGIN_ROOT}/skills/linkedin-studio/SKILL.md` for profile and preferences -- Check `assets/voice-samples/` for voice matching +- Check `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/` for voice matching - Read `assets/templates/my-post-templates.md` for proven post templates — use these in Step 2 (Draft) - Read `assets/frameworks/framework-template.md` if the topic involves a framework or methodology @@ -81,7 +81,7 @@ Run the draft through optimization checks: - [ ] Authentic voice (not AI-sounding) **Voice check:** -Compare against `assets/voice-samples/` to ensure natural tone. +Compare against `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/` to ensure natural tone. Present optimized version with before/after comparison. @@ -207,5 +207,5 @@ Replace placeholders with actual post data. Set `next_planned_topic` manually if - `${CLAUDE_PLUGIN_ROOT}/references/linkedin-formats.md` - `${CLAUDE_PLUGIN_ROOT}/references/scheduling-strategy.md` - `${CLAUDE_PLUGIN_ROOT}/assets/checklists/quality-scorecard.md` -- `${CLAUDE_PLUGIN_ROOT}/assets/voice-samples/` +- `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/` - `${CLAUDE_PLUGIN_ROOT}/assets/drafts/queue.json` diff --git a/commands/post.md b/commands/post.md index db947d1..d5b383f 100644 --- a/commands/post.md +++ b/commands/post.md @@ -36,7 +36,7 @@ Check weekly progress: - If `posts_this_week == weekly_goal - 1`, note: "This is your last post to hit this week's goal." Check for existing assets: -- `assets/voice-samples/` - Match the user's natural voice +- `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/` - Match the user's natural voice - `assets/examples/high-engagement-posts.md` - Study past successful posts and replicable patterns - `assets/frameworks/framework-template.md` - Reference user's documented frameworks for framework posts - `assets/templates/my-post-templates.md` - User's proven post templates with success rates. **Prefer these over generic structures.** diff --git a/commands/react.md b/commands/react.md index c2334f1..d51afe0 100644 --- a/commands/react.md +++ b/commands/react.md @@ -25,7 +25,7 @@ You are a LinkedIn content creator specializing in turning external content into First, load persistent state and personalization: - Read `~/.claude/linkedin-studio.local.md` for posting state (streak, weekly progress, recent topics) -- Read `assets/voice-samples/authentic-voice-samples.md` for voice profile +- Read `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` for voice profile - Check recent posts to avoid topic repetition within 7 days ## Step 1: Get URL(s) @@ -264,7 +264,7 @@ Same as Step 8 — run `state-updater.mjs` with actual post data. ## Reference Files -- `assets/voice-samples/authentic-voice-samples.md` — Voice matching +- `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` — Voice matching - `references/thought-leadership-angles.md` — 8 universal angles - `references/engagement-frameworks.md` — Hooks, structure, CTAs - `assets/checklists/quality-scorecard.md` — Pre-publish check diff --git a/commands/setup.md b/commands/setup.md index aa5784a..1c64a23 100644 --- a/commands/setup.md +++ b/commands/setup.md @@ -26,7 +26,7 @@ Read these 8 asset files and detect placeholder patterns to calculate the curren | Category | Weight | File/Directory | Placeholder Detection | |----------|--------|----------------|----------------------| -| Voice samples | 25 | `assets/voice-samples/authentic-voice-samples.md` | Placeholder if it contains the `` sentinel (or has <50 lines) | +| Voice samples | 25 | `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` | Placeholder if it contains the `` sentinel (or has <50 lines) | | User profile | 20 | `config/user-profile.local.md` | Check if file exists; count `[Your ` placeholders | | Case studies | 15 | `assets/case-studies/*.md` | Count non-template `.md` files (exclude `case-study-template.md`) | | Frameworks | 10 | `assets/frameworks/*.md` | Count non-template `.md` files (exclude `framework-template.md`) | @@ -81,7 +81,7 @@ Based on their answer, run the corresponding sub-workflow below. ## Step 3a: Voice Samples Workflow -**Goal:** Populate `assets/voice-samples/authentic-voice-samples.md` with real voice data. +**Goal:** Populate `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` with real voice data. **Delegate the analysis + profile construction to the `voice-trainer` agent** — invoke it via `Task` with `subagent_type: linkedin-studio:voice-trainer` (foreground, from this command layer). The agent performs the pattern detection and extraction (steps 2–3 below) and returns the structured voice profile; this command owns collecting the samples (step 1) and writing the profile back to disk (steps 4–6). @@ -98,7 +98,7 @@ Based on their answer, run the corresponding sub-workflow below. - Words/phrases they avoid - How they handle technical depth - How they conclude (CTA style, takeaway style) -4. Read the existing `assets/voice-samples/authentic-voice-samples.md` +4. Read the existing `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` 5. **If the file is the shipped placeholder** (it contains ``): **REPLACE it entirely** with the profile built from the user's samples. The placeholder's `` sentinel must NOT survive — if it diff --git a/commands/video.md b/commands/video.md index a9416b5..3033265 100644 --- a/commands/video.md +++ b/commands/video.md @@ -39,7 +39,7 @@ Load video-specific references: - Read `references/linkedin-formats.md` (Video Content Deep Dive section) for algorithm data and technical specs Check for existing assets: -- `assets/voice-samples/` — Match the user's natural voice (REQUIRED before scripting) +- `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/` — Match the user's natural voice (REQUIRED before scripting) - `assets/examples/high-engagement-posts.md` — Study successful patterns ## Step 1: Choose Video Type @@ -87,7 +87,7 @@ Delegate script generation to the `video-scripter` agent — invoke it via `Task - Visual cues (`[CAM:]`, `[SCREEN:]`, `[SLIDE:]`, `[TEXT:]`) - Energy cues (`[ENERGY: up]`, `[PAUSE: 1s]`) - Transition markers (`[CUT]`, `[TRANSITION:]`) -4. Match voice against `assets/voice-samples/` +4. Match voice against `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/` 5. Generate captions, thumbnail suggestion, post caption, and first comment ## Step 5: Quality Check diff --git a/docs/m0/log.md b/docs/m0/log.md index cbc6812..ad571e2 100644 --- a/docs/m0/log.md +++ b/docs/m0/log.md @@ -4,6 +4,36 @@ Running record of decisions, deviations, and out-of-scope follow-ups discovered during M0 execution. Plan: `docs/m0/plan.md` (18 steps). History → git; this file captures only what the commit messages cannot. +## Session 4 — Steps 14–18 (2026-06-18) + +### Step 14 GATE outcome — the D3 convention works; edit count is ~1:1, not reduced + +Prototyped `references/data-path-convention.md` on the voice-readers family: +**38 refs across 19 files repointed** — exactly the plan's prediction. The measured +answer to brief D3's open question (*can a convention reduce edits, or do commands +need literal paths for Claude to act on?*): command/agent prose that tells Claude to +**read** a file needs a resolvable path **on the line**. A "the data dir's +`voice-samples/` (see convention doc)" reference adds a lookup hop and is not directly +actionable. The inline `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/…` +token is both self-resolving **and** points at the doc. So the convention does **not** +cut edit count below ~1 per ref — it makes every edit a **uniform mechanical token +swap** (vs bespoke per-line decisions), with the doc as single source of truth. +**GATE = proceed** (convention confirmed working); Step 15 applies the same uniform +token to the remaining families. This is D3 realized as one token — NOT a re-decision +to literal-edit (alt. a). Applied via an ordered swap (prefixed `${CLAUDE_PLUGIN_ROOT}/ +assets/voice-samples/` form before bare `assets/voice-samples/`, so the bare pass can't +corrupt the prefixed one). + +### Designed inter-step red lint (Step 14 → Step 16) + +After Step 14 the structure lint is **Failed: 1** — `references/*.md: 26 (expected 25)`, +the new convention doc as the 26th ref file. `EXPECT_REFS` bumps to 26 in **Step 16** +(plan Session-4 scope forbids touching tests). This session lands 14→18 in one go, so +the lint is restored to green at Step 16 — no red is left at session end. The only +surviving bare `assets/voice-samples/` is inside the convention doc itself (it documents +the in-plugin placeholder-scaffold location for the fallback rule) — Step 16's +no-bare-path assertion must exempt `references/data-path-convention.md`. + ## Session 3 — Steps 11–13 (2026-06-18) ### Environment reality vs. plan assumptions diff --git a/hooks/prompts/state-update-reminder.md b/hooks/prompts/state-update-reminder.md index 7aa8c69..756eb15 100644 --- a/hooks/prompts/state-update-reminder.md +++ b/hooks/prompts/state-update-reminder.md @@ -54,7 +54,7 @@ Provide reminders naturally based on what was done in the session. If no LinkedI If a LinkedIn post was created or finalized in this session, save the full post text as a voice sample: - Read the full post text from the draft that was just created -- Check if `assets/voice-samples/authentic-voice-samples.md` exists +- Check if `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` exists - Append the full post to the `## Collected Post Samples` section: ``` ### [YYYY-MM-DD] — [post type] ([char count] chars) diff --git a/hooks/prompts/voice-guardian.md b/hooks/prompts/voice-guardian.md index 65ca5ba..ff9239d 100644 --- a/hooks/prompts/voice-guardian.md +++ b/hooks/prompts/voice-guardian.md @@ -28,7 +28,7 @@ If two or more of these are missing, flag it alongside the AI-pattern alert: the ## 2. Six-Dimension Voice Drift Scoring -Read the voice profile and collected post samples from `${CLAUDE_PLUGIN_ROOT}/assets/voice-samples/authentic-voice-samples.md`. +Read the voice profile and collected post samples from `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md`. Score the draft against these 6 dimensions (0 = perfect match, 1 = minor drift per dimension): diff --git a/references/data-path-convention.md b/references/data-path-convention.md new file mode 100644 index 0000000..19ee3dd --- /dev/null +++ b/references/data-path-convention.md @@ -0,0 +1,70 @@ +# Data-Path Convention + +How command, agent, skill, and hook-prompt prose should refer to **user data** vs +**plugin-shipped assets** after the M0 migration. One token, resolved inline, so +the prose Claude reads is directly actionable — no indirection, no stale in-plugin +paths. + +## The two roots + +| Root | Token in prose | Holds | Moves on migration? | +|------|----------------|-------|---------------------| +| **Data root** | `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}` | the user's real data (voice profile, analytics, drafts, plans, …) | yes — auto-migrated out of the plugin on session-start | +| **Plugin root** | `${CLAUDE_PLUGIN_ROOT}` | read-only ships (templates, checklists, references, fonts, framework scaffolds) | no — these stay in the plugin | + +The data root **mirrors** the state file (`~/.claude/linkedin-studio.local.md`). The +default is overridable by the single env var `LINKEDIN_STUDIO_DATA`; the deprecated +aliases `ANALYTICS_ROOT` / `STATE_FILE` / `PLUGIN_ROOT` are honored for one minor +version. This generalizes the proven newsletter pattern +`${LTL_SERIES_ROOT:-$HOME/linkedin-series}` (`commands/newsletter.md`) to all data. + +## Data-root layout (mirrors brief §7.1) + +``` +${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/ + voice-samples/ authentic-voice-samples.md chronicle-voice-drift-log.md + analytics/ exports/ posts/ weekly-reports/ monthly-reports/ ab-tests/ content-history.md + drafts/ queue.json week-*/ carousel/ multiplatform/ repurposed/ + profile/ user-profile.md + frameworks/ .md + audience-insights/ demographics.md engagement-patterns.md + examples/ high-engagement-posts.md + templates/ my-post-templates.md + plans/ -plan-*.md +``` + +## The prose rule + +1. **A path to user data → the data-root token.** Write the full inline form + `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/` — never a bare + `assets//` and never `${CLAUDE_PLUGIN_ROOT}/assets//` for data. The inline + `:-default` expansion means the prose is self-resolving; do **not** write "the data + dir's ``" and force a lookup into this doc. +2. **A path to a plugin-shipped read-only asset → keep `${CLAUDE_PLUGIN_ROOT}`.** + Templates (`config/*.template.*`), checklists, `references/`, fonts, and framework + scaffolds ship with the plugin and stay there. +3. **Preserve existing graceful-degradation guards.** "if it exists", placeholder / + sentinel checks, and `<5`-sample silent-skips stay as written — only the path token + changes. + +## Scaffold fallback (the COPY classes + the voice placeholder) + +Four data files ship a **seed/scaffold in-plugin** and a **canonical instance external**: +`examples/high-engagement-posts.md`, `audience-insights/{demographics,engagement-patterns}.md`, +`templates/my-post-templates.md`. The voice profile is similar — the plugin ships a +placeholder at `assets/voice-samples/authentic-voice-samples.md`; the user's real profile +lives external. **Readers prefer the external instance and fall back to the in-plugin seed +when external is absent** (the code does this in `personalization-score.mjs`; prose keeps +its "if it exists" guard). The external instance is canonical — migration never clobbers it. + +## Why inline, not indirection (the Step-14 GATE finding) + +The prototype on the voice-readers family (19 files, 38 refs) measured the open question +from brief D3: *can a convention reduce edits, or do commands need literal paths for Claude +to act on?* Outcome: command prose that tells Claude to **read** a file needs a resolvable +path on the line. A "the data dir's `voice-samples/` (see this doc)" reference adds a lookup +hop and is not directly actionable. The inline `${LINKEDIN_STUDIO_DATA:-…}/…` token is both +self-resolving **and** points here. So the convention does **not** cut the edit count below +~1 per reference — it makes every edit a **uniform, mechanical** token swap instead of a +bespoke per-line decision, with this doc as the single source of truth. Step 15 applies the +same uniform token to the remaining families (D3 realized as one token — not a re-decision). diff --git a/references/glossary.md b/references/glossary.md index 6949fa4..0083498 100644 --- a/references/glossary.md +++ b/references/glossary.md @@ -248,5 +248,5 @@ Deviation from established personal voice profile. Measured across 6 dimensions: ### Voice Profile Quantified signature of a creator's unique writing style across sentence structure, vocabulary, hook preferences, storytelling approach, tone, and formatting. Updated quarterly. Identity-level traits (avoided words, tone, humor) are protected from automatic modification. -**Used in:** `agents/voice-trainer.md`, `assets/voice-samples/authentic-voice-samples.md` +**Used in:** `agents/voice-trainer.md`, `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/authentic-voice-samples.md` diff --git a/skills/linkedin-studio/SKILL.md b/skills/linkedin-studio/SKILL.md index 44056c9..d98b09e 100644 --- a/skills/linkedin-studio/SKILL.md +++ b/skills/linkedin-studio/SKILL.md @@ -77,7 +77,7 @@ These rules apply to ALL content created by any skill or command: 3. **No external links** in post body (correlate with lower reach — see `references/algorithm-signals-reference.md`) 4. **No corporate buzzwords:** leverage, synergy, paradigm shift, thought leader, disruptive, value proposition, ecosystem, holistic approach 5. **Topic alignment:** Must align with user's 5 core expertise areas (topic-relevance signal) -6. **Voice:** Always read `assets/voice-samples/` before generating content +6. **Voice:** Always read `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/` before generating content 7. **Quality scorecard:** See `assets/checklists/quality-scorecard.md` --- diff --git a/skills/linkedin-voice/SKILL.md b/skills/linkedin-voice/SKILL.md index c69e95a..467eba5 100644 --- a/skills/linkedin-voice/SKILL.md +++ b/skills/linkedin-voice/SKILL.md @@ -138,12 +138,12 @@ Over time, content can drift from your authentic voice -- especially when using **Prevention:** - Quarterly voice audits (use voice-trainer agent) - Read posts aloud before publishing -- Maintain voice samples in `assets/voice-samples/` +- Maintain voice samples in `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/` - Compare drafts against your voice profile ### Voice Samples -**Rule:** Always read `assets/voice-samples/` before generating content. This directory contains reference posts that represent the user's authentic voice. +**Rule:** Always read `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/` before generating content. This directory contains reference posts that represent the user's authentic voice. --- @@ -199,5 +199,5 @@ The differentiation-checker agent evaluates content across five dimensions: |------|--------------| | `references/algorithm-signals-reference.md` | Profile optimization, topic-relevance | | `references/linkedin-visual-style.md` | Visual identity consistency | -| `assets/voice-samples/` | Voice reference (always read before content creation) | +| `${LINKEDIN_STUDIO_DATA:-$HOME/.claude/linkedin-studio}/voice-samples/` | Voice reference (always read before content creation) | | `config/user-profile.template.md` | User personalization setup |