rulereceipt 0.1.26 → 0.1.27
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 +86 -72
- package/dist/browser/analyze.js +2 -2
- package/dist/checks/classify.js +70 -0
- package/dist/cli.js +31 -2
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -9,29 +9,18 @@
|
|
|
9
9
|
Checks whether a Claude Code session actually followed the rules in your
|
|
10
10
|
CLAUDE.md / AGENTS.md — with evidence, not just a vibe.
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
a specific commit. No publishing token exists to be stolen. Check it
|
|
16
|
-
yourself with `npm audit signatures` after installing.
|
|
12
|
+
Runs entirely on your machine. Plain `rulereceipt check` makes zero network
|
|
13
|
+
calls — [Trust, privacy and licensing](#trust-privacy-and-licensing) has the
|
|
14
|
+
full detail, including the three off-by-default opt-ins.
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
[NOTICE.md](./NOTICE.md) before reusing this code.
|
|
16
|
+
## See it in 10 seconds
|
|
20
17
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
counts are knowable, nothing else. None of them fire unless you explicitly
|
|
28
|
-
pass the flag, and `DO_NOT_TRACK=1` / `RULERECEIPT_NO_TELEMETRY=1` forces
|
|
29
|
-
telemetry off even if you do.
|
|
30
|
-
|
|
31
|
-
**Security note:** RuleReceipt never modifies `.claude/settings.json` and
|
|
32
|
-
installs no hooks without explicit action. It only ever reads your
|
|
33
|
-
CLAUDE.md/AGENTS.md and session transcripts — read-only, manual invocation
|
|
34
|
-
only (`rulereceipt check`). No automatic hooks, ever, in v1.
|
|
18
|
+
```bash
|
|
19
|
+
npx rulereceipt demo
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
No install, no config, no API key, no real session needed — prints a sample
|
|
23
|
+
report so you can see the output shape immediately.
|
|
35
24
|
|
|
36
25
|
## Status
|
|
37
26
|
|
|
@@ -59,7 +48,12 @@ Published and live on npm, actively developed.
|
|
|
59
48
|
Claude key; without it they report UNCLEAR rather than guessing.
|
|
60
49
|
- Lines containing no instruction at all — directory listings,
|
|
61
50
|
reference tables, examples — aren't rules, and are reported as such
|
|
62
|
-
instead of being checked.
|
|
51
|
+
instead of being checked. This step is a heuristic over English
|
|
52
|
+
instruction words, so it can be wrong in both directions: run
|
|
53
|
+
`rulereceipt check --show-skipped` once on your rules file to see
|
|
54
|
+
exactly what it excluded. A rule phrased unusually, or written in
|
|
55
|
+
another language, can land there — and a rule dropped silently is
|
|
56
|
+
worse than one reported wrongly.
|
|
63
57
|
4. Prints a report — terminal table by default, `--markdown` for pasting
|
|
64
58
|
into a PR or Slack message, or `--html` for a shareable single file —
|
|
65
59
|
showing what passed, what failed, and a quoted line of evidence for
|
|
@@ -68,30 +62,31 @@ Published and live on npm, actively developed.
|
|
|
68
62
|
that exact file. (It proves the report matches the file, not that the
|
|
69
63
|
file is an unmodified record — see SECURITY.md.)
|
|
70
64
|
|
|
71
|
-
##
|
|
72
|
-
|
|
73
|
-
`check` exits **1** when a rule was actually broken, and **0** otherwise,
|
|
74
|
-
so CI can gate on it. Rules that need human judgment report UNCLEAR and
|
|
75
|
-
never affect the exit code — most rules in a real CLAUDE.md need judgment,
|
|
76
|
-
and gating on those would make every build red on day one.
|
|
77
|
-
|
|
78
|
-
`--exit-zero` prints the report without failing the build. `--require-session`
|
|
79
|
-
does the opposite and is the one to use anywhere automated: it fails when
|
|
80
|
-
there is no session, or an empty one, instead of reporting a pass for a
|
|
81
|
-
check that never actually ran.
|
|
82
|
-
|
|
83
|
-
### A limit worth knowing before you wire this into CI
|
|
65
|
+
## Usage
|
|
84
66
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
67
|
+
```bash
|
|
68
|
+
rulereceipt check # check the latest session in this project
|
|
69
|
+
rulereceipt check --markdown # same, formatted for pasting into a PR/Slack
|
|
70
|
+
rulereceipt check --html # write a shareable single-file HTML report you can send
|
|
71
|
+
rulereceipt check --html report.html # ...to a specific path
|
|
72
|
+
rulereceipt check --show-skipped # list what was treated as documentation and not checked
|
|
73
|
+
rulereceipt check --require-session # fail if there's no session, instead of passing silently
|
|
74
|
+
rulereceipt check --exit-zero # report failures without failing the build
|
|
75
|
+
rulereceipt check --llm # opt-in: grade judgment rules with your own Claude key
|
|
76
|
+
rulereceipt check --share # opt-in: send anonymous pass/fail/unclear counts
|
|
77
|
+
rulereceipt check --telemetry # opt-in: send one random per-machine ID
|
|
78
|
+
rulereceipt check --transcript <path> # check a specific session file
|
|
79
|
+
rulereceipt doctor # list hooks/auto-run tasks configured on this machine
|
|
80
|
+
rulereceipt lint # find contradictions between CLAUDE.md and AGENTS.md
|
|
81
|
+
rulereceipt digest # summarise recent checks; --email to send it
|
|
82
|
+
rulereceipt config # set up email sending (stays on your machine)
|
|
83
|
+
rulereceipt demo # sample output, no setup needed
|
|
84
|
+
rulereceipt demo --markdown
|
|
85
|
+
rulereceipt --version # print the installed version
|
|
86
|
+
rulereceipt verify <session-file> <hash> # spot-check a report you received against the real session file
|
|
87
|
+
```
|
|
92
88
|
|
|
93
|
-
|
|
94
|
-
locally and attach the report to the PR.
|
|
89
|
+
`verify` isn't a routine check — trust your team day to day, same as any status update. It's there for the rare case it actually matters (a dispute, an incident review): give it the session file and the hash printed in the report, and it confirms whether they really match.
|
|
95
90
|
|
|
96
91
|
## Sharing a report
|
|
97
92
|
|
|
@@ -110,39 +105,30 @@ independently confirm it describes the session it claims to.
|
|
|
110
105
|
Nothing is uploaded. The file is written to your working directory and
|
|
111
106
|
goes wherever you choose to send it.
|
|
112
107
|
|
|
113
|
-
##
|
|
108
|
+
## Exit codes
|
|
114
109
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
110
|
+
`check` exits **1** when a rule was actually broken, and **0** otherwise,
|
|
111
|
+
so CI can gate on it. Rules that need human judgment report UNCLEAR and
|
|
112
|
+
never affect the exit code — most rules in a real CLAUDE.md need judgment,
|
|
113
|
+
and gating on those would make every build red on day one.
|
|
118
114
|
|
|
119
|
-
|
|
120
|
-
|
|
115
|
+
`--exit-zero` prints the report without failing the build. `--require-session`
|
|
116
|
+
does the opposite and is the one to use anywhere automated: it fails when
|
|
117
|
+
there is no session, or an empty one, instead of reporting a pass for a
|
|
118
|
+
check that never actually ran.
|
|
121
119
|
|
|
122
|
-
|
|
120
|
+
### A limit worth knowing before you wire this into CI
|
|
123
121
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
rulereceipt
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
rulereceipt check --llm # opt-in: grade judgment rules with your own Claude key
|
|
132
|
-
rulereceipt check --share # opt-in: send anonymous pass/fail/unclear counts
|
|
133
|
-
rulereceipt check --telemetry # opt-in: send one random per-machine ID
|
|
134
|
-
rulereceipt check --transcript <path> # check a specific session file
|
|
135
|
-
rulereceipt doctor # list hooks/auto-run tasks configured on this machine
|
|
136
|
-
rulereceipt lint # find contradictions between CLAUDE.md and AGENTS.md
|
|
137
|
-
rulereceipt digest # summarise recent checks; --email to send it
|
|
138
|
-
rulereceipt config # set up email sending (stays on your machine)
|
|
139
|
-
rulereceipt demo # sample output, no setup needed
|
|
140
|
-
rulereceipt demo --markdown
|
|
141
|
-
rulereceipt --version # print the installed version
|
|
142
|
-
rulereceipt verify <session-file> <hash> # spot-check a report you received against the real session file
|
|
143
|
-
```
|
|
122
|
+
Claude Code writes its session transcript to the machine the agent ran on
|
|
123
|
+
— your laptop. A CI runner is a fresh machine that has never seen it, so a
|
|
124
|
+
CI job cannot check a session that happened on your laptop unless you
|
|
125
|
+
deliberately make that transcript available to the job. See
|
|
126
|
+
[templates/rulereceipt-ci.yml](./templates/rulereceipt-ci.yml), which
|
|
127
|
+
explains the options and, if you use it, fails loudly rather than passing
|
|
128
|
+
on a session it never found.
|
|
144
129
|
|
|
145
|
-
|
|
130
|
+
For most people the honest answer is simpler: run `rulereceipt check --html`
|
|
131
|
+
locally and attach the report to the PR.
|
|
146
132
|
|
|
147
133
|
## Install
|
|
148
134
|
|
|
@@ -161,6 +147,34 @@ npm test
|
|
|
161
147
|
npx tsx src/cli.ts demo
|
|
162
148
|
```
|
|
163
149
|
|
|
150
|
+
## Trust, privacy and licensing
|
|
151
|
+
|
|
152
|
+
**Nothing leaves your machine unless you ask.** Your code, rules, and
|
|
153
|
+
session content never leave your computer, ever. Plain `rulereceipt check`
|
|
154
|
+
makes zero network calls. `--llm`, `--share`, and `--telemetry` are all
|
|
155
|
+
separate, off-by-default opt-ins: `--llm` calls the Claude API using your
|
|
156
|
+
own Anthropic key for rules that need judgment; `--share` sends aggregate
|
|
157
|
+
pass/fail/unclear counts; `--telemetry` sends one random per-machine ID so
|
|
158
|
+
real distinct-install counts are knowable, nothing else. None of them fire
|
|
159
|
+
unless you explicitly pass the flag, and `DO_NOT_TRACK=1` /
|
|
160
|
+
`RULERECEIPT_NO_TELEMETRY=1` forces telemetry off even if you do.
|
|
161
|
+
|
|
162
|
+
**Read-only, and no hooks.** RuleReceipt never modifies
|
|
163
|
+
`.claude/settings.json` and installs no hooks without explicit action. It
|
|
164
|
+
only ever reads your CLAUDE.md/AGENTS.md and session transcripts —
|
|
165
|
+
read-only, manual invocation only (`rulereceipt check`). No automatic
|
|
166
|
+
hooks, ever, in v1.
|
|
167
|
+
|
|
168
|
+
**You can verify the package came from this source.** Every release from
|
|
169
|
+
0.1.19 on is built and published by GitHub Actions and signed with
|
|
170
|
+
[npm provenance](https://docs.npmjs.com/generating-provenance-statements),
|
|
171
|
+
so you can verify the published package was built from this repository at
|
|
172
|
+
a specific commit. No publishing token exists to be stolen. Check it
|
|
173
|
+
yourself with `npm audit signatures` after installing.
|
|
174
|
+
|
|
175
|
+
**Licence.** Source-available software — see [LICENSE](./LICENSE) and
|
|
176
|
+
[NOTICE.md](./NOTICE.md) before reusing this code.
|
|
177
|
+
|
|
164
178
|
## Contact
|
|
165
179
|
|
|
166
180
|
Questions, bugs, or anything else — hello@rulereceipt.dev.
|
package/dist/browser/analyze.js
CHANGED
|
@@ -4,8 +4,8 @@ import { classifyRules } from "../checks/classify.js";
|
|
|
4
4
|
export const CORPUS = {
|
|
5
5
|
files: 559,
|
|
6
6
|
parsed: 23704,
|
|
7
|
-
notRulesPct:
|
|
8
|
-
checkablePct:
|
|
7
|
+
notRulesPct: 64.5,
|
|
8
|
+
checkablePct: 42.7,
|
|
9
9
|
};
|
|
10
10
|
function pct(part, whole) {
|
|
11
11
|
return whole === 0 ? 0 : Math.round((part / whole) * 1000) / 10;
|
package/dist/checks/classify.js
CHANGED
|
@@ -68,12 +68,82 @@ function isEventRecord(rule) {
|
|
|
68
68
|
return false;
|
|
69
69
|
return EVENT_RECORD_TITLE.test(rule.title);
|
|
70
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* A line whose SUBJECT is a command-line option, i.e. the documentation of
|
|
73
|
+
* a flag rather than an instruction to the agent. "`--file=<path>, -f`: Use
|
|
74
|
+
* alternative tasks.json file" describes what the flag does; the imperative
|
|
75
|
+
* belongs to the flag, not to the reader.
|
|
76
|
+
*
|
|
77
|
+
* This is why the directive test alone cannot separate these: command
|
|
78
|
+
* documentation is written with the same verbs as a command, because it is
|
|
79
|
+
* describing a command. The distinguishing property is grammatical — what
|
|
80
|
+
* the sentence is ABOUT — and it is announced by the line beginning with an
|
|
81
|
+
* option token and a separator.
|
|
82
|
+
*
|
|
83
|
+
* Deliberately narrow. It requires the option token to be the first thing on
|
|
84
|
+
* the line and to be followed by a colon, so a rule that merely mentions a
|
|
85
|
+
* flag ("Always pass `--frozen-lockfile` when installing") is untouched.
|
|
86
|
+
*/
|
|
87
|
+
const FLAG_DOCUMENTATION = /^\s*`?\s*-{1,2}[A-Za-z0-9][\w-]*(?:=[^\s,`]*)?(?:\s*,\s*`?\s*-{1,2}[\w-]+`?(?:=[^\s,`]*)?)*\s*`?\s*:/;
|
|
88
|
+
/**
|
|
89
|
+
* English prose almost always contains at least one function word. A shell
|
|
90
|
+
* invocation contains none, and contains at least one token carrying a path
|
|
91
|
+
* separator, a flag prefix, an internal dot, or an assignment.
|
|
92
|
+
*
|
|
93
|
+
* Both conditions are required. "Use modular architecture" has no function
|
|
94
|
+
* word either, but no command-shaped token, so it stays a rule. "Run `npm
|
|
95
|
+
* test` before every push" has command-shaped tokens but also has function
|
|
96
|
+
* words, so it stays a rule too.
|
|
97
|
+
*
|
|
98
|
+
* This is a property of the language, not of the file format — the same
|
|
99
|
+
* reasoning as DIRECTIVE_LANGUAGE above, which is why it is expressed as a
|
|
100
|
+
* test on the text rather than as a list of documentation conventions.
|
|
101
|
+
*/
|
|
102
|
+
const FUNCTION_WORD = /\b(a|an|the|is|are|was|were|be|being|been|to|of|in|on|for|with|when|if|that|this|these|those|and|or|but|not|no|do|does|you|your|it|its|as|at|by|from|before|after|until|unless|any|every|all|each|must|should|never|always|only|via|per|than|then|so|such|into|onto|over|under|about)\b/i;
|
|
103
|
+
const COMMAND_SHAPED_TOKEN = /(?:^|\s)(?:\S*[/\\]\S*|-{1,2}[A-Za-z]\S*|\S+\.\S+|\S+=\S+)/;
|
|
104
|
+
function looksLikeBareCommand(text) {
|
|
105
|
+
const withoutCode = text
|
|
106
|
+
.replace(/```[\s\S]*?```/g, " ")
|
|
107
|
+
.replace(/`[^`]*`/g, " ")
|
|
108
|
+
.trim();
|
|
109
|
+
// If stripping code leaves nothing, the body WAS only code.
|
|
110
|
+
const body = withoutCode.length > 0 ? withoutCode : text.replace(/[`\n]+/g, " ").trim();
|
|
111
|
+
if (!body)
|
|
112
|
+
return false;
|
|
113
|
+
// Long enough to be prose, whatever it contains.
|
|
114
|
+
if (body.split(/\s+/).length > 12)
|
|
115
|
+
return false;
|
|
116
|
+
if (FUNCTION_WORD.test(body))
|
|
117
|
+
return false;
|
|
118
|
+
return COMMAND_SHAPED_TOKEN.test(body);
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A heading that labels a command, with the command as its whole body:
|
|
122
|
+
* "Build release APK" over `.\gradlew assembleRelease`. The title reads as
|
|
123
|
+
* an imperative, but nothing here constrains the agent — it is a how-to, and
|
|
124
|
+
* there is no compliance to check.
|
|
125
|
+
*
|
|
126
|
+
* Guarded by TITLE_OPENS_WITH_DIRECTIVE so a genuine prohibition whose body
|
|
127
|
+
* is the forbidden command ("Never run: `rm -rf /`") is still a rule.
|
|
128
|
+
*/
|
|
129
|
+
function isCommandDocumentation(rule) {
|
|
130
|
+
if (TITLE_OPENS_WITH_DIRECTIVE.test(rule.title))
|
|
131
|
+
return false;
|
|
132
|
+
if (FLAG_DOCUMENTATION.test(rule.text) || FLAG_DOCUMENTATION.test(rule.title))
|
|
133
|
+
return true;
|
|
134
|
+
return looksLikeBareCommand(rule.text);
|
|
135
|
+
}
|
|
71
136
|
function isNotARule(rule) {
|
|
72
137
|
// Checked before the directive test on purpose: an incident note that
|
|
73
138
|
// ends with its lesson contains a real directive, and would otherwise
|
|
74
139
|
// be enforced as though the history itself were the rule.
|
|
75
140
|
if (isEventRecord(rule))
|
|
76
141
|
return true;
|
|
142
|
+
// Same reason as above: command documentation carries real imperatives
|
|
143
|
+
// ("Use alternative tasks.json file"), so the directive test below would
|
|
144
|
+
// otherwise accept it as a rule to check compliance against.
|
|
145
|
+
if (isCommandDocumentation(rule))
|
|
146
|
+
return true;
|
|
77
147
|
const combined = `${rule.title} ${rule.text}`;
|
|
78
148
|
if (DIRECTIVE_LANGUAGE.test(combined))
|
|
79
149
|
return false;
|
package/dist/cli.js
CHANGED
|
@@ -132,7 +132,7 @@ function writeHtmlReport(results, meta, cwd, target) {
|
|
|
132
132
|
}
|
|
133
133
|
}
|
|
134
134
|
async function runCheck(opts) {
|
|
135
|
-
const { markdown, share, email, emailAlways, llm, telemetry, html, exitZero, requireSession, transcriptOverride } = opts;
|
|
135
|
+
const { markdown, share, email, emailAlways, llm, telemetry, html, exitZero, requireSession, showSkipped, transcriptOverride } = opts;
|
|
136
136
|
const cwd = process.cwd();
|
|
137
137
|
const rules = loadRules(cwd);
|
|
138
138
|
if (rules.length === 0) {
|
|
@@ -229,8 +229,35 @@ async function runCheck(opts) {
|
|
|
229
229
|
if (html !== false) {
|
|
230
230
|
writeHtmlReport(results, { sessionFilePath, ruleCount: results.length }, cwd, html);
|
|
231
231
|
}
|
|
232
|
+
// A count alone is not enough. The classifier is a heuristic over English
|
|
233
|
+
// verbs: measured across 559 public rules files it drops non-English
|
|
234
|
+
// content at 97.5% against a 64.5% baseline, and any imperative verb
|
|
235
|
+
// outside its list is invisible to it. Neither gap closes by extending the
|
|
236
|
+
// list — imperative verbs are not a closed class, and the list is
|
|
237
|
+
// English-only by construction.
|
|
238
|
+
//
|
|
239
|
+
// What does close is the silence. "12 items were documentation" reads as
|
|
240
|
+
// reassurance; it is the one place this tool still guesses without saying
|
|
241
|
+
// so, and a rule dropped here never appears in the report at all. Listing
|
|
242
|
+
// them needs no key, works in any language, and lets the person who wrote
|
|
243
|
+
// the rule be the one who decides.
|
|
232
244
|
if (notARule.length > 0) {
|
|
233
|
-
|
|
245
|
+
const n = notARule.length;
|
|
246
|
+
const plural = n === 1 ? "" : "s";
|
|
247
|
+
console.log(`\n(${n} item${plural} in your rules file ${n === 1 ? "was" : "were"} treated as documentation and not checked — directory listings, reference tables, examples.)`);
|
|
248
|
+
if (showSkipped) {
|
|
249
|
+
console.log(`\nSkipped as documentation:\n`);
|
|
250
|
+
for (const { rule } of notARule) {
|
|
251
|
+
const label = rule.title.replace(/\s+/g, " ").trim();
|
|
252
|
+
console.log(` [${rule.id}] ${label.slice(0, 110)}`);
|
|
253
|
+
}
|
|
254
|
+
console.log(`\nIf any of those is actually a rule, the classifier was wrong. It looks for` +
|
|
255
|
+
`\nEnglish instruction words, so a rule written another way — or in another` +
|
|
256
|
+
`\nlanguage — can land here. Worth a look; you know your rules, it doesn't.`);
|
|
257
|
+
}
|
|
258
|
+
else {
|
|
259
|
+
console.log(`Run with --show-skipped to see them.`);
|
|
260
|
+
}
|
|
234
261
|
}
|
|
235
262
|
appendHistory(results, sessionFilePath);
|
|
236
263
|
if (share) {
|
|
@@ -288,6 +315,7 @@ program
|
|
|
288
315
|
.option("--html [path]", `write a shareable single-file HTML report you can email, attach to a ticket, or print to PDF. Defaults to ./${DEFAULT_HTML_REPORT_NAME}. Written locally — nothing is uploaded.`)
|
|
289
316
|
.option("--exit-zero", "always exit 0, even when a rule was broken. Without this, `check` exits 1 on any FAIL so CI can gate on it (rules needing human judgment report UNCLEAR and never affect the exit code).")
|
|
290
317
|
.option("--require-session", "fail (exit 1) if no session is found, or the session is empty, instead of reporting a pass for a check that never actually ran. Use this anywhere automated.")
|
|
318
|
+
.option("--show-skipped", "list the items that were treated as documentation and not checked. Worth running once on any rules file: the classifier is a heuristic over English verbs, so a rule it does not recognise is otherwise dropped without you seeing it.")
|
|
291
319
|
.option("--transcript <path>", "manual override: check this exact .jsonl session file instead of auto-detecting one. Useful if your Claude Code session lives somewhere non-standard that auto-detection doesn't cover.")
|
|
292
320
|
.action((opts) => {
|
|
293
321
|
runCheck({
|
|
@@ -301,6 +329,7 @@ program
|
|
|
301
329
|
html: opts.html ?? false,
|
|
302
330
|
exitZero: Boolean(opts.exitZero),
|
|
303
331
|
requireSession: Boolean(opts.requireSession),
|
|
332
|
+
showSkipped: Boolean(opts.showSkipped),
|
|
304
333
|
transcriptOverride: opts.transcript,
|
|
305
334
|
}).catch((err) => {
|
|
306
335
|
console.error("Something went wrong:", err instanceof Error ? err.message : err);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "rulereceipt",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.27",
|
|
4
4
|
"description": "Checks whether a Claude Code session actually followed your CLAUDE.md / AGENTS.md rules, with evidence.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -37,7 +37,8 @@
|
|
|
37
37
|
"test": "vitest run",
|
|
38
38
|
"test:watch": "vitest",
|
|
39
39
|
"lint": "eslint src tests",
|
|
40
|
-
"prepublishOnly": "npm run build && npm test"
|
|
40
|
+
"prepublishOnly": "npm run build && npm test",
|
|
41
|
+
"build:checker": "esbuild src/browser/analyze.ts --bundle --format=esm --minify --outfile=landing/checker.js"
|
|
41
42
|
},
|
|
42
43
|
"engines": {
|
|
43
44
|
"node": ">=18"
|