3c6f53406b Fix: stop the direction page hanging forever after a re-roll (#469) (#530)
* Fix: stop the direction page hanging forever after a re-roll (#469)

The re-roll leg of the decision-page protocol was documented only in
serve-question.mjs's own header, so agents never ran --update and the
open tab polled a round that could never arrive. Compounding failure
modes: the page poll swallowed every error, the daemon's --timeout was
an absolute guillotine that killed the server under a still-open tab,
a choice posted to a dead server confirmed nothing, and refresh or
Reload on an unresolved round resurrected heartbeats that held the
daemon alive indefinitely.

- new-work.md documents the re-roll leg: rerun concept-seed with
  --from/--reroll, deliver with --update on the same key, never --start
  a second server.
- The page poll terminates and says why: eight consecutive fetch
  failures means the server is gone; the delivery deadline (the
  server's own --idle-grace, inlined into the page) passing means the
  hand never arrived. Both stop heartbeating.
- The daemon's --timeout bounds only the wait for a page to open; once
  the page heartbeats, the server lives while the page does and exits
  after --idle-grace (default 600s) without a beat, including under
  --timeout 0.
- Build this and Re-roll against a dead server fail loudly instead of
  silently swallowing the click.
- The server tracks the window between a collected re-roll answer and
  the --update that replaces the round, and serves the page in waiting
  mode there, so a native refresh re-enters the same bounded wait
  instead of resurrecting dead cards; the in-page Reload button only
  revives a delivered hand.
- --update is exempt from the headless gate and its liveness probe
  trusts a fresh heartbeat over a failed kill probe (sandbox EPERM is
  not death).

Squash of the six review-round commits on this branch, rebased onto
main after the decision-page revamp.

AI assistance: prepared with an AI agent operating under maintainer
instruction (abdulwahabone).

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix review findings: persist the replacement deadline, refuse unloadable hands

A browser-native refresh of the waiting page re-entered the bounded wait
with a fresh delivery deadline and an immediate heartbeat, so refreshing
before each deadline expired could hold the daemon alive and keep --wait
on WAITING indefinitely. The server now records when the re-roll or
followup answer was collected, each served waiting page inherits only
what remains of that one allowance, and a page served after the deadline
renders stalled immediately and never starts its heartbeat.

And a next hand the round could not load used to reload-loop the tab:
GET /'s catch kept the file on disk, so /next-status stayed ready:true
forever. --update now refuses a payload without a non-empty options
array at the sender, and GET / discards an unloadable next file so the
bounded wait resumes.

AI-assisted (Cursor agent) under maintainer instruction.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix review finding: a stalled page recovers a late hand without a click

The stall silenced heartbeats so the idle grace could reclaim the
daemon, but that silence read as a closed tab: after a late --update,
--wait saw the stale beat and reported PAGE CLOSED while the user sat
on the Reload screen, so the agent abandoned the browser path the
recovery UI exists for. The stall screen now keeps a beat-free
/next-status watch that reloads into a delivered hand on its own
(GET never beats, so an abandoned flow is still reclaimed), and --wait
no longer concludes closure from a stale beat while an undelivered
next hand sits on disk.

AI-assisted (Cursor agent) under maintainer instruction.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix review finding: a delivered hand must not mask a closed page

The mid-delivery suppression keyed on the next file existing, but a
closed tab never claims that file, so an unconsumed delivery held
--wait on WAITING indefinitely instead of reporting the closed flow.
The suppression is now age-bound: a stalled page's watch reclaims a
delivered hand within seconds, so a file still unclaimed after a 10s
grace means no page is coming back and the stale beat reads as the
closed page it is.

AI-assisted (Cursor agent) under maintainer instruction.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix review finding: stamp the delivery clock at --update, not the copy

--wait's mid-delivery grace reads the next file's mtime, but
copyFileSync's timestamp behavior is the platform's business: a copy
that preserves the source payload's older mtime would start the grace
already spent and report PAGE CLOSED under a live stalled tab. --update
now touches the delivered file itself, so delivery time is delivery
time everywhere.

AI-assisted (Cursor agent) under maintainer instruction.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix review findings: disable canon during the wait, validate --timeout

The waiting and stall screens disabled only the re-roll buttons; the
footer canon action stayed clickable, and a canon pick posted after
--wait had consumed the re-roll could never be collected: it overwrote
the answer, marked the table closed, and exited the daemon under the
agent. Both disable sites now take the canon exit down with the re-roll
buttons; a delivered hand reloads the page and serves it live again.

And --timeout reached the lifetime timer unvalidated: NaN or a negative
value disarmed the no-page exit and the daemon leaked. It now takes the
default unless the value is a finite non-negative number, keeping 0 as
the explicit wait-forever.

AI-assisted (Cursor agent) under maintainer instruction.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix review finding: a second click must not renew the delivery deadline

dealAgain left the re-roll and canon controls live through the answer
POST and the 700ms fly-out, so a second click posted another re-roll
and the server restamped awaitingNextSince, renewing the deadline this
PR made non-renewable on refresh and on the stall screen. The controls
now go quiet at the click itself, in dealAgain and in answer(), and the
server stamps the allowance only on the transition into the wait, so a
duplicate answer racing the disable keeps the first stamp.

Regression coverage on both sides: the unit deadline test posts a
duplicate re-roll mid-allowance and asserts the budget shrank instead
of resetting, and the e2e stall test asserts both controls are disabled
immediately after the click, before the fly-out.

AI-assisted (Cursor agent) under maintainer instruction.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix review finding: a late delivery must survive its claim window

--update could land a replacement hand after the stalled page went
silent but moments before the daemon's idle deadline: the daemon exited
before the page's 1.5s watch could claim the hand, orphaning a delivery
--update had confirmed, and the next --wait reported a server failure.
The idle exit now defers while an unclaimed next hand is younger than
the claim grace --wait already reads (extracted as one shared
constant), so the page's watch deals it and heartbeats resume; a file
unclaimed past the grace still ends the daemon, bounded as before.

Regression test: deliver at idle-deadline-minus-a-beat, assert the
daemon survives past the deadline and serves the late hand.

AI-assisted (Cursor agent) under maintainer instruction.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix review finding: the claim itself must hold the daemon

The idle-exit hold read only the next file's freshness, but GET /
deletes that file when it serves the claimed round, before the
reloading page can post its first heartbeat: a lifetime tick in that
gap saw no pending hand and a stale beat, and exited under the hand
just claimed. GET / now stamps the claim when it consumes a pending
hand, and the idle exit honors the same bounded grace from that stamp,
so the reloading page gets its seconds to beat while an abandoned claim
still ends the daemon at the grace.

The claim-window regression test now also fetches after the claim, past
another lifetime tick, and asserts the daemon survived the gap;
verified it fails on the previous commit.

AI-assisted (Cursor agent) under maintainer instruction.

Co-authored-by: Cursor <cursoragent@cursor.com>

* Fix review finding: --wait must ride out the claim gap too

The claim deletes the next file --wait's mid-delivery grace watches,
and the reloading page has not beat yet, so --wait in that gap read the
stale beat as PAGE CLOSED while the daemon was alive serving the dealt
round, and the agent abandoned a browser session that had just
recovered. GET / now persists the claim stamp into the per-key state
file, and --wait's suppression honors it under the same bounded grace:
a fresh claim stays WAITING, a claim nobody followed with a beat still
reads as the closed page it is.

Regression test drives --wait through the gap (claim with a stale beat:
WAITING, not exit 4) and past it (backdated claim stamp: exit 4);
verified it fails on the previous commit.

AI-assisted (Cursor agent) under maintainer instruction.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Paul Bakaus <paul.bakaus@gmail.com>
2026-08-16 14:26:02 -07:00
2026-08-16 21:25:33 +00:00
2026-08-14 08:58:23 -04:00
2026-08-16 21:25:33 +00:00
2026-08-16 21:25:33 +00:00
2026-08-16 21:25:33 +00:00
2026-08-16 21:25:33 +00:00
2026-07-29 14:28:14 -07:00

Impeccable

Design guidance for AI coding agents. 1 skill, 23 commands, live browser iteration, and 59 deterministic detector rules for AI-generated frontend design.

Quick start: From your project root, run npx impeccable install, then run /impeccable init inside your AI coding tool. Full docs: impeccable.style.

Why Impeccable?

Anthropic's frontend-design was the first widely-used design skill for Claude. Impeccable started from there.

Every model trained on the same SaaS templates. Skip the guidance and you get the same handful of tells on every project: Inter for everything, purple-to-blue gradients, cards nested in cards, gray text on colored backgrounds, the rounded-square icon tile above every heading.

Impeccable adds:

  • One setup flow. /impeccable init writes PRODUCT.md and offers DESIGN.md, so later commands know the audience, brand/product lane, voice, anti-references, colors, type, and components.
  • 23 commands. A shared design vocabulary with your AI: polish, audit, critique, distill, animate, bolder, quieter, and more.
  • 59 deterministic detector rules plus LLM-only critique checks. The CLI and browser extension run the deterministic rules with no LLM and no API key.

What's Included

The Skill: impeccable

The skill installs as one command:

/impeccable <command> <target>

Start every new project with:

/impeccable init

init asks whether the surface is brand (marketing, landing, portfolio) or product (app UI, dashboard, tool), then writes design context that every later command reads.

23 Commands

All commands are accessed through /impeccable:

Command What it does
/impeccable craft Full shape-then-build flow with visual iteration
/impeccable init One-time setup: gather design context, write PRODUCT.md and DESIGN.md, configure live mode, recommend next steps
/impeccable document Generate root DESIGN.md from existing project code
/impeccable extract Pull reusable components and tokens into the design system
/impeccable shape Plan UX/UI before writing code
/impeccable critique UX design review: hierarchy, clarity, emotional resonance
/impeccable audit Run technical quality checks (a11y, performance, responsive)
/impeccable polish Final pass, design system alignment, and shipping readiness
/impeccable bolder Amplify boring designs
/impeccable quieter Tone down overly bold designs
/impeccable distill Strip to essence
/impeccable harden Error handling, i18n, text overflow, edge cases
/impeccable onboard First-run flows, empty states, activation paths
/impeccable animate Add purposeful motion
/impeccable colorize Introduce strategic color
/impeccable typeset Fix font choices, hierarchy, sizing
/impeccable layout Fix layout, spacing, visual rhythm
/impeccable delight Add moments of joy
/impeccable overdrive Add technically extraordinary effects
/impeccable clarify Improve unclear UX copy
/impeccable adapt Adapt for different devices
/impeccable optimize Performance improvements
/impeccable live Visual variant mode: iterate on elements in the browser

Use /impeccable pin <command> to create standalone shortcuts (e.g., pin audit creates /audit).

Usage Examples

/impeccable audit blog           # Audit blog hub + post pages
/impeccable critique landing     # UX design review
/impeccable polish settings      # Final pass before shipping
/impeccable harden checkout      # Add error handling + edge cases

Or use /impeccable directly with a description:

/impeccable redo this hero section

Anti-Patterns

The skill includes explicit guidance on what to avoid:

  • Don't use overused fonts (Arial, Inter, system defaults)
  • Don't use gray text on colored backgrounds
  • Don't use pure black/gray (always tint)
  • Don't wrap everything in cards or nest cards inside cards
  • Don't use bounce/elastic easing (feels dated)

See It In Action

Visit the Neo Mirai case study to see a before/after case study of a real project transformed with Impeccable commands.

Installation

From the root of your project, run:

npx impeccable install

This shows the harness folders it detected (for example ~/.claude, ~/.codex, ~/.grok, or project-local .cursor), lets you keep the detected set or customize providers, then asks whether to install into the current project or globally. Use --providers=claude,codex,cursor,grok and --scope=project|global to skip those choices in scripts. On Claude Code, Cursor, Codex, GitHub Copilot, and Grok Build, it also installs the provider-native hook manifest for the current project. Works with Cursor, Claude Code, Gemini CLI, Codex CLI, Grok Build, and every other supported tool. Reload your harness afterward.

To refresh an existing install, run:

npx impeccable update

Codex users should open /hooks after install or update and approve the project hook when prompted. Codex tracks trust by hook definition, so updates that change .codex/hooks.json can require approval again. Grok Build users need project folder trust (/hooks-trust or launch with --trust) before .grok/hooks/ scripts run.

Option 2: Git Submodule

For teams that want to keep Impeccable vendored and updated through Git, add this repo as a submodule and link the compiled provider build into your harness folders:

git submodule add https://github.com/pbakaus/impeccable .impeccable
npx impeccable link --source=.impeccable --providers=claude,cursor
git add .gitmodules .impeccable .claude .cursor
git commit -m "Add Impeccable skills"

Use the providers your project needs, for example claude, cursor, gemini, codex, github, grok, opencode, pi, qoder, trae, trae-cn, rovo-dev, or vibe. The command links individual skill folders from .impeccable/dist/universal/ and leaves existing real skill directories untouched unless you pass --force.

To update later:

git submodule update --remote .impeccable
npx impeccable link --source=.impeccable --providers=claude,cursor

Option 3: Plugin install

Claude Code:

/plugin marketplace add pbakaus/impeccable

Claude Code only. After adding the marketplace, open /plugin and install Impeccable from the list.

Grok Build:

grok plugin install pbakaus/impeccable#plugin --trust

Grok Build only. The #plugin suffix installs the slim plugin package (skills, agents, and hooks) instead of the full monorepo. Then run /impeccable init in a Grok session. Project-scoped installs via npx impeccable install --providers=grok also work and write .grok/skills/ plus .grok/hooks/impeccable.json.

Option 4: Download from Website

Visit impeccable.style, download the ZIP for your tool, and extract to your project.

Option 5: Copy from Repository

Cursor:

cp -r dist/cursor/.cursor your-project/

Note: Cursor skills require setup:

  1. Switch to Nightly channel in Cursor Settings → Beta
  2. Enable Agent Skills in Cursor Settings → Rules

Learn more about Cursor skills

Claude Code:

# Project-specific
cp -r dist/claude-code/.claude your-project/

# Or global (applies to all projects)
cp -r dist/claude-code/.claude/* ~/.claude/

OpenCode:

cp -r dist/opencode/.opencode your-project/

Pi:

cp -r dist/pi/.pi your-project/

Gemini CLI:

cp -r dist/gemini/.gemini your-project/

Note: Gemini CLI skills require setup:

  1. Install preview version: npm i -g @google/gemini-cli@preview
  2. Run /settings and enable "Skills"
  3. Run /skills list to verify installation

Learn more about Gemini CLI skills

Codex CLI:

# Project-local
cp -r dist/agents/.agents your-project/
mkdir -p your-project/.codex
cp dist/codex/.codex/hooks.json your-project/.codex/hooks.json

# Or install the skill user-wide. Copy .codex/hooks.json into each project
# where you want the design hook to run.
mkdir -p ~/.agents/skills
cp -r dist/agents/.agents/skills/* ~/.agents/skills/

The asset-producer subagent ships nested inside the skill's own agents/ folder, which Codex auto-discovers. No separate .codex/agents/ copy is needed. The hook is project-local because Codex discovers hooks from .codex/hooks.json next to trusted project config.

GitHub Copilot:

cp -r dist/github/.github your-project/

Trae:

# Trae China (domestic version)
cp -r dist/trae/.trae-cn/skills/* ~/.trae-cn/skills/

# Trae International
cp -r dist/trae/.trae/skills/* ~/.trae/skills/

Note: Trae has two versions with different config directories:

  • Trae China: ~/.trae-cn/skills/
  • Trae International: ~/.trae/skills/

After copying, restart Trae IDE to activate the skills.

Rovo Dev:

# Project-specific
cp -r dist/rovo-dev/.rovodev your-project/

# Or global (applies to all projects)
cp -r dist/rovo-dev/.rovodev/skills/* ~/.rovodev/skills/

Qoder:

# Project-specific
cp -r dist/qoder/.qoder your-project/

# Or global (applies to all projects)
cp -r dist/qoder/.qoder/skills/* ~/.qoder/skills/

Mistral Vibe:

# Project-specific
cp -r dist/vibe/.vibe your-project/

# Or global (applies to all projects)
cp -r dist/vibe/.vibe/skills/* ~/.vibe/skills/

Grok Build:

# Project-specific
cp -r dist/grok/.grok your-project/

# Or global (applies to all projects)
cp -r dist/grok/.grok/skills/* ~/.grok/skills/

Prefer npx impeccable install --providers=grok or grok plugin install pbakaus/impeccable#plugin --trust so the design hook installs too. Project hooks need /hooks-trust (or --trust) once per folder.

Google Antigravity:

# Project-specific
cp -r dist/antigravity/.agent your-project/

# Or global (applies to all projects)
mkdir -p ~/.gemini/config/skills
cp -r dist/antigravity/.agent/skills/* ~/.gemini/config/skills/

Usage

Once installed, every command runs through the single /impeccable skill:

/impeccable audit        # Find issues
/impeccable polish       # Final cleanup
/impeccable distill      # Remove complexity
/impeccable critique     # Full design review

Type /impeccable alone to see the full command list.

Most commands accept an optional argument to focus on a specific area:

/impeccable audit the header
/impeccable polish the checkout form

If you reach for one command often, pin it with /impeccable pin audit to get /audit as a standalone shortcut.

Note: Codex uses skills here, not /prompts: commands. Open /skills or type $impeccable. Repo-local installs live in .agents/skills/; user-wide installs live in ~/.agents/skills/. GitHub Copilot uses .github/skills/. Restart the tool if a newly installed skill does not appear.

Keeping .impeccable out of git

As you run commands, Impeccable writes working files under .impeccable/: critique and polish screenshots, live-mode session and preview state, runtime caches, and per-developer config. Most of it is ephemeral and should not be committed, while a few files are shared project artifacts that belong in the repo. Add this block to your project's .gitignore:

# impeccable-ignore-start
# Ephemeral output, runtime state, and per-dev overrides.
# Unanchored: .impeccable may sit at the repo root or under a nested
# workspace (apps/web/.impeccable/...); anchored patterns would miss it.
# Shared artifacts stay tracked: config.json, live/config.json,
# design.json, critique/*.md.
.impeccable/config.local.json
.impeccable/hook.cache.json
.impeccable/hook.pending.json
.impeccable/*.png
.impeccable/live/server.json
.impeccable/live/sessions/
.impeccable/live/previews/
.impeccable/live/annotations/
.impeccable/live/cache/
.impeccable/live/manual-edit-apply-transaction.json
.impeccable/live/manual-edit-events.jsonl
.impeccable/live/manual-edit-evidence/
.impeccable/live/pending-manual-edits.json
.impeccable/live/deferred-svelte-component-accepts.json
.impeccable/live/*.png
# impeccable-ignore-end

The block is wrapped in # impeccable-ignore-start / # impeccable-ignore-end markers so you can recognize and refresh it later. Patterns are unanchored on purpose: in a monorepo the active project (and its .impeccable/ directory) often lives under a nested workspace path like apps/web/, and a root-anchored pattern would miss it.

Keep these tracked (they are shared project artifacts, do not add them to .gitignore):

  • .impeccable/config.json (unified shared config)
  • .impeccable/live/config.json (live-mode framework wiring)
  • .impeccable/design.json (shared design spec)
  • .impeccable/critique/*.md (review reports)

If an ephemeral file (a screenshot, config.local.json) was committed before you added the block, .gitignore will not untrack it automatically. Run git rm --cached <path> to stop tracking it without deleting your local copy.

Design hook

On Claude Code, GitHub Copilot, Codex, Cursor, and Grok Build, npx impeccable install and npx impeccable update install a provider-native hook manifest along with the skill payload. The hook runs the Impeccable design detector on direct UI file edits and surfaces findings back into the agent flow. Claude Code, GitHub Copilot, Codex, and Grok Build surface findings after the edit (and run a deeper pass on Stop where supported). Cursor blocks bad proposed writes before they land.

Installed hook surfaces:

  • Claude Code: .claude/settings.local.json (gitignored, machine-local) runs ${CLAUDE_PROJECT_DIR}/.claude/skills/impeccable/scripts/hook.mjs. A hook moved into the shared settings.json is honored in place.
  • GitHub Copilot: .github/hooks/impeccable.json (committed, shared by the Copilot CLI and the cloud agent) runs .github/skills/impeccable/scripts/hook.mjs. The Copilot CLI activates it once the file is on the repository's default branch and the folder is trusted.
  • Cursor: .cursor/hooks.json runs .cursor/skills/impeccable/scripts/hook-before-edit.mjs.
  • Codex: .codex/hooks.json runs .agents/skills/impeccable/scripts/hook.mjs.

The installer preserves unrelated hook entries and settings. If a hook manifest is malformed, install/update aborts by default; rerun with --force to back up the malformed file as .bak and replace it.

On an interactive install/update, Impeccable explains the hook and offers to install it (default yes). Your choice is remembered per-developer in the gitignored .impeccable/config.local.json, so you are not asked again; --no-hooks skips it for that run without recording anything. Hook lifecycle settings live under the hook key of .impeccable/config.json; detector ignores live under detector, shared by /impeccable hooks and npx impeccable detect.

For debugging, set hook.auditLog in .impeccable/config.json to a path (or the legacy IMPECCABLE_HOOK_LOG env var) to write one NDJSON line per hook invocation. Leave it unset for normal use.

Build path: comp-first or code-first

When a new surface gets designed, Impeccable either generates a full-fidelity comp first and builds to match it, or builds straight in code with the ambition written into the direction contract and checked at the finish. Comp-first composes bolder and takes longer; code-first is leaner and faster. /impeccable init asks once and records the answer as buildPath in .impeccable/config.json:

{ "buildPath": "comp" }

The values are comp and code, and nothing else is read. Set it in the gitignored .impeccable/config.local.json to override the team's committed value on one machine, which is what you want when your harness has no image generation. In a monorepo, commit it once at the repo root and any workspace that wants something else sets its own. The choice appears at all only where image generation is available, since without it there is nothing to comp.

You do not have to re-run init to set it on a project that predates the setting, and you do not have to edit the file by hand either. Whatever is recorded is a default rather than a lock: every decision page carries a footer toggle, and flipping it binds that session only. Flip it on a project that has recorded nothing and Impeccable asks once, after the round, whether to keep it, then writes your answer. That is the whole migration path for an existing project: use the toggle when the default is wrong, and answer the question that follows.

Codex requires one platform step that Impeccable cannot safely skip: open /hooks after install or update and approve the project hook. There is no Codex marketplace/plugin install flow for this hook.

Full hook docs: impeccable.style/docs/hooks.

Manual copy commands are fallback/debug instructions. The normal path is:

npx impeccable install
npx impeccable update

CLI

Impeccable includes a standalone CLI for detecting anti-patterns without an AI harness:

npx impeccable detect src/                   # scan a directory
npx impeccable detect index.html             # scan an HTML file
npx impeccable detect https://example.com    # scan a URL (Puppeteer)
npx impeccable detect --json .               # CI-friendly JSON output
npx impeccable detect --no-config src/       # raw scan, ignoring project config/context
npx impeccable ignores list                  # show detector ignores
npx impeccable ignores add-file "src/legacy/**"
npx impeccable ignores add-value overused-font Inter --reason "Brand font"

The detector catches 59 deterministic issues across AI slop (side-tab borders, purple gradients, bounce easing, dark glows) and general design quality (line length, cramped padding, small touch targets, skipped headings, and more).

By default, detect respects the same .impeccable/config.json and .impeccable/config.local.json detector config as the design hook: detector.ignoreRules, detector.ignoreFiles, detector.ignoreValues, and detector.designSystem.enabled. Hook lifecycle settings such as hook.enabled only affect automatic hook execution.

For a waiver that should travel with one file instead of the repo config, add an inline comment in the file: <!-- impeccable-disable overused-font: exported brand doc -->. The marker works in any comment syntax, scopes to the whole file (or one line with impeccable-disable-line / impeccable-disable-next-line), and is bypassed by --no-inline-ignores or --no-config.

Full detector docs: impeccable.style/docs/detector.

Supported Tools

Community & Ecosystem

Join the community and ecosystem conversations:

  • GitHub Discussions: file bugs, request features, and help newcomers.
  • Impeccable on npm: grab the CLI, follow releases, and star the package.
  • Follow @pbakaus on Twitter for release notes, sample lint reports, and video highlights of new rules.

Contributing

See DEVELOP.md for contributor guidelines and build instructions.

License

Apache 2.0. See LICENSE.


Created by Paul Bakaus

S
Description
No description provided
Readme
378 MiB
Languages
JavaScript 75.7%
Rust 22.1%
Shell 0.9%
Batchfile 0.9%
CSS 0.2%
Other 0.1%