cachelint 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/LICENSE +27 -0
- package/LICENSE-PRO.md +67 -0
- package/README.md +298 -0
- package/dist/check-2ONJCYKP.js +23 -0
- package/dist/chunk-2JSY5BQA.js +340 -0
- package/dist/chunk-4GHSUBAY.js +28 -0
- package/dist/chunk-4LB5LF2P.js +51 -0
- package/dist/chunk-BUZCVKMZ.js +246 -0
- package/dist/chunk-DYQPXD3G.js +92 -0
- package/dist/chunk-GXASKZ4Z.js +182 -0
- package/dist/chunk-JZU4PCTS.js +80 -0
- package/dist/chunk-MW7BXKPR.js +593 -0
- package/dist/chunk-O7ZE6CFI.js +32 -0
- package/dist/chunk-SQ4IAMTX.js +65 -0
- package/dist/chunk-UUVSBJ62.js +77 -0
- package/dist/chunk-VWE5TDL5.js +91 -0
- package/dist/chunk-WKFGRNYK.js +494 -0
- package/dist/chunk-WPJYUQMU.js +26 -0
- package/dist/chunk-XKRPGMZT.js +197 -0
- package/dist/chunk-ZSEZQ422.js +286 -0
- package/dist/cli.d.ts +46 -0
- package/dist/cli.js +199 -0
- package/dist/gate-63NKQRXL.js +16 -0
- package/dist/hash-XUTGK6SB.js +15 -0
- package/dist/index.d.ts +1200 -0
- package/dist/index.js +184 -0
- package/dist/license-GFDBTQHG.js +19 -0
- package/dist/lint-PQHIDWOY.js +19 -0
- package/dist/sarif-EQ6VJNGN.js +63 -0
- package/dist/step-summary-NKD4N7AY.js +160 -0
- package/dist/store-KZIQANIT.js +14 -0
- package/package.json +85 -0
- package/schema/cachelint.config.schema.json +114 -0
package/dist/cli.js
ADDED
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
DEFAULT_EXACT_MODEL
|
|
4
|
+
} from "./chunk-UUVSBJ62.js";
|
|
5
|
+
import {
|
|
6
|
+
REFERENCE_MODEL
|
|
7
|
+
} from "./chunk-O7ZE6CFI.js";
|
|
8
|
+
import {
|
|
9
|
+
CACHELINT_VERSION
|
|
10
|
+
} from "./chunk-WPJYUQMU.js";
|
|
11
|
+
import {
|
|
12
|
+
EXIT_CHECK_FAILED,
|
|
13
|
+
EXIT_CONFIG_OR_SOURCE_ERROR,
|
|
14
|
+
EXIT_LOCK_PROBLEM,
|
|
15
|
+
EXIT_OK,
|
|
16
|
+
EXIT_PRO_REQUIRED,
|
|
17
|
+
exitCodeFor,
|
|
18
|
+
renderError
|
|
19
|
+
} from "./chunk-XKRPGMZT.js";
|
|
20
|
+
|
|
21
|
+
// src/cli.ts
|
|
22
|
+
import { Command } from "commander";
|
|
23
|
+
import { pathToFileURL } from "url";
|
|
24
|
+
import { realpathSync } from "fs";
|
|
25
|
+
var program = new Command();
|
|
26
|
+
var currentCommandContext = null;
|
|
27
|
+
program.name("cachelint").description(
|
|
28
|
+
"A linter + CI gate for LLM prompt-cache regressions. Catches the silent-invalidator class (interpolated timestamps, unsorted JSON, BOM, mixed line endings) and fails PRs when a committed prompt's stable-prefix hash moves."
|
|
29
|
+
).version(CACHELINT_VERSION, "-v, --version", "Print the cachelint version").option("--no-color", "Disable ANSI color (NO_COLOR env is also honored)").option(
|
|
30
|
+
"--no-cost",
|
|
31
|
+
"Disable cost estimates in all output (human, --json cost block). Affects lint/check output only; a global flag \u2014 place it before the subcommand"
|
|
32
|
+
).option("--verbose", "Verbose output (still redacts secrets)").option("--quiet", "Suppress non-error output").showHelpAfterError(true);
|
|
33
|
+
program.command("lint [paths...]").description(
|
|
34
|
+
"Lint prompt content files for silent-invalidator patterns. Output includes cost estimates by default (disable with the global --no-cost); on CI with GITHUB_STEP_SUMMARY set, a markdown summary section is also written (opt out: CACHELINT_NO_STEP_SUMMARY)."
|
|
35
|
+
).option("-c, --config <file>", "Path to cachelint.config.{json,yaml,yml}").option(
|
|
36
|
+
"--json",
|
|
37
|
+
"Emit results as JSON (stable schema) on stdout. Includes an optional `cost` block (absent under --no-cost or on cost degradation); only cost.totals is summable"
|
|
38
|
+
).option("--sarif <path>", "Write SARIF v2.1 for GitHub Code Scanning to <path> [Pro]").option("--strict", "Treat warn-severity findings as build failures").option("--pack <id>", "Load a provider bug-pattern pack (e.g. anthropic-2026-q1) [Pro]").action(async (paths, opts) => {
|
|
39
|
+
const costEnabled = program.opts().cost !== false;
|
|
40
|
+
currentCommandContext = {
|
|
41
|
+
command: "lint",
|
|
42
|
+
json: opts.json === true,
|
|
43
|
+
cost: costEnabled
|
|
44
|
+
};
|
|
45
|
+
const { lint, renderLintHuman, renderLintJson } = await import("./lint-PQHIDWOY.js");
|
|
46
|
+
const result = lint({
|
|
47
|
+
paths,
|
|
48
|
+
...opts.config !== void 0 ? { config: opts.config } : {},
|
|
49
|
+
...opts.strict !== void 0 ? { strict: opts.strict } : {},
|
|
50
|
+
...opts.pack !== void 0 ? { pack: opts.pack } : {},
|
|
51
|
+
// Global `--no-cost` (commander negation: opts.cost defaults to true).
|
|
52
|
+
// Passed only when disabling — absent means the library default (on).
|
|
53
|
+
...costEnabled ? {} : { cost: false }
|
|
54
|
+
});
|
|
55
|
+
if (opts.sarif !== void 0) {
|
|
56
|
+
const { requirePro } = await import("./gate-63NKQRXL.js");
|
|
57
|
+
requirePro("sarif", "`--sarif` output");
|
|
58
|
+
const { sarifString } = await import("./sarif-EQ6VJNGN.js");
|
|
59
|
+
const { writeFileSync } = await import("fs");
|
|
60
|
+
writeFileSync(opts.sarif, sarifString(result.diagnostics));
|
|
61
|
+
console.error(`cachelint: wrote SARIF to ${opts.sarif}`);
|
|
62
|
+
}
|
|
63
|
+
if (opts.json === true) {
|
|
64
|
+
process.stdout.write(renderLintJson(result));
|
|
65
|
+
} else {
|
|
66
|
+
process.stderr.write(renderLintHuman(result, { color: colorEnabled() }) + "\n");
|
|
67
|
+
}
|
|
68
|
+
const { writeLintStepSummary } = await import("./step-summary-NKD4N7AY.js");
|
|
69
|
+
writeLintStepSummary(result);
|
|
70
|
+
if (result.exitCode !== EXIT_OK) process.exitCode = result.exitCode;
|
|
71
|
+
});
|
|
72
|
+
program.command("hash [paths...]").description("Compute and write cachelint.lock for declared bundles").option("-c, --config <file>", "Path to cachelint.config.{json,yaml,yml}").option("-o, --out <file>", "Output lockfile path (default: cachelint.lock)").option("--json", "Emit the written bundles as JSON on stdout").action(async (paths, opts) => {
|
|
73
|
+
const { hash, renderHashHuman, renderHashJson } = await import("./hash-XUTGK6SB.js");
|
|
74
|
+
const r = hash({
|
|
75
|
+
paths,
|
|
76
|
+
...opts.config !== void 0 ? { config: opts.config } : {},
|
|
77
|
+
...opts.out !== void 0 ? { out: opts.out } : {}
|
|
78
|
+
});
|
|
79
|
+
if (opts.json === true) process.stdout.write(renderHashJson(r, process.cwd()));
|
|
80
|
+
else process.stderr.write(renderHashHuman(r, process.cwd()) + "\n");
|
|
81
|
+
});
|
|
82
|
+
program.command("check [paths...]").description(
|
|
83
|
+
"Verify cachelint.lock against current sources + run the rules (the CI gate). Output includes cost estimates by default (disable with the global --no-cost); on CI with GITHUB_STEP_SUMMARY set, a markdown summary section is also written (opt out: CACHELINT_NO_STEP_SUMMARY)."
|
|
84
|
+
).option("-c, --config <file>", "Path to cachelint.config.{json,yaml,yml}").option("--lockfile <file>", "Lockfile path (default: cachelint.lock)").option("--strict", "Treat warn-severity findings as failures").option("--exact", "Use the Anthropic count-tokens API for authoritative counts [Pro]").option("--exact-limit <n>", "Max --exact API calls per run (default 100)", (v) => Number.parseInt(v, 10)).option(
|
|
85
|
+
"--exact-model <id>",
|
|
86
|
+
`Model whose tokenizer to use for --exact (default ${DEFAULT_EXACT_MODEL}). Dollar figures always use the ${REFERENCE_MODEL} pricing basis regardless of this flag`
|
|
87
|
+
).option("--pack <id>", "Load a provider bug-pattern pack (e.g. anthropic-2026-q1) [Pro]").option(
|
|
88
|
+
"--json",
|
|
89
|
+
"Emit the diff + diagnostics as JSON on stdout. Includes an optional `cost` block (absent under --no-cost or on cost degradation); only cost.totals is summable"
|
|
90
|
+
).action(async (paths, opts) => {
|
|
91
|
+
const costEnabled = program.opts().cost !== false;
|
|
92
|
+
currentCommandContext = {
|
|
93
|
+
command: "check",
|
|
94
|
+
json: opts.json === true,
|
|
95
|
+
cost: costEnabled
|
|
96
|
+
};
|
|
97
|
+
const { check, renderCheckHuman, renderCheckJson } = await import("./check-2ONJCYKP.js");
|
|
98
|
+
const r = await check({
|
|
99
|
+
paths,
|
|
100
|
+
...opts.config !== void 0 ? { config: opts.config } : {},
|
|
101
|
+
...opts.lockfile !== void 0 ? { lockfile: opts.lockfile } : {},
|
|
102
|
+
...opts.strict !== void 0 ? { strict: opts.strict } : {},
|
|
103
|
+
...opts.exact !== void 0 ? { exact: opts.exact } : {},
|
|
104
|
+
...opts.exactLimit !== void 0 && Number.isFinite(opts.exactLimit) ? { exactLimit: opts.exactLimit } : {},
|
|
105
|
+
...opts.exactModel !== void 0 ? { exactModel: opts.exactModel } : {},
|
|
106
|
+
...opts.pack !== void 0 ? { pack: opts.pack } : {},
|
|
107
|
+
// Global `--no-cost` (commander negation: opts.cost defaults to true).
|
|
108
|
+
// Passed only when disabling — absent means the library default (on).
|
|
109
|
+
...costEnabled ? {} : { cost: false }
|
|
110
|
+
});
|
|
111
|
+
if (opts.json === true) process.stdout.write(renderCheckJson(r, process.cwd()));
|
|
112
|
+
else process.stderr.write(renderCheckHuman(r) + "\n");
|
|
113
|
+
const { writeCheckStepSummary } = await import("./step-summary-NKD4N7AY.js");
|
|
114
|
+
writeCheckStepSummary(r);
|
|
115
|
+
if (r.exitCode !== EXIT_OK) process.exitCode = r.exitCode;
|
|
116
|
+
});
|
|
117
|
+
program.command("activate <license-key>").description("Verify and store a Pro license key (offline Ed25519 \u2014 no network)").action(async (key) => {
|
|
118
|
+
const { activate, renderActivateHuman } = await import("./license-GFDBTQHG.js");
|
|
119
|
+
const r = activate(key);
|
|
120
|
+
process.stderr.write(renderActivateHuman(r) + "\n");
|
|
121
|
+
});
|
|
122
|
+
program.command("license").description("Inspect the active license (plan, buyer, expiry, features)").argument("[subcommand]", "status (default)", "status").action(async (sub) => {
|
|
123
|
+
const { licenseStatus, renderLicenseStatusHuman } = await import("./license-GFDBTQHG.js");
|
|
124
|
+
if (sub !== "status") {
|
|
125
|
+
console.error(`cachelint license: unknown subcommand "${sub}" (try: status)`);
|
|
126
|
+
process.exitCode = EXIT_CONFIG_OR_SOURCE_ERROR;
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const s = licenseStatus();
|
|
130
|
+
process.stderr.write(renderLicenseStatusHuman(s) + "\n");
|
|
131
|
+
});
|
|
132
|
+
program.command("deactivate").description("Remove the stored license key (requires --yes to confirm)").option("--yes", "Confirm removal of the stored license key").action(async (opts) => {
|
|
133
|
+
const { deactivate, renderDeactivateHuman } = await import("./license-GFDBTQHG.js");
|
|
134
|
+
const { licenseFilePath } = await import("./store-KZIQANIT.js");
|
|
135
|
+
if (opts.yes !== true) {
|
|
136
|
+
process.stderr.write(`cachelint: would remove the stored license at ${licenseFilePath()} \u2014 re-run with --yes to confirm.
|
|
137
|
+
`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const r = deactivate();
|
|
141
|
+
process.stderr.write(renderDeactivateHuman(r) + "\n");
|
|
142
|
+
});
|
|
143
|
+
function colorEnabled() {
|
|
144
|
+
if (program.opts().color === false) return false;
|
|
145
|
+
if (process.env["NO_COLOR"] != null) return false;
|
|
146
|
+
return process.stderr.isTTY === true;
|
|
147
|
+
}
|
|
148
|
+
async function runCli(argv = process.argv) {
|
|
149
|
+
process.stdout.on("error", (err) => {
|
|
150
|
+
if (err.code === "EPIPE") process.exit(0);
|
|
151
|
+
});
|
|
152
|
+
const rest = argv.slice(2);
|
|
153
|
+
if (rest.length === 1 && (rest[0] === "--version" || rest[0] === "-v")) {
|
|
154
|
+
process.stdout.write(CACHELINT_VERSION + "\n");
|
|
155
|
+
try {
|
|
156
|
+
const { activeLicenseInfo } = await import("./gate-63NKQRXL.js");
|
|
157
|
+
const info = activeLicenseInfo();
|
|
158
|
+
if (info) {
|
|
159
|
+
process.stdout.write(` Pro license \u2014 licensed to ${info.sub}${info.inGracePeriod ? " (in grace period)" : ""}
|
|
160
|
+
`);
|
|
161
|
+
}
|
|
162
|
+
} catch {
|
|
163
|
+
}
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
currentCommandContext = null;
|
|
167
|
+
try {
|
|
168
|
+
await program.parseAsync(argv);
|
|
169
|
+
} catch (err) {
|
|
170
|
+
const verbose = program.opts().verbose === true;
|
|
171
|
+
console.error(renderError(err, { verbose }));
|
|
172
|
+
process.exitCode = exitCodeFor(err);
|
|
173
|
+
if (currentCommandContext !== null) {
|
|
174
|
+
try {
|
|
175
|
+
const { writeFailureStepSummary } = await import("./step-summary-NKD4N7AY.js");
|
|
176
|
+
writeFailureStepSummary(currentCommandContext, exitCodeFor(err), err);
|
|
177
|
+
} catch {
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
var argv1 = process.argv[1];
|
|
183
|
+
if (argv1 !== void 0) {
|
|
184
|
+
let entryHref;
|
|
185
|
+
try {
|
|
186
|
+
entryHref = pathToFileURL(realpathSync(argv1)).href;
|
|
187
|
+
} catch {
|
|
188
|
+
entryHref = pathToFileURL(argv1).href;
|
|
189
|
+
}
|
|
190
|
+
if (entryHref === import.meta.url) void runCli();
|
|
191
|
+
}
|
|
192
|
+
export {
|
|
193
|
+
EXIT_CHECK_FAILED,
|
|
194
|
+
EXIT_CONFIG_OR_SOURCE_ERROR,
|
|
195
|
+
EXIT_LOCK_PROBLEM,
|
|
196
|
+
EXIT_OK,
|
|
197
|
+
EXIT_PRO_REQUIRED,
|
|
198
|
+
runCli
|
|
199
|
+
};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PRO_UPGRADE_HINT,
|
|
3
|
+
_resetLicenseCache,
|
|
4
|
+
activeLicenseInfo,
|
|
5
|
+
isPro,
|
|
6
|
+
requirePro
|
|
7
|
+
} from "./chunk-GXASKZ4Z.js";
|
|
8
|
+
import "./chunk-4LB5LF2P.js";
|
|
9
|
+
import "./chunk-XKRPGMZT.js";
|
|
10
|
+
export {
|
|
11
|
+
PRO_UPGRADE_HINT,
|
|
12
|
+
_resetLicenseCache,
|
|
13
|
+
activeLicenseInfo,
|
|
14
|
+
isPro,
|
|
15
|
+
requirePro
|
|
16
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
hash,
|
|
3
|
+
renderHashHuman,
|
|
4
|
+
renderHashJson
|
|
5
|
+
} from "./chunk-SQ4IAMTX.js";
|
|
6
|
+
import "./chunk-BUZCVKMZ.js";
|
|
7
|
+
import "./chunk-MW7BXKPR.js";
|
|
8
|
+
import "./chunk-4GHSUBAY.js";
|
|
9
|
+
import "./chunk-WPJYUQMU.js";
|
|
10
|
+
import "./chunk-XKRPGMZT.js";
|
|
11
|
+
export {
|
|
12
|
+
hash,
|
|
13
|
+
renderHashHuman,
|
|
14
|
+
renderHashJson
|
|
15
|
+
};
|