ios-agent-mcp 2.0.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 +64 -7
- package/dist/analyzers/memory.d.ts +12 -0
- package/dist/analyzers/memory.js +81 -0
- package/dist/analyzers/memory.js.map +1 -0
- package/dist/analyzers/performance.d.ts +10 -0
- package/dist/analyzers/performance.js +106 -0
- package/dist/analyzers/performance.js.map +1 -0
- package/dist/analyzers/security.d.ts +2 -0
- package/dist/analyzers/security.js +82 -0
- package/dist/analyzers/security.js.map +1 -0
- package/dist/analyzers/skill.d.ts +34 -0
- package/dist/analyzers/skill.js +483 -0
- package/dist/analyzers/skill.js.map +1 -0
- package/dist/analyzers/testing.d.ts +16 -0
- package/dist/analyzers/testing.js +135 -0
- package/dist/analyzers/testing.js.map +1 -0
- package/dist/analyzers/types.d.ts +19 -1
- package/dist/analyzers/types.js +65 -3
- package/dist/analyzers/types.js.map +1 -1
- package/dist/index.js +159 -4
- package/dist/index.js.map +1 -1
- package/dist/report.d.ts +10 -0
- package/dist/report.js +40 -0
- package/dist/report.js.map +1 -1
- package/dist/resources.d.ts +67 -0
- package/dist/resources.js +222 -0
- package/dist/resources.js.map +1 -0
- package/dist/result.d.ts +203 -0
- package/dist/result.js +135 -0
- package/dist/result.js.map +1 -0
- package/dist/scan.d.ts +9 -0
- package/dist/scan.js +110 -0
- package/dist/scan.js.map +1 -1
- package/dist/version.d.ts +1 -0
- package/dist/version.js +4 -0
- package/dist/version.js.map +1 -0
- package/mcp.json +76 -11
- package/package.json +6 -5
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { realpathSync, statSync } from "node:fs";
|
|
2
|
+
import { dirname, join, resolve } from "node:path";
|
|
3
|
+
import { analyzeConcurrency } from "./analyzers/concurrency.js";
|
|
4
|
+
import { analyzeArchitecture } from "./analyzers/architecture.js";
|
|
5
|
+
import { analyzeSwiftUI } from "./analyzers/swiftui.js";
|
|
6
|
+
import { analyzeAvailability } from "./analyzers/availability.js";
|
|
7
|
+
import { analyzeAppStore, analyzeProjectLevelAppStore } from "./analyzers/appstore.js";
|
|
8
|
+
import { analyzeMemory } from "./analyzers/memory.js";
|
|
9
|
+
import { analyzeSecurity } from "./analyzers/security.js";
|
|
10
|
+
import { analyzeTesting, analyzeTestCoverage } from "./analyzers/testing.js";
|
|
11
|
+
import { analyzePerformance } from "./analyzers/performance.js";
|
|
12
|
+
import { sortFindings } from "./analyzers/types.js";
|
|
13
|
+
import { readProjectContext, readSwiftFiles, resolveProjectRoot, summarizeProject, } from "./scan.js";
|
|
14
|
+
/**
|
|
15
|
+
* Resource-backed project state.
|
|
16
|
+
*
|
|
17
|
+
* Tools are verbs the model chooses to call. Resources are nouns a client can
|
|
18
|
+
* read without being asked — so `ios://project/info` lets a client attach the
|
|
19
|
+
* project's shape to context up front, instead of the model having to think to
|
|
20
|
+
* run an analysis first.
|
|
21
|
+
*
|
|
22
|
+
* WHY THERE IS A CONFIGURED ROOT
|
|
23
|
+
*
|
|
24
|
+
* Resources are addressed by a fixed URI with no arguments, so `ios://project/…`
|
|
25
|
+
* only means something if the server knows which project it is. MCP clients
|
|
26
|
+
* already configure servers per project — `.cursor/mcp.json` lives in the
|
|
27
|
+
* repository, `claude mcp add` is run from it — so a root passed at launch fits
|
|
28
|
+
* how the server is actually deployed.
|
|
29
|
+
*
|
|
30
|
+
* WHY THERE IS NO ios://project/build-status
|
|
31
|
+
*
|
|
32
|
+
* It would have to run `xcodebuild`. That needs macOS and Xcode, and it breaks
|
|
33
|
+
* the `filesystem: read, network: none` contract that lets this package install
|
|
34
|
+
* anywhere in ~26 KB. Build and simulator state belong in the separate package
|
|
35
|
+
* that already requires a full toolchain — see ROADMAP.md.
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* The directory `ios-agent` keeps its internal files in.
|
|
39
|
+
*
|
|
40
|
+
* Read-only here: this server never creates it. Its presence is used purely as
|
|
41
|
+
* a project-root marker, the same way git treats `.git` — which is what lets
|
|
42
|
+
* the CLI and this server agree on a root without either configuring the other.
|
|
43
|
+
*/
|
|
44
|
+
export const INTERNAL_DIR = ".ios-agent";
|
|
45
|
+
/** How far up the tree to look. Guards against a symlink loop. */
|
|
46
|
+
const MAX_ASCENT = 64;
|
|
47
|
+
/**
|
|
48
|
+
* Walk up from `start` looking for a directory containing `.ios-agent/`.
|
|
49
|
+
*
|
|
50
|
+
* Returns `undefined` rather than a fallback, so the caller decides what an
|
|
51
|
+
* absent marker means. Guessing here is how a server ends up analyzing a user's
|
|
52
|
+
* home directory because it was launched from the wrong place.
|
|
53
|
+
*/
|
|
54
|
+
export function findProjectRootUpwards(start) {
|
|
55
|
+
let current = resolve(start);
|
|
56
|
+
for (let step = 0; step < MAX_ASCENT; step += 1) {
|
|
57
|
+
try {
|
|
58
|
+
if (statSync(join(current, INTERNAL_DIR)).isDirectory())
|
|
59
|
+
return realpathSync(current);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
// Not here; keep walking.
|
|
63
|
+
}
|
|
64
|
+
const parent = dirname(current);
|
|
65
|
+
if (parent === current)
|
|
66
|
+
return undefined;
|
|
67
|
+
current = parent;
|
|
68
|
+
}
|
|
69
|
+
return undefined;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Resolve the project root, and say how.
|
|
73
|
+
*
|
|
74
|
+
* Order: `--project`, then `IOS_AGENT_PROJECT`, then the nearest ancestor with
|
|
75
|
+
* a `.ios-agent/` marker, then cwd. The marker step is what makes the server
|
|
76
|
+
* work when a client spawns it from a nested directory — previously that
|
|
77
|
+
* silently analyzed whatever subtree it happened to land in.
|
|
78
|
+
*
|
|
79
|
+
* The `source` travels with the root because an implicit root is unfalsifiable:
|
|
80
|
+
* an empty result looks identical whether the project has no Swift or the
|
|
81
|
+
* server is pointed somewhere else entirely.
|
|
82
|
+
*/
|
|
83
|
+
export function resolveRootFrom(argv, env) {
|
|
84
|
+
const flagIndex = argv.indexOf("--project");
|
|
85
|
+
if (flagIndex !== -1 && argv[flagIndex + 1]) {
|
|
86
|
+
return { root: resolve(argv[flagIndex + 1]), source: "flag" };
|
|
87
|
+
}
|
|
88
|
+
if (env.IOS_AGENT_PROJECT) {
|
|
89
|
+
return { root: resolve(env.IOS_AGENT_PROJECT), source: "environment" };
|
|
90
|
+
}
|
|
91
|
+
const cwd = resolve(env.PWD ?? process.cwd());
|
|
92
|
+
const marker = findProjectRootUpwards(cwd);
|
|
93
|
+
if (marker)
|
|
94
|
+
return { root: marker, source: "marker" };
|
|
95
|
+
return { root: cwd, source: "cwd" };
|
|
96
|
+
}
|
|
97
|
+
/** Resolve the project root from an explicit flag, the environment, a marker, or cwd. */
|
|
98
|
+
export function projectRootFrom(argv, env) {
|
|
99
|
+
return resolveRootFrom(argv, env).root;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Every resource states the root it used.
|
|
103
|
+
*
|
|
104
|
+
* The root is implicit — it comes from a flag, the environment, or the working
|
|
105
|
+
* directory the client happened to spawn the server in. A reader who cannot see
|
|
106
|
+
* which of those won has no way to tell an empty project from a wrong path.
|
|
107
|
+
*/
|
|
108
|
+
function payload(uri, body) {
|
|
109
|
+
return {
|
|
110
|
+
uri,
|
|
111
|
+
mimeType: "application/json",
|
|
112
|
+
text: `${JSON.stringify(body, null, 2)}\n`,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
function unavailable(uri, root, reason) {
|
|
116
|
+
return payload(uri, {
|
|
117
|
+
project_root: root,
|
|
118
|
+
available: false,
|
|
119
|
+
reason,
|
|
120
|
+
fix: "Launch the server with `--project /path/to/project`, set IOS_AGENT_PROJECT, or run it from a directory under one holding a `.ios-agent/` marker. MCP clients usually set this in the per-project config file.",
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
/** Scan once; every resource in a read is served from the same snapshot. */
|
|
124
|
+
async function snapshot(root) {
|
|
125
|
+
const resolved = await resolveProjectRoot(root);
|
|
126
|
+
const [files, context] = await Promise.all([
|
|
127
|
+
readSwiftFiles(resolved),
|
|
128
|
+
readProjectContext(resolved),
|
|
129
|
+
]);
|
|
130
|
+
return { resolved, files, context };
|
|
131
|
+
}
|
|
132
|
+
export async function projectInfoResource(uri, root, source) {
|
|
133
|
+
try {
|
|
134
|
+
const { resolved, files } = await snapshot(root);
|
|
135
|
+
if (files.length === 0) {
|
|
136
|
+
return unavailable(uri, resolved, "No Swift files found under this path.");
|
|
137
|
+
}
|
|
138
|
+
const summary = await summarizeProject(resolved, files);
|
|
139
|
+
return payload(uri, {
|
|
140
|
+
project_root: resolved,
|
|
141
|
+
resolved_from: source ?? "cwd",
|
|
142
|
+
available: true,
|
|
143
|
+
swift_files: summary.swiftFileCount,
|
|
144
|
+
lines: summary.lineCount,
|
|
145
|
+
deployment_target: summary.deploymentTarget,
|
|
146
|
+
swift_tools_version: summary.swiftToolsVersion,
|
|
147
|
+
ui_framework: summary.uiFramework,
|
|
148
|
+
architecture: summary.architecture,
|
|
149
|
+
// The evidence ships with the verdict. "MVVM" alone is a guess presented
|
|
150
|
+
// as a fact; the reader must be able to check it.
|
|
151
|
+
architecture_evidence: summary.architectureEvidence,
|
|
152
|
+
uses_dependency_injection: summary.usesDependencyInjection,
|
|
153
|
+
has_tests: summary.hasTests,
|
|
154
|
+
has_package_swift: summary.hasPackageSwift,
|
|
155
|
+
has_xcode_project: summary.hasXcodeProject,
|
|
156
|
+
frameworks: summary.frameworks,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
catch (error) {
|
|
160
|
+
return unavailable(uri, root, error instanceof Error ? error.message : String(error));
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
export async function projectDependenciesResource(uri, root) {
|
|
164
|
+
try {
|
|
165
|
+
const { resolved, files } = await snapshot(root);
|
|
166
|
+
const summary = await summarizeProject(resolved, files);
|
|
167
|
+
return payload(uri, {
|
|
168
|
+
project_root: resolved,
|
|
169
|
+
available: true,
|
|
170
|
+
third_party: summary.dependencies,
|
|
171
|
+
// Apple frameworks are not dependencies you manage, but they are the
|
|
172
|
+
// best available signal for what the app actually does.
|
|
173
|
+
apple_frameworks: summary.frameworks,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
return unavailable(uri, root, error instanceof Error ? error.message : String(error));
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
export async function projectIssuesResource(uri, root) {
|
|
181
|
+
try {
|
|
182
|
+
const { resolved, files, context } = await snapshot(root);
|
|
183
|
+
if (files.length === 0) {
|
|
184
|
+
return unavailable(uri, resolved, "No Swift files found under this path.");
|
|
185
|
+
}
|
|
186
|
+
const categories = {
|
|
187
|
+
concurrency: files.flatMap(analyzeConcurrency),
|
|
188
|
+
architecture: files.flatMap(analyzeArchitecture),
|
|
189
|
+
swiftui: files.flatMap(analyzeSwiftUI),
|
|
190
|
+
availability: files.flatMap(analyzeAvailability),
|
|
191
|
+
memory: files.flatMap(analyzeMemory),
|
|
192
|
+
security: files.flatMap(analyzeSecurity),
|
|
193
|
+
performance: files.flatMap(analyzePerformance),
|
|
194
|
+
testing: [...analyzeTestCoverage(files), ...files.flatMap(analyzeTesting)],
|
|
195
|
+
app_store: [
|
|
196
|
+
...analyzeProjectLevelAppStore(context),
|
|
197
|
+
...files.flatMap((file) => analyzeAppStore(file, context)),
|
|
198
|
+
],
|
|
199
|
+
};
|
|
200
|
+
const all = sortFindings(Object.values(categories).flat());
|
|
201
|
+
return payload(uri, {
|
|
202
|
+
project_root: resolved,
|
|
203
|
+
available: true,
|
|
204
|
+
files_checked: files.length,
|
|
205
|
+
counts: {
|
|
206
|
+
blocker: all.filter((f) => f.severity === "blocker").length,
|
|
207
|
+
serious: all.filter((f) => f.severity === "serious").length,
|
|
208
|
+
minor: all.filter((f) => f.severity === "minor").length,
|
|
209
|
+
total: all.length,
|
|
210
|
+
},
|
|
211
|
+
by_category: Object.fromEntries(Object.entries(categories).map(([name, found]) => [name, found.length])),
|
|
212
|
+
// Capped: a resource is attached to context wholesale, so an unbounded
|
|
213
|
+
// list would crowd out the conversation it is meant to inform.
|
|
214
|
+
issues: all.slice(0, 100),
|
|
215
|
+
truncated: all.length > 100 ? all.length - 100 : 0,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
return unavailable(uri, root, error instanceof Error ? error.message : String(error));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=resources.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resources.js","sourceRoot":"","sources":["../src/resources.ts"],"names":[],"mappings":"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"}
|
package/dist/result.d.ts
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { Finding } from "./analyzers/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* A 0–100 defect-density score.
|
|
5
|
+
*
|
|
6
|
+
* penalty = 10·blockers + 3·serious + 1·minor
|
|
7
|
+
* capacity = filesChecked × 10
|
|
8
|
+
* score = clamp(0, 100, round(100 × (1 − penalty / capacity)))
|
|
9
|
+
*
|
|
10
|
+
* 100 means no findings. The denominator is file count, so the score is a
|
|
11
|
+
* *density*: adding clean files raises it, which is the intended behaviour for
|
|
12
|
+
* tracking one project over time.
|
|
13
|
+
*
|
|
14
|
+
* It is deliberately NOT comparable between projects — a UI-heavy target and a
|
|
15
|
+
* networking library have different rule surfaces. Use it as a direction of
|
|
16
|
+
* travel, and use the counts for anything that matters.
|
|
17
|
+
*/
|
|
18
|
+
export declare function scoreFor(findings: Finding[], filesChecked: number): number;
|
|
19
|
+
export declare const issueSchema: z.ZodObject<{
|
|
20
|
+
file: z.ZodString;
|
|
21
|
+
line: z.ZodNumber;
|
|
22
|
+
severity: z.ZodEnum<["blocker", "serious", "minor"]>;
|
|
23
|
+
rule: z.ZodString;
|
|
24
|
+
message: z.ZodString;
|
|
25
|
+
consequence: z.ZodString;
|
|
26
|
+
fix: z.ZodString;
|
|
27
|
+
doc: z.ZodString;
|
|
28
|
+
excerpt: z.ZodString;
|
|
29
|
+
}, "strip", z.ZodTypeAny, {
|
|
30
|
+
severity: "blocker" | "serious" | "minor";
|
|
31
|
+
file: string;
|
|
32
|
+
line: number;
|
|
33
|
+
message: string;
|
|
34
|
+
rule: string;
|
|
35
|
+
consequence: string;
|
|
36
|
+
fix: string;
|
|
37
|
+
doc: string;
|
|
38
|
+
excerpt: string;
|
|
39
|
+
}, {
|
|
40
|
+
severity: "blocker" | "serious" | "minor";
|
|
41
|
+
file: string;
|
|
42
|
+
line: number;
|
|
43
|
+
message: string;
|
|
44
|
+
rule: string;
|
|
45
|
+
consequence: string;
|
|
46
|
+
fix: string;
|
|
47
|
+
doc: string;
|
|
48
|
+
excerpt: string;
|
|
49
|
+
}>;
|
|
50
|
+
/** The shape every review tool declares as its `outputSchema`. */
|
|
51
|
+
export declare const reviewOutputShape: {
|
|
52
|
+
summary: z.ZodString;
|
|
53
|
+
score: z.ZodNumber;
|
|
54
|
+
counts: z.ZodObject<{
|
|
55
|
+
blocker: z.ZodNumber;
|
|
56
|
+
serious: z.ZodNumber;
|
|
57
|
+
minor: z.ZodNumber;
|
|
58
|
+
total: z.ZodNumber;
|
|
59
|
+
}, "strip", z.ZodTypeAny, {
|
|
60
|
+
blocker: number;
|
|
61
|
+
serious: number;
|
|
62
|
+
minor: number;
|
|
63
|
+
total: number;
|
|
64
|
+
}, {
|
|
65
|
+
blocker: number;
|
|
66
|
+
serious: number;
|
|
67
|
+
minor: number;
|
|
68
|
+
total: number;
|
|
69
|
+
}>;
|
|
70
|
+
files_checked: z.ZodNumber;
|
|
71
|
+
issues: z.ZodArray<z.ZodObject<{
|
|
72
|
+
file: z.ZodString;
|
|
73
|
+
line: z.ZodNumber;
|
|
74
|
+
severity: z.ZodEnum<["blocker", "serious", "minor"]>;
|
|
75
|
+
rule: z.ZodString;
|
|
76
|
+
message: z.ZodString;
|
|
77
|
+
consequence: z.ZodString;
|
|
78
|
+
fix: z.ZodString;
|
|
79
|
+
doc: z.ZodString;
|
|
80
|
+
excerpt: z.ZodString;
|
|
81
|
+
}, "strip", z.ZodTypeAny, {
|
|
82
|
+
severity: "blocker" | "serious" | "minor";
|
|
83
|
+
file: string;
|
|
84
|
+
line: number;
|
|
85
|
+
message: string;
|
|
86
|
+
rule: string;
|
|
87
|
+
consequence: string;
|
|
88
|
+
fix: string;
|
|
89
|
+
doc: string;
|
|
90
|
+
excerpt: string;
|
|
91
|
+
}, {
|
|
92
|
+
severity: "blocker" | "serious" | "minor";
|
|
93
|
+
file: string;
|
|
94
|
+
line: number;
|
|
95
|
+
message: string;
|
|
96
|
+
rule: string;
|
|
97
|
+
consequence: string;
|
|
98
|
+
fix: string;
|
|
99
|
+
doc: string;
|
|
100
|
+
excerpt: string;
|
|
101
|
+
}>, "many">;
|
|
102
|
+
suggestions: z.ZodArray<z.ZodString, "many">;
|
|
103
|
+
};
|
|
104
|
+
export declare const reviewOutputSchema: z.ZodObject<{
|
|
105
|
+
summary: z.ZodString;
|
|
106
|
+
score: z.ZodNumber;
|
|
107
|
+
counts: z.ZodObject<{
|
|
108
|
+
blocker: z.ZodNumber;
|
|
109
|
+
serious: z.ZodNumber;
|
|
110
|
+
minor: z.ZodNumber;
|
|
111
|
+
total: z.ZodNumber;
|
|
112
|
+
}, "strip", z.ZodTypeAny, {
|
|
113
|
+
blocker: number;
|
|
114
|
+
serious: number;
|
|
115
|
+
minor: number;
|
|
116
|
+
total: number;
|
|
117
|
+
}, {
|
|
118
|
+
blocker: number;
|
|
119
|
+
serious: number;
|
|
120
|
+
minor: number;
|
|
121
|
+
total: number;
|
|
122
|
+
}>;
|
|
123
|
+
files_checked: z.ZodNumber;
|
|
124
|
+
issues: z.ZodArray<z.ZodObject<{
|
|
125
|
+
file: z.ZodString;
|
|
126
|
+
line: z.ZodNumber;
|
|
127
|
+
severity: z.ZodEnum<["blocker", "serious", "minor"]>;
|
|
128
|
+
rule: z.ZodString;
|
|
129
|
+
message: z.ZodString;
|
|
130
|
+
consequence: z.ZodString;
|
|
131
|
+
fix: z.ZodString;
|
|
132
|
+
doc: z.ZodString;
|
|
133
|
+
excerpt: z.ZodString;
|
|
134
|
+
}, "strip", z.ZodTypeAny, {
|
|
135
|
+
severity: "blocker" | "serious" | "minor";
|
|
136
|
+
file: string;
|
|
137
|
+
line: number;
|
|
138
|
+
message: string;
|
|
139
|
+
rule: string;
|
|
140
|
+
consequence: string;
|
|
141
|
+
fix: string;
|
|
142
|
+
doc: string;
|
|
143
|
+
excerpt: string;
|
|
144
|
+
}, {
|
|
145
|
+
severity: "blocker" | "serious" | "minor";
|
|
146
|
+
file: string;
|
|
147
|
+
line: number;
|
|
148
|
+
message: string;
|
|
149
|
+
rule: string;
|
|
150
|
+
consequence: string;
|
|
151
|
+
fix: string;
|
|
152
|
+
doc: string;
|
|
153
|
+
excerpt: string;
|
|
154
|
+
}>, "many">;
|
|
155
|
+
suggestions: z.ZodArray<z.ZodString, "many">;
|
|
156
|
+
}, "strip", z.ZodTypeAny, {
|
|
157
|
+
issues: {
|
|
158
|
+
severity: "blocker" | "serious" | "minor";
|
|
159
|
+
file: string;
|
|
160
|
+
line: number;
|
|
161
|
+
message: string;
|
|
162
|
+
rule: string;
|
|
163
|
+
consequence: string;
|
|
164
|
+
fix: string;
|
|
165
|
+
doc: string;
|
|
166
|
+
excerpt: string;
|
|
167
|
+
}[];
|
|
168
|
+
summary: string;
|
|
169
|
+
score: number;
|
|
170
|
+
counts: {
|
|
171
|
+
blocker: number;
|
|
172
|
+
serious: number;
|
|
173
|
+
minor: number;
|
|
174
|
+
total: number;
|
|
175
|
+
};
|
|
176
|
+
files_checked: number;
|
|
177
|
+
suggestions: string[];
|
|
178
|
+
}, {
|
|
179
|
+
issues: {
|
|
180
|
+
severity: "blocker" | "serious" | "minor";
|
|
181
|
+
file: string;
|
|
182
|
+
line: number;
|
|
183
|
+
message: string;
|
|
184
|
+
rule: string;
|
|
185
|
+
consequence: string;
|
|
186
|
+
fix: string;
|
|
187
|
+
doc: string;
|
|
188
|
+
excerpt: string;
|
|
189
|
+
}[];
|
|
190
|
+
summary: string;
|
|
191
|
+
score: number;
|
|
192
|
+
counts: {
|
|
193
|
+
blocker: number;
|
|
194
|
+
serious: number;
|
|
195
|
+
minor: number;
|
|
196
|
+
total: number;
|
|
197
|
+
};
|
|
198
|
+
files_checked: number;
|
|
199
|
+
suggestions: string[];
|
|
200
|
+
}>;
|
|
201
|
+
export type ReviewOutput = z.infer<typeof reviewOutputSchema>;
|
|
202
|
+
/** Build the structured half of a tool result. */
|
|
203
|
+
export declare function buildReviewOutput(title: string, findings: Finding[], filesChecked: number): ReviewOutput;
|
package/dist/result.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import { sortFindings } from "./analyzers/types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Structured tool output.
|
|
5
|
+
*
|
|
6
|
+
* Every review tool returns BOTH a markdown `content` block and this object as
|
|
7
|
+
* `structuredContent`. The markdown is for a human reading the transcript; the
|
|
8
|
+
* object is for a workflow that needs to branch on the result without parsing
|
|
9
|
+
* prose. Returning only JSON would make the transcript unreadable; returning
|
|
10
|
+
* only prose forces every consumer to regex it.
|
|
11
|
+
*
|
|
12
|
+
* Field names are snake_case because this is a wire contract, not internal API.
|
|
13
|
+
*/
|
|
14
|
+
/** Weights behind `score`. Stated here because a score nobody can reproduce is a vibe. */
|
|
15
|
+
const SEVERITY_WEIGHT = {
|
|
16
|
+
blocker: 10,
|
|
17
|
+
serious: 3,
|
|
18
|
+
minor: 1,
|
|
19
|
+
};
|
|
20
|
+
/** One blocker per file scanned is the notional floor. */
|
|
21
|
+
const CAPACITY_PER_FILE = 10;
|
|
22
|
+
/**
|
|
23
|
+
* A 0–100 defect-density score.
|
|
24
|
+
*
|
|
25
|
+
* penalty = 10·blockers + 3·serious + 1·minor
|
|
26
|
+
* capacity = filesChecked × 10
|
|
27
|
+
* score = clamp(0, 100, round(100 × (1 − penalty / capacity)))
|
|
28
|
+
*
|
|
29
|
+
* 100 means no findings. The denominator is file count, so the score is a
|
|
30
|
+
* *density*: adding clean files raises it, which is the intended behaviour for
|
|
31
|
+
* tracking one project over time.
|
|
32
|
+
*
|
|
33
|
+
* It is deliberately NOT comparable between projects — a UI-heavy target and a
|
|
34
|
+
* networking library have different rule surfaces. Use it as a direction of
|
|
35
|
+
* travel, and use the counts for anything that matters.
|
|
36
|
+
*/
|
|
37
|
+
export function scoreFor(findings, filesChecked) {
|
|
38
|
+
if (filesChecked <= 0)
|
|
39
|
+
return 100;
|
|
40
|
+
const penalty = findings.reduce((total, finding) => total + SEVERITY_WEIGHT[finding.severity], 0);
|
|
41
|
+
const capacity = filesChecked * CAPACITY_PER_FILE;
|
|
42
|
+
const score = Math.round(100 * (1 - penalty / capacity));
|
|
43
|
+
return Math.max(0, Math.min(100, score));
|
|
44
|
+
}
|
|
45
|
+
export const issueSchema = z.object({
|
|
46
|
+
file: z.string(),
|
|
47
|
+
line: z.number().int(),
|
|
48
|
+
severity: z.enum(["blocker", "serious", "minor"]),
|
|
49
|
+
rule: z.string(),
|
|
50
|
+
message: z.string(),
|
|
51
|
+
consequence: z.string(),
|
|
52
|
+
fix: z.string(),
|
|
53
|
+
doc: z.string(),
|
|
54
|
+
excerpt: z.string(),
|
|
55
|
+
});
|
|
56
|
+
/** The shape every review tool declares as its `outputSchema`. */
|
|
57
|
+
export const reviewOutputShape = {
|
|
58
|
+
summary: z.string().describe("One-line plain-language result."),
|
|
59
|
+
score: z
|
|
60
|
+
.number()
|
|
61
|
+
.int()
|
|
62
|
+
.describe("0-100 defect density. 100 = no findings. penalty = 10*blockers + 3*serious + 1*minor; capacity = files*10. Comparable across runs on ONE project, not between projects."),
|
|
63
|
+
counts: z.object({
|
|
64
|
+
blocker: z.number().int(),
|
|
65
|
+
serious: z.number().int(),
|
|
66
|
+
minor: z.number().int(),
|
|
67
|
+
total: z.number().int(),
|
|
68
|
+
}),
|
|
69
|
+
files_checked: z.number().int().describe("Swift files actually scanned."),
|
|
70
|
+
issues: z.array(issueSchema).describe("Every finding, most severe first."),
|
|
71
|
+
suggestions: z
|
|
72
|
+
.array(z.string())
|
|
73
|
+
.describe("Prioritized next actions, deduplicated by rule — not a restatement of every issue's fix."),
|
|
74
|
+
};
|
|
75
|
+
export const reviewOutputSchema = z.object(reviewOutputShape);
|
|
76
|
+
/**
|
|
77
|
+
* Collapse findings into a handful of actions.
|
|
78
|
+
*
|
|
79
|
+
* A `suggestions` array that simply repeats every issue's `fix` is noise — the
|
|
80
|
+
* issues already carry those. This groups by rule so a file with forty
|
|
81
|
+
* literal-spacing findings produces one instruction, ordered by total weight.
|
|
82
|
+
*/
|
|
83
|
+
function suggestionsFor(findings) {
|
|
84
|
+
if (findings.length === 0)
|
|
85
|
+
return [];
|
|
86
|
+
const byRule = new Map();
|
|
87
|
+
for (const finding of findings) {
|
|
88
|
+
const entry = byRule.get(finding.rule) ?? {
|
|
89
|
+
count: 0,
|
|
90
|
+
weight: 0,
|
|
91
|
+
fix: finding.fix,
|
|
92
|
+
};
|
|
93
|
+
entry.count += 1;
|
|
94
|
+
entry.weight += SEVERITY_WEIGHT[finding.severity];
|
|
95
|
+
byRule.set(finding.rule, entry);
|
|
96
|
+
}
|
|
97
|
+
return [...byRule.entries()]
|
|
98
|
+
.sort((a, b) => b[1].weight - a[1].weight)
|
|
99
|
+
.slice(0, 5)
|
|
100
|
+
.map(([rule, entry]) => entry.count === 1
|
|
101
|
+
? `${rule}: ${entry.fix}`
|
|
102
|
+
: `${rule} (${entry.count} occurrences): ${entry.fix}`);
|
|
103
|
+
}
|
|
104
|
+
function summarize(title, counts, files) {
|
|
105
|
+
if (counts.total === 0) {
|
|
106
|
+
return `${title}: no findings across ${files} file${files === 1 ? "" : "s"}.`;
|
|
107
|
+
}
|
|
108
|
+
const parts = [];
|
|
109
|
+
if (counts.blocker > 0)
|
|
110
|
+
parts.push(`${counts.blocker} blocker`);
|
|
111
|
+
if (counts.serious > 0)
|
|
112
|
+
parts.push(`${counts.serious} serious`);
|
|
113
|
+
if (counts.minor > 0)
|
|
114
|
+
parts.push(`${counts.minor} minor`);
|
|
115
|
+
return `${title}: ${parts.join(", ")} across ${files} file${files === 1 ? "" : "s"}.`;
|
|
116
|
+
}
|
|
117
|
+
/** Build the structured half of a tool result. */
|
|
118
|
+
export function buildReviewOutput(title, findings, filesChecked) {
|
|
119
|
+
const sorted = sortFindings(findings);
|
|
120
|
+
const counts = {
|
|
121
|
+
blocker: sorted.filter((f) => f.severity === "blocker").length,
|
|
122
|
+
serious: sorted.filter((f) => f.severity === "serious").length,
|
|
123
|
+
minor: sorted.filter((f) => f.severity === "minor").length,
|
|
124
|
+
total: sorted.length,
|
|
125
|
+
};
|
|
126
|
+
return {
|
|
127
|
+
summary: summarize(title, counts, filesChecked),
|
|
128
|
+
score: scoreFor(sorted, filesChecked),
|
|
129
|
+
counts,
|
|
130
|
+
files_checked: filesChecked,
|
|
131
|
+
issues: sorted,
|
|
132
|
+
suggestions: suggestionsFor(sorted),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
//# sourceMappingURL=result.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result.js","sourceRoot":"","sources":["../src/result.ts"],"names":[],"mappings":"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"}
|
package/dist/scan.d.ts
CHANGED
|
@@ -20,6 +20,15 @@ export interface ProjectSummary {
|
|
|
20
20
|
hasTests: boolean;
|
|
21
21
|
hasPackageSwift: boolean;
|
|
22
22
|
hasXcodeProject: boolean;
|
|
23
|
+
/** SwiftUI, UIKit, both, or neither — inferred from imports. */
|
|
24
|
+
uiFramework: "SwiftUI" | "UIKit" | "SwiftUI + UIKit" | "unknown";
|
|
25
|
+
/** Best-effort architecture read. Evidence is reported alongside it. */
|
|
26
|
+
architecture: string;
|
|
27
|
+
architectureEvidence: string[];
|
|
28
|
+
/** Third-party packages, from Package.swift / Podfile / project.pbxproj. */
|
|
29
|
+
dependencies: string[];
|
|
30
|
+
/** True when at least one dependency crosses an injected protocol boundary. */
|
|
31
|
+
usesDependencyInjection: boolean;
|
|
23
32
|
}
|
|
24
33
|
/** A structural overview of the project, independent of rule violations. */
|
|
25
34
|
export declare function summarizeProject(root: string, files: SourceFile[]): Promise<ProjectSummary>;
|