midas-mcp 5.4.0 → 5.15.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/analyzer.d.ts +0 -10
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +1 -24
- package/dist/analyzer.js.map +1 -1
- package/dist/context.d.ts +6 -70
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +6 -425
- package/dist/context.js.map +1 -1
- package/dist/docs/prd.md +5 -0
- package/dist/reality.d.ts +64 -8
- package/dist/reality.d.ts.map +1 -1
- package/dist/reality.js +523 -67
- package/dist/reality.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +2 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/reality.d.ts +45 -4
- package/dist/tools/reality.d.ts.map +1 -1
- package/dist/tools/reality.js +71 -7
- package/dist/tools/reality.js.map +1 -1
- package/dist/tools/verify.d.ts +6 -0
- package/dist/tools/verify.d.ts.map +1 -1
- package/dist/tools/verify.js +21 -0
- package/dist/tools/verify.js.map +1 -1
- package/dist/tui.d.ts.map +1 -1
- package/dist/tui.js +146 -25
- package/dist/tui.js.map +1 -1
- package/docs/prd.md +5 -0
- package/package.json +1 -1
package/dist/reality.js
CHANGED
|
@@ -5,56 +5,204 @@
|
|
|
5
5
|
* categorizes them by what AI can/cannot do,
|
|
6
6
|
* and generates context-aware prompts for Cursor.
|
|
7
7
|
*/
|
|
8
|
-
import { existsSync, readFileSync } from 'fs';
|
|
8
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
9
9
|
import { join } from 'path';
|
|
10
10
|
import { sanitizePath } from './security.js';
|
|
11
11
|
// ============================================================================
|
|
12
|
-
//
|
|
12
|
+
// PERSISTENCE
|
|
13
13
|
// ============================================================================
|
|
14
|
+
const MIDAS_DIR = '.midas';
|
|
15
|
+
const REALITY_STATE_FILE = 'reality-checks.json';
|
|
16
|
+
function getRealityStatePath(projectPath) {
|
|
17
|
+
return join(projectPath, MIDAS_DIR, REALITY_STATE_FILE);
|
|
18
|
+
}
|
|
19
|
+
function loadRealityState(projectPath) {
|
|
20
|
+
const path = getRealityStatePath(projectPath);
|
|
21
|
+
if (existsSync(path)) {
|
|
22
|
+
try {
|
|
23
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return { checkStates: {}, lastProfileHash: '' };
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
return { checkStates: {}, lastProfileHash: '' };
|
|
30
|
+
}
|
|
31
|
+
function saveRealityState(projectPath, state) {
|
|
32
|
+
const dir = join(projectPath, MIDAS_DIR);
|
|
33
|
+
if (!existsSync(dir)) {
|
|
34
|
+
mkdirSync(dir, { recursive: true });
|
|
35
|
+
}
|
|
36
|
+
writeFileSync(getRealityStatePath(projectPath), JSON.stringify(state, null, 2));
|
|
37
|
+
}
|
|
38
|
+
function hashProfile(profile) {
|
|
39
|
+
// Simple hash to detect profile changes
|
|
40
|
+
return JSON.stringify(profile).split('').reduce((a, b) => {
|
|
41
|
+
a = ((a << 5) - a) + b.charCodeAt(0);
|
|
42
|
+
return a & a;
|
|
43
|
+
}, 0).toString(36);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Update the status of a reality check
|
|
47
|
+
*/
|
|
48
|
+
export function updateCheckStatus(projectPath, checkKey, status, skippedReason) {
|
|
49
|
+
const safePath = sanitizePath(projectPath);
|
|
50
|
+
const state = loadRealityState(safePath);
|
|
51
|
+
state.checkStates[checkKey] = {
|
|
52
|
+
status,
|
|
53
|
+
updatedAt: new Date().toISOString(),
|
|
54
|
+
...(skippedReason ? { skippedReason } : {}),
|
|
55
|
+
};
|
|
56
|
+
saveRealityState(safePath, state);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the persisted status for a check
|
|
60
|
+
*/
|
|
61
|
+
export function getCheckStatus(projectPath, checkKey) {
|
|
62
|
+
const safePath = sanitizePath(projectPath);
|
|
63
|
+
const state = loadRealityState(safePath);
|
|
64
|
+
return state.checkStates[checkKey];
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Get all check statuses
|
|
68
|
+
*/
|
|
69
|
+
export function getAllCheckStatuses(projectPath) {
|
|
70
|
+
const safePath = sanitizePath(projectPath);
|
|
71
|
+
const state = loadRealityState(safePath);
|
|
72
|
+
return state.checkStates;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Reset all check statuses (e.g., when profile changes significantly)
|
|
76
|
+
*/
|
|
77
|
+
export function resetCheckStatuses(projectPath) {
|
|
78
|
+
const safePath = sanitizePath(projectPath);
|
|
79
|
+
saveRealityState(safePath, { checkStates: {}, lastProfileHash: '' });
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Map of check keys to the expected generated file paths
|
|
83
|
+
*/
|
|
84
|
+
const EXPECTED_OUTPUTS = {
|
|
85
|
+
PRIVACY_POLICY: ['docs/privacy-policy.md', 'privacy-policy.md', 'PRIVACY.md'],
|
|
86
|
+
TERMS_OF_SERVICE: ['docs/terms-of-service.md', 'docs/terms.md', 'TERMS.md'],
|
|
87
|
+
COOKIE_POLICY: ['docs/cookie-policy.md'],
|
|
88
|
+
GDPR_COMPLIANCE: ['docs/gdpr-checklist.md', 'docs/gdpr.md'],
|
|
89
|
+
CCPA_COMPLIANCE: ['docs/ccpa-checklist.md'],
|
|
90
|
+
AI_DISCLOSURE: ['docs/ai-disclosure.md', 'AI_DISCLOSURE.md'],
|
|
91
|
+
ACCESSIBILITY: ['docs/accessibility.md', 'docs/a11y.md', 'ACCESSIBILITY.md'],
|
|
92
|
+
DATA_RETENTION: ['docs/data-retention.md'],
|
|
93
|
+
INCIDENT_RESPONSE: ['docs/incident-response.md'],
|
|
94
|
+
OSS_LICENSE: ['LICENSE', 'LICENSE.md', 'docs/license.md'],
|
|
95
|
+
HIPAA_COMPLIANCE: ['docs/hipaa-checklist.md'],
|
|
96
|
+
FERPA_COMPLIANCE: ['docs/ferpa-checklist.md'],
|
|
97
|
+
EU_AI_ACT: ['docs/eu-ai-act-assessment.md'],
|
|
98
|
+
SBOM: ['sbom.json', 'docs/sbom-readme.md'],
|
|
99
|
+
DATA_RESIDENCY: ['docs/data-residency.md'],
|
|
100
|
+
};
|
|
101
|
+
/**
|
|
102
|
+
* Detect if any expected output files exist and auto-complete checks
|
|
103
|
+
* Returns array of check keys that were auto-completed
|
|
104
|
+
*/
|
|
105
|
+
export function detectGeneratedDocs(projectPath) {
|
|
106
|
+
const safePath = sanitizePath(projectPath);
|
|
107
|
+
const state = loadRealityState(safePath);
|
|
108
|
+
const autoCompleted = [];
|
|
109
|
+
for (const [checkKey, possibleFiles] of Object.entries(EXPECTED_OUTPUTS)) {
|
|
110
|
+
// Skip if already completed
|
|
111
|
+
if (state.checkStates[checkKey]?.status === 'completed')
|
|
112
|
+
continue;
|
|
113
|
+
// Check if any of the expected files exist
|
|
114
|
+
const fileExists = possibleFiles.some(file => existsSync(join(safePath, file)));
|
|
115
|
+
if (fileExists) {
|
|
116
|
+
// Auto-complete this check
|
|
117
|
+
state.checkStates[checkKey] = {
|
|
118
|
+
status: 'completed',
|
|
119
|
+
updatedAt: new Date().toISOString(),
|
|
120
|
+
};
|
|
121
|
+
autoCompleted.push(checkKey);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (autoCompleted.length > 0) {
|
|
125
|
+
saveRealityState(safePath, state);
|
|
126
|
+
}
|
|
127
|
+
return autoCompleted;
|
|
128
|
+
}
|
|
129
|
+
// Legacy tier mapping for backward compatibility
|
|
130
|
+
const TIER_MAPPING = {
|
|
131
|
+
'generatable': 'ai_assisted',
|
|
132
|
+
'assistable': 'ai_assisted',
|
|
133
|
+
'human_only': 'manual',
|
|
134
|
+
'ai_assisted': 'ai_assisted',
|
|
135
|
+
'manual': 'manual',
|
|
136
|
+
};
|
|
137
|
+
// Default triggered-by generators based on common profile fields
|
|
138
|
+
const DEFAULT_TRIGGERS = {
|
|
139
|
+
PRIVACY_POLICY: (p) => p.collectsSensitiveData
|
|
140
|
+
? 'Project collects sensitive data (health/financial/biometric)'
|
|
141
|
+
: 'Project collects user data',
|
|
142
|
+
TERMS_OF_SERVICE: () => 'Public-facing product',
|
|
143
|
+
COOKIE_POLICY: (p) => p.targetsEU ? 'Targets EU users' : 'Collects user data',
|
|
144
|
+
GDPR_COMPLIANCE: (p) => p.targetsEU ? 'Explicitly targets EU users' : 'May have EU users',
|
|
145
|
+
CCPA_COMPLIANCE: () => 'Targets California or US users',
|
|
146
|
+
COPPA_COMPLIANCE: () => 'May have users under 13',
|
|
147
|
+
AI_DISCLOSURE: () => 'Uses AI for decisions or recommendations',
|
|
148
|
+
PAYMENT_SETUP: () => 'Has payment/subscription features',
|
|
149
|
+
STRIPE_INTEGRATION: () => 'Has payment processing',
|
|
150
|
+
APP_STORE: () => 'Distributes via iOS App Store',
|
|
151
|
+
PLAY_STORE: () => 'Distributes via Google Play Store',
|
|
152
|
+
ACCESSIBILITY: () => 'Public-facing product should be accessible',
|
|
153
|
+
DATA_RETENTION: (p) => p.collectsSensitiveData ? 'Handles sensitive data' : 'Collects user data',
|
|
154
|
+
INCIDENT_RESPONSE: () => 'Production system needs incident handling',
|
|
155
|
+
OSS_LICENSE: () => 'Open source project needs license',
|
|
156
|
+
HIPAA_COMPLIANCE: () => 'Healthcare industry + collects user data',
|
|
157
|
+
FERPA_COMPLIANCE: () => 'Education industry + collects student data',
|
|
158
|
+
EU_AI_ACT: (p) => p.targetsEU
|
|
159
|
+
? 'AI system targeting EU users'
|
|
160
|
+
: 'AI system in regulated industry (healthcare/education/finance)',
|
|
161
|
+
SBOM: () => 'Enterprise/finance audience expects supply chain transparency',
|
|
162
|
+
DATA_RESIDENCY: (p) => p.targetsEU
|
|
163
|
+
? 'EU users require data residency documentation (GDPR)'
|
|
164
|
+
: 'Enterprise customers require data location clarity',
|
|
165
|
+
};
|
|
14
166
|
const REALITY_CHECKS = {
|
|
15
167
|
// ✅ GENERATABLE - AI can draft these
|
|
16
168
|
PRIVACY_POLICY: {
|
|
17
169
|
key: 'PRIVACY_POLICY',
|
|
18
170
|
category: 'Legal',
|
|
19
|
-
tier: '
|
|
171
|
+
tier: 'ai_assisted',
|
|
20
172
|
headline: 'You need a Privacy Policy',
|
|
21
173
|
explanation: 'You collect user data. Users need to know what you collect, why, and how to delete it.',
|
|
22
174
|
priority: 'critical',
|
|
23
|
-
promptTemplate: `
|
|
175
|
+
promptTemplate: `Read docs/brainlift.md and docs/prd.md to understand this project. Then create a privacy policy.
|
|
24
176
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
177
|
+
First, identify from the docs:
|
|
178
|
+
- What user data is collected
|
|
179
|
+
- Why it's collected
|
|
180
|
+
- Who the target users are
|
|
181
|
+
- The business model
|
|
28
182
|
|
|
29
|
-
|
|
183
|
+
Then create docs/privacy-policy.md with sections:
|
|
30
184
|
- What we collect and why
|
|
31
185
|
- How we use the data
|
|
32
|
-
- Third parties we share with
|
|
33
|
-
-
|
|
186
|
+
- Third parties we share with
|
|
187
|
+
- Data retention
|
|
34
188
|
- User rights (access, correct, delete)
|
|
35
|
-
-
|
|
36
|
-
|
|
37
|
-
Save to docs/privacy-policy.md
|
|
189
|
+
- Contact information
|
|
38
190
|
|
|
39
|
-
Add at
|
|
191
|
+
Add at top: "DRAFT - Review with a lawyer before publishing"`,
|
|
40
192
|
condition: (p) => p.collectsUserData,
|
|
41
193
|
},
|
|
42
194
|
TERMS_OF_SERVICE: {
|
|
43
195
|
key: 'TERMS_OF_SERVICE',
|
|
44
196
|
category: 'Legal',
|
|
45
|
-
tier: '
|
|
197
|
+
tier: 'ai_assisted',
|
|
46
198
|
headline: 'You need Terms of Service',
|
|
47
199
|
explanation: 'Any public product needs terms defining the rules of use and liability limits.',
|
|
48
200
|
priority: 'high',
|
|
49
|
-
promptTemplate: `
|
|
50
|
-
|
|
51
|
-
Product type: {{productType}}
|
|
52
|
-
Business model: {{businessModel}}
|
|
53
|
-
Key features: {{keyFeatures}}
|
|
201
|
+
promptTemplate: `Read docs/brainlift.md and docs/prd.md to understand this project. Then create terms of service.
|
|
54
202
|
|
|
55
|
-
|
|
203
|
+
Create docs/terms-of-service.md with sections:
|
|
56
204
|
- Acceptance of terms
|
|
57
|
-
- Description of service
|
|
205
|
+
- Description of service (from what you read)
|
|
58
206
|
- User responsibilities
|
|
59
207
|
- Prohibited uses
|
|
60
208
|
- Intellectual property
|
|
@@ -70,7 +218,7 @@ Add at the top: "DRAFT - Review with a lawyer before publishing"`,
|
|
|
70
218
|
AI_DISCLOSURE: {
|
|
71
219
|
key: 'AI_DISCLOSURE',
|
|
72
220
|
category: 'Transparency',
|
|
73
|
-
tier: '
|
|
221
|
+
tier: 'ai_assisted',
|
|
74
222
|
headline: 'You need an AI disclosure',
|
|
75
223
|
explanation: 'Users should know when AI is involved and that it can make mistakes.',
|
|
76
224
|
priority: 'high',
|
|
@@ -92,7 +240,7 @@ Also add a brief inline disclosure component/text that can be shown in the UI wh
|
|
|
92
240
|
REFUND_POLICY: {
|
|
93
241
|
key: 'REFUND_POLICY',
|
|
94
242
|
category: 'Business',
|
|
95
|
-
tier: '
|
|
243
|
+
tier: 'ai_assisted',
|
|
96
244
|
headline: 'You need a refund policy',
|
|
97
245
|
explanation: 'Paid products need clear refund terms to avoid disputes and chargebacks.',
|
|
98
246
|
priority: 'high',
|
|
@@ -116,7 +264,7 @@ Save to docs/refund-policy.md`,
|
|
|
116
264
|
CONTENT_POLICY: {
|
|
117
265
|
key: 'CONTENT_POLICY',
|
|
118
266
|
category: 'Trust',
|
|
119
|
-
tier: '
|
|
267
|
+
tier: 'ai_assisted',
|
|
120
268
|
headline: 'You need a content policy',
|
|
121
269
|
explanation: 'User-generated content needs rules about what\'s allowed and how violations are handled.',
|
|
122
270
|
priority: 'high',
|
|
@@ -138,7 +286,7 @@ Save to docs/content-policy.md`,
|
|
|
138
286
|
GDPR_COMPLIANCE: {
|
|
139
287
|
key: 'GDPR_COMPLIANCE',
|
|
140
288
|
category: 'Compliance',
|
|
141
|
-
tier: '
|
|
289
|
+
tier: 'ai_assisted',
|
|
142
290
|
headline: 'GDPR applies to your product',
|
|
143
291
|
explanation: 'You\'re targeting EU users. You need lawful basis for data processing, user consent, and data rights.',
|
|
144
292
|
priority: 'critical',
|
|
@@ -169,7 +317,7 @@ Note at top: "This is a technical implementation guide. Legal review required be
|
|
|
169
317
|
CCPA_COMPLIANCE: {
|
|
170
318
|
key: 'CCPA_COMPLIANCE',
|
|
171
319
|
category: 'Compliance',
|
|
172
|
-
tier: '
|
|
320
|
+
tier: 'ai_assisted',
|
|
173
321
|
headline: 'CCPA applies to your product',
|
|
174
322
|
explanation: 'California users have rights to know, delete, and opt-out of data sales.',
|
|
175
323
|
priority: 'high',
|
|
@@ -192,7 +340,7 @@ Save to docs/ccpa-checklist.md`,
|
|
|
192
340
|
ACCESSIBILITY: {
|
|
193
341
|
key: 'ACCESSIBILITY',
|
|
194
342
|
category: 'Inclusion',
|
|
195
|
-
tier: '
|
|
343
|
+
tier: 'ai_assisted',
|
|
196
344
|
headline: 'Consider accessibility (WCAG)',
|
|
197
345
|
explanation: 'Making your product accessible helps more users and may be legally required for some customers.',
|
|
198
346
|
priority: 'medium',
|
|
@@ -214,7 +362,7 @@ Save checklist to docs/accessibility-checklist.md`,
|
|
|
214
362
|
BIAS_ASSESSMENT: {
|
|
215
363
|
key: 'BIAS_ASSESSMENT',
|
|
216
364
|
category: 'Ethics',
|
|
217
|
-
tier: '
|
|
365
|
+
tier: 'ai_assisted',
|
|
218
366
|
headline: 'AI bias assessment needed',
|
|
219
367
|
explanation: 'AI that makes decisions about people can have unintended bias. Document what you\'ve considered.',
|
|
220
368
|
priority: 'high',
|
|
@@ -241,7 +389,7 @@ This is for internal documentation and transparency.`,
|
|
|
241
389
|
PAYMENT_SETUP: {
|
|
242
390
|
key: 'PAYMENT_SETUP',
|
|
243
391
|
category: 'Business',
|
|
244
|
-
tier: '
|
|
392
|
+
tier: 'manual',
|
|
245
393
|
headline: 'You need payment processing',
|
|
246
394
|
explanation: 'You want to charge users. You need a payment provider account first.',
|
|
247
395
|
priority: 'critical',
|
|
@@ -271,7 +419,7 @@ I have my Stripe API keys ready. Use STRIPE_SECRET_KEY and STRIPE_PUBLISHABLE_KE
|
|
|
271
419
|
BUSINESS_REGISTRATION: {
|
|
272
420
|
key: 'BUSINESS_REGISTRATION',
|
|
273
421
|
category: 'Legal',
|
|
274
|
-
tier: '
|
|
422
|
+
tier: 'manual',
|
|
275
423
|
headline: 'Consider business registration',
|
|
276
424
|
explanation: 'If you\'re making money, you may need a business entity for taxes and liability protection.',
|
|
277
425
|
priority: 'medium',
|
|
@@ -293,7 +441,7 @@ Once you have your business set up, update your docs:
|
|
|
293
441
|
TAX_SETUP: {
|
|
294
442
|
key: 'TAX_SETUP',
|
|
295
443
|
category: 'Business',
|
|
296
|
-
tier: '
|
|
444
|
+
tier: 'manual',
|
|
297
445
|
headline: 'You need tax handling',
|
|
298
446
|
explanation: 'Selling internationally means dealing with VAT, GST, and sales tax. Stripe Tax can help.',
|
|
299
447
|
priority: 'high',
|
|
@@ -317,7 +465,7 @@ See Stripe Tax docs for jurisdiction-specific setup.`,
|
|
|
317
465
|
DOMAIN_SSL: {
|
|
318
466
|
key: 'DOMAIN_SSL',
|
|
319
467
|
category: 'Infrastructure',
|
|
320
|
-
tier: '
|
|
468
|
+
tier: 'manual',
|
|
321
469
|
headline: 'You need a domain and SSL',
|
|
322
470
|
explanation: 'To launch publicly, you need a domain name and HTTPS.',
|
|
323
471
|
priority: 'high',
|
|
@@ -339,7 +487,7 @@ Once you have your domain, update:
|
|
|
339
487
|
APP_STORE: {
|
|
340
488
|
key: 'APP_STORE',
|
|
341
489
|
category: 'Distribution',
|
|
342
|
-
tier: '
|
|
490
|
+
tier: 'manual',
|
|
343
491
|
headline: 'App Store submission needed',
|
|
344
492
|
explanation: 'Mobile apps need App Store / Play Store developer accounts and review.',
|
|
345
493
|
priority: 'critical',
|
|
@@ -364,7 +512,7 @@ Check that the app follows platform guidelines before submission.`,
|
|
|
364
512
|
COPPA_COMPLIANCE: {
|
|
365
513
|
key: 'COPPA_COMPLIANCE',
|
|
366
514
|
category: 'Compliance',
|
|
367
|
-
tier: '
|
|
515
|
+
tier: 'manual',
|
|
368
516
|
headline: 'COPPA compliance required',
|
|
369
517
|
explanation: 'Users under 13 require parental consent and special data handling.',
|
|
370
518
|
priority: 'critical',
|
|
@@ -391,7 +539,7 @@ This requires careful legal review - the FTC enforces COPPA strictly.`,
|
|
|
391
539
|
SOC2: {
|
|
392
540
|
key: 'SOC2',
|
|
393
541
|
category: 'Certification',
|
|
394
|
-
tier: '
|
|
542
|
+
tier: 'manual',
|
|
395
543
|
headline: 'Enterprise customers may require SOC 2',
|
|
396
544
|
explanation: 'B2B/enterprise sales often require SOC 2 certification to prove security practices.',
|
|
397
545
|
priority: 'medium',
|
|
@@ -414,6 +562,119 @@ However, you can prepare by implementing:
|
|
|
414
562
|
Create a security checklist: docs/security-checklist.md`,
|
|
415
563
|
condition: (p) => p.targetAudience.includes('enterprise'),
|
|
416
564
|
},
|
|
565
|
+
// ⚠️ ASSISTABLE - Industry-specific regulations
|
|
566
|
+
HIPAA_COMPLIANCE: {
|
|
567
|
+
key: 'HIPAA_COMPLIANCE',
|
|
568
|
+
category: 'Healthcare',
|
|
569
|
+
tier: 'ai_assisted',
|
|
570
|
+
headline: 'Healthcare data requires HIPAA compliance',
|
|
571
|
+
explanation: 'Handling patient health information in the US requires HIPAA compliance. Violations can cost $100-50K per record.',
|
|
572
|
+
priority: 'critical',
|
|
573
|
+
alsoNeeded: ['Legal review of BAA', 'Security audit', 'Employee HIPAA training'],
|
|
574
|
+
promptTemplate: `Read docs/brainlift.md and docs/prd.md. This is a healthcare application that may need HIPAA compliance.
|
|
575
|
+
|
|
576
|
+
Create docs/hipaa-checklist.md covering:
|
|
577
|
+
1. PHI (Protected Health Information) inventory - what health data do we handle?
|
|
578
|
+
2. Access controls - minimum necessary access
|
|
579
|
+
3. Audit logging - who accessed what PHI, when
|
|
580
|
+
4. Encryption - at rest and in transit
|
|
581
|
+
5. BAA requirements - list of vendors needing Business Associate Agreements
|
|
582
|
+
6. Incident response - breach notification within 60 days
|
|
583
|
+
|
|
584
|
+
Add warning: "This checklist requires review by a HIPAA compliance officer or healthcare attorney"`,
|
|
585
|
+
condition: (p) => p.industry.includes('healthcare') && p.collectsUserData,
|
|
586
|
+
},
|
|
587
|
+
FERPA_COMPLIANCE: {
|
|
588
|
+
key: 'FERPA_COMPLIANCE',
|
|
589
|
+
category: 'Education',
|
|
590
|
+
tier: 'ai_assisted',
|
|
591
|
+
headline: 'Education records require FERPA compliance',
|
|
592
|
+
explanation: 'Student education records in US schools are protected by FERPA. Violations can result in loss of federal funding.',
|
|
593
|
+
priority: 'critical',
|
|
594
|
+
alsoNeeded: ['School admin approval', 'Parent consent process', 'Annual notification'],
|
|
595
|
+
promptTemplate: `Read docs/brainlift.md and docs/prd.md. This is an education application that may need FERPA compliance.
|
|
596
|
+
|
|
597
|
+
Create docs/ferpa-checklist.md covering:
|
|
598
|
+
1. Education records inventory - what student records do we access/store?
|
|
599
|
+
2. Consent requirements - when do we need parent/student consent?
|
|
600
|
+
3. Directory information policy - what can be disclosed without consent?
|
|
601
|
+
4. Access controls - only authorized school officials
|
|
602
|
+
5. Record keeping - maintain log of disclosures
|
|
603
|
+
6. Annual notification - how schools notify parents/students
|
|
604
|
+
|
|
605
|
+
Add warning: "This checklist requires review by school legal counsel"`,
|
|
606
|
+
condition: (p) => p.industry.includes('education') && p.collectsUserData,
|
|
607
|
+
},
|
|
608
|
+
EU_AI_ACT: {
|
|
609
|
+
key: 'EU_AI_ACT',
|
|
610
|
+
category: 'AI Regulation',
|
|
611
|
+
tier: 'ai_assisted',
|
|
612
|
+
headline: 'EU AI Act may apply to your AI system',
|
|
613
|
+
explanation: 'The EU AI Act regulates AI systems by risk level. High-risk AI (health, education, employment) has strict requirements.',
|
|
614
|
+
priority: 'high',
|
|
615
|
+
alsoNeeded: ['Risk classification assessment', 'Technical documentation', 'EU representative if non-EU company'],
|
|
616
|
+
promptTemplate: `Read docs/brainlift.md and docs/prd.md. This AI system may be subject to the EU AI Act.
|
|
617
|
+
|
|
618
|
+
Create docs/eu-ai-act-assessment.md covering:
|
|
619
|
+
1. AI use case classification - what does the AI decide or recommend?
|
|
620
|
+
2. Risk level assessment:
|
|
621
|
+
- Unacceptable (banned): social scoring, subliminal manipulation
|
|
622
|
+
- High-risk: education, employment, credit, healthcare, law enforcement
|
|
623
|
+
- Limited risk: chatbots (requires transparency)
|
|
624
|
+
- Minimal risk: spam filters, games
|
|
625
|
+
3. If high-risk, document:
|
|
626
|
+
- Data governance requirements
|
|
627
|
+
- Technical documentation
|
|
628
|
+
- Record keeping
|
|
629
|
+
- Human oversight mechanisms
|
|
630
|
+
- Accuracy, robustness, cybersecurity
|
|
631
|
+
|
|
632
|
+
Add warning: "This requires legal review for final classification"`,
|
|
633
|
+
condition: (p) => p.usesAI && (p.targetsEU || p.industry.some(i => ['healthcare', 'education', 'finance'].includes(i))),
|
|
634
|
+
},
|
|
635
|
+
SBOM: {
|
|
636
|
+
key: 'SBOM',
|
|
637
|
+
category: 'Supply Chain',
|
|
638
|
+
tier: 'ai_assisted',
|
|
639
|
+
headline: 'Generate a Software Bill of Materials (SBOM)',
|
|
640
|
+
explanation: 'An SBOM lists all dependencies in your software. Required by US Executive Order 14028 for government contractors, increasingly expected by enterprise customers.',
|
|
641
|
+
priority: 'medium',
|
|
642
|
+
promptTemplate: `Generate a Software Bill of Materials (SBOM) for this project.
|
|
643
|
+
|
|
644
|
+
Run these commands to generate the SBOM:
|
|
645
|
+
1. For npm: npx @cyclonedx/cyclonedx-npm --output-file sbom.json
|
|
646
|
+
2. Or use: npm sbom --sbom-format cyclonedx
|
|
647
|
+
|
|
648
|
+
Then create docs/sbom-readme.md explaining:
|
|
649
|
+
- What the SBOM contains
|
|
650
|
+
- How to regenerate it
|
|
651
|
+
- When to update it (after dependency changes)
|
|
652
|
+
- License summary of all dependencies
|
|
653
|
+
|
|
654
|
+
Add the sbom.json generation to CI/CD pipeline.`,
|
|
655
|
+
condition: (p) => p.targetAudience.includes('enterprise') || p.industry.includes('finance'),
|
|
656
|
+
},
|
|
657
|
+
DATA_RESIDENCY: {
|
|
658
|
+
key: 'DATA_RESIDENCY',
|
|
659
|
+
category: 'Compliance',
|
|
660
|
+
tier: 'ai_assisted',
|
|
661
|
+
headline: 'Document data residency requirements',
|
|
662
|
+
explanation: 'If you store data in specific regions, you need to document where data lives. GDPR, data sovereignty laws, and enterprise contracts often require this.',
|
|
663
|
+
priority: 'high',
|
|
664
|
+
alsoNeeded: ['Legal review of data transfer agreements', 'Cloud provider region verification'],
|
|
665
|
+
promptTemplate: `Read docs/brainlift.md and docs/prd.md. Create a data residency documentation.
|
|
666
|
+
|
|
667
|
+
Create docs/data-residency.md covering:
|
|
668
|
+
1. Where is user data stored? (AWS region, GCP zone, etc.)
|
|
669
|
+
2. Does data cross borders? (US ↔ EU, etc.)
|
|
670
|
+
3. What legal basis for cross-border transfers? (SCCs, adequacy decisions)
|
|
671
|
+
4. Can customers choose data region? (for enterprise)
|
|
672
|
+
5. Where are backups stored?
|
|
673
|
+
6. Third-party services and their data locations (Stripe, analytics, etc.)
|
|
674
|
+
|
|
675
|
+
Add warning: "Verify regions with your cloud provider dashboard"`,
|
|
676
|
+
condition: (p) => p.targetsEU || p.targetAudience.includes('enterprise'),
|
|
677
|
+
},
|
|
417
678
|
};
|
|
418
679
|
// ============================================================================
|
|
419
680
|
// INFERENCE FUNCTIONS
|
|
@@ -480,19 +741,24 @@ export function inferProjectProfile(projectPath) {
|
|
|
480
741
|
}
|
|
481
742
|
catch { /* ignore parse errors */ }
|
|
482
743
|
}
|
|
483
|
-
//
|
|
744
|
+
// CONSERVATIVE keyword analysis
|
|
745
|
+
// Only trigger on explicit mentions, NOT on broad terms like "global" or "international"
|
|
746
|
+
// AI catch-all will handle nuanced cases
|
|
484
747
|
const keywords = {
|
|
485
|
-
// User data
|
|
486
|
-
collectsUserData: ['user
|
|
487
|
-
collectsSensitiveData: ['health', 'medical', 'financial', 'bank', 'credit', 'ssn', '
|
|
488
|
-
hasUnder13Users: ['kids', 'children', 'child', 'k-12', 'elementary', 'middle school', 'under 13'],
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
748
|
+
// User data - conservative: must be clear user accounts
|
|
749
|
+
collectsUserData: ['user account', 'login', 'signup', 'sign up', 'register', 'authentication', 'auth'],
|
|
750
|
+
collectsSensitiveData: ['health', 'medical', 'hipaa', 'financial', 'bank account', 'credit card', 'ssn', 'social security', 'biometric', 'fingerprint', 'face id'],
|
|
751
|
+
hasUnder13Users: ['kids', 'children', 'child', 'k-12', 'elementary', 'middle school', 'under 13', 'coppa', 'parental consent'],
|
|
752
|
+
// Geography - VERY conservative: only explicit mentions, not "global"
|
|
753
|
+
targetsEU: ['eu', 'europe', 'european union', 'gdpr', 'germany', 'france', 'spain', 'italy', 'netherlands', 'uk users'],
|
|
754
|
+
targetsCalifornia: ['california', 'ccpa', 'california users'],
|
|
755
|
+
// Business - clear signals only
|
|
756
|
+
hasPayments: ['payment', 'stripe', 'paypal', 'billing', 'checkout', 'purchase', 'monetize', 'pricing page'],
|
|
757
|
+
hasSubscriptions: ['subscription', 'monthly plan', 'yearly plan', 'recurring billing', 'saas'],
|
|
758
|
+
hasUserContent: ['upload', 'user generated', 'ugc', 'user posts', 'comments section', 'community content'],
|
|
759
|
+
// AI - detect AI usage with common patterns
|
|
760
|
+
usesAI: ['ai-powered', 'artificial intelligence', 'machine learning', 'gpt', 'llm', 'claude', 'openai', 'langchain', 'uses ai', 'ai features', 'ai model', 'neural network', 'deep learning', 'genai', 'generative ai'],
|
|
761
|
+
aiMakesDecisions: ['ai decides', 'ai recommends', 'automated decision', 'algorithm determines', 'ai-driven', 'ai generates', 'ai creates'],
|
|
496
762
|
};
|
|
497
763
|
for (const [key, terms] of Object.entries(keywords)) {
|
|
498
764
|
if (terms.some(term => content.includes(term))) {
|
|
@@ -551,10 +817,10 @@ function fillPromptTemplate(template, profile, projectPath) {
|
|
|
551
817
|
const brainliftPath = join(safePath, 'docs', 'brainlift.md');
|
|
552
818
|
const prdPath = join(safePath, 'docs', 'prd.md');
|
|
553
819
|
if (existsSync(brainliftPath)) {
|
|
554
|
-
brainliftContent = readFileSync(brainliftPath, 'utf-8')
|
|
820
|
+
brainliftContent = readFileSync(brainliftPath, 'utf-8'); // Read full doc for accurate inference
|
|
555
821
|
}
|
|
556
822
|
if (existsSync(prdPath)) {
|
|
557
|
-
prdContent = readFileSync(prdPath, 'utf-8')
|
|
823
|
+
prdContent = readFileSync(prdPath, 'utf-8'); // Read full doc for accurate inference
|
|
558
824
|
}
|
|
559
825
|
// Build replacements
|
|
560
826
|
const replacements = {
|
|
@@ -588,11 +854,22 @@ function fillPromptTemplate(template, profile, projectPath) {
|
|
|
588
854
|
* Get all applicable reality checks for a project
|
|
589
855
|
*/
|
|
590
856
|
export function getRealityChecks(projectPath) {
|
|
591
|
-
const
|
|
857
|
+
const safePath = sanitizePath(projectPath);
|
|
858
|
+
// Auto-detect generated docs and mark checks complete (feedback loop)
|
|
859
|
+
detectGeneratedDocs(safePath);
|
|
860
|
+
const profile = inferProjectProfile(safePath);
|
|
592
861
|
const checks = [];
|
|
862
|
+
// Load persisted state (after detection so it includes auto-completions)
|
|
863
|
+
const persistedState = loadRealityState(safePath);
|
|
864
|
+
const checkStates = persistedState.checkStates;
|
|
593
865
|
for (const check of Object.values(REALITY_CHECKS)) {
|
|
594
866
|
if (check.condition(profile)) {
|
|
595
|
-
const cursorPrompt = fillPromptTemplate(check.promptTemplate, profile,
|
|
867
|
+
const cursorPrompt = fillPromptTemplate(check.promptTemplate, profile, safePath);
|
|
868
|
+
const persisted = checkStates[check.key];
|
|
869
|
+
// Get triggered-by reason
|
|
870
|
+
const triggeredBy = check.getTriggeredBy
|
|
871
|
+
? check.getTriggeredBy(profile)
|
|
872
|
+
: DEFAULT_TRIGGERS[check.key]?.(profile) || 'Inferred from project profile';
|
|
596
873
|
checks.push({
|
|
597
874
|
key: check.key,
|
|
598
875
|
category: check.category,
|
|
@@ -604,48 +881,227 @@ export function getRealityChecks(projectPath) {
|
|
|
604
881
|
externalLinks: check.externalLinks,
|
|
605
882
|
alsoNeeded: check.alsoNeeded,
|
|
606
883
|
priority: check.priority,
|
|
884
|
+
triggeredBy,
|
|
885
|
+
// Add persisted status
|
|
886
|
+
status: persisted?.status || 'pending',
|
|
887
|
+
statusUpdatedAt: persisted?.updatedAt,
|
|
888
|
+
skippedReason: persisted?.skippedReason,
|
|
607
889
|
});
|
|
608
890
|
}
|
|
609
891
|
}
|
|
610
892
|
// Sort by priority and tier
|
|
611
893
|
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
612
|
-
const tierOrder = {
|
|
894
|
+
const tierOrder = { manual: 0, ai_assisted: 1 };
|
|
613
895
|
checks.sort((a, b) => {
|
|
614
896
|
const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
615
897
|
if (priorityDiff !== 0)
|
|
616
898
|
return priorityDiff;
|
|
617
899
|
return tierOrder[a.tier] - tierOrder[b.tier];
|
|
618
900
|
});
|
|
901
|
+
// Progressive disclosure: first 2 views show only critical + 2 more
|
|
902
|
+
const totalAvailable = checks.length;
|
|
903
|
+
const viewCount = persistedState.viewCount || 0;
|
|
904
|
+
const isFirstSession = viewCount < 2;
|
|
905
|
+
// Increment view count
|
|
906
|
+
const newState = { ...persistedState, viewCount: viewCount + 1 };
|
|
907
|
+
saveRealityState(safePath, newState);
|
|
908
|
+
// On first sessions, limit to critical + 2 non-critical
|
|
909
|
+
let displayChecks = checks;
|
|
910
|
+
if (isFirstSession && checks.length > 4) {
|
|
911
|
+
const critical = checks.filter(c => c.priority === 'critical');
|
|
912
|
+
const nonCritical = checks.filter(c => c.priority !== 'critical').slice(0, 2);
|
|
913
|
+
displayChecks = [...critical, ...nonCritical];
|
|
914
|
+
}
|
|
619
915
|
return {
|
|
620
916
|
profile,
|
|
621
|
-
checks,
|
|
917
|
+
checks: displayChecks,
|
|
622
918
|
summary: {
|
|
623
|
-
total:
|
|
624
|
-
critical:
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
919
|
+
total: displayChecks.length,
|
|
920
|
+
critical: displayChecks.filter(c => c.priority === 'critical').length,
|
|
921
|
+
aiAssisted: displayChecks.filter(c => c.tier === 'ai_assisted').length,
|
|
922
|
+
manual: displayChecks.filter(c => c.tier === 'manual').length,
|
|
923
|
+
pending: displayChecks.filter(c => c.status === 'pending').length,
|
|
924
|
+
completed: displayChecks.filter(c => c.status === 'completed').length,
|
|
925
|
+
skipped: displayChecks.filter(c => c.status === 'skipped').length,
|
|
628
926
|
},
|
|
927
|
+
totalAvailable,
|
|
928
|
+
isFirstSession,
|
|
629
929
|
};
|
|
630
930
|
}
|
|
931
|
+
// ============================================================================
|
|
932
|
+
// AI CATCH-ALL FILTER
|
|
933
|
+
// ============================================================================
|
|
934
|
+
/**
|
|
935
|
+
* AI-powered reality check filter
|
|
936
|
+
*
|
|
937
|
+
* Conservative defaults may miss edge cases. This AI pass:
|
|
938
|
+
* 1. Reviews the full project context
|
|
939
|
+
* 2. Filters out irrelevant checks (e.g., GDPR for US-only app)
|
|
940
|
+
* 3. Adds missing checks based on nuanced understanding
|
|
941
|
+
*
|
|
942
|
+
* Called only when API key is available, falls back to keyword-based otherwise.
|
|
943
|
+
*/
|
|
944
|
+
export async function filterChecksWithAI(profile, checks, projectPath) {
|
|
945
|
+
// Dynamic import to avoid circular dependency
|
|
946
|
+
const { getApiKey } = await import('./config.js');
|
|
947
|
+
const { chat } = await import('./providers.js');
|
|
948
|
+
const apiKey = getApiKey();
|
|
949
|
+
if (!apiKey) {
|
|
950
|
+
// No API key - return checks as-is
|
|
951
|
+
return { filtered: checks, additions: [], removals: [] };
|
|
952
|
+
}
|
|
953
|
+
const safePath = sanitizePath(projectPath);
|
|
954
|
+
// Read full docs for context
|
|
955
|
+
let docsContent = '';
|
|
956
|
+
const brainliftPath = join(safePath, 'docs', 'brainlift.md');
|
|
957
|
+
const prdPath = join(safePath, 'docs', 'prd.md');
|
|
958
|
+
const readmePath = join(safePath, 'README.md');
|
|
959
|
+
if (existsSync(brainliftPath)) {
|
|
960
|
+
docsContent += `## Brainlift:\n${readFileSync(brainliftPath, 'utf-8').slice(0, 4000)}\n\n`;
|
|
961
|
+
}
|
|
962
|
+
if (existsSync(prdPath)) {
|
|
963
|
+
docsContent += `## PRD:\n${readFileSync(prdPath, 'utf-8').slice(0, 4000)}\n\n`;
|
|
964
|
+
}
|
|
965
|
+
if (existsSync(readmePath)) {
|
|
966
|
+
docsContent += `## README:\n${readFileSync(readmePath, 'utf-8').slice(0, 2000)}\n`;
|
|
967
|
+
}
|
|
968
|
+
if (!docsContent) {
|
|
969
|
+
// No docs to analyze - return checks as-is
|
|
970
|
+
return { filtered: checks, additions: [], removals: [] };
|
|
971
|
+
}
|
|
972
|
+
// Combined profile validation + check filtering in a single focused prompt
|
|
973
|
+
const systemPrompt = `Compliance check filter. Given project docs and proposed checks, return JSON only:
|
|
974
|
+
{"keep":["KEY",...], "remove":["KEY",...], "add":["KEY",...]}
|
|
975
|
+
Rules: Remove checks that clearly don't apply. Add missing checks from available list. Be conservative.`;
|
|
976
|
+
// Compact prompt - less tokens, faster response
|
|
977
|
+
const proposed = checks.map(c => c.key).join(',');
|
|
978
|
+
const available = Object.keys(REALITY_CHECKS).filter(k => !checks.some(c => c.key === k)).join(',');
|
|
979
|
+
const prompt = `Docs:\n${docsContent}\n\nProposed: ${proposed}\nAvailable: ${available}\n\nProfile: ${profile.businessModel}, EU:${profile.targetsEU}, AI:${profile.usesAI}, payments:${profile.hasPayments}`;
|
|
980
|
+
try {
|
|
981
|
+
const response = await chat(prompt, {
|
|
982
|
+
systemPrompt,
|
|
983
|
+
maxTokens: 1000,
|
|
984
|
+
useThinking: false, // Fast response
|
|
985
|
+
timeout: 15000, // Quick timeout
|
|
986
|
+
});
|
|
987
|
+
// Parse response
|
|
988
|
+
let jsonStr = response.content;
|
|
989
|
+
if (response.content.includes('```')) {
|
|
990
|
+
const match = response.content.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
991
|
+
if (match)
|
|
992
|
+
jsonStr = match[1];
|
|
993
|
+
}
|
|
994
|
+
const result = JSON.parse(jsonStr.trim());
|
|
995
|
+
// Filter checks based on AI response
|
|
996
|
+
const keepSet = new Set(result.keep || []);
|
|
997
|
+
const removeSet = new Set(result.remove || []);
|
|
998
|
+
const addKeys = result.add || [];
|
|
999
|
+
// Apply filtering
|
|
1000
|
+
let filtered = checks.filter(c => {
|
|
1001
|
+
// If explicitly removed, remove it
|
|
1002
|
+
if (removeSet.has(c.key))
|
|
1003
|
+
return false;
|
|
1004
|
+
// If explicitly kept or not mentioned, keep it (conservative)
|
|
1005
|
+
return true;
|
|
1006
|
+
});
|
|
1007
|
+
// Add any checks AI says we're missing
|
|
1008
|
+
const safePath = sanitizePath(projectPath);
|
|
1009
|
+
const persistedState = loadRealityState(safePath);
|
|
1010
|
+
for (const key of addKeys) {
|
|
1011
|
+
if (REALITY_CHECKS[key] && !filtered.some(c => c.key === key)) {
|
|
1012
|
+
const check = REALITY_CHECKS[key];
|
|
1013
|
+
const cursorPrompt = fillPromptTemplate(check.promptTemplate, profile, projectPath);
|
|
1014
|
+
const persisted = persistedState.checkStates[key];
|
|
1015
|
+
// Get triggered-by reason (AI-added checks)
|
|
1016
|
+
const triggeredBy = check.getTriggeredBy
|
|
1017
|
+
? check.getTriggeredBy(profile)
|
|
1018
|
+
: 'Added by AI analysis of project context';
|
|
1019
|
+
filtered.push({
|
|
1020
|
+
key: check.key,
|
|
1021
|
+
category: check.category,
|
|
1022
|
+
tier: check.tier,
|
|
1023
|
+
headline: check.headline,
|
|
1024
|
+
explanation: check.explanation,
|
|
1025
|
+
cursorPrompt,
|
|
1026
|
+
humanSteps: check.humanSteps,
|
|
1027
|
+
externalLinks: check.externalLinks,
|
|
1028
|
+
alsoNeeded: check.alsoNeeded,
|
|
1029
|
+
priority: check.priority,
|
|
1030
|
+
triggeredBy,
|
|
1031
|
+
status: persisted?.status || 'pending',
|
|
1032
|
+
statusUpdatedAt: persisted?.updatedAt,
|
|
1033
|
+
skippedReason: persisted?.skippedReason,
|
|
1034
|
+
});
|
|
1035
|
+
}
|
|
1036
|
+
}
|
|
1037
|
+
// Re-sort
|
|
1038
|
+
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
1039
|
+
const tierOrder = { manual: 0, ai_assisted: 1 };
|
|
1040
|
+
filtered.sort((a, b) => {
|
|
1041
|
+
const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
1042
|
+
if (priorityDiff !== 0)
|
|
1043
|
+
return priorityDiff;
|
|
1044
|
+
return tierOrder[a.tier] - tierOrder[b.tier];
|
|
1045
|
+
});
|
|
1046
|
+
return {
|
|
1047
|
+
filtered,
|
|
1048
|
+
additions: addKeys,
|
|
1049
|
+
removals: Array.from(removeSet),
|
|
1050
|
+
};
|
|
1051
|
+
}
|
|
1052
|
+
catch (error) {
|
|
1053
|
+
// AI failed - return original checks (conservative fallback)
|
|
1054
|
+
return { filtered: checks, additions: [], removals: [] };
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
/**
|
|
1058
|
+
* Get reality checks with AI filtering (async version)
|
|
1059
|
+
* Use this when you want the most accurate checks
|
|
1060
|
+
*/
|
|
1061
|
+
export async function getRealityChecksWithAI(projectPath) {
|
|
1062
|
+
const basic = getRealityChecks(projectPath);
|
|
1063
|
+
try {
|
|
1064
|
+
const { filtered, additions, removals } = await filterChecksWithAI(basic.profile, basic.checks, projectPath);
|
|
1065
|
+
return {
|
|
1066
|
+
profile: basic.profile,
|
|
1067
|
+
checks: filtered,
|
|
1068
|
+
summary: {
|
|
1069
|
+
total: filtered.length,
|
|
1070
|
+
critical: filtered.filter(c => c.priority === 'critical').length,
|
|
1071
|
+
aiAssisted: filtered.filter(c => c.tier === 'ai_assisted').length,
|
|
1072
|
+
manual: filtered.filter(c => c.tier === 'manual').length,
|
|
1073
|
+
pending: filtered.filter(c => c.status === 'pending').length,
|
|
1074
|
+
completed: filtered.filter(c => c.status === 'completed').length,
|
|
1075
|
+
skipped: filtered.filter(c => c.status === 'skipped').length,
|
|
1076
|
+
},
|
|
1077
|
+
aiFiltered: additions.length > 0 || removals.length > 0,
|
|
1078
|
+
};
|
|
1079
|
+
}
|
|
1080
|
+
catch {
|
|
1081
|
+
// Fallback to basic checks
|
|
1082
|
+
return { ...basic, aiFiltered: false };
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
631
1085
|
/**
|
|
632
1086
|
* Get tier symbol for display
|
|
633
1087
|
*/
|
|
634
1088
|
export function getTierSymbol(tier) {
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
case '
|
|
638
|
-
case '
|
|
1089
|
+
const mapped = TIER_MAPPING[tier] || tier;
|
|
1090
|
+
switch (mapped) {
|
|
1091
|
+
case 'ai_assisted': return '🤖';
|
|
1092
|
+
case 'manual': return '👤';
|
|
1093
|
+
default: return '❓';
|
|
639
1094
|
}
|
|
640
1095
|
}
|
|
641
1096
|
/**
|
|
642
1097
|
* Get tier description
|
|
643
1098
|
*/
|
|
644
1099
|
export function getTierDescription(tier) {
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
case '
|
|
648
|
-
case '
|
|
1100
|
+
const mapped = TIER_MAPPING[tier] || tier;
|
|
1101
|
+
switch (mapped) {
|
|
1102
|
+
case 'ai_assisted': return 'AI can help with this';
|
|
1103
|
+
case 'manual': return 'You need to do this yourself';
|
|
1104
|
+
default: return 'Unknown tier';
|
|
649
1105
|
}
|
|
650
1106
|
}
|
|
651
1107
|
//# sourceMappingURL=reality.js.map
|