fix(linkedin-studio): S15 — UX finish §6c (B1 onboarding inline-draft + B3 carousel full-deck clipboard)

B2 (router tiering) was already delivered in S14, so S15 = B1 + B3 only.
No surface/count/version change -> within-v4.1.0 refinement (S11-S13 precedent).

- B1 (commands/onboarding.md): replace the "Run /linkedin:first-post" dead-end
  hand-off in Phase 3 with the first-post drafting steps embedded inline (3.1 topic
  -> 3.2 3-line draft -> 3.3 QC -> 3.4 present+clipboard -> 3.5 state-update that sets
  first_post_date). Wizard now yields a draft in-flow; 0 dead-end strings. Stays within
  the existing allowed-tools (Read/Bash/AskUserQuestion); UI-brief §12b scope-guard
  honored (no provider seams / progressive-disclosure added).
- B3 (commands/carousel.md): Step 6 now assembles the ENTIRE deck (every slide's copy
  + the caption) into the clipboard payload, not just the caption; full-deck assembly
  precedes the clipboard-helper.mjs call.

Independent /trekreview (2 Opus reviewers): brief-conformance 0 findings; code-correctness
1 MAJOR that is PRE-EXISTING and out of S15 scope (onboarding Phase 2 saves need Write in
allowed-tools; lines 142/157, untouched by the S15 diff) -> DEFERRED to next session per
"ekte design-funn -> neste sesjon". Verdict ALLOW for the delivered scope (not a WARN-override).

Gate: test-runner.sh 74/0/0; node --test 98/98; commands=29; v4.1.0 unchanged.
See docs/remediation/review.md for the full record (ALLOW + 1 deferred MAJOR).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-05-30 21:44:34 +02:00
commit 8c52bdb2e4
3 changed files with 191 additions and 128 deletions

View file

@ -188,11 +188,29 @@ Create one slide per page using the content above.
Export as PDF and upload directly to LinkedIn.
```
Auto-copy the carousel caption text to clipboard silently:
```bash
printf '%s' '<CAROUSEL_CAPTION>' | node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/clipboard-helper.mjs
**Assemble the entire deck** — every slide's copy (header + body, plus its visual note) followed by the caption — into ONE clipboard payload, so the whole carousel travels in a single copy, not just the caption. A carousel's deliverable is the slide text you paste into your design tool *and* the caption; copying only the caption left the bulk of the work uncopied. Build the payload like this:
```
Then confirm: "Caption copied to clipboard."
SLIDE 1 of [TOTAL] — [purpose]
[HEADER]
[BODY line 1]
[BODY line 2]
...
Visual: [visual note]
SLIDE 2 of [TOTAL] — [purpose]
...
— — —
CAPTION
[caption text]
```
Then auto-copy the full deck to clipboard silently:
```bash
printf '%s' '<FULL_DECK_PAYLOAD>' | node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/clipboard-helper.mjs
```
Substitute `<FULL_DECK_PAYLOAD>` with the assembled deck above — all slides' copy + the caption. Then confirm: "Full deck — [N] slides + caption — copied to clipboard."
Offer refinement options as text (no interactive prompt):
"Want to refine? Options: adjust slide text / change visual style / regenerate specific slide / different hook / ready for publishing."

View file

@ -166,28 +166,76 @@ After setup, recalculate and show updated score.
╚═══════════════════════════════════════════╝
```
Check `first_post_date` in state file:
Check `first_post_date` in state file.
**If null (no first post yet):**
- "You're ready to create your first post! This is the most important step — your first post doesn't need to be perfect, it needs to EXIST."
- Use AskUserQuestion:
1. **Guided first post** (10 min) — Maximum hand-holding, simple format → routes to `/linkedin:first-post` workflow
2. **Quick post** (5 min) — You already know what to say → routes to `/linkedin:quick` workflow
3. **Not now** — I'll post later
**If first_post_date is set (returning user):**
- "You already have your first post (published [date]). Ready for your next one? Use `/linkedin:post` or `/linkedin:quick` whenever you are." Move to Phase 4.
**If first_post_date is set:**
- "You already have your first post (published [date]). Ready to create your next one?"
- Use AskUserQuestion:
1. **Create a new post** → suggest `/linkedin:post`
2. **Quick post** → suggest `/linkedin:quick`
3. **Exit onboarding**
**If null (no first post yet) — draft it inline, right here:**
**If user chooses to post (option 1 or 2):** Don't invoke the sub-command directly — instead, tell them:
"Run `/linkedin:first-post` to start the guided first-post flow."
or
"Run `/linkedin:quick` to create a quick post."
"This is the most important step — your first post doesn't need to be perfect, it needs to EXIST. Let's write it now, together, in this flow."
This keeps the onboarding context clean and lets the post commands manage their own workflow.
Use AskUserQuestion:
1. **Write my first post now** — Draft it inline, ~5 minutes, ready to paste
2. **Not now** — I'll post later (you can use `/linkedin:post` or `/linkedin:quick` anytime)
**If "Not now":** move to Phase 4 and mark the first post Pending.
**If "Write my first post now":** walk through these steps **inline** — do NOT hand off to another command. The post is produced right here in the wizard.
### 3.1 — Pick a topic
Use AskUserQuestion:
1. **Something I learned recently** — a specific insight from your work
2. **A tool or approach I recommend** — something that made your work better
3. **An observation about my industry** — a pattern or trend you've noticed
4. **A question I'm genuinely curious about** — start a conversation
Then ask: "Give me a sentence or two about what you have in mind." If expertise areas are set in the state file, steer the topic toward one of their pillars.
### 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):
- **Line 1 — Hook (under 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
Target 150-500 characters (short posts perform well for new accounts). One point, not three. No external links in the post body.
### 3.3 — Quick quality check
Confirm 4 things before presenting:
- [ ] Hook works in 140 chars?
- [ ] ONE clear point (not three)?
- [ ] Ends with a question or invitation?
- [ ] Sounds like THEM (not corporate/AI)?
Fix any miss before showing it.
### 3.4 — Present and copy
Show the post with its character count, the hook highlighted, and one alternative hook. Auto-copy the post text to clipboard silently:
```bash
printf '%s' '<POST_TEXT>' | node ${CLAUDE_PLUGIN_ROOT}/hooks/scripts/clipboard-helper.mjs
```
Then say: "Post copied to clipboard. Go to linkedin.com, click 'Start a post', paste it, and hit Post."
### 3.5 — Record it
Update state deterministically (this sets `first_post_date` automatically when null):
```bash
node --input-type=module -e "
import { writeState, updatePostTracking } from '${CLAUDE_PLUGIN_ROOT}/hooks/scripts/state-updater.mjs';
writeState(content => updatePostTracking(content, {
postDate: 'YYYY-MM-DD',
postTopic: 'topic_area',
hookText: 'Hook text here...',
charCount: NNNN,
format: 'post'
}));
"
```
Replace the placeholders with the actual post data, then continue to Phase 4.
## Phase 4: Summary and Next Steps
@ -201,7 +249,7 @@ Show final status:
```
Profile: [Optimized / Skipped — run /linkedin:profile later]
Personalization: [XX]% [↑ from YY% if improved]
First post: [Published DATE / Pending — run /linkedin:first-post]
First post: [Published DATE / Pending — create anytime with /linkedin:post or /linkedin:quick]
```
**What's next — your first week:**