@produtype/core 0.24.0 → 0.25.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.
|
@@ -1,2 +1,16 @@
|
|
|
1
1
|
import type { ProjectAnalysis } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* A path that is not there is not an empty repository.
|
|
4
|
+
*
|
|
5
|
+
* This check lived in the command-line tool alone, so every other caller — the MCP
|
|
6
|
+
* server an assistant drives, the hosted application, anyone using this as a library —
|
|
7
|
+
* got a report for a directory that does not exist: score 39, "no frontend, backend or
|
|
8
|
+
* database stack signals were detected", which reads as a verdict on a repository rather
|
|
9
|
+
* than a typo in a path. The command-line tool refused the same input outright.
|
|
10
|
+
*
|
|
11
|
+
* The guard belongs where the work starts, so every surface refuses it the same way.
|
|
12
|
+
*/
|
|
13
|
+
export declare class ProjectPathError extends Error {
|
|
14
|
+
constructor(message: string);
|
|
15
|
+
}
|
|
2
16
|
export declare function analyzeProject(projectPath: string): Promise<ProjectAnalysis>;
|
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.ProjectPathError = void 0;
|
|
36
37
|
exports.analyzeProject = analyzeProject;
|
|
37
38
|
const detectPackaging_1 = require("./detectPackaging");
|
|
38
39
|
const importedDependencies_1 = require("./importedDependencies");
|
|
@@ -63,6 +64,7 @@ const detectAiSafety_1 = require("./detectAiSafety");
|
|
|
63
64
|
const detectNotifications_1 = require("./detectNotifications");
|
|
64
65
|
const detectDeployment_1 = require("./detectDeployment");
|
|
65
66
|
const detectStack_1 = require("./detectStack");
|
|
67
|
+
const promises_1 = require("node:fs/promises");
|
|
66
68
|
const packageJsonSchema = zod_1.z
|
|
67
69
|
.object({
|
|
68
70
|
name: zod_1.z.string().optional(),
|
|
@@ -283,8 +285,39 @@ function resolveWorkspacePackageManager(workspace) {
|
|
|
283
285
|
return { manager: 'pip', confidence: 'manifest', warnings: [] };
|
|
284
286
|
return { manager: 'unknown', confidence: 'unknown', warnings: [] };
|
|
285
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* A path that is not there is not an empty repository.
|
|
290
|
+
*
|
|
291
|
+
* This check lived in the command-line tool alone, so every other caller — the MCP
|
|
292
|
+
* server an assistant drives, the hosted application, anyone using this as a library —
|
|
293
|
+
* got a report for a directory that does not exist: score 39, "no frontend, backend or
|
|
294
|
+
* database stack signals were detected", which reads as a verdict on a repository rather
|
|
295
|
+
* than a typo in a path. The command-line tool refused the same input outright.
|
|
296
|
+
*
|
|
297
|
+
* The guard belongs where the work starts, so every surface refuses it the same way.
|
|
298
|
+
*/
|
|
299
|
+
class ProjectPathError extends Error {
|
|
300
|
+
constructor(message) {
|
|
301
|
+
super(message);
|
|
302
|
+
this.name = 'ProjectPathError';
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
exports.ProjectPathError = ProjectPathError;
|
|
306
|
+
async function assertReadableDirectory(root) {
|
|
307
|
+
let stats;
|
|
308
|
+
try {
|
|
309
|
+
stats = await (0, promises_1.stat)(root);
|
|
310
|
+
}
|
|
311
|
+
catch {
|
|
312
|
+
throw new ProjectPathError(`Project path does not exist: ${root}`);
|
|
313
|
+
}
|
|
314
|
+
if (!stats.isDirectory()) {
|
|
315
|
+
throw new ProjectPathError(`Project path is not a directory: ${root}`);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
286
318
|
async function analyzeProject(projectPath) {
|
|
287
319
|
const root = path.resolve(projectPath);
|
|
320
|
+
await assertReadableDirectory(root);
|
|
288
321
|
const allFiles = await (0, fileScanner_1.scanFiles)({ cwd: root });
|
|
289
322
|
const sourceFiles = pickSource(allFiles);
|
|
290
323
|
const configFiles = pickConfig(allFiles);
|
package/dist/mcp/server.js
CHANGED
|
@@ -96,12 +96,40 @@ async function createProdkitMcpServer() {
|
|
|
96
96
|
categoryScores: report.categoryScores,
|
|
97
97
|
compliance: report.compliance,
|
|
98
98
|
detectedStack: report.detectedStack,
|
|
99
|
-
|
|
99
|
+
/**
|
|
100
|
+
* Every finding, not only the critical ones.
|
|
101
|
+
*
|
|
102
|
+
* This returned `criticalIssues` alone, and most repositories have none — so an
|
|
103
|
+
* assistant asking for a report was handed a score, a summary and an empty
|
|
104
|
+
* list, while the category table beside it showed six areas with work
|
|
105
|
+
* outstanding. The findings are what the report is; the rest describes them.
|
|
106
|
+
*/
|
|
107
|
+
findings: report.findings
|
|
108
|
+
.filter((finding) => finding.status !== 'passed' && finding.status !== 'unknown')
|
|
109
|
+
.map((finding) => ({
|
|
100
110
|
id: finding.id,
|
|
101
111
|
title: finding.title,
|
|
112
|
+
category: finding.category,
|
|
102
113
|
severity: finding.severity,
|
|
114
|
+
status: finding.status,
|
|
115
|
+
confidence: finding.confidence,
|
|
116
|
+
description: finding.description,
|
|
103
117
|
businessImpact: finding.businessImpact,
|
|
104
118
|
recommendation: finding.recommendation,
|
|
119
|
+
/**
|
|
120
|
+
* The lines behind the claim, which is the thing this product is for.
|
|
121
|
+
*
|
|
122
|
+
* Nothing on this surface could show one: an assistant could say "your
|
|
123
|
+
* secrets have a fallback" and not where. Trimmed to three, because a
|
|
124
|
+
* model reading twenty-five copies of the same import learns nothing the
|
|
125
|
+
* first one did not tell it.
|
|
126
|
+
*/
|
|
127
|
+
evidence: finding.evidence.slice(0, 3).map((item) => ({
|
|
128
|
+
type: item.type,
|
|
129
|
+
value: item.value,
|
|
130
|
+
...(item.file ? { file: item.file } : {}),
|
|
131
|
+
...(item.line ? { line: item.line } : {}),
|
|
132
|
+
})),
|
|
105
133
|
})),
|
|
106
134
|
gap: report.productProfile?.gap,
|
|
107
135
|
});
|
|
@@ -158,7 +186,18 @@ async function createProdkitMcpServer() {
|
|
|
158
186
|
overallScore: report.overallScore,
|
|
159
187
|
expectedCapabilityScore: report.expectedCapabilityScore,
|
|
160
188
|
launchReady: report.executiveSummary.launchReady,
|
|
189
|
+
/**
|
|
190
|
+
* What the verdict beside it counts.
|
|
191
|
+
*
|
|
192
|
+
* This reported `requiredMissing` alone, so a row read "1 of 3 required
|
|
193
|
+
* missing" next to a sentence saying three essential capabilities are
|
|
194
|
+
* missing or incomplete. A capability half-built is not a capability that is
|
|
195
|
+
* there, which is why the verdict adds the two — and why a caller shown only
|
|
196
|
+
* one of them cannot reconcile the row with the sentence in it.
|
|
197
|
+
*/
|
|
198
|
+
requiredBlocking: (report.productProfile?.gap.requiredMissing ?? 0) + (report.productProfile?.gap.requiredPartial ?? 0),
|
|
161
199
|
requiredMissing: report.productProfile?.gap.requiredMissing ?? 0,
|
|
200
|
+
requiredPartial: report.productProfile?.gap.requiredPartial ?? 0,
|
|
162
201
|
requiredTotal: report.productProfile?.gap.requiredTotal ?? 0,
|
|
163
202
|
verdict: report.executiveSummary.verdict,
|
|
164
203
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@produtype/core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.25.1",
|
|
4
4
|
"description": "Deterministic CLI and library that analyzes a web application repository and reports how far it is from production-ready for the kind of product it is meant to be.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|