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/inline.ts
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* vigiles — Inline rule mode for gradual adoption.
|
|
3
|
+
*
|
|
4
|
+
* Parses `<!-- vigiles:enforce <linter>/<rule> "<why>" -->` HTML comments
|
|
5
|
+
* out of any markdown file, so a project can adopt vigiles one rule at a
|
|
6
|
+
* time without committing to a .spec.ts compile step. Every inline rule
|
|
7
|
+
* goes through the same `checkLinterRule` verification as rules declared
|
|
8
|
+
* in a .spec.ts — typos get closest-match suggestions, disabled rules are
|
|
9
|
+
* flagged, etc.
|
|
10
|
+
*
|
|
11
|
+
* Only `enforce` is supported inline. `guidance` would be a tautology: if
|
|
12
|
+
* you're editing the markdown, the prose around the comment already is
|
|
13
|
+
* the guidance — there's nothing extra for a tool to render.
|
|
14
|
+
*
|
|
15
|
+
* A file is in "inline mode" iff it contains at least one vigiles:enforce
|
|
16
|
+
* comment. Spec mode (`.md.spec.ts` sibling) takes precedence if both
|
|
17
|
+
* exist; the spec compiler emits a fresh file that may overwrite the
|
|
18
|
+
* inline markup, so users should pick one per file.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
export interface InlineRule {
|
|
22
|
+
/** Linter rule reference, e.g. "eslint/no-console". */
|
|
23
|
+
linterRule: string;
|
|
24
|
+
/** Why this rule is enforced (human-readable, shown in agent context). */
|
|
25
|
+
why: string;
|
|
26
|
+
/** 1-based line number of the comment in the source file. */
|
|
27
|
+
line: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface InlineParseResult {
|
|
31
|
+
rules: InlineRule[];
|
|
32
|
+
/** Lines that look like vigiles: markers but failed to parse. */
|
|
33
|
+
errors: { line: number; message: string; raw: string }[];
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Match `<!-- vigiles:enforce <linter>/<rule> "<why>" -->`. The linter
|
|
38
|
+
* reference allows the same characters as the TS-side `SAFE_RULE_NAME_RE`
|
|
39
|
+
* plus `@` for scoped plugin names (e.g. `@typescript-eslint/...`).
|
|
40
|
+
* The `why` is a simple quoted string — if someone wants newlines or
|
|
41
|
+
* embedded quotes, they can move to spec mode.
|
|
42
|
+
*/
|
|
43
|
+
const ENFORCE_RE =
|
|
44
|
+
/<!--\s*vigiles:enforce\s+([@A-Za-z0-9_/:.-]+)\s+"([^"\n]*)"\s*-->/;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Detects any `<!-- vigiles:<kind> -->` comment (valid or not) so we can
|
|
48
|
+
* surface errors for typos and reserved-but-unrecognized kinds. Uses a
|
|
49
|
+
* non-greedy match for the tail so a `-` inside the kind doesn't short-
|
|
50
|
+
* circuit the pattern.
|
|
51
|
+
*/
|
|
52
|
+
const MARKER_RE = /<!--\s*vigiles:([A-Za-z_-]+)[^]*?-->/;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Parse inline vigiles rules out of a markdown file's contents.
|
|
56
|
+
* Does not touch the filesystem and does not verify the rules against
|
|
57
|
+
* any linter — callers can feed the returned rules into
|
|
58
|
+
* `checkLinterRule` themselves.
|
|
59
|
+
*
|
|
60
|
+
* Lines inside fenced code blocks (``` ... ``` or ~~~ ... ~~~) are
|
|
61
|
+
* skipped so illustrative examples in docs don't get treated as live
|
|
62
|
+
* rules.
|
|
63
|
+
*/
|
|
64
|
+
export function parseInlineRules(content: string): InlineParseResult {
|
|
65
|
+
const rules: InlineRule[] = [];
|
|
66
|
+
const errors: InlineParseResult["errors"] = [];
|
|
67
|
+
|
|
68
|
+
const lines = content.split("\n");
|
|
69
|
+
let fenceChar: "`" | "~" | null = null;
|
|
70
|
+
let fenceLen = 0;
|
|
71
|
+
|
|
72
|
+
for (let i = 0; i < lines.length; i++) {
|
|
73
|
+
const line = lines[i];
|
|
74
|
+
|
|
75
|
+
// Track fenced code blocks. CommonMark allows ``` or ~~~ fences
|
|
76
|
+
// with 3+ characters; the closing fence must use the same char and
|
|
77
|
+
// have length >= the opening fence's length. Info-string tokens
|
|
78
|
+
// after the opener are allowed.
|
|
79
|
+
const fenceMatch = /^(\s{0,3})(`{3,}|~{3,})(.*)$/.exec(line);
|
|
80
|
+
if (fenceMatch) {
|
|
81
|
+
const marker = fenceMatch[2];
|
|
82
|
+
const ch = marker[0] as "`" | "~";
|
|
83
|
+
const len = marker.length;
|
|
84
|
+
if (fenceChar === null) {
|
|
85
|
+
fenceChar = ch;
|
|
86
|
+
fenceLen = len;
|
|
87
|
+
continue;
|
|
88
|
+
} else if (ch === fenceChar && len >= fenceLen) {
|
|
89
|
+
// Closing fence — trailing info-string is not allowed per
|
|
90
|
+
// CommonMark; only treat it as a close if the rest of the
|
|
91
|
+
// line is whitespace.
|
|
92
|
+
if (fenceMatch[3].trim() === "") {
|
|
93
|
+
fenceChar = null;
|
|
94
|
+
fenceLen = 0;
|
|
95
|
+
continue;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
if (fenceChar !== null) {
|
|
100
|
+
// Inside a code block — ignore any vigiles markers on this line.
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Strip inline code spans (backtick-wrapped text) so illustrative
|
|
105
|
+
// markers in prose like `<!-- vigiles:enforce ... -->` don't get
|
|
106
|
+
// parsed. CommonMark opens with N backticks and closes with exactly
|
|
107
|
+
// N, so the backreference handles matching-length spans.
|
|
108
|
+
const scannable = line.replace(/(`+)[\s\S]*?\1/g, (m) =>
|
|
109
|
+
" ".repeat(m.length),
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
const enforceMatch = ENFORCE_RE.exec(scannable);
|
|
113
|
+
if (enforceMatch) {
|
|
114
|
+
rules.push({
|
|
115
|
+
linterRule: enforceMatch[1],
|
|
116
|
+
why: enforceMatch[2],
|
|
117
|
+
line: i + 1,
|
|
118
|
+
});
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
// Skip the compiled-file hash header (`<!-- vigiles:sha256:... -->`)
|
|
122
|
+
// entirely — it's not a rule marker and should not be reported.
|
|
123
|
+
if (/<!--\s*vigiles:sha\d+:/.test(scannable)) continue;
|
|
124
|
+
|
|
125
|
+
const markerMatch = MARKER_RE.exec(scannable);
|
|
126
|
+
if (markerMatch) {
|
|
127
|
+
// Looks like a vigiles marker but didn't parse as enforce —
|
|
128
|
+
// surface it so users catch typos like "vigile:enforce" or
|
|
129
|
+
// unquoted why.
|
|
130
|
+
const kind = markerMatch[1];
|
|
131
|
+
if (kind === "enforce") {
|
|
132
|
+
errors.push({
|
|
133
|
+
line: i + 1,
|
|
134
|
+
message:
|
|
135
|
+
'Malformed vigiles:enforce — expected `<!-- vigiles:enforce <linter>/<rule> "<why>" -->`',
|
|
136
|
+
raw: line.trim(),
|
|
137
|
+
});
|
|
138
|
+
} else if (kind !== "disable" && kind !== "ignore") {
|
|
139
|
+
// `vigiles:disable ...` / `vigiles:ignore ...` are reserved for
|
|
140
|
+
// future disable-comment support; don't complain about them.
|
|
141
|
+
errors.push({
|
|
142
|
+
line: i + 1,
|
|
143
|
+
message: `Unknown vigiles marker "${kind}". Only \`vigiles:enforce\` is supported.`,
|
|
144
|
+
raw: line.trim(),
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return { rules, errors };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* True if the content contains at least one parseable vigiles:enforce
|
|
155
|
+
* rule (ignoring fenced code blocks and malformed markers). Used by
|
|
156
|
+
* `require-spec` validation to treat inline mode as spec-equivalent.
|
|
157
|
+
*
|
|
158
|
+
* Deliberately delegates to `parseInlineRules` so a loose prefix regex
|
|
159
|
+
* can't satisfy require-spec with a malformed marker that produces no
|
|
160
|
+
* real enforceable rule.
|
|
161
|
+
*/
|
|
162
|
+
export function hasInlineRules(content: string): boolean {
|
|
163
|
+
return parseInlineRules(content).rules.length > 0;
|
|
164
|
+
}
|