tyran
CLAUDE.md81 starsChanged 4 months agoApache-2.0View on GitHub
Tyran is a Claude Code plugin: skills, agents, PreToolUse hooks, and zero-dependency Node scripts. No build step, no runtime deps, Node ≥ 22. Read CONTRIBUTING.md for the dev loop, the test commands and the enforced rules — they are not repeated here. This file is the rest: the places where the obvious move is the wrong one, and the procedures that have already cost someone a wasted release. The house rule that shapes everything else: one answer lives in one…
In this repository15 other files it gives its agents.
- docs/agents.mdAGENTS.md
- browser-checkSkill
- code-reviewSkill
- deslopSkill
- doctorSkill
- fidelity-gateSkill
- helloSkill
- pr-feedbackSkill
- prompt-tuningSkill
- retroSkill
- root-causeSkill
- runSkill
- setupSkill
- skill-writingSkill
- statusSkill
The file
We split this CLAUDE.md into the rules it gives an agent
3 rules. About 0 are common, 2 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 6
Read CONTRIBUTING.md for the dev loop, the test commands and the enforced rules — they are not repeated here.
- Distinctive: line 12
Before adding a paragraph, look for where the answer already is and link to it.
- Very specific: line 79
Before that, the directory was absent and the gate was silent here.
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md 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
CLAUDE.md
Tyran is a Claude Code plugin: skills, agents, PreToolUse hooks, and
zero-dependency Node scripts. No build step, no runtime deps, Node ≥ 22.
Read CONTRIBUTING.md for the dev loop, the test commands and the enforced rules — they are not repeated here. This file is the rest: the places where the obvious move is the wrong one, and the procedures that have already cost someone a wasted release.
The house rule that shapes everything else: one answer lives in one place. Before adding a paragraph, look for where the answer already is and link to it. Three spellings of one rule is the defect this repo names ADR-21, and it has been found here more than once.
Two surfaces for every document
docs/*.md (GitHub) and site/src/content/docs/*.mdx (the docs site) publish
the same claims. Change both, always. They were migrated from each other
once and immediately diverged, because nobody re-reads a page they already
reviewed. tests/unit/docs-claims.test.mjs checks both surfaces.
Prose in this repo is checkable on purpose:
- numbers are claims. "47 unit tests" in a doc is verified against the actual count. Do not write a number you have not just measured.
docs/doctor.md's severity table must equalSEVERITY_BY_CODEinscripts/doctor.mjs, in both directions. Add a finding code, add the row.- no model name outside
tiers:—tests/unit/agents.test.mjsfails ifhaiku/sonnet/opus/fableappears in any agent or skill file.
Releasing — the version, not the commit
claude plugin update compares the version field, not the commit. A fix
merged to main without a bump reports "already at the latest version" and
reaches nobody. This has happened; it is the reason this section exists.
Bump all three in one commit — they are cross-checked by different tools:
| file | checked by |
.claude-plugin/plugin.json | what claude plugin update reads |
.claude-plugin/marketplace.json | claude plugin tag fails if it disagrees with plugin.json |
package.json | npm-publish.yml fails if it disagrees with the release tag |
Then claude plugin tag . (creates tyran--v<version>), plus a v<version>
tag — both, because they serve different consumers and neither covers the
other: npm-publish.yml computes ${GITHUB_REF_NAME#v}, which tyran--v0.1.1
does not satisfy.
Publishing to npm happens when a v<version> tag is pushed or a GitHub
release is published — npm-publish.yml triggers on both (a pushed tag was
added in 0.1.4, after the manual release step was skipped for three consecutive
versions and npm drifted three releases behind the marketplace). npm refuses a
duplicate version, so if both fire the second fails loudly rather than shipping
something different under the same number. Publishing is irreversible — a
version, once on npm, cannot be replaced.
The gate runs on you too
hooks/scripts/policy-gate.mjs is live in any session working inside a repo
that has adopted Tyran. Two consequences that look like tool failures:
- A
Bashcommand whose text nameshooks/**or.tyran/policies/**is refused — including a validator. Soschema.mjs validate policy .tyran/policies/autonomy.yamlis a command a human runs in a terminal; an agent usesnode scripts/doctor.mjs --state --dir .tyran, which validates the same file and names only the directory. Readis not refused for those paths. Reach for it instead ofcat.
This repository adopted Tyran on 2026-08-17 (0.1.41), so the path classes
now DO apply to Tyran's own source, exactly as they do in a user's repo. The
consequence to plan around: hooks/** is KERNEL, so an agent session cannot
edit the gates here. A change to hooks/scripts/*.mjs is a human edit made
outside a session — which is what the class has always meant everywhere else,
and Tyran's own repo was the last place still exempt from its own rule.
Before that, the directory was absent and the gate was silent here; treat any
instruction that assumes an allowed Edit hooks/… as pre-0.1.41.
The YAML subset is small, and it bites
scripts/yaml-lite.mjs rejects block scalars (>-, |), anchors, tags, flow
mappings and tabs — loudly, with a line number. Hand-writing a long source:
in a .tyran/config.yaml as >- produces a file the policy gate cannot parse,
which makes it refuse every write in that repo. Keep long values on one
single-quoted line. stringify() already does; humans and agents editing by
hand are the risk.
The docs site
- Cross-links must be relative (
../policy-gate/). The site is served fromjjanczur.github.io/tyran, andsite/scripts/check-base-prefix.mjsfails the Pages build on any root-absolute URL missing the base prefix. - Building it locally needs
npx playwright install chromium-headless-shell— the build renders og-images in a browser. Without it the build dies with a stack trace that says nothing about Playwright until you scroll.
cd site && npm ci && GITHUB_PAGES=true npm run build && node scripts/check-base-prefix.mjsnpm run build, not npx astro build: the build first generates the clickable
sandbox board into public/sandbox/ from the journals in site/sandbox/,
shifting their timestamps so the agent ages stay fresh. Call astro directly
and you publish a site whose sandbox link 404s.
Small things that waste a cycle
node --test tests/unit/fails withMODULE_NOT_FOUND. The runner wants the quoted glob:node --test "tests/**/*.test.mjs".scan-repo.mjs --write .tyran/config.yamlalso installs the autonomy policy, before the config, on purpose — see the comment onensureAutonomyPolicy.--ensure-policyis the repair-only path.- Skill descriptions are summed into a context budget CI enforces. A new skill costs every session, whether it fires or not.
- When a change ADDS a file,
git addbefore running the suite. The control-char scan builds its file list fromgit ls-files, so an untracked file is invisible to it: the suite passes locally and CI — which only ever sees committed files — is the first thing to look at the real set. Two separate cycles have been lost to this. - When a change adds or removes TESTS, update the README's count. It is
verified by a CI step, not by the suite, so
node --teststays green and the push is the first thing that disagrees.grep -n 'unit tests' README.mdagainst the run's owntests N. Two cycles lost to this in one session, which is the same shape as the entry above: a check the local loop cannot see. - A field added to
cost.mjs's scan needs two more edits or it is silently null forever.report.sourcesis a WHITELIST projection, andCOST_SCHEMAis what discards caches written before the field existed. Missing either, an unchanged transcript keeps its old record and the field reads null on every later run — which produced a reported span of ONE day where the truth was nineteen. A wrong number, not a missing one.
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.

