projscan 1.10.0 → 2.0.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 +47 -19
- package/dist/cli/_shared.d.ts +3 -1
- package/dist/cli/_shared.js +37 -15
- package/dist/cli/_shared.js.map +1 -1
- package/dist/cli/commands/analyze.js +4 -1
- package/dist/cli/commands/analyze.js.map +1 -1
- package/dist/cli/commands/ci.js +19 -2
- package/dist/cli/commands/ci.js.map +1 -1
- package/dist/cli/commands/doctor.js +6 -1
- package/dist/cli/commands/doctor.js.map +1 -1
- package/dist/cli/commands/explain.js +4 -5
- package/dist/cli/commands/explain.js.map +1 -1
- package/dist/cli/commands/plugin.d.ts +3 -3
- package/dist/cli/commands/plugin.js +31 -38
- package/dist/cli/commands/plugin.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/plugins.d.ts +68 -5
- package/dist/core/plugins.js +229 -39
- package/dist/core/plugins.js.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +1 -0
- package/dist/index.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 +5 -8
- package/dist/mcp/tools/plugin.js +18 -36
- package/dist/mcp/tools/plugin.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 +4 -4
- package/docs/2.0-MIGRATION.md +80 -0
- package/docs/PLUGIN-AUTHORING.md +209 -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 +7 -3
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import { calculateScore } from '../utils/scoreCalculator.js';
|
|
2
|
+
export const CLI_JSON_SCHEMA_VERSION = 2;
|
|
3
|
+
function emitJson(payload) {
|
|
4
|
+
console.log(JSON.stringify({ schemaVersion: CLI_JSON_SCHEMA_VERSION, ...payload }, null, 2));
|
|
5
|
+
}
|
|
2
6
|
export function reportAnalysisJson(report) {
|
|
3
|
-
|
|
7
|
+
emitJson(report);
|
|
4
8
|
}
|
|
5
9
|
export function reportHealthJson(issues) {
|
|
6
10
|
const { score, grade, errors, warnings, infos } = calculateScore(issues);
|
|
7
11
|
console.log(JSON.stringify({
|
|
12
|
+
schemaVersion: CLI_JSON_SCHEMA_VERSION,
|
|
8
13
|
health: {
|
|
9
14
|
score,
|
|
10
15
|
grade,
|
|
@@ -19,6 +24,7 @@ export function reportHealthJson(issues) {
|
|
|
19
24
|
export function reportCiJson(issues, threshold) {
|
|
20
25
|
const { score, grade, errors, warnings, infos } = calculateScore(issues);
|
|
21
26
|
console.log(JSON.stringify({
|
|
27
|
+
schemaVersion: CLI_JSON_SCHEMA_VERSION,
|
|
22
28
|
ci: {
|
|
23
29
|
score,
|
|
24
30
|
grade,
|
|
@@ -33,57 +39,57 @@ export function reportCiJson(issues, threshold) {
|
|
|
33
39
|
}, null, 2));
|
|
34
40
|
}
|
|
35
41
|
export function reportDiffJson(diff) {
|
|
36
|
-
|
|
42
|
+
emitJson({ diff });
|
|
37
43
|
}
|
|
38
44
|
export function reportExplanationJson(explanation) {
|
|
39
|
-
|
|
45
|
+
emitJson(explanation);
|
|
40
46
|
}
|
|
41
47
|
export function reportDiagramJson(layers) {
|
|
42
|
-
|
|
48
|
+
emitJson({ architecture: layers });
|
|
43
49
|
}
|
|
44
50
|
export function reportStructureJson(tree) {
|
|
45
|
-
|
|
51
|
+
emitJson({ structure: tree });
|
|
46
52
|
}
|
|
47
53
|
export function reportDependenciesJson(report) {
|
|
48
|
-
|
|
54
|
+
emitJson(report);
|
|
49
55
|
}
|
|
50
56
|
export function reportHotspotsJson(report) {
|
|
51
|
-
|
|
57
|
+
emitJson({ hotspots: report });
|
|
52
58
|
}
|
|
53
59
|
export function reportFileJson(inspection) {
|
|
54
|
-
|
|
60
|
+
emitJson({ file: inspection });
|
|
55
61
|
}
|
|
56
62
|
export function reportOutdatedJson(report) {
|
|
57
|
-
|
|
63
|
+
emitJson({ outdated: report });
|
|
58
64
|
}
|
|
59
65
|
export function reportAuditJson(report) {
|
|
60
|
-
|
|
66
|
+
emitJson({ audit: report });
|
|
61
67
|
}
|
|
62
68
|
export function reportUpgradeJson(preview) {
|
|
63
|
-
|
|
69
|
+
emitJson({ upgrade: preview });
|
|
64
70
|
}
|
|
65
71
|
export function reportCoverageJson(report) {
|
|
66
|
-
|
|
72
|
+
emitJson({ coverage: report });
|
|
67
73
|
}
|
|
68
74
|
export function reportCouplingJson(report) {
|
|
69
|
-
|
|
75
|
+
emitJson({ coupling: report });
|
|
70
76
|
}
|
|
71
77
|
export function reportPrDiffJson(report) {
|
|
72
|
-
|
|
78
|
+
emitJson({ prDiff: report });
|
|
73
79
|
}
|
|
74
80
|
export function reportReviewJson(report) {
|
|
75
|
-
|
|
81
|
+
emitJson({ review: report });
|
|
76
82
|
}
|
|
77
83
|
export function reportFixSuggestJson(result) {
|
|
78
|
-
|
|
84
|
+
emitJson({ fixSuggest: result });
|
|
79
85
|
}
|
|
80
86
|
export function reportExplainIssueJson(explanation) {
|
|
81
|
-
|
|
87
|
+
emitJson({ issueExplanation: explanation });
|
|
82
88
|
}
|
|
83
89
|
export function reportImpactJson(report) {
|
|
84
|
-
|
|
90
|
+
emitJson({ impact: report });
|
|
85
91
|
}
|
|
86
92
|
export function reportWorkspacesJson(info) {
|
|
87
|
-
|
|
93
|
+
emitJson({ workspaces: info });
|
|
88
94
|
}
|
|
89
95
|
//# sourceMappingURL=jsonReporter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"jsonReporter.js","sourceRoot":"","sources":["../../src/reporters/jsonReporter.ts"],"names":[],"mappings":"AAsBA,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAE7D,MAAM,
|
|
1
|
+
{"version":3,"file":"jsonReporter.js","sourceRoot":"","sources":["../../src/reporters/jsonReporter.ts"],"names":[],"mappings":"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"}
|
package/dist/tool-manifest.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projscan",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"mcpProtocolVersion": "2025-03-26",
|
|
5
|
-
"generatedAt": "2026-05-
|
|
5
|
+
"generatedAt": "2026-05-18T11:46:44.815Z",
|
|
6
6
|
"toolCount": 28,
|
|
7
7
|
"tools": [
|
|
8
8
|
{
|
|
@@ -714,7 +714,7 @@
|
|
|
714
714
|
},
|
|
715
715
|
{
|
|
716
716
|
"name": "projscan_plugin",
|
|
717
|
-
"description": "
|
|
717
|
+
"description": "Discover and validate stable local analyzer and reporter plugins under .projscan-plugins/. Execution is opt-in via the PROJSCAN_PLUGINS_PREVIEW=1 env flag because plugins are local code. Use action:\"list\" to see what is discoverable today, action:\"validate\" to check a manifest before committing it.",
|
|
718
718
|
"inputSchema": {
|
|
719
719
|
"type": "object",
|
|
720
720
|
"properties": {
|
|
@@ -724,7 +724,7 @@
|
|
|
724
724
|
"list",
|
|
725
725
|
"validate"
|
|
726
726
|
],
|
|
727
|
-
"description": "\"list\" enumerates manifests under <root>/.projscan-plugins/ with discovery status. \"validate\" lints a manifest at the given path against
|
|
727
|
+
"description": "\"list\" enumerates manifests under <root>/.projscan-plugins/ with discovery status. \"validate\" lints a manifest at the given path against schema v1."
|
|
728
728
|
},
|
|
729
729
|
"manifest_path": {
|
|
730
730
|
"type": "string",
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Migrating to projscan 2.0
|
|
2
|
+
|
|
3
|
+
projscan 2.0 stabilizes the local plugin platform and uses the major version
|
|
4
|
+
boundary to clean up deferred 1.x surfaces. MCP tool names and input schemas
|
|
5
|
+
remain stable; the intentional changes are concentrated in CLI JSON metadata,
|
|
6
|
+
the npm TypeScript API, and plugin contract documentation.
|
|
7
|
+
|
|
8
|
+
## Breaking Changes
|
|
9
|
+
|
|
10
|
+
### CLI JSON has `schemaVersion: 2`
|
|
11
|
+
|
|
12
|
+
Every built-in CLI JSON reporter now includes `schemaVersion: 2` at the top
|
|
13
|
+
level. Existing command data keys remain present, so most consumers can migrate
|
|
14
|
+
by ignoring the new field or branching on it.
|
|
15
|
+
|
|
16
|
+
Before:
|
|
17
|
+
|
|
18
|
+
```json
|
|
19
|
+
{
|
|
20
|
+
"health": {
|
|
21
|
+
"score": 100
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
After:
|
|
27
|
+
|
|
28
|
+
```json
|
|
29
|
+
{
|
|
30
|
+
"schemaVersion": 2,
|
|
31
|
+
"health": {
|
|
32
|
+
"score": 100
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Deprecated regex extractors are removed
|
|
38
|
+
|
|
39
|
+
The deprecated `extractImports` and `extractExports` helpers are removed from
|
|
40
|
+
the npm public API. They were JS/TS-only regex helpers and could not represent
|
|
41
|
+
the multi-language AST graph accurately.
|
|
42
|
+
|
|
43
|
+
Use one of these instead:
|
|
44
|
+
|
|
45
|
+
- `buildCodeGraph(rootPath, files)` for full graph construction.
|
|
46
|
+
- `importsOf(graph, file)` for imports from one file.
|
|
47
|
+
- `exportsOf(graph, file)` for exports from one file.
|
|
48
|
+
- `inspectFile(rootPath, file)` for a user-facing file explanation.
|
|
49
|
+
|
|
50
|
+
### Language ids are extensible
|
|
51
|
+
|
|
52
|
+
Built-in language ids remain documented, but `LanguageId` is no longer a closed
|
|
53
|
+
union. Consumers that need only built-ins should use `BuiltinLanguageId`.
|
|
54
|
+
Consumers that accept plugin-provided languages should use `LanguageId`.
|
|
55
|
+
|
|
56
|
+
## Plugin Contract
|
|
57
|
+
|
|
58
|
+
Plugin manifests use `schemaVersion: 1` in 2.0. Analyzer plugins export
|
|
59
|
+
`check(rootPath, files)`. Reporter plugins export `render(context)`.
|
|
60
|
+
|
|
61
|
+
The stable manifest fields are:
|
|
62
|
+
|
|
63
|
+
- `schemaVersion`
|
|
64
|
+
- `name`
|
|
65
|
+
- `kind`
|
|
66
|
+
- `module`
|
|
67
|
+
- analyzer-only `category`
|
|
68
|
+
- reporter-only `commands`
|
|
69
|
+
- optional `description`
|
|
70
|
+
|
|
71
|
+
Plugin execution stays local. projscan does not fetch remote plugin code.
|
|
72
|
+
|
|
73
|
+
## Compatibility Notes
|
|
74
|
+
|
|
75
|
+
MCP tool names and input schemas remain stable. The major-version changes are
|
|
76
|
+
limited to documented CLI JSON metadata, npm TypeScript API cleanup, and plugin
|
|
77
|
+
contract stabilization.
|
|
78
|
+
|
|
79
|
+
During migration, prefer MCP tools or CLI JSON over console output. Console
|
|
80
|
+
format remains human-facing and may change visually between releases.
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
# Plugin Authoring
|
|
2
|
+
|
|
3
|
+
projscan 2.0 stabilizes the local analyzer and reporter plugin contract.
|
|
4
|
+
Plugin execution is opt-in via `PROJSCAN_PLUGINS_PREVIEW=1` so repositories
|
|
5
|
+
must explicitly trust local plugin code before it runs.
|
|
6
|
+
|
|
7
|
+
Plugins are local code. Enabling the opt-in flag means you trust the plugin code in
|
|
8
|
+
the repository, the same way you trust project scripts in `package.json`.
|
|
9
|
+
projscan does not fetch remote plugin code.
|
|
10
|
+
|
|
11
|
+
## Layout
|
|
12
|
+
|
|
13
|
+
Plugin manifests live under `.projscan-plugins/`:
|
|
14
|
+
|
|
15
|
+
```text
|
|
16
|
+
.projscan-plugins/
|
|
17
|
+
policy.projscan-plugin.json
|
|
18
|
+
policy.mjs
|
|
19
|
+
team-summary.projscan-plugin.json
|
|
20
|
+
team-summary.mjs
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Manifest
|
|
24
|
+
|
|
25
|
+
Analyzer plugins add issues to the normal projscan issue stream:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"schemaVersion": 1,
|
|
30
|
+
"name": "policy",
|
|
31
|
+
"kind": "analyzer",
|
|
32
|
+
"module": "./policy.mjs",
|
|
33
|
+
"category": "custom",
|
|
34
|
+
"description": "Project-specific policy checks"
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Reporter plugins render CLI output for selected commands:
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"schemaVersion": 1,
|
|
43
|
+
"name": "team-summary",
|
|
44
|
+
"kind": "reporter",
|
|
45
|
+
"module": "./team-summary.mjs",
|
|
46
|
+
"commands": ["doctor", "analyze", "ci"],
|
|
47
|
+
"description": "Compact team health summary"
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
Fields:
|
|
52
|
+
|
|
53
|
+
- `schemaVersion`: must be `1`.
|
|
54
|
+
- `name`: stable plugin identifier. Issue ids are prefixed with `plugin:<name>:`.
|
|
55
|
+
- `kind`: `analyzer` or `reporter`.
|
|
56
|
+
- `module`: relative path inside the plugin directory. Absolute paths and `..` are rejected.
|
|
57
|
+
- `category`: analyzer-only fallback issue category when a plugin issue omits one.
|
|
58
|
+
- `commands`: reporter-only list of CLI commands the reporter supports: `doctor`, `analyze`, `ci`.
|
|
59
|
+
- `description`: optional summary for humans and agents.
|
|
60
|
+
|
|
61
|
+
## Schema
|
|
62
|
+
|
|
63
|
+
The machine-readable manifest schema lives at
|
|
64
|
+
[`docs/plugin.schema.json`](plugin.schema.json). The examples under
|
|
65
|
+
[`docs/examples/plugins/`](examples/plugins/) are tested in CI.
|
|
66
|
+
|
|
67
|
+
## Analyzer Module
|
|
68
|
+
|
|
69
|
+
The module must export a `check(rootPath, files)` function, either as the
|
|
70
|
+
default export or a named export.
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
export default {
|
|
74
|
+
check: async (rootPath, files) => {
|
|
75
|
+
return files
|
|
76
|
+
.filter((file) => file.relativePath.endsWith('.ts'))
|
|
77
|
+
.filter((file) => file.relativePath.includes('legacy'))
|
|
78
|
+
.map((file) => ({
|
|
79
|
+
id: 'legacy-typescript-file',
|
|
80
|
+
title: 'Legacy TypeScript file',
|
|
81
|
+
description: `${file.relativePath} is under the legacy tree.`,
|
|
82
|
+
severity: 'warning',
|
|
83
|
+
category: 'custom',
|
|
84
|
+
fixAvailable: false,
|
|
85
|
+
locations: [{ file: file.relativePath, line: 1 }],
|
|
86
|
+
}));
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Required issue fields:
|
|
92
|
+
|
|
93
|
+
- `id`
|
|
94
|
+
- `title`
|
|
95
|
+
- `description`
|
|
96
|
+
- `severity`: `error`, `warning`, or `info`
|
|
97
|
+
- `category`
|
|
98
|
+
- `fixAvailable`
|
|
99
|
+
|
|
100
|
+
Malformed issues are dropped so one bad plugin cannot poison the issue stream.
|
|
101
|
+
|
|
102
|
+
## Reporter Module
|
|
103
|
+
|
|
104
|
+
Reporter plugins are CLI-only. The module must export a
|
|
105
|
+
`render(context)` function, either as the default export or a named export.
|
|
106
|
+
|
|
107
|
+
```js
|
|
108
|
+
export default {
|
|
109
|
+
render: async ({ command, payload }) => {
|
|
110
|
+
if (command === 'ci') {
|
|
111
|
+
return `CI ${payload.ci.pass ? 'passed' : 'failed'}: ${payload.ci.score}/100`;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const issues = payload.issues ?? [];
|
|
115
|
+
const score = payload.health?.score ?? 'analysis';
|
|
116
|
+
return `${command}: ${issues.length} issue(s), score ${score}`;
|
|
117
|
+
},
|
|
118
|
+
};
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
`context` contains:
|
|
122
|
+
|
|
123
|
+
- `command`: `doctor`, `analyze`, or `ci`.
|
|
124
|
+
- `rootPath`: absolute project root.
|
|
125
|
+
- `manifest`: the validated reporter manifest.
|
|
126
|
+
- `payload`: the command payload.
|
|
127
|
+
|
|
128
|
+
Payloads:
|
|
129
|
+
|
|
130
|
+
- `doctor`: `{ health, issues }`
|
|
131
|
+
- `analyze`: the same `AnalysisReport` shape returned by `--format json`
|
|
132
|
+
- `ci`: `{ ci: { score, grade, pass, threshold, totalIssues, errors, warnings, info, issues } }`
|
|
133
|
+
|
|
134
|
+
Renderers must return a string. They should not write directly to stdout or
|
|
135
|
+
stderr; projscan writes the returned text after the renderer succeeds.
|
|
136
|
+
|
|
137
|
+
## Custom Presentation
|
|
138
|
+
|
|
139
|
+
Reporter plugins are the customization boundary for team-specific presentation.
|
|
140
|
+
Use them for white-label reports, team-branded summaries, and output shaped for
|
|
141
|
+
local workflows. The built-in HTML reporter stays the default core renderer
|
|
142
|
+
instead of growing project-specific theming flags.
|
|
143
|
+
|
|
144
|
+
## Validate
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
projscan plugin validate .projscan-plugins/policy.projscan-plugin.json
|
|
148
|
+
projscan plugin validate .projscan-plugins/policy.projscan-plugin.json --format json
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Validation reports structured diagnostics with a stable `code`, the manifest
|
|
152
|
+
`field` when applicable, a `message`, and sometimes a `hint`.
|
|
153
|
+
|
|
154
|
+
## List
|
|
155
|
+
|
|
156
|
+
```sh
|
|
157
|
+
projscan plugin list
|
|
158
|
+
projscan plugin list --format json
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
The list command discovers manifests whether or not execution is enabled. It
|
|
162
|
+
shows `enabled:false` until the opt-in flag is set.
|
|
163
|
+
|
|
164
|
+
## Enable
|
|
165
|
+
|
|
166
|
+
```sh
|
|
167
|
+
PROJSCAN_PLUGINS_PREVIEW=1 projscan doctor
|
|
168
|
+
PROJSCAN_PLUGINS_PREVIEW=1 projscan ci
|
|
169
|
+
PROJSCAN_PLUGINS_PREVIEW=1 projscan analyze
|
|
170
|
+
PROJSCAN_PLUGINS_PREVIEW=1 projscan doctor --reporter team-summary
|
|
171
|
+
PROJSCAN_PLUGINS_PREVIEW=1 projscan analyze --reporter team-summary
|
|
172
|
+
PROJSCAN_PLUGINS_PREVIEW=1 projscan ci --reporter team-summary
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
When enabled, analyzer plugin issues are merged into the same issue stream as
|
|
176
|
+
built-in analyzer issues. That means they affect health scores and CI gates in
|
|
177
|
+
the same way.
|
|
178
|
+
|
|
179
|
+
Reporter plugins are selected with `--reporter <name>` on supported commands.
|
|
180
|
+
Do not combine `--reporter` with `--format json`, `markdown`, `sarif`, or
|
|
181
|
+
`html`; reporter output is its own stdout text.
|
|
182
|
+
|
|
183
|
+
## MCP
|
|
184
|
+
|
|
185
|
+
The `projscan_plugin` MCP tool supports:
|
|
186
|
+
|
|
187
|
+
- `action: "list"`
|
|
188
|
+
- `action: "validate"` with `manifest_path`
|
|
189
|
+
|
|
190
|
+
Plugin execution for MCP `projscan_doctor` and `projscan_analyze` follows the
|
|
191
|
+
same `PROJSCAN_PLUGINS_PREVIEW` flag as the CLI.
|
|
192
|
+
|
|
193
|
+
Reporter rendering is CLI-only. MCP tools continue to return structured
|
|
194
|
+
payloads.
|
|
195
|
+
|
|
196
|
+
## Failure Isolation
|
|
197
|
+
|
|
198
|
+
- One plugin failing to load does not stop other plugins.
|
|
199
|
+
- One plugin throwing during `check` does not stop built-in analyzers.
|
|
200
|
+
- Malformed issues are dropped.
|
|
201
|
+
- One reporter failing to load or render exits that CLI command with a
|
|
202
|
+
diagnostic instead of falling back to a misleading built-in report.
|
|
203
|
+
- Runtime plugin warnings go to stderr so JSON stdout stays parseable.
|
|
204
|
+
|
|
205
|
+
## Compatibility
|
|
206
|
+
|
|
207
|
+
This is the stable 2.0 plugin contract for local analyzer and reporter plugins.
|
|
208
|
+
New optional manifest fields may be added in 2.x; existing required fields keep
|
|
209
|
+
their names and types.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
check: async (_rootPath, files) => {
|
|
3
|
+
return files
|
|
4
|
+
.filter((file) => file.relativePath.endsWith('.ts'))
|
|
5
|
+
.filter((file) => file.relativePath.includes('legacy'))
|
|
6
|
+
.map((file) => ({
|
|
7
|
+
id: 'legacy-typescript-file',
|
|
8
|
+
title: 'Legacy TypeScript file',
|
|
9
|
+
description: `${file.relativePath} is under the legacy tree.`,
|
|
10
|
+
severity: 'warning',
|
|
11
|
+
category: 'custom',
|
|
12
|
+
fixAvailable: false,
|
|
13
|
+
locations: [{ file: file.relativePath, line: 1 }],
|
|
14
|
+
}));
|
|
15
|
+
},
|
|
16
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export default {
|
|
2
|
+
render: async ({ command, payload }) => {
|
|
3
|
+
if (command === 'ci') {
|
|
4
|
+
const ci = payload.ci;
|
|
5
|
+
return `team-radar ci ${ci.pass ? 'pass' : 'fail'} ${ci.score}/100 ${ci.grade} ${ci.totalIssues} issue(s)`;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
if (command === 'doctor') {
|
|
9
|
+
const health = payload.health;
|
|
10
|
+
const issues = Array.isArray(payload.issues) ? payload.issues.length : 0;
|
|
11
|
+
return `team-radar doctor ${health.score}/100 ${health.grade} ${issues} issue(s)`;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const issues = Array.isArray(payload.issues) ? payload.issues.length : 0;
|
|
15
|
+
return `team-radar analyze ${issues} issue(s)`;
|
|
16
|
+
},
|
|
17
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/abhiyoheswaran1/projscan/blob/main/docs/plugin.schema.json",
|
|
4
|
+
"title": "projscan plugin manifest",
|
|
5
|
+
"description": "Manifest schema for local projscan analyzer and reporter plugins.",
|
|
6
|
+
"oneOf": [
|
|
7
|
+
{
|
|
8
|
+
"title": "Analyzer plugin",
|
|
9
|
+
"type": "object",
|
|
10
|
+
"additionalProperties": false,
|
|
11
|
+
"required": ["schemaVersion", "name", "kind", "module", "category"],
|
|
12
|
+
"properties": {
|
|
13
|
+
"schemaVersion": {
|
|
14
|
+
"const": 1
|
|
15
|
+
},
|
|
16
|
+
"name": {
|
|
17
|
+
"type": "string",
|
|
18
|
+
"pattern": "^[a-z0-9._/-]{1,65}$"
|
|
19
|
+
},
|
|
20
|
+
"kind": {
|
|
21
|
+
"const": "analyzer"
|
|
22
|
+
},
|
|
23
|
+
"module": {
|
|
24
|
+
"type": "string",
|
|
25
|
+
"minLength": 1
|
|
26
|
+
},
|
|
27
|
+
"category": {
|
|
28
|
+
"type": "string",
|
|
29
|
+
"minLength": 1
|
|
30
|
+
},
|
|
31
|
+
"description": {
|
|
32
|
+
"type": "string"
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"title": "Reporter plugin",
|
|
38
|
+
"type": "object",
|
|
39
|
+
"additionalProperties": false,
|
|
40
|
+
"required": ["schemaVersion", "name", "kind", "module", "commands"],
|
|
41
|
+
"properties": {
|
|
42
|
+
"schemaVersion": {
|
|
43
|
+
"const": 1
|
|
44
|
+
},
|
|
45
|
+
"name": {
|
|
46
|
+
"type": "string",
|
|
47
|
+
"pattern": "^[a-z0-9._/-]{1,65}$"
|
|
48
|
+
},
|
|
49
|
+
"kind": {
|
|
50
|
+
"const": "reporter"
|
|
51
|
+
},
|
|
52
|
+
"module": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"minLength": 1
|
|
55
|
+
},
|
|
56
|
+
"commands": {
|
|
57
|
+
"type": "array",
|
|
58
|
+
"minItems": 1,
|
|
59
|
+
"items": {
|
|
60
|
+
"enum": ["doctor", "analyze", "ci"]
|
|
61
|
+
},
|
|
62
|
+
"uniqueItems": true
|
|
63
|
+
},
|
|
64
|
+
"description": {
|
|
65
|
+
"type": "string"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
}
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projscan",
|
|
3
3
|
"mcpName": "io.github.abhiyoheswaran1/projscan",
|
|
4
|
-
"version": "
|
|
5
|
-
"description": "Agent-first code intelligence. MCP server (2025-03-26) with AST parsing for JavaScript, TypeScript, Python, Go, Java, Ruby, Rust, PHP, C#, Kotlin, Swift, and C++; code graph, file + per-function AST cyclomatic complexity, per-function fan-in + fan-out, coupling + cycle detection, structural PR diff with HTML reporter, coverage report with HTML reporter, intent-grounded one-call PR review (projscan_review with optional `intent` arg) and long-running PR-watch mode with structured per-bucket deltas (projscan_review_watch), rule-driven fix suggestions + mechanical apply layer with rollback (projscan_apply_fix, projscan_fix_suggest, projscan_explain_issue), source-to-sink taint analysis (projscan_taint) with truncation reporting, transitive blast-radius analysis with cross-repo mode (projscan_impact for files and symbols), cross-repo workspace registration + intelligence (projscan_workspace_graph), per-function semantic search chunks (sub-file embeddings), per-rule confidence + severity drift + cost-summary analytics with live streaming (projscan_cost_summary), analyzer plugin API
|
|
4
|
+
"version": "2.0.0",
|
|
5
|
+
"description": "Agent-first code intelligence. MCP server (2025-03-26) with AST parsing for JavaScript, TypeScript, Python, Go, Java, Ruby, Rust, PHP, C#, Kotlin, Swift, and C++; code graph, file + per-function AST cyclomatic complexity, per-function fan-in + fan-out, coupling + cycle detection, structural PR diff with HTML reporter, coverage report with HTML reporter, intent-grounded one-call PR review (projscan_review with optional `intent` arg) and long-running PR-watch mode with structured per-bucket deltas (projscan_review_watch), rule-driven fix suggestions + mechanical apply layer with rollback (projscan_apply_fix, projscan_fix_suggest, projscan_explain_issue), source-to-sink taint analysis (projscan_taint) with truncation reporting, transitive blast-radius analysis with cross-repo mode (projscan_impact for files and symbols), cross-repo workspace registration + intelligence (projscan_workspace_graph), per-function semantic search chunks (sub-file embeddings), per-rule confidence + severity drift + cost-summary analytics with live streaming (projscan_cost_summary), stable local analyzer + reporter plugin API (projscan_plugin, CLI --reporter, opt-in via PROJSCAN_PLUGINS_PREVIEW=1), monorepo workspace awareness with cross-package import policy + per-package dependencies / outdated / audit, BM25 + optional semantic search, cursor pagination, progress notifications, context-budgeted output, and a stable-surface CI guard. CLI on the side.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./dist/index.js",
|
|
8
8
|
"types": "./dist/index.d.ts",
|
|
@@ -11,7 +11,11 @@
|
|
|
11
11
|
},
|
|
12
12
|
"files": [
|
|
13
13
|
"dist",
|
|
14
|
-
"README.md"
|
|
14
|
+
"README.md",
|
|
15
|
+
"docs/2.0-MIGRATION.md",
|
|
16
|
+
"docs/PLUGIN-AUTHORING.md",
|
|
17
|
+
"docs/plugin.schema.json",
|
|
18
|
+
"docs/examples/plugins"
|
|
15
19
|
],
|
|
16
20
|
"scripts": {
|
|
17
21
|
"build": "tsc && node scripts/copy-wasm.mjs && node scripts/generate-tool-manifest.mjs",
|