trimference 0.3.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.
Files changed (117) hide show
  1. package/BENCHMARK.md +39 -0
  2. package/CHANGELOG.md +41 -0
  3. package/CONTRIBUTING.md +43 -0
  4. package/DEMO.md +106 -0
  5. package/LICENSE +21 -0
  6. package/README.md +268 -0
  7. package/SECURITY.md +17 -0
  8. package/action.yml +182 -0
  9. package/dist/baseline.d.ts +13 -0
  10. package/dist/baseline.d.ts.map +1 -0
  11. package/dist/baseline.js +23 -0
  12. package/dist/baseline.js.map +1 -0
  13. package/dist/cli.d.ts +3 -0
  14. package/dist/cli.d.ts.map +1 -0
  15. package/dist/cli.js +245 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/config.d.ts +31 -0
  18. package/dist/config.d.ts.map +1 -0
  19. package/dist/config.js +127 -0
  20. package/dist/config.js.map +1 -0
  21. package/dist/ecoLLM.d.ts +33 -0
  22. package/dist/ecoLLM.d.ts.map +1 -0
  23. package/dist/ecoLLM.js +98 -0
  24. package/dist/ecoLLM.js.map +1 -0
  25. package/dist/impact.d.ts +22 -0
  26. package/dist/impact.d.ts.map +1 -0
  27. package/dist/impact.js +80 -0
  28. package/dist/impact.js.map +1 -0
  29. package/dist/index.d.ts +16 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +14 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/models.d.ts +21 -0
  34. package/dist/models.d.ts.map +1 -0
  35. package/dist/models.js +58 -0
  36. package/dist/models.js.map +1 -0
  37. package/dist/prComment.d.ts +36 -0
  38. package/dist/prComment.d.ts.map +1 -0
  39. package/dist/prComment.js +45 -0
  40. package/dist/prComment.js.map +1 -0
  41. package/dist/reporters/impactTracker.d.ts +21 -0
  42. package/dist/reporters/impactTracker.d.ts.map +1 -0
  43. package/dist/reporters/impactTracker.js +54 -0
  44. package/dist/reporters/impactTracker.js.map +1 -0
  45. package/dist/reporters/jsonReporter.d.ts +4 -0
  46. package/dist/reporters/jsonReporter.d.ts.map +1 -0
  47. package/dist/reporters/jsonReporter.js +34 -0
  48. package/dist/reporters/jsonReporter.js.map +1 -0
  49. package/dist/reporters/markdownReporter.d.ts +4 -0
  50. package/dist/reporters/markdownReporter.d.ts.map +1 -0
  51. package/dist/reporters/markdownReporter.js +97 -0
  52. package/dist/reporters/markdownReporter.js.map +1 -0
  53. package/dist/reporters/sarifReporter.d.ts +4 -0
  54. package/dist/reporters/sarifReporter.d.ts.map +1 -0
  55. package/dist/reporters/sarifReporter.js +63 -0
  56. package/dist/reporters/sarifReporter.js.map +1 -0
  57. package/dist/reporters/terminalReporter.d.ts +10 -0
  58. package/dist/reporters/terminalReporter.d.ts.map +1 -0
  59. package/dist/reporters/terminalReporter.js +80 -0
  60. package/dist/reporters/terminalReporter.js.map +1 -0
  61. package/dist/rules/agentLoopWithoutBudget.d.ts +3 -0
  62. package/dist/rules/agentLoopWithoutBudget.d.ts.map +1 -0
  63. package/dist/rules/agentLoopWithoutBudget.js +105 -0
  64. package/dist/rules/agentLoopWithoutBudget.js.map +1 -0
  65. package/dist/rules/expensiveModelSimpleTask.d.ts +3 -0
  66. package/dist/rules/expensiveModelSimpleTask.d.ts.map +1 -0
  67. package/dist/rules/expensiveModelSimpleTask.js +119 -0
  68. package/dist/rules/expensiveModelSimpleTask.js.map +1 -0
  69. package/dist/rules/frequentCron.d.ts +3 -0
  70. package/dist/rules/frequentCron.d.ts.map +1 -0
  71. package/dist/rules/frequentCron.js +64 -0
  72. package/dist/rules/frequentCron.js.map +1 -0
  73. package/dist/rules/helpers.d.ts +65 -0
  74. package/dist/rules/helpers.d.ts.map +1 -0
  75. package/dist/rules/helpers.js +383 -0
  76. package/dist/rules/helpers.js.map +1 -0
  77. package/dist/rules/hugeContext.d.ts +3 -0
  78. package/dist/rules/hugeContext.d.ts.map +1 -0
  79. package/dist/rules/hugeContext.js +74 -0
  80. package/dist/rules/hugeContext.js.map +1 -0
  81. package/dist/rules/imageGenerationLoop.d.ts +3 -0
  82. package/dist/rules/imageGenerationLoop.d.ts.map +1 -0
  83. package/dist/rules/imageGenerationLoop.js +72 -0
  84. package/dist/rules/imageGenerationLoop.js.map +1 -0
  85. package/dist/rules/index.d.ts +14 -0
  86. package/dist/rules/index.d.ts.map +1 -0
  87. package/dist/rules/index.js +24 -0
  88. package/dist/rules/index.js.map +1 -0
  89. package/dist/rules/missingRateLimit.d.ts +3 -0
  90. package/dist/rules/missingRateLimit.d.ts.map +1 -0
  91. package/dist/rules/missingRateLimit.js +78 -0
  92. package/dist/rules/missingRateLimit.js.map +1 -0
  93. package/dist/rules/noLlmCache.d.ts +3 -0
  94. package/dist/rules/noLlmCache.d.ts.map +1 -0
  95. package/dist/rules/noLlmCache.js +71 -0
  96. package/dist/rules/noLlmCache.js.map +1 -0
  97. package/dist/rules/noTokenLimit.d.ts +3 -0
  98. package/dist/rules/noTokenLimit.d.ts.map +1 -0
  99. package/dist/rules/noTokenLimit.js +59 -0
  100. package/dist/rules/noTokenLimit.js.map +1 -0
  101. package/dist/rules/repeatedEmbeddings.d.ts +3 -0
  102. package/dist/rules/repeatedEmbeddings.d.ts.map +1 -0
  103. package/dist/rules/repeatedEmbeddings.js +81 -0
  104. package/dist/rules/repeatedEmbeddings.js.map +1 -0
  105. package/dist/rules/sequentialLlmCalls.d.ts +3 -0
  106. package/dist/rules/sequentialLlmCalls.d.ts.map +1 -0
  107. package/dist/rules/sequentialLlmCalls.js +69 -0
  108. package/dist/rules/sequentialLlmCalls.js.map +1 -0
  109. package/dist/scanner.d.ts +23 -0
  110. package/dist/scanner.d.ts.map +1 -0
  111. package/dist/scanner.js +217 -0
  112. package/dist/scanner.js.map +1 -0
  113. package/dist/types.d.ts +93 -0
  114. package/dist/types.d.ts.map +1 -0
  115. package/dist/types.js +11 -0
  116. package/dist/types.js.map +1 -0
  117. package/package.json +75 -0
@@ -0,0 +1,217 @@
1
+ import { promises as fs } from "node:fs";
2
+ import path from "node:path";
3
+ import fg from "fast-glob";
4
+ import { IGNORE_GLOBS, SCANNABLE_EXTENSIONS } from "./config.js";
5
+ import { SEVERITY_WEIGHT, suggestedFirstPass } from "./impact.js";
6
+ import { rules } from "./rules/index.js";
7
+ import { applyInlineDisables, dedupeFindings, isTestFile } from "./rules/helpers.js";
8
+ /** High-impact rules whose findings should be softened inside test files. */
9
+ const HIGH_IMPACT_RULE_IDS = new Set([
10
+ "no-llm-cache",
11
+ "repeated-embeddings",
12
+ "image-generation-loop",
13
+ ]);
14
+ /** Discover scannable files under a directory (or a single file). */
15
+ export async function discoverFiles(rootPath) {
16
+ const stat = await fs.stat(rootPath).catch(() => null);
17
+ if (!stat) {
18
+ throw new Error(`Path does not exist: ${rootPath}`);
19
+ }
20
+ if (stat.isFile()) {
21
+ return isScannable(rootPath) ? [path.resolve(rootPath)] : [];
22
+ }
23
+ const extGroup = SCANNABLE_EXTENSIONS.map((e) => e.replace(/^\./, "")).join(",");
24
+ const pattern = `**/*.{${extGroup}}`;
25
+ const entries = await fg(pattern, {
26
+ cwd: rootPath,
27
+ ignore: IGNORE_GLOBS,
28
+ absolute: true,
29
+ dot: false,
30
+ followSymbolicLinks: false,
31
+ });
32
+ return entries.sort();
33
+ }
34
+ function isScannable(filePath) {
35
+ return SCANNABLE_EXTENSIONS.includes(path.extname(filePath).toLowerCase());
36
+ }
37
+ async function readSourceFile(absPath, rootPath) {
38
+ const content = await fs.readFile(absPath, "utf8");
39
+ // Prefer paths relative to the scanned root for readable reports.
40
+ const rel = path.relative(rootPath, absPath) || path.basename(absPath);
41
+ return {
42
+ path: normalizePath(rel),
43
+ content,
44
+ lines: content.split(/\r?\n/),
45
+ };
46
+ }
47
+ function normalizePath(p) {
48
+ return p.split(path.sep).join("/");
49
+ }
50
+ /** Run all rules against a single in-memory source file. */
51
+ export function runRules(file, context, ignoredRules, onRuleError) {
52
+ const findings = [];
53
+ for (const rule of rules) {
54
+ if (ignoredRules?.has(rule.id))
55
+ continue;
56
+ try {
57
+ findings.push(...rule.scan(file, context));
58
+ }
59
+ catch (error) {
60
+ // Keep scanning, but make the failure observable to callers/reporters.
61
+ onRuleError?.(rule.id, error);
62
+ }
63
+ }
64
+ // Soften high-impact findings inside test/spec files to keep reports credible.
65
+ const controlled = isTestFile(file.path)
66
+ ? findings.map(softenIfHighImpactTest)
67
+ : findings;
68
+ // Honor inline `// trimference-disable...` directives before deduping.
69
+ return dedupeFindings(applyInlineDisables(file, controlled));
70
+ }
71
+ function softenIfHighImpactTest(finding) {
72
+ if (finding.severity !== "high" || !HIGH_IMPACT_RULE_IDS.has(finding.ruleId)) {
73
+ return finding;
74
+ }
75
+ return {
76
+ ...finding,
77
+ severity: "low",
78
+ impact: {
79
+ ...finding.impact,
80
+ score: Math.min(finding.impact.score, 30),
81
+ confidence: "low",
82
+ },
83
+ };
84
+ }
85
+ export function sortFindings(findings) {
86
+ return [...findings].sort((a, b) => {
87
+ const sev = SEVERITY_WEIGHT[b.severity] - SEVERITY_WEIGHT[a.severity];
88
+ if (sev !== 0)
89
+ return sev;
90
+ const score = b.impact.score - a.impact.score;
91
+ if (score !== 0)
92
+ return score;
93
+ if (a.filePath !== b.filePath)
94
+ return a.filePath < b.filePath ? -1 : 1;
95
+ return a.line - b.line;
96
+ });
97
+ }
98
+ function meetsMinSeverity(severity, min) {
99
+ return SEVERITY_WEIGHT[severity] >= SEVERITY_WEIGHT[min];
100
+ }
101
+ function buildSummary(filesScanned, findings, baselineSuppressed = 0, ruleErrors = [], durationMs = 0) {
102
+ const high = findings.filter((f) => f.severity === "high").length;
103
+ const medium = findings.filter((f) => f.severity === "medium").length;
104
+ const low = findings.filter((f) => f.severity === "low").length;
105
+ const averageImpactScore = findings.length === 0
106
+ ? 0
107
+ : Math.round(findings.reduce((sum, f) => sum + f.impact.score, 0) / findings.length);
108
+ const findingsByRule = {};
109
+ const findingsByFile = {};
110
+ const findingsByCategory = {};
111
+ for (const f of findings) {
112
+ findingsByRule[f.ruleId] = (findingsByRule[f.ruleId] ?? 0) + 1;
113
+ findingsByFile[f.filePath] = (findingsByFile[f.filePath] ?? 0) + 1;
114
+ findingsByCategory[f.wasteCategory] =
115
+ (findingsByCategory[f.wasteCategory] ?? 0) + 1;
116
+ }
117
+ const topCategory = pickTopCategory(findingsByCategory);
118
+ const priorityPoints = high * 3 + medium * 2 + low;
119
+ return {
120
+ filesScanned,
121
+ durationMs,
122
+ totalFindings: findings.length,
123
+ high,
124
+ medium,
125
+ low,
126
+ averageImpactScore,
127
+ // Compatibility field. Unlike the old average, this cannot increase when
128
+ // a finding is fixed. Human-facing reports use counts and confidence.
129
+ overallImpactScore: Math.min(100, priorityPoints * 4),
130
+ priorityPoints,
131
+ baselineSuppressed,
132
+ ruleErrors,
133
+ topFindings: findings.slice(0, 3),
134
+ findingsByRule,
135
+ findingsByFile,
136
+ findingsByCategory,
137
+ topCategory,
138
+ suggestedFirstPass: suggestedFirstPass(findings),
139
+ };
140
+ }
141
+ function pickTopCategory(counts) {
142
+ let top = null;
143
+ let best = -1;
144
+ for (const [category, count] of Object.entries(counts)) {
145
+ if (count > best) {
146
+ best = count;
147
+ top = category;
148
+ }
149
+ }
150
+ return top;
151
+ }
152
+ /** Scan a path and return sorted findings plus a summary. */
153
+ export async function scan(options) {
154
+ const startedAt = Date.now();
155
+ const rootPath = path.resolve(options.path);
156
+ const minSeverity = options.minSeverity ?? "low";
157
+ const ignoredRules = new Set(options.ignoredRules ?? []);
158
+ const ignoredPaths = options.ignoredPaths ?? [];
159
+ const context = { provider: options.provider };
160
+ const baselineKeys = new Set(options.baselineKeys ?? []);
161
+ const includedPaths = options.includedPaths
162
+ ? new Set([...options.includedPaths].map(normalizePath))
163
+ : undefined;
164
+ const files = await discoverFiles(rootPath);
165
+ let allFindings = [];
166
+ const ruleErrors = [];
167
+ let scannedCount = 0;
168
+ for (const absPath of files) {
169
+ const file = await readSourceFile(absPath, rootPath);
170
+ if (includedPaths && !includedPaths.has(file.path))
171
+ continue;
172
+ if (isIgnoredPath(file.path, ignoredPaths))
173
+ continue;
174
+ scannedCount++;
175
+ allFindings.push(...runRules(file, context, ignoredRules, (ruleId, error) => {
176
+ ruleErrors.push({
177
+ ruleId,
178
+ filePath: file.path,
179
+ message: error instanceof Error ? error.message : String(error),
180
+ });
181
+ }));
182
+ }
183
+ allFindings = allFindings.filter((f) => meetsMinSeverity(f.severity, minSeverity));
184
+ const severityFilteredCount = allFindings.length;
185
+ allFindings = allFindings.filter((f) => !baselineKeys.has(findingFingerprint(f)));
186
+ const sorted = sortFindings(allFindings);
187
+ return {
188
+ summary: buildSummary(scannedCount, sorted, severityFilteredCount - allFindings.length, ruleErrors, Date.now() - startedAt),
189
+ findings: sorted,
190
+ };
191
+ }
192
+ /** Stable-enough key for accepting existing findings in a baseline file. */
193
+ export function findingFingerprint(finding) {
194
+ const evidence = (finding.snippet ?? "")
195
+ .replace(/\s+/g, " ")
196
+ .trim()
197
+ .slice(0, 160);
198
+ return [finding.ruleId, finding.filePath, evidence].join("::");
199
+ }
200
+ /** Match a relative path against glob patterns or backwards-compatible substrings. */
201
+ function isIgnoredPath(relPath, ignoredPaths) {
202
+ if (ignoredPaths.length === 0)
203
+ return false;
204
+ return ignoredPaths.some((entry) => {
205
+ const pattern = entry.replace(/^\.\//, "");
206
+ if (!/[?*]/.test(pattern))
207
+ return pattern.length > 0 && relPath.includes(pattern);
208
+ const escaped = pattern.replace(/[.+^${}()|[\]\\]/g, "\\$&");
209
+ const regex = escaped
210
+ .replace(/\*\*/g, "\u0000")
211
+ .replace(/\*/g, "[^/]*")
212
+ .replace(/\?/g, "[^/]")
213
+ .replace(/\u0000/g, ".*");
214
+ return new RegExp(`^(?:${regex})$`).test(relPath);
215
+ });
216
+ }
217
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,93 @@
1
+ export type Severity = "low" | "medium" | "high";
2
+ export type Level = "low" | "medium" | "high";
3
+ export type WasteCategory = "repeated-inference" | "token-bloat" | "model-overkill" | "redundant-embedding" | "unbounded-generation" | "background-compute-drift" | "multimodal-cost-explosion";
4
+ /** Human-readable labels for waste categories, used in reports. */
5
+ export declare const WASTE_CATEGORY_LABEL: Record<WasteCategory, string>;
6
+ export type ImpactEstimate = {
7
+ computeWaste: Level;
8
+ /** @deprecated Experimental legacy field; not shown in human reports. */
9
+ carbonImpact: Level;
10
+ /** @deprecated Experimental legacy field; not shown in human reports. */
11
+ waterImpact: Level;
12
+ costImpact: Level;
13
+ confidence: Level;
14
+ /** Directional impact score from 1 to 100. Higher means more potential avoidable waste. */
15
+ score: number;
16
+ explanation: string;
17
+ };
18
+ export type Finding = {
19
+ ruleId: string;
20
+ title: string;
21
+ severity: Severity;
22
+ wasteCategory: WasteCategory;
23
+ filePath: string;
24
+ line: number;
25
+ column?: number;
26
+ snippet?: string;
27
+ message: string;
28
+ recommendation: string;
29
+ /** Concrete remediation checklist. Not an automatic code patch. */
30
+ fixRecipe: string[];
31
+ impact: ImpactEstimate;
32
+ };
33
+ export type SourceFile = {
34
+ path: string;
35
+ content: string;
36
+ lines: string[];
37
+ };
38
+ /** Optional context passed to rules, e.g. a configured provider fallback. */
39
+ export type RuleContext = {
40
+ provider?: import("./models.js").Provider;
41
+ };
42
+ export type Rule = {
43
+ id: string;
44
+ title: string;
45
+ severity: Severity;
46
+ wasteCategory: WasteCategory;
47
+ description: string;
48
+ recommendation: string;
49
+ fixRecipe: string[];
50
+ scan: (file: SourceFile, context?: RuleContext) => Finding[];
51
+ };
52
+ export type ScanSummary = {
53
+ filesScanned: number;
54
+ durationMs: number;
55
+ totalFindings: number;
56
+ high: number;
57
+ medium: number;
58
+ low: number;
59
+ averageImpactScore: number;
60
+ /**
61
+ * @deprecated Retained for API compatibility. This is a monotonic heuristic
62
+ * priority index, not measured impact, and is not shown in human reports.
63
+ */
64
+ overallImpactScore: number;
65
+ /** Monotonic weighted finding total: high=3, medium=2, low=1. */
66
+ priorityPoints: number;
67
+ /** Findings hidden because they matched a supplied baseline. */
68
+ baselineSuppressed: number;
69
+ /** Rule failures encountered while scanning; empty during normal operation. */
70
+ ruleErrors: Array<{
71
+ ruleId: string;
72
+ filePath: string;
73
+ message: string;
74
+ }>;
75
+ topFindings: Finding[];
76
+ findingsByRule: Record<string, number>;
77
+ findingsByFile: Record<string, number>;
78
+ /** Counts keyed by WasteCategory, only for categories that appear. */
79
+ findingsByCategory: Partial<Record<WasteCategory, number>>;
80
+ /** The waste category with the most findings, or null when there are none. */
81
+ topCategory: WasteCategory | null;
82
+ /** Plain-English review-style actions derived from the top findings. */
83
+ suggestedFirstPass: string[];
84
+ };
85
+ export type ScanResult = {
86
+ summary: ScanSummary;
87
+ findings: Finding[];
88
+ };
89
+ export type ScanOptions = {
90
+ path: string;
91
+ minSeverity?: Severity;
92
+ };
93
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,11 @@
1
+ /** Human-readable labels for waste categories, used in reports. */
2
+ export const WASTE_CATEGORY_LABEL = {
3
+ "repeated-inference": "Repeated inference",
4
+ "token-bloat": "Token bloat",
5
+ "model-overkill": "Model right-sizing",
6
+ "redundant-embedding": "Redundant embedding",
7
+ "unbounded-generation": "Unbounded generation",
8
+ "background-compute-drift": "Frequent background work",
9
+ "multimodal-cost-explosion": "Repeated image generation",
10
+ };
11
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAYA,mEAAmE;AACnE,MAAM,CAAC,MAAM,oBAAoB,GAAkC;IACjE,oBAAoB,EAAE,oBAAoB;IAC1C,aAAa,EAAE,aAAa;IAC5B,gBAAgB,EAAE,oBAAoB;IACtC,qBAAqB,EAAE,qBAAqB;IAC5C,sBAAsB,EAAE,sBAAsB;IAC9C,0BAA0B,EAAE,0BAA0B;IACtD,2BAA2B,EAAE,2BAA2B;CACzD,CAAC"}
package/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "trimference",
3
+ "version": "0.3.0",
4
+ "description": "Local static analysis for LLM application efficiency safeguards.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "homepage": "https://github.com/psamme/trimference#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/psamme/trimference.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/psamme/trimference/issues"
14
+ },
15
+ "keywords": [
16
+ "ai",
17
+ "llm",
18
+ "cli",
19
+ "linter",
20
+ "static-analysis",
21
+ "developer-tools",
22
+ "llmops",
23
+ "cost-optimization",
24
+ "sarif",
25
+ "code-quality",
26
+ "github-actions",
27
+ "typescript"
28
+ ],
29
+ "bin": {
30
+ "trimference": "dist/cli.js"
31
+ },
32
+ "main": "dist/index.js",
33
+ "types": "dist/index.d.ts",
34
+ "files": [
35
+ "dist",
36
+ "action.yml",
37
+ "README.md",
38
+ "DEMO.md",
39
+ "CHANGELOG.md",
40
+ "CONTRIBUTING.md",
41
+ "SECURITY.md",
42
+ "BENCHMARK.md",
43
+ "LICENSE"
44
+ ],
45
+ "exports": {
46
+ ".": {
47
+ "types": "./dist/index.d.ts",
48
+ "import": "./dist/index.js"
49
+ }
50
+ },
51
+ "scripts": {
52
+ "build": "tsc",
53
+ "dev": "tsx src/cli.ts",
54
+ "test": "vitest run",
55
+ "test:watch": "vitest",
56
+ "scan:example": "tsx src/cli.ts scan --path examples/wasteful-ai-app",
57
+ "scan:self": "tsx src/cli.ts scan --path src --summary",
58
+ "verify": "npm run build && npm test && node dist/cli.js scan --path src --summary --fail-on low && node dist/cli.js scan --path examples/cleaner-ai-app --summary --fail-on low",
59
+ "prepublishOnly": "npm run verify"
60
+ },
61
+ "dependencies": {
62
+ "commander": "^12.1.0",
63
+ "fast-glob": "^3.3.2",
64
+ "picocolors": "^1.1.1"
65
+ },
66
+ "devDependencies": {
67
+ "@types/node": "^22.10.0",
68
+ "tsx": "^4.19.2",
69
+ "typescript": "^5.7.2",
70
+ "vitest": "^2.1.8"
71
+ },
72
+ "engines": {
73
+ "node": ">=18"
74
+ }
75
+ }