@vibekiln/cutline-mcp-cli 0.1.2 → 0.3.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/dist/commands/setup.d.ts +2 -0
- package/dist/commands/setup.js +70 -0
- package/dist/index.js +9 -1
- package/dist/servers/{chunk-WWTNBUIX.js → chunk-6Y3AEXE3.js} +29 -24
- package/dist/servers/{chunk-UBBAYTW3.js → chunk-IDSVMCGM.js} +4 -2
- package/dist/servers/cutline-server.js +193 -24
- package/dist/servers/{data-client-2YBU5KRO.js → data-client-PPEF2XUI.js} +1 -1
- package/dist/servers/exploration-server.js +1 -1
- package/dist/servers/{graph-metrics-DCNR7JZN.js → graph-metrics-KLHCMDFT.js} +1 -1
- package/dist/servers/integrations-server.js +1 -1
- package/dist/servers/output-server.js +1 -1
- package/dist/servers/premortem-server.js +1 -1
- package/dist/servers/tools-server.js +1 -1
- package/package.json +1 -1
package/dist/commands/setup.d.ts
CHANGED
package/dist/commands/setup.js
CHANGED
|
@@ -27,6 +27,7 @@ const SERVER_NAMES = [
|
|
|
27
27
|
'output',
|
|
28
28
|
'integrations',
|
|
29
29
|
];
|
|
30
|
+
const AUDIT_DIMENSIONS = ['engineering', 'security', 'reliability', 'scalability', 'compliance'];
|
|
30
31
|
async function detectTier(options) {
|
|
31
32
|
const refreshToken = await getRefreshToken();
|
|
32
33
|
if (!refreshToken)
|
|
@@ -83,6 +84,67 @@ function prompt(question) {
|
|
|
83
84
|
});
|
|
84
85
|
});
|
|
85
86
|
}
|
|
87
|
+
function parseDimensionCsv(csv) {
|
|
88
|
+
if (!csv)
|
|
89
|
+
return [];
|
|
90
|
+
return csv
|
|
91
|
+
.split(',')
|
|
92
|
+
.map((s) => s.trim())
|
|
93
|
+
.filter(Boolean);
|
|
94
|
+
}
|
|
95
|
+
function normalizeAuditDimensions(values) {
|
|
96
|
+
const allowed = new Set(AUDIT_DIMENSIONS);
|
|
97
|
+
const deduped = [...new Set(values.map((v) => v.trim().toLowerCase()).filter(Boolean))];
|
|
98
|
+
const valid = deduped.filter((v) => allowed.has(v));
|
|
99
|
+
const invalid = deduped.filter((v) => !allowed.has(v));
|
|
100
|
+
return { valid, invalid };
|
|
101
|
+
}
|
|
102
|
+
async function resolveHiddenAuditDimensions(options) {
|
|
103
|
+
const explicit = [
|
|
104
|
+
...(options.hideAuditDimension ?? []),
|
|
105
|
+
...parseDimensionCsv(options.hideAuditDimensions),
|
|
106
|
+
];
|
|
107
|
+
if (explicit.length > 0) {
|
|
108
|
+
const { valid, invalid } = normalizeAuditDimensions(explicit);
|
|
109
|
+
if (invalid.length > 0) {
|
|
110
|
+
console.log(chalk.yellow(` Ignoring invalid audit dimensions: ${invalid.join(', ')} ` +
|
|
111
|
+
`(allowed: ${AUDIT_DIMENSIONS.join(', ')})`));
|
|
112
|
+
}
|
|
113
|
+
return valid;
|
|
114
|
+
}
|
|
115
|
+
const answer = await prompt(chalk.cyan(` Hide any code audit dimensions? ` +
|
|
116
|
+
`(comma-separated: ${AUDIT_DIMENSIONS.join(', ')}; Enter for none): `));
|
|
117
|
+
if (!answer)
|
|
118
|
+
return [];
|
|
119
|
+
const { valid, invalid } = normalizeAuditDimensions(parseDimensionCsv(answer));
|
|
120
|
+
if (invalid.length > 0) {
|
|
121
|
+
console.log(chalk.yellow(` Ignoring invalid audit dimensions: ${invalid.join(', ')} ` +
|
|
122
|
+
`(allowed: ${AUDIT_DIMENSIONS.join(', ')})`));
|
|
123
|
+
}
|
|
124
|
+
return valid;
|
|
125
|
+
}
|
|
126
|
+
function writeAuditVisibilityConfig(hiddenDimensions) {
|
|
127
|
+
const configDir = join(homedir(), '.cutline-mcp');
|
|
128
|
+
const configPath = join(configDir, 'config.json');
|
|
129
|
+
let existing = {};
|
|
130
|
+
try {
|
|
131
|
+
if (existsSync(configPath)) {
|
|
132
|
+
existing = JSON.parse(readFileSync(configPath, 'utf-8'));
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
existing = {};
|
|
137
|
+
}
|
|
138
|
+
const next = {
|
|
139
|
+
...existing,
|
|
140
|
+
audit_visibility: {
|
|
141
|
+
...(existing.audit_visibility ?? {}),
|
|
142
|
+
hidden_dimensions: hiddenDimensions,
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
mkdirSync(configDir, { recursive: true });
|
|
146
|
+
writeFileSync(configPath, JSON.stringify(next, null, 2) + '\n');
|
|
147
|
+
}
|
|
86
148
|
function resolveServeRuntime() {
|
|
87
149
|
// Optional explicit override for advanced environments.
|
|
88
150
|
if (process.env.CUTLINE_MCP_BIN?.trim()) {
|
|
@@ -222,6 +284,14 @@ export async function setupCommand(options) {
|
|
|
222
284
|
}
|
|
223
285
|
catch { /* ignore parse errors */ }
|
|
224
286
|
}
|
|
287
|
+
const hiddenAuditDimensions = await resolveHiddenAuditDimensions(options);
|
|
288
|
+
writeAuditVisibilityConfig(hiddenAuditDimensions);
|
|
289
|
+
if (hiddenAuditDimensions.length > 0) {
|
|
290
|
+
console.log(chalk.dim(` Hidden audit dimensions: ${hiddenAuditDimensions.join(', ')}\n`));
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
console.log(chalk.dim(' Hidden audit dimensions: none\n'));
|
|
294
|
+
}
|
|
225
295
|
// ── 3. Write MCP server config to IDEs ───────────────────────────────────
|
|
226
296
|
const runtime = resolveServeRuntime();
|
|
227
297
|
const serverConfig = buildServerConfig(runtime);
|
package/dist/index.js
CHANGED
|
@@ -54,7 +54,15 @@ program
|
|
|
54
54
|
.option('--staging', 'Use staging environment')
|
|
55
55
|
.option('--skip-login', 'Skip authentication (use existing credentials)')
|
|
56
56
|
.option('--project-root <path>', 'Project root directory for IDE rules (default: cwd)')
|
|
57
|
-
.
|
|
57
|
+
.option('--hide-audit-dimension <name>', 'Hide one audit dimension in surfaced code audit output (repeatable)', (value, prev) => [...prev, value], [])
|
|
58
|
+
.option('--hide-audit-dimensions <csv>', 'Hide multiple audit dimensions (comma-separated: engineering,security,reliability,scalability,compliance)')
|
|
59
|
+
.action((opts) => setupCommand({
|
|
60
|
+
staging: opts.staging,
|
|
61
|
+
skipLogin: opts.skipLogin,
|
|
62
|
+
projectRoot: opts.projectRoot,
|
|
63
|
+
hideAuditDimension: opts.hideAuditDimension,
|
|
64
|
+
hideAuditDimensions: opts.hideAuditDimensions,
|
|
65
|
+
}));
|
|
58
66
|
program
|
|
59
67
|
.command('init')
|
|
60
68
|
.description('Generate IDE rules only (setup runs this automatically)')
|
|
@@ -287,6 +287,24 @@ async function exchangeRefreshToken(refreshToken, firebaseApiKey, maxRetries = 3
|
|
|
287
287
|
}
|
|
288
288
|
throw lastError || new Error("Token exchange failed after retries");
|
|
289
289
|
}
|
|
290
|
+
var AUDIT_DIMENSIONS = ["engineering", "security", "reliability", "scalability", "compliance"];
|
|
291
|
+
function readLocalCutlineConfig() {
|
|
292
|
+
try {
|
|
293
|
+
const configPath = path.join(os.homedir(), ".cutline-mcp", "config.json");
|
|
294
|
+
if (!fs.existsSync(configPath))
|
|
295
|
+
return null;
|
|
296
|
+
return JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
297
|
+
} catch {
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
function getHiddenAuditDimensions() {
|
|
302
|
+
const config = readLocalCutlineConfig();
|
|
303
|
+
const hidden = config?.audit_visibility?.hidden_dimensions ?? [];
|
|
304
|
+
const allowed = new Set(AUDIT_DIMENSIONS);
|
|
305
|
+
const normalized = [...new Set(hidden.map((d) => String(d).trim().toLowerCase()).filter((d) => allowed.has(d)))];
|
|
306
|
+
return normalized;
|
|
307
|
+
}
|
|
290
308
|
function getStoredApiKey() {
|
|
291
309
|
if (process.env.CUTLINE_API_KEY) {
|
|
292
310
|
return {
|
|
@@ -294,15 +312,9 @@ function getStoredApiKey() {
|
|
|
294
312
|
environment: process.env.CUTLINE_ENV === "staging" ? "staging" : "production"
|
|
295
313
|
};
|
|
296
314
|
}
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
|
|
301
|
-
if (config.apiKey) {
|
|
302
|
-
return { apiKey: config.apiKey, environment: config.environment };
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
} catch {
|
|
315
|
+
const config = readLocalCutlineConfig();
|
|
316
|
+
if (config?.apiKey) {
|
|
317
|
+
return { apiKey: config.apiKey, environment: config.environment };
|
|
306
318
|
}
|
|
307
319
|
return null;
|
|
308
320
|
}
|
|
@@ -314,21 +326,13 @@ async function getStoredToken() {
|
|
|
314
326
|
environment: process.env.CUTLINE_ENV === "staging" ? "staging" : "production"
|
|
315
327
|
};
|
|
316
328
|
}
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
return {
|
|
325
|
-
refreshToken: config.refreshToken,
|
|
326
|
-
environment: config.environment
|
|
327
|
-
};
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
} catch (e) {
|
|
331
|
-
console.error("[MCP Auth] Failed to read config file:", e);
|
|
329
|
+
const config = readLocalCutlineConfig();
|
|
330
|
+
if (config?.refreshToken) {
|
|
331
|
+
console.error("[MCP Auth] Using token from ~/.cutline-mcp/config.json", config.environment ? `(environment: ${config.environment})` : "");
|
|
332
|
+
return {
|
|
333
|
+
refreshToken: config.refreshToken,
|
|
334
|
+
environment: config.environment
|
|
335
|
+
};
|
|
332
336
|
}
|
|
333
337
|
return null;
|
|
334
338
|
}
|
|
@@ -991,6 +995,7 @@ export {
|
|
|
991
995
|
mapErrorToMcp,
|
|
992
996
|
validateAuth,
|
|
993
997
|
resolveAuthContext,
|
|
998
|
+
getHiddenAuditDimensions,
|
|
994
999
|
requirePremiumWithAutoAuth,
|
|
995
1000
|
resolveAuthContextFree,
|
|
996
1001
|
getPublicSiteUrlForCurrentAuth,
|
|
@@ -565,7 +565,8 @@ function computeGraphMetrics(entities, edges, constraints, bindings, conflicts,
|
|
|
565
565
|
gdpr: "GDPR/CCPA",
|
|
566
566
|
owasp: "OWASP LLM Top 10",
|
|
567
567
|
glba: "GLBA",
|
|
568
|
-
ferpa: "FERPA/COPPA"
|
|
568
|
+
ferpa: "FERPA/COPPA",
|
|
569
|
+
ios: "iOS App Store"
|
|
569
570
|
};
|
|
570
571
|
const detectedFrameworks = /* @__PURE__ */ new Set();
|
|
571
572
|
for (const c of constraints) {
|
|
@@ -879,7 +880,8 @@ function computeGenericGraphMetrics(entities, edges, constraints, bindings) {
|
|
|
879
880
|
gdpr: "GDPR/CCPA",
|
|
880
881
|
owasp: "OWASP LLM Top 10",
|
|
881
882
|
glba: "GLBA",
|
|
882
|
-
ferpa: "FERPA/COPPA"
|
|
883
|
+
ferpa: "FERPA/COPPA",
|
|
884
|
+
ios: "iOS App Store"
|
|
883
885
|
};
|
|
884
886
|
const detectedFrameworks = /* @__PURE__ */ new Set();
|
|
885
887
|
for (const c of constraints) {
|
|
@@ -43,6 +43,7 @@ import {
|
|
|
43
43
|
getAllNodesLight,
|
|
44
44
|
getEntitiesWithEmbeddings,
|
|
45
45
|
getGraphMetadata,
|
|
46
|
+
getHiddenAuditDimensions,
|
|
46
47
|
getIdeaReport,
|
|
47
48
|
getNodesByCategories,
|
|
48
49
|
getNodesMissingEmbeddings,
|
|
@@ -74,13 +75,13 @@ import {
|
|
|
74
75
|
upsertEntities,
|
|
75
76
|
upsertNodes,
|
|
76
77
|
validateRequestSize
|
|
77
|
-
} from "./chunk-
|
|
78
|
+
} from "./chunk-6Y3AEXE3.js";
|
|
78
79
|
import {
|
|
79
80
|
GraphTraverser,
|
|
80
81
|
computeGenericGraphMetrics,
|
|
81
82
|
computeMetricsFromGraph,
|
|
82
83
|
detectConstraintConflicts
|
|
83
|
-
} from "./chunk-
|
|
84
|
+
} from "./chunk-IDSVMCGM.js";
|
|
84
85
|
|
|
85
86
|
// ../mcp/dist/mcp/src/cutline-server.js
|
|
86
87
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -415,6 +416,21 @@ var PATH_PATTERNS = [
|
|
|
415
416
|
categories: ["compliance", "security"],
|
|
416
417
|
domain: "fedramp",
|
|
417
418
|
priority: "high"
|
|
419
|
+
},
|
|
420
|
+
{
|
|
421
|
+
patterns: [
|
|
422
|
+
/\/ios/i,
|
|
423
|
+
/\/swift/i,
|
|
424
|
+
/\/storekit/i,
|
|
425
|
+
/\/appstore/i,
|
|
426
|
+
/\/testflight/i,
|
|
427
|
+
/\.swift$/i,
|
|
428
|
+
/Info\.plist$/i
|
|
429
|
+
],
|
|
430
|
+
keywords: ["ios", "app-store", "storekit", "iap", "testflight", "swift", "mobile"],
|
|
431
|
+
categories: ["compliance", "security", "risk"],
|
|
432
|
+
domain: "ios_app_store",
|
|
433
|
+
priority: "high"
|
|
418
434
|
}
|
|
419
435
|
];
|
|
420
436
|
var CODE_PATTERNS = [
|
|
@@ -589,6 +605,20 @@ var CODE_PATTERNS = [
|
|
|
589
605
|
keywords: ["csa", "ccm", "cloud", "aws", "gcp", "azure", "cloud-security"],
|
|
590
606
|
categories: ["compliance", "security"],
|
|
591
607
|
domain: "csa_ccm"
|
|
608
|
+
},
|
|
609
|
+
{
|
|
610
|
+
patterns: [
|
|
611
|
+
/StoreKit/i,
|
|
612
|
+
/SKPaymentQueue/i,
|
|
613
|
+
/SKProductsRequest/i,
|
|
614
|
+
/ASAuthorizationAppleID/i,
|
|
615
|
+
/UIApplicationOpenSettingsURLString/i,
|
|
616
|
+
/ATTrackingManager/i,
|
|
617
|
+
/\.swift$/i
|
|
618
|
+
],
|
|
619
|
+
keywords: ["ios", "app-store", "storekit", "iap", "apple-signin", "tracking-consent"],
|
|
620
|
+
categories: ["compliance", "security"],
|
|
621
|
+
domain: "ios_app_store"
|
|
592
622
|
}
|
|
593
623
|
];
|
|
594
624
|
function analyzeFilePaths(paths) {
|
|
@@ -2977,7 +3007,8 @@ var FRAMEWORK_ID_PREFIXES = {
|
|
|
2977
3007
|
gdpr: "GDPR/CCPA",
|
|
2978
3008
|
owasp: "OWASP LLM Top 10",
|
|
2979
3009
|
glba: "GLBA",
|
|
2980
|
-
ferpa: "FERPA/COPPA"
|
|
3010
|
+
ferpa: "FERPA/COPPA",
|
|
3011
|
+
ios: "iOS App Store"
|
|
2981
3012
|
};
|
|
2982
3013
|
function resolveFramework(constraintId) {
|
|
2983
3014
|
if (!constraintId.startsWith("constraint:blueprint:"))
|
|
@@ -5405,6 +5436,53 @@ var BLUEPRINT_RULES = [
|
|
|
5405
5436
|
framework: "ferpa_coppa"
|
|
5406
5437
|
}
|
|
5407
5438
|
]
|
|
5439
|
+
},
|
|
5440
|
+
// ── iOS App Store Review Guidelines (mobile iOS apps) ──────────────────────
|
|
5441
|
+
{
|
|
5442
|
+
trigger: (eco, ctx) => {
|
|
5443
|
+
const iosSignals = /ios|swift|swiftui|xcode|uikit|storekit|appstore|testflight|cocoapods|xcframework/i;
|
|
5444
|
+
const hasLang = eco.languages.some((l) => /swift|objective-c|objc/i.test(l));
|
|
5445
|
+
const hasFramework = eco.frameworks.some((f) => iosSignals.test(f));
|
|
5446
|
+
const hasDep = eco.all_dependencies.some((d) => iosSignals.test(d));
|
|
5447
|
+
const descMatch = ctx?.productDescription ? /\b(ios|iphone|ipad|app\s*store|testflight|storekit|in-app purchase|apple sign in)\b/i.test(ctx.productDescription) : false;
|
|
5448
|
+
const entityMatch = ctx?.existingEntityNames?.some((n) => /\b(ios|mobile|iphone|ipad|app store|iap|storekit)\b/i.test(n)) ?? false;
|
|
5449
|
+
return hasLang || hasFramework || hasDep || descMatch || entityMatch;
|
|
5450
|
+
},
|
|
5451
|
+
constraints: [
|
|
5452
|
+
{
|
|
5453
|
+
id_suffix: "ios_app_store_privacy_disclosure",
|
|
5454
|
+
category: "compliance",
|
|
5455
|
+
summary: "[iOS App Store 5.1] Apps collecting user data MUST provide accurate privacy disclosures and clear in-app data handling flows.",
|
|
5456
|
+
keywords: ["ios", "app-store", "privacy", "disclosure", "tracking", "app-privacy"],
|
|
5457
|
+
severity: "critical",
|
|
5458
|
+
action: "Document data collection in App Privacy labels and align runtime behavior. Gate tracking behind explicit consent where required.",
|
|
5459
|
+
checklist_ref: "IOS-5.1",
|
|
5460
|
+
file_patterns: ["**/ios/**", "**/*.swift", "**/privacy*", "**/tracking/**", "**/analytics/**"],
|
|
5461
|
+
framework: "ios_app_store"
|
|
5462
|
+
},
|
|
5463
|
+
{
|
|
5464
|
+
id_suffix: "ios_app_store_iap_policy",
|
|
5465
|
+
category: "compliance",
|
|
5466
|
+
summary: "[iOS App Store 3.1] Digital goods/services sold in-app MUST use Apple's In-App Purchase flows where required.",
|
|
5467
|
+
keywords: ["ios", "app-store", "iap", "storekit", "payments", "digital-goods"],
|
|
5468
|
+
severity: "warning",
|
|
5469
|
+
action: "Use StoreKit for digital purchases in iOS app surfaces. Avoid bypass payment links that violate App Store rules.",
|
|
5470
|
+
checklist_ref: "IOS-3.1",
|
|
5471
|
+
file_patterns: ["**/ios/**", "**/*.swift", "**/billing/**", "**/payment/**", "**/storekit/**"],
|
|
5472
|
+
framework: "ios_app_store"
|
|
5473
|
+
},
|
|
5474
|
+
{
|
|
5475
|
+
id_suffix: "ios_app_store_account_deletion",
|
|
5476
|
+
category: "compliance",
|
|
5477
|
+
summary: "[iOS App Store 5.1.1(v)] If account creation exists, apps MUST provide in-app account deletion with data handling consistent with policy.",
|
|
5478
|
+
keywords: ["ios", "app-store", "account-deletion", "user-account", "privacy-rights"],
|
|
5479
|
+
severity: "warning",
|
|
5480
|
+
action: "Expose account deletion in-app for iOS users and ensure backend deletion flow is implemented and verifiable.",
|
|
5481
|
+
checklist_ref: "IOS-5.1.1",
|
|
5482
|
+
file_patterns: ["**/ios/**", "**/*.swift", "**/api/account*", "**/api/user*", "**/settings/**"],
|
|
5483
|
+
framework: "ios_app_store"
|
|
5484
|
+
}
|
|
5485
|
+
]
|
|
5408
5486
|
}
|
|
5409
5487
|
];
|
|
5410
5488
|
function buildBlueprintConstraints(ecosystem, context) {
|
|
@@ -5504,6 +5582,7 @@ Flag if the codebase contains signals that would require specific compliance fra
|
|
|
5504
5582
|
- **OWASP LLM**: OpenAI, Anthropic, LangChain, vector DBs (Pinecone, Weaviate), RAG pipelines, AI agents
|
|
5505
5583
|
- **GLBA**: Plaid, banking SDKs, KYC/AML, lending/mortgage/wealth management code
|
|
5506
5584
|
- **FERPA/COPPA**: EdTech integrations (Clever, Canvas), student/minor data, classroom/school references
|
|
5585
|
+
- **iOS App Store**: iOS/Swift codepaths, App Store/TestFlight distribution, StoreKit/IAP, mobile app privacy policies
|
|
5507
5586
|
|
|
5508
5587
|
For each detected signal, note:
|
|
5509
5588
|
- H1. Which framework(s) apply and why
|
|
@@ -5551,7 +5630,7 @@ Return a JSON object with exactly these fields:
|
|
|
5551
5630
|
- referenceClasses (string[]): Security frameworks or standards that apply (e.g., "OWASP Top 10 2021", "SOC 2 Type II").
|
|
5552
5631
|
- constraints (object?): Resource constraints \u2014 team, budget_usd, deadline_days, must_ship_scope.
|
|
5553
5632
|
- checklist_summary (object): Keys are checklist IDs (A1-A8, B1-B6, C1-C7, D1-D8, E1-E4, F1-F3, G-*, H1-H3, I1-I8, J1-J6, K1-K8), values are "pass"|"fail"|"warn"|"not_applicable". This forces systematic coverage.
|
|
5554
|
-
- compliance_signals (array of {framework: "pci_dss"|"hipaa"|"fedramp"|"gdpr_ccpa"|"owasp_llm"|"glba"|"ferpa_coppa"|"csa_ccm", signal: string, confidence: number}?): Detected compliance framework signals. Return [] if none.
|
|
5633
|
+
- compliance_signals (array of {framework: "pci_dss"|"hipaa"|"fedramp"|"gdpr_ccpa"|"owasp_llm"|"glba"|"ferpa_coppa"|"csa_ccm"|"ios_app_store", signal: string, confidence: number}?): Detected compliance framework signals. Return [] if none.
|
|
5555
5634
|
|
|
5556
5635
|
Be concrete and specific. Reference file paths and line numbers where possible. If a checklist item cannot be assessed from the provided files, mark it "not_applicable" and note why. Cover ALL sections A through K.`;
|
|
5557
5636
|
var FULL_SYSTEM_PROMPT = `You are a product analyst reviewing a codebase. Given file contents, an ecosystem fingerprint, and existing constraints, extract structured product context.
|
|
@@ -6156,13 +6235,15 @@ async function seedBlueprintConstraints(productId, ecosystem, deps, blueprintCon
|
|
|
6156
6235
|
const FRAMEWORK_LABELS = {
|
|
6157
6236
|
baseline: "Security Baseline",
|
|
6158
6237
|
soc2: "SOC 2",
|
|
6238
|
+
csa_ccm: "CSA Controls Matrix",
|
|
6159
6239
|
pci_dss: "PCI-DSS",
|
|
6160
6240
|
hipaa: "HIPAA",
|
|
6161
6241
|
fedramp: "FedRAMP",
|
|
6162
6242
|
gdpr_ccpa: "GDPR/CCPA",
|
|
6163
6243
|
owasp_llm: "OWASP LLM Top 10",
|
|
6164
6244
|
glba: "GLBA",
|
|
6165
|
-
ferpa_coppa: "FERPA/COPPA"
|
|
6245
|
+
ferpa_coppa: "FERPA/COPPA",
|
|
6246
|
+
ios_app_store: "iOS App Store"
|
|
6166
6247
|
};
|
|
6167
6248
|
const newEntities = [];
|
|
6168
6249
|
const newEdges = [];
|
|
@@ -6966,10 +7047,27 @@ function deltaStr(current, previous) {
|
|
|
6966
7047
|
return " (no change)";
|
|
6967
7048
|
return diff > 0 ? ` (**+${diff}** since last scan)` : ` (**${diff}** since last scan)`;
|
|
6968
7049
|
}
|
|
6969
|
-
function formatAuditOutput(result, reportId, publicSiteUrl = "https://thecutline.ai") {
|
|
7050
|
+
function formatAuditOutput(result, reportId, publicSiteUrl = "https://thecutline.ai", hiddenAuditDimensions = []) {
|
|
6970
7051
|
const m = result.metrics;
|
|
6971
7052
|
const p = result.previousMetrics;
|
|
6972
7053
|
const isRescan = !!p;
|
|
7054
|
+
const hiddenSet = new Set(hiddenAuditDimensions.map((d) => String(d).trim().toLowerCase()));
|
|
7055
|
+
const securityVisible = !hiddenSet.has("security");
|
|
7056
|
+
const inferFindingDimension = (category) => {
|
|
7057
|
+
const c = (category ?? "").toLowerCase();
|
|
7058
|
+
if (["reliability"].includes(c))
|
|
7059
|
+
return "reliability";
|
|
7060
|
+
if (["scalability", "performance"].includes(c))
|
|
7061
|
+
return "scalability";
|
|
7062
|
+
if (["compliance"].includes(c))
|
|
7063
|
+
return "compliance";
|
|
7064
|
+
if (["code_quality", "general"].includes(c))
|
|
7065
|
+
return "engineering";
|
|
7066
|
+
return "security";
|
|
7067
|
+
};
|
|
7068
|
+
const hasComplianceFrameworks = result.frameworksLoaded.length > 0;
|
|
7069
|
+
const complianceCurrent = hasComplianceFrameworks ? Math.round((m.nfr_coverage?.compliance ?? 0) * 100) : void 0;
|
|
7070
|
+
const compliancePrevious = hasComplianceFrameworks ? Math.round((p?.nfr_coverage?.compliance ?? 0) * 100) : void 0;
|
|
6973
7071
|
const lines = [
|
|
6974
7072
|
`# Cutline Code Audit`,
|
|
6975
7073
|
``,
|
|
@@ -6979,8 +7077,50 @@ function formatAuditOutput(result, reportId, publicSiteUrl = "https://thecutline
|
|
|
6979
7077
|
if (result.frameworksLoaded.length > 0) {
|
|
6980
7078
|
lines.push(`**Compliance frameworks:** ${result.frameworksLoaded.join(", ")}`);
|
|
6981
7079
|
}
|
|
6982
|
-
|
|
6983
|
-
|
|
7080
|
+
const scoreRows = [
|
|
7081
|
+
{
|
|
7082
|
+
key: "engineering",
|
|
7083
|
+
label: "Engineering",
|
|
7084
|
+
current: m.engineering_readiness_pct ?? 0,
|
|
7085
|
+
previous: p?.engineering_readiness_pct
|
|
7086
|
+
},
|
|
7087
|
+
{
|
|
7088
|
+
key: "security",
|
|
7089
|
+
label: "Security",
|
|
7090
|
+
current: m.security_readiness_pct ?? 0,
|
|
7091
|
+
previous: p?.security_readiness_pct
|
|
7092
|
+
},
|
|
7093
|
+
{
|
|
7094
|
+
key: "reliability",
|
|
7095
|
+
label: "Reliability",
|
|
7096
|
+
current: m.reliability_readiness_pct ?? 0,
|
|
7097
|
+
previous: p?.reliability_readiness_pct
|
|
7098
|
+
},
|
|
7099
|
+
{
|
|
7100
|
+
key: "scalability",
|
|
7101
|
+
label: "Scalability",
|
|
7102
|
+
current: m.scalability_readiness_pct ?? 0,
|
|
7103
|
+
previous: p?.scalability_readiness_pct
|
|
7104
|
+
},
|
|
7105
|
+
{
|
|
7106
|
+
key: "compliance",
|
|
7107
|
+
label: "Compliance",
|
|
7108
|
+
current: complianceCurrent,
|
|
7109
|
+
previous: compliancePrevious,
|
|
7110
|
+
na: !hasComplianceFrameworks
|
|
7111
|
+
}
|
|
7112
|
+
].filter((row) => !hiddenSet.has(row.key));
|
|
7113
|
+
lines.push(``, `## Readiness Scores`, ``, `| Pillar | Score |${isRescan ? " Change |" : ""}`, `|--------|-------|${isRescan ? "--------|" : ""}`);
|
|
7114
|
+
if (scoreRows.length > 0) {
|
|
7115
|
+
for (const row of scoreRows) {
|
|
7116
|
+
lines.push(`| ${row.label} | ${row.na ? "N/A" : `${row.current ?? 0}%`} |${isRescan ? row.na ? " (n/a) |" : deltaStr(row.current, row.previous) + " |" : ""}`);
|
|
7117
|
+
}
|
|
7118
|
+
lines.push(``);
|
|
7119
|
+
} else {
|
|
7120
|
+
lines.push(`| _Hidden by local audit visibility settings_ | - |${isRescan ? " - |" : ""}`, ``);
|
|
7121
|
+
}
|
|
7122
|
+
lines.push(`**Constraints mapped:** ${m.complexity_factors?.nfr_count ?? 0} (${m.complexity_factors?.critical_nfr_count ?? 0} critical)`, `**Binding coverage:** ${result.bindingHealth.coverage_pct}%`);
|
|
7123
|
+
if (securityVisible && result.scaFindings && result.scaFindings.total > 0) {
|
|
6984
7124
|
const sca = result.scaFindings;
|
|
6985
7125
|
lines.push(``, `## Dependency Vulnerabilities (SCA)`, ``, `**${sca.total} known vulnerabilities** found (${sca.critical} critical, ${sca.high} high)`, ``);
|
|
6986
7126
|
for (const v of sca.top) {
|
|
@@ -6991,12 +7131,13 @@ function formatAuditOutput(result, reportId, publicSiteUrl = "https://thecutline
|
|
|
6991
7131
|
lines.push(`- ...and ${sca.total - sca.top.length} more`);
|
|
6992
7132
|
}
|
|
6993
7133
|
}
|
|
6994
|
-
const
|
|
6995
|
-
const
|
|
7134
|
+
const visibleFindings = result.gatedGapDetails.filter((g) => !hiddenSet.has(inferFindingDimension(g.category)));
|
|
7135
|
+
const totalFindings = visibleFindings.length;
|
|
7136
|
+
const criticalCount = visibleFindings.filter((g) => g.severity === "critical" || g.severity === "high").length;
|
|
6996
7137
|
if (totalFindings > 0) {
|
|
6997
|
-
const topFinding =
|
|
7138
|
+
const topFinding = visibleFindings[0];
|
|
6998
7139
|
lines.push(``, `## #1 Finding \u2014 Fix This Now`, ``, `**[${topFinding.severity.toUpperCase()}] ${topFinding.title}**`, `*Category: ${topFinding.category}*`, ``, topFinding.description || "Address this finding to improve your readiness scores.", ``, `> Fix this issue, then re-run \`code_audit\` to see your scores improve.`);
|
|
6999
|
-
const remaining =
|
|
7140
|
+
const remaining = visibleFindings.slice(1);
|
|
7000
7141
|
if (remaining.length > 0) {
|
|
7001
7142
|
lines.push(``, `## ${remaining.length} More Finding${remaining.length > 1 ? "s" : ""} Detected`);
|
|
7002
7143
|
const teaserLimit = Math.min(remaining.length, 5);
|
|
@@ -8222,6 +8363,20 @@ Why AI: ${idea.whyAI}`
|
|
|
8222
8363
|
}
|
|
8223
8364
|
const freeAuth = await resolveAuthContextFree(scanArgs.auth_token);
|
|
8224
8365
|
const uid = freeAuth.effectiveUid;
|
|
8366
|
+
const hiddenAuditDimensions = getHiddenAuditDimensions();
|
|
8367
|
+
const hiddenSet = new Set(hiddenAuditDimensions);
|
|
8368
|
+
const inferFindingDimension = (category) => {
|
|
8369
|
+
const c = (category ?? "").toLowerCase();
|
|
8370
|
+
if (["reliability"].includes(c))
|
|
8371
|
+
return "reliability";
|
|
8372
|
+
if (["scalability", "performance"].includes(c))
|
|
8373
|
+
return "scalability";
|
|
8374
|
+
if (["compliance"].includes(c))
|
|
8375
|
+
return "compliance";
|
|
8376
|
+
if (["code_quality", "general"].includes(c))
|
|
8377
|
+
return "engineering";
|
|
8378
|
+
return "security";
|
|
8379
|
+
};
|
|
8225
8380
|
{
|
|
8226
8381
|
const rateInfo = await getScanRateLimit();
|
|
8227
8382
|
const now = /* @__PURE__ */ new Date();
|
|
@@ -8258,24 +8413,36 @@ Why AI: ${idea.whyAI}`
|
|
|
8258
8413
|
let reportId;
|
|
8259
8414
|
let reportSiteUrl = "https://thecutline.ai";
|
|
8260
8415
|
try {
|
|
8416
|
+
const reportMetrics = {};
|
|
8417
|
+
if (!hiddenSet.has("engineering")) {
|
|
8418
|
+
reportMetrics.engineering_readiness_pct = result.metrics.engineering_readiness_pct ?? 0;
|
|
8419
|
+
}
|
|
8420
|
+
if (!hiddenSet.has("security")) {
|
|
8421
|
+
reportMetrics.security_readiness_pct = result.metrics.security_readiness_pct ?? 0;
|
|
8422
|
+
}
|
|
8423
|
+
if (!hiddenSet.has("reliability")) {
|
|
8424
|
+
reportMetrics.reliability_readiness_pct = result.metrics.reliability_readiness_pct ?? 0;
|
|
8425
|
+
}
|
|
8426
|
+
if (!hiddenSet.has("scalability")) {
|
|
8427
|
+
reportMetrics.scalability_readiness_pct = result.metrics.scalability_readiness_pct ?? 0;
|
|
8428
|
+
}
|
|
8429
|
+
if (!hiddenSet.has("compliance")) {
|
|
8430
|
+
reportMetrics.compliance_readiness_pct = result.frameworksLoaded.length > 0 ? Math.round((result.metrics.nfr_coverage?.compliance ?? 0) * 100) : null;
|
|
8431
|
+
}
|
|
8432
|
+
const visibleFindings = result.gatedGapDetails.filter((f) => !hiddenSet.has(inferFindingDimension(f.category)));
|
|
8261
8433
|
const saved = await saveScanReport({
|
|
8262
|
-
metrics:
|
|
8263
|
-
engineering_readiness_pct: result.metrics.engineering_readiness_pct ?? 0,
|
|
8264
|
-
security_readiness_pct: result.metrics.security_readiness_pct ?? 0,
|
|
8265
|
-
reliability_readiness_pct: result.metrics.reliability_readiness_pct ?? 0,
|
|
8266
|
-
scalability_readiness_pct: result.metrics.scalability_readiness_pct ?? 0
|
|
8267
|
-
},
|
|
8434
|
+
metrics: reportMetrics,
|
|
8268
8435
|
ecosystem: result.ecosystem,
|
|
8269
8436
|
binding_coverage_pct: result.bindingHealth.coverage_pct,
|
|
8270
8437
|
frameworks_loaded: result.frameworksLoaded,
|
|
8271
8438
|
sensitive_data_count: result.sensitiveDataCount,
|
|
8272
|
-
findings:
|
|
8439
|
+
findings: visibleFindings.map((f) => ({
|
|
8273
8440
|
title: f.title,
|
|
8274
8441
|
severity: f.severity,
|
|
8275
8442
|
category: f.category,
|
|
8276
8443
|
description: f.description
|
|
8277
8444
|
})),
|
|
8278
|
-
sca_summary: result.scaFindings ? {
|
|
8445
|
+
sca_summary: !hiddenSet.has("security") && result.scaFindings ? {
|
|
8279
8446
|
total: result.scaFindings.total,
|
|
8280
8447
|
critical: result.scaFindings.critical,
|
|
8281
8448
|
high: result.scaFindings.high
|
|
@@ -8287,7 +8454,7 @@ Why AI: ${idea.whyAI}`
|
|
|
8287
8454
|
console.error("[code_audit] Report persistence failed (non-fatal):", e);
|
|
8288
8455
|
}
|
|
8289
8456
|
return {
|
|
8290
|
-
content: [{ type: "text", text: formatAuditOutput(result, reportId, reportSiteUrl) }]
|
|
8457
|
+
content: [{ type: "text", text: formatAuditOutput(result, reportId, reportSiteUrl, hiddenAuditDimensions) }]
|
|
8291
8458
|
};
|
|
8292
8459
|
}
|
|
8293
8460
|
const authCtx = await resolveAuthContext(args.auth_token);
|
|
@@ -8941,7 +9108,8 @@ Meta: ${JSON.stringify(output.meta)}` }
|
|
|
8941
9108
|
gdpr: "GDPR/CCPA",
|
|
8942
9109
|
owasp: "OWASP LLM Top 10",
|
|
8943
9110
|
glba: "GLBA",
|
|
8944
|
-
ferpa: "FERPA/COPPA"
|
|
9111
|
+
ferpa: "FERPA/COPPA",
|
|
9112
|
+
ios: "iOS App Store"
|
|
8945
9113
|
};
|
|
8946
9114
|
const formattedConstraints = topConstraints.map((c) => {
|
|
8947
9115
|
const framework = detectFramework2(c.id);
|
|
@@ -9931,7 +10099,7 @@ ${JSON.stringify(metrics, null, 2)}` }
|
|
|
9931
10099
|
getAllNodes(product_id),
|
|
9932
10100
|
getAllBindings(product_id)
|
|
9933
10101
|
]);
|
|
9934
|
-
const { computeMetricsFromGraph: computeMetricsFromGraph2 } = await import("./graph-metrics-
|
|
10102
|
+
const { computeMetricsFromGraph: computeMetricsFromGraph2 } = await import("./graph-metrics-KLHCMDFT.js");
|
|
9935
10103
|
const updatedMetrics = computeMetricsFromGraph2(rgrEntities, rgrEdges, rgrConstraints, rgrBindings, updatedPhases);
|
|
9936
10104
|
await updateGraphMetadata(product_id, {
|
|
9937
10105
|
...meta ?? {
|
|
@@ -10357,7 +10525,8 @@ Meta: ${JSON.stringify({
|
|
|
10357
10525
|
gdpr_ccpa: "GDPR/CCPA",
|
|
10358
10526
|
owasp_llm: "OWASP LLM Top 10",
|
|
10359
10527
|
glba: "GLBA",
|
|
10360
|
-
ferpa_coppa: "FERPA/COPPA"
|
|
10528
|
+
ferpa_coppa: "FERPA/COPPA",
|
|
10529
|
+
ios_app_store: "iOS App Store"
|
|
10361
10530
|
};
|
|
10362
10531
|
const names = result.frameworksLoaded.map((f) => fwLabels[f] || f);
|
|
10363
10532
|
sections.push(`- Compliance frameworks loaded: **${names.join(", ")}**`);
|
|
@@ -14,7 +14,7 @@ import {
|
|
|
14
14
|
requirePremiumWithAutoAuth,
|
|
15
15
|
updateExplorationSession,
|
|
16
16
|
validateRequestSize
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-6Y3AEXE3.js";
|
|
18
18
|
|
|
19
19
|
// ../mcp/dist/mcp/src/exploration-server.js
|
|
20
20
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
requirePremiumWithAutoAuth,
|
|
14
14
|
validateAuth,
|
|
15
15
|
validateRequestSize
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-6Y3AEXE3.js";
|
|
17
17
|
|
|
18
18
|
// ../mcp/dist/mcp/src/integrations-server.js
|
|
19
19
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
mapErrorToMcp,
|
|
14
14
|
requirePremiumWithAutoAuth,
|
|
15
15
|
validateRequestSize
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-6Y3AEXE3.js";
|
|
17
17
|
|
|
18
18
|
// ../mcp/dist/mcp/src/output-server.js
|
|
19
19
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
@@ -21,7 +21,7 @@ import {
|
|
|
21
21
|
requirePremiumWithAutoAuth,
|
|
22
22
|
validateAuth,
|
|
23
23
|
validateRequestSize
|
|
24
|
-
} from "./chunk-
|
|
24
|
+
} from "./chunk-6Y3AEXE3.js";
|
|
25
25
|
|
|
26
26
|
// ../mcp/dist/mcp/src/tools-server.js
|
|
27
27
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
package/package.json
CHANGED