midas-mcp 5.5.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 +42 -8
- package/dist/reality.d.ts.map +1 -1
- package/dist/reality.js +383 -95
- 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 +38 -3
- package/dist/tools/reality.d.ts.map +1 -1
- package/dist/tools/reality.js +59 -5
- 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: `
|
|
201
|
+
promptTemplate: `Read docs/brainlift.md and docs/prd.md to understand this project. Then create terms of service.
|
|
50
202
|
|
|
51
|
-
|
|
52
|
-
Business model: {{businessModel}}
|
|
53
|
-
Key features: {{keyFeatures}}
|
|
54
|
-
|
|
55
|
-
Include sections:
|
|
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
|
|
@@ -495,9 +756,9 @@ export function inferProjectProfile(projectPath) {
|
|
|
495
756
|
hasPayments: ['payment', 'stripe', 'paypal', 'billing', 'checkout', 'purchase', 'monetize', 'pricing page'],
|
|
496
757
|
hasSubscriptions: ['subscription', 'monthly plan', 'yearly plan', 'recurring billing', 'saas'],
|
|
497
758
|
hasUserContent: ['upload', 'user generated', 'ugc', 'user posts', 'comments section', 'community content'],
|
|
498
|
-
// AI -
|
|
499
|
-
usesAI: ['ai-powered', 'artificial intelligence', 'machine learning', 'gpt', 'llm', 'claude
|
|
500
|
-
aiMakesDecisions: ['ai decides', 'ai recommends', 'automated decision', 'algorithm determines', 'ai-driven'],
|
|
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'],
|
|
501
762
|
};
|
|
502
763
|
for (const [key, terms] of Object.entries(keywords)) {
|
|
503
764
|
if (terms.some(term => content.includes(term))) {
|
|
@@ -556,10 +817,10 @@ function fillPromptTemplate(template, profile, projectPath) {
|
|
|
556
817
|
const brainliftPath = join(safePath, 'docs', 'brainlift.md');
|
|
557
818
|
const prdPath = join(safePath, 'docs', 'prd.md');
|
|
558
819
|
if (existsSync(brainliftPath)) {
|
|
559
|
-
brainliftContent = readFileSync(brainliftPath, 'utf-8')
|
|
820
|
+
brainliftContent = readFileSync(brainliftPath, 'utf-8'); // Read full doc for accurate inference
|
|
560
821
|
}
|
|
561
822
|
if (existsSync(prdPath)) {
|
|
562
|
-
prdContent = readFileSync(prdPath, 'utf-8')
|
|
823
|
+
prdContent = readFileSync(prdPath, 'utf-8'); // Read full doc for accurate inference
|
|
563
824
|
}
|
|
564
825
|
// Build replacements
|
|
565
826
|
const replacements = {
|
|
@@ -593,11 +854,22 @@ function fillPromptTemplate(template, profile, projectPath) {
|
|
|
593
854
|
* Get all applicable reality checks for a project
|
|
594
855
|
*/
|
|
595
856
|
export function getRealityChecks(projectPath) {
|
|
596
|
-
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);
|
|
597
861
|
const checks = [];
|
|
862
|
+
// Load persisted state (after detection so it includes auto-completions)
|
|
863
|
+
const persistedState = loadRealityState(safePath);
|
|
864
|
+
const checkStates = persistedState.checkStates;
|
|
598
865
|
for (const check of Object.values(REALITY_CHECKS)) {
|
|
599
866
|
if (check.condition(profile)) {
|
|
600
|
-
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';
|
|
601
873
|
checks.push({
|
|
602
874
|
key: check.key,
|
|
603
875
|
category: check.category,
|
|
@@ -609,28 +881,51 @@ export function getRealityChecks(projectPath) {
|
|
|
609
881
|
externalLinks: check.externalLinks,
|
|
610
882
|
alsoNeeded: check.alsoNeeded,
|
|
611
883
|
priority: check.priority,
|
|
884
|
+
triggeredBy,
|
|
885
|
+
// Add persisted status
|
|
886
|
+
status: persisted?.status || 'pending',
|
|
887
|
+
statusUpdatedAt: persisted?.updatedAt,
|
|
888
|
+
skippedReason: persisted?.skippedReason,
|
|
612
889
|
});
|
|
613
890
|
}
|
|
614
891
|
}
|
|
615
892
|
// Sort by priority and tier
|
|
616
893
|
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
617
|
-
const tierOrder = {
|
|
894
|
+
const tierOrder = { manual: 0, ai_assisted: 1 };
|
|
618
895
|
checks.sort((a, b) => {
|
|
619
896
|
const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
620
897
|
if (priorityDiff !== 0)
|
|
621
898
|
return priorityDiff;
|
|
622
899
|
return tierOrder[a.tier] - tierOrder[b.tier];
|
|
623
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
|
+
}
|
|
624
915
|
return {
|
|
625
916
|
profile,
|
|
626
|
-
checks,
|
|
917
|
+
checks: displayChecks,
|
|
627
918
|
summary: {
|
|
628
|
-
total:
|
|
629
|
-
critical:
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
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,
|
|
633
926
|
},
|
|
927
|
+
totalAvailable,
|
|
928
|
+
isFirstSession,
|
|
634
929
|
};
|
|
635
930
|
}
|
|
636
931
|
// ============================================================================
|
|
@@ -662,48 +957,26 @@ export async function filterChecksWithAI(profile, checks, projectPath) {
|
|
|
662
957
|
const prdPath = join(safePath, 'docs', 'prd.md');
|
|
663
958
|
const readmePath = join(safePath, 'README.md');
|
|
664
959
|
if (existsSync(brainliftPath)) {
|
|
665
|
-
docsContent += `## Brainlift:\n${readFileSync(brainliftPath, 'utf-8').slice(0,
|
|
960
|
+
docsContent += `## Brainlift:\n${readFileSync(brainliftPath, 'utf-8').slice(0, 4000)}\n\n`;
|
|
666
961
|
}
|
|
667
962
|
if (existsSync(prdPath)) {
|
|
668
|
-
docsContent += `## PRD:\n${readFileSync(prdPath, 'utf-8').slice(0,
|
|
963
|
+
docsContent += `## PRD:\n${readFileSync(prdPath, 'utf-8').slice(0, 4000)}\n\n`;
|
|
669
964
|
}
|
|
670
965
|
if (existsSync(readmePath)) {
|
|
671
|
-
docsContent += `## README:\n${readFileSync(readmePath, 'utf-8').slice(0,
|
|
966
|
+
docsContent += `## README:\n${readFileSync(readmePath, 'utf-8').slice(0, 2000)}\n`;
|
|
672
967
|
}
|
|
673
968
|
if (!docsContent) {
|
|
674
969
|
// No docs to analyze - return checks as-is
|
|
675
970
|
return { filtered: checks, additions: [], removals: [] };
|
|
676
971
|
}
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
Rules:
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
5. Consider geographic requirements (EU → GDPR, California → CCPA, etc.)
|
|
686
|
-
|
|
687
|
-
Respond ONLY with JSON.`;
|
|
688
|
-
const prompt = `# Project Documentation
|
|
689
|
-
${docsContent}
|
|
690
|
-
|
|
691
|
-
# Inferred Profile
|
|
692
|
-
${JSON.stringify(profile, null, 2)}
|
|
693
|
-
|
|
694
|
-
# Proposed Checks
|
|
695
|
-
${checks.map(c => `- ${c.key}: ${c.headline}`).join('\n')}
|
|
696
|
-
|
|
697
|
-
# All Available Checks (not currently triggered)
|
|
698
|
-
${Object.keys(REALITY_CHECKS).filter(k => !checks.some(c => c.key === k)).map(k => `- ${k}: ${REALITY_CHECKS[k].headline}`).join('\n')}
|
|
699
|
-
|
|
700
|
-
Review this and respond with:
|
|
701
|
-
{
|
|
702
|
-
"keep": ["CHECK_KEY", ...], // Checks that definitely apply
|
|
703
|
-
"remove": ["CHECK_KEY", ...], // Checks that don't apply (with reason)
|
|
704
|
-
"add": ["CHECK_KEY", ...], // Checks from available list that SHOULD apply
|
|
705
|
-
"reasoning": "Brief explanation of key decisions"
|
|
706
|
-
}`;
|
|
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}`;
|
|
707
980
|
try {
|
|
708
981
|
const response = await chat(prompt, {
|
|
709
982
|
systemPrompt,
|
|
@@ -732,10 +1005,17 @@ Review this and respond with:
|
|
|
732
1005
|
return true;
|
|
733
1006
|
});
|
|
734
1007
|
// Add any checks AI says we're missing
|
|
1008
|
+
const safePath = sanitizePath(projectPath);
|
|
1009
|
+
const persistedState = loadRealityState(safePath);
|
|
735
1010
|
for (const key of addKeys) {
|
|
736
1011
|
if (REALITY_CHECKS[key] && !filtered.some(c => c.key === key)) {
|
|
737
1012
|
const check = REALITY_CHECKS[key];
|
|
738
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';
|
|
739
1019
|
filtered.push({
|
|
740
1020
|
key: check.key,
|
|
741
1021
|
category: check.category,
|
|
@@ -747,12 +1027,16 @@ Review this and respond with:
|
|
|
747
1027
|
externalLinks: check.externalLinks,
|
|
748
1028
|
alsoNeeded: check.alsoNeeded,
|
|
749
1029
|
priority: check.priority,
|
|
1030
|
+
triggeredBy,
|
|
1031
|
+
status: persisted?.status || 'pending',
|
|
1032
|
+
statusUpdatedAt: persisted?.updatedAt,
|
|
1033
|
+
skippedReason: persisted?.skippedReason,
|
|
750
1034
|
});
|
|
751
1035
|
}
|
|
752
1036
|
}
|
|
753
1037
|
// Re-sort
|
|
754
1038
|
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
755
|
-
const tierOrder = {
|
|
1039
|
+
const tierOrder = { manual: 0, ai_assisted: 1 };
|
|
756
1040
|
filtered.sort((a, b) => {
|
|
757
1041
|
const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
758
1042
|
if (priorityDiff !== 0)
|
|
@@ -784,9 +1068,11 @@ export async function getRealityChecksWithAI(projectPath) {
|
|
|
784
1068
|
summary: {
|
|
785
1069
|
total: filtered.length,
|
|
786
1070
|
critical: filtered.filter(c => c.priority === 'critical').length,
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
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,
|
|
790
1076
|
},
|
|
791
1077
|
aiFiltered: additions.length > 0 || removals.length > 0,
|
|
792
1078
|
};
|
|
@@ -800,20 +1086,22 @@ export async function getRealityChecksWithAI(projectPath) {
|
|
|
800
1086
|
* Get tier symbol for display
|
|
801
1087
|
*/
|
|
802
1088
|
export function getTierSymbol(tier) {
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
case '
|
|
806
|
-
case '
|
|
1089
|
+
const mapped = TIER_MAPPING[tier] || tier;
|
|
1090
|
+
switch (mapped) {
|
|
1091
|
+
case 'ai_assisted': return '🤖';
|
|
1092
|
+
case 'manual': return '👤';
|
|
1093
|
+
default: return '❓';
|
|
807
1094
|
}
|
|
808
1095
|
}
|
|
809
1096
|
/**
|
|
810
1097
|
* Get tier description
|
|
811
1098
|
*/
|
|
812
1099
|
export function getTierDescription(tier) {
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
case '
|
|
816
|
-
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';
|
|
817
1105
|
}
|
|
818
1106
|
}
|
|
819
1107
|
//# sourceMappingURL=reality.js.map
|