@promptci/core 0.0.1 → 0.1.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 (205) hide show
  1. package/LICENSE +201 -201
  2. package/README.md +29 -30
  3. package/dist/agent-practices.d.ts +2 -0
  4. package/dist/agent-practices.d.ts.map +1 -1
  5. package/dist/agent-practices.js +91 -51
  6. package/dist/agent-practices.js.map +1 -1
  7. package/dist/ai-config.d.ts +139 -0
  8. package/dist/ai-config.d.ts.map +1 -0
  9. package/dist/ai-config.js +523 -0
  10. package/dist/ai-config.js.map +1 -0
  11. package/dist/baseline.d.ts +17 -1
  12. package/dist/baseline.d.ts.map +1 -1
  13. package/dist/baseline.js +5 -1
  14. package/dist/baseline.js.map +1 -1
  15. package/dist/buried-critical.d.ts +17 -0
  16. package/dist/buried-critical.d.ts.map +1 -0
  17. package/dist/buried-critical.js +112 -0
  18. package/dist/buried-critical.js.map +1 -0
  19. package/dist/canonical-owner.d.ts.map +1 -1
  20. package/dist/canonical-owner.js +18 -8
  21. package/dist/canonical-owner.js.map +1 -1
  22. package/dist/command-validity.d.ts.map +1 -1
  23. package/dist/command-validity.js +53 -74
  24. package/dist/command-validity.js.map +1 -1
  25. package/dist/conflicts.d.ts.map +1 -1
  26. package/dist/conflicts.js +1 -42
  27. package/dist/conflicts.js.map +1 -1
  28. package/dist/context-analysis.d.ts.map +1 -1
  29. package/dist/context-analysis.js +13 -2
  30. package/dist/context-analysis.js.map +1 -1
  31. package/dist/context-bloat.d.ts +19 -0
  32. package/dist/context-bloat.d.ts.map +1 -1
  33. package/dist/context-bloat.js +82 -9
  34. package/dist/context-bloat.js.map +1 -1
  35. package/dist/context-optimizer.d.ts.map +1 -1
  36. package/dist/context-optimizer.js +15 -54
  37. package/dist/context-optimizer.js.map +1 -1
  38. package/dist/copilot-instructions-detector.d.ts +24 -0
  39. package/dist/copilot-instructions-detector.d.ts.map +1 -0
  40. package/dist/copilot-instructions-detector.js +145 -0
  41. package/dist/copilot-instructions-detector.js.map +1 -0
  42. package/dist/cursor-rules-detector.d.ts +17 -0
  43. package/dist/cursor-rules-detector.d.ts.map +1 -0
  44. package/dist/cursor-rules-detector.js +110 -0
  45. package/dist/cursor-rules-detector.js.map +1 -0
  46. package/dist/custom-rules.d.ts +52 -0
  47. package/dist/custom-rules.d.ts.map +1 -0
  48. package/dist/custom-rules.js +298 -0
  49. package/dist/custom-rules.js.map +1 -0
  50. package/dist/dead-references.js +11 -9
  51. package/dist/dead-references.js.map +1 -1
  52. package/dist/detectors.d.ts +1 -3
  53. package/dist/detectors.d.ts.map +1 -1
  54. package/dist/detectors.js +46 -53
  55. package/dist/detectors.js.map +1 -1
  56. package/dist/duplicates.d.ts.map +1 -1
  57. package/dist/duplicates.js +10 -5
  58. package/dist/duplicates.js.map +1 -1
  59. package/dist/evidence.d.ts +28 -0
  60. package/dist/evidence.d.ts.map +1 -0
  61. package/dist/evidence.js +38 -0
  62. package/dist/evidence.js.map +1 -0
  63. package/dist/fix-engine.d.ts +11 -0
  64. package/dist/fix-engine.d.ts.map +1 -1
  65. package/dist/fix-engine.js +88 -71
  66. package/dist/fix-engine.js.map +1 -1
  67. package/dist/frameworks/python.d.ts.map +1 -1
  68. package/dist/frameworks/python.js +5 -2
  69. package/dist/frameworks/python.js.map +1 -1
  70. package/dist/health-score.d.ts.map +1 -1
  71. package/dist/health-score.js +1 -0
  72. package/dist/health-score.js.map +1 -1
  73. package/dist/hooks-settings-detector.d.ts +17 -0
  74. package/dist/hooks-settings-detector.d.ts.map +1 -0
  75. package/dist/hooks-settings-detector.js +228 -0
  76. package/dist/hooks-settings-detector.js.map +1 -0
  77. package/dist/index.d.ts +22 -7
  78. package/dist/index.d.ts.map +1 -1
  79. package/dist/index.js +18 -6
  80. package/dist/index.js.map +1 -1
  81. package/dist/manifest-consistency.d.ts +6 -0
  82. package/dist/manifest-consistency.d.ts.map +1 -1
  83. package/dist/manifest-consistency.js +26 -5
  84. package/dist/manifest-consistency.js.map +1 -1
  85. package/dist/markdown-fences.d.ts +112 -0
  86. package/dist/markdown-fences.d.ts.map +1 -0
  87. package/dist/markdown-fences.js +174 -0
  88. package/dist/markdown-fences.js.map +1 -0
  89. package/dist/mcp-config-detector.d.ts +20 -0
  90. package/dist/mcp-config-detector.d.ts.map +1 -0
  91. package/dist/mcp-config-detector.js +237 -0
  92. package/dist/mcp-config-detector.js.map +1 -0
  93. package/dist/missing-context.d.ts.map +1 -1
  94. package/dist/missing-context.js +2 -52
  95. package/dist/missing-context.js.map +1 -1
  96. package/dist/model-budgets.d.ts +69 -0
  97. package/dist/model-budgets.d.ts.map +1 -0
  98. package/dist/model-budgets.js +110 -0
  99. package/dist/model-budgets.js.map +1 -0
  100. package/dist/negative-instructions.d.ts +26 -0
  101. package/dist/negative-instructions.d.ts.map +1 -0
  102. package/dist/negative-instructions.js +174 -0
  103. package/dist/negative-instructions.js.map +1 -0
  104. package/dist/path-containment.d.ts +12 -0
  105. package/dist/path-containment.d.ts.map +1 -0
  106. package/dist/path-containment.js +53 -0
  107. package/dist/path-containment.js.map +1 -0
  108. package/dist/project-type.d.ts +12 -2
  109. package/dist/project-type.d.ts.map +1 -1
  110. package/dist/project-type.js +25 -12
  111. package/dist/project-type.js.map +1 -1
  112. package/dist/prompt-cache.d.ts.map +1 -1
  113. package/dist/prompt-cache.js +26 -19
  114. package/dist/prompt-cache.js.map +1 -1
  115. package/dist/promptci-gitignore.d.ts +34 -0
  116. package/dist/promptci-gitignore.d.ts.map +1 -0
  117. package/dist/promptci-gitignore.js +42 -0
  118. package/dist/promptci-gitignore.js.map +1 -0
  119. package/dist/repo-context.d.ts +27 -0
  120. package/dist/repo-context.d.ts.map +1 -1
  121. package/dist/repo-context.js +42 -11
  122. package/dist/repo-context.js.map +1 -1
  123. package/dist/report.d.ts +1 -0
  124. package/dist/report.d.ts.map +1 -1
  125. package/dist/report.js +132 -61
  126. package/dist/report.js.map +1 -1
  127. package/dist/scan.d.ts.map +1 -1
  128. package/dist/scan.js +7 -3
  129. package/dist/scan.js.map +1 -1
  130. package/dist/scanner.d.ts +13 -1
  131. package/dist/scanner.d.ts.map +1 -1
  132. package/dist/scanner.js +49 -37
  133. package/dist/scanner.js.map +1 -1
  134. package/dist/security-pack.d.ts +20 -0
  135. package/dist/security-pack.d.ts.map +1 -1
  136. package/dist/security-pack.js +27 -10
  137. package/dist/security-pack.js.map +1 -1
  138. package/dist/skills-detector.d.ts +19 -0
  139. package/dist/skills-detector.d.ts.map +1 -0
  140. package/dist/skills-detector.js +203 -0
  141. package/dist/skills-detector.js.map +1 -0
  142. package/dist/stale-instructions.d.ts +4 -1
  143. package/dist/stale-instructions.d.ts.map +1 -1
  144. package/dist/stale-instructions.js +51 -54
  145. package/dist/stale-instructions.js.map +1 -1
  146. package/dist/subagents-detector.d.ts +19 -0
  147. package/dist/subagents-detector.d.ts.map +1 -0
  148. package/dist/subagents-detector.js +155 -0
  149. package/dist/subagents-detector.js.map +1 -0
  150. package/dist/suppression.d.ts.map +1 -1
  151. package/dist/suppression.js +11 -45
  152. package/dist/suppression.js.map +1 -1
  153. package/dist/types.d.ts +28 -3
  154. package/dist/types.d.ts.map +1 -1
  155. package/dist/types.js +25 -1
  156. package/dist/types.js.map +1 -1
  157. package/dist/vague-guidance.d.ts +15 -2
  158. package/dist/vague-guidance.d.ts.map +1 -1
  159. package/dist/vague-guidance.js +42 -40
  160. package/dist/vague-guidance.js.map +1 -1
  161. package/dist/windsurf-detector.d.ts +22 -0
  162. package/dist/windsurf-detector.d.ts.map +1 -0
  163. package/dist/windsurf-detector.js +87 -0
  164. package/dist/windsurf-detector.js.map +1 -0
  165. package/dist/within-section-dedup.d.ts +16 -0
  166. package/dist/within-section-dedup.d.ts.map +1 -0
  167. package/dist/within-section-dedup.js +153 -0
  168. package/dist/within-section-dedup.js.map +1 -0
  169. package/package.json +11 -8
  170. package/dist/cheap-model-routing.d.ts +0 -4
  171. package/dist/cheap-model-routing.d.ts.map +0 -1
  172. package/dist/cheap-model-routing.js +0 -43
  173. package/dist/cheap-model-routing.js.map +0 -1
  174. package/dist/cli-output-filter.d.ts +0 -16
  175. package/dist/cli-output-filter.d.ts.map +0 -1
  176. package/dist/cli-output-filter.js +0 -88
  177. package/dist/cli-output-filter.js.map +0 -1
  178. package/dist/context-cost.d.ts +0 -4
  179. package/dist/context-cost.d.ts.map +0 -1
  180. package/dist/context-cost.js +0 -70
  181. package/dist/context-cost.js.map +0 -1
  182. package/dist/context-recommender.d.ts +0 -3
  183. package/dist/context-recommender.d.ts.map +0 -1
  184. package/dist/context-recommender.js +0 -329
  185. package/dist/context-recommender.js.map +0 -1
  186. package/dist/llm.d.ts +0 -6
  187. package/dist/llm.d.ts.map +0 -1
  188. package/dist/llm.js +0 -67
  189. package/dist/llm.js.map +0 -1
  190. package/dist/mcp-detector.d.ts +0 -4
  191. package/dist/mcp-detector.d.ts.map +0 -1
  192. package/dist/mcp-detector.js +0 -131
  193. package/dist/mcp-detector.js.map +0 -1
  194. package/dist/native-tool-preference.d.ts +0 -20
  195. package/dist/native-tool-preference.d.ts.map +0 -1
  196. package/dist/native-tool-preference.js +0 -94
  197. package/dist/native-tool-preference.js.map +0 -1
  198. package/dist/output-verbosity.d.ts +0 -3
  199. package/dist/output-verbosity.d.ts.map +0 -1
  200. package/dist/output-verbosity.js +0 -83
  201. package/dist/output-verbosity.js.map +0 -1
  202. package/dist/product-boundary.d.ts +0 -11
  203. package/dist/product-boundary.d.ts.map +0 -1
  204. package/dist/product-boundary.js +0 -301
  205. package/dist/product-boundary.js.map +0 -1
@@ -0,0 +1,523 @@
1
+ /**
2
+ * Shared support for the AI-setup config detectors (issue #23).
3
+ *
4
+ * These detectors audit the non-markdown surfaces of a 2026 AI setup — skills,
5
+ * subagents, hooks/permissions, MCP servers, and Cursor rules — against
6
+ * filesystem reality, the same way `command-validity` verifies documented
7
+ * commands. Everything here is deterministic and offline: no network, no LLM,
8
+ * identical output for identical input.
9
+ *
10
+ * This module holds the pieces every one of those detectors needs: a minimal
11
+ * YAML-frontmatter parser, root-relative path helpers that refuse to escape the
12
+ * repo, a stable file lister, and issue path normalization.
13
+ */
14
+ import * as fs from 'node:fs';
15
+ import * as path from 'node:path';
16
+ import * as crypto from 'node:crypto';
17
+ import fg from 'fast-glob';
18
+ import micromatch from 'micromatch';
19
+ import { MAX_FILE_SIZE, BINARY_CHECK_BYTES, isBinary } from './scanner.js';
20
+ import { resolveWithinRoot } from './path-containment.js';
21
+ // ── Path helpers ──────────────────────────────────────────────────────────────
22
+ /** Convert a filesystem path to forward-slash form for stable, portable output. */
23
+ export function toPosix(p) {
24
+ return p.replace(/\\/g, '/');
25
+ }
26
+ // Re-exported so the many `resolveWithinRoot` importers under core keep their
27
+ // import path while the implementation lives in the shared path-containment
28
+ // module. Every filesystem read below routes through it so a hostile config
29
+ // value cannot point the detector at `/etc/passwd`.
30
+ export { resolveWithinRoot };
31
+ /** True when a repo-relative path exists and is a regular file. */
32
+ export function isFileWithinRoot(repoRoot, relativePath) {
33
+ const abs = resolveWithinRoot(repoRoot, relativePath);
34
+ if (!abs)
35
+ return false;
36
+ try {
37
+ return fs.statSync(abs).isFile();
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ }
43
+ /** Read a repo-relative text file, or `undefined` if it is absent/unreadable. */
44
+ export function readTextWithinRoot(repoRoot, relativePath) {
45
+ const abs = resolveWithinRoot(repoRoot, relativePath);
46
+ if (!abs)
47
+ return undefined;
48
+ try {
49
+ return fs.readFileSync(abs, 'utf8');
50
+ }
51
+ catch {
52
+ return undefined;
53
+ }
54
+ }
55
+ /**
56
+ * Directory names excluded when discovering config files: dependency trees and
57
+ * vendored copies whose own `.claude/` must never pollute the scan. This is a
58
+ * *discovery* policy — do not reuse it to answer "does glob X match anything in
59
+ * the repo" (see detectCursorRules, which needs `dist/`/`build/` visible).
60
+ */
61
+ const DISCOVERY_IGNORE = [
62
+ '**/node_modules/**', '**/.git/**', '**/dist/**', '**/build/**',
63
+ '**/worktrees/**', '**/.worktrees/**',
64
+ ];
65
+ /**
66
+ * List files under `repoRoot` matching `patterns`, returned as repo-relative
67
+ * POSIX paths in sorted order. Dotfiles are included (these configs all live in
68
+ * dot-directories). Pass `ignore` to override the default discovery excludes.
69
+ */
70
+ export function listFiles(repoRoot, patterns, ignore = DISCOVERY_IGNORE) {
71
+ let matches;
72
+ try {
73
+ matches = fg.sync(patterns, {
74
+ cwd: repoRoot,
75
+ dot: true,
76
+ onlyFiles: true,
77
+ followSymbolicLinks: false,
78
+ ignore,
79
+ });
80
+ }
81
+ catch {
82
+ return [];
83
+ }
84
+ return matches.map(toPosix).sort();
85
+ }
86
+ // ── Config-file discovery ─────────────────────────────────────────────────────
87
+ /** Repo-relative globs for each config surface the ai_config detectors audit. */
88
+ const AI_CONFIG_GLOBS = {
89
+ skills: ['.claude/**/SKILL.md'],
90
+ agents: ['.claude/agents/**/*.md'],
91
+ settings: ['.claude/settings.json', '.claude/settings.local.json'],
92
+ mcp: ['.mcp.json'],
93
+ cursorRules: ['.cursor/rules/**/*.mdc'],
94
+ // GitHub Copilot per-file instructions. The legacy single-file
95
+ // `.github/copilot-instructions.md` has no `applyTo` support, so it is NOT
96
+ // discovered here — only the per-file format under `.github/instructions/`.
97
+ copilotInstructions: ['.github/instructions/**/*.md'],
98
+ };
99
+ /** An AiConfigFiles with every surface empty — for contexts built without discovery. */
100
+ export function emptyAiConfigFiles() {
101
+ return { skills: [], agents: [], settings: [], mcp: [], cursorRules: [], copilotInstructions: [] };
102
+ }
103
+ /**
104
+ * Same size/binary gate the markdown scanner applies before reading a file
105
+ * (scanner.ts): regular file, at most MAX_FILE_SIZE bytes, no NUL byte in the
106
+ * leading BINARY_CHECK_BYTES.
107
+ */
108
+ function passesScanGuards(repoRoot, relativePath) {
109
+ const abs = resolveWithinRoot(repoRoot, relativePath);
110
+ if (!abs)
111
+ return false;
112
+ try {
113
+ const stat = fs.statSync(abs);
114
+ if (!stat.isFile() || stat.size > MAX_FILE_SIZE)
115
+ return false;
116
+ const fd = fs.openSync(abs, 'r');
117
+ try {
118
+ const head = Buffer.alloc(Math.min(stat.size, BINARY_CHECK_BYTES));
119
+ const read = fs.readSync(fd, head, 0, head.length, 0);
120
+ return !isBinary(head.subarray(0, read));
121
+ }
122
+ finally {
123
+ fs.closeSync(fd);
124
+ }
125
+ }
126
+ catch {
127
+ return false;
128
+ }
129
+ }
130
+ /**
131
+ * Discover the config files the ai_config detectors audit, honoring the same
132
+ * user-facing scan policy the markdown scanner applies: `exclude` patterns
133
+ * always remove files, an explicit `include` list bounds the scan scope (when
134
+ * unset, each surface's own globs are the scope — these configs are not in the
135
+ * scanner's DEFAULT_PATTERNS), and oversized or binary files are skipped.
136
+ *
137
+ * Called from buildRepoContext so detectors consume pre-discovered lists; a
138
+ * `--exclude` that silences a markdown finding for a file silences that file's
139
+ * ai_config findings too, instead of the detectors re-walking the repo with a
140
+ * policy of their own.
141
+ */
142
+ export function discoverAiConfigFiles(repoRoot, policy = {}) {
143
+ const ignore = [...DISCOVERY_IGNORE, ...(policy.exclude ?? [])];
144
+ const include = policy.include && policy.include.length > 0 ? policy.include : undefined;
145
+ const discover = (patterns) => {
146
+ let files = listFiles(repoRoot, [...patterns], ignore);
147
+ if (include)
148
+ files = micromatch(files, include, { dot: true }).sort();
149
+ return files.filter((file) => passesScanGuards(repoRoot, file));
150
+ };
151
+ return {
152
+ skills: discover(AI_CONFIG_GLOBS.skills),
153
+ agents: discover(AI_CONFIG_GLOBS.agents),
154
+ settings: discover(AI_CONFIG_GLOBS.settings),
155
+ mcp: discover(AI_CONFIG_GLOBS.mcp),
156
+ cursorRules: discover(AI_CONFIG_GLOBS.cursorRules),
157
+ copilotInstructions: discover(AI_CONFIG_GLOBS.copilotInstructions),
158
+ };
159
+ }
160
+ /** Stable short hash for building per-finding IDs. */
161
+ export function shortHash(input) {
162
+ return crypto.createHash('sha1').update(input).digest('hex').slice(0, 12);
163
+ }
164
+ /**
165
+ * Rewrite an issue's `filePaths`/`locations` from the detectors' repo-relative
166
+ * POSIX form to the absolute form the scanner stores in `InstructionFile.path`.
167
+ * Inline `promptci-ignore` suppressions match on exact path equality with the
168
+ * scanned file, so detector output must speak the same dialect or the
169
+ * `ai_config` category is unsuppressable. Display strings (titles, summaries,
170
+ * evidence) keep the friendly relative form; baseline fingerprints re-normalize
171
+ * absolute paths back to repo-relative, so they are unaffected.
172
+ */
173
+ export function withScannerPaths(repoRoot, issues) {
174
+ const abs = (p) => path.resolve(repoRoot, p);
175
+ return issues.map((issue) => ({
176
+ ...issue,
177
+ filePaths: issue.filePaths.map(abs),
178
+ locations: issue.locations.map((loc) => ({ ...loc, filePath: abs(loc.filePath) })),
179
+ }));
180
+ }
181
+ /** Extensions treated as invokable local scripts in hook/MCP command strings. */
182
+ export const SCRIPT_EXT_RE = /\.(sh|bash|zsh|js|mjs|cjs|ts|py|rb|ps1)$/i;
183
+ /**
184
+ * 1-based line where `needle` first appears in `raw`, or 1 when not found.
185
+ * JSON sources store strings escaped, so a parsed command containing quotes or
186
+ * backslashes never appears verbatim — fall back to the JSON-escaped form
187
+ * before giving up on a real line number.
188
+ */
189
+ export function lineOf(raw, needle) {
190
+ let idx = raw.indexOf(needle);
191
+ if (idx < 0)
192
+ idx = raw.indexOf(JSON.stringify(needle).slice(1, -1));
193
+ if (idx < 0)
194
+ return 1;
195
+ return raw.slice(0, idx).split(/\r?\n/).length;
196
+ }
197
+ function stripBom(s) {
198
+ return s.charCodeAt(0) === 0xfeff ? s.slice(1) : s;
199
+ }
200
+ /**
201
+ * A closing fence must sit at column 0. An indented `---` is content — most
202
+ * commonly a markdown horizontal rule inside a `|` block-scalar description —
203
+ * and must not terminate the frontmatter.
204
+ */
205
+ const FENCE_RE = /^(?:---|\.\.\.)[ \t]*$/;
206
+ /**
207
+ * Strip a YAML trailing comment (` #` onward) from a raw value, tracking quote
208
+ * state so a `#` inside a quoted string survives. A quote only *opens* at the
209
+ * start of the value or after whitespace/`[`/`{`/`(`/`,` — an apostrophe inside
210
+ * a word (o'brien) is content, not a quote.
211
+ */
212
+ function stripTrailingComment(raw) {
213
+ let quote = '';
214
+ let prev = '';
215
+ for (let i = 0; i < raw.length; i++) {
216
+ const c = raw[i];
217
+ if (quote) {
218
+ if (c === quote)
219
+ quote = '';
220
+ }
221
+ else if ((c === '"' || c === "'") && (prev === '' || /[\s[{(,]/.test(prev))) {
222
+ quote = c;
223
+ }
224
+ else if (c === '#' && (prev === '' || /\s/.test(prev))) {
225
+ return raw.slice(0, i).trimEnd();
226
+ }
227
+ prev = c;
228
+ }
229
+ return raw;
230
+ }
231
+ /**
232
+ * Split on top-level `,` only — commas inside quotes or `[]`/`{}`/`()` are left
233
+ * alone. Without this, a glob with a brace-expansion pattern such as a
234
+ * `{ts,tsx}` suffix would be shredded at the inner comma, producing bogus
235
+ * dead-glob findings. A quote only opens at the start of an item, so an
236
+ * apostrophe inside a path does not swallow the following commas.
237
+ */
238
+ function splitTopLevelCommas(inner) {
239
+ const parts = [];
240
+ let cur = '';
241
+ let depth = 0;
242
+ let quote = '';
243
+ for (const c of inner) {
244
+ if (quote) {
245
+ cur += c;
246
+ if (c === quote)
247
+ quote = '';
248
+ continue;
249
+ }
250
+ if ((c === '"' || c === "'") && cur.trim() === '') {
251
+ quote = c;
252
+ cur += c;
253
+ continue;
254
+ }
255
+ if (c === '[' || c === '{' || c === '(') {
256
+ depth++;
257
+ cur += c;
258
+ continue;
259
+ }
260
+ if (c === ']' || c === '}' || c === ')') {
261
+ if (depth > 0)
262
+ depth--;
263
+ cur += c;
264
+ continue;
265
+ }
266
+ if (c === ',' && depth === 0) {
267
+ parts.push(cur);
268
+ cur = '';
269
+ continue;
270
+ }
271
+ cur += c;
272
+ }
273
+ parts.push(cur);
274
+ return parts.map((s) => unquote(s.trim())).filter((s) => s !== '');
275
+ }
276
+ function unquote(s) {
277
+ if (s.length >= 2 && ((s.startsWith('"') && s.endsWith('"')) || (s.startsWith("'") && s.endsWith("'")))) {
278
+ return s.slice(1, -1);
279
+ }
280
+ return s;
281
+ }
282
+ function parseScalar(raw) {
283
+ // Comment stripping happens BEFORE dispatch so `globs: ["*.ts"] # note` is
284
+ // still recognized as a sequence, not left behind as a raw bracketed string.
285
+ const value = stripTrailingComment(raw).trim();
286
+ if (value === '')
287
+ return null;
288
+ // Fully-quoted string: contents verbatim (a `#` inside was preserved above).
289
+ const unquoted = unquote(value);
290
+ if (unquoted !== value)
291
+ return unquoted;
292
+ // Inline flow sequence: [a, b, c]
293
+ if (value.startsWith('[') && value.endsWith(']')) {
294
+ const inner = value.slice(1, -1).trim();
295
+ if (inner === '')
296
+ return [];
297
+ return splitTopLevelCommas(inner);
298
+ }
299
+ if (value === 'true')
300
+ return true;
301
+ if (value === 'false')
302
+ return false;
303
+ if (value === 'null' || value === '~')
304
+ return null;
305
+ if (/^-?\d+(\.\d+)?$/.test(value))
306
+ return Number(value);
307
+ return value;
308
+ }
309
+ /**
310
+ * Parse a minimal YAML frontmatter block from the top of a document.
311
+ *
312
+ * Handles the subset these config files actually use: top-level `key: value`
313
+ * scalars, quoted strings, booleans/numbers, inline `[a, b]` sequences, block
314
+ * lists (` - item`), and `|`/`>` block scalars. Nested mappings are recorded
315
+ * as errors rather than silently dropped, since none of the audited surfaces
316
+ * use them and a nested block is far more likely to be a mistake.
317
+ *
318
+ * This is deliberately not a full YAML engine — it exists to answer "is the
319
+ * frontmatter structurally sane and what are its top-level keys", not to load
320
+ * arbitrary YAML.
321
+ */
322
+ export function parseFrontmatter(content) {
323
+ const text = stripBom(content);
324
+ const lines = text.split(/\r?\n/);
325
+ const result = {
326
+ present: false,
327
+ closed: false,
328
+ data: {},
329
+ keyLines: {},
330
+ fenceEndLine: -1,
331
+ bodyStartLine: 1,
332
+ errors: [],
333
+ };
334
+ if (lines.length === 0 || lines[0].trim() !== '---') {
335
+ return result; // no frontmatter — whole file is body
336
+ }
337
+ result.present = true;
338
+ let lastListKey = null;
339
+ let blockScalarKey = null;
340
+ let blockScalarFold = false;
341
+ let blockScalarLines = [];
342
+ const flushBlockScalar = () => {
343
+ if (blockScalarKey === null)
344
+ return;
345
+ const joined = blockScalarFold ? blockScalarLines.join(' ') : blockScalarLines.join('\n');
346
+ result.data[blockScalarKey] = joined.trim();
347
+ blockScalarKey = null;
348
+ blockScalarLines = [];
349
+ };
350
+ let i = 1;
351
+ for (; i < lines.length; i++) {
352
+ const line = lines[i];
353
+ const trimmed = line.trim();
354
+ if (blockScalarKey !== null) {
355
+ // Block scalar continues while lines are indented or blank; the closing
356
+ // fence (column 0 only) or a non-indented line ends it.
357
+ if (FENCE_RE.test(line)) {
358
+ flushBlockScalar();
359
+ result.closed = true;
360
+ result.fenceEndLine = i + 1;
361
+ result.bodyStartLine = i + 2;
362
+ break;
363
+ }
364
+ if (trimmed === '' || /^\s/.test(line)) {
365
+ blockScalarLines.push(trimmed);
366
+ continue;
367
+ }
368
+ flushBlockScalar();
369
+ // fall through to normal handling of this line
370
+ }
371
+ if (FENCE_RE.test(line)) {
372
+ result.closed = true;
373
+ result.fenceEndLine = i + 1;
374
+ result.bodyStartLine = i + 2;
375
+ break;
376
+ }
377
+ if (trimmed === '' || trimmed.startsWith('#'))
378
+ continue;
379
+ // Block list item belonging to the most recent empty-valued key.
380
+ const listMatch = /^(\s*)-\s+(.*)$/.exec(line);
381
+ if (listMatch && lastListKey) {
382
+ const arr = result.data[lastListKey];
383
+ const item = unquote(stripTrailingComment(listMatch[2]).trim());
384
+ if (item !== '') {
385
+ if (Array.isArray(arr))
386
+ arr.push(item);
387
+ else
388
+ result.data[lastListKey] = [item];
389
+ }
390
+ continue;
391
+ }
392
+ const kvMatch = /^(\s*)([^:\s][^:]*):\s?(.*)$/.exec(line);
393
+ if (!kvMatch) {
394
+ result.errors.push(`Unparseable frontmatter line ${i + 1}: ${trimmed.slice(0, 60)}`);
395
+ continue;
396
+ }
397
+ const indent = kvMatch[1].length;
398
+ const key = kvMatch[2].trim();
399
+ const rawValue = kvMatch[3];
400
+ if (indent > 0) {
401
+ // Nested mapping — outside the supported subset.
402
+ result.errors.push(`Nested frontmatter key "${key}" on line ${i + 1} is not supported`);
403
+ continue;
404
+ }
405
+ if (Object.prototype.hasOwnProperty.call(result.data, key)) {
406
+ result.errors.push(`Duplicate frontmatter key "${key}" (line ${i + 1})`);
407
+ }
408
+ else {
409
+ result.keyLines[key] = i + 1; // first occurrence
410
+ }
411
+ const valueTrimmed = rawValue.trim();
412
+ if (valueTrimmed === '|' || valueTrimmed === '|-' || valueTrimmed === '|+' ||
413
+ valueTrimmed === '>' || valueTrimmed === '>-' || valueTrimmed === '>+') {
414
+ blockScalarKey = key;
415
+ blockScalarFold = valueTrimmed.startsWith('>');
416
+ blockScalarLines = [];
417
+ lastListKey = null;
418
+ continue;
419
+ }
420
+ if (valueTrimmed === '') {
421
+ // Either an empty scalar or the header of a block list on following lines.
422
+ result.data[key] = null;
423
+ lastListKey = key;
424
+ continue;
425
+ }
426
+ result.data[key] = parseScalar(rawValue);
427
+ lastListKey = null;
428
+ }
429
+ if (blockScalarKey !== null)
430
+ flushBlockScalar();
431
+ if (!result.closed) {
432
+ // Unterminated frontmatter — the whole remainder was consumed as fm.
433
+ result.bodyStartLine = lines.length + 1;
434
+ }
435
+ return result;
436
+ }
437
+ /** Coerce a frontmatter value that may be a list or comma/newline string into a string[]. */
438
+ export function asStringList(value) {
439
+ if (value === null || value === undefined)
440
+ return [];
441
+ if (Array.isArray(value))
442
+ return value.map((v) => String(v).trim()).filter(Boolean);
443
+ if (typeof value === 'string') {
444
+ // Top-level commas only — `*.{ts,tsx}` is one glob, not two fragments.
445
+ return value.split('\n').flatMap((line) => splitTopLevelCommas(line));
446
+ }
447
+ return [String(value)];
448
+ }
449
+ // ── Shared finding shapes ─────────────────────────────────────────────────────
450
+ /** Fill in the defaults every ai_config finding shares. */
451
+ export function aiConfigIssue(issue) {
452
+ return {
453
+ severity: 'warning',
454
+ category: 'ai_config',
455
+ confidence: 0.8,
456
+ ...issue,
457
+ };
458
+ }
459
+ /**
460
+ * The three structural frontmatter findings every frontmatter-bearing surface
461
+ * (skills, subagents, Cursor rules) shares: block missing entirely, block never
462
+ * closed, and parse errors inside the block. When the block is missing the
463
+ * caller should skip its per-surface field checks, keyed off `fm.present` as
464
+ * before — this helper only builds the findings.
465
+ */
466
+ export function frontmatterStructureIssues(filePath, content, fm, surface) {
467
+ const sid = (kind, key) => `ai-config-${surface.idPrefix}-${kind}-${shortHash(key)}`;
468
+ if (!fm.present) {
469
+ return [aiConfigIssue({
470
+ id: sid('no-frontmatter', filePath),
471
+ severity: surface.noFrontmatterSeverity ?? 'high',
472
+ title: surface.noFrontmatterTitle ?? `${surface.noun} is missing YAML frontmatter`,
473
+ summary: `${filePath} has no \`---\` frontmatter block. ${surface.why}`,
474
+ filePaths: [filePath],
475
+ locations: [{ filePath, startLine: 1, endLine: 1 }],
476
+ evidence: [`First line: ${content.split(/\r?\n/)[0]?.slice(0, 60) ?? '(empty)'}`],
477
+ recommendation: surface.recommendation,
478
+ confidence: surface.noFrontmatterConfidence ?? 0.85,
479
+ })];
480
+ }
481
+ const issues = [];
482
+ if (!fm.closed) {
483
+ issues.push(aiConfigIssue({
484
+ id: sid('unterminated', filePath),
485
+ severity: 'high',
486
+ title: `${surface.noun} frontmatter is not closed`,
487
+ summary: `${filePath} opens a \`---\` frontmatter block that is never closed.`,
488
+ filePaths: [filePath],
489
+ locations: [{ filePath, startLine: 1, endLine: 1 }],
490
+ evidence: ['Opening `---` on line 1 has no closing `---`.'],
491
+ recommendation: 'Close the frontmatter block with a `---` line.',
492
+ confidence: 0.9,
493
+ }));
494
+ }
495
+ for (const err of fm.errors) {
496
+ // BUG-007: surface a clear, user-facing finding that names the offending
497
+ // key and points at the offending line, rather than a generic "structural
498
+ // problem" anchored to the whole fence. The parser embeds the key (in
499
+ // quotes) and the line number in each error string; lift both out so the
500
+ // title and location are specific. Nested maps are the common trigger —
501
+ // e.g. `tools:` followed by an indented `read: true`.
502
+ const offendingKey = /"([^"]+)"/.exec(err)?.[1];
503
+ const errLine = Number(/\bline (\d+)\b/.exec(err)?.[1]);
504
+ const anchorLine = Number.isInteger(errLine) && errLine > 0
505
+ ? errLine
506
+ : (fm.fenceEndLine > 0 ? fm.fenceEndLine : 1);
507
+ issues.push(aiConfigIssue({
508
+ id: sid('fm-error', `${filePath}|${err}`),
509
+ title: offendingKey
510
+ ? `${surface.noun} frontmatter has an unsupported key "${offendingKey}"`
511
+ : `${surface.noun} frontmatter has a structural problem`,
512
+ summary: `${filePath}: ${err}.`,
513
+ filePaths: [filePath],
514
+ locations: [{ filePath, startLine: anchorLine, endLine: anchorLine }],
515
+ evidence: [err],
516
+ recommendation: 'Fix the frontmatter so it is valid YAML with flat, unique keys. ' +
517
+ 'Nested maps are not supported — flatten the offending key or move it into the body.',
518
+ confidence: 0.7,
519
+ }));
520
+ }
521
+ return issues;
522
+ }
523
+ //# sourceMappingURL=ai-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-config.js","sourceRoot":"","sources":["../src/ai-config.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,23 @@
1
1
  import type { PromptCiIssue, Baseline } from './types.js';
2
+ /**
3
+ * The fields identity is computed from. Structural rather than
4
+ * `PromptCiIssue`, so an issue rehydrated from a report's JSON — where
5
+ * `category` is a plain string — fingerprints identically to the live one.
6
+ */
7
+ export type FingerprintableIssue = {
8
+ category: string;
9
+ title: string;
10
+ filePaths: string[];
11
+ evidence: string[];
12
+ };
2
13
  /**
3
14
  * Computes a stable fingerprint for an issue.
4
15
  *
16
+ * This is the ONE answer to "are these two findings the same issue?" — the
17
+ * report's trend section used to carry a second, incompatible one that keyed
18
+ * on severity, so a severity bump read as "resolved + new" in the trend while
19
+ * the baseline correctly reported one issue that had worsened.
20
+ *
5
21
  * Excluded from fingerprint:
6
22
  * - `id` (may change between versions)
7
23
  * - `severity` (tracked separately for worsening detection)
@@ -11,7 +27,7 @@ import type { PromptCiIssue, Baseline } from './types.js';
11
27
  * Included:
12
28
  * - `category`, `title`, sorted `filePaths`, sorted `evidence`
13
29
  */
14
- export declare function computeFingerprint(issue: PromptCiIssue, repoRoot?: string): string;
30
+ export declare function computeFingerprint(issue: FingerprintableIssue, repoRoot?: string): string;
15
31
  /**
16
32
  * Creates a baseline from a list of active issues.
17
33
  * Produces deterministic JSON when sorted by fingerprint.
@@ -1 +1 @@
1
- {"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,aAAa,EAAiB,QAAQ,EAAE,MAAM,YAAY,CAAC;AA4ExF;;;;;;;;;;;GAWG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,aAAa,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAgBlF;AAID;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAWnF;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,QAAQ,CAiB7E;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,aAAa,EAAE,EACvB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB;IAAE,SAAS,EAAE,aAAa,EAAE,CAAC;IAAC,eAAe,EAAE,aAAa,EAAE,CAAA;CAAE,CA2BlE"}
1
+ {"version":3,"file":"baseline.d.ts","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAiB,aAAa,EAAiB,QAAQ,EAAE,MAAM,YAAY,CAAC;AA4ExF;;;;GAIG;AACH,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB,CAAC;AAEF;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,oBAAoB,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAgBzF;AAID;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,aAAa,EAAE,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,QAAQ,CAWnF;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,IAAI,QAAQ,CAiB7E;AAID;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAC7B,MAAM,EAAE,aAAa,EAAE,EACvB,QAAQ,EAAE,QAAQ,EAClB,QAAQ,CAAC,EAAE,MAAM,GAChB;IAAE,SAAS,EAAE,aAAa,EAAE,CAAC;IAAC,eAAe,EAAE,aAAa,EAAE,CAAA;CAAE,CA2BlE"}
package/dist/baseline.js CHANGED
@@ -63,10 +63,14 @@ function normalizeEvidenceForRoot(evidence, repoRoot) {
63
63
  ];
64
64
  return rootCandidates.reduce((current, normalizedRoot) => current.replaceAll(normalizedRoot, '<repo>'), normalizedEvidence);
65
65
  }
66
- // ── Fingerprinting ────────────────────────────────────────────────────────────
67
66
  /**
68
67
  * Computes a stable fingerprint for an issue.
69
68
  *
69
+ * This is the ONE answer to "are these two findings the same issue?" — the
70
+ * report's trend section used to carry a second, incompatible one that keyed
71
+ * on severity, so a severity bump read as "resolved + new" in the trend while
72
+ * the baseline correctly reported one issue that had worsened.
73
+ *
70
74
  * Excluded from fingerprint:
71
75
  * - `id` (may change between versions)
72
76
  * - `severity` (tracked separately for worsening detection)
@@ -1 +1 @@
1
- {"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"baseline.js","sourceRoot":"","sources":["../src/baseline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Buried critical-instruction detector (FEAT-011).
3
+ *
4
+ * Models weight earlier context more heavily than later context. A long file
5
+ * that puts its highest-stakes instructions ("never", "critical", "MUST NOT",
6
+ * "security", "dangerous") at the very bottom risks having them under-weighted.
7
+ *
8
+ * Heuristic: for files longer than 100 lines, find the lines carrying a
9
+ * high-severity keyword. If more than 60% of those lines sit in the bottom half
10
+ * of the file, emit an info finding suggesting the critical guidance be
11
+ * promoted toward the top.
12
+ *
13
+ * Deterministic and rule-based: no clock, network, or randomness.
14
+ */
15
+ import type { InstructionFile, PromptCiIssue } from './types.js';
16
+ export declare function detectBuriedCriticalInstructions(files: InstructionFile[]): PromptCiIssue[];
17
+ //# sourceMappingURL=buried-critical.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"buried-critical.d.ts","sourceRoot":"","sources":["../src/buried-critical.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAsCjE,wBAAgB,gCAAgC,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,aAAa,EAAE,CAqE1F"}