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.
Files changed (156) hide show
  1. package/.claude/settings.json +46 -0
  2. package/.claude/settings.local.json +8 -0
  3. package/.claude-plugin/hooks/post-edit.sh +34 -0
  4. package/.claude-plugin/hooks/pre-edit.sh +40 -0
  5. package/.claude-plugin/hooks/session-start.sh +38 -0
  6. package/.claude-plugin/marketplace.json +14 -0
  7. package/.claude-plugin/plugin.json +47 -0
  8. package/.github/workflows/ci.yml +81 -0
  9. package/.prettierignore +1 -0
  10. package/.vigiles/generated.d.ts +205 -0
  11. package/CLAUDE.md +95 -0
  12. package/CLAUDE.md.spec.ts +142 -0
  13. package/CONTRIBUTING.md +121 -0
  14. package/LICENSE +21 -0
  15. package/README.md +377 -0
  16. package/action.yml +25 -0
  17. package/dist/action.d.ts +7 -0
  18. package/dist/action.d.ts.map +1 -0
  19. package/dist/action.js +180 -0
  20. package/dist/action.js.map +1 -0
  21. package/dist/cli.d.ts +12 -0
  22. package/dist/cli.d.ts.map +1 -0
  23. package/dist/cli.js +1267 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/cli.test.d.ts +2 -0
  26. package/dist/cli.test.d.ts.map +1 -0
  27. package/dist/cli.test.js +650 -0
  28. package/dist/cli.test.js.map +1 -0
  29. package/dist/compile.d.ts +101 -0
  30. package/dist/compile.d.ts.map +1 -0
  31. package/dist/compile.js +503 -0
  32. package/dist/compile.js.map +1 -0
  33. package/dist/evolve.d.ts +132 -0
  34. package/dist/evolve.d.ts.map +1 -0
  35. package/dist/evolve.js +599 -0
  36. package/dist/evolve.js.map +1 -0
  37. package/dist/freshness.d.ts +67 -0
  38. package/dist/freshness.d.ts.map +1 -0
  39. package/dist/freshness.js +244 -0
  40. package/dist/freshness.js.map +1 -0
  41. package/dist/freshness.test.d.ts +2 -0
  42. package/dist/freshness.test.d.ts.map +1 -0
  43. package/dist/freshness.test.js +356 -0
  44. package/dist/freshness.test.js.map +1 -0
  45. package/dist/generate-types.d.ts +34 -0
  46. package/dist/generate-types.d.ts.map +1 -0
  47. package/dist/generate-types.js +381 -0
  48. package/dist/generate-types.js.map +1 -0
  49. package/dist/inline.d.ts +58 -0
  50. package/dist/inline.d.ts.map +1 -0
  51. package/dist/inline.js +142 -0
  52. package/dist/inline.js.map +1 -0
  53. package/dist/inline.test.d.ts +5 -0
  54. package/dist/inline.test.d.ts.map +1 -0
  55. package/dist/inline.test.js +152 -0
  56. package/dist/inline.test.js.map +1 -0
  57. package/dist/linters.d.ts +38 -0
  58. package/dist/linters.d.ts.map +1 -0
  59. package/dist/linters.js +588 -0
  60. package/dist/linters.js.map +1 -0
  61. package/dist/proofs.d.ts +272 -0
  62. package/dist/proofs.d.ts.map +1 -0
  63. package/dist/proofs.js +622 -0
  64. package/dist/proofs.js.map +1 -0
  65. package/dist/proofs.test.d.ts +9 -0
  66. package/dist/proofs.test.d.ts.map +1 -0
  67. package/dist/proofs.test.js +952 -0
  68. package/dist/proofs.test.js.map +1 -0
  69. package/dist/spec.d.ts +258 -0
  70. package/dist/spec.d.ts.map +1 -0
  71. package/dist/spec.js +113 -0
  72. package/dist/spec.js.map +1 -0
  73. package/dist/spec.test.d.ts +2 -0
  74. package/dist/spec.test.d.ts.map +1 -0
  75. package/dist/spec.test.js +1222 -0
  76. package/dist/spec.test.js.map +1 -0
  77. package/dist/types.d.ts +101 -0
  78. package/dist/types.d.ts.map +1 -0
  79. package/dist/types.js +3 -0
  80. package/dist/types.js.map +1 -0
  81. package/dist/validate.d.ts +10 -0
  82. package/dist/validate.d.ts.map +1 -0
  83. package/dist/validate.js +286 -0
  84. package/dist/validate.js.map +1 -0
  85. package/dist/validate.test.d.ts +2 -0
  86. package/dist/validate.test.d.ts.map +1 -0
  87. package/dist/validate.test.js +531 -0
  88. package/dist/validate.test.js.map +1 -0
  89. package/docs/agent-setup.md +85 -0
  90. package/docs/agent-workflows.md +103 -0
  91. package/docs/comparison.md +71 -0
  92. package/docs/freshness.md +124 -0
  93. package/docs/inline-mode.md +119 -0
  94. package/docs/linter-support.md +166 -0
  95. package/docs/spec-format.md +194 -0
  96. package/eslint.config.mjs +79 -0
  97. package/examples/CLAUDE.md +54 -0
  98. package/examples/CLAUDE.md.spec.ts +65 -0
  99. package/examples/SKILL.md +50 -0
  100. package/examples/SKILL.md.spec.ts +57 -0
  101. package/fixtures/example-project/CLAUDE.md +11 -0
  102. package/fixtures/example-project/package.json +9 -0
  103. package/fixtures/example-project/src/index.ts +3 -0
  104. package/fixtures/example-project/src/utils.test.ts +2 -0
  105. package/fixtures/example-project/src/utils.ts +3 -0
  106. package/logo.png +0 -0
  107. package/package.json +42 -0
  108. package/research/adoption-strategy.md +111 -0
  109. package/research/agent-integration.md +145 -0
  110. package/research/ai-code-quality.md +197 -0
  111. package/research/code-search-for-agents.md +313 -0
  112. package/research/competitive-landscape.md +163 -0
  113. package/research/doc-freshness.md +516 -0
  114. package/research/executable-specs.md +368 -0
  115. package/research/feature-ideas.md +464 -0
  116. package/research/formal-proofs-for-agents.md +338 -0
  117. package/research/fp-for-agent-harness.md +150 -0
  118. package/research/fp-for-deterministic-ai.md +131 -0
  119. package/research/self-evolving-specs.md +298 -0
  120. package/schemas/claude-md-strict.yml +18 -0
  121. package/schemas/claude-md.yml +6 -0
  122. package/schemas/skill-strict.yml +12 -0
  123. package/schemas/skill.yml +5 -0
  124. package/skills/audit-feedback-loop/SKILL.md +76 -0
  125. package/skills/edit-spec/SKILL.md +131 -0
  126. package/skills/enforce-rules-format/SKILL.md +71 -0
  127. package/skills/generate-logo/SKILL.md +102 -0
  128. package/skills/generate-rule/SKILL.md +90 -0
  129. package/skills/linter-docs/clippy.md +241 -0
  130. package/skills/linter-docs/eslint.md +384 -0
  131. package/skills/linter-docs/pylint.md +288 -0
  132. package/skills/linter-docs/rubocop.md +277 -0
  133. package/skills/linter-docs/ruff.md +187 -0
  134. package/skills/linter-docs/stylelint.md +247 -0
  135. package/skills/migrate-to-spec/SKILL.md +124 -0
  136. package/skills/pr-to-lint-rule/SKILL.md +97 -0
  137. package/skills/strengthen/SKILL.md +168 -0
  138. package/src/action.ts +214 -0
  139. package/src/cli.test.ts +914 -0
  140. package/src/cli.ts +1631 -0
  141. package/src/compile.ts +691 -0
  142. package/src/evolve.ts +781 -0
  143. package/src/freshness.test.ts +449 -0
  144. package/src/freshness.ts +299 -0
  145. package/src/generate-types.ts +448 -0
  146. package/src/inline.test.ts +206 -0
  147. package/src/inline.ts +164 -0
  148. package/src/linters.ts +739 -0
  149. package/src/proofs.test.ts +1314 -0
  150. package/src/proofs.ts +849 -0
  151. package/src/spec.test.ts +1471 -0
  152. package/src/spec.ts +427 -0
  153. package/src/types.ts +117 -0
  154. package/src/validate.test.ts +701 -0
  155. package/src/validate.ts +381 -0
  156. 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
+ }