tiger-style
Check the column budget of the lines a change adds, and read that change against the TIGER_STYLE shape rules a script cannot judge. Use when the user asks about line length or column limits, to check code shape, what the column check flagged, or why a commit was blocked on a column budget.
- License 许可
- ISC
- 最近更新
- 2026-11-13
- Stars
- ★ 3
- Forks
- 0
01 前言 Front Matter
- name
- tiger-style
- description
- Check the column budget of the lines a change adds, and read that change against the TIGER_STYLE shape rules a script cannot judge. Use when the user asks about line length or column limits, to check code shape, what the column check flagged, or why a commit was blocked on a column budget.
02 资源文件 2
- 脚本scripts/tiger-check-cli.ts11.9 KB
- 脚本scripts/unicode-width.ts6.6 KB
03 SKILL.md 正文
tiger-style — one shape rule computed, the rest read by hand
One TIGER_STYLE rule is cheap to compute from a diff; the rest need a reader. This skill keeps the two halves apart and labelled — a checklist presented as a gate is worse than no gate.
When to use
- Before committing a change that adds source lines.
- When a build reports
TIGER: BLOCKorTIGER: NAGand you need to know which limit applied. - When reviewing someone else's diff for shape rather than correctness.
Run the check
"${CLAUDE_PLUGIN_ROOT}"/bin/tiger-check [repo-path]
Reads the staged diff (git diff --cached), never HEAD. --cached compares HEAD against the index, which is exactly what the commit will contain. HEAD would compare against the working tree instead: it would flag unstaged edits the commit does not carry, and it fails outright in a repository with no commits yet — a failure the checker would read as "nothing was added".
Only lines the change adds are measured. Existing long lines are somebody else's commit.
| exit | signal | meaning |
|---|---|---|
| 0 | TIGER: CLEAN <n> file(s)[, <m> skipped] |
no added line is over its limit, across the <n> files examined; <m> more were staged but not measured |
| 1 | TIGER: BLOCK <n> |
<n> added lines exceed a limit the repo declared |
| 2 | TIGER: NAG <n> |
<n> added lines exceed the built-in 100-column fallback |
| 64 | — | the path is not a git work tree |
Each offence prints as path:line: <width> cols (limit <n>) before the verdict line.
CLEAN 0 files and CLEAN 3 files are both exit 0, and the difference matters: the first means nothing was measured. A caller that reads only the exit code cannot tell "checked, fine" from "checked nothing" — which is how a mis-typed git add reads as a pass.
CLEAN 0 files, 2 skipped is a commit whose every file was prose, data, or declared off — a docs-only commit, or a bump of a lockfile the skip lists below name, reaches it correctly, and it is not a mistake. A bare CLEAN 0 files means no added, copied, modified, renamed or type-changed path was staged at all, which has two causes: an empty index, or a commit that stages only deletions. Deletions have no added lines to measure, so they reach neither counter. Check your git add over the bare form when the commit was meant to change a file.
One run can turn up both kinds at once — a declared-limit offence in one file and a fallback offence in another. Every offence is printed either way, but <n> in the verdict counts only the kind the verdict names. A fallback offence is never counted into a BLOCK and never blocks a commit, whatever else the run found.
Which limit applies
Resolved per file, first match wins:
WHETSTONE_TIGER_COLS— a positive integer. Declared, so it blocks.0, a negative, or a non-number is not a limit: the value is dropped, resolution continues at the next source, and the reason is named on stderr. An operator who setWHETSTONE_TIGER_COLS=0in a repo with an.editorconfiglimit therefore gets that repo's limit — still aBLOCK, at a number they did not choose. The stderr line is what makes that visible..editorconfigmax_line_lengthfor the nearest section matching the file. Declared, so it blocks. A value ofoffis spec-legal and skips the file entirely.
"Nearest" is directory distance: the walk starts beside the file and stops at a root = true. It is not specificity. Where two sections in the same file both match, EditorConfig's own rule applies and the later one wins — [*.py] followed by [*] loses to the [*], which is the opposite of what CSS-shaped intuition predicts.
A later section only wins if its value parses. max_line_length = 0 in the more specific section leaves the broader section's limit standing, which is the same silent-ignore trap as the env var and gets the same treatment: the value is named on stderr rather than dropped.
- Nothing declared → 100 columns, and exceeding it only nags.
The distinction is the point: a repo that has stated its limit gets it enforced, and a repo that has not gets told, never stopped. The env var carries whetstone's own prefix rather than a DOSSIER_ one — this is whetstone's behaviour, and it must be configurable by someone who has never installed the dossier plugin.
Prose and data formats are skipped outright by suffix — .md, .markdown, .rst, .txt, .json, .jsonl, .csv, .tsv, .svg, .lock, .snap — and by exact name: go.sum, yarn.lock, pnpm-lock.yaml, poetry.lock, Cargo.lock. A column limit describes a statement, not a record.
The name list carries only the lockfiles the suffix list misses. pnpm-lock.yaml is generated and .github/workflows/ci.yaml is written by hand, so skipping every .yaml to reach the first is not on offer. Both lists are literal, not a guess at what looks generated: a lockfile named anything else — gradle.lockfile, say — is measured like source, and one of its added lines over a declared limit exits 1.
The manual pass — what the script cannot judge
These are the TIGER_STYLE rules that need a reader. Nothing enforces them. They produce no exit code, and no tool will stop a commit that violates them. Read the diff against them yourself:
- Function length. A function that does not fit on a screen is doing more than one thing. Split at the seam, not at the line count.
- Assert adequacy. A non-trivial function asserts its preconditions. Assert the negative space too — the states that must never occur, not only the ones you expect.
- Loop bounds. Every loop states an upper bound. An unbounded loop is a hang waiting for the input that triggers it.
- Limits as numbers. Every limit is a named constant with a stated unit, never an inline literal. A magic number is a decision nobody can find later.
Treat this list the way skill-smith treats its reference checklists: the exit code is the gate, the checklist is the judgement, and the two are never described as the same thing.
Inside a dossier build run with --review, the dossier-reviewer agent reads the diff against this same list. That does not make it a gate: its findings on these four rules are capped at Warn:, which cannot block a commit, and --review is opt-in — it auto-fires only for destructive-class tasks, a category that has nothing to do with code shape. A second reader is worth having. It is not enforcement.
What is deliberately not here
Four TIGER_STYLE rules are absent on purpose, so their absence does not read as an oversight:
- Assert the negative space, as a testing rule — already carried by the repo's testing standard:
run-slicefails a slice whose test passed on its first run, and every gate is required to ship a positive and a negative test. That is the same rule enforced one level up, at the test rather than at the function. - Zero technical debt — a caught bug becomes a recorded, testable invariant through the dossier plugin's backprop verb.
ds:status --driftruns those checks when asked, and only a high-recurrence class is promoted to a write-time guard; nothing runs them on every commit by itself. DenyingTODO/FIXMEmarkers at write time is a personal coding standard and belongs in an operator's own harness, not shipped to everyone who installs a plugin. - Say why in comments — same reason. Comment policy is one person's taste; a plugin that ships it imposes it.
- Static allocation, no recursion — TIGER_STYLE was written for a database in Zig. These rules do not translate to Python, bash and markdown, and pretending otherwise would be cargo cult.
Not built yet
Named so nobody assumes they work:
- Formatter configs as a limit source. Prettier's
printWidth, black'sline-lengthand rustfmt'smax_widthare not read. OnlyWHETSTONE_TIGER_COLSand.editorconfigare. - Full
.editorconfigglob syntax. Brace expansion,*,**,?,[seq]and[!seq]work. Escapes and numeric ranges ({1..9}) do not, and[^seq]is a literal^— the spec's negation form is[!seq]. Brace expansion is capped at 256 alternatives; past the cap the pattern is left unexpanded and matches nothing, so the file falls back to the advisory limit instead of stalling the commit. - Configurable tab width. A tab advances to the next multiple of 8.
.editorconfig'stab_widthandindent_sizeare not read. - Per-language parsing. Width is display columns — a tab advances to its stop, a CJK or otherwise wide character counts two, a combining mark counts zero. What is missing is an AST, so nothing here can measure a function's length or find a magic number, which is exactly why those rules sit in the manual pass above rather than in the table.
Verification
Done = tiger-check exits 0 or 2 and you have read the diff against the manual pass. Exit 1 is not done: a declared limit was exceeded.
Dossier breadcrumb
In a repo with a live dossier ledger, record a non-clean verdict as one §S line through the dossier plugin's append tooling — tiger=block@<n> or tiger=nag@<n>. A clean run writes nothing; a log that records every uneventful pass buries the events that matter. No dossier → skip, no-op: this skill ships no hooks and needs no dossier to run.
04 同仓库更多 Skills
suite-review
Run the monthly evidence review of this repo's plugin skills. Count each skill's opportunities and uses with the census, read what happened where a skill was not used, put each skill in one bucket, act, and write the actions as predictions with a check date. Use when the operator says "suite review" or "monthly review", or when the suite-review line on LATER.md is due.
tdd-cycle
Drive one vertical slice of behaviour red-green-refactor, seam agreed before the first test. Use when the user asks to add a feature, fix a bug, write code test-first, says "TDD this", "red green refactor", or "write a failing test first". Inside a live dossier:build covenant, invoke dossier:build instead — it composes this skill's run-slice command as its RED/GREEN proof.
prototype
Build a throwaway prototype to answer a design question. Use when the user wants to sanity-check whether a state model or logic feels right, or explore what a UI should look like.
doubt-pass
Adversarial doubt cycle on a plan or design before any code exists — distinct from post-hoc code review. Use when the user asks to doubt this, stress-test a design before building it, poke holes in a plan, find the flaw in an approach, or before committing to a non-trivial architecture or design decision.
diagnose
Diagnose a hard bug or a performance regression with a feedback loop first, then ranked hypotheses, instrumentation and a regression test. Use when the user says diagnose or debug this, or reports something broken, throwing, failing or slow.
verify
Empirical fact-check for freshness-sensitive claims — versions, EOLs, package latest, GitHub Action SHAs, k8s apiVersions, AI model deprecations, image tags, or anything the model's training cutoff may have staled. Primary sources only. Auto-fires as a PreToolUse hook on Edit/Write, but only in a repo with a `.scratchpad/dossier/` directory; `/dossier:verify` on demand works anywhere. Invoke when user says "verify", "fact-check", "is this current", "/dossier:verify", "double check", or when reviewing freshness claims.