@lyse-labs/lyse 0.1.0-alpha.2 → 0.2.0-alpha.1

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 (140) hide show
  1. package/LICENSE +0 -5
  2. package/dist/cli.js +67 -11
  3. package/dist/cli.js.map +1 -1
  4. package/dist/commands/__tests__/add-ci-gate.test.d.ts +1 -0
  5. package/dist/commands/__tests__/add-ci-gate.test.js +149 -0
  6. package/dist/commands/__tests__/add-ci-gate.test.js.map +1 -0
  7. package/dist/commands/add-ci-gate.d.ts +26 -0
  8. package/dist/commands/add-ci-gate.js +429 -0
  9. package/dist/commands/add-ci-gate.js.map +1 -0
  10. package/dist/commands/audit-pipeline.js +1 -1
  11. package/dist/commands/audit-pipeline.js.map +1 -1
  12. package/dist/commands/fix.d.ts +8 -8
  13. package/dist/commands/init.d.ts +3 -3
  14. package/dist/commands/init.js +1 -1
  15. package/dist/commands/init.js.map +1 -1
  16. package/dist/commands/mcp-setup.js +1 -1
  17. package/dist/commands/mcp-setup.js.map +1 -1
  18. package/dist/config/schema.d.ts +14 -14
  19. package/dist/llm/__tests__/layer4-stage.test.d.ts +1 -0
  20. package/dist/llm/__tests__/layer4-stage.test.js +157 -0
  21. package/dist/llm/__tests__/layer4-stage.test.js.map +1 -0
  22. package/dist/llm/__tests__/validator.test.d.ts +1 -0
  23. package/dist/llm/__tests__/validator.test.js +156 -0
  24. package/dist/llm/__tests__/validator.test.js.map +1 -0
  25. package/dist/llm/connectors/__tests__/anthropic-adapter.test.d.ts +1 -0
  26. package/dist/llm/connectors/__tests__/anthropic-adapter.test.js +99 -0
  27. package/dist/llm/connectors/__tests__/anthropic-adapter.test.js.map +1 -0
  28. package/dist/llm/connectors/__tests__/cache.test.d.ts +1 -0
  29. package/dist/llm/connectors/__tests__/cache.test.js +50 -0
  30. package/dist/llm/connectors/__tests__/cache.test.js.map +1 -0
  31. package/dist/llm/connectors/__tests__/noop-adapter.test.d.ts +1 -0
  32. package/dist/llm/connectors/__tests__/noop-adapter.test.js +21 -0
  33. package/dist/llm/connectors/__tests__/noop-adapter.test.js.map +1 -0
  34. package/dist/llm/connectors/__tests__/openai-compatible-adapter.test.d.ts +1 -0
  35. package/dist/llm/connectors/__tests__/openai-compatible-adapter.test.js +82 -0
  36. package/dist/llm/connectors/__tests__/openai-compatible-adapter.test.js.map +1 -0
  37. package/dist/llm/connectors/__tests__/resolver.test.d.ts +1 -0
  38. package/dist/llm/connectors/__tests__/resolver.test.js +140 -0
  39. package/dist/llm/connectors/__tests__/resolver.test.js.map +1 -0
  40. package/dist/llm/connectors/anthropic-adapter.d.ts +12 -0
  41. package/dist/llm/connectors/anthropic-adapter.js +57 -0
  42. package/dist/llm/connectors/anthropic-adapter.js.map +1 -0
  43. package/dist/llm/connectors/cache.d.ts +15 -0
  44. package/dist/llm/connectors/cache.js +57 -0
  45. package/dist/llm/connectors/cache.js.map +1 -0
  46. package/dist/llm/connectors/noop-adapter.d.ts +4 -0
  47. package/dist/llm/connectors/noop-adapter.js +12 -0
  48. package/dist/llm/connectors/noop-adapter.js.map +1 -0
  49. package/dist/llm/connectors/openai-compatible-adapter.d.ts +13 -0
  50. package/dist/llm/connectors/openai-compatible-adapter.js +47 -0
  51. package/dist/llm/connectors/openai-compatible-adapter.js.map +1 -0
  52. package/dist/llm/connectors/resolver.d.ts +9 -0
  53. package/dist/llm/connectors/resolver.js +162 -0
  54. package/dist/llm/connectors/resolver.js.map +1 -0
  55. package/dist/llm/connectors/types.d.ts +25 -0
  56. package/dist/llm/connectors/types.js +13 -0
  57. package/dist/llm/connectors/types.js.map +1 -0
  58. package/dist/llm/layer4-stage.d.ts +8 -6
  59. package/dist/llm/layer4-stage.js +145 -9
  60. package/dist/llm/layer4-stage.js.map +1 -1
  61. package/dist/llm/rubric-stub.d.ts +8 -0
  62. package/dist/llm/rubric-stub.js +4 -0
  63. package/dist/llm/rubric-stub.js.map +1 -0
  64. package/dist/llm/validator.d.ts +18 -0
  65. package/dist/llm/validator.js +81 -0
  66. package/dist/llm/validator.js.map +1 -0
  67. package/dist/parsers/ai-tokens.d.ts +10 -0
  68. package/dist/parsers/ai-tokens.js +156 -0
  69. package/dist/parsers/ai-tokens.js.map +1 -0
  70. package/dist/reliability/catalogue/__tests__/sub-axes.test.js +13 -3
  71. package/dist/reliability/catalogue/__tests__/sub-axes.test.js.map +1 -1
  72. package/dist/reliability/catalogue/sub-axes.js +16 -0
  73. package/dist/reliability/catalogue/sub-axes.js.map +1 -1
  74. package/dist/reliability/types.d.ts +1 -1
  75. package/dist/rule-runner.js +1 -1
  76. package/dist/rule-runner.js.map +1 -1
  77. package/dist/rules/ai-governance-ai-content-live-region.d.ts +11 -0
  78. package/dist/rules/ai-governance-ai-content-live-region.js +304 -0
  79. package/dist/rules/ai-governance-ai-content-live-region.js.map +1 -0
  80. package/dist/rules/ai-governance-ai-loading-error-states.d.ts +9 -0
  81. package/dist/rules/ai-governance-ai-loading-error-states.js +214 -0
  82. package/dist/rules/ai-governance-ai-loading-error-states.js.map +1 -0
  83. package/dist/rules/ai-governance-ai-marker-anti-patterns.d.ts +15 -0
  84. package/dist/rules/ai-governance-ai-marker-anti-patterns.js +178 -0
  85. package/dist/rules/ai-governance-ai-marker-anti-patterns.js.map +1 -0
  86. package/dist/rules/ai-governance-ai-marker-component-present.d.ts +28 -0
  87. package/dist/rules/ai-governance-ai-marker-component-present.js +320 -0
  88. package/dist/rules/ai-governance-ai-marker-component-present.js.map +1 -0
  89. package/dist/rules/ai-governance-ai-token-requires-marker.d.ts +17 -0
  90. package/dist/rules/ai-governance-ai-token-requires-marker.js +206 -0
  91. package/dist/rules/ai-governance-ai-token-requires-marker.js.map +1 -0
  92. package/dist/rules/ai-governance-ai-tokens-reserved.d.ts +8 -0
  93. package/dist/rules/ai-governance-ai-tokens-reserved.js +85 -0
  94. package/dist/rules/ai-governance-ai-tokens-reserved.js.map +1 -0
  95. package/dist/rules/ai-governance-disclaimer-present.d.ts +18 -0
  96. package/dist/rules/ai-governance-disclaimer-present.js +210 -0
  97. package/dist/rules/ai-governance-disclaimer-present.js.map +1 -0
  98. package/dist/rules/ai-governance-explainability-affordance.d.ts +14 -0
  99. package/dist/rules/ai-governance-explainability-affordance.js +196 -0
  100. package/dist/rules/ai-governance-explainability-affordance.js.map +1 -0
  101. package/dist/rules/ai-governance-feedback-control-present.d.ts +19 -0
  102. package/dist/rules/ai-governance-feedback-control-present.js +223 -0
  103. package/dist/rules/ai-governance-feedback-control-present.js.map +1 -0
  104. package/dist/rules/ai-governance-human-control-affordances.d.ts +16 -0
  105. package/dist/rules/ai-governance-human-control-affordances.js +228 -0
  106. package/dist/rules/ai-governance-human-control-affordances.js.map +1 -0
  107. package/dist/rules/ai-governance-value-gate-doc-present.d.ts +18 -0
  108. package/dist/rules/ai-governance-value-gate-doc-present.js +206 -0
  109. package/dist/rules/ai-governance-value-gate-doc-present.js.map +1 -0
  110. package/dist/rules/ai-surface-agent-instruction-files.d.ts +33 -0
  111. package/dist/rules/ai-surface-agent-instruction-files.js +310 -0
  112. package/dist/rules/ai-surface-agent-instruction-files.js.map +1 -0
  113. package/dist/rules/ai-surface-llms-txt-structure.d.ts +18 -0
  114. package/dist/rules/ai-surface-llms-txt-structure.js +200 -0
  115. package/dist/rules/ai-surface-llms-txt-structure.js.map +1 -0
  116. package/dist/rules/ai-surface-mcp-config-present.d.ts +23 -0
  117. package/dist/rules/ai-surface-mcp-config-present.js +193 -0
  118. package/dist/rules/ai-surface-mcp-config-present.js.map +1 -0
  119. package/dist/rules/ai-surface-shadcn-registry-valid.d.ts +22 -0
  120. package/dist/rules/ai-surface-shadcn-registry-valid.js +247 -0
  121. package/dist/rules/ai-surface-shadcn-registry-valid.js.map +1 -0
  122. package/dist/rules/components-contracts-strictness.d.ts +48 -0
  123. package/dist/rules/components-contracts-strictness.js +372 -0
  124. package/dist/rules/components-contracts-strictness.js.map +1 -0
  125. package/dist/rules/registry.js +32 -0
  126. package/dist/rules/registry.js.map +1 -1
  127. package/dist/rules/tokens-dtcg-conformance.d.ts +55 -19
  128. package/dist/rules/tokens-dtcg-conformance.js +320 -82
  129. package/dist/rules/tokens-dtcg-conformance.js.map +1 -1
  130. package/dist/scorer.js +4 -3
  131. package/dist/scorer.js.map +1 -1
  132. package/dist/types.d.ts +2 -2
  133. package/dist/types.js.map +1 -1
  134. package/package.json +41 -16
  135. package/rules-manifest.json +431 -6
  136. package/schemas/v1/lyse-rules.json +1 -1
  137. package/dist/commands/ci-setup.d.ts +0 -9
  138. package/dist/commands/ci-setup.js +0 -42
  139. package/dist/commands/ci-setup.js.map +0 -1
  140. package/dist/commands/templates/lyse-workflow.yml.template +0 -30
@@ -0,0 +1,206 @@
1
+ import { existsSync, readFileSync, statSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import fg from "fast-glob";
4
+ import { createLyseRule } from "./_rule-module.js";
5
+ import { scanForMarkerComponents, makeAllowlistCheck, } from "./ai-governance-ai-marker-component-present.js";
6
+ import { detectReservedAiTokens } from "../parsers/ai-tokens.js";
7
+ const RULE_ID = "ai-governance/value-gate-doc-present";
8
+ const MAX_DOC_BYTES = 500_000;
9
+ const DISABLE_DIRECTIVE = `lyse-disable ${RULE_ID}`;
10
+ const ALLOWLIST_CANDIDATES = [
11
+ "README.md",
12
+ "README",
13
+ "README.mdx",
14
+ "readme.md",
15
+ ".lyse.yaml",
16
+ ".lyse.yml",
17
+ ];
18
+ // Ordered candidate paths — checked before the broad glob.
19
+ const STATIC_CANDIDATES = [
20
+ "AI_GOVERNANCE.md",
21
+ "docs/ai-value-gate.md",
22
+ "docs/ai-governance.md",
23
+ "docs/ai-readiness.md",
24
+ "docs/ai-checklist.md",
25
+ ".lyse/ai-value-gate.md",
26
+ ];
27
+ // Glob patterns for docs/**/ — name-constrained to avoid catching arbitrary AI docs.
28
+ const DOC_GLOBS = [
29
+ "docs/**/ai-value-gate.md",
30
+ "docs/**/ai-governance.md",
31
+ "docs/**/ai-readiness.md",
32
+ "docs/**/ai-checklist.md",
33
+ "**/AI_GOVERNANCE.md",
34
+ ];
35
+ const IGNORE = [
36
+ "**/node_modules/**",
37
+ "**/dist/**",
38
+ "**/build/**",
39
+ "**/.git/**",
40
+ "**/.next/**",
41
+ "**/out/**",
42
+ "**/coverage/**",
43
+ ];
44
+ // Gate-language patterns modelled on ServiceNow "10-Q" value gate.
45
+ // Each pattern targets a distinct phrasing family; case-insensitive.
46
+ const GATE_PATTERNS = [
47
+ /\bis\s+ai\s+needed\b/i,
48
+ /\bvalue[-\s]gate\b/i,
49
+ /\bgo\s*\/\s*no[-\s]go\b/i,
50
+ /\bshould\s+this\s+(?:feature\s+)?(?:be|use)\s+ai\b/i,
51
+ /\bis\s+ai\s+the\s+right\s+tool\b/i,
52
+ /\bai[-\s]readiness\b/i,
53
+ /\bwhy\s+ai\s*\?/i,
54
+ /\bdeterministic\s+rule\b.*instead\s+of\s+ai\b/i,
55
+ ];
56
+ export function detectGateLanguage(content) {
57
+ return GATE_PATTERNS.some((re) => re.test(content));
58
+ }
59
+ function readDocIfSmall(absPath) {
60
+ try {
61
+ const stat = statSync(absPath);
62
+ if (!stat.isFile() || stat.size > MAX_DOC_BYTES || stat.size === 0)
63
+ return null;
64
+ return readFileSync(absPath, "utf8");
65
+ }
66
+ catch {
67
+ return null;
68
+ }
69
+ }
70
+ export function discoverGateDoc(repoRoot) {
71
+ for (const candidate of STATIC_CANDIDATES) {
72
+ const abs = join(repoRoot, candidate);
73
+ if (!existsSync(abs))
74
+ continue;
75
+ const content = readDocIfSmall(abs);
76
+ if (content === null)
77
+ continue;
78
+ return { rel: candidate, hasGateLanguage: detectGateLanguage(content) };
79
+ }
80
+ let hits = [];
81
+ try {
82
+ hits = fg.sync(DOC_GLOBS, {
83
+ cwd: repoRoot,
84
+ absolute: false,
85
+ dot: true,
86
+ ignore: IGNORE,
87
+ onlyFiles: true,
88
+ unique: true,
89
+ }).sort();
90
+ }
91
+ catch {
92
+ // non-fatal
93
+ }
94
+ for (const rel of hits) {
95
+ const content = readDocIfSmall(join(repoRoot, rel));
96
+ if (content === null)
97
+ continue;
98
+ return { rel, hasGateLanguage: detectGateLanguage(content) };
99
+ }
100
+ return null;
101
+ }
102
+ const isAllowlisted = makeAllowlistCheck(DISABLE_DIRECTIVE);
103
+ function hasAiSurface(repoRoot) {
104
+ const markerComponents = scanForMarkerComponents(repoRoot);
105
+ if (markerComponents.length > 0)
106
+ return true;
107
+ const reservedTokens = detectReservedAiTokens(repoRoot);
108
+ return reservedTokens.length > 0;
109
+ }
110
+ const evaluate = async (ctx, _files) => {
111
+ const findings = [];
112
+ if (!ctx.repoRoot) {
113
+ return { findings, opportunities: 0 };
114
+ }
115
+ if (isAllowlisted(ctx.repoRoot)) {
116
+ return { findings, opportunities: 0 };
117
+ }
118
+ if (!hasAiSurface(ctx.repoRoot)) {
119
+ return { findings, opportunities: 0 };
120
+ }
121
+ const gateDoc = discoverGateDoc(ctx.repoRoot);
122
+ if (gateDoc === null) {
123
+ findings.push({
124
+ ruleId: RULE_ID,
125
+ axis: "ai-governance",
126
+ severity: "warning",
127
+ location: { file: "AI_GOVERNANCE.md", line: 1, column: 1 },
128
+ message: "No AI value-gate governance doc found — this DS ships an AI surface but documents no go/no-go decision gate (ServiceNow \"10-Q\" pattern). Create a doc that answers: is AI needed, is it the right tool, what is the fallback?",
129
+ suggestion: "add AI_GOVERNANCE.md (or docs/ai-governance.md / .lyse/ai-value-gate.md) with a structured checklist requiring AI justification before shipping each AI feature",
130
+ });
131
+ return { findings, opportunities: 1 };
132
+ }
133
+ if (!gateDoc.hasGateLanguage) {
134
+ findings.push({
135
+ ruleId: RULE_ID,
136
+ axis: "ai-governance",
137
+ severity: "warning",
138
+ location: { file: gateDoc.rel, line: 1, column: 1 },
139
+ message: `Value-gate doc found at \`${gateDoc.rel}\` but contains no gate language — it does not pose the "is AI needed / go/no-go" decision question`,
140
+ suggestion: 'add explicit gate questions: "Is AI needed?", "Is AI the right tool?", "What is the deterministic fallback?" — phrasing that forces a go/no-go decision before shipping',
141
+ });
142
+ return { findings, opportunities: 1 };
143
+ }
144
+ findings.push({
145
+ ruleId: RULE_ID,
146
+ axis: "ai-governance",
147
+ severity: "info",
148
+ location: { file: gateDoc.rel, line: 1, column: 1 },
149
+ message: `AI value-gate governance doc detected at \`${gateDoc.rel}\` — DS documents an AI go/no-go decision process`,
150
+ suggestion: "keep the gate doc up to date as the AI surface evolves; link to it from AGENTS.md or llms.txt so agents can discover it",
151
+ });
152
+ return { findings, opportunities: 1 };
153
+ };
154
+ export const rule = createLyseRule({
155
+ meta: {
156
+ axis: "ai-governance",
157
+ lyseRuleId: RULE_ID,
158
+ defaultSeverity: "warning",
159
+ shortDescription: "AI value-gate governance doc must be present when an AI surface exists",
160
+ fullDescription: "When a design system ships an AI surface — detected by an AI-marker component (via the `AI_MARKER_NAMES` vocabulary from Track 3.2) or reserved AI-marker design tokens (Track 3.1, `detectReservedAiTokens`) — this rule checks that a governance value-gate doc exists and contains structured go/no-go decision language. Candidate locations scanned: `AI_GOVERNANCE.md` at repo root, `docs/ai-value-gate.md`, `docs/ai-governance.md`, `docs/ai-readiness.md`, `docs/ai-checklist.md`, `.lyse/ai-value-gate.md`, and `AI_GOVERNANCE.md` anywhere in the repo tree. A doc is considered valid if it contains at least one of: \"is AI needed\", \"value gate\", \"go/no-go\", \"should this be AI\", \"is AI the right tool\", \"ai-readiness\", \"why AI?\", or \"deterministic rule ... instead of AI\". Emits `warning` when AI surface exists but no doc found, or doc found but lacks gate language. Emits `info` when a valid value-gate doc is present. Emits nothing when the DS has no AI surface.",
161
+ helpUri: "https://github.com/lyse-labs/lyse/blob/main/docs/rules/ai-governance-value-gate-doc-present.md",
162
+ rationale: `Why it matters
163
+
164
+ Before shipping AI-powered UI, design systems need a structured answer to "is AI actually needed here?" — a go/no-go gate. ServiceNow's internal "10-Q" process documents exactly this: a fixed checklist of questions (is AI the right tool? can a deterministic rule solve this? what is the fallback?) that must be answered before any AI feature lands.
165
+
166
+ Without this gate doc, individual teams ship AI features ad-hoc, often when a rule-based solution would have been faster, cheaper, and more auditable. The governance debt accumulates: inconsistent AI use, unclear fallback paths, and no audit trail.
167
+
168
+ This rule does NOT enforce the answers — only that the DS ships the document, ensuring the gate process is defined and discoverable. Teams that use Lyse for AI governance get a measurable signal: "this DS has defined its AI decision process."
169
+
170
+ The cross-condition logic — checking for an AI surface first via \`scanForMarkerComponents\` and \`detectReservedAiTokens\` — ensures DSs without any AI surface are not penalised. The rule is additive: it rewards good governance without creating false positives for projects that have not yet adopted AI features.`,
171
+ examples: [
172
+ {
173
+ good: `# AI Value Gate
174
+
175
+ ## Is AI needed?
176
+ - [ ] Can a deterministic rule solve this instead?
177
+ - [ ] Does ML outperform a rule on this specific input distribution?
178
+ - [ ] What is the fallback if the model is unavailable?
179
+
180
+ Go/no-go: answer all three before shipping.`,
181
+ bad: `# AI Guidelines
182
+
183
+ Use \`AILabel\` on all AI-generated content surfaces.
184
+ Follow the design tokens from the \`ai\` namespace.`,
185
+ },
186
+ ],
187
+ allowlist: [
188
+ "repos containing `lyse-disable ai-governance/value-gate-doc-present` in README, README.md, README.mdx, readme.md, .lyse.yaml, or .lyse.yml — rule is N/A",
189
+ "DSs with no AI surface (no AI-marker component and no reserved AI tokens) — rule emits nothing",
190
+ "doc files larger than 500 KB — skipped to avoid pathological cases",
191
+ "files under `node_modules/`, `dist/`, `build/`, `.git/`, `.next/`, `out/`, `coverage/`",
192
+ ],
193
+ },
194
+ defaultOptions: [],
195
+ create: () => ({ evaluate }),
196
+ });
197
+ export const _internal = {
198
+ detectGateLanguage,
199
+ discoverGateDoc,
200
+ isAllowlisted,
201
+ DISABLE_DIRECTIVE,
202
+ ALLOWLIST_CANDIDATES,
203
+ STATIC_CANDIDATES,
204
+ DOC_GLOBS,
205
+ };
206
+ //# sourceMappingURL=ai-governance-value-gate-doc-present.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-governance-value-gate-doc-present.js","sourceRoot":"","sources":["../../src/rules/ai-governance-value-gate-doc-present.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { Rule } from "../types.js";
2
+ type FrontmatterParseResult = {
3
+ ok: true;
4
+ data: Record<string, unknown>;
5
+ } | {
6
+ ok: false;
7
+ error: string;
8
+ };
9
+ declare function parseFrontmatter(content: string): FrontmatterParseResult;
10
+ declare function isStringOrStringArray(value: unknown): value is string | string[];
11
+ interface CursorValidation {
12
+ errors: string[];
13
+ warnings: string[];
14
+ }
15
+ declare function validateCursorRuleFrontmatter(fm: Record<string, unknown>): CursorValidation;
16
+ interface SkillValidation {
17
+ errors: string[];
18
+ warnings: string[];
19
+ }
20
+ declare function validateSkillFrontmatter(fm: Record<string, unknown>): SkillValidation;
21
+ export declare const rule: Rule;
22
+ export declare const _internal: {
23
+ parseFrontmatter: typeof parseFrontmatter;
24
+ validateCursorRuleFrontmatter: typeof validateCursorRuleFrontmatter;
25
+ validateSkillFrontmatter: typeof validateSkillFrontmatter;
26
+ isStringOrStringArray: typeof isStringOrStringArray;
27
+ CURSOR_RULE_GLOBS: string[];
28
+ CLAUDE_SKILL_GLOBS: string[];
29
+ TOKEN_BUDGET_BYTES: number;
30
+ DESCRIPTION_MAX_CHARS: number;
31
+ KEBAB_CASE_RE: RegExp;
32
+ };
33
+ export {};
@@ -0,0 +1,310 @@
1
+ import { readFileSync, statSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import fg from "fast-glob";
4
+ import { parse as parseYaml } from "yaml";
5
+ import { isPathExcluded } from "./_exclude.js";
6
+ import { createLyseRule } from "./_rule-module.js";
7
+ const RULE_ID = "ai-surface/agent-instruction-files";
8
+ const MAX_FILE_BYTES = 500_000;
9
+ const TOKEN_BUDGET_BYTES = 5_000;
10
+ const DESCRIPTION_MAX_CHARS = 200;
11
+ const CURSOR_RULE_GLOBS = [".cursor/rules/**/*.mdc"];
12
+ const CLAUDE_SKILL_GLOBS = [".claude/skills/*/SKILL.md"];
13
+ const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\s*(?:\r?\n|$)/;
14
+ const KEBAB_CASE_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
15
+ function readFileIfSmall(absPath) {
16
+ try {
17
+ const stat = statSync(absPath);
18
+ if (!stat.isFile())
19
+ return { content: null, bytes: 0 };
20
+ const bytes = stat.size;
21
+ if (bytes > MAX_FILE_BYTES)
22
+ return { content: null, bytes };
23
+ return { content: readFileSync(absPath, "utf8"), bytes };
24
+ }
25
+ catch {
26
+ return { content: null, bytes: 0 };
27
+ }
28
+ }
29
+ function parseFrontmatter(content) {
30
+ const match = content.match(FRONTMATTER_RE);
31
+ if (!match) {
32
+ return { ok: false, error: "no YAML frontmatter (expected `---` fenced block at top of file)" };
33
+ }
34
+ const yamlBody = match[1] ?? "";
35
+ let parsed;
36
+ try {
37
+ parsed = parseYaml(yamlBody);
38
+ }
39
+ catch (e) {
40
+ return {
41
+ ok: false,
42
+ error: `frontmatter YAML parse error: ${e instanceof Error ? e.message : "unknown"}`,
43
+ };
44
+ }
45
+ if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
46
+ return { ok: false, error: "frontmatter must be a YAML mapping" };
47
+ }
48
+ return { ok: true, data: parsed };
49
+ }
50
+ function isStringOrStringArray(value) {
51
+ if (typeof value === "string")
52
+ return value.length > 0;
53
+ if (Array.isArray(value)) {
54
+ return value.length > 0 && value.every((v) => typeof v === "string" && v.length > 0);
55
+ }
56
+ return false;
57
+ }
58
+ function validateCursorRuleFrontmatter(fm) {
59
+ const errors = [];
60
+ const warnings = [];
61
+ const description = fm.description;
62
+ if (typeof description !== "string" || description.trim().length === 0) {
63
+ errors.push("missing required `description` (string)");
64
+ }
65
+ else if (description.trim().length > DESCRIPTION_MAX_CHARS) {
66
+ warnings.push(`\`description\` is ${description.trim().length} chars (> ${DESCRIPTION_MAX_CHARS}); consider shortening`);
67
+ }
68
+ if (!("globs" in fm)) {
69
+ errors.push("missing required `globs` (string or array of strings)");
70
+ }
71
+ else if (!isStringOrStringArray(fm.globs)) {
72
+ errors.push("`globs` must be a non-empty string or array of strings");
73
+ }
74
+ if ("alwaysApply" in fm && typeof fm.alwaysApply !== "boolean") {
75
+ warnings.push("`alwaysApply` should be a boolean");
76
+ }
77
+ return { errors, warnings };
78
+ }
79
+ function validateSkillFrontmatter(fm) {
80
+ const errors = [];
81
+ const warnings = [];
82
+ const name = fm.name;
83
+ if (typeof name !== "string" || name.trim().length === 0) {
84
+ errors.push("missing required `name` (kebab-case string)");
85
+ }
86
+ else if (!KEBAB_CASE_RE.test(name)) {
87
+ warnings.push(`\`name\` "${name}" should be kebab-case (lowercase, hyphen-separated)`);
88
+ }
89
+ const description = fm.description;
90
+ if (typeof description !== "string" || description.trim().length === 0) {
91
+ errors.push("missing required `description` (string)");
92
+ }
93
+ else if (description.trim().length > DESCRIPTION_MAX_CHARS) {
94
+ warnings.push(`\`description\` is ${description.trim().length} chars (> ${DESCRIPTION_MAX_CHARS}); consider shortening`);
95
+ }
96
+ return { errors, warnings };
97
+ }
98
+ function discoverFiles(ctx, patterns) {
99
+ if (!ctx.repoRoot)
100
+ return [];
101
+ let entries = [];
102
+ try {
103
+ entries = fg.sync(patterns, {
104
+ cwd: ctx.repoRoot,
105
+ absolute: false,
106
+ dot: true,
107
+ onlyFiles: true,
108
+ ignore: ["**/node_modules/**", "**/dist/**", "**/build/**", "**/.git/**"],
109
+ followSymbolicLinks: false,
110
+ });
111
+ }
112
+ catch {
113
+ return [];
114
+ }
115
+ const out = [];
116
+ for (const rel of entries) {
117
+ if (isPathExcluded(rel, ctx.excludePaths))
118
+ continue;
119
+ out.push(rel);
120
+ }
121
+ return out.sort();
122
+ }
123
+ const evaluate = async (ctx, _files) => {
124
+ const findings = [];
125
+ if (!ctx.repoRoot) {
126
+ return { findings, opportunities: 0 };
127
+ }
128
+ const cursorFiles = discoverFiles(ctx, CURSOR_RULE_GLOBS);
129
+ const skillFiles = discoverFiles(ctx, CLAUDE_SKILL_GLOBS);
130
+ if (cursorFiles.length === 0 && skillFiles.length === 0) {
131
+ findings.push({
132
+ ruleId: RULE_ID,
133
+ axis: "ai-surface",
134
+ severity: "warning",
135
+ location: { file: ".cursor/rules/", line: 1, column: 1 },
136
+ message: "No agent instruction files found (neither `.cursor/rules/*.mdc` nor `.claude/skills/*/SKILL.md`)",
137
+ suggestion: "add at least one Cursor rule under `.cursor/rules/` or one Claude skill under `.claude/skills/<name>/SKILL.md` so coding agents have project-specific guidance",
138
+ });
139
+ // WHY: opportunities: 1 (not 0) signals one actionable fix exists (add any instruction file).
140
+ // This differs from sibling rules that return opportunities: N (one per candidate path) because
141
+ // here the surface is entirely absent — there is no per-file opportunity to count.
142
+ return { findings, opportunities: 1 };
143
+ }
144
+ let opportunities = 0;
145
+ for (const rel of cursorFiles) {
146
+ opportunities += 1;
147
+ const abs = join(ctx.repoRoot, rel);
148
+ const { content, bytes } = readFileIfSmall(abs);
149
+ const relPath = rel;
150
+ if (content === null) {
151
+ findings.push({
152
+ ruleId: RULE_ID,
153
+ axis: "ai-surface",
154
+ severity: "warning",
155
+ location: { file: relPath, line: 1, column: 1 },
156
+ message: `Cursor rule file ${relPath} could not be read (file too large: ${bytes} bytes)`,
157
+ suggestion: `split the rule into smaller files (≤ ${MAX_FILE_BYTES} bytes)`,
158
+ });
159
+ continue;
160
+ }
161
+ if (bytes > TOKEN_BUDGET_BYTES) {
162
+ findings.push({
163
+ ruleId: RULE_ID,
164
+ axis: "ai-surface",
165
+ severity: "warning",
166
+ location: { file: relPath, line: 1, column: 1 },
167
+ message: `Cursor rule file is ${bytes} bytes (> ${TOKEN_BUDGET_BYTES} byte token-budget) — costs agents context`,
168
+ suggestion: "split into smaller focused rules or trim prose; agents pay context cost on every load",
169
+ });
170
+ }
171
+ const parsed = parseFrontmatter(content);
172
+ if (!parsed.ok) {
173
+ findings.push({
174
+ ruleId: RULE_ID,
175
+ axis: "ai-surface",
176
+ severity: "error",
177
+ location: { file: relPath, line: 1, column: 1 },
178
+ message: `Cursor rule ${relPath} has invalid frontmatter: ${parsed.error}`,
179
+ suggestion: "wrap frontmatter in `---` markers and ensure `description` + `globs` keys are present",
180
+ });
181
+ continue;
182
+ }
183
+ const { errors, warnings } = validateCursorRuleFrontmatter(parsed.data);
184
+ for (const err of errors) {
185
+ findings.push({
186
+ ruleId: RULE_ID,
187
+ axis: "ai-surface",
188
+ severity: "error",
189
+ location: { file: relPath, line: 1, column: 1 },
190
+ message: `Cursor rule ${relPath}: ${err}`,
191
+ suggestion: "see https://cursor.com/docs/context/rules for the expected frontmatter shape",
192
+ });
193
+ }
194
+ for (const warn of warnings) {
195
+ findings.push({
196
+ ruleId: RULE_ID,
197
+ axis: "ai-surface",
198
+ severity: "warning",
199
+ location: { file: relPath, line: 1, column: 1 },
200
+ message: `Cursor rule ${relPath}: ${warn}`,
201
+ });
202
+ }
203
+ }
204
+ for (const rel of skillFiles) {
205
+ opportunities += 1;
206
+ const abs = join(ctx.repoRoot, rel);
207
+ const { content, bytes } = readFileIfSmall(abs);
208
+ const relPath = rel;
209
+ if (content === null) {
210
+ findings.push({
211
+ ruleId: RULE_ID,
212
+ axis: "ai-surface",
213
+ severity: "warning",
214
+ location: { file: relPath, line: 1, column: 1 },
215
+ message: `Claude skill file ${relPath} could not be read (file too large: ${bytes} bytes)`,
216
+ suggestion: `keep SKILL.md focused (≤ ${MAX_FILE_BYTES} bytes); use linked files for verbose references`,
217
+ });
218
+ continue;
219
+ }
220
+ if (bytes > TOKEN_BUDGET_BYTES) {
221
+ findings.push({
222
+ ruleId: RULE_ID,
223
+ axis: "ai-surface",
224
+ severity: "warning",
225
+ location: { file: relPath, line: 1, column: 1 },
226
+ message: `Claude skill file is ${bytes} bytes (> ${TOKEN_BUDGET_BYTES} byte token-budget) — costs agents context`,
227
+ suggestion: "trim SKILL.md and offload verbose references to linked files",
228
+ });
229
+ }
230
+ const parsed = parseFrontmatter(content);
231
+ if (!parsed.ok) {
232
+ findings.push({
233
+ ruleId: RULE_ID,
234
+ axis: "ai-surface",
235
+ severity: "error",
236
+ location: { file: relPath, line: 1, column: 1 },
237
+ message: `Claude skill ${relPath} has invalid frontmatter: ${parsed.error}`,
238
+ suggestion: "wrap frontmatter in `---` markers and ensure `name` + `description` keys are present",
239
+ });
240
+ continue;
241
+ }
242
+ const { errors, warnings } = validateSkillFrontmatter(parsed.data);
243
+ for (const err of errors) {
244
+ findings.push({
245
+ ruleId: RULE_ID,
246
+ axis: "ai-surface",
247
+ severity: "error",
248
+ location: { file: relPath, line: 1, column: 1 },
249
+ message: `Claude skill ${relPath}: ${err}`,
250
+ suggestion: "see https://docs.claude.com/en/docs/agents-and-tools/skills for the expected frontmatter shape",
251
+ });
252
+ }
253
+ for (const warn of warnings) {
254
+ findings.push({
255
+ ruleId: RULE_ID,
256
+ axis: "ai-surface",
257
+ severity: "warning",
258
+ location: { file: relPath, line: 1, column: 1 },
259
+ message: `Claude skill ${relPath}: ${warn}`,
260
+ });
261
+ }
262
+ }
263
+ return { findings, opportunities };
264
+ };
265
+ export const rule = createLyseRule({
266
+ meta: {
267
+ axis: "ai-surface",
268
+ lyseRuleId: RULE_ID,
269
+ defaultSeverity: "warning",
270
+ shortDescription: "Repo should ship Cursor rules or Claude skills with valid frontmatter",
271
+ fullDescription: "Scans for agent instruction bundles at the repo root: `.cursor/rules/*.mdc` (Cursor rules) and `.claude/skills/*/SKILL.md` (Anthropic Claude skills). When neither is present, emits a single warning — the repo gives coding agents no project-specific guidance signal. When found, each file is parsed for YAML frontmatter and validated: Cursor rules must declare `description` and `globs`; Claude skills must declare `name` (kebab-case) and `description` (≤200 chars). Files larger than 5 KB raise a token-budget warning (they cost agents context on every load). Malformed frontmatter and missing required keys raise errors; oversize, non-kebab `name`, and overlong descriptions raise warnings.",
272
+ helpUri: "https://github.com/lyse-labs/lyse/blob/main/docs/rules/ai-surface-agent-instruction-files.md",
273
+ rationale: `Why it matters
274
+
275
+ Cursor rules and Claude skills are the contract surface for two of the most-used coding agents in 2026. Without at least one of these bundles, the agent has no project-specific guidance beyond \`AGENTS.md\` or \`CLAUDE.md\` — and even those don't carry the same auto-attach semantics as Cursor's \`globs\` field or the same auto-load semantics as Claude's skill manifest.
276
+
277
+ Beyond presence, the *frontmatter* matters. Cursor uses \`globs\` to decide which rule fires for which file edit; missing \`globs\` silently disables the rule. Claude skills are loaded by their \`name\` + \`description\` pair (the description is the agent's "tool selection" prompt); a missing description means the skill is invisible to the agent's decision loop.
278
+
279
+ Token budget is the third signal. The Anthropic agent skills documentation (Oct 2026) and the Cursor rules documentation both recommend keeping individual files small — long instruction files crowd out the actual context the agent needs to read, and inflate per-call cost. The 5 KB heuristic is the same threshold the Claude skill examples use.`,
280
+ examples: [
281
+ {
282
+ good: '---\\ndescription: TypeScript style guide for this monorepo\\nglobs: ["src/**/*.ts", "src/**/*.tsx"]\\nalwaysApply: false\\n---\\n\\n# TypeScript style\\n\\nUse strict mode. Prefer type aliases over interfaces.',
283
+ bad: '---\\n# missing required `description` and `globs`\\n---\\n\\n# TypeScript style\\n\\nUse strict mode.',
284
+ },
285
+ {
286
+ good: '---\\nname: pr-checklist\\ndescription: Generates a PR checklist from the diff (≤200 chars)\\nversion: 1.0.0\\n---\\n\\n# PR checklist skill\\n\\nProcedural instructions for the agent.',
287
+ bad: '---\\nname: PR_Checklist\\n# missing `description`; `name` is not kebab-case\\n---\\n\\n# PR checklist',
288
+ },
289
+ ],
290
+ allowlist: [
291
+ "files larger than 500 KB — skipped to avoid pathological cases (and counted as an oversize warning)",
292
+ "files matching `ctx.excludePaths` config",
293
+ "repos that ship only AGENTS.md/CLAUDE.md but neither Cursor rules nor Claude skills — emit one warning (not error) to nudge adoption",
294
+ ],
295
+ },
296
+ defaultOptions: [],
297
+ create: () => ({ evaluate }),
298
+ });
299
+ export const _internal = {
300
+ parseFrontmatter,
301
+ validateCursorRuleFrontmatter,
302
+ validateSkillFrontmatter,
303
+ isStringOrStringArray,
304
+ CURSOR_RULE_GLOBS,
305
+ CLAUDE_SKILL_GLOBS,
306
+ TOKEN_BUDGET_BYTES,
307
+ DESCRIPTION_MAX_CHARS,
308
+ KEBAB_CASE_RE,
309
+ };
310
+ //# sourceMappingURL=ai-surface-agent-instruction-files.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-surface-agent-instruction-files.js","sourceRoot":"","sources":["../../src/rules/ai-surface-agent-instruction-files.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,18 @@
1
+ import type { Rule } from "../types.js";
2
+ interface StructureIssue {
3
+ line: number;
4
+ message: string;
5
+ suggestion: string;
6
+ }
7
+ interface StructureReport {
8
+ hasH1: boolean;
9
+ hasBlockquote: boolean;
10
+ hasSection: boolean;
11
+ malformedLinks: StructureIssue[];
12
+ }
13
+ declare function analyseStructure(content: string): StructureReport;
14
+ export declare const rule: Rule;
15
+ export declare const _internal: {
16
+ analyseStructure: typeof analyseStructure;
17
+ };
18
+ export {};