publish-post
Skill0 starsChanged 3 months agoMITView on GitHub
End-to-end publishing pipeline for a new article on shane.logsdon.io. Runs text quality passes (humanize → ms-style-pass), generates brand-system design artifacts (blog hero OG + in-page hero, LinkedIn post image), screenshots them at 1x, wires images into the article frontmatter and site, routes LinkedIn companion text through humanize and writing-review, saves it to Obsidian, builds the site, bumps the service worker cache version, and commits + pushes. One pre-commit gate can halt the run: unresolved [TK] gaps, claims that fail independent verification, or a missing provenance block. Otherwise unattended — no human checkpoints. Working directory must be the site repo root. Triggers: "publish <slug>", "publish the post", "run the publish pipeline", "/publish-post".
5 other projects use a version of this.
Adopted by
Most recently taken up by
- content-mill/claude-plugin-linkedintheir own version, first seen Nov 1, 2026
- content-mill/claude-plugin-xtheir own version, first seen Nov 1, 2026
- content-mill/claude-plugin-blueskytheir own version, first seen Oct 25, 2026
- content-mill/claude-plugin-facebooktheir own version, first seen Oct 25, 2026
- content-mill/claude-plugin-instagramtheir own version, first seen Oct 25, 2026
Used with
Projects with it also use review-posts and draft-post.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
content-millSkillreview-posts
Show the user's Bluesky posts in Content Mill, meaning what is scheduled, published, failed or still a draft, and cancel a scheduled one. Use when the user asks what is scheduled, what went out, or whether something failed.
Also in 5 projects that use this
spirosbaxSkilldraft-post
Draft a LinkedIn post for the user's own company page, in that company's own voice, plus an optional visual brief. Reads COMPANY_NAME and COMPANY_LINKEDIN from insaight:get_config, loads the company's real past posts through the Insaight MCP, extracts their hook, structure, length, emoji, hashtag and language patterns, then writes two variants against that reference. If the user pastes a link, fetches the article and treats it as the brief. Use this whenever the user wants something published on LinkedIn, even when they never name a skill or their company: 'write a LinkedIn post', 'draft a post about [topic]', 'post idea for [theme]', 'create LinkedIn content', 'help me write a post', 'make a post from this article', 'turn this into a post', or a pasted URL with 'post this'. Prefer it over drafting freehand — a post written without the style reference reads generic and off-voice. For one-to-one cold DMs or emails to a prospect, use draft-outreach instead.
Also in 5 projects that use this
Other versions5 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 5
- content-mill/claude-plugin-bluesky★ 0same name
- content-mill/claude-plugin-facebook★ 0same name
- content-mill/claude-plugin-instagram★ 0same name
- content-mill/claude-plugin-linkedin★ 0same name
- content-mill/claude-plugin-x★ 0same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository11 other files it gives its agents.
- adversarial-diff-verifySkill
- agent-harnessSkill
- agents-md-generatorSkill
- plugin-manifest-referenceSkill
- research-sweepSkill
- workflow-designSkill
- workflow-feature-specSkill
- workflow-monthly-skill-auditSkill
- workflow-stage-draftSkill
- workflow-vault-weeklySkill
- work-trackingSkill
The file
We split this skill into the rules it gives an agent
12 rules. About 0 are common, 7 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈7
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 28
Run all steps in order. Do not skip steps.
- Distinctive: line 54
Read the file. Extract and hold in context:
- Distinctive: line 95
Run this Node script, substituting the actual artifact path:
- Very specific: line 132
If image already exists, replace it.
- Distinctive: line 209
After writing, commit the vault:
- Distinctive: line 223
Verify the build succeeds (exit code 0) before continuing.
- Very specific: line 229
Read public/sw.js. Find the current cache name (pattern: shane-logsdon-io-static-vN).
- Distinctive: line 245
If any are found, stop. Do not build, do not commit.
- Distinctive: line 277
Do not stage dist/.
- Very specific: line 279
Commit message. Carry the provenance block from pre-commit into the body so each publish records what came from Shane and what the model …
- Very specific: line 296
Default: run all thirteen steps in order, end-to-end, no checkpoints.
- Very specific: line 300
If <step> is not in {locate, humanize, style, hero, hero-shot, frontmatter, linkedin, linkedin-shot, companion, build, sw-bump, commit}, …
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
For drafting upstream of this skill, use /workflow-stage-draft — it covers the idea → draft → humanize → ms-style-pass chain that gets a piece ready for publish-post. This skill assumes the draft is already voice-corrected and style-checked.
Skill: publish-post
Orchestrates the full publishing pipeline for a single article. One invocation, end to end.
Input
- Required: article slug (kebab-case, e.g.
the-ax-shift) - Brand slug is always
shane-personal-v2
Steps
Run all steps in order. Do not skip steps. Do not pause for review.
| Step name | What it does |
locate | Find the article and extract metadata |
humanize | Voice pass on the article body |
style | MS Writing Style Guide pass |
hero | Generate blog hero artifact |
hero-shot | Screenshot blog hero (OG + in-page) |
frontmatter | Wire image filenames into article YAML |
linkedin | Generate LinkedIn post artifact |
linkedin-shot | Screenshot LinkedIn image |
companion | Save companion text to Obsidian |
build | Build the site |
sw-bump | Bump service worker cache version |
pre-commit | Gate: gaps, claim verification, provenance |
commit | Stage files, commit, push |
Step locate — Locate the article
find ./pages/articles -name "<slug>.md" | head -1Read the file. Extract and hold in context:
title— full article title from frontmatterdate— formatted asYYYY.MM.DDfor artifact datelinesdescription— fromresources/data/articles-list.json(key = slug), used as dekcategory— derived from the file's directory path (e.g.pages/articles/strategic-insights/→strategic insights)
Step humanize — Text pass: humanize
Invoke the humanize skill on the full article body (everything below the YAML frontmatter). The skill edits prose in place. Apply all its rules. Write the result back to the .md file using the Edit tool.
Step style — Text pass: ms-style-pass
Invoke the ms-style-pass skill on the same file, after humanize has run. It operates on what humanize produced — do not re-run humanize after this step. Write the result back to the .md file.
Step hero — Generate blog hero
Invoke the design-blog-hero skill with brand slug shane-personal-v2 and the following brief:
- Headline: article title, sentence-case, ending in a period (max ~12 words)
- Dek: article description (from
locate) - Category: article category (from
locate) - Dateline: article date formatted
YYYY.MM.DD - Accent word: the single most specific noun or subject word in the title (the word that names what the article is actually about)
The skill writes: ./design/shane-personal-v2/artifacts/blog-hero-YYYY-MM-DD-<slug>.html
Step hero-shot — Screenshot the blog hero (1x)
Find the artifact:
ls -t ./design/shane-personal-v2/artifacts/blog-hero-*-<slug>.html | head -1Run this Node script, substituting the actual artifact path:
const puppeteer = require('puppeteer');
const path = require('path');
const ARTIFACT = '<artifact-path-from-above>';
const OUT = path.resolve('./public/images');
(async () => {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-web-security']
});
const page = await browser.newPage();
await page.goto('file://' + ARTIFACT, { waitUntil: 'networkidle0' });
await page.setViewport({ width: 1200, height: 630, deviceScaleFactor: 1 });
const og = await page.$('.canvas-og');
await og.screenshot({ path: OUT + '/<slug>-og.png', type: 'png' });
await page.setViewport({ width: 1440, height: 600, deviceScaleFactor: 1 });
const hero = await page.$('.canvas-hero');
await hero.screenshot({ path: OUT + '/<slug>-hero.png', type: 'png' });
await browser.close();
console.log('done');
})();Critical: deviceScaleFactor: 1 always. 2x PNGs cause LinkedIn CDN blur because the declared og:image dimensions won't match the actual file dimensions.
Step frontmatter — Update article frontmatter
Edit the article's YAML frontmatter to set:
image: <slug>-og.png
heroImage: <slug>-hero.pngIf image already exists, replace it. If it doesn't, add both fields after slug:.
Step linkedin — Generate LinkedIn post
Invoke the design-linkedin-post skill with brand slug shane-personal-v2 and the following brief:
- Headline: max 8 words from the title (legibility at feed scale)
- Supporting line: article description
- CTA: omit
- Attribution: omit
- Companion text style: insight
The skill writes: ./design/shane-personal-v2/artifacts/linkedin-YYYY-MM-DD-<slug>.html
Step linkedin-shot — Screenshot the LinkedIn image (1x)
Find the artifact:
ls -t ./design/shane-personal-v2/artifacts/linkedin-*-<slug>.html | head -1Run:
const puppeteer = require('puppeteer');
const path = require('path');
const ARTIFACT = '<artifact-path-from-above>';
const OUT = path.resolve('./design/shane-personal-v2/screenshots');
(async () => {
const browser = await puppeteer.launch({
headless: true,
args: ['--no-sandbox', '--disable-web-security']
});
const page = await browser.newPage();
await page.goto('file://' + ARTIFACT, { waitUntil: 'networkidle0' });
await page.setViewport({ width: 1200, height: 627, deviceScaleFactor: 1 });
const canvas = await page.$('.canvas');
await canvas.screenshot({ path: OUT + '/linkedin-<slug>.png', type: 'png' });
await browser.close();
console.log('done');
})();Step companion — Extract companion text and save to Obsidian
Extract the companion text from the HTML comment block at the end of the LinkedIn artifact:
awk '/COMPANION TEXT:/{found=1; next} /-->/{if(found) exit} found{print}' \
./design/shane-personal-v2/artifacts/linkedin-*-<slug>.htmlThe companion text is published prose, and Publishing/LinkedIn/ is the only surface with a live audience. The design skill that produced it does not voice-check it, so run two passes before it goes near the vault:
- Invoke
humanizeon the companion text and apply the result. - Invoke
writing-reviewand resolve everyask-authorfinding. The post is short enough that one invented number is the whole risk.
Then invoke the obsidian skill to create a note at Publishing/LinkedIn/<slug>.md with this content:
# <title>
**Published:** <YYYY-MM-DD>
**Article:** https://shane.logsdon.io/<type>/<category>/<slug>/
**Image:** `design/shane-personal-v2/screenshots/linkedin-<slug>.png`
---
<companion text verbatim>After writing, commit the vault:
VAULT="$HOME/Library/Mobile Documents/iCloud~md~obsidian/Documents/Personal"
git -C "$VAULT" add '<the note paths you wrote>' && git -C "$VAULT" commit -m "docs: linkedin companion for <slug>"Step build — Build the site
composer buildVerify the build succeeds (exit code 0) before continuing.
Step sw-bump — Bump the service worker cache version
Read public/sw.js. Find the current cache name (pattern: shane-logsdon-io-static-vN). Increment N by 1. Use Edit with replace_all: true to update both occurrences (in install and activate handlers).
Step pre-commit — the gate before publishing
Three checks. A failure in any of them stops the run: do not build, do not commit.
1. No unresolved gaps. Scan the article body for [TK markers, using the exact article path that locate resolved:
grep -n '\[TK' "<article-path-from-locate>"Exit code 1 means no hits, which is the pass case. Do not rely on a ** glob here: globstar is off by default in the bash macOS ships, so ** silently behaves like a single *.
If any are found, stop. Do not build, do not commit. Report each hit with its line number and ask Shane to either supply the material or explicitly authorise cutting the passage.
An unresolved [TK] at publish time means the article contains either a reader-facing placeholder or, worse, a specific someone invented to fill the gap. Neither ships. This is the one gate in this pipeline that overrides the no-checkpoints rule.
2. Claims survive an adversary. Collect every checkable factual claim in the article — numbers, dates, percentages, quotations, causal claims, "first / largest / most" assertions, and customer or vendor statements — together with the path or note that backs each one. Then invoke the verify skill once per claim. One verifier per claim; never batch two claims into one verifier, and never hand the verifier this session's context or your own summary of it.
A claim with no artifact behind it cannot pass verify, and that is by design — it returns drop with unsettled:, which means the claim needs Shane's sourcing or should be cut. Do not paper over a drop by rewording the claim into something weaker but still unsourced.
3. Provenance for the commit body.
Author material: <which sections or language came from Shane, the vault, or his own input>
Model contribution: <organization, structure, connective prose, artifact generation>
Open items: noneStep commit — Commit and push
Stage these files:
pages/articles/**/<slug>.md
public/images/<slug>-og.png
public/images/<slug>-hero.png
public/sw.js
design/shane-personal-v2/artifacts/blog-hero-*-<slug>.html
design/shane-personal-v2/artifacts/linkedin-*-<slug>.html
design/shane-personal-v2/screenshots/linkedin-<slug>.png
design/shane-personal-v2/components/components.htmlDo not stage dist/.
Commit message. Carry the provenance block from pre-commit into the body so each publish records what came from Shane and what the model wrote:
feat: publish <slug>
Author material: <sections or language from Shane, the vault, or his own input>
Model contribution: <organization, structure, connective prose, artifact generation>
Open items: none
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>Push to origin.
Resume protocol
Default: run all thirteen steps in order, end-to-end, no checkpoints. pre-commit is the only step that can halt a run.
--resume-from <step>: skip every step before <step> and begin from <step>. The named step itself runs. The rest of the pipeline runs end-to-end from there.
If <step> is not in {locate, humanize, style, hero, hero-shot, frontmatter, linkedin, linkedin-shot, companion, build, sw-bump, commit}, print:
Invalid resume-from step: <step>
Valid steps: locate, humanize, style, hero, hero-shot, frontmatter, linkedin, linkedin-shot, companion, build, sw-bump, pre-commit, commit…and stop.
Required state when resuming
locate always runs first inside any resume — even when --resume-from <later-step> is passed — because every later step depends on the slug-derived metadata (title, date, description, category). Treat locate as implicit setup, not a step that can itself be skipped from the user's perspective.
State required to start at each step:
--resume-from locate— same as a fresh run; needs the slug.--resume-from humanize— needs the article file (located vialocate).--resume-from style— needs an article that has been throughhumanize(or that the user explicitly says is voice-correct). Confirm before running.--resume-from hero— needs the cleaned article body (text passes done). Pulls headline / dek / category / dateline from the located article.--resume-from hero-shot— needs./design/shane-personal-v2/artifacts/blog-hero-*-<slug>.html. If absent, prompt the user or run fromhero.--resume-from frontmatter— needs./public/images/<slug>-og.pngand./public/images/<slug>-hero.png. If absent, prompt or run fromhero-shot.--resume-from linkedin— needs the article metadata; independent of hero artifacts.--resume-from linkedin-shot— needs./design/shane-personal-v2/artifacts/linkedin-*-<slug>.html. If absent, prompt or run fromlinkedin.--resume-from companion— needs the LinkedIn artifact HTML (companion text is embedded in the comment block) and the LinkedIn screenshot path. If either is absent, prompt or run fromlinkedin-shot.--resume-from build— independent of prior artifacts; just runscomposer buildagainst the working tree as it stands.--resume-from sw-bump— needs a successful build. Ifcomposer buildhas not run since the last edit topublic/, ask whether to runbuildfirst.--resume-from pre-commit— needs the final article body. Runs the gap scan, the claim verification, and the provenance block.commitwill not run while any[TK]remains unresolved, or while any claim came backunsettled.--resume-from commit— needs all final files in place (article, images, sw.js bump, design artifacts, screenshots, components.html). Showgit statusbefore committing so the user can verify the staged set; abort and ask for direction if the diff looks unexpected.
Examples:
/publish-post the-ax-shift --resume-from hero # text passes already done by hand
/publish-post the-ax-shift --resume-from build # everything generated, just rebuild + ship
/publish-post the-ax-shift --resume-from commit # final retry after a transient build hiccupRules
- No open
[TK]ships, and no unsourced claim ships.pre-commitis a hard gate, not a warning. If it fires, stop and ask. This is the only step in the pipeline allowed to override the no-checkpoints rule. - Order is fixed. humanize before ms-style-pass. Hero before LinkedIn. Screenshots before frontmatter update. Build before commit.
- 1x screenshots always.
deviceScaleFactor: 1— never 2. Keeps declared og:image dimensions matching the actual PNG. - Companion text to Obsidian, not to the repo. The companion text note goes to Obsidian only, and only after it has been through
humanizeandwriting-review. - Stage all design artifacts and screenshots. Design artifacts, screenshots, and components.html are committed for documentation. Only dist/ is excluded.
- If design-blog-hero or design-linkedin-post need an accent word and none is obvious, use the most specific noun in the title — the word that would answer "what is this article actually about?"
- If the build fails, stop. Do not commit. Report what failed and suggest
--resume-from buildafter the user fixes the underlying issue.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

