vigiles 2.0.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/.claude/settings.json +46 -0
- package/.claude/settings.local.json +8 -0
- package/.claude-plugin/hooks/post-edit.sh +34 -0
- package/.claude-plugin/hooks/pre-edit.sh +40 -0
- package/.claude-plugin/hooks/session-start.sh +38 -0
- package/.claude-plugin/marketplace.json +14 -0
- package/.claude-plugin/plugin.json +47 -0
- package/.github/workflows/ci.yml +81 -0
- package/.prettierignore +1 -0
- package/.vigiles/generated.d.ts +205 -0
- package/CLAUDE.md +95 -0
- package/CLAUDE.md.spec.ts +142 -0
- package/CONTRIBUTING.md +121 -0
- package/LICENSE +21 -0
- package/README.md +377 -0
- package/action.yml +25 -0
- package/dist/action.d.ts +7 -0
- package/dist/action.d.ts.map +1 -0
- package/dist/action.js +180 -0
- package/dist/action.js.map +1 -0
- package/dist/cli.d.ts +12 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +1267 -0
- package/dist/cli.js.map +1 -0
- package/dist/cli.test.d.ts +2 -0
- package/dist/cli.test.d.ts.map +1 -0
- package/dist/cli.test.js +650 -0
- package/dist/cli.test.js.map +1 -0
- package/dist/compile.d.ts +101 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +503 -0
- package/dist/compile.js.map +1 -0
- package/dist/evolve.d.ts +132 -0
- package/dist/evolve.d.ts.map +1 -0
- package/dist/evolve.js +599 -0
- package/dist/evolve.js.map +1 -0
- package/dist/freshness.d.ts +67 -0
- package/dist/freshness.d.ts.map +1 -0
- package/dist/freshness.js +244 -0
- package/dist/freshness.js.map +1 -0
- package/dist/freshness.test.d.ts +2 -0
- package/dist/freshness.test.d.ts.map +1 -0
- package/dist/freshness.test.js +356 -0
- package/dist/freshness.test.js.map +1 -0
- package/dist/generate-types.d.ts +34 -0
- package/dist/generate-types.d.ts.map +1 -0
- package/dist/generate-types.js +381 -0
- package/dist/generate-types.js.map +1 -0
- package/dist/inline.d.ts +58 -0
- package/dist/inline.d.ts.map +1 -0
- package/dist/inline.js +142 -0
- package/dist/inline.js.map +1 -0
- package/dist/inline.test.d.ts +5 -0
- package/dist/inline.test.d.ts.map +1 -0
- package/dist/inline.test.js +152 -0
- package/dist/inline.test.js.map +1 -0
- package/dist/linters.d.ts +38 -0
- package/dist/linters.d.ts.map +1 -0
- package/dist/linters.js +588 -0
- package/dist/linters.js.map +1 -0
- package/dist/proofs.d.ts +272 -0
- package/dist/proofs.d.ts.map +1 -0
- package/dist/proofs.js +622 -0
- package/dist/proofs.js.map +1 -0
- package/dist/proofs.test.d.ts +9 -0
- package/dist/proofs.test.d.ts.map +1 -0
- package/dist/proofs.test.js +952 -0
- package/dist/proofs.test.js.map +1 -0
- package/dist/spec.d.ts +258 -0
- package/dist/spec.d.ts.map +1 -0
- package/dist/spec.js +113 -0
- package/dist/spec.js.map +1 -0
- package/dist/spec.test.d.ts +2 -0
- package/dist/spec.test.d.ts.map +1 -0
- package/dist/spec.test.js +1222 -0
- package/dist/spec.test.js.map +1 -0
- package/dist/types.d.ts +101 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/validate.d.ts +10 -0
- package/dist/validate.d.ts.map +1 -0
- package/dist/validate.js +286 -0
- package/dist/validate.js.map +1 -0
- package/dist/validate.test.d.ts +2 -0
- package/dist/validate.test.d.ts.map +1 -0
- package/dist/validate.test.js +531 -0
- package/dist/validate.test.js.map +1 -0
- package/docs/agent-setup.md +85 -0
- package/docs/agent-workflows.md +103 -0
- package/docs/comparison.md +71 -0
- package/docs/freshness.md +124 -0
- package/docs/inline-mode.md +119 -0
- package/docs/linter-support.md +166 -0
- package/docs/spec-format.md +194 -0
- package/eslint.config.mjs +79 -0
- package/examples/CLAUDE.md +54 -0
- package/examples/CLAUDE.md.spec.ts +65 -0
- package/examples/SKILL.md +50 -0
- package/examples/SKILL.md.spec.ts +57 -0
- package/fixtures/example-project/CLAUDE.md +11 -0
- package/fixtures/example-project/package.json +9 -0
- package/fixtures/example-project/src/index.ts +3 -0
- package/fixtures/example-project/src/utils.test.ts +2 -0
- package/fixtures/example-project/src/utils.ts +3 -0
- package/logo.png +0 -0
- package/package.json +42 -0
- package/research/adoption-strategy.md +111 -0
- package/research/agent-integration.md +145 -0
- package/research/ai-code-quality.md +197 -0
- package/research/code-search-for-agents.md +313 -0
- package/research/competitive-landscape.md +163 -0
- package/research/doc-freshness.md +516 -0
- package/research/executable-specs.md +368 -0
- package/research/feature-ideas.md +464 -0
- package/research/formal-proofs-for-agents.md +338 -0
- package/research/fp-for-agent-harness.md +150 -0
- package/research/fp-for-deterministic-ai.md +131 -0
- package/research/self-evolving-specs.md +298 -0
- package/schemas/claude-md-strict.yml +18 -0
- package/schemas/claude-md.yml +6 -0
- package/schemas/skill-strict.yml +12 -0
- package/schemas/skill.yml +5 -0
- package/skills/audit-feedback-loop/SKILL.md +76 -0
- package/skills/edit-spec/SKILL.md +131 -0
- package/skills/enforce-rules-format/SKILL.md +71 -0
- package/skills/generate-logo/SKILL.md +102 -0
- package/skills/generate-rule/SKILL.md +90 -0
- package/skills/linter-docs/clippy.md +241 -0
- package/skills/linter-docs/eslint.md +384 -0
- package/skills/linter-docs/pylint.md +288 -0
- package/skills/linter-docs/rubocop.md +277 -0
- package/skills/linter-docs/ruff.md +187 -0
- package/skills/linter-docs/stylelint.md +247 -0
- package/skills/migrate-to-spec/SKILL.md +124 -0
- package/skills/pr-to-lint-rule/SKILL.md +97 -0
- package/skills/strengthen/SKILL.md +168 -0
- package/src/action.ts +214 -0
- package/src/cli.test.ts +914 -0
- package/src/cli.ts +1631 -0
- package/src/compile.ts +691 -0
- package/src/evolve.ts +781 -0
- package/src/freshness.test.ts +449 -0
- package/src/freshness.ts +299 -0
- package/src/generate-types.ts +448 -0
- package/src/inline.test.ts +206 -0
- package/src/inline.ts +164 -0
- package/src/linters.ts +739 -0
- package/src/proofs.test.ts +1314 -0
- package/src/proofs.ts +849 -0
- package/src/spec.test.ts +1471 -0
- package/src/spec.ts +427 -0
- package/src/types.ts +117 -0
- package/src/validate.test.ts +701 -0
- package/src/validate.ts +381 -0
- package/tsconfig.json +23 -0
package/src/validate.ts
ADDED
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
import { readFileSync, lstatSync, existsSync } from "node:fs";
|
|
2
|
+
import { globSync } from "glob";
|
|
3
|
+
import { resolve, basename as pathBasename } from "node:path";
|
|
4
|
+
import { cosmiconfigSync } from "cosmiconfig";
|
|
5
|
+
|
|
6
|
+
import { hasInlineRules } from "./inline.js";
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
ParsedRule,
|
|
10
|
+
ValidationError,
|
|
11
|
+
ValidationResult,
|
|
12
|
+
ReadResult,
|
|
13
|
+
FileResult,
|
|
14
|
+
ValidatePathsResult,
|
|
15
|
+
RulesConfig,
|
|
16
|
+
VigilesConfig,
|
|
17
|
+
MarkerType,
|
|
18
|
+
ParseOptions,
|
|
19
|
+
ValidateOptions,
|
|
20
|
+
ValidatePathsOptions,
|
|
21
|
+
ReadOptions,
|
|
22
|
+
} from "./types.js";
|
|
23
|
+
|
|
24
|
+
// Re-export all types for consumers
|
|
25
|
+
export type {
|
|
26
|
+
ParsedRule,
|
|
27
|
+
ValidationError,
|
|
28
|
+
ValidationResult,
|
|
29
|
+
ReadResult,
|
|
30
|
+
FileResult,
|
|
31
|
+
ValidatePathsResult,
|
|
32
|
+
RulesConfig,
|
|
33
|
+
VigilesConfig,
|
|
34
|
+
MarkerType,
|
|
35
|
+
ParseOptions,
|
|
36
|
+
ValidateOptions,
|
|
37
|
+
ValidatePathsOptions,
|
|
38
|
+
ReadOptions,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Constants & regex
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
const GUIDANCE_RE = /\*\*Guidance only\*\*/;
|
|
46
|
+
const DISABLE_RE = /<!--\s*vigiles-disable\s*-->/;
|
|
47
|
+
const RULE_HEADER_RE = /^###\s+(.+)$/;
|
|
48
|
+
const CHECKBOX_RE = /^- \[([ xX])\]\s+(.+)$/;
|
|
49
|
+
|
|
50
|
+
const VALID_MARKERS: readonly MarkerType[] = ["headings", "checkboxes"];
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Default config
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
const DEFAULT_FILES: string[] = ["CLAUDE.md"];
|
|
57
|
+
|
|
58
|
+
const DEFAULT_RULES: Required<RulesConfig> = {
|
|
59
|
+
"require-spec": "warn",
|
|
60
|
+
"require-skill-spec": "warn",
|
|
61
|
+
freshness: "warn",
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const DEFAULT_CONFIG: VigilesConfig = {
|
|
65
|
+
ruleMarkers: ["headings", "checkboxes"],
|
|
66
|
+
rules: DEFAULT_RULES,
|
|
67
|
+
files: DEFAULT_FILES,
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
// Instruction file discovery
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
|
|
74
|
+
export function findInstructionFiles(
|
|
75
|
+
cwd: string = process.cwd(),
|
|
76
|
+
configFiles?: string[],
|
|
77
|
+
): string[] {
|
|
78
|
+
const candidates = configFiles ?? DEFAULT_FILES;
|
|
79
|
+
return candidates.filter((f) => existsSync(resolve(cwd, f)));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ---------------------------------------------------------------------------
|
|
83
|
+
// Config loading
|
|
84
|
+
// ---------------------------------------------------------------------------
|
|
85
|
+
|
|
86
|
+
export function loadConfig(): VigilesConfig {
|
|
87
|
+
try {
|
|
88
|
+
const explorer = cosmiconfigSync("vigiles", {
|
|
89
|
+
searchPlaces: [".vigilesrc.json"],
|
|
90
|
+
mergeSearchPlaces: false,
|
|
91
|
+
});
|
|
92
|
+
const result = explorer.search();
|
|
93
|
+
if (!result?.config) return { ...DEFAULT_CONFIG };
|
|
94
|
+
|
|
95
|
+
const userConfig = result.config as Partial<VigilesConfig> & {
|
|
96
|
+
rules?: Partial<RulesConfig>;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const config: VigilesConfig = {
|
|
100
|
+
...DEFAULT_CONFIG,
|
|
101
|
+
...userConfig,
|
|
102
|
+
rules: { ...DEFAULT_RULES, ...userConfig.rules },
|
|
103
|
+
files: Array.isArray(userConfig.files) ? userConfig.files : DEFAULT_FILES,
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
if (
|
|
107
|
+
!Array.isArray(config.ruleMarkers) ||
|
|
108
|
+
!config.ruleMarkers.every((m): m is MarkerType =>
|
|
109
|
+
(VALID_MARKERS as readonly string[]).includes(m),
|
|
110
|
+
)
|
|
111
|
+
) {
|
|
112
|
+
console.warn(
|
|
113
|
+
`Invalid ruleMarkers in config: ${JSON.stringify(config.ruleMarkers)}. Using default.`,
|
|
114
|
+
);
|
|
115
|
+
config.ruleMarkers = [...DEFAULT_CONFIG.ruleMarkers];
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return config;
|
|
119
|
+
} catch {
|
|
120
|
+
return { ...DEFAULT_CONFIG };
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// ---------------------------------------------------------------------------
|
|
125
|
+
// Parsing
|
|
126
|
+
// ---------------------------------------------------------------------------
|
|
127
|
+
|
|
128
|
+
export function parseRules(
|
|
129
|
+
content: string,
|
|
130
|
+
{ ruleMarkers }: ParseOptions = {},
|
|
131
|
+
): ParsedRule[] {
|
|
132
|
+
const markers = ruleMarkers ?? DEFAULT_CONFIG.ruleMarkers;
|
|
133
|
+
const lines = content.split("\n");
|
|
134
|
+
const rules: ParsedRule[] = [];
|
|
135
|
+
|
|
136
|
+
let currentRule: ParsedRule | null = null;
|
|
137
|
+
|
|
138
|
+
for (let i = 0; i < lines.length; i++) {
|
|
139
|
+
const line = lines[i];
|
|
140
|
+
const headerMatch = markers.includes("headings")
|
|
141
|
+
? line.match(RULE_HEADER_RE)
|
|
142
|
+
: null;
|
|
143
|
+
const checkboxMatch = markers.includes("checkboxes")
|
|
144
|
+
? line.match(CHECKBOX_RE)
|
|
145
|
+
: null;
|
|
146
|
+
|
|
147
|
+
if (headerMatch ?? checkboxMatch) {
|
|
148
|
+
if (currentRule) {
|
|
149
|
+
rules.push(currentRule);
|
|
150
|
+
}
|
|
151
|
+
const title = headerMatch
|
|
152
|
+
? headerMatch[1].trim()
|
|
153
|
+
: (checkboxMatch as RegExpMatchArray)[2].trim();
|
|
154
|
+
currentRule = {
|
|
155
|
+
title,
|
|
156
|
+
line: i + 1,
|
|
157
|
+
enforcement: "missing",
|
|
158
|
+
enforcedBy: null,
|
|
159
|
+
};
|
|
160
|
+
continue;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (!currentRule || currentRule.enforcement !== "missing") continue;
|
|
164
|
+
|
|
165
|
+
const enforcedMatch = line.match(/\*\*Enforced by:\*\*\s*`([^`]+)`/);
|
|
166
|
+
if (enforcedMatch) {
|
|
167
|
+
currentRule.enforcement = "enforced";
|
|
168
|
+
currentRule.enforcedBy = enforcedMatch[1] ?? null;
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (GUIDANCE_RE.test(line)) {
|
|
173
|
+
currentRule.enforcement = "guidance";
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (DISABLE_RE.test(line)) {
|
|
178
|
+
currentRule.enforcement = "disabled";
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (currentRule) {
|
|
184
|
+
rules.push(currentRule);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return rules;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// ---------------------------------------------------------------------------
|
|
191
|
+
// Core validation
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
|
|
194
|
+
export function validate(
|
|
195
|
+
content: string,
|
|
196
|
+
{ ruleMarkers, rules: rulesConfig, filePath }: ValidateOptions = {},
|
|
197
|
+
): ValidationResult {
|
|
198
|
+
const activeRules = rulesConfig ?? DEFAULT_RULES;
|
|
199
|
+
const parsedRules = parseRules(content, { ruleMarkers });
|
|
200
|
+
const enforced = parsedRules.filter(
|
|
201
|
+
(r) => r.enforcement === "enforced",
|
|
202
|
+
).length;
|
|
203
|
+
const guidanceOnly = parsedRules.filter(
|
|
204
|
+
(r) => r.enforcement === "guidance",
|
|
205
|
+
).length;
|
|
206
|
+
const disabled = parsedRules.filter(
|
|
207
|
+
(r) => r.enforcement === "disabled",
|
|
208
|
+
).length;
|
|
209
|
+
const missingCount = parsedRules.filter(
|
|
210
|
+
(r) => r.enforcement === "missing",
|
|
211
|
+
).length;
|
|
212
|
+
|
|
213
|
+
const errors: ValidationError[] = [];
|
|
214
|
+
const warnings: ValidationError[] = [];
|
|
215
|
+
const disableComment = /<!--\s*vigiles-disable\s+require-spec\s*-->/;
|
|
216
|
+
|
|
217
|
+
if (filePath) {
|
|
218
|
+
const basename = pathBasename(filePath);
|
|
219
|
+
const isInstruction = basename === "CLAUDE.md" || basename === "AGENTS.md";
|
|
220
|
+
const isSkill = basename === "SKILL.md";
|
|
221
|
+
|
|
222
|
+
// --- require-spec (CLAUDE.md / AGENTS.md) ---
|
|
223
|
+
const specSeverity = activeRules["require-spec"];
|
|
224
|
+
if (specSeverity && isInstruction && !disableComment.test(content)) {
|
|
225
|
+
const specPath = filePath + ".spec.ts";
|
|
226
|
+
// Inline mode counts as a spec — any parseable
|
|
227
|
+
// `<!-- vigiles:enforce ... -->` comment means the file is
|
|
228
|
+
// verified on `vigiles audit` even without a .spec.ts sibling.
|
|
229
|
+
// Delegate to the real parser so a malformed marker can't
|
|
230
|
+
// satisfy require-spec with a rule that audit can't verify.
|
|
231
|
+
const hasInline = hasInlineRules(content);
|
|
232
|
+
if (!existsSync(specPath) && !hasInline) {
|
|
233
|
+
const msg: ValidationError = {
|
|
234
|
+
rule: "require-spec",
|
|
235
|
+
message: `No spec file found for "${filePath}". Expected "${specPath}". Run \`npx vigiles init --target=${filePath}\` to create one, add inline \`<!-- vigiles:enforce ... -->\` comments, or disable with <!-- vigiles-disable require-spec -->.`,
|
|
236
|
+
line: 1,
|
|
237
|
+
};
|
|
238
|
+
if (specSeverity === "error") {
|
|
239
|
+
errors.push(msg);
|
|
240
|
+
} else {
|
|
241
|
+
warnings.push(msg);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// --- require-skill-spec (SKILL.md) ---
|
|
247
|
+
const skillSeverity = activeRules["require-skill-spec"];
|
|
248
|
+
if (skillSeverity && isSkill && !disableComment.test(content)) {
|
|
249
|
+
const specPath = filePath + ".spec.ts";
|
|
250
|
+
if (!existsSync(specPath)) {
|
|
251
|
+
const msg: ValidationError = {
|
|
252
|
+
rule: "require-skill-spec",
|
|
253
|
+
message: `No spec file found for "${filePath}". Expected "${specPath}".`,
|
|
254
|
+
line: 1,
|
|
255
|
+
};
|
|
256
|
+
if (skillSeverity === "error") {
|
|
257
|
+
errors.push(msg);
|
|
258
|
+
} else {
|
|
259
|
+
warnings.push(msg);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return {
|
|
266
|
+
rules: parsedRules,
|
|
267
|
+
enforced,
|
|
268
|
+
guidanceOnly,
|
|
269
|
+
disabled,
|
|
270
|
+
missing: missingCount,
|
|
271
|
+
total: parsedRules.length,
|
|
272
|
+
errors,
|
|
273
|
+
warnings,
|
|
274
|
+
valid: errors.length === 0,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ---------------------------------------------------------------------------
|
|
279
|
+
// File reading
|
|
280
|
+
// ---------------------------------------------------------------------------
|
|
281
|
+
|
|
282
|
+
export function readInstructionFile(
|
|
283
|
+
filePath: string,
|
|
284
|
+
options: ReadOptions = {},
|
|
285
|
+
): ReadResult {
|
|
286
|
+
try {
|
|
287
|
+
const stat = lstatSync(filePath);
|
|
288
|
+
if (stat.isSymbolicLink() && !options.followSymlinks) {
|
|
289
|
+
return {
|
|
290
|
+
content: null,
|
|
291
|
+
skipped: true,
|
|
292
|
+
reason: `${filePath} is a symlink (use --follow-symlinks to include)`,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
} catch {
|
|
296
|
+
return {
|
|
297
|
+
content: null,
|
|
298
|
+
skipped: false,
|
|
299
|
+
reason: `File not found: ${filePath}`,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
try {
|
|
304
|
+
return {
|
|
305
|
+
content: readFileSync(filePath, "utf-8"),
|
|
306
|
+
skipped: false,
|
|
307
|
+
reason: null,
|
|
308
|
+
};
|
|
309
|
+
} catch {
|
|
310
|
+
return {
|
|
311
|
+
content: null,
|
|
312
|
+
skipped: false,
|
|
313
|
+
reason: `Could not read: ${filePath}`,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// ---------------------------------------------------------------------------
|
|
319
|
+
// Glob expansion
|
|
320
|
+
// ---------------------------------------------------------------------------
|
|
321
|
+
|
|
322
|
+
export function expandGlobs(patterns: string[]): string[] {
|
|
323
|
+
const GLOB_CHARS = /[*?{[]/;
|
|
324
|
+
const paths: string[] = [];
|
|
325
|
+
|
|
326
|
+
for (const pattern of patterns) {
|
|
327
|
+
if (GLOB_CHARS.test(pattern)) {
|
|
328
|
+
const matches = globSync(pattern, { cwd: process.cwd() });
|
|
329
|
+
for (const match of matches.sort()) {
|
|
330
|
+
paths.push(resolve(match));
|
|
331
|
+
}
|
|
332
|
+
} else {
|
|
333
|
+
paths.push(pattern);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return paths;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// ---------------------------------------------------------------------------
|
|
341
|
+
// Multi-file validation
|
|
342
|
+
// ---------------------------------------------------------------------------
|
|
343
|
+
|
|
344
|
+
export function validatePaths(
|
|
345
|
+
paths: string[],
|
|
346
|
+
{
|
|
347
|
+
followSymlinks = false,
|
|
348
|
+
ruleMarkers,
|
|
349
|
+
rules: rulesConfig,
|
|
350
|
+
}: ValidatePathsOptions = {},
|
|
351
|
+
): ValidatePathsResult {
|
|
352
|
+
const fileResults: FileResult[] = [];
|
|
353
|
+
let allValid = true;
|
|
354
|
+
|
|
355
|
+
for (const filePath of paths) {
|
|
356
|
+
const { content, skipped, reason } = readInstructionFile(filePath, {
|
|
357
|
+
followSymlinks,
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
if (skipped || content === null) {
|
|
361
|
+
fileResults.push({
|
|
362
|
+
path: filePath,
|
|
363
|
+
skipped,
|
|
364
|
+
reason,
|
|
365
|
+
result: null,
|
|
366
|
+
});
|
|
367
|
+
if (!skipped) allValid = false;
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const result = validate(content, {
|
|
372
|
+
ruleMarkers,
|
|
373
|
+
rules: rulesConfig,
|
|
374
|
+
filePath,
|
|
375
|
+
});
|
|
376
|
+
fileResults.push({ path: filePath, skipped: false, reason: null, result });
|
|
377
|
+
if (!result.valid) allValid = false;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
return { fileResults, valid: allValid };
|
|
381
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2022",
|
|
4
|
+
"module": "Node16",
|
|
5
|
+
"moduleResolution": "Node16",
|
|
6
|
+
"outDir": "dist",
|
|
7
|
+
"rootDir": "src",
|
|
8
|
+
"declaration": true,
|
|
9
|
+
"declarationMap": true,
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"strict": true,
|
|
12
|
+
"noUncheckedIndexedAccess": false,
|
|
13
|
+
"noUnusedLocals": true,
|
|
14
|
+
"noUnusedParameters": true,
|
|
15
|
+
"exactOptionalPropertyTypes": false,
|
|
16
|
+
"forceConsistentCasingInFileNames": true,
|
|
17
|
+
"skipLibCheck": true,
|
|
18
|
+
"esModuleInterop": true,
|
|
19
|
+
"resolveJsonModule": true
|
|
20
|
+
},
|
|
21
|
+
"include": ["src/**/*.ts"],
|
|
22
|
+
"exclude": ["node_modules", "dist"]
|
|
23
|
+
}
|