linkedin-post — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited linkedin-post (Agent Skill) and scored it 92/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 2 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 2 flagged
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
The text {match} tells the agent to skip the normal "ask the user first" gate. Used adversarially it removes the human-in-the-loop check before destructive or sensitive actions, turning a normally-gated agent into a fire-and-forget executor.
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
Take one of Chan's ideas and turn it into a post that is credible to specialists, plain enough for anyone who follows her, and worth reading to the end. Write it for LinkedIn, for X, or for both, and hand it back as one markdown file she can copy from.
This skill has no browser step. Do not call claude-in-chrome, Playwright-as-automation, chrome-devtools, or any other page-driving tool. Do not open LinkedIn or X. Do not open a composer, type into one, resolve mention chips, attach files, or click Post, not even "to stage a draft".
This is deliberate. LinkedIn's editor cannot be driven reliably (it swallows the space after a mention chip, lands backspaces in the wrong place, and exposes no usable file input), and a post published under Chan's name is hers to send. She pastes, tags, attaches and publishes. Your whole deliverable is a file that makes that take a minute.
Still allowed, because they aren't browser automation:
involved.
chan-monkey-illustrationsskill. An API call, not a browser.
| File | Read |
|---|---|
references/voice.md | every run. The writing system: two registers, openers, closers, the "Never" list, the plain-language pass |
references/platforms.md | every run. LinkedIn against X: audience, mechanics, shapes per mode, how to translate |
references/brand.md | every run. Who Chan is, who she writes for, vocabulary, visual tokens |
references/hashtag-bank.md | when a LinkedIn post takes hashtags |
references/recap-playbook.md | recap mode: research, verification, photos, mentions |
references/visuals.md | at Step 4: which visual route, and the render recipes |
examples/post.md | a worked handoff file: one idea, both platforms, with a visual |
Mode, from the /linkedin-post <mode> argument, else inferred, else asked once:
| Mode | Use when |
|---|---|
| story | something happened to her and it changed how she thinks |
| idea | she has a claim and something to back it: a build, a number, a method |
| take | she read or watched something and has a view on it |
| recap | she went to an event and wants to credit the people and say what stuck |
Platforms: linkedin, x, or both. If she named one, use it. The default is both: when she doesn't name a platform, write both and say so. platforms.md lists the cases where a mode doesn't suit a platform; skip those and say why.
The real material. The voice depends on true specifics, so gather them before drafting. Ask only for what is missing, in one message:
what she thinks now.
the numbers they replaced, links.
agrees, extends or disagrees.
stuck; and whether she has photos (paths, or "no").
Never invent an anecdote, a quote, a number or a detail of Chan's experience. If the draft needs one she hasn't given, ask. If she doesn't have it, write without it. (voice.md, first section.)
Don't block on completeness. One round of questions, then draft with what there is and list the gaps in the handoff.
two. Check any figure you plan to repeat.
recap-playbook.md. Confirm each name's spelling, current title and employer.For X, find a handle only from the person's own site or profile, or the event's page.
Never invent a profile or a handle. A wrong tag points Chan at a stranger in public.
Before writing, state to yourself in one sentence what the post says. If it takes two sentences, there are two posts; pick one and offer Chan the other for later.
Then choose the register from voice.md: Story when the material is a moment, Operator when it is a claim with evidence. Recaps use a Story spine with Operator facts.
platforms.md and the register fromvoice.md. LinkedIn first when writing both, unless it's a take on fresh news, which goes to X first.
different opening, different length. platforms.md → "What changes in translation".
voice.md, last section) on both.in **…** and run node scripts/style.mjs output/<date>-<slug>/post.md, which turns them into bold that survives a paste. What may be bold, how much, and why it is rationed: platforms.md → "Bold on LinkedIn". Stories and most takes get none. X gets none.
Read the LinkedIn preview line it prints: if the first 210 characters are warm-up, rewrite the top.
LinkedIn copy carries bold, and that style.mjs --plain removes it.
Read references/visuals.md now. In short:
a few numbers, a straight sequence → an HTML layout rendered to PNG; one quotable line → a quote card.
the brand monkey acting out the post's one idea, generated by the chan-monkey-illustrations skill with the logo as reference (visuals.md §8). It is the only image-model route; every other visual is drawn by code. Exact things (numbers, architecture) are never generated.
field of square pixels, one orange pixel. No halftone dots or colour gradients (retired from the brand; see brand.md).
the first comment (LinkedIn) or a reply (X) so the image can be attached.
--look hand..mmdrenders to a short MP4 with render-video.mjs. Don't make one by default.
One generated visual per post at most (its LinkedIn and X renders count as one). Say in one line which route you took and why.
post.mdOne file: output/<YYYY-MM-DD>-<slug>/post.md. Chan should be able to publish from it without asking you anything. examples/post.md shows the format. Sections, in order, skipping any that don't apply:
3. **`## X`.** How to post it (single, or post 1 then replies), then the copy in a fence opened
with ```` ```text x ````. Thread posts are separated by a line containing only `---`. If a link
belongs in a reply, give it on its own line under the block.
4. **`## Who to mention`.** A table: `# | Name in the copy | LinkedIn: type this, pick the entry
that says… | X handle (and where it was verified) | Confidence`. The "pick the entry that says"
column matters most on LinkedIn: give the headline that separates the right person from their
namesakes, and flag near-misses. Unverified people stay plain text on both platforms.
5. **`## Photos`.** Suggested order, lead shot first, and which one goes on X.
6. **`## Visual`.** Each generated file (PNG, and MP4 if one was made), which platform it is for,
and what it shows.
7. **`## Link`** (take mode). The URL, and where to put it on each platform: LinkedIn body
(preview card) or first comment; X reply. Recommend one. Never a link preview and an attached
image on the same LinkedIn post.
8. **`## Verified facts behind the copy`.** What each claim rests on, with sources.
9. **`## Judgement calls made in the copy`.** Where you softened, sharpened, cut, or chose between
two true framings, with the alternative spelled out. Include anything Chan was asked for and
didn't have, and how the copy works around it.
The fence labels matter: `count.mjs` finds the copy by them.
Then, in chat: the file path, the counts, the visual route in one line, and any open question.
## Setup / dependencies
- Text-only posts need nothing installed. `scripts/count.mjs` uses only Node.
- Rendering needs `npm install` and `npx playwright install chromium` from the repo root.
`node_modules/` is gitignored, so a fresh clone won't have it; `ERR_MODULE_NOT_FOUND` means this.
- `render-mermaid.mjs` falls back to a global `mmdc` when the local one is missing, so a Mermaid
render succeeding doesn't mean the HTML routes will.
- Video (`render-video.mjs`) also needs `ffmpeg` on PATH; the script exits 2 without it.
- The monkey route (`render-monkey.mjs`) needs the `chan-monkey-illustrations` skill installed
(`~/.claude/skills/`, or `CHAN_MONKEY_SKILL`), Python 3 with `openai pillow numpy`, and
`OPENAI_API_KEY` in the environment or a `.env` here or in that skill's folder. It is the only
route that uses an image model or an API key; it exits 2 when any of these is missing.
## Guardrails
- **Only real material.** No invented anecdotes, quotes, numbers or people.
- **English, NZ spelling.** The voice in `voice.md`; nothing from its "Never" list.
- **Each platform gets its own post.** Never a trimmed copy of the other.
- **Bold is a signpost, not emphasis.** LinkedIn only, label lines and lead-ins only, never the
first line, a name, a hashtag or a link. It costs screen-reader and search legibility, so it
is rationed (`platforms.md`).
- **Never open LinkedIn or X.** The deliverable is `post.md`. Chan publishes.
- **Never invent an identity or a handle.** Unverified means plain text.
- **Don't reuse the reference writers' sentences, stories or names.** The system borrows their
craft, not their content.
- **One visual per post, and it must show something the text can't** — a structure, or the idea
as a picture. Real photos and screenshots outrank anything rendered. Never diagram or
illustrate an event or the people in it, and never stack an image on a link preview.
- **The image model draws the monkey and nothing else.** No AI portraits, scenes, charts or
screenshots. Numbers and architecture are drawn by code.
- Keep generated work in `output/` (gitignored); never write drafts to the repo root.~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.