@zenginui/cli 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,184 @@
1
+ # @zenginui/cli
2
+
3
+ The `zengin` command. The gate that actually protects the codebase: run it pre-commit and on every pull request. Same engine, same violations, same fixes as the MCP server and the hook.
4
+
5
+ ```
6
+ zengin check [paths...] check files (default: everything in scope)
7
+ zengin explain [rule] what each rule checks
8
+ zengin init write a zengin.config.yaml in the current directory
9
+ zengin create <dir> a new project that owns its components, with the engine, MCP, hook and Storybook wired
10
+ zengin add <items...> components or templates from the registry into this project
11
+ zengin theme [name] list the registry's themes, or swap this project's brand for one
12
+ zengin brand --name <name> a brand from a name, a logo or a color: tokens, favicon, wordmark, index.html
13
+ zengin tokens zengin/tokens*.json to src/styles/generated/tokens.css
14
+ zengin figma export|import|connect|plugin tokens to Figma variables and back, Code Connect, the plugin
15
+ zengin mock <presets...> typed, seeded mock data modules into src/mock
16
+ zengin registry build the registry, from a Zengin repository checkout
17
+ zengin report, rollup drift and adoption, per repository and across them
18
+ ```
19
+
20
+ ## check
21
+
22
+ ```bash
23
+ zengin check # whole scope, pretty output
24
+ zengin check src/features/review # a directory
25
+ zengin check --changed origin/main # only files changed since a ref, plus uncommitted and untracked
26
+ zengin check --staged # only staged files (pre-commit)
27
+ zengin check --format github # GitHub Actions annotations on the PR
28
+ zengin check --format json > report.json # the full violation shape, for the rollup
29
+ zengin check --rule color-literal --severity error
30
+ zengin check --fail-on never # report, never fail
31
+ ```
32
+
33
+ | Option | Values | Default |
34
+ | --- | --- | --- |
35
+ | `--config <path>` | | nearest `zengin.config.yaml` above cwd |
36
+ | `--changed [ref]` | git ref | `HEAD` when given without a value |
37
+ | `--staged` | | off |
38
+ | `--rule <id>` | repeatable | all rules |
39
+ | `--severity` | `error`, `warn`, `info` | all |
40
+ | `--fail-on` | `error`, `warn`, `info`, `never` | `error` |
41
+ | `--format` | `pretty`, `json`, `github` | `pretty` |
42
+ | `--max <n>` | | `200` |
43
+
44
+ Exit codes: `0` clean or below `--fail-on`, `1` violations at or above `--fail-on`, `2` usage or configuration error.
45
+
46
+ Stylesheets outside the selected files still resolve class names for the files inside it, so `zengin check src/one/file.tsx` sees what `className="btn"` does even though the CSS file was not selected.
47
+
48
+ ## Pre-commit
49
+
50
+ With [husky](https://typicode.github.io/husky/) or any pre-commit runner:
51
+
52
+ ```bash
53
+ zengin check --staged
54
+ ```
55
+
56
+ ## GitHub Actions
57
+
58
+ Until the packages are published, build them from source in the workflow. With published packages this collapses to one `npx zenginui check` step.
59
+
60
+ ```yaml
61
+ name: design-system
62
+ on: [pull_request]
63
+ jobs:
64
+ zengin:
65
+ runs-on: ubuntu-latest
66
+ steps:
67
+ - uses: actions/checkout@v4
68
+ with: { fetch-depth: 0 } # --changed needs the base ref
69
+ - uses: pnpm/action-setup@v4
70
+ - uses: actions/setup-node@v4
71
+ with: { node-version: 22, cache: pnpm }
72
+ - run: pnpm install --frozen-lockfile
73
+ - run: pnpm build
74
+ - run: pnpm exec zengin check --changed origin/${{ github.base_ref }} --format github
75
+ ```
76
+
77
+ `--format github` emits workflow commands, so each violation appears as an inline annotation on the changed line with the fix in the message. The job fails on `error` severity unless `--fail-on` says otherwise.
78
+
79
+ ## create and add
80
+
81
+ ```bash
82
+ zengin create acme --template marketing # blank | marketing | review | saas | chat | auth | docs | storefront
83
+ zengin create acme --template saas --framework next # the App Router under src/app; the template mounted client-side from page.tsx
84
+ zengin create acme --no-storybook
85
+ zengin create acme --registry ./r --local ../zengin # a local registry, packages linked from a checkout
86
+ cd acme && zengin add dialog tooltip # more items; --force overwrites files that exist
87
+ zengin tokens # after editing zengin/tokens*.json (dev and build run it)
88
+ ```
89
+
90
+ `create` writes the project, copies the template's components in with the owned pragma, merges their manifest entries into `zengin/components.json`, builds `tokens.css`, and runs the engine on the result before it prints. `add` does the same for further items and records any npm packages they need in `package.json`. See [@zenginui/registry](../registry) for the layout and the registry format.
91
+
92
+ ## upgrade
93
+
94
+ ```bash
95
+ zengin upgrade # a report: what the registry changed since each owned file was copied, and what you changed
96
+ zengin upgrade --write # take every upstream change the project did not touch; move the pinned version
97
+ zengin upgrade button card # only these items
98
+ zengin upgrade --write --force # take upstream over a conflict too
99
+ ```
100
+
101
+ Every owned file carries the hash of what was copied in its pragma (`/* zengin-owned Button, forked from @zenginui/ui@0.1.0, sha 3f9a1c0b2d4e */`). Comparing that hash with the file now says whether you edited it; comparing it with the registry says whether the system moved. Four answers per file: `current`, `upstream` (taken with `--write`), `local` (yours, left alone), `conflict` (both moved: the report shows the diff, `--force` takes upstream). Files copied before hashes existed show as `unknown` when they differ. When nothing is left behind, `zengin.config.yaml` is moved to the registry's version.
102
+
103
+ ## theme and brand
104
+
105
+ ```bash
106
+ zengin theme # list: brutal, default, meadow, plex, spec-sheet, zengin
107
+ zengin theme plex # swap the brand file and the fonts link; nothing else changes
108
+ zengin fonts # list: archivo, brutal, dm, fraunces, geist, inter, manrope, playfair, plex, space
109
+ zengin fonts fraunces # the three font tokens and the fonts link; palette untouched
110
+ zengin fonts geist --self-host # woff2 files into public/fonts, @font-face in src/theme/fonts.css
111
+ zengin brand --name Acme --fonts plex # a pairing instead of --font-display/--font-sans/--font-mono
112
+ zengin icons # list: lucide, tabler, phosphor, heroicons, feather, radix, material, bootstrap
113
+ zengin icons tabler # every <Icon.Name /> draws from Tabler; direct icon-package imports become violations
114
+ zengin create acme --theme spec-sheet # or at creation
115
+
116
+ zengin brand --name "Acme Reviews" --logo logo.svg --font-display Archivo --font-sans Inter --radius round
117
+ zengin brand --name Nova --primary "#7C3AED" # from a color alone
118
+ ```
119
+
120
+ `theme` replaces `src/theme/brand.css` with the theme's file and puts its Google Fonts link in `index.html` (one link, marked `data-zengin="fonts"`, replaced by the next theme). `brand` derives a whole palette from one color, in OKLCH so steps look even, and pushes every pairing the components rely on (text on surface, on-primary on primary, soft-foreground on soft, and so on) until it meets WCAG AA in both schemes. It writes the brand file, `zengin/brand.json` (the inputs, for re-running), the logo into `public/`, a favicon when there is no SVG logo, `src/brand.ts` and a `BrandMark` component, and patches the title, theme-color, icon and fonts in `index.html`. An SVG logo also supplies the primary; a PNG needs `--primary`. Both commands run the engine afterwards; the project stays clean because the brand file is a foundation.
121
+
122
+ ## mock
123
+
124
+ ```bash
125
+ zengin mock customers invoices # presets: users, customers, companies, products, orders, invoices, events, messages, metrics
126
+ zengin mock users --count 50 --seed 3
127
+ zengin mock --schema mock.json # your own entities; see @zenginui/mock for the field kinds
128
+ ```
129
+
130
+ Writes `src/mock/rng.ts` (a seeded generator and the pools, no dependency) and one typed module per entity exporting the type, a factory and the array. The same seed gives the same data on every run, so screenshots and previews do not drift. See [@zenginui/mock](../mock).
131
+
132
+ ## figma
133
+
134
+ ```bash
135
+ zengin figma export # zengin/tokens*.json -> figma/variables.json (the Variables payload)
136
+ zengin figma plugin # a plugin into figma/plugin/ that imports that payload into any file
137
+ zengin figma import figma/local.json # what the plugin exported -> a report of what changed in Figma
138
+ zengin figma import figma/local.json --write # and update the token files; then zengin tokens
139
+ zengin figma connect --map figma/map.json # Code Connect files from zengin/components.json
140
+ ```
141
+
142
+ One naming rule carries both directions: `color.primary.soft` is `color/primary/soft` in Figma with `var(--color-primary-soft)` as its code syntax. See [@zenginui/figma](../figma).
143
+
144
+ ## report and rollup
145
+
146
+ ```bash
147
+ zengin report --out zengin-report.json # in each consuming repository, in CI
148
+ zengin rollup reports/*.json --previous last.json # across repositories; markdown by default
149
+ zengin rollup reports/*.json --format html --out rollup.html
150
+ ```
151
+
152
+ `report` is the check plus an inventory: component usage (adoption), suppressions with and without reasons, owned forks with their versions, uncontracted components, and the pinned system version. `rollup` ranks repositories by drift, shows violations per 100 files so sizes compare, flags who is behind the latest version, computes deltas against a previous rollup, and lists what needs attention in priority order. See [`@zenginui/rollup`](../rollup) for the workflow recipe.
153
+
154
+ ## explain
155
+
156
+ ```bash
157
+ zengin explain # all rules, plus how scope and suppressions work
158
+ zengin explain classname-policy
159
+ ```
160
+
161
+ ## init
162
+
163
+ ```bash
164
+ zengin init # a commented zengin.config.yaml template
165
+ zengin init --from shadcn # derive zengin/tokens.json, zengin/components.json and a config from a shadcn/ui project
166
+ zengin init --from package @umami/react-zen # the same from an installed design-system package: its CSS variables (names kept) and .d.ts
167
+ zengin init --from shadcn --dir ../app --force
168
+ ```
169
+
170
+ The template refuses to overwrite an existing config. The shadcn path reads the theme CSS, the Tailwind config and `components/ui`, writes the definitions, and prints the defaults worth reviewing. See [`@zenginui/adapter-shadcn`](../adapter-shadcn). The package path reads `node_modules/<name>`: its theme stylesheet, its precompiled utilities and its `.d.ts`; see [`@zenginui/adapter-css`](../adapter-css).
171
+
172
+ ## Not included, on purpose
173
+
174
+ `zengin fix` would apply `exact` fixes automatically. It is a small addition, and it is left out for now because the first milestone's non-goals exclude tools that edit code. It is a candidate for later once the false-positive rate of the rules is measured on a real codebase.
175
+
176
+ ## History
177
+
178
+ ```bash
179
+ zengin report --into reports # files reports/<repo>/<time>.json instead of one --out file
180
+ zengin rollup reports --format html --out public/rollup/index.html
181
+ zengin report --into reports --at 2026-09-01T06:00:00Z --commit abc1234 --ref main # backfill from an older checkout
182
+ ```
183
+
184
+ `zengin rollup` accepts directories and reads every snapshot under them as history: the newest run per repository is the row, the one before is the delta, the series is the trend. See [`@zenginui/rollup`](../rollup#history-and-trends).
@@ -0,0 +1,38 @@
1
+ import { type RuleId, type Severity, type Summary, type Violation } from "@zenginui/engine";
2
+ export type Format = "pretty" | "json" | "github";
3
+ export interface CheckOptions {
4
+ /** Explicit config path. Otherwise the nearest zengin.config.yaml walking up from cwd. */
5
+ config?: string;
6
+ cwd: string;
7
+ /** Project-relative or absolute paths. Empty means the whole scope. */
8
+ paths: string[];
9
+ /** Only files changed relative to this git ref. */
10
+ changed?: string;
11
+ /** Only files staged in git. */
12
+ staged: boolean;
13
+ rules?: RuleId[];
14
+ severity?: Severity;
15
+ /** Lowest severity that makes the exit code non-zero. */
16
+ failOn: Severity | "never";
17
+ format: Format;
18
+ /** Maximum violations rendered in pretty output. */
19
+ max: number;
20
+ }
21
+ export declare const DEFAULT_CHECK: Omit<CheckOptions, "cwd">;
22
+ export interface CheckResult {
23
+ projectDir: string;
24
+ configPath: string;
25
+ system: {
26
+ package: string;
27
+ version: string;
28
+ };
29
+ filesChecked: number;
30
+ violations: Violation[];
31
+ summary: Summary;
32
+ /** 0 clean or below fail-on, 1 violations at or above fail-on. Usage and config errors throw. */
33
+ exitCode: 0 | 1;
34
+ }
35
+ export declare function findConfig(explicit: string | undefined, startDir: string): string;
36
+ export declare function runCheck(opts: CheckOptions): Promise<CheckResult>;
37
+ /** Reads a file for callers that want to show source context. */
38
+ export declare function readSource(projectDir: string, rel: string): string | undefined;
package/dist/check.js ADDED
@@ -0,0 +1,92 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { dirname, isAbsolute, join, relative, resolve } from "node:path";
3
+ import { createEngine, loadConfigFile, readProjectFiles, resolveConfig, summarize, } from "@zenginui/engine";
4
+ import { changedFiles, stagedFiles } from "./git.js";
5
+ export const DEFAULT_CHECK = { paths: [], staged: false, failOn: "error", format: "pretty", max: 200 };
6
+ const CONFIG_NAMES = ["zengin.config.yaml", "zengin.config.yml"];
7
+ const RANK = { error: 3, warn: 2, info: 1 };
8
+ export function findConfig(explicit, startDir) {
9
+ if (explicit) {
10
+ const p = resolve(startDir, explicit);
11
+ if (!existsSync(p))
12
+ throw new Error(`Config not found at ${p}.`);
13
+ return p;
14
+ }
15
+ let dir = resolve(startDir);
16
+ for (;;) {
17
+ for (const name of CONFIG_NAMES) {
18
+ const p = join(dir, name);
19
+ if (existsSync(p))
20
+ return p;
21
+ }
22
+ const parent = dirname(dir);
23
+ if (parent === dir)
24
+ break;
25
+ dir = parent;
26
+ }
27
+ throw new Error(`No zengin.config.yaml found in ${startDir} or any parent. Run \`zengin init\` to create one, or pass --config.`);
28
+ }
29
+ function selectFiles(opts, projectDir, config) {
30
+ const inScope = readProjectFiles(projectDir, config.scope.include, config.scope.exclude);
31
+ const byPath = new Map(inScope.map((f) => [f.path, f]));
32
+ const toRel = (p) => relative(projectDir, isAbsolute(p) ? p : resolve(opts.cwd, p)).replace(/\\/g, "/");
33
+ let wanted;
34
+ if (opts.staged)
35
+ wanted = stagedFiles(projectDir);
36
+ else if (opts.changed !== undefined)
37
+ wanted = changedFiles(projectDir, opts.changed);
38
+ if (wanted) {
39
+ const explicit = opts.paths.map(toRel);
40
+ return wanted
41
+ .filter((p) => byPath.has(p) && (explicit.length === 0 || explicit.some((e) => p === e || p.startsWith(e + "/"))))
42
+ .map((p) => byPath.get(p));
43
+ }
44
+ if (opts.paths.length === 0)
45
+ return inScope;
46
+ const out = [];
47
+ for (const raw of opts.paths) {
48
+ const rel = toRel(raw);
49
+ const abs = join(projectDir, rel);
50
+ if (!existsSync(abs))
51
+ throw new Error(`Path not found: ${raw}`);
52
+ const hit = byPath.get(rel);
53
+ if (hit) {
54
+ out.push(hit);
55
+ continue;
56
+ }
57
+ // A directory: every in-scope file under it. A file outside scope: nothing, by design.
58
+ for (const f of inScope)
59
+ if (f.path.startsWith(rel + "/"))
60
+ out.push(f);
61
+ }
62
+ return out;
63
+ }
64
+ export async function runCheck(opts) {
65
+ const configPath = findConfig(opts.config, opts.cwd);
66
+ const { config, dir } = loadConfigFile(configPath);
67
+ const resolved = resolveConfig(config, dir);
68
+ const engine = await createEngine(resolved);
69
+ const files = selectFiles(opts, dir, resolved);
70
+ // Stylesheets outside the selection still resolve class names for the files in it.
71
+ engine.loadStylesheets(readProjectFiles(dir, resolved.scope.include, resolved.scope.exclude).filter((f) => /\.css$/.test(f.path)));
72
+ let violations = engine.check(files);
73
+ if (opts.rules)
74
+ violations = violations.filter((v) => opts.rules.includes(v.rule));
75
+ if (opts.severity)
76
+ violations = violations.filter((v) => v.severity === opts.severity);
77
+ const failing = opts.failOn !== "never" && violations.some((v) => RANK[v.severity] >= RANK[opts.failOn]);
78
+ return {
79
+ projectDir: dir,
80
+ configPath,
81
+ system: { package: resolved.system.package, version: resolved.system.version },
82
+ filesChecked: files.length,
83
+ violations,
84
+ summary: summarize(violations),
85
+ exitCode: failing ? 1 : 0,
86
+ };
87
+ }
88
+ /** Reads a file for callers that want to show source context. */
89
+ export function readSource(projectDir, rel) {
90
+ const p = join(projectDir, rel);
91
+ return existsSync(p) ? readFileSync(p, "utf8") : undefined;
92
+ }
@@ -0,0 +1,6 @@
1
+ import type { CheckResult } from "./check.js";
2
+ /** Terminal rendering: grouped by file, one line per violation, fix underneath. */
3
+ export declare function renderPretty(result: CheckResult, max: number): string;
4
+ /** GitHub Actions workflow commands, so violations become inline annotations on the pull request. */
5
+ export declare function renderGithub(result: CheckResult): string;
6
+ export declare function renderJson(result: CheckResult): string;
@@ -0,0 +1,65 @@
1
+ import { renderSummary } from "@zenginui/engine";
2
+ const useColor = () => process.stdout.isTTY === true && !process.env["NO_COLOR"] && process.env["TERM"] !== "dumb";
3
+ function paint(code) {
4
+ return useColor() ? (s) => `[${code}m${s}` : (s) => s;
5
+ }
6
+ const bold = paint("1");
7
+ const dim = paint("2");
8
+ const red = paint("31");
9
+ const yellow = paint("33");
10
+ const blue = paint("34");
11
+ const green = paint("32");
12
+ const cyan = paint("36");
13
+ const SEVERITY_PAINT = { error: red, warn: yellow, info: blue };
14
+ /** Terminal rendering: grouped by file, one line per violation, fix underneath. */
15
+ export function renderPretty(result, max) {
16
+ const { violations, filesChecked, system } = result;
17
+ const lines = [];
18
+ const shown = violations.slice(0, max);
19
+ let current = "";
20
+ for (const v of shown) {
21
+ if (v.file !== current) {
22
+ current = v.file;
23
+ if (lines.length)
24
+ lines.push("");
25
+ lines.push(bold(v.file));
26
+ }
27
+ const loc = dim(`${v.range.start.line}:${v.range.start.col}`);
28
+ lines.push(` ${loc} ${SEVERITY_PAINT[v.severity](v.severity.padEnd(5))} ${cyan(v.rule)} ${v.message}`);
29
+ lines.push(` ${dim("found")} ${oneLine(v.found)}`);
30
+ if (v.fix.replace !== null)
31
+ lines.push(` ${dim(`fix (${v.fix.confidence})`)} ${green(oneLine(v.fix.replace))}`);
32
+ if (v.fix.note)
33
+ lines.push(` ${dim("note")} ${v.fix.note}`);
34
+ if (v.note)
35
+ lines.push(` ${dim("note")} ${v.note}`);
36
+ }
37
+ if (lines.length)
38
+ lines.push("");
39
+ const summary = renderSummary(result.summary);
40
+ const tail = `${filesChecked} file${filesChecked === 1 ? "" : "s"} checked against ${system.package}@${system.version}: ${summary}`;
41
+ lines.push(violations.length ? (result.exitCode ? red(tail) : yellow(tail)) : green(tail));
42
+ if (shown.length < violations.length)
43
+ lines.push(dim(`Showing the first ${shown.length}. Raise --max or use --format json for all.`));
44
+ return lines.join("\n");
45
+ }
46
+ /** GitHub Actions workflow commands, so violations become inline annotations on the pull request. */
47
+ export function renderGithub(result) {
48
+ const level = (s) => (s === "error" ? "error" : s === "warn" ? "warning" : "notice");
49
+ const esc = (s) => s.replace(/%/g, "%25").replace(/\r/g, "%0D").replace(/\n/g, "%0A");
50
+ const prop = (s) => esc(s).replace(/:/g, "%3A").replace(/,/g, "%2C");
51
+ const lines = result.violations.map((v) => {
52
+ const fix = v.fix.replace !== null ? ` Fix (${v.fix.confidence}): ${v.fix.replace}` : "";
53
+ return `::${level(v.severity)} file=${prop(v.file)},line=${v.range.start.line},endLine=${v.range.end.line},col=${v.range.start.col},endColumn=${v.range.end.col},title=${prop(`zengin ${v.rule}`)}::${esc(v.message + fix)}`;
54
+ });
55
+ lines.push(`${result.filesChecked} files checked against ${result.system.package}@${result.system.version}: ${renderSummary(result.summary)}`);
56
+ return lines.join("\n");
57
+ }
58
+ export function renderJson(result) {
59
+ const { projectDir, configPath, system, filesChecked, summary, violations, exitCode } = result;
60
+ return JSON.stringify({ projectDir, configPath, system, filesChecked, exitCode, summary, violations }, null, 2);
61
+ }
62
+ function oneLine(s) {
63
+ const flat = s.replace(/\s+/g, " ").trim();
64
+ return flat.length > 120 ? flat.slice(0, 117) + "..." : flat;
65
+ }
package/dist/git.d.ts ADDED
@@ -0,0 +1,4 @@
1
+ /** Files added, copied, modified or renamed relative to `ref`, plus uncommitted and untracked changes. */
2
+ export declare function changedFiles(projectDir: string, ref: string): string[];
3
+ /** Files staged for the next commit. */
4
+ export declare function stagedFiles(projectDir: string): string[];
package/dist/git.js ADDED
@@ -0,0 +1,39 @@
1
+ import { execFileSync } from "node:child_process";
2
+ import { join, relative } from "node:path";
3
+ function git(projectDir, args) {
4
+ let out;
5
+ try {
6
+ out = execFileSync("git", args, { cwd: projectDir, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
7
+ }
8
+ catch (e) {
9
+ const msg = e instanceof Error && "stderr" in e ? String(e.stderr).trim() : String(e);
10
+ throw new Error(`git ${args.join(" ")} failed in ${projectDir}: ${msg || "is this a git repository?"}`);
11
+ }
12
+ return out
13
+ .split("\n")
14
+ .map((l) => l.trim())
15
+ .filter(Boolean);
16
+ }
17
+ /**
18
+ * git reports paths relative to the repository root. The project may be a package inside a monorepo,
19
+ * so every path is re-based onto the project directory and anything outside it is dropped.
20
+ */
21
+ function toProject(projectDir, rootRelative) {
22
+ const root = git(projectDir, ["rev-parse", "--show-toplevel"])[0];
23
+ return rootRelative
24
+ .map((p) => relative(projectDir, join(root, p)).replace(/\\/g, "/"))
25
+ .filter((p) => p && !p.startsWith("../") && p !== "..")
26
+ .sort();
27
+ }
28
+ /** Files added, copied, modified or renamed relative to `ref`, plus uncommitted and untracked changes. */
29
+ export function changedFiles(projectDir, ref) {
30
+ const base = ref || "HEAD";
31
+ const committed = git(projectDir, ["diff", "--name-only", "--diff-filter=ACMR", `${base}...HEAD`]);
32
+ const working = git(projectDir, ["diff", "--name-only", "--diff-filter=ACMR", "HEAD"]);
33
+ const untracked = git(projectDir, ["ls-files", "--others", "--exclude-standard", "--full-name"]);
34
+ return toProject(projectDir, [...new Set([...committed, ...working, ...untracked])]);
35
+ }
36
+ /** Files staged for the next commit. */
37
+ export function stagedFiles(projectDir) {
38
+ return toProject(projectDir, git(projectDir, ["diff", "--name-only", "--cached", "--diff-filter=ACMR"]));
39
+ }
@@ -0,0 +1,31 @@
1
+ #!/usr/bin/env node
2
+ import { type CheckOptions } from "./check.js";
3
+ import { type ScaffoldOptions } from "./scaffold.js";
4
+ interface Parsed {
5
+ command: "check" | "explain" | "init" | "report" | "rollup" | "create" | "add" | "tokens" | "registry" | "theme" | "fonts" | "icons" | "upgrade" | "brand" | "figma" | "mock" | "help";
6
+ positional: string[];
7
+ check: CheckOptions;
8
+ init: {
9
+ from?: string;
10
+ dir?: string;
11
+ force: boolean;
12
+ };
13
+ report: {
14
+ repo?: string;
15
+ includeViolations: boolean;
16
+ out?: string;
17
+ into?: string;
18
+ at?: string;
19
+ commit?: string;
20
+ ref?: string;
21
+ };
22
+ rollup: {
23
+ previous?: string;
24
+ format: "markdown" | "json" | "html";
25
+ out?: string;
26
+ };
27
+ scaffold: ScaffoldOptions;
28
+ }
29
+ export declare function parseArgs(argv: string[], cwd: string): Parsed;
30
+ export declare function explain(rule: string | undefined): string;
31
+ export {};