@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.
- package/LICENSE +201 -0
- package/README.md +30 -0
- package/dist/agent-practices.d.ts +22 -0
- package/dist/agent-practices.d.ts.map +1 -0
- package/dist/agent-practices.js +582 -0
- package/dist/agent-practices.js.map +1 -0
- package/dist/baseline.d.ts +40 -0
- package/dist/baseline.d.ts.map +1 -0
- package/dist/baseline.js +172 -0
- package/dist/baseline.js.map +1 -0
- package/dist/canonical-owner.d.ts +3 -0
- package/dist/canonical-owner.d.ts.map +1 -0
- package/dist/canonical-owner.js +131 -0
- package/dist/canonical-owner.js.map +1 -0
- package/dist/cheap-model-routing.d.ts +4 -0
- package/dist/cheap-model-routing.d.ts.map +1 -0
- package/dist/cheap-model-routing.js +43 -0
- package/dist/cheap-model-routing.js.map +1 -0
- package/dist/ci-alignment.d.ts +10 -0
- package/dist/ci-alignment.d.ts.map +1 -0
- package/dist/ci-alignment.js +343 -0
- package/dist/ci-alignment.js.map +1 -0
- package/dist/cli-output-filter.d.ts +16 -0
- package/dist/cli-output-filter.d.ts.map +1 -0
- package/dist/cli-output-filter.js +88 -0
- package/dist/cli-output-filter.js.map +1 -0
- package/dist/command-validity.d.ts +12 -0
- package/dist/command-validity.d.ts.map +1 -0
- package/dist/command-validity.js +543 -0
- package/dist/command-validity.js.map +1 -0
- package/dist/conflicts.d.ts +49 -0
- package/dist/conflicts.d.ts.map +1 -0
- package/dist/conflicts.js +1065 -0
- package/dist/conflicts.js.map +1 -0
- package/dist/context-analysis.d.ts +15 -0
- package/dist/context-analysis.d.ts.map +1 -0
- package/dist/context-analysis.js +24 -0
- package/dist/context-analysis.js.map +1 -0
- package/dist/context-bloat.d.ts +17 -0
- package/dist/context-bloat.d.ts.map +1 -0
- package/dist/context-bloat.js +148 -0
- package/dist/context-bloat.js.map +1 -0
- package/dist/context-cost.d.ts +4 -0
- package/dist/context-cost.d.ts.map +1 -0
- package/dist/context-cost.js +70 -0
- package/dist/context-cost.js.map +1 -0
- package/dist/context-optimizer.d.ts +14 -0
- package/dist/context-optimizer.d.ts.map +1 -0
- package/dist/context-optimizer.js +219 -0
- package/dist/context-optimizer.js.map +1 -0
- package/dist/context-recommender.d.ts +3 -0
- package/dist/context-recommender.d.ts.map +1 -0
- package/dist/context-recommender.js +329 -0
- package/dist/context-recommender.js.map +1 -0
- package/dist/dead-references.d.ts +33 -0
- package/dist/dead-references.d.ts.map +1 -0
- package/dist/dead-references.js +607 -0
- package/dist/dead-references.js.map +1 -0
- package/dist/detectors.d.ts +11 -0
- package/dist/detectors.d.ts.map +1 -0
- package/dist/detectors.js +160 -0
- package/dist/detectors.js.map +1 -0
- package/dist/duplicates.d.ts +36 -0
- package/dist/duplicates.d.ts.map +1 -0
- package/dist/duplicates.js +436 -0
- package/dist/duplicates.js.map +1 -0
- package/dist/fix-engine.d.ts +16 -0
- package/dist/fix-engine.d.ts.map +1 -0
- package/dist/fix-engine.js +179 -0
- package/dist/fix-engine.js.map +1 -0
- package/dist/framework-packs.d.ts +8 -0
- package/dist/framework-packs.d.ts.map +1 -0
- package/dist/framework-packs.js +22 -0
- package/dist/framework-packs.js.map +1 -0
- package/dist/frameworks/dotnet.d.ts +4 -0
- package/dist/frameworks/dotnet.d.ts.map +1 -0
- package/dist/frameworks/dotnet.js +77 -0
- package/dist/frameworks/dotnet.js.map +1 -0
- package/dist/frameworks/go.d.ts +4 -0
- package/dist/frameworks/go.d.ts.map +1 -0
- package/dist/frameworks/go.js +39 -0
- package/dist/frameworks/go.js.map +1 -0
- package/dist/frameworks/nextjs.d.ts +4 -0
- package/dist/frameworks/nextjs.d.ts.map +1 -0
- package/dist/frameworks/nextjs.js +66 -0
- package/dist/frameworks/nextjs.js.map +1 -0
- package/dist/frameworks/python.d.ts +4 -0
- package/dist/frameworks/python.d.ts.map +1 -0
- package/dist/frameworks/python.js +66 -0
- package/dist/frameworks/python.js.map +1 -0
- package/dist/frameworks/rust.d.ts +4 -0
- package/dist/frameworks/rust.d.ts.map +1 -0
- package/dist/frameworks/rust.js +72 -0
- package/dist/frameworks/rust.js.map +1 -0
- package/dist/frameworks/unity.d.ts +4 -0
- package/dist/frameworks/unity.d.ts.map +1 -0
- package/dist/frameworks/unity.js +74 -0
- package/dist/frameworks/unity.js.map +1 -0
- package/dist/health-score.d.ts +37 -0
- package/dist/health-score.d.ts.map +1 -0
- package/dist/health-score.js +164 -0
- package/dist/health-score.js.map +1 -0
- package/dist/index.d.ts +31 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +23 -0
- package/dist/index.js.map +1 -0
- package/dist/llm.d.ts +6 -0
- package/dist/llm.d.ts.map +1 -0
- package/dist/llm.js +67 -0
- package/dist/llm.js.map +1 -0
- package/dist/manifest-consistency.d.ts +37 -0
- package/dist/manifest-consistency.d.ts.map +1 -0
- package/dist/manifest-consistency.js +659 -0
- package/dist/manifest-consistency.js.map +1 -0
- package/dist/mcp-detector.d.ts +4 -0
- package/dist/mcp-detector.d.ts.map +1 -0
- package/dist/mcp-detector.js +131 -0
- package/dist/mcp-detector.js.map +1 -0
- package/dist/missing-context.d.ts +15 -0
- package/dist/missing-context.d.ts.map +1 -0
- package/dist/missing-context.js +517 -0
- package/dist/missing-context.js.map +1 -0
- package/dist/native-tool-preference.d.ts +20 -0
- package/dist/native-tool-preference.d.ts.map +1 -0
- package/dist/native-tool-preference.js +94 -0
- package/dist/native-tool-preference.js.map +1 -0
- package/dist/output-verbosity.d.ts +3 -0
- package/dist/output-verbosity.d.ts.map +1 -0
- package/dist/output-verbosity.js +83 -0
- package/dist/output-verbosity.js.map +1 -0
- package/dist/product-boundary.d.ts +11 -0
- package/dist/product-boundary.d.ts.map +1 -0
- package/dist/product-boundary.js +301 -0
- package/dist/product-boundary.js.map +1 -0
- package/dist/project-type.d.ts +15 -0
- package/dist/project-type.d.ts.map +1 -0
- package/dist/project-type.js +116 -0
- package/dist/project-type.js.map +1 -0
- package/dist/prompt-cache.d.ts +3 -0
- package/dist/prompt-cache.d.ts.map +1 -0
- package/dist/prompt-cache.js +151 -0
- package/dist/prompt-cache.js.map +1 -0
- package/dist/repo-context.d.ts +37 -0
- package/dist/repo-context.d.ts.map +1 -0
- package/dist/repo-context.js +221 -0
- package/dist/repo-context.js.map +1 -0
- package/dist/report.d.ts +87 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +558 -0
- package/dist/report.js.map +1 -0
- package/dist/scan.d.ts +7 -0
- package/dist/scan.d.ts.map +1 -0
- package/dist/scan.js +43 -0
- package/dist/scan.js.map +1 -0
- package/dist/scanner.d.ts +3 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.js +216 -0
- package/dist/scanner.js.map +1 -0
- package/dist/security-pack.d.ts +4 -0
- package/dist/security-pack.d.ts.map +1 -0
- package/dist/security-pack.js +403 -0
- package/dist/security-pack.js.map +1 -0
- package/dist/stale-instructions.d.ts +20 -0
- package/dist/stale-instructions.d.ts.map +1 -0
- package/dist/stale-instructions.js +269 -0
- package/dist/stale-instructions.js.map +1 -0
- package/dist/suppression.d.ts +64 -0
- package/dist/suppression.d.ts.map +1 -0
- package/dist/suppression.js +338 -0
- package/dist/suppression.js.map +1 -0
- package/dist/types.d.ts +128 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/vague-guidance.d.ts +12 -0
- package/dist/vague-guidance.d.ts.map +1 -0
- package/dist/vague-guidance.js +235 -0
- package/dist/vague-guidance.js.map +1 -0
- package/package.json +54 -0
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent-practices detector for PromptCI.
|
|
3
|
+
*
|
|
4
|
+
* Checks whether instruction files include key behavioral guidelines that
|
|
5
|
+
* reduce hallucination, silent failure, and scope creep in AI agents.
|
|
6
|
+
* Each check scans the combined content of ALL instruction files — guidance
|
|
7
|
+
* can legitimately live in any file, so we don't require it to be in a
|
|
8
|
+
* specific one.
|
|
9
|
+
*
|
|
10
|
+
* Checks:
|
|
11
|
+
* - Verification loop — "run lint/tests before saying done"
|
|
12
|
+
* - Honesty policy — "report failures, don't fake success"
|
|
13
|
+
* - Read-before-edit — "read a file before editing it"
|
|
14
|
+
* - Scope control — "prefer focused diffs, don't rewrite unrelated code"
|
|
15
|
+
* - Plan-first guidance — "outline the approach before coding"
|
|
16
|
+
* - No contradictory "do not edit" header in human-maintained files
|
|
17
|
+
*
|
|
18
|
+
* All findings are info/warning severity — these are guidance gaps, not errors.
|
|
19
|
+
*/
|
|
20
|
+
import * as crypto from 'node:crypto';
|
|
21
|
+
import * as path from 'node:path';
|
|
22
|
+
const CHECKS = [
|
|
23
|
+
{
|
|
24
|
+
id: 'no-verification-loop',
|
|
25
|
+
title: 'No verification loop instruction',
|
|
26
|
+
summary: 'No instruction found asking the agent to run lint, typecheck, or tests before marking work complete. ' +
|
|
27
|
+
'Without this, agents commonly report "done" without validating that changes compile or pass tests.',
|
|
28
|
+
patterns: [
|
|
29
|
+
/run\s+(pnpm|npm|yarn)\s+(lint|test|build|typecheck)/i,
|
|
30
|
+
// AP1: a bare command mention ("pnpm build outputs to dist/") describes
|
|
31
|
+
// WHAT the command does, not an instruction to run it before finishing —
|
|
32
|
+
// exclude when immediately followed by a descriptive verb.
|
|
33
|
+
/pnpm\s+(lint|test|build|typecheck)\b(?!\s+(?:outputs?|generates?|creates?|produces?|writes?|emits?|runs?)\b)/i,
|
|
34
|
+
// AP1: `\b` prevents "npm" from matching as a substring of "pnpm" —
|
|
35
|
+
// "pnpm build outputs to dist/" would otherwise vacuously satisfy this
|
|
36
|
+
// pattern via the embedded "npm build" substring.
|
|
37
|
+
/\bnpm\s+(run\s+)?(lint|test|build)/i,
|
|
38
|
+
/before\s+(you\s+)?(say|mark|call|consider|report)\s+(it\s+)?(done|complete|finished)/i,
|
|
39
|
+
/before\s+(finishing|completing|submitting|pushing|closing)/i,
|
|
40
|
+
/work\s+is\s+not\s+complete\s+until/i,
|
|
41
|
+
],
|
|
42
|
+
recommendation: 'Add an explicit rule such as: "Before saying done, run pnpm lint && pnpm typecheck && pnpm test && pnpm build. ' +
|
|
43
|
+
'Work is not complete until all checks pass."',
|
|
44
|
+
severity: 'warning',
|
|
45
|
+
confidence: 0.75,
|
|
46
|
+
fixRecipe: 'Before saying work is complete, run pnpm lint, pnpm typecheck, pnpm test, and pnpm build. If any command fails, report the failure and stop.',
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: 'no-honesty-policy',
|
|
50
|
+
title: 'No "report failures honestly" instruction',
|
|
51
|
+
summary: 'No instruction found requiring the agent to report test failures, errors, or build breakage honestly. ' +
|
|
52
|
+
'Without this, agents may claim success when commands actually fail.',
|
|
53
|
+
patterns: [
|
|
54
|
+
/report\s+(the\s+)?(fail|error|break)/i,
|
|
55
|
+
/don.?t\s+(fake|claim|pretend)\s+success/i,
|
|
56
|
+
// AP1: word-boundary + exclude the common filler idiom "to be honest,
|
|
57
|
+
// ..." (a conversational hedge, not a directive about reporting truth),
|
|
58
|
+
// and no longer accidentally matches inside "dishonestly".
|
|
59
|
+
/(?<!to\s+be\s+)\bhonestly?\b/i,
|
|
60
|
+
/if\s+(a\s+)?(test|build|check)s?\s+(fail|break|error)/i,
|
|
61
|
+
/never\s+skip\s+(failing\s+)?test/i,
|
|
62
|
+
/never\s+hide\s+(error|fail)/i,
|
|
63
|
+
],
|
|
64
|
+
recommendation: 'Add a rule such as: "If tests fail, report the failure. Never claim success when commands error or tests are skipped."',
|
|
65
|
+
severity: 'info',
|
|
66
|
+
confidence: 0.7,
|
|
67
|
+
fixRecipe: 'If tests or commands fail, report the failure honestly. Never claim success when checks are skipped or error.',
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
id: 'no-read-before-edit',
|
|
71
|
+
title: 'No "read before edit" instruction',
|
|
72
|
+
summary: "No instruction found requiring the agent to read a file before editing it. " +
|
|
73
|
+
"Agents that edit files they haven't read may clobber context or introduce inconsistencies.",
|
|
74
|
+
patterns: [
|
|
75
|
+
/read\s+(the\s+|a\s+|any\s+)?file\s+(fully\s+|first\s+)?before\s+(edit|modif|chang)/i,
|
|
76
|
+
/read\s+(the\s+|a\s+)?file.*before\s+(edit|modif|chang)/i,
|
|
77
|
+
/must\s+(first\s+)?read\s+(the\s+|a\s+|any\s+)?(file|source|code)/i,
|
|
78
|
+
/always\s+read\s+(the\s+|a\s+)?(file|source|code)/i,
|
|
79
|
+
/inspect\s+(the\s+|a\s+)?file\s+before/i,
|
|
80
|
+
/read.*before.*edit/i,
|
|
81
|
+
// AP5: reversed word order — "Before editing, read the relevant files."
|
|
82
|
+
// The rule reads before/edit FIRST and "read" second, so the read.*edit
|
|
83
|
+
// patterns above never matched this common phrasing.
|
|
84
|
+
/before\s+(?:you\s+)?(?:edit|modif|chang)\w*\b[^.\n]{0,40}\bread(?:ing)?\b/i,
|
|
85
|
+
],
|
|
86
|
+
recommendation: 'Add a rule such as: "Always read a file fully before editing it."',
|
|
87
|
+
severity: 'info',
|
|
88
|
+
confidence: 0.7,
|
|
89
|
+
fixRecipe: 'Read a file before editing it. Preserve unrelated user changes.',
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: 'no-scope-control',
|
|
93
|
+
title: 'No scope-control instruction',
|
|
94
|
+
summary: 'No instruction found telling the agent to avoid rewriting unrelated systems or making sweeping changes. ' +
|
|
95
|
+
'Unconstrained agents often refactor things they were not asked to touch.',
|
|
96
|
+
patterns: [
|
|
97
|
+
/focused\s+(diff|change|edit|pr|commit)/i,
|
|
98
|
+
/don.?t\s+rewrite\s+unrelated/i,
|
|
99
|
+
/do\s+not\s+rewrite\s+unrelated/i,
|
|
100
|
+
/avoid\s+unrelated\s+(change|edit|refactor)/i,
|
|
101
|
+
/minimal\s+(change|diff|edit)/i,
|
|
102
|
+
/small\s+(change|diff|edit|pr|commit)/i,
|
|
103
|
+
/targeted\s+(change|edit|fix)/i,
|
|
104
|
+
/prefer\s+(small|focused|minimal)\s+(diff|change|pr)/i,
|
|
105
|
+
],
|
|
106
|
+
recommendation: 'Add a rule such as: "Prefer focused, minimal diffs. Do not rewrite or refactor code unrelated to the current task."',
|
|
107
|
+
severity: 'info',
|
|
108
|
+
confidence: 0.65,
|
|
109
|
+
fixRecipe: 'Prefer focused, minimal diffs. Do not rewrite or refactor code unrelated to the current task.',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
id: 'no-plan-first',
|
|
113
|
+
title: 'No "plan before implementing" instruction',
|
|
114
|
+
summary: 'No instruction found asking the agent to outline an approach for complex tasks before writing code. ' +
|
|
115
|
+
'Planning prompts significantly reduce wasted implementation effort on multi-file changes.',
|
|
116
|
+
patterns: [
|
|
117
|
+
/plan\s+(first|before|ahead)/i,
|
|
118
|
+
/outline\s+(the\s+)?(approach|plan|design|steps)\s+before/i,
|
|
119
|
+
/think\s+(?:through|it\s+through)\s+(?:the\s+)?(?:approach|plan|design|steps|task|problem)/i,
|
|
120
|
+
/propose\s+(a\s+)?(plan|approach|design)/i,
|
|
121
|
+
/before\s+implement\w+.*\b(plan|outline|design)\b/i,
|
|
122
|
+
/design\s+before\s+(implement|cod)/i,
|
|
123
|
+
],
|
|
124
|
+
recommendation: 'Add a rule such as: "For complex or multi-file tasks, outline the approach and get confirmation before writing code."',
|
|
125
|
+
severity: 'info',
|
|
126
|
+
confidence: 0.65,
|
|
127
|
+
fixRecipe: 'For complex or multi-file tasks, outline the approach and get confirmation before writing code.',
|
|
128
|
+
},
|
|
129
|
+
];
|
|
130
|
+
// ── Do-not-edit header check ──────────────────────────────────────────────────
|
|
131
|
+
// BUG-007: Broadened to match "DO NOT EDIT" anywhere in a line (not just at line
|
|
132
|
+
// start) so "<!-- AUTO-GENERATED — DO NOT EDIT MANUALLY -->" is caught.
|
|
133
|
+
const DO_NOT_EDIT_RE = /\bdo\s+not\s+(edit|modify|change)\b/im;
|
|
134
|
+
// BUG-H1: Also explicitly match HTML-comment auto-generated headers:
|
|
135
|
+
// <!-- AUTO-GENERATED — DO NOT EDIT -->
|
|
136
|
+
// <!-- auto-generated -->
|
|
137
|
+
// The existing DO_NOT_EDIT_RE handles "DO NOT EDIT" inside comments, but the
|
|
138
|
+
// auto-generated marker alone (without "DO NOT EDIT" text) was not matched.
|
|
139
|
+
const AUTO_GENERATED_RE = /<!--\s*auto[- ]?generated|auto[- ]?generated|<!--\s*generated\s*(?:by\s+\w+)?|this\s+file\s+is\s+generated|generated\s+by\s+\w+/i;
|
|
140
|
+
/**
|
|
141
|
+
* BUG-003 / BUG-K1: Detect Claude-specific XML tags/blocks embedded in non-Claude
|
|
142
|
+
* instruction files.
|
|
143
|
+
*
|
|
144
|
+
* Covers:
|
|
145
|
+
* 1. <claude:thinking> / <claude:…> — namespaced XML (Claude 3 tool-use format)
|
|
146
|
+
* 2. </claude:thinking> — closing tags of the same form
|
|
147
|
+
* 3. <instructions-for-claude> — outer wrapper block format
|
|
148
|
+
*
|
|
149
|
+
* All forms are only meaningful to Claude; other tools treat them as literal text.
|
|
150
|
+
*
|
|
151
|
+
* BUG-K1: Broadened from `/<claude:[a-z]/` to `/<\/?claude:[\w-]+/` to reliably
|
|
152
|
+
* catch all `<claude:*>` namespace variants regardless of tag-name length or case.
|
|
153
|
+
*/
|
|
154
|
+
const CLAUDE_XML_TAG_RE = /<\/?claude:[\w-]+|<instructions-for-claude[\s>]/i;
|
|
155
|
+
// AP6: a forwarding pointer only counts when it delegates to a *canonical
|
|
156
|
+
// instruction file* (AGENTS.md / CLAUDE.md), so a stub can defer its behavioral
|
|
157
|
+
// rules there. The target is restricted to those filenames (with an optional
|
|
158
|
+
// `./` or `.github/` prefix and surrounding backtick/quote) — a pointer to an
|
|
159
|
+
// arbitrary doc like `CONTRIBUTING.md` is NOT a behavioral-guidance delegation
|
|
160
|
+
// and must not exempt the file. The `.md` extension is required so prose like
|
|
161
|
+
// "use Claude to review" cannot match the bare word.
|
|
162
|
+
const FORWARDING_RE = /(?:follow|use|see|refer\s+to|canonical\s+(?:source|instructions?)|pointer\s+to)\s+[`'"*]*(?:\.?\/)?(?:\.github\/)?(?:AGENTS|CLAUDE)\.md\b/i;
|
|
163
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
164
|
+
function issueId(checkId) {
|
|
165
|
+
const hash = crypto.createHash('sha1').update(checkId).digest('hex').slice(0, 12);
|
|
166
|
+
return `agent-practice-${hash}`;
|
|
167
|
+
}
|
|
168
|
+
function doNotEditIssueId(filePath) {
|
|
169
|
+
const hash = crypto.createHash('sha1').update(`do-not-edit:${filePath}`).digest('hex').slice(0, 12);
|
|
170
|
+
return `do-not-edit-${hash}`;
|
|
171
|
+
}
|
|
172
|
+
function perFileBehaviorIssueId(filePath) {
|
|
173
|
+
const hash = crypto.createHash('sha1').update(`per-file-behavior:${filePath}`).digest('hex').slice(0, 12);
|
|
174
|
+
return `per-file-behavior-${hash}`;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* B1: canonical-owner.ts independently derives an id via the exact same
|
|
178
|
+
* `behavior-duplication:${filePath}` hash input for a conceptually similar
|
|
179
|
+
* (but distinct) finding — the two detectors collide on id for the same
|
|
180
|
+
* file. Namespacing the hash input per-detector keeps the ids distinct even
|
|
181
|
+
* for the same file path; the public id PREFIX is unchanged so existing
|
|
182
|
+
* baselines/tooling that pattern-match on `behavior-duplication-` still work.
|
|
183
|
+
*/
|
|
184
|
+
function matchesAny(text, patterns) {
|
|
185
|
+
return patterns.some((p) => p.test(text));
|
|
186
|
+
}
|
|
187
|
+
/** AP2: 1-indexed line number containing character offset `index` in `content`. */
|
|
188
|
+
function lineNumberAtOffset(content, index) {
|
|
189
|
+
let line = 1;
|
|
190
|
+
for (let i = 0; i < index && i < content.length; i++) {
|
|
191
|
+
if (content[i] === '\n')
|
|
192
|
+
line++;
|
|
193
|
+
}
|
|
194
|
+
return line;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* BUG-002: Strip human-facing checklist items from content before running
|
|
198
|
+
* agent-practice pattern checks.
|
|
199
|
+
*
|
|
200
|
+
* Lines like "- [ ] Run pnpm test" or "* [ ] npm run build" are PR / human
|
|
201
|
+
* checklists, not agent behavioral rules. Matching them causes the
|
|
202
|
+
* verification-loop check to incorrectly conclude the rule is present.
|
|
203
|
+
*
|
|
204
|
+
* Strips lines that:
|
|
205
|
+
* - start with optional whitespace + list marker (-, *, +, or digit.)
|
|
206
|
+
* - followed by a GitHub-style checkbox `[ ]` or `[x]`
|
|
207
|
+
*/
|
|
208
|
+
function stripChecklistLines(content) {
|
|
209
|
+
return content
|
|
210
|
+
.split('\n')
|
|
211
|
+
.filter((line) => !/^\s*(?:[-*+]|\d+\.)\s+/.test(line) || !/\[[ x?]\]/.test(line))
|
|
212
|
+
.join('\n');
|
|
213
|
+
}
|
|
214
|
+
function stripCodeBlocks(content) {
|
|
215
|
+
const lines = content.split('\n');
|
|
216
|
+
const kept = [];
|
|
217
|
+
let inBlock = false;
|
|
218
|
+
let fenceChar = null;
|
|
219
|
+
let fenceLen = 0;
|
|
220
|
+
for (const line of lines) {
|
|
221
|
+
if (!inBlock) {
|
|
222
|
+
const openMatch = /^ {0,3}([`~]{3,})/.exec(line);
|
|
223
|
+
if (openMatch) {
|
|
224
|
+
inBlock = true;
|
|
225
|
+
fenceChar = openMatch[1][0] ?? null;
|
|
226
|
+
fenceLen = openMatch[1].length;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
kept.push(line);
|
|
230
|
+
}
|
|
231
|
+
else {
|
|
232
|
+
const closeMatch = /^ {0,3}([`~]{3,})\s*$/.exec(line);
|
|
233
|
+
if (closeMatch && closeMatch[1][0] === fenceChar && closeMatch[1].length >= fenceLen) {
|
|
234
|
+
inBlock = false;
|
|
235
|
+
fenceChar = null;
|
|
236
|
+
fenceLen = 0;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return kept.join('\n');
|
|
241
|
+
}
|
|
242
|
+
function normalizedFilePath(filePath) {
|
|
243
|
+
return filePath.replace(/\\/g, '/').toLowerCase();
|
|
244
|
+
}
|
|
245
|
+
function isCopilotInstructionsFile(file) {
|
|
246
|
+
const normalizedPath = normalizedFilePath(file.path);
|
|
247
|
+
return (file.fileType === 'copilot' ||
|
|
248
|
+
normalizedPath === '.github/copilot-instructions.md' ||
|
|
249
|
+
normalizedPath.endsWith('/.github/copilot-instructions.md') ||
|
|
250
|
+
normalizedPath.includes('/.github/instructions/'));
|
|
251
|
+
}
|
|
252
|
+
function isCursorRulesFile(file) {
|
|
253
|
+
const normalizedPath = normalizedFilePath(file.path);
|
|
254
|
+
const baseName = path.basename(file.path).toLowerCase();
|
|
255
|
+
return (file.fileType === 'cursor' ||
|
|
256
|
+
baseName === '.cursorrules' ||
|
|
257
|
+
normalizedPath.includes('/.cursor/rules/'));
|
|
258
|
+
}
|
|
259
|
+
function isClaudeInstructionsFile(file) {
|
|
260
|
+
const normalizedPath = normalizedFilePath(file.path);
|
|
261
|
+
const baseName = path.basename(file.path).toLowerCase();
|
|
262
|
+
return (file.fileType === 'claude' ||
|
|
263
|
+
baseName === 'claude.md' ||
|
|
264
|
+
normalizedPath.includes('/.claude/'));
|
|
265
|
+
}
|
|
266
|
+
function perFileBehaviorTarget(file) {
|
|
267
|
+
if (isCursorRulesFile(file)) {
|
|
268
|
+
return { label: 'Cursor rules file', agent: 'Cursor agents' };
|
|
269
|
+
}
|
|
270
|
+
if (isClaudeInstructionsFile(file)) {
|
|
271
|
+
return { label: 'CLAUDE.md', agent: 'Claude' };
|
|
272
|
+
}
|
|
273
|
+
if (isCopilotInstructionsFile(file)) {
|
|
274
|
+
return { label: '.github/copilot-instructions.md', agent: 'GitHub Copilot' };
|
|
275
|
+
}
|
|
276
|
+
return undefined;
|
|
277
|
+
}
|
|
278
|
+
function isForwardingFile(file) {
|
|
279
|
+
// AP6: A header forwarding pointer near the top ("Follow AGENTS.md for the
|
|
280
|
+
// canonical instructions") delegates behavioral rules to the canonical file
|
|
281
|
+
// regardless of the stub's total length — this is the exact remedy the
|
|
282
|
+
// per-check gap findings recommend, so a file that already does it should not
|
|
283
|
+
// be flagged for "missing" guidance it forwards to.
|
|
284
|
+
const head = file.content.split(/\r?\n/).slice(0, 12).join('\n');
|
|
285
|
+
if (FORWARDING_RE.test(head))
|
|
286
|
+
return true;
|
|
287
|
+
// Legacy: a short file with a forwarding pointer anywhere.
|
|
288
|
+
return file.lineCount < 20 && FORWARDING_RE.test(file.content);
|
|
289
|
+
}
|
|
290
|
+
// ── Detector ─────────────────────────────────────────────────────────────────
|
|
291
|
+
export function detectAgentPractices(files) {
|
|
292
|
+
if (files.length === 0)
|
|
293
|
+
return [];
|
|
294
|
+
const issues = [];
|
|
295
|
+
const combinedContent = files.map((f) => f.content).join('\n');
|
|
296
|
+
// BUG-002: For the verification-loop check, strip human-facing checklist items
|
|
297
|
+
// (lines like "- [ ] Run pnpm test") so that a PR checklist doesn't satisfy the
|
|
298
|
+
// requirement for an agent behavioral directive.
|
|
299
|
+
const combinedContentNoChecklists = stripChecklistLines(combinedContent);
|
|
300
|
+
const allFilePaths = files.map((f) => f.path);
|
|
301
|
+
// ── Per-practice checks (scan combined content) ───────────────────────────
|
|
302
|
+
for (const check of CHECKS) {
|
|
303
|
+
// BUG-002: Verification loop requires an imperative agent directive, not a checklist item.
|
|
304
|
+
const contentToSearch = check.id === 'no-verification-loop'
|
|
305
|
+
? combinedContentNoChecklists
|
|
306
|
+
: combinedContent;
|
|
307
|
+
if (matchesAny(contentToSearch, check.patterns))
|
|
308
|
+
continue;
|
|
309
|
+
issues.push({
|
|
310
|
+
id: issueId(check.id),
|
|
311
|
+
severity: check.severity,
|
|
312
|
+
category: 'agent_practices',
|
|
313
|
+
title: check.title,
|
|
314
|
+
summary: check.summary,
|
|
315
|
+
filePaths: allFilePaths,
|
|
316
|
+
locations: [],
|
|
317
|
+
evidence: [`Pattern not found across ${files.length} instruction file(s).`],
|
|
318
|
+
recommendation: check.recommendation,
|
|
319
|
+
fixRecipe: check.fixRecipe,
|
|
320
|
+
confidence: check.confidence,
|
|
321
|
+
});
|
|
322
|
+
}
|
|
323
|
+
// ── Per-file behavioral gap check for tool-specific files ──────────────────
|
|
324
|
+
// Tool-specific files must stand alone. If CLAUDE.md has no behavioral rules
|
|
325
|
+
// while AGENTS.md has them, combined-content checks pass but Claude still lacks
|
|
326
|
+
// those instructions. Fires only when ALL key behavioral patterns are absent
|
|
327
|
+
// from the tool-specific file and present in at least one other file.
|
|
328
|
+
const KEY_BEHAVIORAL_PATTERNS = [
|
|
329
|
+
...CHECKS.find((c) => c.id === 'no-verification-loop').patterns,
|
|
330
|
+
...CHECKS.find((c) => c.id === 'no-honesty-policy').patterns,
|
|
331
|
+
...CHECKS.find((c) => c.id === 'no-scope-control').patterns,
|
|
332
|
+
...CHECKS.find((c) => c.id === 'no-read-before-edit').patterns,
|
|
333
|
+
];
|
|
334
|
+
const hasBehavioralGuidance = (file) => matchesAny(stripChecklistLines(file.content), KEY_BEHAVIORAL_PATTERNS);
|
|
335
|
+
// AP4: files that already get the coarse "no behavioral guidance at all"
|
|
336
|
+
// finding below. The per-check loop further down skips these — without
|
|
337
|
+
// this, a file missing ALL guidance got the coarse warning PLUS up to 5
|
|
338
|
+
// granular "missing critical guidance" issues (one per CHECKS entry),
|
|
339
|
+
// inflating the health-score penalty ~6x for a single underlying gap.
|
|
340
|
+
const filesWithNoGuidanceFinding = new Set();
|
|
341
|
+
for (const file of files) {
|
|
342
|
+
const target = perFileBehaviorTarget(file);
|
|
343
|
+
if (!target)
|
|
344
|
+
continue;
|
|
345
|
+
if (hasBehavioralGuidance(file)) {
|
|
346
|
+
// ── Duplication vs Forwarding Check ────────────────────────────────────
|
|
347
|
+
continue;
|
|
348
|
+
}
|
|
349
|
+
// It lacks behavioral guidance. Is it a forwarding file?
|
|
350
|
+
if (isForwardingFile(file))
|
|
351
|
+
continue;
|
|
352
|
+
// At least one OTHER file must have behavioral guidance — otherwise the
|
|
353
|
+
// combined-content check already fires and we'd be duplicating the finding.
|
|
354
|
+
const fileWithGuidance = files.find((f) => f !== file && hasBehavioralGuidance(f));
|
|
355
|
+
if (!fileWithGuidance)
|
|
356
|
+
continue;
|
|
357
|
+
filesWithNoGuidanceFinding.add(file.path);
|
|
358
|
+
issues.push({
|
|
359
|
+
id: perFileBehaviorIssueId(file.path),
|
|
360
|
+
severity: 'warning',
|
|
361
|
+
category: 'agent_practices',
|
|
362
|
+
title: `No behavioral guidance in ${target.label}`,
|
|
363
|
+
summary: `The ${target.label} contains no agent behavioral rules (verification loop, honesty policy, ` +
|
|
364
|
+
`scope control, read-before-edit). ${target.agent} may rely on this file, so they will have no behavioral ` +
|
|
365
|
+
`guidance even though another instruction file in the repo contains related rules.`,
|
|
366
|
+
filePaths: [file.path],
|
|
367
|
+
locations: [{ filePath: file.path }],
|
|
368
|
+
evidence: [
|
|
369
|
+
`No behavioral patterns found in ${path.basename(file.path)}.`,
|
|
370
|
+
`Some behavioral guidance appears in ${path.basename(fileWithGuidance.path)}.`,
|
|
371
|
+
],
|
|
372
|
+
recommendation: `Add at minimum: a verification rule ("run tests before saying done"), a scope-control rule ` +
|
|
373
|
+
`("prefer focused diffs"), a read-before-edit rule, and an honesty policy directly to ` +
|
|
374
|
+
`${target.label} so behavioral guidance is available in the file this tool reads. ` +
|
|
375
|
+
`Alternatively, use a forwarding pointer if the tool supports it (e.g., "Follow AGENTS.md").`,
|
|
376
|
+
confidence: 0.8,
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
// ── Specific guidance cross-file gap check ──────────────────────────────────
|
|
380
|
+
// For each check in CHECKS, if the guidance exists in some file (like AGENTS.md),
|
|
381
|
+
// but is missing in a tool-specific file (which is not a forwarding file),
|
|
382
|
+
// emit a warning.
|
|
383
|
+
for (const check of CHECKS) {
|
|
384
|
+
const sourceFile = files.find((f) => matchesAny(stripChecklistLines(f.content), check.patterns));
|
|
385
|
+
if (!sourceFile)
|
|
386
|
+
continue;
|
|
387
|
+
for (const file of files) {
|
|
388
|
+
if (file === sourceFile)
|
|
389
|
+
continue;
|
|
390
|
+
const target = perFileBehaviorTarget(file);
|
|
391
|
+
if (!target)
|
|
392
|
+
continue;
|
|
393
|
+
if (isForwardingFile(file))
|
|
394
|
+
continue;
|
|
395
|
+
// AP4: already covered by the coarser "no behavioral guidance at all"
|
|
396
|
+
// finding above — don't also pile on a granular per-check issue.
|
|
397
|
+
if (filesWithNoGuidanceFinding.has(file.path))
|
|
398
|
+
continue;
|
|
399
|
+
const hasThisGuidance = matchesAny(stripChecklistLines(file.content), check.patterns);
|
|
400
|
+
if (!hasThisGuidance) {
|
|
401
|
+
const checkName = check.title.toLowerCase().replace('no ', '').replace(' instruction', '');
|
|
402
|
+
const hash = crypto.createHash('sha1').update(`behavior-gap:${file.path}:${check.id}`).digest('hex').slice(0, 12);
|
|
403
|
+
issues.push({
|
|
404
|
+
id: `per-file-behavioral-gap-${hash}`,
|
|
405
|
+
severity: 'warning',
|
|
406
|
+
category: 'agent_practices',
|
|
407
|
+
title: `Missing critical guidance "${check.title.replace('No ', '').replace(' instruction', '')}" in ${target.label}`,
|
|
408
|
+
summary: `The ${target.label} lacks the critical behavioral guidance regarding "${checkName}" ` +
|
|
409
|
+
`which is defined in ${path.basename(sourceFile.path)}. Since ${target.agent} reads ${target.label}, ` +
|
|
410
|
+
`they will miss this rule.`,
|
|
411
|
+
filePaths: [file.path, sourceFile.path],
|
|
412
|
+
locations: [{ filePath: file.path }],
|
|
413
|
+
evidence: [
|
|
414
|
+
`Guidance pattern not found in ${path.basename(file.path)}.`,
|
|
415
|
+
`Guidance defined in ${path.basename(sourceFile.path)}.`,
|
|
416
|
+
],
|
|
417
|
+
recommendation: `Add the missing guidance to ${target.label} (e.g., "${check.recommendation.replace('Add a rule such as: ', '').replace(/"/g, '')}") ` +
|
|
418
|
+
`or use a forwarding pointer (e.g., "Follow ${path.basename(sourceFile.path)}").`,
|
|
419
|
+
fixRecipe: check.fixRecipe,
|
|
420
|
+
confidence: 0.8,
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
// ── BUG-007: Agent-behavior section in copilot-instructions.md ──────────────
|
|
426
|
+
// Copilot-instructions.md is scoped to GitHub Copilot. When it contains a
|
|
427
|
+
// section heading like "## Agent Behavior" or "## Behavior Rules", the author
|
|
428
|
+
// likely intended it as an AGENTS.md-style file and mis-placed it. Flag this
|
|
429
|
+
// so the content can be moved or the structure clarified.
|
|
430
|
+
//
|
|
431
|
+
// Only fires when the section ALSO contains at least one agent-practice pattern —
|
|
432
|
+
// a heading alone could be a coincidence ("Behavior" in a broader context).
|
|
433
|
+
const AGENT_BEHAVIOR_HEADING_RE = /^#{1,4}\s+(?:agent\s+behavior|ai\s+behavior|agent\s+rules|behavior\s+rules|agent\s+instructions)\b/im;
|
|
434
|
+
const copilotAgentBehaviorId = (p) => {
|
|
435
|
+
const h = crypto.createHash('sha1').update(`copilot-agent-behavior:${p}`).digest('hex').slice(0, 12);
|
|
436
|
+
return `copilot-agent-behavior-${h}`;
|
|
437
|
+
};
|
|
438
|
+
const hasGeneralBehaviorOnlyInCopilot = () => {
|
|
439
|
+
const copilotFiles = files.filter(isCopilotInstructionsFile);
|
|
440
|
+
if (copilotFiles.length === 0)
|
|
441
|
+
return false;
|
|
442
|
+
const hasGuidanceInCopilot = copilotFiles.some(hasBehavioralGuidance);
|
|
443
|
+
if (!hasGuidanceInCopilot)
|
|
444
|
+
return false;
|
|
445
|
+
const otherFiles = files.filter(f => !isCopilotInstructionsFile(f) && f.fileType !== 'readme' && f.fileType !== 'docs');
|
|
446
|
+
return !otherFiles.some(hasBehavioralGuidance);
|
|
447
|
+
};
|
|
448
|
+
if (hasGeneralBehaviorOnlyInCopilot()) {
|
|
449
|
+
const copilotFile = files.find(isCopilotInstructionsFile);
|
|
450
|
+
issues.push({
|
|
451
|
+
id: copilotAgentBehaviorId(copilotFile.path),
|
|
452
|
+
severity: 'warning',
|
|
453
|
+
category: 'structure',
|
|
454
|
+
title: 'General behavioral guidance placed only in Copilot instructions',
|
|
455
|
+
summary: 'General agent behavioral rules (verification loop, honesty policy, etc.) appear only in ' +
|
|
456
|
+
'Copilot-specific instruction files. Other agents (Claude, Cursor, etc.) will likely ' +
|
|
457
|
+
'not see these rules.',
|
|
458
|
+
filePaths: [copilotFile.path],
|
|
459
|
+
locations: [{ filePath: copilotFile.path }],
|
|
460
|
+
evidence: ['Behavioral directives found only in Copilot-specific files.'],
|
|
461
|
+
recommendation: 'Move general agent behavior rules to AGENTS.md or CLAUDE.md so all agents benefit. ' +
|
|
462
|
+
'Keep copilot-instructions.md focused on Copilot-specific context.',
|
|
463
|
+
confidence: 0.7,
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
for (const file of files) {
|
|
467
|
+
if (file.fileType !== 'copilot')
|
|
468
|
+
continue;
|
|
469
|
+
if (!AGENT_BEHAVIOR_HEADING_RE.test(file.content))
|
|
470
|
+
continue;
|
|
471
|
+
if (!matchesAny(file.content, KEY_BEHAVIORAL_PATTERNS))
|
|
472
|
+
continue;
|
|
473
|
+
// Check if we already flagged this via general-behavior-only check
|
|
474
|
+
if (issues.some(i => i.id === copilotAgentBehaviorId(file.path)))
|
|
475
|
+
continue;
|
|
476
|
+
issues.push({
|
|
477
|
+
id: copilotAgentBehaviorId(file.path),
|
|
478
|
+
severity: 'warning',
|
|
479
|
+
category: 'structure',
|
|
480
|
+
title: 'Agent-behavior section in copilot-instructions.md',
|
|
481
|
+
summary: 'copilot-instructions.md contains an "Agent Behavior" (or similar) heading with ' +
|
|
482
|
+
'agent-practice directives. This file is scoped to GitHub Copilot; agent behavioral ' +
|
|
483
|
+
'rules are typically placed in AGENTS.md or CLAUDE.md where all agents can read them.',
|
|
484
|
+
filePaths: [file.path],
|
|
485
|
+
locations: [{ filePath: file.path }],
|
|
486
|
+
evidence: ['Agent behavioral directives found inside a Copilot-specific instruction file.'],
|
|
487
|
+
recommendation: 'Move agent behavior rules to AGENTS.md or CLAUDE.md, or copy them there so all agents ' +
|
|
488
|
+
'benefit. Keep copilot-instructions.md focused on Copilot-specific context (repo structure, ' +
|
|
489
|
+
'technology stack, Copilot feature preferences).',
|
|
490
|
+
confidence: 0.65,
|
|
491
|
+
});
|
|
492
|
+
}
|
|
493
|
+
// ── "Do not edit" in human-maintained files ───────────────────────────────
|
|
494
|
+
// Flag instruction files that contain a "do not edit" / "auto-generated"
|
|
495
|
+
// header but are clearly human-maintained (no generator comment nearby).
|
|
496
|
+
for (const file of files) {
|
|
497
|
+
const first500 = file.content.slice(0, 500);
|
|
498
|
+
const doNotEditMatch = DO_NOT_EDIT_RE.exec(first500);
|
|
499
|
+
const hasDoNotEdit = !!doNotEditMatch;
|
|
500
|
+
const hasGeneratorNote = AUTO_GENERATED_RE.test(first500);
|
|
501
|
+
// AP2: DO_NOT_EDIT_RE searches the first 500 chars of the file, not just
|
|
502
|
+
// the first line — a header on line 8 was previously always reported at
|
|
503
|
+
// the hardcoded lines 1-3. Compute the REAL line the match landed on.
|
|
504
|
+
const doNotEditLine = doNotEditMatch ? lineNumberAtOffset(file.content, doNotEditMatch.index) : 1;
|
|
505
|
+
if (hasDoNotEdit && !hasGeneratorNote) {
|
|
506
|
+
// Case 1 (original): "do not edit" present but NO generator note.
|
|
507
|
+
issues.push({
|
|
508
|
+
id: doNotEditIssueId(file.path),
|
|
509
|
+
severity: 'warning',
|
|
510
|
+
category: 'agent_practices',
|
|
511
|
+
title: '"Do not edit" header in human-maintained instruction file',
|
|
512
|
+
summary: `This instruction file starts with a "do not edit" directive but does not appear to be auto-generated. ` +
|
|
513
|
+
`Humans and agents need to update instruction files regularly — this header may prevent necessary edits.`,
|
|
514
|
+
filePaths: [file.path],
|
|
515
|
+
locations: [{ filePath: file.path, startLine: doNotEditLine, endLine: doNotEditLine }],
|
|
516
|
+
evidence: [doNotEditMatch[0].trim()],
|
|
517
|
+
recommendation: 'Remove the "do not edit" header, or add a clear generator comment explaining which tool generates this file.',
|
|
518
|
+
confidence: 0.8,
|
|
519
|
+
});
|
|
520
|
+
}
|
|
521
|
+
else if (hasDoNotEdit && hasGeneratorNote && file.lineCount > 5) {
|
|
522
|
+
// BUG-007 / BUG-H1: File claims to be AUTO-GENERATED + DO-NOT-EDIT, but is clearly
|
|
523
|
+
// long enough to be human-maintained (runbooks, conventions, etc.).
|
|
524
|
+
// Threshold lowered from 20 → 5 lines: even short instruction files (e.g. a brief
|
|
525
|
+
// QA.md) are human-maintained and should not carry a misleading auto-gen header.
|
|
526
|
+
const autoGenId = (p) => {
|
|
527
|
+
const hash = crypto.createHash('sha1').update(`auto-gen-header:${p}`).digest('hex').slice(0, 12);
|
|
528
|
+
return `auto-gen-header-${hash}`;
|
|
529
|
+
};
|
|
530
|
+
issues.push({
|
|
531
|
+
id: autoGenId(file.path),
|
|
532
|
+
severity: 'warning',
|
|
533
|
+
category: 'agent_practices',
|
|
534
|
+
title: 'Misleading auto-generated header on human-maintained instruction file',
|
|
535
|
+
summary: `This instruction file has an "AUTO-GENERATED — DO NOT EDIT" header, but with ${file.lineCount} lines ` +
|
|
536
|
+
`of hand-written content it is clearly human-maintained. The header may prevent agents and humans ` +
|
|
537
|
+
`from updating the file when needed.`,
|
|
538
|
+
filePaths: [file.path],
|
|
539
|
+
locations: [{ filePath: file.path, startLine: doNotEditLine, endLine: doNotEditLine }],
|
|
540
|
+
evidence: [doNotEditMatch[0].trim()],
|
|
541
|
+
recommendation: 'Remove the auto-generated header. If this file is genuinely generated, add a clear generator ' +
|
|
542
|
+
'comment that names the tool and the source template so the origin is unambiguous.',
|
|
543
|
+
confidence: 0.75,
|
|
544
|
+
});
|
|
545
|
+
}
|
|
546
|
+
// BUG-007: Detect Claude-specific XML tags in non-Claude instruction files.
|
|
547
|
+
// <claude:thinking> and similar tags are Claude-specific features; embedding them
|
|
548
|
+
// in .cursorrules or AGENTS.md is tool-specific syntax in the wrong file type.
|
|
549
|
+
// Skip README files — they often document Claude XML tags as examples rather than using them
|
|
550
|
+
// Skip if the tag only appears inside fenced code blocks (documentation, not real usage)
|
|
551
|
+
const contentOutsideCode = stripCodeBlocks(file.content).replace(/`[^`\n]+`/g, '');
|
|
552
|
+
if (file.fileType !== 'claude' && file.fileType !== 'readme' && CLAUDE_XML_TAG_RE.test(contentOutsideCode)) {
|
|
553
|
+
const claudeTagId = (p) => {
|
|
554
|
+
const hash = crypto.createHash('sha1').update(`claude-xml-tag:${p}`).digest('hex').slice(0, 12);
|
|
555
|
+
return `claude-xml-tag-${hash}`;
|
|
556
|
+
};
|
|
557
|
+
// AP3: search contentOutsideCode (the same text the gate above tested),
|
|
558
|
+
// not the raw file.content — otherwise an earlier occurrence of the
|
|
559
|
+
// tag that exists ONLY inside a code fence (documentation, not real
|
|
560
|
+
// usage) could be picked as evidence instead of the real match that
|
|
561
|
+
// actually made the gate fire.
|
|
562
|
+
const firstTag = (/<\/?claude:[\w-]+/i).exec(contentOutsideCode)?.[0] ?? '<claude:...>';
|
|
563
|
+
issues.push({
|
|
564
|
+
id: claudeTagId(file.path),
|
|
565
|
+
severity: 'warning',
|
|
566
|
+
category: 'structure',
|
|
567
|
+
title: 'Claude-specific XML tag in non-Claude instruction file',
|
|
568
|
+
summary: `This file contains a Claude-specific XML tag (${firstTag}) but is not a CLAUDE.md file. ` +
|
|
569
|
+
`Claude XML tags are only interpreted by Claude; other AI tools (Cursor, Copilot, etc.) ` +
|
|
570
|
+
`will treat them as literal text or ignore them.`,
|
|
571
|
+
filePaths: [file.path],
|
|
572
|
+
locations: [{ filePath: file.path }],
|
|
573
|
+
evidence: [`Found: ${firstTag} in ${file.fileType} file`],
|
|
574
|
+
recommendation: 'Move Claude-specific directives to CLAUDE.md, or replace this tag with a plain-text ' +
|
|
575
|
+
'instruction that all tools can understand.',
|
|
576
|
+
confidence: 0.85,
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
return issues;
|
|
581
|
+
}
|
|
582
|
+
//# sourceMappingURL=agent-practices.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-practices.js","sourceRoot":"","sources":["../src/agent-practices.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { PromptCiIssue, Baseline } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Computes a stable fingerprint for an issue.
|
|
4
|
+
*
|
|
5
|
+
* Excluded from fingerprint:
|
|
6
|
+
* - `id` (may change between versions)
|
|
7
|
+
* - `severity` (tracked separately for worsening detection)
|
|
8
|
+
* - `locations` / line numbers (unstable across minor file edits)
|
|
9
|
+
* - `confidence` (heuristic, may fluctuate)
|
|
10
|
+
*
|
|
11
|
+
* Included:
|
|
12
|
+
* - `category`, `title`, sorted `filePaths`, sorted `evidence`
|
|
13
|
+
*/
|
|
14
|
+
export declare function computeFingerprint(issue: PromptCiIssue, repoRoot?: string): string;
|
|
15
|
+
/**
|
|
16
|
+
* Creates a baseline from a list of active issues.
|
|
17
|
+
* Produces deterministic JSON when sorted by fingerprint.
|
|
18
|
+
*/
|
|
19
|
+
export declare function createBaseline(issues: PromptCiIssue[], repoRoot?: string): Baseline;
|
|
20
|
+
/**
|
|
21
|
+
* Type-guard that asserts a parsed JSON value is a valid Baseline array.
|
|
22
|
+
* Throws a descriptive error if the shape is unexpected.
|
|
23
|
+
*/
|
|
24
|
+
export declare function assertValidBaseline(value: unknown): asserts value is Baseline;
|
|
25
|
+
/**
|
|
26
|
+
* Partitions a list of active issues into new and baselined issues.
|
|
27
|
+
*
|
|
28
|
+
* An issue is considered NEW (not baselined) if:
|
|
29
|
+
* - Its fingerprint is not in the baseline, OR
|
|
30
|
+
* - Its fingerprint IS in the baseline but its severity has worsened
|
|
31
|
+
* (e.g. was `warning`, now `high`).
|
|
32
|
+
*
|
|
33
|
+
* Issues with the same fingerprint and equal-or-lesser severity are
|
|
34
|
+
* considered baselined (pre-existing) and do not fail `--fail-on-new` CI.
|
|
35
|
+
*/
|
|
36
|
+
export declare function filterNewIssues(issues: PromptCiIssue[], baseline: Baseline, repoRoot?: string): {
|
|
37
|
+
newIssues: PromptCiIssue[];
|
|
38
|
+
baselinedIssues: PromptCiIssue[];
|
|
39
|
+
};
|
|
40
|
+
//# sourceMappingURL=baseline.d.ts.map
|