@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,338 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Inline suppression annotations for PromptCI.
|
|
3
|
+
*
|
|
4
|
+
* Annotation formats supported in any instruction file:
|
|
5
|
+
*
|
|
6
|
+
* Section-scoped (suppresses the section containing or immediately following):
|
|
7
|
+
* <!-- promptci-ignore: <category>
|
|
8
|
+
* reason: <required explanation> -->
|
|
9
|
+
*
|
|
10
|
+
* Range-scoped (suppresses everything between start and end markers):
|
|
11
|
+
* <!-- promptci-ignore-start: <category>
|
|
12
|
+
* reason: <required explanation> -->
|
|
13
|
+
* ...content...
|
|
14
|
+
* <!-- promptci-ignore-end -->
|
|
15
|
+
*
|
|
16
|
+
* Rules:
|
|
17
|
+
* - `reason` is required. Missing reason → warning issue, issue is NOT suppressed.
|
|
18
|
+
* - `category` must be a valid IssueCategory value or the special token `all`.
|
|
19
|
+
* - Invalid category → warning issue, annotation does not suppress anything.
|
|
20
|
+
* - An unpaired `promptci-ignore-start` (no matching end) → warning issue.
|
|
21
|
+
*/
|
|
22
|
+
import * as crypto from 'node:crypto';
|
|
23
|
+
// ── Constants ─────────────────────────────────────────────────────────────────
|
|
24
|
+
/** All valid IssueCategory values plus the catch-all 'all' token. */
|
|
25
|
+
const VALID_ISSUE_CATEGORIES = [
|
|
26
|
+
'duplicate',
|
|
27
|
+
'conflict',
|
|
28
|
+
'context_bloat',
|
|
29
|
+
'missing_context',
|
|
30
|
+
'stale_instruction',
|
|
31
|
+
'vague_guidance',
|
|
32
|
+
'structure',
|
|
33
|
+
'command_validity',
|
|
34
|
+
'agent_practices',
|
|
35
|
+
'security',
|
|
36
|
+
];
|
|
37
|
+
const VALID_SUPPRESSION_CATEGORIES = new Set([
|
|
38
|
+
...VALID_ISSUE_CATEGORIES,
|
|
39
|
+
'all',
|
|
40
|
+
]);
|
|
41
|
+
// ── Regex patterns ────────────────────────────────────────────────────────────
|
|
42
|
+
/**
|
|
43
|
+
* Matches: <!-- promptci-ignore: <category> [optional body] -->
|
|
44
|
+
* Body may contain `reason: ...` on same line or a following line.
|
|
45
|
+
*/
|
|
46
|
+
const SINGLE_RE = /<!--\s*promptci-ignore:\s*(\S+)([\s\S]*?)-->/g;
|
|
47
|
+
/** Matches: <!-- promptci-ignore-start: <category> [optional body] --> */
|
|
48
|
+
const RANGE_START_RE = /<!--\s*promptci-ignore-start:\s*(\S+)([\s\S]*?)-->/g;
|
|
49
|
+
/** Matches: <!-- promptci-ignore-end --> */
|
|
50
|
+
const RANGE_END_RE = /<!--\s*promptci-ignore-end\s*-->/g;
|
|
51
|
+
// ── Helpers ───────────────────────────────────────────────────────────────────
|
|
52
|
+
/** Convert a character offset in content to a 1-indexed line number. */
|
|
53
|
+
function charToLine(content, offset) {
|
|
54
|
+
return content.slice(0, offset).split('\n').length;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Find the end line for a section-scoped annotation.
|
|
58
|
+
* Uses the already-parsed sections: the scope is the end of whichever section
|
|
59
|
+
* contains the annotation line, or the first section that starts after it.
|
|
60
|
+
*/
|
|
61
|
+
function getSectionEndLine(annotationLine, annotationEndLine, sections, fileLineCount, content) {
|
|
62
|
+
const lines = content.split(/\r?\n/);
|
|
63
|
+
// Find the section that contains this line
|
|
64
|
+
for (let i = 0; i < sections.length; i++) {
|
|
65
|
+
const section = sections[i];
|
|
66
|
+
if (section.startLine <= annotationLine && section.endLine >= annotationLine) {
|
|
67
|
+
// SU2: an annotation on the LAST line of a section, immediately
|
|
68
|
+
// followed by another section's heading, is almost always meant to
|
|
69
|
+
// scope that NEXT section — writing a suppression comment right above
|
|
70
|
+
// the heading it applies to is the natural placement. Since sections
|
|
71
|
+
// are contiguous, such an annotation is always *contained* by the
|
|
72
|
+
// PREVIOUS section under a naive "which section contains this line"
|
|
73
|
+
// lookup, so it silently suppressed zero lines of the intended target.
|
|
74
|
+
const next = sections[i + 1];
|
|
75
|
+
const gapLines = lines.slice(annotationEndLine, Math.max(annotationEndLine, next?.startLine ? next.startLine - 1 : annotationEndLine));
|
|
76
|
+
const gapIsBlank = gapLines.every((line) => line.trim() === '');
|
|
77
|
+
if (next && next.startLine > annotationEndLine && gapIsBlank) {
|
|
78
|
+
return next.endLine;
|
|
79
|
+
}
|
|
80
|
+
return section.endLine;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
// Annotation falls between sections — use the next section's end
|
|
84
|
+
for (const section of sections) {
|
|
85
|
+
if (section.startLine > annotationLine) {
|
|
86
|
+
return section.endLine;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return fileLineCount;
|
|
90
|
+
}
|
|
91
|
+
/** Extract the `reason:` value from the comment body, or null if absent. */
|
|
92
|
+
function extractReason(body) {
|
|
93
|
+
const m = /reason:\s*(.+?)(?:\n|$)/i.exec(body);
|
|
94
|
+
return m ? m[1].trim() : null;
|
|
95
|
+
}
|
|
96
|
+
function validate(rawCategory, reason) {
|
|
97
|
+
if (!VALID_SUPPRESSION_CATEGORIES.has(rawCategory)) {
|
|
98
|
+
return {
|
|
99
|
+
valid: false,
|
|
100
|
+
message: `Unknown suppression category "${rawCategory}". ` +
|
|
101
|
+
`Valid values: ${[...VALID_SUPPRESSION_CATEGORIES].join(', ')}.`,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
if (!reason || !reason.trim()) {
|
|
105
|
+
return {
|
|
106
|
+
valid: false,
|
|
107
|
+
message: 'promptci-ignore annotation is missing a required "reason:" field. ' +
|
|
108
|
+
'Add: reason: <explanation of why this is intentional>.',
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
return { valid: true };
|
|
112
|
+
}
|
|
113
|
+
function annotationIssueId(filePath, startLine, tag) {
|
|
114
|
+
const key = `suppression-invalid:${filePath}:${startLine}:${tag}`;
|
|
115
|
+
const hash = crypto.createHash('sha1').update(key).digest('hex').slice(0, 12);
|
|
116
|
+
return `suppression-invalid-${hash}`;
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* SU3: blank out fenced code block lines (``` or ~~~, matching fence
|
|
120
|
+
* char/length per CommonMark — the same tracking used in scanner.ts,
|
|
121
|
+
* vague-guidance.ts, stale-instructions.ts, conflicts.ts, and
|
|
122
|
+
* command-validity.ts) so a documented EXAMPLE of the annotation syntax
|
|
123
|
+
* doesn't get parsed as a real suppression. The old approach only stripped
|
|
124
|
+
* ` ``` ` fences via a single regex — a ~~~ fence containing an annotation
|
|
125
|
+
* example was scanned as prose and treated as a REAL suppression.
|
|
126
|
+
*
|
|
127
|
+
* Replaces each fenced line with an empty string (not removing it) so the
|
|
128
|
+
* total line count is unchanged — `charToLine` offsets computed against this
|
|
129
|
+
* output still map to the correct line numbers in the ORIGINAL file.
|
|
130
|
+
*/
|
|
131
|
+
function stripFencedBlocksPreservingLines(content) {
|
|
132
|
+
const lines = content.split('\n');
|
|
133
|
+
const kept = [];
|
|
134
|
+
let inBlock = false;
|
|
135
|
+
let fenceChar = null;
|
|
136
|
+
let fenceLen = 0;
|
|
137
|
+
for (const line of lines) {
|
|
138
|
+
if (!inBlock) {
|
|
139
|
+
const openMatch = /^ {0,3}([`~]{3,})/.exec(line);
|
|
140
|
+
if (openMatch) {
|
|
141
|
+
inBlock = true;
|
|
142
|
+
fenceChar = openMatch[1][0] ?? null;
|
|
143
|
+
fenceLen = openMatch[1].length;
|
|
144
|
+
kept.push('');
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
kept.push(line);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
const closeMatch = /^ {0,3}([`~]{3,})\s*$/.exec(line);
|
|
151
|
+
if (closeMatch && closeMatch[1][0] === fenceChar && closeMatch[1].length >= fenceLen) {
|
|
152
|
+
inBlock = false;
|
|
153
|
+
fenceChar = null;
|
|
154
|
+
fenceLen = 0;
|
|
155
|
+
}
|
|
156
|
+
kept.push('');
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return kept.join('\n');
|
|
160
|
+
}
|
|
161
|
+
// ── Main parser ───────────────────────────────────────────────────────────────
|
|
162
|
+
function parseFileSuppressions(file) {
|
|
163
|
+
const { content, path: filePath, sections, lineCount } = file;
|
|
164
|
+
const annotations = [];
|
|
165
|
+
// Strip fenced code blocks before parsing so examples of the annotation
|
|
166
|
+
// syntax (e.g. in README.md) don't get interpreted as real suppressions.
|
|
167
|
+
// Preserve line count so offsets still map to the original file's lines.
|
|
168
|
+
const contentForParsing = stripFencedBlocksPreservingLines(content);
|
|
169
|
+
// ── Single annotations ────────────────────────────────────────────────────
|
|
170
|
+
const singleRe = new RegExp(SINGLE_RE.source, SINGLE_RE.flags);
|
|
171
|
+
let m;
|
|
172
|
+
while ((m = singleRe.exec(contentForParsing)) !== null) {
|
|
173
|
+
const rawCategory = m[1]?.trim() ?? '';
|
|
174
|
+
const body = m[2] ?? '';
|
|
175
|
+
const reason = extractReason(body);
|
|
176
|
+
const startLine = charToLine(contentForParsing, m.index);
|
|
177
|
+
const annotationEndLine = charToLine(contentForParsing, m.index + m[0].length);
|
|
178
|
+
const endLine = getSectionEndLine(startLine, annotationEndLine, sections, lineCount, content);
|
|
179
|
+
const { valid, message } = validate(rawCategory, reason);
|
|
180
|
+
annotations.push({
|
|
181
|
+
filePath,
|
|
182
|
+
category: valid ? rawCategory : 'all',
|
|
183
|
+
reason: reason ?? '',
|
|
184
|
+
startLine,
|
|
185
|
+
endLine,
|
|
186
|
+
valid,
|
|
187
|
+
validationMessage: message,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
const markers = [];
|
|
191
|
+
const startRe = new RegExp(RANGE_START_RE.source, RANGE_START_RE.flags);
|
|
192
|
+
while ((m = startRe.exec(contentForParsing)) !== null) {
|
|
193
|
+
markers.push({
|
|
194
|
+
kind: 'start',
|
|
195
|
+
offset: m.index,
|
|
196
|
+
endOffset: m.index + m[0].length,
|
|
197
|
+
category: m[1]?.trim() ?? '',
|
|
198
|
+
reason: extractReason(m[2] ?? ''),
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
const endRe = new RegExp(RANGE_END_RE.source, RANGE_END_RE.flags);
|
|
202
|
+
while ((m = endRe.exec(contentForParsing)) !== null) {
|
|
203
|
+
markers.push({ kind: 'end', offset: m.index, endOffset: m.index + m[0].length });
|
|
204
|
+
}
|
|
205
|
+
markers.sort((a, b) => a.offset - b.offset);
|
|
206
|
+
const pushUnpairedStart = (start) => {
|
|
207
|
+
annotations.push({
|
|
208
|
+
filePath,
|
|
209
|
+
category: 'all',
|
|
210
|
+
reason: '',
|
|
211
|
+
startLine: charToLine(contentForParsing, start.offset),
|
|
212
|
+
endLine: lineCount,
|
|
213
|
+
valid: false,
|
|
214
|
+
validationMessage: 'promptci-ignore-start has no matching promptci-ignore-end marker.',
|
|
215
|
+
});
|
|
216
|
+
};
|
|
217
|
+
let openStart = null;
|
|
218
|
+
for (const marker of markers) {
|
|
219
|
+
if (marker.kind === 'start') {
|
|
220
|
+
// A new start before the previous one was ever closed — the previous
|
|
221
|
+
// one is unpaired.
|
|
222
|
+
if (openStart)
|
|
223
|
+
pushUnpairedStart(openStart);
|
|
224
|
+
openStart = marker;
|
|
225
|
+
continue;
|
|
226
|
+
}
|
|
227
|
+
// end marker
|
|
228
|
+
if (!openStart)
|
|
229
|
+
continue; // stray end with nothing open — ignore
|
|
230
|
+
const startLine = charToLine(contentForParsing, openStart.offset);
|
|
231
|
+
const endLine = charToLine(contentForParsing, marker.endOffset);
|
|
232
|
+
const { valid, message } = validate(openStart.category ?? '', openStart.reason ?? null);
|
|
233
|
+
annotations.push({
|
|
234
|
+
filePath,
|
|
235
|
+
category: valid ? openStart.category : 'all',
|
|
236
|
+
reason: openStart.reason ?? '',
|
|
237
|
+
startLine,
|
|
238
|
+
endLine,
|
|
239
|
+
valid,
|
|
240
|
+
validationMessage: message,
|
|
241
|
+
});
|
|
242
|
+
openStart = null;
|
|
243
|
+
}
|
|
244
|
+
if (openStart)
|
|
245
|
+
pushUnpairedStart(openStart);
|
|
246
|
+
return annotations;
|
|
247
|
+
}
|
|
248
|
+
// ── Public API ────────────────────────────────────────────────────────────────
|
|
249
|
+
/**
|
|
250
|
+
* Parse all suppression annotations from a set of instruction files.
|
|
251
|
+
* Returns both valid and invalid annotations; call `buildValidationIssues`
|
|
252
|
+
* to surface invalid ones as actual scan issues.
|
|
253
|
+
*/
|
|
254
|
+
export function parseSuppressions(files) {
|
|
255
|
+
return files.flatMap(parseFileSuppressions);
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Convert invalid suppression annotations into PromptCiIssue warning objects
|
|
259
|
+
* so they appear in the scan report and count against the health score.
|
|
260
|
+
* This ensures bad suppressions are visible rather than silently ignored.
|
|
261
|
+
*/
|
|
262
|
+
export function buildValidationIssues(annotations) {
|
|
263
|
+
return annotations
|
|
264
|
+
.filter((ann) => !ann.valid)
|
|
265
|
+
.map((ann) => ({
|
|
266
|
+
id: annotationIssueId(ann.filePath, ann.startLine, ann.validationMessage ?? ''),
|
|
267
|
+
severity: 'warning',
|
|
268
|
+
category: 'structure',
|
|
269
|
+
title: 'Invalid promptci-ignore annotation',
|
|
270
|
+
summary: ann.validationMessage ?? 'This promptci-ignore annotation is malformed.',
|
|
271
|
+
filePaths: [ann.filePath],
|
|
272
|
+
locations: [{ filePath: ann.filePath, startLine: ann.startLine }],
|
|
273
|
+
evidence: [],
|
|
274
|
+
recommendation: 'Ensure the annotation has a valid category and a non-empty reason field. ' +
|
|
275
|
+
'Example: <!-- promptci-ignore: vague_guidance\n reason: This section documents examples. -->',
|
|
276
|
+
confidence: 1.0,
|
|
277
|
+
}));
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Partition a flat issues array into active and suppressed based on the
|
|
281
|
+
* provided annotations.
|
|
282
|
+
*
|
|
283
|
+
* Matching rules:
|
|
284
|
+
* - Category match: annotation.category === 'all' OR === issue.category
|
|
285
|
+
* - Location match (issue HAS locations): at least one location's filePath +
|
|
286
|
+
* line range overlaps with the annotation's scope
|
|
287
|
+
* - Location match (issue has EMPTY locations, e.g. agent_practices): match
|
|
288
|
+
* by filePath alone (any of issue.filePaths matches annotation.filePath)
|
|
289
|
+
*
|
|
290
|
+
* Only valid annotations are used for matching; invalid ones emit warning issues
|
|
291
|
+
* that are never themselves suppressed.
|
|
292
|
+
*/
|
|
293
|
+
export function applySuppressions(issues, annotations) {
|
|
294
|
+
const validAnnotations = annotations.filter((a) => a.valid);
|
|
295
|
+
const active = [];
|
|
296
|
+
const suppressed = [];
|
|
297
|
+
for (const issue of issues) {
|
|
298
|
+
const isSuppressed = validAnnotations.some((ann) => {
|
|
299
|
+
// Category check
|
|
300
|
+
if (ann.category !== 'all' && ann.category !== issue.category)
|
|
301
|
+
return false;
|
|
302
|
+
// Issues with explicit locations: require line overlap in the same file.
|
|
303
|
+
// If a location has no startLine (e.g. dead-ref detector found a file but
|
|
304
|
+
// not the exact line), fall back to file-path-only matching so that an
|
|
305
|
+
// annotation anywhere in the file can suppress it.
|
|
306
|
+
if (issue.locations.length > 0) {
|
|
307
|
+
const locationMatches = issue.locations.some((loc) => {
|
|
308
|
+
if (loc.filePath !== ann.filePath)
|
|
309
|
+
return false;
|
|
310
|
+
if (loc.startLine === undefined)
|
|
311
|
+
return true;
|
|
312
|
+
const issueEnd = loc.endLine ?? loc.startLine;
|
|
313
|
+
return loc.startLine <= ann.endLine && issueEnd >= ann.startLine;
|
|
314
|
+
});
|
|
315
|
+
if (locationMatches)
|
|
316
|
+
return true;
|
|
317
|
+
const issueFilePaths = new Set(issue.filePaths);
|
|
318
|
+
const hasLocationInIssueFile = issue.locations.some((loc) => issueFilePaths.has(loc.filePath));
|
|
319
|
+
if (!hasLocationInIssueFile && issue.filePaths.includes(ann.filePath))
|
|
320
|
+
return true;
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
// Issues with no locations (e.g. agent_practices, missing_context):
|
|
324
|
+
// match if any of the issue's filePaths is the annotated file
|
|
325
|
+
return issue.filePaths.includes(ann.filePath);
|
|
326
|
+
});
|
|
327
|
+
if (isSuppressed) {
|
|
328
|
+
suppressed.push(issue);
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
active.push(issue);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
return { active, suppressed };
|
|
335
|
+
}
|
|
336
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
337
|
+
const _check = true;
|
|
338
|
+
//# sourceMappingURL=suppression.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"suppression.js","sourceRoot":"","sources":["../src/suppression.ts"],"names":[],"mappings":"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"}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/** Core data types for PromptCI */
|
|
2
|
+
export type FileType = 'claude' | 'agents' | 'cursor' | 'copilot' | 'readme' | 'docs' | 'prompt' | 'unknown';
|
|
3
|
+
export type IssueSeverity = 'info' | 'warning' | 'high' | 'critical';
|
|
4
|
+
export type IssueCategory = 'duplicate' | 'conflict' | 'context_bloat' | 'missing_context' | 'stale_instruction' | 'vague_guidance' | 'structure' | 'command_validity' | 'agent_practices' | 'security';
|
|
5
|
+
export type InstructionSection = {
|
|
6
|
+
id: string;
|
|
7
|
+
filePath: string;
|
|
8
|
+
heading?: string;
|
|
9
|
+
startLine: number;
|
|
10
|
+
endLine: number;
|
|
11
|
+
text: string;
|
|
12
|
+
normalizedText: string;
|
|
13
|
+
};
|
|
14
|
+
export type InstructionFile = {
|
|
15
|
+
path: string;
|
|
16
|
+
fileType: FileType;
|
|
17
|
+
content: string;
|
|
18
|
+
sections: InstructionSection[];
|
|
19
|
+
lineCount: number;
|
|
20
|
+
charCount: number;
|
|
21
|
+
estimatedTokens: number;
|
|
22
|
+
};
|
|
23
|
+
export type PromptCiIssue = {
|
|
24
|
+
id: string;
|
|
25
|
+
severity: IssueSeverity;
|
|
26
|
+
category: IssueCategory;
|
|
27
|
+
title: string;
|
|
28
|
+
summary: string;
|
|
29
|
+
filePaths: string[];
|
|
30
|
+
locations: Array<{
|
|
31
|
+
filePath: string;
|
|
32
|
+
startLine?: number;
|
|
33
|
+
endLine?: number;
|
|
34
|
+
}>;
|
|
35
|
+
evidence: string[];
|
|
36
|
+
recommendation: string;
|
|
37
|
+
fixRecipe?: string;
|
|
38
|
+
confidence: number;
|
|
39
|
+
tags?: string[];
|
|
40
|
+
impact?: 'high' | 'medium' | 'low';
|
|
41
|
+
};
|
|
42
|
+
export type TopFix = {
|
|
43
|
+
title: string;
|
|
44
|
+
reason: string;
|
|
45
|
+
suggestedAction: string;
|
|
46
|
+
};
|
|
47
|
+
export type ProjectType = 'auto' | 'typescript' | 'nextjs' | 'unity' | 'dotnet' | 'python' | 'go' | 'rust' | 'unknown';
|
|
48
|
+
export type ScanTrend = {
|
|
49
|
+
previousScanDate?: string;
|
|
50
|
+
scoreDelta: number;
|
|
51
|
+
newIssuesCount: number;
|
|
52
|
+
resolvedIssuesCount: number;
|
|
53
|
+
severityDeltas: Record<string, number>;
|
|
54
|
+
categoryDeltas: Record<string, number>;
|
|
55
|
+
newIssueIds: string[];
|
|
56
|
+
resolvedIssueIds: string[];
|
|
57
|
+
streak: number;
|
|
58
|
+
};
|
|
59
|
+
export type ScanReport = {
|
|
60
|
+
schemaVersion: '0.1';
|
|
61
|
+
generatedAt: string;
|
|
62
|
+
repoPath: string;
|
|
63
|
+
projectType: ProjectType;
|
|
64
|
+
healthScore: number;
|
|
65
|
+
metrics?: ScanMetrics;
|
|
66
|
+
filesScanned: InstructionFile[];
|
|
67
|
+
issues: PromptCiIssue[];
|
|
68
|
+
topFixes: TopFix[];
|
|
69
|
+
newIssues?: PromptCiIssue[];
|
|
70
|
+
baselinedIssues?: PromptCiIssue[];
|
|
71
|
+
trend?: ScanTrend;
|
|
72
|
+
/**
|
|
73
|
+
* Issues silenced by inline `promptci-ignore` annotations.
|
|
74
|
+
* Not counted in healthScore or topFixes.
|
|
75
|
+
* Absent when there are no suppressions.
|
|
76
|
+
*/
|
|
77
|
+
suppressedIssues?: PromptCiIssue[];
|
|
78
|
+
};
|
|
79
|
+
export type ScanMetrics = {
|
|
80
|
+
estimatedInstructionTokens: number;
|
|
81
|
+
instructionFileCount: number;
|
|
82
|
+
largestInstructionFiles: Array<{
|
|
83
|
+
path: string;
|
|
84
|
+
estimatedTokens: number;
|
|
85
|
+
}>;
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* R1: report.json intentionally omits InstructionFile's `content` and
|
|
89
|
+
* `sections` fields for each scanned file (they'd bloat the JSON with
|
|
90
|
+
* duplicated file content). The old JSON serializer built this compact shape
|
|
91
|
+
* ad hoc while still stamping `schemaVersion: '0.1'` (ScanReport's version),
|
|
92
|
+
* so any consumer that parsed report.json and typed it as `ScanReport` got
|
|
93
|
+
* `undefined` for `.filesScanned[i].content`/`.sections` despite TypeScript
|
|
94
|
+
* saying those fields are required strings/arrays.
|
|
95
|
+
*
|
|
96
|
+
* This type names the ACTUAL compact shape `generateJsonReport` produces, so
|
|
97
|
+
* consumers (the web dashboard, eval tooling) can import and use the type
|
|
98
|
+
* that matches what's really on disk, instead of the full in-memory
|
|
99
|
+
* `ScanReport`/`InstructionFile` types.
|
|
100
|
+
*/
|
|
101
|
+
export type CompactFileSummary = Pick<InstructionFile, 'path' | 'fileType' | 'lineCount' | 'charCount' | 'estimatedTokens'>;
|
|
102
|
+
export type ScanReportJson = Omit<ScanReport, 'filesScanned'> & {
|
|
103
|
+
filesScanned: CompactFileSummary[];
|
|
104
|
+
};
|
|
105
|
+
export type BaselineEntry = {
|
|
106
|
+
id: string;
|
|
107
|
+
category: string;
|
|
108
|
+
title: string;
|
|
109
|
+
filePaths: string[];
|
|
110
|
+
/**
|
|
111
|
+
* Severity recorded when the issue was baselined.
|
|
112
|
+
* If the current scan produces the same fingerprint but a higher severity,
|
|
113
|
+
* the issue is treated as worsened (i.e. new).
|
|
114
|
+
*/
|
|
115
|
+
severity: string;
|
|
116
|
+
fingerprint: string;
|
|
117
|
+
};
|
|
118
|
+
export type Baseline = BaselineEntry[];
|
|
119
|
+
export type ScanInput = {
|
|
120
|
+
repoPath: string;
|
|
121
|
+
projectType?: ProjectType;
|
|
122
|
+
include?: string[];
|
|
123
|
+
exclude?: string[];
|
|
124
|
+
baseline?: Baseline;
|
|
125
|
+
contextBudget?: number;
|
|
126
|
+
fileContextBudget?: number;
|
|
127
|
+
};
|
|
128
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,mCAAmC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Vague-guidance detector for PromptCI.
|
|
3
|
+
*
|
|
4
|
+
* Flags sections that contain non-actionable, vague guidance.
|
|
5
|
+
* Emits at most one issue per section regardless of how many vague phrases
|
|
6
|
+
* appear in it.
|
|
7
|
+
*
|
|
8
|
+
* Heuristic detector — confidence 0.8, severity "info".
|
|
9
|
+
*/
|
|
10
|
+
import type { InstructionFile, PromptCiIssue } from './types.js';
|
|
11
|
+
export declare function detectVagueGuidance(files: InstructionFile[]): PromptCiIssue[];
|
|
12
|
+
//# sourceMappingURL=vague-guidance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"vague-guidance.d.ts","sourceRoot":"","sources":["../src/vague-guidance.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,eAAe,EAAsB,aAAa,EAAE,MAAM,YAAY,CAAC;AA8LrF,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,aAAa,EAAE,CAsD7E"}
|