Skip to content

Getting started

One command, then one restart, then one paste. That is the whole of it, and the restart is the only part no script can do — Claude Code loads plugins at startup and nothing inside a session can make the app reload itself.

Terminal window
curl -fsSL https://raw.githubusercontent.com/jjanczur/tyran/main/install.sh | sh

It checks Node, installs the plugin, installs the secrets scanner the write gate needs, and then prints the prompt to paste after you restart. That last prompt runs setup, proves the install works, and opens the dashboard in your browser — because everything before that point is files on disk, and the board is where any of it becomes something you can look at.

You do not need to know what a gate, a tier or an autonomy class is to use this. Setup works out what it can from the repository and asks you at most one question in ordinary words. Everything it decides is written down with the reason, and all of it is editable later from the dashboard’s Settings tab rather than by hand.

Paste this into Claude Code and let it install itself. /plugin is a slash command a human has to type, so this asks for the same steps through the claude CLI instead, which Claude Code can run on its own — followed by the restart those steps require, and a walkthrough of what setup inferred before it is trusted:

Install the Tyran plugin in this repository and set it up for me.
1. Run: claude plugin marketplace add jjanczur/tyran
2. Run: claude plugin install tyran@tyran
3. Tell me to restart Claude Code, so the hooks and agents load.
4. After the restart, run /tyran:setup, then /tyran:doctor. Setup writes the
two files under .tyran/ that Tyran needs - the config, and the autonomy
policy the write gate reads - and doctor proves the install actually
works. Walk me through what setup inferred - especially the validation
commands and the deployment autonomy class - before we commit it.
Docs: https://jjanczur.github.io/tyran/getting-started/

Or type the same three steps yourself:

  1. /plugin marketplace add jjanczur/tyran
  2. /plugin install tyran@tyran
  3. /tyran:hello

/tyran:hello confirms three things: the plugin loaded, skills are namespaced under /tyran:*, and ${CLAUDE_PLUGIN_ROOT} resolves to the installed copy.

Requirements: Claude Code ≥ 2.1. No Node dependencies, no build step — Tyran’s scripts are plain Node ≥ 22, bundled with the plugin; you don’t run npm at any point.

Published on npm as @jjanczur/tyran — scoped, not the bare name tyran, which carries an unpublish tombstone from 2021-03-30 that npm’s abuse policy blocks anyone from reusing, permanently. The command stays tyran either way; only the registry name is scoped, the same pattern as @angular/cli giving you ng.

bin/tyran.mjs exposes the same scripts the plugin’s hooks and skills already call — doctor, scan-repo, tiers, journal, schema, knowledge, board, cost, answer, overnight, statusline, stop-check, scan-control-chars, desc-budget — for a shell or a CI job with no Claude Code session. Zero dependencies; --help lists them.

Terminal window
npx @jjanczur/tyran doctor --hooks # is any gate installed but unable to fire?
npx @jjanczur/tyran scan-repo --dir . # what this repo looks like, with provenance
npx @jjanczur/tyran answer render # the sheet of questions waiting on you

Exit codes propagate to the digit, so a CI step reddens exactly when the underlying script does.

/tyran:setup

Setup scans your repo deterministically (languages, package manager, validation commands, CI, merge history), infers your deployment autonomy class — the safest class is always the default and P3 is never inferred — writes .tyran/config.yaml with every field annotated value · source · confidence, and asks you one batch of questions, only about fields it could not establish.

It also installs the autonomy policy under .tyran/policies/, from the shipped template, before it writes the config. That file is what the write gate reads, and a .tyran/ without one refuses every write in the repository — so it is not a separate step you can be left holding. It is only ever created, never overwritten: a policy you have tightened by hand survives every later run of setup. Editing it afterwards is human-only, by design.

It also offers to install a /tyran shortcut into .claude/skills/tyran/, so you can type /tyran instead of /tyran:run. That file is a few lines that hand straight over to the plugin, so updates keep reaching the playbook. It lands in your repo and will show up in your next commit, which is why setup asks first.

/tyran add rate limiting to the public API

Tyran interviews you briefly (only questions that change the plan), triages the task (S/M/L/XL × risk), plans, and drives scout → implementer → reviewer with an enforced evidence contract. You will be stopped only at gates: plan acceptance, irreversible operations, product decisions.

Tyran replies in your language; its artifacts (code, commits, state files) are in English.

CommandWhat it does
/tyran:statusWhere the work is, read from the journal. Names the agents that have not reported yet — the most useful line and the easiest to miss.
/tyran:doctorWhether Tyran itself is healthy here: gates that cannot fire, journal-vs-projection drift, orphaned leases, config that fails its schema.
/tyran:retroLearn from a finished initiative. Usually you do not type this — see below.
/tyran:helloInstallation smoke test.
Terminal window
echo "wrong branch — hold everything" > .tyran/STOP

The conductor checks before every spawn and every merge, halts, and reports where it got to. Delete the file to resume. It needs no session, so it works from a phone at 3am, and it is the one check in the plugin that fails closed.

When every ticket in an initiative is merged and no retrospective has been recorded since the last merge, a Stop hook refuses one turn and says which initiative is owed one. Deciding not to run one is a legitimate answer — record it and the gate is satisfied. You are never blocked twice, whatever you choose.

This exists because the retro is the step most easily skipped: it happens after the work is done, after the merge, when the interesting part is over. It is also the only mechanism by which Tyran gets better at your repo rather than repeating itself.

Terminal window
claude plugin update tyran@tyran

Then restart Claude Code — hooks and agents are read when a session starts, so an update that is not restarted into is an update you are not running.

The plugin is installed per scope, not per repository (claude plugin list shows yours; --scope user is the default). At user scope one update reaches every repo you use Tyran in.

An update does not touch your .tyran/ directory. That is your repository’s data — config, knowledge, journals, and the autonomy policy — and it is deliberately outside the plugin so a version change cannot rewrite the boundary you set. Re-running /tyran:setup after an update is safe and optional: it re-scans and rewrites config.yaml, and it never overwrites an existing .tyran/policies/autonomy.yaml.

If a repo was set up before the policy was installed automatically — the symptom is every write being refused with “has a .tyran/ directory but no .tyran/policies/autonomy.yaml — this repairs it and changes nothing else:

Terminal window
node "${CLAUDE_PLUGIN_ROOT}/scripts/scan-repo.mjs" --dir . --ensure-policy
Terminal window
claude plugin disable tyran

Your .tyran/ directory is plain data in your repo — it survives uninstall/reinstall and any plugin version change.