Source for weindie.com.
WeIndie is four skills for working with AI. The site explains what each one is
for, and gives each skill (/spec, /drift, /kiss, /ship) a page you can
send to someone.
The set is deliberately not named after one of its members, and all four are labelled simply as WeIndie skills.
skill.json has an optional partOf field that overrides that label, for a
skill that belongs to a named method. It is unused, and on current evidence it
stays that way: /spec carried partOf: Spec First until that method was
retired. It is not merely unpublished — there is no public artefact to
point a reader at, so the label was a claim nobody could check, and
"spec-first" already means something else in API design. The field is kept for
the day a skill belongs to a method that is actually live; setting it is one
line and the build does the rest.
What /spec did keep from that method is said on the page instead, in the
optional provenance block in skill.json — a short account of where the
skill came from, rendered as an unnumbered section after the source. It exists
because spec-first.com now redirects to /spec, and a reader who follows
that redirect would otherwise land on a skill with no idea why. Only /spec
has one, and the build checks that a provenance block that exists actually
says something.
A skill is one SKILL.md file. Its page on the site has a prompt you can paste
into a conversation to try the idea once, and the file itself when you want to
keep it. The download goes in a folder named after the skill:
.claude/skills/kiss/SKILL.md Claude Code, one project
~/.claude/skills/kiss/SKILL.md Claude Code, all your projects
Cursor reads .cursor/skills/, Codex and the specification .agents/skills/.
Each page gives the path for the environment you pick, and a command that puts
the file there:
mkdir -p .claude/skills/kiss && curl -fsSL https://weindie.com/kiss/SKILL.md -o .claude/skills/kiss/SKILL.md
The frontmatter says which copy you are holding: name matches the folder,
metadata.version and metadata.source give the version and the page it came
from, and based_on appears only on a customised download.
Changing a skill is editing the file. The bullets under "Defaults you can change" are the lines written to be swapped — the customise controls on the page do exactly that, and by hand nothing stops you at those lines.
canonical skill + platform packaging = platform download
canonical skill + your preferences = custom skill
There is exactly one version of each skill. There are no separate Claude Code, Cursor or Codex variants — only different install paths.
skills/
catalogue.json which skills exist, in what order, and the default label
<slug>/SKILL.md the canonical skill — the single source of truth
<slug>/skill.json page content and customisation options
changelog.json what changed and when — also the site version
fonts/ self-hosted woff2 + OFL licences (no third-party requests)
src/
site.css styling shared by every generated page
home.html homepage template
skill.js skill-page behaviour (copy, customise, diff, download)
changelog.html changelog template
not-built.html "what was left out" template
404.html not-found page template
build.js the generator — no dependencies
Everything else in the repository root is generated. Do not edit it by hand:
index.html homepage
changelog.html changelog -> weindie.com/changelog
not-built.html what was left out -> weindie.com/not-built
feed.xml Atom feed of the changelog
404.html not-found page
<slug>.html skill page -> weindie.com/<slug>
<slug>/SKILL.md raw skill -> weindie.com/<slug>/SKILL.md
og.png, og/<slug>.png link-preview cards
The page is <slug>.html rather than <slug>/index.html on purpose: Cloudflare
Pages serves /kiss straight from kiss.html, where a directory would
308-redirect to /kiss/ and leave the address bar disagreeing with the canonical
tag. These URLs get pasted into conversations, so the short form should be what
people end up on.
node build.js
Requires Node, and Google Chrome for the link-preview cards. The cards are laid
out as HTML in .cache/og/ and screenshotted headless at 1200x630, because they
use the site's own webfonts and an SVG rasteriser only sees system fonts. PNGs
are only re-rendered when their source HTML changes, so a normal build does no
image work.
The build refuses to run if a skill is inconsistent. It checks that:
- the
namein SKILL.md frontmatter matches the folder name metadata.sourcematches the public URL- every customisation option has exactly one default
- each default option line appears in SKILL.md exactly once
- no non-default option line appears in SKILL.md
- the bullets under "Defaults you can change" correspond one-to-one with the
options in
skill.json - a
provenanceblock, where one exists, has at least one non-empty paragraph and links that carry all three of a href, a key and a description
That last set is what keeps customisation honest: the browser generates a custom skill by replacing those exact lines, so if the text drifts apart the build stops rather than silently producing a file that changes nothing.
The changelog is held to the same standard — see below.
mkdir skills/<slug>and addSKILL.mdandskill.json. Copy an existing pair — the shapes are small and self-explanatory.- Add the slug to
skills/catalogue.json. node build.js, then commit the generated output.
Nothing assumes a particular number of skills.
changelog.json is the source for /changelog, /feed.xml and the version
in the footer. There is no version string anywhere else, so the footer and the
changelog cannot disagree.
{
"entries": [
{
"date": "2026-09-03",
"site": "1.2", optional — a site release
"skills": { "spec": "0.2" }, optional — skill versions this moved
"title": "A way in",
"body": ["one paragraph", "another"]
}
]
}
Entries are newest first, and an entry needs site, skills or both. body is
an array of plain paragraphs rather than Markdown, so the build stays dependency
free — same reason skill.json stores structured strings.
The build refuses to run unless:
- every date is a real
YYYY-MM-DD, and no entry is newer than the one above it - every entry has a title and at least one non-empty paragraph
- every slug named under
skillsis in the catalogue - every skill has at least one entry, and the newest entry naming a skill states that skill's current version in SKILL.md
- no two entries produce the same feed id
- at least one entry declares a
siteversion
That fourth rule is the point of the file: bump a skill's version and the build stops until you have written what changed. The site asks people to read a file before running it, so it owes them a way to find out when the file moved.
Feed ids are https://weindie.com/changelog#<date>-<version>, which is also the
anchor on the page. They are permanent — never regenerate an id for an entry that
has already been published, or every reader will show it as new.
SKILL.md frontmatter stays inside the six fields of the
Agent Skills specification. Version and
source live in the metadata map so the file remains spec-valid:
---
name: drift
description: ...
license: MIT
metadata:
version: "0.1"
source: https://weindie.com/drift
---
A customised download adds based_on: WeIndie /drift v0.1 to the same map.
python3 -m http.server 8787
Then http://127.0.0.1:8787/. Locally, skill pages need the extension
(/kiss.html) — Cloudflare Pages resolves the extensionless /kiss in
production, Python's server does not.
Two self-hosted faces, latin subset, ~78 kB total:
IBM Plex Mono structure, labels, code, skill names
Newsreader questions and prose (one variable file, weights 400-600)
They are served from /fonts/ rather than a font CDN so the site keeps making
zero third-party requests. See fonts/README.md.
Light and dark, driven entirely by CSS custom properties. A small script in
<head> applies a stored choice before the first paint; with no stored choice
there is no data-theme attribute and prefers-color-scheme decides. Never
declare a colour inside a @media (prefers-color-scheme) or [data-theme]
block — only redefine tokens there, or the un-stamped state breaks.
There is also a print stylesheet: ink on white, panels open, controls hidden.
No analytics, no trackers, no third-party requests, no build-time or run-time network calls, no cookies. Problem matching, customisation and download generation all happen in the visitor's browser.
One thing is stored: the theme toggle writes weindie-theme to localStorage
so a chosen light or dark setting survives a reload. Nothing else is kept, and
nothing is ever sent anywhere. The claim on the site — that what you type stays
in the browser — is literally true. Keep it that way, and keep this paragraph
accurate: the whole argument here is that the claims survive inspection.
Production is Cloudflare Pages, project weindie-site, connected to this
repository. Pushing to main deploys automatically.
The Pages project kept its original name when the repository was renamed from
weindie-site to weindie. They are separate things: Pages tracks the
repository by id rather than by name, so the rename did not break the link.
Production branch main
Build command (none — generated output is committed)
Output directory / (repository root)
Pages hostname weindie-site.pages.dev
Canonical hostname weindie.com
Pages has no build command on purpose: the generated files are committed, so the
deployed site is exactly what is in the repository, and Pages stays a plain
static host. Run node build.js before committing.
https://www.weindie.com redirects to https://weindie.com with a 301 that
preserves the path and query string.
Open an issue: https://github.com/aarontaylor-dev/weindie/issues.
A skill that misfired is the most useful thing to report — the site tells people to read a file before they run it, so a file that behaves unexpectedly is a defect in the writing, not in the reader.
MIT — see LICENSE.txt. This covers the site and the skills.
The site copy says the work can be used, changed and forked; the licence is what
makes that true rather than merely stated. Each canonical SKILL.md also carries
license: MIT in its frontmatter, so a downloaded skill states its own terms
without needing to be traced back here.
LICENSE.txt rather than LICENSE so the same file is both recognised by GitHub
and served as readable text at https://weindie.com/LICENSE.txt.
The copyright line names Aaron Taylor, so the grant has an identifiable holder. A fork should put its own name there.