The canonical Python stack has been pinned in ~/dev/.context/AGENT.md since
local-dev v0.3.1, and three skills still had Go examples with no Python
counterpart. Following the structure #18 settled on, and the existing
solid/references/go-adaptation.md precedent, each gets a reference file rather
than inline dilution of the primary doc.
The stack table itself is NOT copied into any of them — each cites AGENT.md as
its home. This repo spent today learning what a second copy of a fact costs.
Content is the part a linter cannot give you:
- clean-code: `raise X from e` as the analogue of `%w` error wrapping, dataclass
over `dict[str, Any]`, and the two traps where the broken version reads
tidier — mutable default arguments, and `if not amount:` for a value where
zero is legitimate.
- solid: `Protocol` over `ABC`, defined at the consumer, which is what makes DIP
real in Python; plus what does NOT carry over from the Go rewrite (return
structs/accept interfaces, embedding, package-as-deployment-unit).
- code-review: a Python anti-pattern table parallel to the Go one, opening with
the rule that a finding ruff or mypy already catches is not a review comment,
it is a question about why the gate is not running.
Refs #20
Bump-Type: minor
Second half of #29. The index was hand-maintained and had already drifted
once: dream and experiment-spec shipped with no row, their rows lost to a
squash-merge after a directory reorg. bin/check-index caught that after the
fact; generating removes the class.
The curated summary and trigger move into each SKILL.md as `summary:` and
`use_when:`, carried over verbatim from the existing table rather than derived
from `description:` — description is a paragraph written for model selection,
and truncating it would have produced a worse table than the one it replaced.
Knowledge now lives once, next to the skill it describes, which is the argument
that made a hand-maintained index the wrong shape to begin with.
The table is written between markers, so the wiring docs, the relationships
tree and the trigger table below it are untouched — those are prose, not data.
Rows are alphabetical now; the conceptual grouping the old order carried lives
in the relationships tree, which is the better place for it. A fourth column
shows each skill's surfaces, which is what makes #29's field visible rather
than merely enforced.
bin/check-index and its test are deleted: a generated table cannot drift in
either direction, so `generate-index --check` subsumes both of its assertions.
Verified by mutation — deleting the tdd row turns `task check` red with
"SKILLS_INDEX.md is out of date".
Closes#29
Bump-Type: minor
The repo distributes three ways — the Claude Code plugin, the symlinked
library for Crush/Pi/OpenCode, and claude.ai, which has no fetch path at all —
and treated every skill as belonging to all of them. claude.ai is not a fourth
host to mirror: gitea-ci, playwright and web-shot have no shell and no cluster
there, so a flat mirror installs skills that can never fire and degrades
selection for the ones that can.
Every SKILL.md now carries `surfaces:`, defaulting to [claude-code, library];
dream, own-the-task and grill-me add `chat`, matching what is actually
installed on claude.ai today. Claude Code accepts the extra key —
`claude plugin validate` passes and the skills still load.
bin/check-surfaces validates values, not just presence, because the failure
mode here is silent: a wrong surface removes a skill from a harness rather than
erroring. install.sh links only skills whose surfaces include `library`, and a
source that declares nothing at all is treated as foreign and linked as-is —
Anthropic's official set has no reason to know this convention. doctor's
missing-link check skips non-library skills for the same reason.
Refs #29
Bump-Type: minor
Skills move to skills/<name>/SKILL.md and the repo gains both manifests, so
it is now a marketplace hosting exactly one plugin whose root is the repo
root (source: "."). Claude Code installs it from git; no symlinks involved.
Why: install.sh wired the library into every consuming repo as an ABSOLUTE
symlink to $CHECKOUT_DIR. The checkout moved three times, most recently when
local-dev@1966d5a dropped the embedded .skills/ copy, and nothing migrated
the links — 243 dangling links across 12 repos, hidden from git by the
**/.claude/skills/ ignore rule. Worse, ~/dev spans two path roots
(/Users/mathias on the iCloud-shared Macs, /home/mathias on koala), so an
absolute link stored there is broken on one side by construction. Re-running
the installer cannot fix that; only removing the mechanism can.
install.sh keeps the harnesses with no plugin system and drops from 42 links
per harness to one directory link, via the convergent ~/.agents/skills
convention that Pi and OpenCode both read:
~/.agents/skills -> $CHECKOUT_DIR/skills
~/.config/crush/skills -> ~/.agents/skills
~/.config/opencode/skills -> ~/.agents/skills
wire_claude_repo is deleted outright rather than gated behind a flag — on a
mixed-root fleet it cannot be made correct. Antigravity and Vibe targets go
too; both unused. CHECKOUT_DIR keeps its host-local default deliberately:
pointing it into ~/dev would put a git working tree inside iCloud with two
concurrent writers.
Taskfile drops five near-identical wiring loops (they had already drifted)
and delegates to install.sh. `task check` gains frontmatter validation, a
stray-root-entry gate, and manifest/tag version parity — the loose
secret-hygiene.md sat unloadable for months because nothing checked layout.
Verified: claude plugin validate . passes clean; a probe skill loaded as
skills:zzprobe through --plugin-dir; task check green at 42 skills.
Refs: #23
Bump-Type: minor
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BeAp5LTscnz2W7Ubt4eJ6m