@promptci/core 0.0.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 (179) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +30 -0
  3. package/dist/agent-practices.d.ts +22 -0
  4. package/dist/agent-practices.d.ts.map +1 -0
  5. package/dist/agent-practices.js +582 -0
  6. package/dist/agent-practices.js.map +1 -0
  7. package/dist/baseline.d.ts +40 -0
  8. package/dist/baseline.d.ts.map +1 -0
  9. package/dist/baseline.js +172 -0
  10. package/dist/baseline.js.map +1 -0
  11. package/dist/canonical-owner.d.ts +3 -0
  12. package/dist/canonical-owner.d.ts.map +1 -0
  13. package/dist/canonical-owner.js +131 -0
  14. package/dist/canonical-owner.js.map +1 -0
  15. package/dist/cheap-model-routing.d.ts +4 -0
  16. package/dist/cheap-model-routing.d.ts.map +1 -0
  17. package/dist/cheap-model-routing.js +43 -0
  18. package/dist/cheap-model-routing.js.map +1 -0
  19. package/dist/ci-alignment.d.ts +10 -0
  20. package/dist/ci-alignment.d.ts.map +1 -0
  21. package/dist/ci-alignment.js +343 -0
  22. package/dist/ci-alignment.js.map +1 -0
  23. package/dist/cli-output-filter.d.ts +16 -0
  24. package/dist/cli-output-filter.d.ts.map +1 -0
  25. package/dist/cli-output-filter.js +88 -0
  26. package/dist/cli-output-filter.js.map +1 -0
  27. package/dist/command-validity.d.ts +12 -0
  28. package/dist/command-validity.d.ts.map +1 -0
  29. package/dist/command-validity.js +543 -0
  30. package/dist/command-validity.js.map +1 -0
  31. package/dist/conflicts.d.ts +49 -0
  32. package/dist/conflicts.d.ts.map +1 -0
  33. package/dist/conflicts.js +1065 -0
  34. package/dist/conflicts.js.map +1 -0
  35. package/dist/context-analysis.d.ts +15 -0
  36. package/dist/context-analysis.d.ts.map +1 -0
  37. package/dist/context-analysis.js +24 -0
  38. package/dist/context-analysis.js.map +1 -0
  39. package/dist/context-bloat.d.ts +17 -0
  40. package/dist/context-bloat.d.ts.map +1 -0
  41. package/dist/context-bloat.js +148 -0
  42. package/dist/context-bloat.js.map +1 -0
  43. package/dist/context-cost.d.ts +4 -0
  44. package/dist/context-cost.d.ts.map +1 -0
  45. package/dist/context-cost.js +70 -0
  46. package/dist/context-cost.js.map +1 -0
  47. package/dist/context-optimizer.d.ts +14 -0
  48. package/dist/context-optimizer.d.ts.map +1 -0
  49. package/dist/context-optimizer.js +219 -0
  50. package/dist/context-optimizer.js.map +1 -0
  51. package/dist/context-recommender.d.ts +3 -0
  52. package/dist/context-recommender.d.ts.map +1 -0
  53. package/dist/context-recommender.js +329 -0
  54. package/dist/context-recommender.js.map +1 -0
  55. package/dist/dead-references.d.ts +33 -0
  56. package/dist/dead-references.d.ts.map +1 -0
  57. package/dist/dead-references.js +607 -0
  58. package/dist/dead-references.js.map +1 -0
  59. package/dist/detectors.d.ts +11 -0
  60. package/dist/detectors.d.ts.map +1 -0
  61. package/dist/detectors.js +160 -0
  62. package/dist/detectors.js.map +1 -0
  63. package/dist/duplicates.d.ts +36 -0
  64. package/dist/duplicates.d.ts.map +1 -0
  65. package/dist/duplicates.js +436 -0
  66. package/dist/duplicates.js.map +1 -0
  67. package/dist/fix-engine.d.ts +16 -0
  68. package/dist/fix-engine.d.ts.map +1 -0
  69. package/dist/fix-engine.js +179 -0
  70. package/dist/fix-engine.js.map +1 -0
  71. package/dist/framework-packs.d.ts +8 -0
  72. package/dist/framework-packs.d.ts.map +1 -0
  73. package/dist/framework-packs.js +22 -0
  74. package/dist/framework-packs.js.map +1 -0
  75. package/dist/frameworks/dotnet.d.ts +4 -0
  76. package/dist/frameworks/dotnet.d.ts.map +1 -0
  77. package/dist/frameworks/dotnet.js +77 -0
  78. package/dist/frameworks/dotnet.js.map +1 -0
  79. package/dist/frameworks/go.d.ts +4 -0
  80. package/dist/frameworks/go.d.ts.map +1 -0
  81. package/dist/frameworks/go.js +39 -0
  82. package/dist/frameworks/go.js.map +1 -0
  83. package/dist/frameworks/nextjs.d.ts +4 -0
  84. package/dist/frameworks/nextjs.d.ts.map +1 -0
  85. package/dist/frameworks/nextjs.js +66 -0
  86. package/dist/frameworks/nextjs.js.map +1 -0
  87. package/dist/frameworks/python.d.ts +4 -0
  88. package/dist/frameworks/python.d.ts.map +1 -0
  89. package/dist/frameworks/python.js +66 -0
  90. package/dist/frameworks/python.js.map +1 -0
  91. package/dist/frameworks/rust.d.ts +4 -0
  92. package/dist/frameworks/rust.d.ts.map +1 -0
  93. package/dist/frameworks/rust.js +72 -0
  94. package/dist/frameworks/rust.js.map +1 -0
  95. package/dist/frameworks/unity.d.ts +4 -0
  96. package/dist/frameworks/unity.d.ts.map +1 -0
  97. package/dist/frameworks/unity.js +74 -0
  98. package/dist/frameworks/unity.js.map +1 -0
  99. package/dist/health-score.d.ts +37 -0
  100. package/dist/health-score.d.ts.map +1 -0
  101. package/dist/health-score.js +164 -0
  102. package/dist/health-score.js.map +1 -0
  103. package/dist/index.d.ts +31 -0
  104. package/dist/index.d.ts.map +1 -0
  105. package/dist/index.js +23 -0
  106. package/dist/index.js.map +1 -0
  107. package/dist/llm.d.ts +6 -0
  108. package/dist/llm.d.ts.map +1 -0
  109. package/dist/llm.js +67 -0
  110. package/dist/llm.js.map +1 -0
  111. package/dist/manifest-consistency.d.ts +37 -0
  112. package/dist/manifest-consistency.d.ts.map +1 -0
  113. package/dist/manifest-consistency.js +659 -0
  114. package/dist/manifest-consistency.js.map +1 -0
  115. package/dist/mcp-detector.d.ts +4 -0
  116. package/dist/mcp-detector.d.ts.map +1 -0
  117. package/dist/mcp-detector.js +131 -0
  118. package/dist/mcp-detector.js.map +1 -0
  119. package/dist/missing-context.d.ts +15 -0
  120. package/dist/missing-context.d.ts.map +1 -0
  121. package/dist/missing-context.js +517 -0
  122. package/dist/missing-context.js.map +1 -0
  123. package/dist/native-tool-preference.d.ts +20 -0
  124. package/dist/native-tool-preference.d.ts.map +1 -0
  125. package/dist/native-tool-preference.js +94 -0
  126. package/dist/native-tool-preference.js.map +1 -0
  127. package/dist/output-verbosity.d.ts +3 -0
  128. package/dist/output-verbosity.d.ts.map +1 -0
  129. package/dist/output-verbosity.js +83 -0
  130. package/dist/output-verbosity.js.map +1 -0
  131. package/dist/product-boundary.d.ts +11 -0
  132. package/dist/product-boundary.d.ts.map +1 -0
  133. package/dist/product-boundary.js +301 -0
  134. package/dist/product-boundary.js.map +1 -0
  135. package/dist/project-type.d.ts +15 -0
  136. package/dist/project-type.d.ts.map +1 -0
  137. package/dist/project-type.js +116 -0
  138. package/dist/project-type.js.map +1 -0
  139. package/dist/prompt-cache.d.ts +3 -0
  140. package/dist/prompt-cache.d.ts.map +1 -0
  141. package/dist/prompt-cache.js +151 -0
  142. package/dist/prompt-cache.js.map +1 -0
  143. package/dist/repo-context.d.ts +37 -0
  144. package/dist/repo-context.d.ts.map +1 -0
  145. package/dist/repo-context.js +221 -0
  146. package/dist/repo-context.js.map +1 -0
  147. package/dist/report.d.ts +87 -0
  148. package/dist/report.d.ts.map +1 -0
  149. package/dist/report.js +558 -0
  150. package/dist/report.js.map +1 -0
  151. package/dist/scan.d.ts +7 -0
  152. package/dist/scan.d.ts.map +1 -0
  153. package/dist/scan.js +43 -0
  154. package/dist/scan.js.map +1 -0
  155. package/dist/scanner.d.ts +3 -0
  156. package/dist/scanner.d.ts.map +1 -0
  157. package/dist/scanner.js +216 -0
  158. package/dist/scanner.js.map +1 -0
  159. package/dist/security-pack.d.ts +4 -0
  160. package/dist/security-pack.d.ts.map +1 -0
  161. package/dist/security-pack.js +403 -0
  162. package/dist/security-pack.js.map +1 -0
  163. package/dist/stale-instructions.d.ts +20 -0
  164. package/dist/stale-instructions.d.ts.map +1 -0
  165. package/dist/stale-instructions.js +269 -0
  166. package/dist/stale-instructions.js.map +1 -0
  167. package/dist/suppression.d.ts +64 -0
  168. package/dist/suppression.d.ts.map +1 -0
  169. package/dist/suppression.js +338 -0
  170. package/dist/suppression.js.map +1 -0
  171. package/dist/types.d.ts +128 -0
  172. package/dist/types.d.ts.map +1 -0
  173. package/dist/types.js +3 -0
  174. package/dist/types.js.map +1 -0
  175. package/dist/vague-guidance.d.ts +12 -0
  176. package/dist/vague-guidance.d.ts.map +1 -0
  177. package/dist/vague-guidance.js +235 -0
  178. package/dist/vague-guidance.js.map +1 -0
  179. package/package.json +54 -0
@@ -0,0 +1,517 @@
1
+ /**
2
+ * Missing-context detector for PromptCI.
3
+ *
4
+ * Flags files when they fail to mention project-specific keywords
5
+ * required for its detected type (Unity, TypeScript, etc.).
6
+ *
7
+ * The detector fires only when NONE of the required keywords are present,
8
+ * minimising false positives on repos that use custom tooling with different
9
+ * command names.
10
+ *
11
+ * All findings are heuristic — wording is cautious.
12
+ */
13
+ import * as crypto from 'node:crypto';
14
+ const PROJECT_TYPE_RULES = {
15
+ unity: {
16
+ // Any one of these signals Unity-specific coverage
17
+ keywords: ['unity', 'monobehaviour', 'gameobject', 'c#', 'unity editor', 'prefab'],
18
+ label: 'Unity',
19
+ minimumPresent: 1,
20
+ },
21
+ typescript: {
22
+ // Broad signals: build command, test runner, or tsconfig reference
23
+ keywords: [
24
+ 'npm run build', 'pnpm run build', 'yarn run build', 'pnpm build', 'npm run test',
25
+ 'npm test', 'pnpm test', 'yarn test', 'vitest', 'jest', 'typescript', 'tsconfig',
26
+ 'tsc ', 'ts-node',
27
+ ],
28
+ label: 'TypeScript',
29
+ minimumPresent: 1,
30
+ },
31
+ dotnet: {
32
+ keywords: ['dotnet', '.sln', '.csproj', 'xunit', 'nunit', 'mstest', 'dotnet test', 'dotnet build'],
33
+ label: '.NET',
34
+ minimumPresent: 1,
35
+ },
36
+ nextjs: {
37
+ keywords: ['next.js', 'nextjs', 'next build', 'next dev', 'app router', 'pages router', 'vercel'],
38
+ label: 'Next.js',
39
+ minimumPresent: 1,
40
+ },
41
+ python: {
42
+ keywords: ['python', 'pip', 'poetry', 'venv', 'pytest', 'pyproject.toml'],
43
+ label: 'Python',
44
+ minimumPresent: 1,
45
+ },
46
+ go: {
47
+ keywords: ['golang', 'go.mod', 'go test', 'go build'],
48
+ label: 'Go',
49
+ minimumPresent: 1,
50
+ },
51
+ rust: {
52
+ keywords: ['rust', 'cargo', 'cargo build', 'cargo test', 'rustc'],
53
+ label: 'Rust',
54
+ minimumPresent: 1,
55
+ },
56
+ };
57
+ function issueId(key) {
58
+ const hash = crypto.createHash('sha1').update(key).digest('hex').slice(0, 12);
59
+ return `missing-context-${hash}`;
60
+ }
61
+ /**
62
+ * MC1: raw `String.includes` let bare single-word keywords match as a
63
+ * substring of an unrelated word — 'go' inside "algorithm"/"logo"/"django"/
64
+ * "good", 'pip' inside "pipeline", 'rust' inside "trust", 'unity' inside
65
+ * "community"/"opportunity" — making the "Missing X-specific context" check
66
+ * effectively unable to ever fire for those project types.
67
+ * Multi-word/punctuated keywords ("go test", "go.mod", "c#") are specific
68
+ * enough that plain substring matching is safe and stays unchanged; only a
69
+ * BARE alphanumeric word gets a `\b` word-boundary regex instead.
70
+ */
71
+ function keywordPresent(content, keyword) {
72
+ const kw = keyword.toLowerCase();
73
+ if (/[^a-z0-9]/.test(kw))
74
+ return content.includes(kw);
75
+ return new RegExp(`\\b${kw}\\b`).test(content);
76
+ }
77
+ // ── Global checks (independent of project type) ───────────────────────────────
78
+ /**
79
+ * MC2: extracts the CONTENT of closed fenced code blocks (``` or ~~~,
80
+ * matching fence char/length per CommonMark) so setup-command patterns can be
81
+ * tested against real block content only.
82
+ *
83
+ * The old approach used a single regex per pattern like
84
+ * `` ` ```[\s\S]*?command` `` (non-greedy but unbounded) — it could skip
85
+ * straight past the block's OWN closing fence and match a command mentioned
86
+ * arbitrarily later in the file, in prose or in a completely different code
87
+ * block. A tempered-dot rewrite (`` `(?:(?!```)[\s\S])*?` ``) looked like a
88
+ * fix but has the same underlying flaw for a file with an EVEN number of
89
+ * fences: the regex engine, unable to start a match at the true opening
90
+ * fence (blocked by the closing fence ahead), backtracks to start at the
91
+ * CLOSING fence instead and treats IT as a new opener — with no third fence
92
+ * left to bound it, everything after is fair game again. Regex alone can't
93
+ * reliably track "am I inside a block" across a variable number of fences;
94
+ * a real line-by-line scan (the same fence-tracking approach used 5x
95
+ * elsewhere in this codebase) does.
96
+ */
97
+ function extractFencedBlockContents(content) {
98
+ const lines = content.split('\n');
99
+ const blocks = [];
100
+ let inBlock = false;
101
+ let fenceChar = null;
102
+ let fenceLen = 0;
103
+ let current = [];
104
+ for (const line of lines) {
105
+ if (!inBlock) {
106
+ const openMatch = /^ {0,3}([`~]{3,})/.exec(line);
107
+ if (openMatch) {
108
+ inBlock = true;
109
+ fenceChar = openMatch[1][0] ?? null;
110
+ fenceLen = openMatch[1].length;
111
+ current = [];
112
+ }
113
+ continue;
114
+ }
115
+ const closeMatch = /^ {0,3}([`~]{3,})\s*$/.exec(line);
116
+ if (closeMatch && closeMatch[1][0] === fenceChar && closeMatch[1].length >= fenceLen) {
117
+ inBlock = false;
118
+ blocks.push(current.join('\n'));
119
+ fenceChar = null;
120
+ fenceLen = 0;
121
+ continue;
122
+ }
123
+ current.push(line);
124
+ }
125
+ // An unclosed trailing fence's partial content is intentionally excluded —
126
+ // this check only counts CONFIRMED, closed blocks as real documentation.
127
+ return blocks.join('\n');
128
+ }
129
+ /** Patterns that only count when found INSIDE a fenced code block's content. */
130
+ const FENCED_SETUP_COMMAND_PATTERNS = [
131
+ /(npm|pnpm|yarn|uv)\s+(install|ci|run|sync|test|build|lint|add)/,
132
+ /dotnet\s+(build|test|restore)/,
133
+ /make\s+(build|test|install)/,
134
+ /cargo\s+(build|test)/,
135
+ /go\s+(build|test)/,
136
+ /uv\s+(run|sync|install|add)\b/,
137
+ // BUG-011: Python
138
+ /pip\s+(install|sync|download)\b/,
139
+ /python\s+-m\s+(venv|pytest|pip|build)\b/,
140
+ /python\s+setup\.py\b/,
141
+ /\bpytest\b/,
142
+ ];
143
+ /**
144
+ * Patterns that signal a setup/validation command via inline backticks or
145
+ * plain prose — these are legitimate signals regardless of surrounding
146
+ * fence context, so they're tested against the FULL content, not just
147
+ * extracted block contents.
148
+ */
149
+ const PROSE_SETUP_COMMAND_PATTERNS = [
150
+ // Inline backtick commands: `npm run build`, `pnpm test`, `uv sync`, etc.
151
+ /`(npm|pnpm|yarn)\s+(run\s+)?(build|test|lint|install)/,
152
+ /`(uv|dotnet)\s+(run|sync|install|test|build)/,
153
+ // BUG-011: Python — inline backtick
154
+ /`pip\s+(install|sync)/,
155
+ /`python\s+-m\s+(venv|pytest|pip)/,
156
+ /`pytest\b/,
157
+ // BUG-011: Go — inline backtick (fenced block pattern exists above but not inline)
158
+ /`go\s+(build|test|run|install)\b/,
159
+ // Plain prose with explicit command names
160
+ /(npm|pnpm|yarn)\s+run\s+(build|test|lint)/i,
161
+ /pnpm\s+(build|test|install|lint)/i,
162
+ /\buv\s+(run|sync|install|add)\b/i,
163
+ /\b(install|build|test)\s+(command|step|instruction)/i,
164
+ /how\s+to\s+(install|build|run|test)/i,
165
+ /getting\s+started/i,
166
+ // BUG-011: Python — plain prose
167
+ /\bpip\s+install\b/i,
168
+ /\bpython\s+-m\s+(venv|pytest|pip|build)\b/i,
169
+ /\bpytest\b/i,
170
+ /\bpython\s+setup\.py\b/i,
171
+ // BUG-011: Go — plain prose
172
+ /\bgo\s+(test|build|run)\s/i,
173
+ ];
174
+ /**
175
+ * Patterns that signal security / secret-handling awareness in instructions.
176
+ * Use non-word-boundary suffixes so plurals match: "secrets", "credentials", etc.
177
+ */
178
+ const SECURITY_PATTERNS = [
179
+ /\b(auth(entication|orization)?|rls|row.?level.?security)\b/i,
180
+ // Plurals: secrets, credentials, passwords, tokens, api keys
181
+ /\bsecrets?\b/i,
182
+ /\bcredentials?\b/i,
183
+ /\bpasswords?\b/i,
184
+ /\bapi.?keys?\b/i,
185
+ /\btokens?\b/i,
186
+ /\b(sanitize|sanitises?|injection)\b/i,
187
+ /\bvalidate\b.{0,50}\b(input|param)s?\b/i,
188
+ /\b(never\s+commit|do\s+not\s+commit|don.?t\s+commit).{0,50}\b(secret|key|credential)s?\b/i,
189
+ /\b(env(ironment)?\s+var|\.env)\b/i,
190
+ /\b(ssm|parameter\s+store|vault|keychain)\b/i,
191
+ ];
192
+ function checkSetupCommands(files) {
193
+ const combined = files.map((f) => f.content).join('\n');
194
+ const fencedContent = extractFencedBlockContents(combined);
195
+ const hasSetup = FENCED_SETUP_COMMAND_PATTERNS.some((p) => p.test(fencedContent)) ||
196
+ PROSE_SETUP_COMMAND_PATTERNS.some((p) => p.test(combined));
197
+ if (hasSetup)
198
+ return null;
199
+ return {
200
+ id: issueId('missing-setup-commands'),
201
+ severity: 'warning',
202
+ category: 'missing_context',
203
+ title: 'No setup or validation commands found',
204
+ summary: 'No instruction file appears to document how to install dependencies, build, or run tests. ' +
205
+ 'Agents cannot reliably set up or validate the project without this.',
206
+ filePaths: files.map((f) => f.path),
207
+ locations: [],
208
+ evidence: ['No install/build/test commands found in any instruction file.'],
209
+ recommendation: 'Add a "Setup" or "Getting Started" section with the exact commands to install, build, and test: ' +
210
+ 'e.g. "pnpm install && pnpm build && pnpm test".',
211
+ confidence: 0.75,
212
+ };
213
+ }
214
+ /**
215
+ * BUG-007: Detect credential sections that describe storage but omit the security
216
+ * surface (token scopes, validation, expiry/rotation).
217
+ *
218
+ * Fires when:
219
+ * 1. At least one instruction file mentions credential/token/OAuth handling
220
+ * (indicating there IS a credentials section)
221
+ * 2. But none of the files document scopes, token validation, or expiry handling
222
+ *
223
+ * This catches the pattern from GraftCLI: Security/Credentials sections that tell
224
+ * agents WHERE tokens are stored but not what scopes they need or how they expire.
225
+ */
226
+ const CREDENTIAL_SECTION_PATTERNS = [
227
+ /\b(?:oauth|api\s*key|access\s*token|bearer\s*token|personal\s*access\s*token|github\s+token|gitlab\s+token)/i,
228
+ /\bPAT\b/,
229
+ /\b(?:store[sd]?\s+(?:in|to)\s+(?:keychain|config|file)|credential\s+storage|token\s+storage)/i,
230
+ ];
231
+ const CREDENTIAL_SURFACE_PATTERNS = [
232
+ // Token scopes / permissions documented
233
+ /\b(?:scope[sd]?|permission[sd]?|required\s+scope|token\s+scope)/i,
234
+ // Validation / verification in any form (validate, validated, verifying, verified, etc.)
235
+ /\b(?:validat(?:e[sd]?|ion|ing)|verif(?:y|ied|ies|ication|ying))\b/i,
236
+ // Expiry or rotation documented
237
+ /\btoken\s+(?:expir[yi]|refresh|rotat)/i,
238
+ /\b(?:expir(?:y|ation|e[sd]?|ing)|refresh\s+token|token\s+rotation)\b/i,
239
+ ];
240
+ function checkCredentialSurface(files) {
241
+ const combined = files.map((f) => f.content).join('\n');
242
+ const hasCredentialSection = CREDENTIAL_SECTION_PATTERNS.some((p) => p.test(combined));
243
+ if (!hasCredentialSection)
244
+ return null;
245
+ const hasSurfaceDoc = CREDENTIAL_SURFACE_PATTERNS.some((p) => p.test(combined));
246
+ if (hasSurfaceDoc)
247
+ return null;
248
+ return {
249
+ id: issueId('missing-credential-surface'),
250
+ severity: 'warning',
251
+ category: 'missing_context',
252
+ title: 'Credential section missing scope/validation/expiry documentation',
253
+ summary: 'Instruction files describe credential or token storage but do not document ' +
254
+ 'required token scopes, how tokens are validated on first use, or how token ' +
255
+ 'expiry and rotation are handled. Agents cannot safely handle auth edge cases ' +
256
+ 'without this information.',
257
+ filePaths: files.map((f) => f.path),
258
+ locations: [],
259
+ evidence: ['Credential/token handling found, but no mention of scopes, validation, or expiry.'],
260
+ recommendation: 'Add documentation covering: (1) which OAuth scopes or API permissions are required, ' +
261
+ '(2) how tokens are validated on first use or after rotation, ' +
262
+ '(3) what happens when a token expires (error message, re-auth flow).',
263
+ confidence: 0.65,
264
+ };
265
+ }
266
+ function checkSecurityRules(files) {
267
+ const combined = files.map((f) => f.content).join('\n');
268
+ const hasSecurity = SECURITY_PATTERNS.some((p) => p.test(combined));
269
+ if (hasSecurity)
270
+ return null;
271
+ return {
272
+ id: issueId('missing-security-rules'),
273
+ severity: 'info',
274
+ category: 'missing_context',
275
+ title: 'No security or secret-handling rules found',
276
+ summary: 'No instruction file mentions authentication, secrets, API keys, RLS policies, or input validation. ' +
277
+ 'Agents that handle user data or credentials benefit from explicit security guidelines.',
278
+ filePaths: files.map((f) => f.path),
279
+ locations: [],
280
+ evidence: ['No auth, secrets, RLS, or input validation keywords found.'],
281
+ recommendation: 'Add a security section covering: how auth is handled, where secrets live (.env, vault), ' +
282
+ 'RLS/data-isolation requirements, and what agents must never commit or log.',
283
+ confidence: 0.65,
284
+ };
285
+ }
286
+ // ── BUG-008: Secret rotation / incident response check ───────────────────────
287
+ /**
288
+ * Patterns indicating the project handles security-sensitive credentials or keys
289
+ * (JWT, TLS, API signing keys, etc.) that require rotation procedures.
290
+ */
291
+ const SECURITY_SENSITIVE_PATTERNS = [
292
+ /\b(?:jwt|jwks|json\s+web\s+token)\b/i,
293
+ /\b(?:tls|ssl)\s+(?:cert(?:ificate)?|key)\b/i,
294
+ /\bsigning\s+key\b/i,
295
+ /\b(?:private|secret)\s+key\b/i,
296
+ /\bapi\s+(?:secret|signing)\b/i,
297
+ /\b(?:redis|kafka|broker)\s+(?:credential|password|secret|auth)\b/i,
298
+ /\bkey\s+(?:store|vault|ring)\b/i,
299
+ ];
300
+ /**
301
+ * Patterns indicating rotation, key compromise response, or incident procedures
302
+ * are documented.
303
+ */
304
+ const SECRET_ROTATION_PATTERNS = [
305
+ /\b(?:rotat(?:e[sd]?|ion|ing)|key\s+rotation|secret\s+rotation|credential\s+rotation)\b/i,
306
+ /\b(?:compromis(?:e[sd]?|ing)|key\s+compromise|breach)\b/i,
307
+ /\b(?:incident\s+response|security\s+incident)\b/i,
308
+ /\b(?:revo(?:ke[sd]?|cation)|invalidat(?:e[sd]?|ion))\b/i,
309
+ /\bwhat\s+to\s+do\s+(?:if|when)\s+.{0,40}(?:key|secret|credential|token)\b/i,
310
+ /\b(?:key|secret|token|cert(?:ificate)?)\s+expir(?:y|ation|es?|ing)\b/i,
311
+ /\brenew(?:ing|al|ed)?\s+(?:cert(?:ificate)?|key|token)\b/i,
312
+ ];
313
+ function checkSecretRotation(files) {
314
+ const combined = files.map((f) => f.content).join('\n');
315
+ const hasSensitiveKeys = SECURITY_SENSITIVE_PATTERNS.some((p) => p.test(combined));
316
+ if (!hasSensitiveKeys)
317
+ return null;
318
+ const hasRotationDocs = SECRET_ROTATION_PATTERNS.some((p) => p.test(combined));
319
+ if (hasRotationDocs)
320
+ return null;
321
+ return {
322
+ id: issueId('missing-secret-rotation'),
323
+ severity: 'warning',
324
+ category: 'missing_context',
325
+ title: 'Security-sensitive project missing secret rotation / incident response guidance',
326
+ summary: 'Instruction files reference security-sensitive credentials or cryptographic keys (JWT, TLS, signing keys, etc.) ' +
327
+ 'but do not document how to rotate them, what to do on suspected compromise, or how to handle expiry. ' +
328
+ 'Agents cannot safely manage security events without this information.',
329
+ filePaths: files.map((f) => f.path),
330
+ locations: [],
331
+ evidence: [
332
+ 'Security-sensitive key/credential references found, but no rotation, compromise, or incident-response procedure documented.',
333
+ ],
334
+ recommendation: 'Add a section covering: (1) how to rotate secrets or API keys, ' +
335
+ '(2) what to do if a key is suspected compromised (revoke, re-issue, notify), ' +
336
+ '(3) how certificate/token expiry is handled.',
337
+ confidence: 0.65,
338
+ };
339
+ }
340
+ // ── BUG-009: Environment variable list completeness check ─────────────────────
341
+ /**
342
+ * Pattern for environment variable names: ALL_CAPS identifiers of ≥ 4 chars.
343
+ * Common enough to appear in any project that uses env vars.
344
+ */
345
+ const ENV_VAR_NAME_RE = /\b([A-Z][A-Z0-9_]{3,})\b/g;
346
+ /**
347
+ * Patterns that indicate a comprehensive env var section exists.
348
+ */
349
+ const ENV_SECTION_PATTERNS = [
350
+ /\b(?:environment\s+variables?|env\s+vars?|required\s+(?:env(?:ironment)?\s+)?variables?)\b/i,
351
+ /\.env\.example\b/i,
352
+ /\benv(?:ironment)?\s+(?:setup|configuration|config)\b/i,
353
+ /\bconfigure\s+(?:your\s+)?environment\b/i,
354
+ ];
355
+ /** Common all-caps words that are not environment variable names. */
356
+ const ENV_VAR_STOP_WORDS = new Set([
357
+ 'API', 'URL', 'HTTP', 'HTML', 'JSON', 'SQL', 'JWT', 'TLS', 'SSL', 'RLS',
358
+ 'CRUD', 'REST', 'CORS', 'UUID', 'AUTH', 'NOTE', 'TODO', 'HACK', 'FIXME',
359
+ 'WARN', 'INFO', 'NULL', 'TRUE', 'FALSE', 'NONE', 'VOID', 'ENUM', 'TYPE',
360
+ 'TEST', 'PROD', 'DEV', 'DOCS', 'SKIP', 'PASS', 'FAIL', 'DONE', 'STOP',
361
+ ]);
362
+ function checkEnvVarList(files) {
363
+ const combined = files.map((f) => f.content).join('\n');
364
+ const hasEnvSection = ENV_SECTION_PATTERNS.some((p) => p.test(combined));
365
+ if (hasEnvSection)
366
+ return null;
367
+ // Count distinct env-var-like names, excluding common false-positive caps words
368
+ const envVarNames = new Set();
369
+ let m;
370
+ const re = new RegExp(ENV_VAR_NAME_RE.source, ENV_VAR_NAME_RE.flags);
371
+ while ((m = re.exec(combined)) !== null) {
372
+ const name = m[1];
373
+ if (!ENV_VAR_STOP_WORDS.has(name) && name.includes('_')) {
374
+ // Must contain an underscore — distinguishes real env vars from acronyms
375
+ envVarNames.add(name);
376
+ }
377
+ }
378
+ // Only flag when ≥ 3 distinct env vars are named but there's no dedicated section
379
+ if (envVarNames.size < 3)
380
+ return null;
381
+ const examples = [...envVarNames].slice(0, 4).join(', ');
382
+ return {
383
+ id: issueId('missing-env-var-list'),
384
+ severity: 'info',
385
+ category: 'missing_context',
386
+ title: 'Environment variables referenced but no comprehensive list documented',
387
+ summary: `Instruction files mention ${envVarNames.size} environment variable name(s) (e.g. ${examples}) ` +
388
+ `but do not have a dedicated section listing all required variables. ` +
389
+ `Agents and new developers cannot reliably set up the project without a complete env var reference.`,
390
+ filePaths: files.map((f) => f.path),
391
+ locations: [],
392
+ evidence: [`${envVarNames.size} env-var-like names found: ${examples}…`],
393
+ recommendation: 'Add an "Environment Variables" or "Required Environment" section listing every required variable, ' +
394
+ 'its purpose, and a safe example value. A .env.example file reference also satisfies this check.',
395
+ confidence: 0.6,
396
+ };
397
+ }
398
+ // ── BUG-010: Copilot topic-silence check ──────────────────────────────────────
399
+ /**
400
+ * Topics that agents commonly need guidance on. Each entry defines:
401
+ * - label: Human-readable topic name (used in the issue title/summary)
402
+ * - patterns: At least one must match in a file for it to "cover" the topic
403
+ * - recommendation: Specific advice for the copilot-instructions.md author
404
+ *
405
+ * Only fires when at least one OTHER instruction file covers the topic —
406
+ * the point is that copilot-instructions.md is the lone silent outlier.
407
+ */
408
+ const CROSS_FILE_TOPICS = [
409
+ {
410
+ label: 'file naming convention',
411
+ patterns: [
412
+ /\b(?:kebab-case|camelCase|camelcase|PascalCase|pascalcase|snake_case|naming\s+convention)\b/i,
413
+ ],
414
+ recommendation: 'Add a file naming rule to copilot-instructions.md (e.g. "Use kebab-case for all ' +
415
+ 'files") so Copilot agents apply the same convention as the rest of the codebase.',
416
+ },
417
+ ];
418
+ /**
419
+ * BUG-010: Detect when copilot-instructions.md is present but completely silent
420
+ * on a topic that is covered — and possibly in conflict — across other files.
421
+ *
422
+ * A Copilot agent reads only its own instruction file and has no guidance on the
423
+ * topic, even though human/Claude/Cursor agents are told what to do (or disagree
424
+ * about it).
425
+ */
426
+ function checkCopilotTopicSilences(files) {
427
+ const normalizedPath = (file) => file.path.replace(/\\/g, '/').toLowerCase();
428
+ const isRootCopilotFile = (file) => {
429
+ const normalized = normalizedPath(file);
430
+ return normalized === '.github/copilot-instructions.md' ||
431
+ normalized.endsWith('/.github/copilot-instructions.md');
432
+ };
433
+ const isCopilotInstructionFile = (file) => file.fileType === 'copilot' || normalizedPath(file).includes('/.github/instructions/');
434
+ const copilotFile = files.find(isRootCopilotFile);
435
+ if (!copilotFile)
436
+ return [];
437
+ const copilotFiles = files.filter(isCopilotInstructionFile);
438
+ const otherFiles = files.filter((f) => !copilotFiles.includes(f));
439
+ if (otherFiles.length === 0)
440
+ return [];
441
+ const issues = [];
442
+ for (const { label, patterns, recommendation } of CROSS_FILE_TOPICS) {
443
+ const mentionedInOthers = otherFiles.some((f) => patterns.some((p) => p.test(f.content)));
444
+ if (!mentionedInOthers)
445
+ continue; // topic not documented anywhere — skip
446
+ const mentionedInCopilot = copilotFiles.some((f) => patterns.some((p) => p.test(f.content)));
447
+ if (mentionedInCopilot)
448
+ continue; // copilot already covers it — skip
449
+ issues.push({
450
+ id: issueId(`copilot-topic-silence:${label}`),
451
+ severity: 'info',
452
+ category: 'missing_context',
453
+ title: `copilot-instructions.md is silent on: ${label}`,
454
+ summary: `Other instruction files document the ${label}, but Copilot instruction files ` +
455
+ `do not mention it. Copilot agents will have no guidance on this topic.`,
456
+ filePaths: [copilotFile.path],
457
+ locations: [{ filePath: copilotFile.path }],
458
+ evidence: [
459
+ `"${label}" is documented in other instruction files but absent from Copilot instruction files.`,
460
+ ],
461
+ recommendation,
462
+ confidence: 0.6,
463
+ });
464
+ }
465
+ return issues;
466
+ }
467
+ export function detectMissingContext(files, projectType) {
468
+ const issues = [];
469
+ // ── Global checks (always run) ────────────────────────────────────────────
470
+ const setupIssue = checkSetupCommands(files);
471
+ if (setupIssue)
472
+ issues.push(setupIssue);
473
+ const securityIssue = checkSecurityRules(files);
474
+ if (securityIssue)
475
+ issues.push(securityIssue);
476
+ // BUG-007: credential section present but security surface (scopes/validation/expiry) absent
477
+ const credSurfaceIssue = checkCredentialSurface(files);
478
+ if (credSurfaceIssue)
479
+ issues.push(credSurfaceIssue);
480
+ // BUG-008: security-sensitive keys referenced but no rotation / incident-response docs
481
+ const secretRotationIssue = checkSecretRotation(files);
482
+ if (secretRotationIssue)
483
+ issues.push(secretRotationIssue);
484
+ // BUG-009: env vars referenced but no comprehensive env var list / section
485
+ const envVarIssue = checkEnvVarList(files);
486
+ if (envVarIssue)
487
+ issues.push(envVarIssue);
488
+ // BUG-010: copilot-instructions.md missing topics covered by other files
489
+ issues.push(...checkCopilotTopicSilences(files));
490
+ // ── Project-type-specific checks ──────────────────────────────────────────
491
+ if (projectType === 'auto' || projectType === 'unknown')
492
+ return issues;
493
+ const rules = PROJECT_TYPE_RULES[projectType];
494
+ if (!rules)
495
+ return issues;
496
+ const allContent = files.map((f) => f.content.toLowerCase()).join('\n');
497
+ const present = rules.keywords.filter((kw) => keywordPresent(allContent, kw));
498
+ if (present.length < rules.minimumPresent) {
499
+ const exampleKeywords = rules.keywords.slice(0, 3).join(', ');
500
+ issues.push({
501
+ id: issueId(projectType),
502
+ severity: 'warning',
503
+ category: 'missing_context',
504
+ title: `Missing ${rules.label}-specific context`,
505
+ summary: `This appears to be a ${rules.label} project, but the instruction files do not mention ` +
506
+ `any ${rules.label}-specific context (e.g. build commands, test runner, tooling).`,
507
+ filePaths: files.map((f) => f.path),
508
+ locations: [],
509
+ evidence: [`No keywords found matching: ${exampleKeywords}…`],
510
+ recommendation: `Add a section covering ${rules.label}-specific build commands, testing conventions, ` +
511
+ `and common project patterns so AI assistants have enough context to help effectively.`,
512
+ confidence: 0.75,
513
+ });
514
+ }
515
+ return issues;
516
+ }
517
+ //# sourceMappingURL=missing-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"missing-context.js","sourceRoot":"","sources":["../src/missing-context.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,MAAM,MAAM,aAAa,CAAC;AAGtC,MAAM,kBAAkB,GAGpB;IACF,KAAK,EAAE;QACL,mDAAmD;QACnD,QAAQ,EAAE,CAAC,OAAO,EAAE,eAAe,EAAE,YAAY,EAAE,IAAI,EAAE,cAAc,EAAE,QAAQ,CAAC;QAClF,KAAK,EAAE,OAAO;QACd,cAAc,EAAE,CAAC;KAClB;IACD,UAAU,EAAE;QACV,mEAAmE;QACnE,QAAQ,EAAE;YACR,eAAe,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,YAAY,EAAE,cAAc;YACjF,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,UAAU;YAChF,MAAM,EAAE,SAAS;SAClB;QACD,KAAK,EAAE,YAAY;QACnB,cAAc,EAAE,CAAC;KAClB;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,aAAa,EAAE,cAAc,CAAC;QAClG,KAAK,EAAE,MAAM;QACb,cAAc,EAAE,CAAC;KAClB;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,CAAC,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,UAAU,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,CAAC;QACjG,KAAK,EAAE,SAAS;QAChB,cAAc,EAAE,CAAC;KAClB;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,CAAC,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,CAAC;QACzE,KAAK,EAAE,QAAQ;QACf,cAAc,EAAE,CAAC;KAClB;IACD,EAAE,EAAE;QACF,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,UAAU,CAAC;QACrD,KAAK,EAAE,IAAI;QACX,cAAc,EAAE,CAAC;KAClB;IACD,IAAI,EAAE;QACJ,QAAQ,EAAE,CAAC,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,OAAO,CAAC;QACjE,KAAK,EAAE,MAAM;QACb,cAAc,EAAE,CAAC;KAClB;CACF,CAAC;AAEF,SAAS,OAAO,CAAC,GAAW;IAC1B,MAAM,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC9E,OAAO,mBAAmB,IAAI,EAAE,CAAC;AACnC,CAAC;AAED;;;;;;;;;GASG;AACH,SAAS,cAAc,CAAC,OAAe,EAAE,OAAe;IACtD,MAAM,EAAE,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC;IACjC,IAAI,WAAW,CAAC,IAAI,CAAC,EAAE,CAAC;QAAE,OAAO,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;IACtD,OAAO,IAAI,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACjD,CAAC;AAED,iFAAiF;AAEjF;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAS,0BAA0B,CAAC,OAAe;IACjD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,MAAM,GAAa,EAAE,CAAC;IAC5B,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,SAAS,GAAkB,IAAI,CAAC;IACpC,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,OAAO,GAAa,EAAE,CAAC;IAE3B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,SAAS,GAAG,mBAAmB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACjD,IAAI,SAAS,EAAE,CAAC;gBACd,OAAO,GAAG,IAAI,CAAC;gBACf,SAAS,GAAG,SAAS,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;gBACrC,QAAQ,GAAG,SAAS,CAAC,CAAC,CAAE,CAAC,MAAM,CAAC;gBAChC,OAAO,GAAG,EAAE,CAAC;YACf,CAAC;YACD,SAAS;QACX,CAAC;QACD,MAAM,UAAU,GAAG,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,IAAI,UAAU,IAAI,UAAU,CAAC,CAAC,CAAE,CAAC,CAAC,CAAC,KAAK,SAAS,IAAI,UAAU,CAAC,CAAC,CAAE,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC;YACvF,OAAO,GAAG,KAAK,CAAC;YAChB,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC;YAChC,SAAS,GAAG,IAAI,CAAC;YACjB,QAAQ,GAAG,CAAC,CAAC;YACb,SAAS;QACX,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACrB,CAAC;IACD,2EAA2E;IAC3E,yEAAyE;IAEzE,OAAO,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC3B,CAAC;AAED,gFAAgF;AAChF,MAAM,6BAA6B,GAAG;IACpC,gEAAgE;IAChE,+BAA+B;IAC/B,6BAA6B;IAC7B,sBAAsB;IACtB,mBAAmB;IACnB,+BAA+B;IAC/B,kBAAkB;IAClB,iCAAiC;IACjC,yCAAyC;IACzC,sBAAsB;IACtB,YAAY;CACb,CAAC;AAEF;;;;;GAKG;AACH,MAAM,4BAA4B,GAAG;IACnC,0EAA0E;IAC1E,uDAAuD;IACvD,8CAA8C;IAC9C,oCAAoC;IACpC,uBAAuB;IACvB,kCAAkC;IAClC,WAAW;IACX,mFAAmF;IACnF,kCAAkC;IAClC,0CAA0C;IAC1C,4CAA4C;IAC5C,mCAAmC;IACnC,kCAAkC;IAClC,sDAAsD;IACtD,sCAAsC;IACtC,oBAAoB;IACpB,gCAAgC;IAChC,oBAAoB;IACpB,4CAA4C;IAC5C,aAAa;IACb,yBAAyB;IACzB,4BAA4B;IAC5B,4BAA4B;CAC7B,CAAC;AAEF;;;GAGG;AACH,MAAM,iBAAiB,GAAG;IACxB,6DAA6D;IAC7D,6DAA6D;IAC7D,eAAe;IACf,mBAAmB;IACnB,iBAAiB;IACjB,iBAAiB;IACjB,cAAc;IACd,sCAAsC;IACtC,yCAAyC;IACzC,2FAA2F;IAC3F,mCAAmC;IACnC,6CAA6C;CAC9C,CAAC;AAEF,SAAS,kBAAkB,CAAC,KAAwB;IAClD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,aAAa,GAAG,0BAA0B,CAAC,QAAQ,CAAC,CAAC;IAC3D,MAAM,QAAQ,GACZ,6BAA6B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAChE,4BAA4B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC7D,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC;IAE1B,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,wBAAwB,CAAC;QACrC,QAAQ,EAAE,SAAS;QACnB,QAAQ,EAAE,iBAAiB;QAC3B,KAAK,EAAE,uCAAuC;QAC9C,OAAO,EACL,4FAA4F;YAC5F,qEAAqE;QACvE,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACnC,SAAS,EAAE,EAAE;QACb,QAAQ,EAAE,CAAC,+DAA+D,CAAC;QAC3E,cAAc,EACZ,kGAAkG;YAClG,iDAAiD;QACnD,UAAU,EAAE,IAAI;KACjB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,2BAA2B,GAAG;IAClC,8GAA8G;IAC9G,SAAS;IACT,+FAA+F;CAChG,CAAC;AAEF,MAAM,2BAA2B,GAAG;IAClC,wCAAwC;IACxC,kEAAkE;IAClE,yFAAyF;IACzF,oEAAoE;IACpE,gCAAgC;IAChC,wCAAwC;IACxC,uEAAuE;CACxE,CAAC;AAEF,SAAS,sBAAsB,CAAC,KAAwB;IACtD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAExD,MAAM,oBAAoB,GAAG,2BAA2B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACvF,IAAI,CAAC,oBAAoB;QAAE,OAAO,IAAI,CAAC;IAEvC,MAAM,aAAa,GAAG,2BAA2B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAChF,IAAI,aAAa;QAAE,OAAO,IAAI,CAAC;IAE/B,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,4BAA4B,CAAC;QACzC,QAAQ,EAAE,SAAS;QACnB,QAAQ,EAAE,iBAAiB;QAC3B,KAAK,EAAE,kEAAkE;QACzE,OAAO,EACL,6EAA6E;YAC7E,6EAA6E;YAC7E,+EAA+E;YAC/E,2BAA2B;QAC7B,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACnC,SAAS,EAAE,EAAE;QACb,QAAQ,EAAE,CAAC,mFAAmF,CAAC;QAC/F,cAAc,EACZ,sFAAsF;YACtF,+DAA+D;YAC/D,sEAAsE;QACxE,UAAU,EAAE,IAAI;KACjB,CAAC;AACJ,CAAC;AAED,SAAS,kBAAkB,CAAC,KAAwB;IAClD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,WAAW,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACpE,IAAI,WAAW;QAAE,OAAO,IAAI,CAAC;IAE7B,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,wBAAwB,CAAC;QACrC,QAAQ,EAAE,MAAM;QAChB,QAAQ,EAAE,iBAAiB;QAC3B,KAAK,EAAE,4CAA4C;QACnD,OAAO,EACL,qGAAqG;YACrG,wFAAwF;QAC1F,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACnC,SAAS,EAAE,EAAE;QACb,QAAQ,EAAE,CAAC,4DAA4D,CAAC;QACxE,cAAc,EACZ,0FAA0F;YAC1F,4EAA4E;QAC9E,UAAU,EAAE,IAAI;KACjB,CAAC;AACJ,CAAC;AAED,gFAAgF;AAEhF;;;GAGG;AACH,MAAM,2BAA2B,GAAG;IAClC,sCAAsC;IACtC,6CAA6C;IAC7C,oBAAoB;IACpB,+BAA+B;IAC/B,+BAA+B;IAC/B,mEAAmE;IACnE,iCAAiC;CAClC,CAAC;AAEF;;;GAGG;AACH,MAAM,wBAAwB,GAAG;IAC/B,yFAAyF;IACzF,0DAA0D;IAC1D,kDAAkD;IAClD,yDAAyD;IACzD,4EAA4E;IAC5E,uEAAuE;IACvE,2DAA2D;CAC5D,CAAC;AAEF,SAAS,mBAAmB,CAAC,KAAwB;IACnD,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,gBAAgB,GAAG,2BAA2B,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACnF,IAAI,CAAC,gBAAgB;QAAE,OAAO,IAAI,CAAC;IAEnC,MAAM,eAAe,GAAG,wBAAwB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC/E,IAAI,eAAe;QAAE,OAAO,IAAI,CAAC;IAEjC,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,yBAAyB,CAAC;QACtC,QAAQ,EAAE,SAAS;QACnB,QAAQ,EAAE,iBAAiB;QAC3B,KAAK,EAAE,iFAAiF;QACxF,OAAO,EACL,kHAAkH;YAClH,uGAAuG;YACvG,uEAAuE;QACzE,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACnC,SAAS,EAAE,EAAE;QACb,QAAQ,EAAE;YACR,6HAA6H;SAC9H;QACD,cAAc,EACZ,iEAAiE;YACjE,+EAA+E;YAC/E,8CAA8C;QAChD,UAAU,EAAE,IAAI;KACjB,CAAC;AACJ,CAAC;AAED,iFAAiF;AAEjF;;;GAGG;AACH,MAAM,eAAe,GAAG,2BAA2B,CAAC;AAEpD;;GAEG;AACH,MAAM,oBAAoB,GAAG;IAC3B,6FAA6F;IAC7F,mBAAmB;IACnB,wDAAwD;IACxD,0CAA0C;CAC3C,CAAC;AAEF,qEAAqE;AACrE,MAAM,kBAAkB,GAAG,IAAI,GAAG,CAAC;IACjC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK;IACvE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO;IACvE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;IACvE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM;CACtE,CAAC,CAAC;AAEH,SAAS,eAAe,CAAC,KAAwB;IAC/C,MAAM,QAAQ,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxD,MAAM,aAAa,GAAG,oBAAoB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IACzE,IAAI,aAAa;QAAE,OAAO,IAAI,CAAC;IAE/B,gFAAgF;IAChF,MAAM,WAAW,GAAG,IAAI,GAAG,EAAU,CAAC;IACtC,IAAI,CAAyB,CAAC;IAC9B,MAAM,EAAE,GAAG,IAAI,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,eAAe,CAAC,KAAK,CAAC,CAAC;IACrE,OAAO,CAAC,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAE,CAAC;QACnB,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACxD,yEAAyE;YACzE,WAAW,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACxB,CAAC;IACH,CAAC;IAED,kFAAkF;IAClF,IAAI,WAAW,CAAC,IAAI,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAEtC,MAAM,QAAQ,GAAG,CAAC,GAAG,WAAW,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAEzD,OAAO;QACL,EAAE,EAAE,OAAO,CAAC,sBAAsB,CAAC;QACnC,QAAQ,EAAE,MAAM;QAChB,QAAQ,EAAE,iBAAiB;QAC3B,KAAK,EAAE,uEAAuE;QAC9E,OAAO,EACL,6BAA6B,WAAW,CAAC,IAAI,uCAAuC,QAAQ,IAAI;YAChG,sEAAsE;YACtE,oGAAoG;QACtG,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACnC,SAAS,EAAE,EAAE;QACb,QAAQ,EAAE,CAAC,GAAG,WAAW,CAAC,IAAI,8BAA8B,QAAQ,GAAG,CAAC;QACxE,cAAc,EACZ,oGAAoG;YACpG,iGAAiG;QACnG,UAAU,EAAE,GAAG;KAChB,CAAC;AACJ,CAAC;AAED,iFAAiF;AAEjF;;;;;;;;GAQG;AACH,MAAM,iBAAiB,GAIlB;IACH;QACE,KAAK,EAAE,wBAAwB;QAC/B,QAAQ,EAAE;YACR,8FAA8F;SAC/F;QACD,cAAc,EACZ,kFAAkF;YAClF,kFAAkF;KACrF;CACF,CAAC;AAEF;;;;;;;GAOG;AACH,SAAS,yBAAyB,CAAC,KAAwB;IACzD,MAAM,cAAc,GAAG,CAAC,IAAqB,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAC9F,MAAM,iBAAiB,GAAG,CAAC,IAAqB,EAAE,EAAE;QAClD,MAAM,UAAU,GAAG,cAAc,CAAC,IAAI,CAAC,CAAC;QACxC,OAAO,UAAU,KAAK,iCAAiC;YACrD,UAAU,CAAC,QAAQ,CAAC,kCAAkC,CAAC,CAAC;IAC5D,CAAC,CAAC;IACF,MAAM,wBAAwB,GAAG,CAAC,IAAqB,EAAE,EAAE,CACzD,IAAI,CAAC,QAAQ,KAAK,SAAS,IAAI,cAAc,CAAC,IAAI,CAAC,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAAC;IAEzF,MAAM,WAAW,GAAG,KAAK,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;IAClD,IAAI,CAAC,WAAW;QAAE,OAAO,EAAE,CAAC;IAE5B,MAAM,YAAY,GAAG,KAAK,CAAC,MAAM,CAAC,wBAAwB,CAAC,CAAC;IAC5D,MAAM,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC;IAClE,IAAI,UAAU,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAEvC,MAAM,MAAM,GAAoB,EAAE,CAAC;IAEnC,KAAK,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,IAAI,iBAAiB,EAAE,CAAC;QACpE,MAAM,iBAAiB,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAC9C,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CACxC,CAAC;QACF,IAAI,CAAC,iBAAiB;YAAE,SAAS,CAAC,uCAAuC;QAEzE,MAAM,kBAAkB,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CACjD,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CACxC,CAAC;QACF,IAAI,kBAAkB;YAAE,SAAS,CAAC,mCAAmC;QAErE,MAAM,CAAC,IAAI,CAAC;YACV,EAAE,EAAE,OAAO,CAAC,yBAAyB,KAAK,EAAE,CAAC;YAC7C,QAAQ,EAAE,MAAM;YAChB,QAAQ,EAAE,iBAAiB;YAC3B,KAAK,EAAE,yCAAyC,KAAK,EAAE;YACvD,OAAO,EACL,wCAAwC,KAAK,kCAAkC;gBAC/E,wEAAwE;YAC1E,SAAS,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC;YAC7B,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,WAAW,CAAC,IAAI,EAAE,CAAC;YAC3C,QAAQ,EAAE;gBACR,IAAI,KAAK,uFAAuF;aACjG;YACD,cAAc;YACd,UAAU,EAAE,GAAG;SAChB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,MAAM,UAAU,oBAAoB,CAClC,KAAwB,EACxB,WAAwB;IAExB,MAAM,MAAM,GAAoB,EAAE,CAAC;IAEnC,6EAA6E;IAC7E,MAAM,UAAU,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAC7C,IAAI,UAAU;QAAE,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAExC,MAAM,aAAa,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IAChD,IAAI,aAAa;QAAE,MAAM,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IAE9C,6FAA6F;IAC7F,MAAM,gBAAgB,GAAG,sBAAsB,CAAC,KAAK,CAAC,CAAC;IACvD,IAAI,gBAAgB;QAAE,MAAM,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC;IAEpD,uFAAuF;IACvF,MAAM,mBAAmB,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IACvD,IAAI,mBAAmB;QAAE,MAAM,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAE1D,2EAA2E;IAC3E,MAAM,WAAW,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,WAAW;QAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IAE1C,yEAAyE;IACzE,MAAM,CAAC,IAAI,CAAC,GAAG,yBAAyB,CAAC,KAAK,CAAC,CAAC,CAAC;IAEjD,6EAA6E;IAC7E,IAAI,WAAW,KAAK,MAAM,IAAI,WAAW,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IAEvE,MAAM,KAAK,GAAG,kBAAkB,CAAC,WAA8C,CAAC,CAAC;IACjF,IAAI,CAAC,KAAK;QAAE,OAAO,MAAM,CAAC;IAE1B,MAAM,UAAU,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxE,MAAM,OAAO,GAAG,KAAK,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,cAAc,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC;IAE9E,IAAI,OAAO,CAAC,MAAM,GAAG,KAAK,CAAC,cAAc,EAAE,CAAC;QAC1C,MAAM,eAAe,GAAG,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC9D,MAAM,CAAC,IAAI,CAAC;YACV,EAAE,EAAE,OAAO,CAAC,WAAW,CAAC;YACxB,QAAQ,EAAE,SAAS;YACnB,QAAQ,EAAE,iBAAiB;YAC3B,KAAK,EAAE,WAAW,KAAK,CAAC,KAAK,mBAAmB;YAChD,OAAO,EACL,wBAAwB,KAAK,CAAC,KAAK,qDAAqD;gBACxF,OAAO,KAAK,CAAC,KAAK,gEAAgE;YACpF,SAAS,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YACnC,SAAS,EAAE,EAAE;YACb,QAAQ,EAAE,CAAC,+BAA+B,eAAe,GAAG,CAAC;YAC7D,cAAc,EACZ,0BAA0B,KAAK,CAAC,KAAK,iDAAiD;gBACtF,uFAAuF;YACzF,UAAU,EAAE,IAAI;SACjB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Native tool preference detector for PromptCI.
3
+ *
4
+ * Checks whether Claude-targeted instruction files guide agents to prefer
5
+ * Claude Code's built-in tools (Read, Grep, Glob) over equivalent Bash
6
+ * commands (cat, grep, find).
7
+ *
8
+ * Why it matters:
9
+ * - Built-in tools are faster (dedicated implementation, no subprocess overhead)
10
+ * - They use fewer tokens — Glob returns compact path lists, Grep returns
11
+ * matched lines only, Read returns file content without shell noise
12
+ * - They bypass the PreToolUse hook entirely, so no RTK rewrite is needed
13
+ * - They respect Claude Code's permission model without separate Bash approval
14
+ *
15
+ * Only fires when Claude-targeted instruction files exist but contain no
16
+ * guidance about preferring these tools.
17
+ */
18
+ import type { InstructionFile, PromptCiIssue } from './types.js';
19
+ export declare function detectNativeToolPreference(files: InstructionFile[]): PromptCiIssue[];
20
+ //# sourceMappingURL=native-tool-preference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"native-tool-preference.d.ts","sourceRoot":"","sources":["../src/native-tool-preference.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAgCjE,wBAAgB,0BAA0B,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,aAAa,EAAE,CAkDpF"}
@@ -0,0 +1,94 @@
1
+ /**
2
+ * Native tool preference detector for PromptCI.
3
+ *
4
+ * Checks whether Claude-targeted instruction files guide agents to prefer
5
+ * Claude Code's built-in tools (Read, Grep, Glob) over equivalent Bash
6
+ * commands (cat, grep, find).
7
+ *
8
+ * Why it matters:
9
+ * - Built-in tools are faster (dedicated implementation, no subprocess overhead)
10
+ * - They use fewer tokens — Glob returns compact path lists, Grep returns
11
+ * matched lines only, Read returns file content without shell noise
12
+ * - They bypass the PreToolUse hook entirely, so no RTK rewrite is needed
13
+ * - They respect Claude Code's permission model without separate Bash approval
14
+ *
15
+ * Only fires when Claude-targeted instruction files exist but contain no
16
+ * guidance about preferring these tools.
17
+ */
18
+ import * as path from 'node:path';
19
+ // Positive signals — guidance about preferring native/built-in tools already exists.
20
+ // Tool names (Read, Grep, Glob) are matched case-sensitively — they are always
21
+ // capitalized when referring to Claude Code built-ins, unlike shell commands.
22
+ const NATIVE_TOOL_SIGNALS = [
23
+ /\bRead\s+tool\b/,
24
+ /\bGrep\s+tool\b/,
25
+ /\bGlob\s+tool\b/,
26
+ /\b[Uu]se\s+Read\b/,
27
+ /\b[Uu]se\s+Grep\b/,
28
+ /\b[Uu]se\s+Glob\b/,
29
+ /\b[Pp]refer\s+Read\b/,
30
+ /\b[Pp]refer\s+Grep\b/,
31
+ /\b[Pp]refer\s+Glob\b/,
32
+ /\bdedicated\s+tools?\b.*\binstead\s+of\s+(cat|grep|find)\b/i,
33
+ /\b(avoid|instead\s+of|not)\b.*\b(cat|grep|find)\b.*\b(Read|Grep|Glob)\b/,
34
+ /\b(Read|Grep|Glob)\b.*\b(instead\s+of|not|avoid)\b.*\b(cat|grep|find)\b/i,
35
+ /built[- ]in\s+tools?\s+(over|instead\s+of|not|vs\.?)\s+(bash|shell|cat|grep|find)/i,
36
+ ];
37
+ function isClaudeFile(file) {
38
+ const p = file.path.replace(/\\/g, '/').toLowerCase();
39
+ const base = path.basename(file.path).toLowerCase();
40
+ return (file.fileType === 'claude' ||
41
+ base === 'claude.md' ||
42
+ p.includes('/.claude/') ||
43
+ base === 'claude.local.md');
44
+ }
45
+ export function detectNativeToolPreference(files) {
46
+ const claudeFiles = files.filter(isClaudeFile);
47
+ if (claudeFiles.length === 0)
48
+ return [];
49
+ const combinedClaudeContent = claudeFiles.map((f) => f.content).join('\n');
50
+ const hasGuidance = NATIVE_TOOL_SIGNALS.some((pat) => pat.test(combinedClaudeContent));
51
+ if (hasGuidance)
52
+ return [];
53
+ // Also check all instruction files — guidance in AGENTS.md counts if Claude reads it.
54
+ const combinedAll = files.map((f) => f.content).join('\n');
55
+ const hasGuidanceAnywhere = NATIVE_TOOL_SIGNALS.some((pat) => pat.test(combinedAll));
56
+ if (hasGuidanceAnywhere)
57
+ return [];
58
+ return [
59
+ {
60
+ id: 'native-tool-preference-missing',
61
+ severity: 'info',
62
+ category: 'agent_practices',
63
+ title: 'No guidance to prefer built-in tools over Bash equivalents (Claude Code)',
64
+ summary: "Claude Code's built-in Read, Grep, and Glob tools are faster and more token-efficient " +
65
+ 'than their Bash equivalents (cat/head/tail, grep/rg, find). Instructions that explicitly ' +
66
+ 'prefer these tools reduce context noise and bypass the Bash permission prompt entirely. ' +
67
+ 'No such guidance was found in Claude-targeted instruction files.',
68
+ filePaths: claudeFiles.map((f) => f.path),
69
+ locations: [],
70
+ evidence: [
71
+ `No Read/Grep/Glob preference guidance found in: ${claudeFiles.map((f) => path.basename(f.path)).join(', ')}.`,
72
+ ],
73
+ recommendation: 'Add a rule to Claude-targeted files instructing agents to prefer Read over cat/head/tail, ' +
74
+ 'Grep over grep/rg, and Glob over find when working with local files. Reserve Bash for ' +
75
+ 'operations that have no dedicated tool (e.g. git commands, running tests).',
76
+ fixRecipe: [
77
+ '## Tool Selection (Claude Code)',
78
+ '',
79
+ 'Prefer dedicated tools over Bash for file operations — they are faster, more token-efficient,',
80
+ 'and bypass the Bash permission prompt:',
81
+ '',
82
+ '- **File search**: Use `Glob` (not `find` or `ls`)',
83
+ '- **Content search**: Use `Grep` (not `grep` or `rg`)',
84
+ '- **Read files**: Use `Read` (not `cat`/`head`/`tail`)',
85
+ '',
86
+ 'Reserve Bash for operations without a dedicated tool: git commands, running builds/tests,',
87
+ 'package manager commands, and other shell-only operations.',
88
+ ].join('\n'),
89
+ confidence: 0.7,
90
+ impact: 'medium',
91
+ },
92
+ ];
93
+ }
94
+ //# sourceMappingURL=native-tool-preference.js.map