projscan 1.11.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -30
- package/dist/cli/_shared.d.ts +2 -1
- package/dist/cli/_shared.js +23 -18
- package/dist/cli/_shared.js.map +1 -1
- package/dist/cli/commands/analyze.js +7 -3
- package/dist/cli/commands/analyze.js.map +1 -1
- package/dist/cli/commands/applyFix.js +2 -2
- package/dist/cli/commands/applyFix.js.map +1 -1
- package/dist/cli/commands/audit.js +2 -2
- package/dist/cli/commands/audit.js.map +1 -1
- package/dist/cli/commands/badge.js +2 -1
- package/dist/cli/commands/badge.js.map +1 -1
- package/dist/cli/commands/ci.js +3 -3
- package/dist/cli/commands/ci.js.map +1 -1
- package/dist/cli/commands/coupling.js +2 -2
- package/dist/cli/commands/coupling.js.map +1 -1
- package/dist/cli/commands/coverage.js +2 -2
- package/dist/cli/commands/coverage.js.map +1 -1
- package/dist/cli/commands/dependencies.js +2 -2
- package/dist/cli/commands/dependencies.js.map +1 -1
- package/dist/cli/commands/diagram.js +2 -2
- package/dist/cli/commands/diagram.js.map +1 -1
- package/dist/cli/commands/diff.js +2 -2
- package/dist/cli/commands/diff.js.map +1 -1
- package/dist/cli/commands/doctor.js +3 -3
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/explain.js +6 -7
- package/dist/cli/commands/explain.js.map +1 -1
- package/dist/cli/commands/explainIssue.js +2 -2
- package/dist/cli/commands/explainIssue.js.map +1 -1
- package/dist/cli/commands/file.js +2 -2
- package/dist/cli/commands/file.js.map +1 -1
- package/dist/cli/commands/fix.js +2 -1
- package/dist/cli/commands/fix.js.map +1 -1
- package/dist/cli/commands/fixSuggest.js +2 -2
- package/dist/cli/commands/fixSuggest.js.map +1 -1
- package/dist/cli/commands/help.js +2 -1
- package/dist/cli/commands/help.js.map +1 -1
- package/dist/cli/commands/hotspots.js +2 -2
- package/dist/cli/commands/hotspots.js.map +1 -1
- package/dist/cli/commands/impact.js +2 -2
- package/dist/cli/commands/impact.js.map +1 -1
- package/dist/cli/commands/init.js +2 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/installHook.js +2 -1
- package/dist/cli/commands/installHook.js.map +1 -1
- package/dist/cli/commands/mcp.js +2 -1
- package/dist/cli/commands/mcp.js.map +1 -1
- package/dist/cli/commands/memory.js +5 -4
- package/dist/cli/commands/memory.js.map +1 -1
- package/dist/cli/commands/outdated.js +2 -2
- package/dist/cli/commands/outdated.js.map +1 -1
- package/dist/cli/commands/plugin.d.ts +3 -3
- package/dist/cli/commands/plugin.js +91 -11
- package/dist/cli/commands/plugin.js.map +1 -1
- package/dist/cli/commands/prDiff.js +2 -2
- package/dist/cli/commands/prDiff.js.map +1 -1
- package/dist/cli/commands/preflight.d.ts +1 -0
- package/dist/cli/commands/preflight.js +80 -0
- package/dist/cli/commands/preflight.js.map +1 -0
- package/dist/cli/commands/review.js +2 -2
- package/dist/cli/commands/review.js.map +1 -1
- package/dist/cli/commands/search.js +2 -2
- package/dist/cli/commands/search.js.map +1 -1
- package/dist/cli/commands/session.js +5 -5
- package/dist/cli/commands/session.js.map +1 -1
- package/dist/cli/commands/structure.js +2 -2
- package/dist/cli/commands/structure.js.map +1 -1
- package/dist/cli/commands/taint.js +2 -2
- package/dist/cli/commands/taint.js.map +1 -1
- package/dist/cli/commands/upgrade.js +2 -2
- package/dist/cli/commands/upgrade.js.map +1 -1
- package/dist/cli/commands/watch.js +2 -1
- package/dist/cli/commands/watch.js.map +1 -1
- package/dist/cli/commands/workspace.js +4 -2
- package/dist/cli/commands/workspace.js.map +1 -1
- package/dist/cli/commands/workspaces.js +2 -2
- package/dist/cli/commands/workspaces.js.map +1 -1
- package/dist/cli/index.js +2 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/core/fileInspector.d.ts +2 -5
- package/dist/core/fileInspector.js +28 -103
- package/dist/core/fileInspector.js.map +1 -1
- package/dist/core/languages/LanguageAdapter.d.ts +3 -1
- package/dist/core/languages/LanguageAdapter.js +13 -1
- package/dist/core/languages/LanguageAdapter.js.map +1 -1
- package/dist/core/pluginDx.d.ts +14 -0
- package/dist/core/pluginDx.js +298 -0
- package/dist/core/pluginDx.js.map +1 -0
- package/dist/core/plugins.d.ts +5 -6
- package/dist/core/plugins.js +99 -13
- package/dist/core/plugins.js.map +1 -1
- package/dist/core/preflight.d.ts +11 -0
- package/dist/core/preflight.js +416 -0
- package/dist/core/preflight.js.map +1 -0
- package/dist/core/review.js +101 -1
- package/dist/core/review.js.map +1 -1
- package/dist/core/sessionResources.d.ts +6 -0
- package/dist/core/sessionResources.js +216 -0
- package/dist/core/sessionResources.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/mcp/resources.js +25 -0
- package/dist/mcp/resources.js.map +1 -1
- package/dist/mcp/tools/_shared.d.ts +1 -1
- package/dist/mcp/tools/_shared.js +3 -15
- package/dist/mcp/tools/_shared.js.map +1 -1
- package/dist/mcp/tools/explain.js +1 -3
- package/dist/mcp/tools/explain.js.map +1 -1
- package/dist/mcp/tools/plugin.d.ts +4 -7
- package/dist/mcp/tools/plugin.js +6 -9
- package/dist/mcp/tools/plugin.js.map +1 -1
- package/dist/mcp/tools/preflight.d.ts +2 -0
- package/dist/mcp/tools/preflight.js +51 -0
- package/dist/mcp/tools/preflight.js.map +1 -0
- package/dist/mcp/tools.js +2 -0
- package/dist/mcp/tools.js.map +1 -1
- package/dist/reporters/htmlReporter.d.ts +2 -1
- package/dist/reporters/htmlReporter.js +70 -0
- package/dist/reporters/htmlReporter.js.map +1 -1
- package/dist/reporters/jsonReporter.d.ts +1 -0
- package/dist/reporters/jsonReporter.js +25 -19
- package/dist/reporters/jsonReporter.js.map +1 -1
- package/dist/tool-manifest.json +35 -5
- package/dist/types.d.ts +137 -0
- package/dist/utils/banner.js +14 -4
- package/dist/utils/banner.js.map +1 -1
- package/dist/utils/fileWalker.js +4 -0
- package/dist/utils/fileWalker.js.map +1 -1
- package/dist/utils/formatSupport.d.ts +58 -0
- package/dist/utils/formatSupport.js +63 -0
- package/dist/utils/formatSupport.js.map +1 -0
- package/docs/2.0-MIGRATION.md +80 -0
- package/docs/PLUGIN-AUTHORING.md +239 -0
- package/docs/examples/plugins/policy.mjs +16 -0
- package/docs/examples/plugins/policy.projscan-plugin.json +8 -0
- package/docs/examples/plugins/team-radar.mjs +17 -0
- package/docs/examples/plugins/team-radar.projscan-plugin.json +8 -0
- package/docs/plugin.schema.json +70 -0
- package/package.json +10 -4
|
@@ -0,0 +1,416 @@
|
|
|
1
|
+
import { scanRepository } from './repositoryScanner.js';
|
|
2
|
+
import { collectIssues } from './issueEngine.js';
|
|
3
|
+
import { analyzeHotspots } from './hotspotAnalyzer.js';
|
|
4
|
+
import { computeReview } from './review.js';
|
|
5
|
+
import { loadSession } from './session.js';
|
|
6
|
+
import { PLUGIN_PREVIEW_FLAG, pluginsEnabled } from './plugins.js';
|
|
7
|
+
import { getChangedFiles } from '../utils/changedFiles.js';
|
|
8
|
+
import { loadConfig, applyConfigToIssues } from '../utils/config.js';
|
|
9
|
+
import { calculateScore } from '../utils/scoreCalculator.js';
|
|
10
|
+
const DEFAULT_MAX_CHANGED_FILES = 50;
|
|
11
|
+
const MAX_EVIDENCE_FILES = 40;
|
|
12
|
+
export async function computePreflight(rootPath, options = {}) {
|
|
13
|
+
const mode = options.mode ?? 'before_edit';
|
|
14
|
+
const configResult = await loadConfig(rootPath).catch(() => ({ config: { ignore: [] } }));
|
|
15
|
+
const scan = await scanRepository(rootPath, { ignore: configResult.config.ignore });
|
|
16
|
+
const issues = applyConfigToIssues(await collectIssuesWithPluginOption(rootPath, scan.files, options.enablePlugins), configResult.config);
|
|
17
|
+
const health = calculateScore(issues);
|
|
18
|
+
const changedFiles = await safeChangedFiles(rootPath, mode, options.baseRef);
|
|
19
|
+
const session = await safeSession(rootPath);
|
|
20
|
+
const hotspots = await safeHotspots(rootPath, scan.files, issues);
|
|
21
|
+
const review = await safeReview(rootPath, mode, options);
|
|
22
|
+
const reasons = buildPreflightReasons({
|
|
23
|
+
mode,
|
|
24
|
+
issues,
|
|
25
|
+
changedFiles,
|
|
26
|
+
health,
|
|
27
|
+
session,
|
|
28
|
+
hotspots,
|
|
29
|
+
review,
|
|
30
|
+
maxChangedFiles: options.maxChangedFiles ?? DEFAULT_MAX_CHANGED_FILES,
|
|
31
|
+
});
|
|
32
|
+
const verdict = decidePreflightVerdict(reasons);
|
|
33
|
+
const evidence = buildEvidence({
|
|
34
|
+
health,
|
|
35
|
+
changedFiles,
|
|
36
|
+
session,
|
|
37
|
+
hotspots,
|
|
38
|
+
issues,
|
|
39
|
+
pluginsEnabledForRun: options.enablePlugins === true || pluginsEnabled(),
|
|
40
|
+
review,
|
|
41
|
+
});
|
|
42
|
+
const truncated = evidence.session?.truncated === true ||
|
|
43
|
+
changedFiles.files.length > MAX_EVIDENCE_FILES ||
|
|
44
|
+
(evidence.hotspots?.touched.length ?? 0) > MAX_EVIDENCE_FILES;
|
|
45
|
+
const report = {
|
|
46
|
+
schemaVersion: 1,
|
|
47
|
+
mode,
|
|
48
|
+
verdict,
|
|
49
|
+
summary: '',
|
|
50
|
+
reasons,
|
|
51
|
+
evidence,
|
|
52
|
+
requiredChecks: buildRequiredChecks(mode, health, changedFiles, review),
|
|
53
|
+
suggestedNextActions: buildSuggestedActions(reasons, mode, changedFiles),
|
|
54
|
+
toolCalls: buildToolCalls(reasons, mode, changedFiles),
|
|
55
|
+
...(truncated ? { truncated: true } : {}),
|
|
56
|
+
};
|
|
57
|
+
return { ...report, summary: summarizePreflight(report) };
|
|
58
|
+
}
|
|
59
|
+
export function decidePreflightVerdict(reasons) {
|
|
60
|
+
if (reasons.some((reason) => reason.severity === 'error'))
|
|
61
|
+
return 'block';
|
|
62
|
+
if (reasons.some((reason) => reason.severity === 'warning'))
|
|
63
|
+
return 'caution';
|
|
64
|
+
return 'proceed';
|
|
65
|
+
}
|
|
66
|
+
export function summarizePreflight(report) {
|
|
67
|
+
if (report.reasons.length === 0) {
|
|
68
|
+
return `${report.verdict}: no blocking or cautionary signals found`;
|
|
69
|
+
}
|
|
70
|
+
return `${report.verdict}: ${report.reasons[0].message}`;
|
|
71
|
+
}
|
|
72
|
+
async function collectIssuesWithPluginOption(rootPath, files, enablePlugins) {
|
|
73
|
+
if (enablePlugins !== true)
|
|
74
|
+
return collectIssues(rootPath, files);
|
|
75
|
+
const previous = process.env[PLUGIN_PREVIEW_FLAG];
|
|
76
|
+
process.env[PLUGIN_PREVIEW_FLAG] = '1';
|
|
77
|
+
try {
|
|
78
|
+
return await collectIssues(rootPath, files);
|
|
79
|
+
}
|
|
80
|
+
finally {
|
|
81
|
+
if (previous === undefined)
|
|
82
|
+
delete process.env[PLUGIN_PREVIEW_FLAG];
|
|
83
|
+
else
|
|
84
|
+
process.env[PLUGIN_PREVIEW_FLAG] = previous;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
async function safeChangedFiles(rootPath, mode, baseRef) {
|
|
88
|
+
if (mode === 'before_edit') {
|
|
89
|
+
return {
|
|
90
|
+
available: false,
|
|
91
|
+
count: 0,
|
|
92
|
+
files: [],
|
|
93
|
+
baseRef: null,
|
|
94
|
+
reason: 'changed-file detection is not required before edits',
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
try {
|
|
98
|
+
const result = await getChangedFiles(rootPath, baseRef);
|
|
99
|
+
return {
|
|
100
|
+
available: result.available,
|
|
101
|
+
count: result.files.length,
|
|
102
|
+
files: result.files,
|
|
103
|
+
baseRef: result.baseRef,
|
|
104
|
+
...(result.reason ? { reason: result.reason } : {}),
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
return {
|
|
109
|
+
available: false,
|
|
110
|
+
count: 0,
|
|
111
|
+
files: [],
|
|
112
|
+
baseRef: null,
|
|
113
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
async function safeSession(rootPath) {
|
|
118
|
+
const { session } = await loadSession(rootPath);
|
|
119
|
+
const touchedFiles = Object.values(session.touchedFiles)
|
|
120
|
+
.sort((a, b) => {
|
|
121
|
+
const byTime = Date.parse(b.lastTouchedAt) - Date.parse(a.lastTouchedAt);
|
|
122
|
+
return byTime !== 0 ? byTime : a.file.localeCompare(b.file);
|
|
123
|
+
})
|
|
124
|
+
.map((touch) => touch.file);
|
|
125
|
+
return {
|
|
126
|
+
id: session.id,
|
|
127
|
+
touchedFiles,
|
|
128
|
+
eventCount: session.events.length,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
async function safeHotspots(rootPath, files, issues) {
|
|
132
|
+
try {
|
|
133
|
+
return await analyzeHotspots(rootPath, files, issues, { limit: 20 });
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return null;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
async function safeReview(rootPath, mode, options) {
|
|
140
|
+
if (mode === 'before_edit') {
|
|
141
|
+
return { available: false, reason: 'review is not required before edits' };
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const report = await computeReview(rootPath, {
|
|
145
|
+
base: options.baseRef,
|
|
146
|
+
head: options.headRef,
|
|
147
|
+
});
|
|
148
|
+
return {
|
|
149
|
+
available: report.available,
|
|
150
|
+
verdict: report.available ? report.verdict : undefined,
|
|
151
|
+
summary: report.available ? report.summary.join('; ') : undefined,
|
|
152
|
+
reason: report.available ? undefined : report.reason,
|
|
153
|
+
newTaintFlows: report.available ? report.newTaintFlows.length : undefined,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
return {
|
|
158
|
+
available: false,
|
|
159
|
+
reason: err instanceof Error ? err.message : String(err),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
function buildPreflightReasons(input) {
|
|
164
|
+
const reasons = [];
|
|
165
|
+
const changedSet = new Set(input.changedFiles.files);
|
|
166
|
+
const changedOnly = input.mode !== 'before_edit' && input.changedFiles.available;
|
|
167
|
+
for (const issue of input.issues) {
|
|
168
|
+
if (!issue.id.startsWith('plugin:'))
|
|
169
|
+
continue;
|
|
170
|
+
reasons.push({
|
|
171
|
+
severity: issue.severity,
|
|
172
|
+
source: 'plugin',
|
|
173
|
+
issueId: issue.id,
|
|
174
|
+
file: firstIssueFile(issue),
|
|
175
|
+
message: `${issue.severity === 'error' ? 'Plugin policy blocks' : 'Plugin policy flags'} ${issue.id}: ${issue.title}`,
|
|
176
|
+
tool: 'projscan_plugin',
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (changedOnly) {
|
|
180
|
+
const changedIssues = input.issues.filter((issue) => issueTouchesChangedFile(issue, changedSet));
|
|
181
|
+
const error = changedIssues.find((issue) => issue.severity === 'error');
|
|
182
|
+
const warning = changedIssues.find((issue) => issue.severity === 'warning');
|
|
183
|
+
if (error) {
|
|
184
|
+
reasons.push({
|
|
185
|
+
severity: 'error',
|
|
186
|
+
source: 'doctor',
|
|
187
|
+
issueId: error.id,
|
|
188
|
+
file: firstIssueFile(error),
|
|
189
|
+
message: `Health error on changed file: ${error.title}`,
|
|
190
|
+
tool: 'projscan_doctor',
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
else if (warning) {
|
|
194
|
+
reasons.push({
|
|
195
|
+
severity: 'warning',
|
|
196
|
+
source: 'doctor',
|
|
197
|
+
issueId: warning.id,
|
|
198
|
+
file: firstIssueFile(warning),
|
|
199
|
+
message: `Health warning on changed file: ${warning.title}`,
|
|
200
|
+
tool: 'projscan_doctor',
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
else if (input.mode !== 'before_edit' && !input.changedFiles.available) {
|
|
205
|
+
reasons.push({
|
|
206
|
+
severity: 'warning',
|
|
207
|
+
source: 'changed-files',
|
|
208
|
+
message: `Changed files unavailable: ${input.changedFiles.reason ?? 'unknown reason'}`,
|
|
209
|
+
tool: 'projscan_review',
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
if (input.mode !== 'before_edit' &&
|
|
213
|
+
input.changedFiles.available &&
|
|
214
|
+
input.changedFiles.count > input.maxChangedFiles) {
|
|
215
|
+
reasons.push({
|
|
216
|
+
severity: 'warning',
|
|
217
|
+
source: 'changed-files',
|
|
218
|
+
message: `${input.changedFiles.count} changed files exceeds the preflight threshold of ${input.maxChangedFiles}`,
|
|
219
|
+
tool: 'projscan_review',
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
if (input.review.available) {
|
|
223
|
+
if ((input.review.newTaintFlows ?? 0) > 0) {
|
|
224
|
+
reasons.push({
|
|
225
|
+
severity: 'error',
|
|
226
|
+
source: 'taint',
|
|
227
|
+
message: `${input.review.newTaintFlows} new taint flow(s) found in review`,
|
|
228
|
+
tool: 'projscan_review',
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
if (input.review.verdict === 'block') {
|
|
232
|
+
reasons.push({
|
|
233
|
+
severity: 'error',
|
|
234
|
+
source: 'review',
|
|
235
|
+
message: 'Review verdict is block',
|
|
236
|
+
tool: 'projscan_review',
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
else if (input.review.verdict === 'review') {
|
|
240
|
+
reasons.push({
|
|
241
|
+
severity: 'warning',
|
|
242
|
+
source: 'review',
|
|
243
|
+
message: 'Review verdict requires careful review',
|
|
244
|
+
tool: 'projscan_review',
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
else if (input.mode !== 'before_edit') {
|
|
249
|
+
reasons.push({
|
|
250
|
+
severity: 'warning',
|
|
251
|
+
source: 'review',
|
|
252
|
+
message: `Review unavailable: ${input.review.reason ?? 'unknown reason'}`,
|
|
253
|
+
tool: 'projscan_review',
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
const touched = new Set(input.session.touchedFiles);
|
|
257
|
+
const hotspotTouches = input.hotspots?.available === true
|
|
258
|
+
? input.hotspots.hotspots.filter((hotspot) => touched.has(hotspot.relativePath) && hotspot.riskScore >= 40)
|
|
259
|
+
: [];
|
|
260
|
+
for (const hotspot of hotspotTouches.slice(0, 3)) {
|
|
261
|
+
reasons.push({
|
|
262
|
+
severity: 'warning',
|
|
263
|
+
source: 'hotspots',
|
|
264
|
+
file: hotspot.relativePath,
|
|
265
|
+
message: `Touched file overlaps high-risk hotspot ${hotspot.relativePath} (risk ${hotspot.riskScore})`,
|
|
266
|
+
tool: 'projscan_hotspots',
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
if (input.health.errors > 0 && input.mode === 'before_merge' && !changedOnly) {
|
|
270
|
+
reasons.push({
|
|
271
|
+
severity: 'warning',
|
|
272
|
+
source: 'doctor',
|
|
273
|
+
message: `${input.health.errors} project health error(s) exist; changed-file scoping was unavailable`,
|
|
274
|
+
tool: 'projscan_doctor',
|
|
275
|
+
});
|
|
276
|
+
}
|
|
277
|
+
return reasons;
|
|
278
|
+
}
|
|
279
|
+
function buildEvidence(input) {
|
|
280
|
+
const pluginIssues = input.issues.filter((issue) => issue.id.startsWith('plugin:'));
|
|
281
|
+
const touched = input.hotspots?.available === true
|
|
282
|
+
? input.hotspots.hotspots
|
|
283
|
+
.filter((hotspot) => input.session.touchedFiles.includes(hotspot.relativePath))
|
|
284
|
+
.slice(0, MAX_EVIDENCE_FILES)
|
|
285
|
+
.map((hotspot) => ({ file: hotspot.relativePath, riskScore: hotspot.riskScore }))
|
|
286
|
+
: [];
|
|
287
|
+
const sessionTouchedFiles = input.session.touchedFiles.slice(0, MAX_EVIDENCE_FILES);
|
|
288
|
+
const changedEvidenceFiles = input.changedFiles.files.slice(0, MAX_EVIDENCE_FILES);
|
|
289
|
+
return {
|
|
290
|
+
health: {
|
|
291
|
+
score: input.health.score,
|
|
292
|
+
grade: input.health.grade,
|
|
293
|
+
errors: input.health.errors,
|
|
294
|
+
warnings: input.health.warnings,
|
|
295
|
+
infos: input.health.infos,
|
|
296
|
+
},
|
|
297
|
+
changedFiles: {
|
|
298
|
+
available: input.changedFiles.available,
|
|
299
|
+
count: input.changedFiles.count,
|
|
300
|
+
files: changedEvidenceFiles,
|
|
301
|
+
...(input.changedFiles.reason ? { reason: input.changedFiles.reason } : {}),
|
|
302
|
+
},
|
|
303
|
+
review: {
|
|
304
|
+
available: input.review.available,
|
|
305
|
+
...(input.review.verdict ? { verdict: input.review.verdict } : {}),
|
|
306
|
+
...(input.review.summary ? { summary: input.review.summary } : {}),
|
|
307
|
+
...(input.review.reason ? { reason: input.review.reason } : {}),
|
|
308
|
+
},
|
|
309
|
+
session: {
|
|
310
|
+
id: input.session.id,
|
|
311
|
+
touchedFiles: sessionTouchedFiles,
|
|
312
|
+
totalTouchedFiles: input.session.touchedFiles.length,
|
|
313
|
+
eventCount: input.session.eventCount,
|
|
314
|
+
...(input.session.touchedFiles.length > MAX_EVIDENCE_FILES ? { truncated: true } : {}),
|
|
315
|
+
},
|
|
316
|
+
hotspots: { touched },
|
|
317
|
+
plugins: {
|
|
318
|
+
enabled: input.pluginsEnabledForRun,
|
|
319
|
+
errorIssues: pluginIssues.filter((issue) => issue.severity === 'error').length,
|
|
320
|
+
warningIssues: pluginIssues.filter((issue) => issue.severity === 'warning').length,
|
|
321
|
+
},
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function buildRequiredChecks(mode, health, changedFiles, review) {
|
|
325
|
+
const checks = [
|
|
326
|
+
{
|
|
327
|
+
name: 'health',
|
|
328
|
+
status: health.errors > 0 ? 'fail' : health.warnings > 0 ? 'warn' : 'pass',
|
|
329
|
+
reason: `${health.errors} error(s), ${health.warnings} warning(s), ${health.infos} info`,
|
|
330
|
+
},
|
|
331
|
+
];
|
|
332
|
+
checks.push({
|
|
333
|
+
name: 'changed-files',
|
|
334
|
+
status: changedFiles.available ? 'pass' : 'unavailable',
|
|
335
|
+
reason: changedFiles.available
|
|
336
|
+
? `${changedFiles.count} changed file(s)`
|
|
337
|
+
: changedFiles.reason ?? 'changed-file detection unavailable',
|
|
338
|
+
});
|
|
339
|
+
checks.push({
|
|
340
|
+
name: 'review',
|
|
341
|
+
status: mode === 'before_edit'
|
|
342
|
+
? 'unavailable'
|
|
343
|
+
: !review.available
|
|
344
|
+
? 'unavailable'
|
|
345
|
+
: review.verdict === 'block'
|
|
346
|
+
? 'fail'
|
|
347
|
+
: review.verdict === 'review'
|
|
348
|
+
? 'warn'
|
|
349
|
+
: 'pass',
|
|
350
|
+
reason: mode === 'before_edit'
|
|
351
|
+
? 'review is not required before edits'
|
|
352
|
+
: review.available
|
|
353
|
+
? review.summary ?? review.verdict
|
|
354
|
+
: review.reason ?? 'review unavailable',
|
|
355
|
+
});
|
|
356
|
+
return checks;
|
|
357
|
+
}
|
|
358
|
+
function buildSuggestedActions(reasons, mode, changedFiles) {
|
|
359
|
+
if (reasons.length === 0)
|
|
360
|
+
return [];
|
|
361
|
+
const actions = [];
|
|
362
|
+
if (reasons.some((reason) => reason.source === 'review' || reason.source === 'taint')) {
|
|
363
|
+
actions.push({
|
|
364
|
+
label: 'Inspect the full review before continuing',
|
|
365
|
+
command: 'projscan review --format json',
|
|
366
|
+
tool: 'projscan_review',
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
if (reasons.some((reason) => reason.source === 'doctor' || reason.source === 'plugin')) {
|
|
370
|
+
actions.push({
|
|
371
|
+
label: 'Inspect health issues and plugin policy findings',
|
|
372
|
+
command: 'projscan doctor --format json',
|
|
373
|
+
tool: 'projscan_doctor',
|
|
374
|
+
});
|
|
375
|
+
}
|
|
376
|
+
if (reasons.some((reason) => reason.source === 'hotspots')) {
|
|
377
|
+
actions.push({
|
|
378
|
+
label: 'Inspect touched hotspots',
|
|
379
|
+
command: 'projscan hotspots --format json',
|
|
380
|
+
tool: 'projscan_hotspots',
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
if (mode !== 'before_edit' && !changedFiles.available) {
|
|
384
|
+
actions.push({
|
|
385
|
+
label: 'Run preflight with an explicit base ref',
|
|
386
|
+
command: 'projscan preflight --base-ref main --format json',
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
return dedupeActions(actions);
|
|
390
|
+
}
|
|
391
|
+
function buildToolCalls(reasons, mode, changedFiles) {
|
|
392
|
+
return buildSuggestedActions(reasons, mode, changedFiles).map((action) => ({
|
|
393
|
+
label: action.label,
|
|
394
|
+
...(action.tool ? { tool: action.tool } : {}),
|
|
395
|
+
...(action.args ? { args: action.args } : {}),
|
|
396
|
+
}));
|
|
397
|
+
}
|
|
398
|
+
function dedupeActions(actions) {
|
|
399
|
+
const seen = new Set();
|
|
400
|
+
const out = [];
|
|
401
|
+
for (const action of actions) {
|
|
402
|
+
const key = `${action.label}:${action.command ?? action.tool ?? ''}`;
|
|
403
|
+
if (seen.has(key))
|
|
404
|
+
continue;
|
|
405
|
+
seen.add(key);
|
|
406
|
+
out.push(action);
|
|
407
|
+
}
|
|
408
|
+
return out;
|
|
409
|
+
}
|
|
410
|
+
function issueTouchesChangedFile(issue, changedFiles) {
|
|
411
|
+
return (issue.locations ?? []).some((location) => location.file && changedFiles.has(location.file));
|
|
412
|
+
}
|
|
413
|
+
function firstIssueFile(issue) {
|
|
414
|
+
return issue.locations?.find((location) => location.file)?.file;
|
|
415
|
+
}
|
|
416
|
+
//# sourceMappingURL=preflight.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"preflight.js","sourceRoot":"","sources":["../../src/core/preflight.ts"],"names":[],"mappings":"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"}
|
package/dist/core/review.js
CHANGED
|
@@ -126,6 +126,7 @@ export async function computeReview(rootPath, options = {}) {
|
|
|
126
126
|
const riskyFunctions = findRiskyFunctions(baseGraph, headGraph, prDiff);
|
|
127
127
|
// Dependency changes across root + workspaces.
|
|
128
128
|
const dependencyChanges = diffManifests(basePackageManifests, headPackageManifests);
|
|
129
|
+
const contractChanges = buildContractChanges(prDiff, baseGraph, headGraph, basePackageManifests, headPackageManifests);
|
|
129
130
|
// 1.6+ — taint flows newly introduced at head. A flow is "new" iff
|
|
130
131
|
// (a) the (sourceFn, sinkFn) pair didn't exist at base, AND
|
|
131
132
|
// (b) at least one file along the flow's path is in the PR diff.
|
|
@@ -149,6 +150,7 @@ export async function computeReview(rootPath, options = {}) {
|
|
|
149
150
|
newCycles,
|
|
150
151
|
riskyFunctions,
|
|
151
152
|
dependencyChanges,
|
|
153
|
+
contractChanges,
|
|
152
154
|
newTaintFlows,
|
|
153
155
|
verdict,
|
|
154
156
|
summary,
|
|
@@ -399,8 +401,22 @@ async function readOneManifest(dir, manifestFile, workspaceName) {
|
|
|
399
401
|
manifestFile,
|
|
400
402
|
dependencies: parsed.dependencies ?? {},
|
|
401
403
|
devDependencies: parsed.devDependencies ?? {},
|
|
404
|
+
entrypoints: readEntrypoints(parsed),
|
|
402
405
|
};
|
|
403
406
|
}
|
|
407
|
+
function readEntrypoints(parsed) {
|
|
408
|
+
const out = {};
|
|
409
|
+
for (const field of ['main', 'module', 'types', 'exports', 'bin']) {
|
|
410
|
+
const value = parsed[field];
|
|
411
|
+
if (value === undefined)
|
|
412
|
+
continue;
|
|
413
|
+
out[field] = entrypointValue(value);
|
|
414
|
+
}
|
|
415
|
+
return out;
|
|
416
|
+
}
|
|
417
|
+
function entrypointValue(value) {
|
|
418
|
+
return typeof value === 'string' ? value : JSON.stringify(value);
|
|
419
|
+
}
|
|
404
420
|
function diffManifests(base, head) {
|
|
405
421
|
const out = [];
|
|
406
422
|
const allManifests = new Set([...base.keys(), ...head.keys()]);
|
|
@@ -417,6 +433,81 @@ function diffManifests(base, head) {
|
|
|
417
433
|
out.sort((a, b) => a.manifestFile.localeCompare(b.manifestFile));
|
|
418
434
|
return out;
|
|
419
435
|
}
|
|
436
|
+
function buildContractChanges(prDiff, baseGraph, headGraph, baseManifests, headManifests) {
|
|
437
|
+
const changes = [];
|
|
438
|
+
for (const file of prDiff.filesAdded) {
|
|
439
|
+
const entry = headGraph.files.get(file);
|
|
440
|
+
for (const exp of entry?.exports ?? []) {
|
|
441
|
+
changes.push(exportContractChange('export-added', file, exp.name));
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
for (const file of prDiff.filesRemoved) {
|
|
445
|
+
const entry = baseGraph.files.get(file);
|
|
446
|
+
for (const exp of entry?.exports ?? []) {
|
|
447
|
+
changes.push(exportContractChange('export-removed', file, exp.name));
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
for (const file of prDiff.filesModified) {
|
|
451
|
+
for (const symbol of file.exportsAdded) {
|
|
452
|
+
changes.push(exportContractChange('export-added', file.relativePath, symbol));
|
|
453
|
+
}
|
|
454
|
+
for (const symbol of file.exportsRemoved) {
|
|
455
|
+
changes.push(exportContractChange('export-removed', file.relativePath, symbol));
|
|
456
|
+
}
|
|
457
|
+
for (const rename of file.exportsRenamed) {
|
|
458
|
+
changes.push({
|
|
459
|
+
kind: 'export-renamed',
|
|
460
|
+
file: file.relativePath,
|
|
461
|
+
symbol: rename.to,
|
|
462
|
+
before: rename.from,
|
|
463
|
+
after: rename.to,
|
|
464
|
+
confidence: 'high',
|
|
465
|
+
why: `Export "${rename.from}" was renamed to "${rename.to}" in ${file.relativePath}; downstream imports of the old name can fail at compile time or runtime.`,
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
changes.push(...entrypointContractChanges(baseManifests, headManifests));
|
|
470
|
+
return changes;
|
|
471
|
+
}
|
|
472
|
+
function exportContractChange(kind, file, symbol) {
|
|
473
|
+
return {
|
|
474
|
+
kind,
|
|
475
|
+
file,
|
|
476
|
+
symbol,
|
|
477
|
+
confidence: 'high',
|
|
478
|
+
why: kind === 'export-added'
|
|
479
|
+
? `Export "${symbol}" was added in ${file}; downstream code may start depending on a new public API.`
|
|
480
|
+
: `Export "${symbol}" was removed from ${file}; downstream imports can fail at compile time or runtime.`,
|
|
481
|
+
};
|
|
482
|
+
}
|
|
483
|
+
function entrypointContractChanges(base, head) {
|
|
484
|
+
const out = [];
|
|
485
|
+
const allManifests = new Set([...base.keys(), ...head.keys()]);
|
|
486
|
+
for (const manifestFile of allManifests) {
|
|
487
|
+
const baseEntrypoints = base.get(manifestFile)?.entrypoints ?? {};
|
|
488
|
+
const headEntrypoints = head.get(manifestFile)?.entrypoints ?? {};
|
|
489
|
+
const fields = new Set([...Object.keys(baseEntrypoints), ...Object.keys(headEntrypoints)]);
|
|
490
|
+
for (const field of fields) {
|
|
491
|
+
const before = baseEntrypoints[field];
|
|
492
|
+
const after = headEntrypoints[field];
|
|
493
|
+
if (before === after)
|
|
494
|
+
continue;
|
|
495
|
+
const kind = field === 'exports' ? 'public-export-changed' : 'entrypoint-changed';
|
|
496
|
+
out.push({
|
|
497
|
+
kind,
|
|
498
|
+
file: manifestFile,
|
|
499
|
+
symbol: field,
|
|
500
|
+
...(before !== undefined ? { before } : {}),
|
|
501
|
+
...(after !== undefined ? { after } : {}),
|
|
502
|
+
confidence: 'high',
|
|
503
|
+
why: kind === 'public-export-changed'
|
|
504
|
+
? `${manifestFile} package "exports" changed; consumers may resolve different public modules.`
|
|
505
|
+
: `${manifestFile} package "${field}" changed from ${before ?? '<unset>'} to ${after ?? '<unset>'}; package consumers may load a different entrypoint.`,
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
return out.sort((a, b) => `${a.file}:${a.symbol ?? ''}`.localeCompare(`${b.file}:${b.symbol ?? ''}`));
|
|
510
|
+
}
|
|
420
511
|
function diffOneManifest(base, head, manifestFile) {
|
|
421
512
|
const workspace = head?.workspace ?? base?.workspace ?? '';
|
|
422
513
|
const baseDeps = base?.dependencies ?? {};
|
|
@@ -645,7 +736,15 @@ export function shapeReviewForTier(report, tier) {
|
|
|
645
736
|
const riskyFunctionsAdded = report.riskyFunctions.length;
|
|
646
737
|
const depsChanged = report.dependencyChanges.length;
|
|
647
738
|
const taintFlowsAdded = report.newTaintFlows?.length ?? 0;
|
|
648
|
-
const
|
|
739
|
+
const contractChanges = report.contractChanges?.length ?? 0;
|
|
740
|
+
const totals = {
|
|
741
|
+
filesChanged,
|
|
742
|
+
cyclesAdded,
|
|
743
|
+
riskyFunctionsAdded,
|
|
744
|
+
depsChanged,
|
|
745
|
+
taintFlowsAdded,
|
|
746
|
+
contractChanges,
|
|
747
|
+
};
|
|
649
748
|
if (tier === 'verdict-only') {
|
|
650
749
|
return {
|
|
651
750
|
available: report.available,
|
|
@@ -683,6 +782,7 @@ export function shapeReviewForTier(report, tier) {
|
|
|
683
782
|
newCycles: report.newCycles.slice(0, 3),
|
|
684
783
|
riskyFunctions: report.riskyFunctions.slice(0, 3),
|
|
685
784
|
dependencyChanges: report.dependencyChanges.slice(0, 3),
|
|
785
|
+
contractChanges: report.contractChanges?.slice(0, TOP) ?? [],
|
|
686
786
|
newTaintFlows: report.newTaintFlows?.slice(0, 5) ?? [],
|
|
687
787
|
verdict: report.verdict,
|
|
688
788
|
summary: report.summary,
|