@super-repo/czar
v0.2.0
Published
Czar (aka commitizar / caesar) — Conventional Commits parser, formatter, validator, AI suggest, sanity checks, walk-through commits, and Ink components
Downloads
2,543
Maintainers
Readme
@super-repo/czar
Czar (aka commitizar / caesar) — Conventional Commits parser, formatter, validator, AI-suggest, sanity checks, walk-through commits, and Ink components for an interactive builder. The package ships three interchangeable bin names; pick the one your fingers prefer.
Why
When you commit the same change across N stack repos at once, you want one structured message that lands identically everywhere. Free-form text invites drift. This package gives you:
- CLI (
czar) —validate,build,push,walksubcommands. Single binary; works on its own without any other@super-repo/*package. - Data layer (
format,parse,validate) — pure, no UI, no peer deps. - AI suggest — generates a Conventional Commits message from your staged diff via Anthropic or OpenAI; works on the whole staged set or per-file.
- Sanity checks — block commits that include
.env*files, large files, private keys, or strings that match common secret patterns. - Ink components (
CommitForm,CommitPreview) — drop-in for any Ink-based TUI.
Install
pnpm add @super-repo/czar
# Components require ink + react + ink-text-input as peers (already in any Ink TUI).
pnpm add ink react ink-text-inputThe package ships three interchangeable bin names — pick whichever reads best:
| Bin | Style |
| ------------ | -------------------------------------- |
| czar | short, the canonical name |
| commitizar | full mnemonic, similar to commitizen |
| caesar | because the czar historically was |
czar push # ←
commitizar push # all three resolve to the same CLI
caesar push # ←CLI
| Subcommand | What it does |
| -------------- | ------------------------------------------------------------------------------------------------------------- |
| czar validate| Validate a Conventional Commits message from a file or stdin. Drop-in for commit-msg hooks. (Default.) |
| czar build | Launch the Ink form; print the formatted message to stdout on submit. |
| czar push | Build (or accept) a message → stage → run sanity checks → commit → push. Includes shortcuts and AI suggest. |
| czar walk | Iterate every changed file; commit each one individually. Combine with --ai for per-file generation. |
czar validate
czar .git/COMMIT_EDITMSG # validate the prepared message file
echo "feat: tokens" | czar # validate from stdin
czar --strict .git/COMMIT_EDITMSG # treat warnings (long subject etc.) as errorsExit codes: 0 valid, 1 invalid (or warning under --strict).
czar build
git commit -F <(czar build) # build interactively, pipe to git
czar build --body # include body step in the formThe form renders to stderr so stdout stays clean for piping. Exits 130 on Esc.
czar push
Single-repo workflow: banner → status block → message resolution → sanity checks → stage → commit → push.
czar push # full interactive flow
czar push g # quick chore: shortcut → "chore: quick commit"
czar push "rotate session tokens" # description provided, prompt for type/scope
czar push -m "fix(api): 404" # fully non-interactive
czar push --ai # AI-suggest from staged diff, review in form
czar push --no-push -m "..." # commit without pushingThe first screen of the interactive flow is a shortcut chooser:
g quick chore chore: quick commit
w WIP chore: WIP
f formatting style: formatting
d docs touch-up docs: minor updates
…
a AI suggest generate message from staged diff (when key available)
s step (walk) commit each changed file individually
c group commit compose one message for every staged changeCustom shortcuts are configurable via czar.config.ts (see Config).
czar walk
Walk through each changed file in turn — pick a per-file message, optionally letting the AI suggest one and previewing before commit. Useful for breaking up a big WIP working tree into clean per-file commits.
czar walk # interactive: type each message
czar walk --ai # AI suggests per file, you confirm/edit/skip
czar walk --ai --auto # AI-only, no prompts (cleanup runs)
czar walk --no-push # commit per-file but skip push at endPer-file flow (AI mode):
[3/12] packages/cli/src/main.ts (modified)
+ new lines in green
- removed in red
∴ ai refactor(cli): extract argument parser
[y] commit [e] edit [s] skip [q] quit
›q aborts mid-walk; commits already made are kept. After the loop, czar pushes unless --no-push.
You can also reach walk from the czar push first screen by pressing s.
Flags (push / walk)
| Flag | Where | Purpose |
| ------------------- | ------------ | --------------------------------------------------------------------- |
| -m, --message | push | Provide a complete conventional message; skips the form. |
| --ai | push, walk | Generate via AI; requires ANTHROPIC_API_KEY or OPENAI_API_KEY. |
| --auto | walk | Accept every AI suggestion without prompting (only with --ai). |
| --body | push, build | Include a body step in the interactive form. |
| --strict | all | Treat validation warnings as errors. |
| --no-stage | push | Skip git add -A before committing. |
| --no-push | push, walk | Commit only, do not push. |
| --no-sanity | push | Skip pre-commit sanity checks. |
| -c, --config | all | Path to czar.config.{ts,js,mjs,json}. |
Config
Optional czar.config.ts at the project root:
import { defineConfig } from '@super-repo/czar'
export default defineConfig({
body: false, // include the body step by default
strict: false, // treat warnings as errors
// Quick-commit shortcuts shown on the first screen of `czar push`.
shortcuts: [
{ key: 'g', label: 'quick chore', message: 'chore: quick commit' },
{ key: 'w', label: 'WIP', message: 'chore: WIP' },
{ key: 'f', label: 'formatting', message: 'style: formatting' },
],
// Pre-commit safeguards.
sanity: {
enabled: true,
maxFileSizeWarn: 1 * 1024 * 1024,
maxFileSizeError: 5 * 1024 * 1024,
blockEnvFiles: true,
envFileAllowlist: ['.env.example', '.env.template', '.env.sample'],
detectSecrets: true,
blockPrivateKeys: true,
skipPaths: ['pnpm-lock.yaml', 'package-lock.json', 'yarn.lock'],
},
// AI suggest defaults — see "Environment" below.
ai: {
enabled: true,
provider: 'anthropic',
model: 'claude-haiku-4-5-20251001',
apiKeyEnv: 'ANTHROPIC_API_KEY',
logLines: 20,
maxDiffBytes: 16 * 1024,
},
})Override the lookup with -c <path>. Plain .json configs are also supported.
Config discovery
Czar resolves its config in this order:
-c <path>if passed on the command line.czar.config.{ts,mjs,js,cjs,json}in the current working directory.- The closest
package.jsonwalking up from cwd — read itsczarfield.
The package.json fallback accepts three shapes (matching super and rune):
// (a) Single config file
{ "czar": { "config": "./czar.config.ts" } }
// (b) Array of config files — loaded in order, merged left-to-right
{ "czar": { "config": ["./shared/base.config.ts", "./local/overrides.ts"] } }
// (c) Inline config (with optional `extends`, resolved relative to package.json)
{
"czar": {
"extends": "./shared/base.czar.ts",
"shortcuts": [{ "key": "u", "label": "ui touch-up", "message": "style: ui" }]
}
}This makes a single package.json a one-stop config surface for super, czar, and rune blocks side by side.
Extending configs
Any czar config (file or package.json inline) can declare an extends field. Relative paths resolve against the config file's directory (or the package.json's directory for inline blocks). Pass a single path or an array; later overlays earlier.
import { defineConfig } from '@super-repo/czar'
export default defineConfig({
extends: './shared/base.czar.ts',
shortcuts: [
{ key: 'u', label: 'ui touch-up', message: 'style: ui touch-up' },
],
})Merge semantics: shortcuts merge by key (overlay wins per-key, novel keys appended); sanity and ai shallow-merged; scalars (body, strict) last-defined wins. Circular extends chains throw CzarConfigError.
Sanity checks
Run after git add -A, before commit. Errors block; bypass with --no-sanity.
| Check | Trigger | Severity |
| -------------- | --------------------------------------------------------------------------------------------- | -------- |
| env-file | A staged file matches .env* and isn't on the allowlist. | error |
| env-plaintext| In an env file: a KEY=VALUE line where the key looks secret-y (TOKEN/SECRET/PASSWORD/…) and the value is ≥ 12 chars and not an obvious placeholder. | error |
| private-key | Filename matches id_rsa, *.pem, *.key, *.ppk, *.p12, etc. | error |
| large-file | Staged file exceeds maxFileSizeError / maxFileSizeWarn. | error / warning |
| secret | File content matches one of: AWS access/secret key, Anthropic / OpenAI / GitHub / Slack / Google / Stripe key, JWT, PEM block. | error in env files, warning otherwise |
skipPaths excludes lockfiles by default. Binary extensions (images, archives, executables) are not scanned for secret patterns.
Environment
czar push and czar walk automatically load .env and .env.local from cwd at startup, without overriding values already set in the shell. So ANTHROPIC_API_KEY (or OPENAI_API_KEY) defined in your project's .env is enough — no manual source needed.
When the key is detected, the status block shows:
ai mode true · anthropic claude-haiku-4-5-20251001Pass --ai to make AI generate the message by default; without the flag, AI is offered as the a shortcut on the first screen.
Use from package.json (commit-msg hook)
{
"scripts": { "validate-commit": "czar" },
"simple-git-hooks": { "commit-msg": "czar $1" },
"devDependencies": {
"@super-repo/czar": "^0.0.6",
"simple-git-hooks": "^2.11.0"
}
}Husky:
# .husky/commit-msg
npx czar "$1"Lefthook:
commit-msg:
commands:
czar:
run: npx czar {1}Data layer
import {
formatCommitMessage,
parseCommitMessage,
validateCommitMessage,
} from '@super-repo/czar'
formatCommitMessage({ type: 'feat', scope: 'auth', description: 'rotate tokens', breaking: true })
// → "feat(auth)!: rotate tokens"
parseCommitMessage('fix(api): handle 404')
// → { message: { type: 'fix', scope: 'api', description: 'handle 404' }, errors: [] }
validateCommitMessage({ type: 'feat', description: 'a long description'.repeat(20) })
// → { valid: false, issues: [{ severity: 'error', field: 'description', ... }] }Components
<CommitForm />
Multi-step builder: shortcuts → type → scope → description → breaking → review.
import { render } from 'ink'
import { CommitForm } from '@super-repo/czar'
render(
<CommitForm
aiAvailable
walkAvailable
onAiRequest={() => /* unmount + run AI suggest */}
onWalkRequest={() => /* unmount + run walk */}
onSubmit={(msg) => console.log(formatCommitMessage(msg))}
onCancel={() => process.exit(0)}
/>
)| Prop | Type | Default | Purpose |
| ---------------- | --------------------------------- | ------- | ------------------------------------------------------ |
| initial | Partial<CommitMessage> | {} | Pre-fill any field. With type + description set, jumps straight to review. |
| showBodyStep | boolean | false | Add a single-line body step before review. |
| shortcuts | ShortcutConfig[] | builtin | Custom quick-commit shortcuts (key/label/message). |
| aiAvailable | boolean | false | Render the a AI shortcut on the first screen. |
| walkAvailable | boolean | false | Render the s walk shortcut on the first screen. |
| onAiRequest | () => void | — | Called when the user picks a. |
| onWalkRequest | () => void | — | Called when the user picks s. |
| onSubmit | (message: CommitMessage) => void| — | Called with the assembled message on confirm. |
| onCancel | () => void | — | Called when the user presses Esc. |
<CommitPreview />
Renders a CommitMessage with live validation issues underneath.
<CommitPreview message={{ type: 'feat', description: 'add login' }} />Validation rules
| Rule | Severity |
| ----------------------------------------------------------------------------------------------------- | -------- |
| Type must be one of: feat/fix/docs/style/refactor/perf/test/chore/build/ci/revert. | error |
| Description required and non-empty. | error |
| Description ≤ 100 characters. | error |
| Description ≤ 72 characters (recommended). | warning |
| Description should not end with a period. | warning |
| Description should not have leading/trailing whitespace. | warning |
| Scope, when provided, must be non-empty and free of ( ) ! : and whitespace. | error |
Format reference
<type>[(<scope>)][!]: <description>
[<body>]
[<footer-key>: <footer-value>]
[<footer-key>: <footer-value>]- The
!after type/scope marks a breaking change. Equivalent to aBREAKING CHANGE: …footer. - Body and footers are separated by blank lines.
- Footer keys match
[A-Za-z0-9-]+(or the literalBREAKING CHANGE/BREAKING-CHANGE).
See conventionalcommits.org for the full spec.
