@fringetools/cli 0.1.0 → 0.1.1

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 CHANGED
@@ -17,8 +17,6 @@ That is the whole setup. `init` adds a `PostToolUse` hook to `.claude/settings.j
17
17
 
18
18
  Prefer a permanent command? `npm install -D @fringetools/cli`, then `npx fringe check`. Existing messy repo? Run `npx @fringetools/cli check --update-baseline` once so only *new* problems gate (see [Baseline](#baseline)).
19
19
 
20
- > **Until the first npm release:** build from source — `pnpm install && pnpm --filter @fringetools/cli build`, then use `node packages/cli/dist/bin.js` in place of `npx @fringetools/cli`.
21
-
22
20
  ## Commands
23
21
 
24
22
  | Command | What it does |
@@ -29,7 +27,7 @@ Prefer a permanent command? `npm install -D @fringetools/cli`, then `npx fringe
29
27
  | `fringe check --hook` | One-line `fringe/hook.v1` JSON for CI / pre-commit. Exits `1` on errors. Claude Code does **not** feed this back — use `--claude-hook` for agents. Wins over `--json`. |
30
28
  | `fringe check --update-baseline` | Accept today's findings into `fringe.baseline.json` (commit it). Exit `0`. |
31
29
  | `fringe check --record [--manifest <path>]` | Also append this run to `.fringe/ledger.jsonl`, optionally merging an agent flow manifest. |
32
- | `fringe init [path]` | Add the Claude Code hook and install the skill. Safe to re-run; refuses to touch an invalid `settings.json`. |
30
+ | `fringe init [path]` | Add the Claude Code hook and install the three skills. Safe to re-run; refuses to touch an invalid `settings.json`. |
33
31
  | `fringe open [path] [--port <n>]` | Serve the ledger as a plain HTML page on `127.0.0.1:4173` (loopback only). Ctrl+C to stop. |
34
32
  | `fringe --help` · `--version` | Usage / version. |
35
33
 
@@ -103,7 +101,9 @@ After each edit the hook runs; with errors it returns, for example:
103
101
 
104
102
  This was verified end to end with real agent runs: the agent receives the findings and fixes them (see [docs/architecture.md](../../docs/architecture.md)). Why `--claude-hook` and not `--hook`: Claude Code only passes a hook's output to the agent as `decision`/`reason` JSON on exit 0 (or stderr on exit 2); the CI contract `--hook` exits 1 and is ignored.
105
103
 
106
- **The skill** (`.claude/skills/fringe/SKILL.md`, source: [skill/SKILL.md](skill/SKILL.md)) teaches the agent the ladder: *Flow* (cut what users don't need, with evidence in `.fringe/manifest.json`) → *Edges* (fix every hook finding, never silence a rule to get green) → *Polish* (tokens, scales, no AI look), then record the run. It is plain Markdown, so any agent platform that reads `AGENTS.md`/skill files can use it as-is.
104
+ **Skills** — `fringe init` installs three into `.claude/skills/` (sources in [skill/](skill/)): `fringe` (below), `fringe-review` ("review this page": runs the checker and reports ranked, plain-English findings with fixes; invoke as `/fringe-review`) and `fringe-audit` (whole-repo plan: top rules and files, a suggested fix order, offers `--update-baseline`; `/fringe-audit`). Reviews run `fringe check`, so Claude Code will ask you to approve that command once (or allow `Bash(fringe:*)` / `Bash(npx:*)`).
105
+
106
+ **The main skill** (`.claude/skills/fringe/SKILL.md`) teaches the agent the ladder: *Flow* (cut what users don't need, with evidence in `.fringe/manifest.json`) → *Edges* (fix every hook finding, never silence a rule to get green) → *Polish* (tokens, scales, no AI look), then record the run. It is plain Markdown, so any agent platform that reads `AGENTS.md`/skill files can use it as-is.
107
107
 
108
108
  For CI or pre-commit use `fringe check --hook` (or just the exit code):
109
109
 
package/dist/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  runCli
4
- } from "./chunk-AOUZJDQL.js";
4
+ } from "./chunk-W23H5MXS.js";
5
5
 
6
6
  // src/bin.ts
7
7
  process.exitCode = await runCli(process.argv.slice(2));
@@ -1166,7 +1166,7 @@ import { createServer } from "http";
1166
1166
  var esc = (s) => s.replace(/[&<>"']/g, (c) => `&#${c.charCodeAt(0)};`);
1167
1167
  function renderBoard(records, corrupt = 0) {
1168
1168
  const rows = [...records].reverse().map((r) => {
1169
- const flows = (r.manifest?.flows ?? []).map((f) => `<li>${esc(f.action)}: ${esc(f.what)} <small>(${esc(f.evidence)}) \u2014 judged</small></li>`).join("");
1169
+ const flows = (r.manifest?.flows ?? []).map((f) => `<li>${esc(f.action)}: ${esc(f.what)} <small>(${esc(f.evidence)}) \u2014 judged${f.unsupported ? ", <b>no evidence</b>" : ""}</small></li>`).join("");
1170
1170
  return `<tr><td>${esc(r.ts)}</td><td>${r.score}</td><td>${r.counts.error}</td><td>${r.counts.review}</td><td>${r.counts.signal}</td><td>${r.files}</td><td>${r.suppressed ?? 0}</td><td>${flows ? `<ul>${flows}</ul>` : ""}</td></tr>`;
1171
1171
  }).join("");
1172
1172
  const body = records.length === 0 ? "<p>No runs yet. Run <code>fringe check --record</code> and reload.</p>" : `<table><tr><th>When</th><th>Health</th><th>Errors</th><th>Review</th><th>Signals</th><th>Files</th><th>Suppressed</th><th>Flow cuts</th></tr>${rows}</table>`;
@@ -1190,7 +1190,8 @@ import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as rea
1190
1190
  import { dirname, join as join7 } from "path";
1191
1191
  import { fileURLToPath } from "url";
1192
1192
  var HOOK = "npx -y @fringetools/cli check --claude-hook";
1193
- var SKILL = fileURLToPath(new URL("../skill/SKILL.md", import.meta.url));
1193
+ var SKILLS = { fringe: "SKILL.md", "fringe-review": "review.md", "fringe-audit": "audit.md" };
1194
+ var skillSrc = (file) => fileURLToPath(new URL(`../skill/${file}`, import.meta.url));
1194
1195
  function initProject(root) {
1195
1196
  const done = [];
1196
1197
  const settingsPath = join7(root, ".claude", "settings.json");
@@ -1210,12 +1211,14 @@ function initProject(root) {
1210
1211
  writeFileSync2(settingsPath, JSON.stringify(settings, null, 2) + "\n");
1211
1212
  done.push(`hook added to ${settingsPath}`);
1212
1213
  }
1213
- const skillPath = join7(root, ".claude", "skills", "fringe", "SKILL.md");
1214
- if (existsSync2(skillPath)) done.push("skill already present");
1215
- else {
1216
- mkdirSync2(dirname(skillPath), { recursive: true });
1217
- copyFileSync(SKILL, skillPath);
1218
- done.push(`skill installed at ${skillPath}`);
1214
+ for (const [name, file] of Object.entries(SKILLS)) {
1215
+ const skillPath = join7(root, ".claude", "skills", name, "SKILL.md");
1216
+ if (existsSync2(skillPath)) done.push(`skill ${name} already present`);
1217
+ else {
1218
+ mkdirSync2(dirname(skillPath), { recursive: true });
1219
+ copyFileSync(skillSrc(file), skillPath);
1220
+ done.push(`skill ${name} installed`);
1221
+ }
1219
1222
  }
1220
1223
  return done;
1221
1224
  }
@@ -1235,6 +1238,13 @@ function readManifest(path2, warn) {
1235
1238
  try {
1236
1239
  const parsed = JSON.parse(raw);
1237
1240
  if (!parsed || !Array.isArray(parsed.flows)) throw new Error("missing flows");
1241
+ for (const f of parsed.flows) {
1242
+ if (f.action === "cut" && weakEvidence(f.evidence)) {
1243
+ f.unsupported = true;
1244
+ warn(`fringe: warning: cut '${f.what}' has no real evidence \u2014 recorded as unsupported
1245
+ `);
1246
+ }
1247
+ }
1238
1248
  return parsed;
1239
1249
  } catch {
1240
1250
  warn(`fringe: warning: manifest at ${path2} is not valid '{ flows: [...] }' JSON \u2014 continuing without
@@ -1242,6 +1252,7 @@ function readManifest(path2, warn) {
1242
1252
  return void 0;
1243
1253
  }
1244
1254
  }
1255
+ var weakEvidence = (e) => typeof e !== "string" || e.trim().length < 12 || /^(n\/?a|none|tbd|obvious|unnecessary|clutter|unused)\.?$/i.test(e.trim());
1245
1256
  var HELP = `fringe check [--json] [--hook] [--record] [--manifest <path>] [path]
1246
1257
  fringe check --claude-hook [path] for Claude Code hooks (feeds errors back to the agent)
1247
1258
  fringe check --update-baseline [path] accept today's findings; only new ones gate
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  runCli
3
- } from "./chunk-AOUZJDQL.js";
3
+ } from "./chunk-W23H5MXS.js";
4
4
  export {
5
5
  runCli
6
6
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fringetools/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "bin": {
package/skill/SKILL.md CHANGED
@@ -5,6 +5,8 @@ description: Use when writing or changing frontend code (JSX/TSX, HTML, CSS, Vue
5
5
 
6
6
  # Fringe
7
7
 
8
+ Command: use `fringe` if it is on the PATH, otherwise `npx -y @fringetools/cli` in its place (e.g. `npx -y @fringetools/cli check --json .`).
9
+
8
10
  Ponytail trims your code. Fringe handles your edges. Work the ladder in order.
9
11
 
10
12
  1. **Flow (judged).** Before building, ask what the user can skip. Cut steps, screens or options nobody needs. Write each decision with its evidence to `.fringe/manifest.json`: `{"flows":[{"action":"cut"|"kept","what":"...","evidence":"..."}]}`. No evidence, no cut.
package/skill/audit.md ADDED
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: fringe-audit
3
+ description: Use when the user wants a whole-repo UX/accessibility audit, a plan for an existing frontend with many problems, or to adopt Fringe on a messy repo ("audit this codebase", "where do we start", "adopt fringe"). Produces a prioritized plan and sets a baseline.
4
+ ---
5
+
6
+ # Fringe audit
7
+
8
+ Command: use `fringe` if it is on the PATH, otherwise `npx -y @fringetools/cli` in its place (e.g. `npx -y @fringetools/cli check --json .`).
9
+
10
+ 1. Run `fringe check --json .` and note the totals. Do not print every finding.
11
+ 2. Summarize as a plan:
12
+ - Totals by severity and the health score, plus files scanned and skipped.
13
+ - **Top rules** by count, and **top files** by error count (the 5 worst of each).
14
+ - For each top rule: what it means in one sentence, a typical fix, and whether it is likely mechanical (add `aria-label`, add `alt`) or needs design judgment.
15
+ - A suggested order: mechanical `error` fixes first, then `error`s needing judgment, then `review`, then `signal`.
16
+ 3. Offer the baseline: "`fringe check --update-baseline` records today's findings so only new problems gate; commit `fringe.baseline.json`." Run it only if the user agrees.
17
+ 4. Ask before changing code. If the user says go, fix one rule at a time, re-run `fringe check` after each, and re-baseline at the end so the file shrinks.
18
+
19
+ Spot-check a few reported findings by reading the code before presenting them as certain; Fringe is static and can be wrong. Say which you checked.
@@ -0,0 +1,18 @@
1
+ ---
2
+ name: fringe-review
3
+ description: Use when the user asks to review, check or critique a UI, page or component for accessibility and UX edges ("review this page", "what's wrong with this form", "is this accessible"). Runs Fringe and reports ranked findings with fixes, in plain English.
4
+ ---
5
+
6
+ # Fringe review
7
+
8
+ Command: use `fringe` if it is on the PATH, otherwise `npx -y @fringetools/cli` in its place (e.g. `npx -y @fringetools/cli check --json .`).
9
+
10
+ 1. Run `fringe check --json .` from the project root. Fringe takes directories, not single files, so if the user named one file or page, run on the folder and keep only the findings whose `loc.file` matches it.
11
+ 2. Report in plain English, no jargon left unexplained:
12
+ - **Must fix** — `error` findings. Group by rule. For each: where (`file:line`), what is wrong in one sentence, the exact fix, and who is hurt (for example "a screen reader says only 'button'"). Cite the `wcag` criterion when present.
13
+ - **Worth a look** — `review` findings. These are heuristics: say they need your judgment, and give your call on each.
14
+ - **Polish** — `signal` findings, only the top few. Never present them as bugs.
15
+ 3. End with the health score as measured, the count by severity, and one line on what was not checked (Fringe is static: it cannot judge contrast on images, real focus order, or whether copy is good).
16
+ 4. Change no code unless asked. If asked to fix, fix `error` items first and re-run to confirm.
17
+
18
+ Do not invent findings the tool did not report, and do not call the score an estimate: it is measured.