midas-mcp 5.41.0 → 5.43.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/analyzer.d.ts.map +1 -1
- package/dist/analyzer.js +9 -2
- package/dist/analyzer.js.map +1 -1
- package/dist/context.d.ts +264 -5
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +634 -7
- package/dist/context.js.map +1 -1
- package/dist/experiments/recursive-planning/recursive-session.d.ts +201 -0
- package/dist/experiments/recursive-planning/recursive-session.d.ts.map +1 -0
- package/dist/experiments/recursive-planning/recursive-session.js +348 -0
- package/dist/experiments/recursive-planning/recursive-session.js.map +1 -0
- package/dist/experiments/recursive-planning/recursive-session.test.d.ts +19 -0
- package/dist/experiments/recursive-planning/recursive-session.test.d.ts.map +1 -0
- package/dist/experiments/recursive-planning/recursive-session.test.js +799 -0
- package/dist/experiments/recursive-planning/recursive-session.test.js.map +1 -0
- package/dist/preflight.d.ts +140 -0
- package/dist/preflight.d.ts.map +1 -0
- package/dist/preflight.js +1100 -0
- package/dist/preflight.js.map +1 -0
- package/dist/server.js +4 -4
- 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 +4 -2
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/preflight.d.ts +121 -0
- package/dist/tools/preflight.d.ts.map +1 -0
- package/dist/tools/preflight.js +144 -0
- package/dist/tools/preflight.js.map +1 -0
- package/dist/tools/verify.js +2 -2
- package/dist/tools/verify.js.map +1 -1
- package/dist/tui.js +53 -53
- package/dist/tui.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,1100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preflight Check Module
|
|
3
|
+
*
|
|
4
|
+
* Analyzes project docs to infer what requirements apply,
|
|
5
|
+
* categorizes them by what AI can/cannot do,
|
|
6
|
+
* and generates context-aware prompts for Cursor.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync, mkdirSync } from 'fs';
|
|
9
|
+
import writeFileAtomic from 'write-file-atomic';
|
|
10
|
+
import { join } from 'path';
|
|
11
|
+
import { sanitizePath } from './security.js';
|
|
12
|
+
import { discoverDocsSync, getPlanningContext } from './docs-discovery.js';
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// PERSISTENCE
|
|
15
|
+
// ============================================================================
|
|
16
|
+
const MIDAS_DIR = '.midas';
|
|
17
|
+
const PREFLIGHT_STATE_FILE = 'preflight-checks.json';
|
|
18
|
+
function getPreflightStatePath(projectPath) {
|
|
19
|
+
return join(projectPath, MIDAS_DIR, PREFLIGHT_STATE_FILE);
|
|
20
|
+
}
|
|
21
|
+
export function loadPreflightState(projectPath) {
|
|
22
|
+
const path = getPreflightStatePath(projectPath);
|
|
23
|
+
if (existsSync(path)) {
|
|
24
|
+
try {
|
|
25
|
+
return JSON.parse(readFileSync(path, 'utf-8'));
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return { checkStates: {}, lastProfileHash: '' };
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Try legacy reality-checks.json for backward compatibility
|
|
32
|
+
const legacyPath = join(projectPath, MIDAS_DIR, 'reality-checks.json');
|
|
33
|
+
if (existsSync(legacyPath)) {
|
|
34
|
+
try {
|
|
35
|
+
return JSON.parse(readFileSync(legacyPath, 'utf-8'));
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
return { checkStates: {}, lastProfileHash: '' };
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return { checkStates: {}, lastProfileHash: '' };
|
|
42
|
+
}
|
|
43
|
+
function savePreflightState(projectPath, state) {
|
|
44
|
+
const dir = join(projectPath, MIDAS_DIR);
|
|
45
|
+
if (!existsSync(dir)) {
|
|
46
|
+
mkdirSync(dir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
// Use atomic write to prevent corruption from concurrent access
|
|
49
|
+
writeFileAtomic.sync(getPreflightStatePath(projectPath), JSON.stringify(state, null, 2));
|
|
50
|
+
}
|
|
51
|
+
function hashProfile(profile) {
|
|
52
|
+
// Simple hash to detect profile changes
|
|
53
|
+
return JSON.stringify(profile).split('').reduce((a, b) => {
|
|
54
|
+
a = ((a << 5) - a) + b.charCodeAt(0);
|
|
55
|
+
return a & a;
|
|
56
|
+
}, 0).toString(36);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Update the status of a preflight check
|
|
60
|
+
*/
|
|
61
|
+
export function updateCheckStatus(projectPath, checkKey, status, skippedReason) {
|
|
62
|
+
const safePath = sanitizePath(projectPath);
|
|
63
|
+
const state = loadPreflightState(safePath);
|
|
64
|
+
state.checkStates[checkKey] = {
|
|
65
|
+
status,
|
|
66
|
+
updatedAt: new Date().toISOString(),
|
|
67
|
+
...(skippedReason ? { skippedReason } : {}),
|
|
68
|
+
};
|
|
69
|
+
savePreflightState(safePath, state);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get the persisted status for a check
|
|
73
|
+
*/
|
|
74
|
+
export function getCheckStatus(projectPath, checkKey) {
|
|
75
|
+
const safePath = sanitizePath(projectPath);
|
|
76
|
+
const state = loadPreflightState(safePath);
|
|
77
|
+
return state.checkStates[checkKey];
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Get all check statuses
|
|
81
|
+
*/
|
|
82
|
+
export function getAllCheckStatuses(projectPath) {
|
|
83
|
+
const safePath = sanitizePath(projectPath);
|
|
84
|
+
const state = loadPreflightState(safePath);
|
|
85
|
+
return state.checkStates;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Reset all check statuses (e.g., when profile changes significantly)
|
|
89
|
+
*/
|
|
90
|
+
export function resetCheckStatuses(projectPath) {
|
|
91
|
+
const safePath = sanitizePath(projectPath);
|
|
92
|
+
savePreflightState(safePath, { checkStates: {}, lastProfileHash: '' });
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Map of check keys to the expected generated file paths
|
|
96
|
+
*/
|
|
97
|
+
const EXPECTED_OUTPUTS = {
|
|
98
|
+
PRIVACY_POLICY: ['docs/privacy-policy.md', 'privacy-policy.md', 'PRIVACY.md'],
|
|
99
|
+
TERMS_OF_SERVICE: ['docs/terms-of-service.md', 'docs/terms.md', 'TERMS.md'],
|
|
100
|
+
COOKIE_POLICY: ['docs/cookie-policy.md'],
|
|
101
|
+
GDPR_COMPLIANCE: ['docs/gdpr-checklist.md', 'docs/gdpr.md'],
|
|
102
|
+
CCPA_COMPLIANCE: ['docs/ccpa-checklist.md'],
|
|
103
|
+
AI_DISCLOSURE: ['docs/ai-disclosure.md', 'AI_DISCLOSURE.md'],
|
|
104
|
+
ACCESSIBILITY: ['docs/accessibility.md', 'docs/a11y.md', 'ACCESSIBILITY.md'],
|
|
105
|
+
DATA_RETENTION: ['docs/data-retention.md'],
|
|
106
|
+
INCIDENT_RESPONSE: ['docs/incident-response.md'],
|
|
107
|
+
OSS_LICENSE: ['LICENSE', 'LICENSE.md', 'docs/license.md'],
|
|
108
|
+
HIPAA_COMPLIANCE: ['docs/hipaa-checklist.md'],
|
|
109
|
+
FERPA_COMPLIANCE: ['docs/ferpa-checklist.md'],
|
|
110
|
+
EU_AI_ACT: ['docs/eu-ai-act-assessment.md'],
|
|
111
|
+
SBOM: ['sbom.json', 'docs/sbom-readme.md'],
|
|
112
|
+
DATA_RESIDENCY: ['docs/data-residency.md'],
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Detect if any expected output files exist and auto-complete checks
|
|
116
|
+
* Returns array of check keys that were auto-completed
|
|
117
|
+
*/
|
|
118
|
+
export function detectGeneratedDocs(projectPath) {
|
|
119
|
+
const safePath = sanitizePath(projectPath);
|
|
120
|
+
const state = loadPreflightState(safePath);
|
|
121
|
+
const autoCompleted = [];
|
|
122
|
+
for (const [checkKey, possibleFiles] of Object.entries(EXPECTED_OUTPUTS)) {
|
|
123
|
+
// Skip if already completed
|
|
124
|
+
if (state.checkStates[checkKey]?.status === 'completed')
|
|
125
|
+
continue;
|
|
126
|
+
// Check if any of the expected files exist
|
|
127
|
+
const fileExists = possibleFiles.some(file => existsSync(join(safePath, file)));
|
|
128
|
+
if (fileExists) {
|
|
129
|
+
// Auto-complete this check
|
|
130
|
+
state.checkStates[checkKey] = {
|
|
131
|
+
status: 'completed',
|
|
132
|
+
updatedAt: new Date().toISOString(),
|
|
133
|
+
};
|
|
134
|
+
autoCompleted.push(checkKey);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (autoCompleted.length > 0) {
|
|
138
|
+
savePreflightState(safePath, state);
|
|
139
|
+
}
|
|
140
|
+
return autoCompleted;
|
|
141
|
+
}
|
|
142
|
+
// Legacy tier mapping for backward compatibility
|
|
143
|
+
const TIER_MAPPING = {
|
|
144
|
+
'generatable': 'ai_assisted',
|
|
145
|
+
'assistable': 'ai_assisted',
|
|
146
|
+
'human_only': 'manual',
|
|
147
|
+
'ai_assisted': 'ai_assisted',
|
|
148
|
+
'manual': 'manual',
|
|
149
|
+
};
|
|
150
|
+
// Default triggered-by generators based on common profile fields
|
|
151
|
+
const DEFAULT_TRIGGERS = {
|
|
152
|
+
PRIVACY_POLICY: (p) => p.collectsSensitiveData
|
|
153
|
+
? 'Project collects sensitive data (health/financial/biometric)'
|
|
154
|
+
: 'Project collects user data',
|
|
155
|
+
TERMS_OF_SERVICE: () => 'Public-facing product',
|
|
156
|
+
COOKIE_POLICY: (p) => p.targetsEU ? 'Targets EU users' : 'Collects user data',
|
|
157
|
+
GDPR_COMPLIANCE: (p) => p.targetsEU ? 'Explicitly targets EU users' : 'May have EU users',
|
|
158
|
+
CCPA_COMPLIANCE: () => 'Targets California or US users',
|
|
159
|
+
COPPA_COMPLIANCE: () => 'May have users under 13',
|
|
160
|
+
AI_DISCLOSURE: () => 'Uses AI for decisions or recommendations',
|
|
161
|
+
PAYMENT_SETUP: () => 'Has payment/subscription features',
|
|
162
|
+
STRIPE_INTEGRATION: () => 'Has payment processing',
|
|
163
|
+
APP_STORE: () => 'Distributes via iOS App Store',
|
|
164
|
+
PLAY_STORE: () => 'Distributes via Google Play Store',
|
|
165
|
+
ACCESSIBILITY: () => 'Public-facing product should be accessible',
|
|
166
|
+
DATA_RETENTION: (p) => p.collectsSensitiveData ? 'Handles sensitive data' : 'Collects user data',
|
|
167
|
+
INCIDENT_RESPONSE: () => 'Production system needs incident handling',
|
|
168
|
+
OSS_LICENSE: () => 'Open source project needs license',
|
|
169
|
+
HIPAA_COMPLIANCE: () => 'Healthcare industry + collects user data',
|
|
170
|
+
FERPA_COMPLIANCE: () => 'Education industry + collects student data',
|
|
171
|
+
EU_AI_ACT: (p) => p.targetsEU
|
|
172
|
+
? 'AI system targeting EU users'
|
|
173
|
+
: 'AI system in regulated industry (healthcare/education/finance)',
|
|
174
|
+
SBOM: () => 'Enterprise/finance audience expects supply chain transparency',
|
|
175
|
+
DATA_RESIDENCY: (p) => p.targetsEU
|
|
176
|
+
? 'EU users require data residency documentation (GDPR)'
|
|
177
|
+
: 'Enterprise customers require data location clarity',
|
|
178
|
+
};
|
|
179
|
+
const PREFLIGHT_CHECKS = {
|
|
180
|
+
// ✅ GENERATABLE - AI can draft these
|
|
181
|
+
PRIVACY_POLICY: {
|
|
182
|
+
key: 'PRIVACY_POLICY',
|
|
183
|
+
category: 'Legal',
|
|
184
|
+
tier: 'ai_assisted',
|
|
185
|
+
headline: 'You need a Privacy Policy',
|
|
186
|
+
explanation: 'You collect user data. Users need to know what you collect, why, and how to delete it.',
|
|
187
|
+
priority: 'critical',
|
|
188
|
+
promptTemplate: `Read docs/brainlift.md and docs/prd.md to understand this project. Then create a privacy policy.
|
|
189
|
+
|
|
190
|
+
First, identify from the docs:
|
|
191
|
+
- What user data is collected
|
|
192
|
+
- Why it's collected
|
|
193
|
+
- Who the target users are
|
|
194
|
+
- The business model
|
|
195
|
+
|
|
196
|
+
Then create docs/privacy-policy.md with sections:
|
|
197
|
+
- What we collect and why
|
|
198
|
+
- How we use the data
|
|
199
|
+
- Third parties we share with
|
|
200
|
+
- Data retention
|
|
201
|
+
- User rights (access, correct, delete)
|
|
202
|
+
- Contact information
|
|
203
|
+
|
|
204
|
+
Add at top: "DRAFT - Review with a lawyer before publishing"`,
|
|
205
|
+
condition: (p) => p.collectsUserData,
|
|
206
|
+
},
|
|
207
|
+
TERMS_OF_SERVICE: {
|
|
208
|
+
key: 'TERMS_OF_SERVICE',
|
|
209
|
+
category: 'Legal',
|
|
210
|
+
tier: 'ai_assisted',
|
|
211
|
+
headline: 'You need Terms of Service',
|
|
212
|
+
explanation: 'Any public product needs terms defining the rules of use and liability limits.',
|
|
213
|
+
priority: 'high',
|
|
214
|
+
promptTemplate: `Read docs/brainlift.md and docs/prd.md to understand this project. Then create terms of service.
|
|
215
|
+
|
|
216
|
+
Create docs/terms-of-service.md with sections:
|
|
217
|
+
- Acceptance of terms
|
|
218
|
+
- Description of service (from what you read)
|
|
219
|
+
- User responsibilities
|
|
220
|
+
- Prohibited uses
|
|
221
|
+
- Intellectual property
|
|
222
|
+
- Limitation of liability
|
|
223
|
+
- Termination
|
|
224
|
+
- Governing law (placeholder for jurisdiction)
|
|
225
|
+
|
|
226
|
+
Save to docs/terms-of-service.md
|
|
227
|
+
|
|
228
|
+
Add at the top: "DRAFT - Review with a lawyer before publishing"`,
|
|
229
|
+
condition: (p) => p.collectsUserData || p.hasPayments,
|
|
230
|
+
},
|
|
231
|
+
AI_DISCLOSURE: {
|
|
232
|
+
key: 'AI_DISCLOSURE',
|
|
233
|
+
category: 'Transparency',
|
|
234
|
+
tier: 'ai_assisted',
|
|
235
|
+
headline: 'You need an AI disclosure',
|
|
236
|
+
explanation: 'Users should know when AI is involved and that it can make mistakes.',
|
|
237
|
+
priority: 'high',
|
|
238
|
+
promptTemplate: `Create an AI transparency disclosure for this project.
|
|
239
|
+
|
|
240
|
+
Based on the brainlift, this project uses AI to: {{aiUsage}}
|
|
241
|
+
|
|
242
|
+
Create a user-friendly disclosure explaining:
|
|
243
|
+
- What AI does in this product
|
|
244
|
+
- That AI can make mistakes or produce inaccurate results
|
|
245
|
+
- How users can report issues or get human help
|
|
246
|
+
- Any limitations users should know about
|
|
247
|
+
|
|
248
|
+
Save to docs/ai-disclosure.md
|
|
249
|
+
|
|
250
|
+
Also add a brief inline disclosure component/text that can be shown in the UI where AI is used.`,
|
|
251
|
+
condition: (p) => p.usesAI,
|
|
252
|
+
},
|
|
253
|
+
REFUND_POLICY: {
|
|
254
|
+
key: 'REFUND_POLICY',
|
|
255
|
+
category: 'Business',
|
|
256
|
+
tier: 'ai_assisted',
|
|
257
|
+
headline: 'You need a refund policy',
|
|
258
|
+
explanation: 'Paid products need clear refund terms to avoid disputes and chargebacks.',
|
|
259
|
+
priority: 'high',
|
|
260
|
+
promptTemplate: `Create a refund policy for this project.
|
|
261
|
+
|
|
262
|
+
Business model: {{businessModel}}
|
|
263
|
+
Subscription type: {{subscriptionType}}
|
|
264
|
+
|
|
265
|
+
Include:
|
|
266
|
+
- Refund eligibility (time period, conditions)
|
|
267
|
+
- How to request a refund
|
|
268
|
+
- Processing time
|
|
269
|
+
- Exceptions (if any)
|
|
270
|
+
- Contact information
|
|
271
|
+
|
|
272
|
+
Keep it simple and fair - generous refund policies reduce chargebacks.
|
|
273
|
+
|
|
274
|
+
Save to docs/refund-policy.md`,
|
|
275
|
+
condition: (p) => p.hasPayments,
|
|
276
|
+
},
|
|
277
|
+
CONTENT_POLICY: {
|
|
278
|
+
key: 'CONTENT_POLICY',
|
|
279
|
+
category: 'Trust',
|
|
280
|
+
tier: 'ai_assisted',
|
|
281
|
+
headline: 'You need a content policy',
|
|
282
|
+
explanation: 'User-generated content needs rules about what\'s allowed and how violations are handled.',
|
|
283
|
+
priority: 'high',
|
|
284
|
+
promptTemplate: `Create a content policy for this project.
|
|
285
|
+
|
|
286
|
+
This product allows users to: {{userContentType}}
|
|
287
|
+
|
|
288
|
+
Include:
|
|
289
|
+
- What content is allowed
|
|
290
|
+
- What content is prohibited (hate speech, harassment, illegal content, etc.)
|
|
291
|
+
- How violations are reported
|
|
292
|
+
- How we handle violations (warning, removal, ban)
|
|
293
|
+
- Appeal process
|
|
294
|
+
|
|
295
|
+
Save to docs/content-policy.md`,
|
|
296
|
+
condition: (p) => p.hasUserContent,
|
|
297
|
+
},
|
|
298
|
+
// ⚠️ ASSISTABLE - AI can create guide, needs professional verification
|
|
299
|
+
GDPR_COMPLIANCE: {
|
|
300
|
+
key: 'GDPR_COMPLIANCE',
|
|
301
|
+
category: 'Compliance',
|
|
302
|
+
tier: 'ai_assisted',
|
|
303
|
+
headline: 'GDPR applies to your product',
|
|
304
|
+
explanation: 'You\'re targeting EU users. You need lawful basis for data processing, user consent, and data rights.',
|
|
305
|
+
priority: 'critical',
|
|
306
|
+
alsoNeeded: ['Legal review of implementation', 'DPA registration if required', 'Data Processing Agreements with vendors'],
|
|
307
|
+
promptTemplate: `Create a GDPR compliance implementation guide for this project.
|
|
308
|
+
|
|
309
|
+
Based on analysis:
|
|
310
|
+
- Data collected: {{dataCollected}}
|
|
311
|
+
- Processing purposes: {{processingPurposes}}
|
|
312
|
+
- Third parties: {{thirdParties}}
|
|
313
|
+
|
|
314
|
+
Generate:
|
|
315
|
+
1. Data inventory table (what data, why collected, legal basis, retention period)
|
|
316
|
+
2. Consent flow requirements (what needs explicit consent vs legitimate interest)
|
|
317
|
+
3. User rights implementation checklist:
|
|
318
|
+
- Right to access (export user data)
|
|
319
|
+
- Right to rectification (edit profile)
|
|
320
|
+
- Right to erasure (delete account)
|
|
321
|
+
- Right to portability (download data)
|
|
322
|
+
4. Cookie consent requirements
|
|
323
|
+
5. Privacy by design checklist
|
|
324
|
+
|
|
325
|
+
Save to docs/gdpr-implementation.md
|
|
326
|
+
|
|
327
|
+
Note at top: "This is a technical implementation guide. Legal review required before launch."`,
|
|
328
|
+
condition: (p) => p.targetsEU && p.collectsUserData,
|
|
329
|
+
},
|
|
330
|
+
CCPA_COMPLIANCE: {
|
|
331
|
+
key: 'CCPA_COMPLIANCE',
|
|
332
|
+
category: 'Compliance',
|
|
333
|
+
tier: 'ai_assisted',
|
|
334
|
+
headline: 'CCPA applies to your product',
|
|
335
|
+
explanation: 'California users have rights to know, delete, and opt-out of data sales.',
|
|
336
|
+
priority: 'high',
|
|
337
|
+
alsoNeeded: ['Legal review', '"Do Not Sell" link if applicable'],
|
|
338
|
+
promptTemplate: `Create a CCPA compliance checklist for this project.
|
|
339
|
+
|
|
340
|
+
Data collected: {{dataCollected}}
|
|
341
|
+
California users expected: Yes
|
|
342
|
+
|
|
343
|
+
Include:
|
|
344
|
+
1. Right to know (disclosure of data collected)
|
|
345
|
+
2. Right to delete implementation
|
|
346
|
+
3. Right to opt-out (if selling data)
|
|
347
|
+
4. Non-discrimination requirements
|
|
348
|
+
5. Privacy policy updates needed for CCPA
|
|
349
|
+
|
|
350
|
+
Save to docs/ccpa-checklist.md`,
|
|
351
|
+
condition: (p) => p.targetsCalifornia && p.collectsUserData,
|
|
352
|
+
},
|
|
353
|
+
ACCESSIBILITY: {
|
|
354
|
+
key: 'ACCESSIBILITY',
|
|
355
|
+
category: 'Inclusion',
|
|
356
|
+
tier: 'ai_assisted',
|
|
357
|
+
headline: 'Consider accessibility (WCAG)',
|
|
358
|
+
explanation: 'Making your product accessible helps more users and may be legally required for some customers.',
|
|
359
|
+
priority: 'medium',
|
|
360
|
+
alsoNeeded: ['Actual testing with screen readers', 'User testing with diverse users'],
|
|
361
|
+
promptTemplate: `Create an accessibility checklist and implementation guide for this project.
|
|
362
|
+
|
|
363
|
+
Review the codebase and create:
|
|
364
|
+
1. WCAG 2.1 AA compliance checklist with current status
|
|
365
|
+
2. Priority fixes needed (semantic HTML, ARIA labels, color contrast)
|
|
366
|
+
3. Keyboard navigation requirements
|
|
367
|
+
4. Screen reader compatibility notes
|
|
368
|
+
5. Form accessibility (labels, error messages)
|
|
369
|
+
|
|
370
|
+
For each issue found, provide the fix.
|
|
371
|
+
|
|
372
|
+
Save checklist to docs/accessibility-checklist.md`,
|
|
373
|
+
condition: (p) => p.targetAudience.some(a => ['enterprise', 'education', 'government'].includes(a)) || p.collectsUserData,
|
|
374
|
+
},
|
|
375
|
+
BIAS_ASSESSMENT: {
|
|
376
|
+
key: 'BIAS_ASSESSMENT',
|
|
377
|
+
category: 'Ethics',
|
|
378
|
+
tier: 'ai_assisted',
|
|
379
|
+
headline: 'AI bias assessment needed',
|
|
380
|
+
explanation: 'AI that makes decisions about people can have unintended bias. Document what you\'ve considered.',
|
|
381
|
+
priority: 'high',
|
|
382
|
+
alsoNeeded: ['Testing with diverse user groups', 'Regular monitoring for bias', 'Human override mechanism'],
|
|
383
|
+
promptTemplate: `Create an AI bias assessment document for this project.
|
|
384
|
+
|
|
385
|
+
AI is used for: {{aiUsage}}
|
|
386
|
+
Decisions affected: {{decisionsAffected}}
|
|
387
|
+
|
|
388
|
+
Document:
|
|
389
|
+
1. What decisions the AI influences
|
|
390
|
+
2. Potential sources of bias (training data, model architecture)
|
|
391
|
+
3. Protected characteristics that could be affected
|
|
392
|
+
4. Mitigation strategies implemented
|
|
393
|
+
5. Monitoring plan for detecting bias
|
|
394
|
+
6. Human override / appeal mechanism
|
|
395
|
+
|
|
396
|
+
Save to docs/ai-bias-assessment.md
|
|
397
|
+
|
|
398
|
+
This is for internal documentation and transparency.`,
|
|
399
|
+
condition: (p) => p.aiMakesDecisions,
|
|
400
|
+
},
|
|
401
|
+
// ❌ HUMAN ONLY - Requires real-world action
|
|
402
|
+
PAYMENT_SETUP: {
|
|
403
|
+
key: 'PAYMENT_SETUP',
|
|
404
|
+
category: 'Business',
|
|
405
|
+
tier: 'manual',
|
|
406
|
+
headline: 'You need payment processing',
|
|
407
|
+
explanation: 'You want to charge users. You need a payment provider account first.',
|
|
408
|
+
priority: 'critical',
|
|
409
|
+
humanSteps: [
|
|
410
|
+
'Go to stripe.com and create an account',
|
|
411
|
+
'Complete business verification (1-3 days)',
|
|
412
|
+
'Set up your bank account for payouts',
|
|
413
|
+
'Get your API keys from the dashboard',
|
|
414
|
+
],
|
|
415
|
+
externalLinks: ['https://stripe.com', 'https://stripe.com/docs/keys'],
|
|
416
|
+
promptTemplate: `Implement Stripe payment integration for this project.
|
|
417
|
+
|
|
418
|
+
Requirements from PRD:
|
|
419
|
+
- Business model: {{businessModel}}
|
|
420
|
+
- Pricing: {{pricing}}
|
|
421
|
+
|
|
422
|
+
Implement:
|
|
423
|
+
1. Stripe SDK setup with environment variables for keys
|
|
424
|
+
2. Checkout session creation for {{checkoutType}}
|
|
425
|
+
3. Webhook handler for payment events (payment_intent.succeeded, subscription events)
|
|
426
|
+
4. Customer portal link for subscription management
|
|
427
|
+
5. Subscription status middleware to gate premium features
|
|
428
|
+
|
|
429
|
+
I have my Stripe API keys ready. Use STRIPE_SECRET_KEY and STRIPE_PUBLISHABLE_KEY env vars.`,
|
|
430
|
+
condition: (p) => p.hasPayments,
|
|
431
|
+
},
|
|
432
|
+
BUSINESS_REGISTRATION: {
|
|
433
|
+
key: 'BUSINESS_REGISTRATION',
|
|
434
|
+
category: 'Legal',
|
|
435
|
+
tier: 'manual',
|
|
436
|
+
headline: 'Consider business registration',
|
|
437
|
+
explanation: 'If you\'re making money, you may need a business entity for taxes and liability protection.',
|
|
438
|
+
priority: 'medium',
|
|
439
|
+
humanSteps: [
|
|
440
|
+
'Research business structures (LLC, Corp, Sole Prop)',
|
|
441
|
+
'Register in your state/country',
|
|
442
|
+
'Get an EIN (US) or equivalent tax ID',
|
|
443
|
+
'Open a business bank account',
|
|
444
|
+
],
|
|
445
|
+
externalLinks: ['https://www.sba.gov/business-guide/launch-your-business/choose-business-structure'],
|
|
446
|
+
promptTemplate: `No code needed. This is a business/legal step.
|
|
447
|
+
|
|
448
|
+
Once you have your business set up, update your docs:
|
|
449
|
+
- Add business name to privacy policy and terms
|
|
450
|
+
- Add business address to contact information
|
|
451
|
+
- Update payment provider with business details`,
|
|
452
|
+
condition: (p) => p.hasPayments && p.businessModel !== 'free',
|
|
453
|
+
},
|
|
454
|
+
TAX_SETUP: {
|
|
455
|
+
key: 'TAX_SETUP',
|
|
456
|
+
category: 'Business',
|
|
457
|
+
tier: 'manual',
|
|
458
|
+
headline: 'You need tax handling',
|
|
459
|
+
explanation: 'Selling internationally means dealing with VAT, GST, and sales tax. Stripe Tax can help.',
|
|
460
|
+
priority: 'high',
|
|
461
|
+
humanSteps: [
|
|
462
|
+
'Enable Stripe Tax in your Stripe dashboard',
|
|
463
|
+
'Register for VAT/GST in required jurisdictions',
|
|
464
|
+
'Or use a service like Paddle that handles tax for you',
|
|
465
|
+
],
|
|
466
|
+
externalLinks: ['https://stripe.com/tax', 'https://paddle.com'],
|
|
467
|
+
promptTemplate: `Add Stripe Tax integration to handle VAT/GST automatically.
|
|
468
|
+
|
|
469
|
+
Enable tax calculation in checkout:
|
|
470
|
+
1. Add tax_behavior: 'exclusive' or 'inclusive' to prices
|
|
471
|
+
2. Enable automatic_tax in checkout sessions
|
|
472
|
+
3. Collect customer address for tax calculation
|
|
473
|
+
4. Display tax amounts in checkout UI
|
|
474
|
+
|
|
475
|
+
See Stripe Tax docs for jurisdiction-specific setup.`,
|
|
476
|
+
condition: (p) => p.hasPayments && p.targetsEU,
|
|
477
|
+
},
|
|
478
|
+
DOMAIN_SSL: {
|
|
479
|
+
key: 'DOMAIN_SSL',
|
|
480
|
+
category: 'Infrastructure',
|
|
481
|
+
tier: 'manual',
|
|
482
|
+
headline: 'You need a domain and SSL',
|
|
483
|
+
explanation: 'To launch publicly, you need a domain name and HTTPS.',
|
|
484
|
+
priority: 'high',
|
|
485
|
+
humanSteps: [
|
|
486
|
+
'Choose and purchase a domain (Namecheap, Cloudflare, etc.)',
|
|
487
|
+
'Point DNS to your hosting provider',
|
|
488
|
+
'SSL is usually automatic with modern hosts (Vercel, Netlify, etc.)',
|
|
489
|
+
],
|
|
490
|
+
externalLinks: ['https://www.namecheap.com', 'https://www.cloudflare.com'],
|
|
491
|
+
promptTemplate: `No code needed for domain purchase.
|
|
492
|
+
|
|
493
|
+
Once you have your domain, update:
|
|
494
|
+
1. Environment variables with production URL
|
|
495
|
+
2. OAuth redirect URLs if using social login
|
|
496
|
+
3. Stripe webhook URLs
|
|
497
|
+
4. Any hardcoded localhost references`,
|
|
498
|
+
condition: (p) => p.collectsUserData || p.hasPayments,
|
|
499
|
+
},
|
|
500
|
+
APP_STORE: {
|
|
501
|
+
key: 'APP_STORE',
|
|
502
|
+
category: 'Distribution',
|
|
503
|
+
tier: 'manual',
|
|
504
|
+
headline: 'App Store submission needed',
|
|
505
|
+
explanation: 'Mobile apps need App Store / Play Store developer accounts and review.',
|
|
506
|
+
priority: 'critical',
|
|
507
|
+
humanSteps: [
|
|
508
|
+
'Apple: Enroll in Apple Developer Program ($99/year)',
|
|
509
|
+
'Google: Create Google Play Developer account ($25 one-time)',
|
|
510
|
+
'Prepare screenshots, descriptions, privacy policy URL',
|
|
511
|
+
'Submit for review (Apple: 1-7 days, Google: hours to days)',
|
|
512
|
+
],
|
|
513
|
+
externalLinks: ['https://developer.apple.com/programs/', 'https://play.google.com/console'],
|
|
514
|
+
promptTemplate: `Prepare app store submission materials:
|
|
515
|
+
|
|
516
|
+
1. Generate app screenshots for required sizes
|
|
517
|
+
2. Write app store description (short and long)
|
|
518
|
+
3. Create app preview video (optional but recommended)
|
|
519
|
+
4. Prepare answers for review questions (data usage, permissions)
|
|
520
|
+
5. Ensure privacy policy URL is live and accessible
|
|
521
|
+
|
|
522
|
+
Check that the app follows platform guidelines before submission.`,
|
|
523
|
+
condition: (p) => p.targetAudience.includes('mobile'),
|
|
524
|
+
},
|
|
525
|
+
COPPA_COMPLIANCE: {
|
|
526
|
+
key: 'COPPA_COMPLIANCE',
|
|
527
|
+
category: 'Compliance',
|
|
528
|
+
tier: 'manual',
|
|
529
|
+
headline: 'COPPA compliance required',
|
|
530
|
+
explanation: 'Users under 13 require parental consent and special data handling.',
|
|
531
|
+
priority: 'critical',
|
|
532
|
+
humanSteps: [
|
|
533
|
+
'Implement age gate / verification',
|
|
534
|
+
'Get verifiable parental consent mechanism',
|
|
535
|
+
'Limit data collection for children',
|
|
536
|
+
'Review with lawyer specializing in children\'s privacy',
|
|
537
|
+
],
|
|
538
|
+
externalLinks: ['https://www.ftc.gov/business-guidance/resources/complying-coppa-frequently-asked-questions'],
|
|
539
|
+
alsoNeeded: ['Legal review', 'Parental consent mechanism'],
|
|
540
|
+
promptTemplate: `Implement COPPA-compliant age verification:
|
|
541
|
+
|
|
542
|
+
1. Add age gate before registration
|
|
543
|
+
2. If under 13, collect parent email
|
|
544
|
+
3. Send parental consent request
|
|
545
|
+
4. Only allow account creation after consent verified
|
|
546
|
+
5. Limit data collection for child accounts
|
|
547
|
+
6. Add easy way for parents to review/delete child data
|
|
548
|
+
|
|
549
|
+
This requires careful legal review - the FTC enforces COPPA strictly.`,
|
|
550
|
+
condition: (p) => p.hasUnder13Users,
|
|
551
|
+
},
|
|
552
|
+
SOC2: {
|
|
553
|
+
key: 'SOC2',
|
|
554
|
+
category: 'Certification',
|
|
555
|
+
tier: 'manual',
|
|
556
|
+
headline: 'Enterprise customers may require SOC 2',
|
|
557
|
+
explanation: 'B2B/enterprise sales often require SOC 2 certification to prove security practices.',
|
|
558
|
+
priority: 'medium',
|
|
559
|
+
humanSteps: [
|
|
560
|
+
'This is a 6-12 month process costing $20K-100K+',
|
|
561
|
+
'Choose a SOC 2 auditor (Vanta, Drata can help automate)',
|
|
562
|
+
'Implement required controls',
|
|
563
|
+
'Undergo Type I then Type II audit',
|
|
564
|
+
],
|
|
565
|
+
externalLinks: ['https://vanta.com', 'https://drata.com'],
|
|
566
|
+
promptTemplate: `SOC 2 is a certification process, not code.
|
|
567
|
+
|
|
568
|
+
However, you can prepare by implementing:
|
|
569
|
+
1. Access control (role-based permissions, MFA)
|
|
570
|
+
2. Audit logging (who did what, when)
|
|
571
|
+
3. Encryption at rest and in transit
|
|
572
|
+
4. Incident response procedures
|
|
573
|
+
5. Vendor management documentation
|
|
574
|
+
|
|
575
|
+
Create a security checklist: docs/security-checklist.md`,
|
|
576
|
+
condition: (p) => p.targetAudience.includes('enterprise'),
|
|
577
|
+
},
|
|
578
|
+
// ⚠️ ASSISTABLE - Industry-specific regulations
|
|
579
|
+
HIPAA_COMPLIANCE: {
|
|
580
|
+
key: 'HIPAA_COMPLIANCE',
|
|
581
|
+
category: 'Healthcare',
|
|
582
|
+
tier: 'ai_assisted',
|
|
583
|
+
headline: 'Healthcare data requires HIPAA compliance',
|
|
584
|
+
explanation: 'Handling patient health information in the US requires HIPAA compliance. Violations can cost $100-50K per record.',
|
|
585
|
+
priority: 'critical',
|
|
586
|
+
alsoNeeded: ['Legal review of BAA', 'Security audit', 'Employee HIPAA training'],
|
|
587
|
+
promptTemplate: `Read docs/brainlift.md and docs/prd.md. This is a healthcare application that may need HIPAA compliance.
|
|
588
|
+
|
|
589
|
+
Create docs/hipaa-checklist.md covering:
|
|
590
|
+
1. PHI (Protected Health Information) inventory - what health data do we handle?
|
|
591
|
+
2. Access controls - minimum necessary access
|
|
592
|
+
3. Audit logging - who accessed what PHI, when
|
|
593
|
+
4. Encryption - at rest and in transit
|
|
594
|
+
5. BAA requirements - list of vendors needing Business Associate Agreements
|
|
595
|
+
6. Incident response - breach notification within 60 days
|
|
596
|
+
|
|
597
|
+
Add warning: "This checklist requires review by a HIPAA compliance officer or healthcare attorney"`,
|
|
598
|
+
condition: (p) => p.industry.includes('healthcare') && p.collectsUserData,
|
|
599
|
+
},
|
|
600
|
+
FERPA_COMPLIANCE: {
|
|
601
|
+
key: 'FERPA_COMPLIANCE',
|
|
602
|
+
category: 'Education',
|
|
603
|
+
tier: 'ai_assisted',
|
|
604
|
+
headline: 'Education records require FERPA compliance',
|
|
605
|
+
explanation: 'Student education records in US schools are protected by FERPA. Violations can result in loss of federal funding.',
|
|
606
|
+
priority: 'critical',
|
|
607
|
+
alsoNeeded: ['School admin approval', 'Parent consent process', 'Annual notification'],
|
|
608
|
+
promptTemplate: `Read docs/brainlift.md and docs/prd.md. This is an education application that may need FERPA compliance.
|
|
609
|
+
|
|
610
|
+
Create docs/ferpa-checklist.md covering:
|
|
611
|
+
1. Education records inventory - what student records do we access/store?
|
|
612
|
+
2. Consent requirements - when do we need parent/student consent?
|
|
613
|
+
3. Directory information policy - what can be disclosed without consent?
|
|
614
|
+
4. Access controls - only authorized school officials
|
|
615
|
+
5. Record keeping - maintain log of disclosures
|
|
616
|
+
6. Annual notification - how schools notify parents/students
|
|
617
|
+
|
|
618
|
+
Add warning: "This checklist requires review by school legal counsel"`,
|
|
619
|
+
condition: (p) => p.industry.includes('education') && p.collectsUserData,
|
|
620
|
+
},
|
|
621
|
+
EU_AI_ACT: {
|
|
622
|
+
key: 'EU_AI_ACT',
|
|
623
|
+
category: 'AI Regulation',
|
|
624
|
+
tier: 'ai_assisted',
|
|
625
|
+
headline: 'EU AI Act may apply to your AI system',
|
|
626
|
+
explanation: 'The EU AI Act regulates AI systems by risk level. High-risk AI (health, education, employment) has strict requirements.',
|
|
627
|
+
priority: 'high',
|
|
628
|
+
alsoNeeded: ['Risk classification assessment', 'Technical documentation', 'EU representative if non-EU company'],
|
|
629
|
+
promptTemplate: `Read docs/brainlift.md and docs/prd.md. This AI system may be subject to the EU AI Act.
|
|
630
|
+
|
|
631
|
+
Create docs/eu-ai-act-assessment.md covering:
|
|
632
|
+
1. AI use case classification - what does the AI decide or recommend?
|
|
633
|
+
2. Risk level assessment:
|
|
634
|
+
- Unacceptable (banned): social scoring, subliminal manipulation
|
|
635
|
+
- High-risk: education, employment, credit, healthcare, law enforcement
|
|
636
|
+
- Limited risk: chatbots (requires transparency)
|
|
637
|
+
- Minimal risk: spam filters, games
|
|
638
|
+
3. If high-risk, document:
|
|
639
|
+
- Data governance requirements
|
|
640
|
+
- Technical documentation
|
|
641
|
+
- Record keeping
|
|
642
|
+
- Human oversight mechanisms
|
|
643
|
+
- Accuracy, robustness, cybersecurity
|
|
644
|
+
|
|
645
|
+
Add warning: "This requires legal review for final classification"`,
|
|
646
|
+
condition: (p) => p.usesAI && (p.targetsEU || p.industry.some(i => ['healthcare', 'education', 'finance'].includes(i))),
|
|
647
|
+
},
|
|
648
|
+
SBOM: {
|
|
649
|
+
key: 'SBOM',
|
|
650
|
+
category: 'Supply Chain',
|
|
651
|
+
tier: 'ai_assisted',
|
|
652
|
+
headline: 'Generate a Software Bill of Materials (SBOM)',
|
|
653
|
+
explanation: 'An SBOM lists all dependencies in your software. Required by US Executive Order 14028 for government contractors, increasingly expected by enterprise customers.',
|
|
654
|
+
priority: 'medium',
|
|
655
|
+
promptTemplate: `Generate a Software Bill of Materials (SBOM) for this project.
|
|
656
|
+
|
|
657
|
+
Run these commands to generate the SBOM:
|
|
658
|
+
1. For npm: npx @cyclonedx/cyclonedx-npm --output-file sbom.json
|
|
659
|
+
2. Or use: npm sbom --sbom-format cyclonedx
|
|
660
|
+
|
|
661
|
+
Then create docs/sbom-readme.md explaining:
|
|
662
|
+
- What the SBOM contains
|
|
663
|
+
- How to regenerate it
|
|
664
|
+
- When to update it (after dependency changes)
|
|
665
|
+
- License summary of all dependencies
|
|
666
|
+
|
|
667
|
+
Add the sbom.json generation to CI/CD pipeline.`,
|
|
668
|
+
condition: (p) => p.targetAudience.includes('enterprise') || p.industry.includes('finance'),
|
|
669
|
+
},
|
|
670
|
+
DATA_RESIDENCY: {
|
|
671
|
+
key: 'DATA_RESIDENCY',
|
|
672
|
+
category: 'Compliance',
|
|
673
|
+
tier: 'ai_assisted',
|
|
674
|
+
headline: 'Document data residency requirements',
|
|
675
|
+
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.',
|
|
676
|
+
priority: 'high',
|
|
677
|
+
alsoNeeded: ['Legal review of data transfer agreements', 'Cloud provider region verification'],
|
|
678
|
+
promptTemplate: `Read docs/brainlift.md and docs/prd.md. Create a data residency documentation.
|
|
679
|
+
|
|
680
|
+
Create docs/data-residency.md covering:
|
|
681
|
+
1. Where is user data stored? (AWS region, GCP zone, etc.)
|
|
682
|
+
2. Does data cross borders? (US ↔ EU, etc.)
|
|
683
|
+
3. What legal basis for cross-border transfers? (SCCs, adequacy decisions)
|
|
684
|
+
4. Can customers choose data region? (for enterprise)
|
|
685
|
+
5. Where are backups stored?
|
|
686
|
+
6. Third-party services and their data locations (Stripe, analytics, etc.)
|
|
687
|
+
|
|
688
|
+
Add warning: "Verify regions with your cloud provider dashboard"`,
|
|
689
|
+
condition: (p) => p.targetsEU || p.targetAudience.includes('enterprise'),
|
|
690
|
+
},
|
|
691
|
+
};
|
|
692
|
+
// ============================================================================
|
|
693
|
+
// INFERENCE FUNCTIONS
|
|
694
|
+
// ============================================================================
|
|
695
|
+
/**
|
|
696
|
+
* Infer project profile from brainlift, PRD, and package.json
|
|
697
|
+
*/
|
|
698
|
+
export function inferProjectProfile(projectPath) {
|
|
699
|
+
const safePath = sanitizePath(projectPath);
|
|
700
|
+
const profile = {
|
|
701
|
+
collectsUserData: false,
|
|
702
|
+
collectsSensitiveData: false,
|
|
703
|
+
hasUnder13Users: false,
|
|
704
|
+
targetsEU: false,
|
|
705
|
+
targetsCalifornia: false,
|
|
706
|
+
hasPayments: false,
|
|
707
|
+
hasSubscriptions: false,
|
|
708
|
+
hasUserContent: false,
|
|
709
|
+
usesAI: false,
|
|
710
|
+
aiMakesDecisions: false,
|
|
711
|
+
isOpenSource: false,
|
|
712
|
+
targetAudience: [],
|
|
713
|
+
businessModel: 'free',
|
|
714
|
+
industry: [],
|
|
715
|
+
};
|
|
716
|
+
// Discover and read all planning docs (intelligent detection)
|
|
717
|
+
const docsResult = discoverDocsSync(safePath);
|
|
718
|
+
let content = getPlanningContext(docsResult).toLowerCase();
|
|
719
|
+
// Also check README if not already included
|
|
720
|
+
const readmePath = join(safePath, 'README.md');
|
|
721
|
+
const packagePath = join(safePath, 'package.json');
|
|
722
|
+
if (existsSync(readmePath) && !docsResult.readme) {
|
|
723
|
+
content += readFileSync(readmePath, 'utf-8').toLowerCase() + '\n';
|
|
724
|
+
}
|
|
725
|
+
// Package.json analysis
|
|
726
|
+
if (existsSync(packagePath)) {
|
|
727
|
+
try {
|
|
728
|
+
const pkg = JSON.parse(readFileSync(packagePath, 'utf-8'));
|
|
729
|
+
const deps = Object.keys(pkg.dependencies || {}).join(' ').toLowerCase();
|
|
730
|
+
const allContent = JSON.stringify(pkg).toLowerCase();
|
|
731
|
+
// Check for payment libraries
|
|
732
|
+
if (deps.includes('stripe') || deps.includes('paypal') || deps.includes('paddle')) {
|
|
733
|
+
profile.hasPayments = true;
|
|
734
|
+
}
|
|
735
|
+
// Check for AI libraries
|
|
736
|
+
if (deps.includes('openai') || deps.includes('anthropic') || deps.includes('langchain') || deps.includes('ai')) {
|
|
737
|
+
profile.usesAI = true;
|
|
738
|
+
}
|
|
739
|
+
// Check for auth (implies user data)
|
|
740
|
+
if (deps.includes('next-auth') || deps.includes('passport') || deps.includes('clerk') || deps.includes('auth0') || deps.includes('firebase')) {
|
|
741
|
+
profile.collectsUserData = true;
|
|
742
|
+
}
|
|
743
|
+
// Open source check
|
|
744
|
+
if (pkg.license && pkg.license !== 'UNLICENSED' && pkg.license !== 'proprietary') {
|
|
745
|
+
profile.isOpenSource = true;
|
|
746
|
+
}
|
|
747
|
+
content += allContent + '\n';
|
|
748
|
+
}
|
|
749
|
+
catch { /* ignore parse errors */ }
|
|
750
|
+
}
|
|
751
|
+
// CONSERVATIVE keyword analysis
|
|
752
|
+
// Only trigger on explicit mentions, NOT on broad terms like "global" or "international"
|
|
753
|
+
// AI catch-all will handle nuanced cases
|
|
754
|
+
const keywords = {
|
|
755
|
+
// User data - conservative: must be clear user accounts
|
|
756
|
+
collectsUserData: ['user account', 'login', 'signup', 'sign up', 'register', 'authentication', 'auth'],
|
|
757
|
+
collectsSensitiveData: ['health', 'medical', 'hipaa', 'financial', 'bank account', 'credit card', 'ssn', 'social security', 'biometric', 'fingerprint', 'face id'],
|
|
758
|
+
hasUnder13Users: ['kids', 'children', 'child', 'k-12', 'elementary', 'middle school', 'under 13', 'coppa', 'parental consent'],
|
|
759
|
+
// Geography - VERY conservative: only explicit mentions, not "global"
|
|
760
|
+
targetsEU: ['eu', 'europe', 'european union', 'gdpr', 'germany', 'france', 'spain', 'italy', 'netherlands', 'uk users'],
|
|
761
|
+
targetsCalifornia: ['california', 'ccpa', 'california users'],
|
|
762
|
+
// Business - clear signals only
|
|
763
|
+
hasPayments: ['payment', 'stripe', 'paypal', 'billing', 'checkout', 'purchase', 'monetize', 'pricing page'],
|
|
764
|
+
hasSubscriptions: ['subscription', 'monthly plan', 'yearly plan', 'recurring billing', 'saas'],
|
|
765
|
+
hasUserContent: ['upload', 'user generated', 'ugc', 'user posts', 'comments section', 'community content'],
|
|
766
|
+
// AI - detect AI usage with common patterns
|
|
767
|
+
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'],
|
|
768
|
+
aiMakesDecisions: ['ai decides', 'ai recommends', 'automated decision', 'algorithm determines', 'ai-driven', 'ai generates', 'ai creates'],
|
|
769
|
+
};
|
|
770
|
+
for (const [key, terms] of Object.entries(keywords)) {
|
|
771
|
+
if (terms.some(term => content.includes(term))) {
|
|
772
|
+
profile[key] = true;
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
// Target audience inference
|
|
776
|
+
if (content.includes('student') || content.includes('education') || content.includes('learn')) {
|
|
777
|
+
profile.targetAudience.push('students');
|
|
778
|
+
}
|
|
779
|
+
if (content.includes('enterprise') || content.includes('b2b') || content.includes('business') || content.includes('team')) {
|
|
780
|
+
profile.targetAudience.push('enterprise');
|
|
781
|
+
}
|
|
782
|
+
if (content.includes('developer') || content.includes('api') || content.includes('sdk')) {
|
|
783
|
+
profile.targetAudience.push('developers');
|
|
784
|
+
}
|
|
785
|
+
if (content.includes('mobile') || content.includes('ios') || content.includes('android') || content.includes('app store')) {
|
|
786
|
+
profile.targetAudience.push('mobile');
|
|
787
|
+
}
|
|
788
|
+
// Business model inference
|
|
789
|
+
if (content.includes('free') && !content.includes('freemium') && !content.includes('premium')) {
|
|
790
|
+
profile.businessModel = 'free';
|
|
791
|
+
}
|
|
792
|
+
else if (content.includes('freemium') || (content.includes('free') && content.includes('premium'))) {
|
|
793
|
+
profile.businessModel = 'freemium';
|
|
794
|
+
}
|
|
795
|
+
else if (content.includes('subscription') || content.includes('saas')) {
|
|
796
|
+
profile.businessModel = 'subscription';
|
|
797
|
+
}
|
|
798
|
+
else if (content.includes('enterprise') || content.includes('b2b')) {
|
|
799
|
+
profile.businessModel = 'b2b';
|
|
800
|
+
}
|
|
801
|
+
else if (profile.hasPayments) {
|
|
802
|
+
profile.businessModel = 'paid';
|
|
803
|
+
}
|
|
804
|
+
// Industry inference
|
|
805
|
+
if (content.includes('health') || content.includes('medical') || content.includes('patient')) {
|
|
806
|
+
profile.industry.push('healthcare');
|
|
807
|
+
}
|
|
808
|
+
if (content.includes('finance') || content.includes('banking') || content.includes('invest')) {
|
|
809
|
+
profile.industry.push('finance');
|
|
810
|
+
}
|
|
811
|
+
if (content.includes('education') || content.includes('school') || content.includes('course')) {
|
|
812
|
+
profile.industry.push('education');
|
|
813
|
+
}
|
|
814
|
+
return profile;
|
|
815
|
+
}
|
|
816
|
+
/**
|
|
817
|
+
* Generate context-aware prompt by filling in template variables
|
|
818
|
+
*/
|
|
819
|
+
function fillPromptTemplate(template, profile, projectPath) {
|
|
820
|
+
const safePath = sanitizePath(projectPath);
|
|
821
|
+
// Use intelligent docs discovery
|
|
822
|
+
const docsResult = discoverDocsSync(safePath);
|
|
823
|
+
const brainliftContent = docsResult.brainlift?.content || '';
|
|
824
|
+
const prdContent = docsResult.prd?.content || '';
|
|
825
|
+
// Build replacements
|
|
826
|
+
const replacements = {
|
|
827
|
+
'{{dataCollected}}': profile.collectsSensitiveData
|
|
828
|
+
? 'user accounts, emails, and sensitive data (health/financial)'
|
|
829
|
+
: profile.collectsUserData
|
|
830
|
+
? 'user accounts, emails, preferences'
|
|
831
|
+
: 'minimal data',
|
|
832
|
+
'{{targetUsers}}': profile.targetAudience.length > 0
|
|
833
|
+
? profile.targetAudience.join(', ')
|
|
834
|
+
: 'general users',
|
|
835
|
+
'{{businessModel}}': profile.businessModel,
|
|
836
|
+
'{{productType}}': profile.usesAI ? 'AI-powered application' : 'web application',
|
|
837
|
+
'{{keyFeatures}}': brainliftContent.slice(0, 200) || 'See brainlift for details',
|
|
838
|
+
'{{aiUsage}}': profile.usesAI ? 'AI features (see brainlift for specifics)' : 'No AI',
|
|
839
|
+
'{{processingPurposes}}': 'account management, service delivery, analytics',
|
|
840
|
+
'{{thirdParties}}': profile.hasPayments ? 'Stripe for payments, analytics provider' : 'analytics provider',
|
|
841
|
+
'{{subscriptionType}}': profile.hasSubscriptions ? 'recurring subscription' : 'one-time purchase',
|
|
842
|
+
'{{userContentType}}': 'create and share content',
|
|
843
|
+
'{{decisionsAffected}}': 'recommendations, personalization',
|
|
844
|
+
'{{pricing}}': 'See PRD for pricing details',
|
|
845
|
+
'{{checkoutType}}': profile.hasSubscriptions ? 'subscription' : 'one-time payment',
|
|
846
|
+
};
|
|
847
|
+
let result = template;
|
|
848
|
+
for (const [key, value] of Object.entries(replacements)) {
|
|
849
|
+
result = result.replace(new RegExp(key.replace(/[{}]/g, '\\$&'), 'g'), value);
|
|
850
|
+
}
|
|
851
|
+
return result;
|
|
852
|
+
}
|
|
853
|
+
/**
|
|
854
|
+
* Get all applicable preflight checks for a project
|
|
855
|
+
*/
|
|
856
|
+
export function getPreflightChecks(projectPath) {
|
|
857
|
+
const safePath = sanitizePath(projectPath);
|
|
858
|
+
// Auto-detect generated docs and mark checks complete (feedback loop)
|
|
859
|
+
detectGeneratedDocs(safePath);
|
|
860
|
+
const profile = inferProjectProfile(safePath);
|
|
861
|
+
const checks = [];
|
|
862
|
+
// Load persisted state (after detection so it includes auto-completions)
|
|
863
|
+
const persistedState = loadPreflightState(safePath);
|
|
864
|
+
const checkStates = persistedState.checkStates;
|
|
865
|
+
for (const check of Object.values(PREFLIGHT_CHECKS)) {
|
|
866
|
+
if (check.condition(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';
|
|
873
|
+
checks.push({
|
|
874
|
+
key: check.key,
|
|
875
|
+
category: check.category,
|
|
876
|
+
tier: check.tier,
|
|
877
|
+
headline: check.headline,
|
|
878
|
+
explanation: check.explanation,
|
|
879
|
+
cursorPrompt,
|
|
880
|
+
humanSteps: check.humanSteps,
|
|
881
|
+
externalLinks: check.externalLinks,
|
|
882
|
+
alsoNeeded: check.alsoNeeded,
|
|
883
|
+
priority: check.priority,
|
|
884
|
+
triggeredBy,
|
|
885
|
+
// Add persisted status
|
|
886
|
+
status: persisted?.status || 'pending',
|
|
887
|
+
statusUpdatedAt: persisted?.updatedAt,
|
|
888
|
+
skippedReason: persisted?.skippedReason,
|
|
889
|
+
});
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
// Sort by priority and tier
|
|
893
|
+
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
894
|
+
const tierOrder = { manual: 0, ai_assisted: 1 };
|
|
895
|
+
checks.sort((a, b) => {
|
|
896
|
+
const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
897
|
+
if (priorityDiff !== 0)
|
|
898
|
+
return priorityDiff;
|
|
899
|
+
return tierOrder[a.tier] - tierOrder[b.tier];
|
|
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
|
+
savePreflightState(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
|
+
}
|
|
915
|
+
return {
|
|
916
|
+
profile,
|
|
917
|
+
checks: displayChecks,
|
|
918
|
+
summary: {
|
|
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,
|
|
926
|
+
},
|
|
927
|
+
totalAvailable,
|
|
928
|
+
isFirstSession,
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
// ============================================================================
|
|
932
|
+
// AI CATCH-ALL FILTER
|
|
933
|
+
// ============================================================================
|
|
934
|
+
/**
|
|
935
|
+
* AI-powered preflight 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
|
+
// Use intelligent docs discovery
|
|
955
|
+
const docsResult = discoverDocsSync(safePath);
|
|
956
|
+
const docsContent = getPlanningContext(docsResult);
|
|
957
|
+
if (!docsContent || docsResult.totalDocsFound === 0) {
|
|
958
|
+
// No docs to analyze - return checks as-is
|
|
959
|
+
return { filtered: checks, additions: [], removals: [] };
|
|
960
|
+
}
|
|
961
|
+
// Combined profile validation + check filtering in a single focused prompt
|
|
962
|
+
const systemPrompt = `Compliance check filter. Given project docs and proposed checks, return JSON only:
|
|
963
|
+
{"keep":["KEY",...], "remove":["KEY",...], "add":["KEY",...]}
|
|
964
|
+
Rules: Remove checks that clearly don't apply. Add missing checks from available list. Be conservative.`;
|
|
965
|
+
// Compact prompt - less tokens, faster response
|
|
966
|
+
const proposed = checks.map(c => c.key).join(',');
|
|
967
|
+
const available = Object.keys(PREFLIGHT_CHECKS).filter(k => !checks.some(c => c.key === k)).join(',');
|
|
968
|
+
const prompt = `Docs:\n${docsContent}\n\nProposed: ${proposed}\nAvailable: ${available}\n\nProfile: ${profile.businessModel}, EU:${profile.targetsEU}, AI:${profile.usesAI}, payments:${profile.hasPayments}`;
|
|
969
|
+
try {
|
|
970
|
+
const response = await chat(prompt, {
|
|
971
|
+
systemPrompt,
|
|
972
|
+
maxTokens: 1000,
|
|
973
|
+
useThinking: false, // Fast response
|
|
974
|
+
timeout: 15000, // Quick timeout
|
|
975
|
+
});
|
|
976
|
+
// Parse response
|
|
977
|
+
let jsonStr = response.content;
|
|
978
|
+
if (response.content.includes('```')) {
|
|
979
|
+
const match = response.content.match(/```(?:json)?\s*([\s\S]*?)```/);
|
|
980
|
+
if (match)
|
|
981
|
+
jsonStr = match[1];
|
|
982
|
+
}
|
|
983
|
+
const result = JSON.parse(jsonStr.trim());
|
|
984
|
+
// Filter checks based on AI response
|
|
985
|
+
const keepSet = new Set(result.keep || []);
|
|
986
|
+
const removeSet = new Set(result.remove || []);
|
|
987
|
+
const addKeys = result.add || [];
|
|
988
|
+
// Apply filtering
|
|
989
|
+
let filtered = checks.filter(c => {
|
|
990
|
+
// If explicitly removed, remove it
|
|
991
|
+
if (removeSet.has(c.key))
|
|
992
|
+
return false;
|
|
993
|
+
// If explicitly kept or not mentioned, keep it (conservative)
|
|
994
|
+
return true;
|
|
995
|
+
});
|
|
996
|
+
// Add any checks AI says we're missing
|
|
997
|
+
const safePath = sanitizePath(projectPath);
|
|
998
|
+
const persistedState = loadPreflightState(safePath);
|
|
999
|
+
for (const key of addKeys) {
|
|
1000
|
+
if (PREFLIGHT_CHECKS[key] && !filtered.some(c => c.key === key)) {
|
|
1001
|
+
const check = PREFLIGHT_CHECKS[key];
|
|
1002
|
+
const cursorPrompt = fillPromptTemplate(check.promptTemplate, profile, projectPath);
|
|
1003
|
+
const persisted = persistedState.checkStates[key];
|
|
1004
|
+
// Get triggered-by reason (AI-added checks)
|
|
1005
|
+
const triggeredBy = check.getTriggeredBy
|
|
1006
|
+
? check.getTriggeredBy(profile)
|
|
1007
|
+
: 'Added by AI analysis of project context';
|
|
1008
|
+
filtered.push({
|
|
1009
|
+
key: check.key,
|
|
1010
|
+
category: check.category,
|
|
1011
|
+
tier: check.tier,
|
|
1012
|
+
headline: check.headline,
|
|
1013
|
+
explanation: check.explanation,
|
|
1014
|
+
cursorPrompt,
|
|
1015
|
+
humanSteps: check.humanSteps,
|
|
1016
|
+
externalLinks: check.externalLinks,
|
|
1017
|
+
alsoNeeded: check.alsoNeeded,
|
|
1018
|
+
priority: check.priority,
|
|
1019
|
+
triggeredBy,
|
|
1020
|
+
status: persisted?.status || 'pending',
|
|
1021
|
+
statusUpdatedAt: persisted?.updatedAt,
|
|
1022
|
+
skippedReason: persisted?.skippedReason,
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
// Re-sort
|
|
1027
|
+
const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
|
|
1028
|
+
const tierOrder = { manual: 0, ai_assisted: 1 };
|
|
1029
|
+
filtered.sort((a, b) => {
|
|
1030
|
+
const priorityDiff = priorityOrder[a.priority] - priorityOrder[b.priority];
|
|
1031
|
+
if (priorityDiff !== 0)
|
|
1032
|
+
return priorityDiff;
|
|
1033
|
+
return tierOrder[a.tier] - tierOrder[b.tier];
|
|
1034
|
+
});
|
|
1035
|
+
return {
|
|
1036
|
+
filtered,
|
|
1037
|
+
additions: addKeys,
|
|
1038
|
+
removals: Array.from(removeSet),
|
|
1039
|
+
};
|
|
1040
|
+
}
|
|
1041
|
+
catch (error) {
|
|
1042
|
+
// AI failed - return original checks (conservative fallback)
|
|
1043
|
+
return { filtered: checks, additions: [], removals: [] };
|
|
1044
|
+
}
|
|
1045
|
+
}
|
|
1046
|
+
/**
|
|
1047
|
+
* Get preflight checks with AI filtering (async version)
|
|
1048
|
+
* Use this when you want the most accurate checks
|
|
1049
|
+
*/
|
|
1050
|
+
export async function getPreflightChecksWithAI(projectPath) {
|
|
1051
|
+
const basic = getPreflightChecks(projectPath);
|
|
1052
|
+
try {
|
|
1053
|
+
const { filtered, additions, removals } = await filterChecksWithAI(basic.profile, basic.checks, projectPath);
|
|
1054
|
+
return {
|
|
1055
|
+
profile: basic.profile,
|
|
1056
|
+
checks: filtered,
|
|
1057
|
+
summary: {
|
|
1058
|
+
total: filtered.length,
|
|
1059
|
+
critical: filtered.filter(c => c.priority === 'critical').length,
|
|
1060
|
+
aiAssisted: filtered.filter(c => c.tier === 'ai_assisted').length,
|
|
1061
|
+
manual: filtered.filter(c => c.tier === 'manual').length,
|
|
1062
|
+
pending: filtered.filter(c => c.status === 'pending').length,
|
|
1063
|
+
completed: filtered.filter(c => c.status === 'completed').length,
|
|
1064
|
+
skipped: filtered.filter(c => c.status === 'skipped').length,
|
|
1065
|
+
},
|
|
1066
|
+
aiFiltered: additions.length > 0 || removals.length > 0,
|
|
1067
|
+
};
|
|
1068
|
+
}
|
|
1069
|
+
catch {
|
|
1070
|
+
// Fallback to basic checks
|
|
1071
|
+
return { ...basic, aiFiltered: false };
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* Get tier symbol for display
|
|
1076
|
+
*/
|
|
1077
|
+
export function getTierSymbol(tier) {
|
|
1078
|
+
const mapped = TIER_MAPPING[tier] || tier;
|
|
1079
|
+
switch (mapped) {
|
|
1080
|
+
case 'ai_assisted': return '🤖';
|
|
1081
|
+
case 'manual': return '👤';
|
|
1082
|
+
default: return '❓';
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
/**
|
|
1086
|
+
* Get tier description
|
|
1087
|
+
*/
|
|
1088
|
+
export function getTierDescription(tier) {
|
|
1089
|
+
const mapped = TIER_MAPPING[tier] || tier;
|
|
1090
|
+
switch (mapped) {
|
|
1091
|
+
case 'ai_assisted': return 'AI can help with this';
|
|
1092
|
+
case 'manual': return 'You need to do this yourself';
|
|
1093
|
+
default: return 'Unknown tier';
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
// Function aliases for backward compatibility
|
|
1097
|
+
export const getRealityChecks = getPreflightChecks;
|
|
1098
|
+
export const getRealityChecksWithAI = getPreflightChecksWithAI;
|
|
1099
|
+
export const loadRealityState = loadPreflightState;
|
|
1100
|
+
//# sourceMappingURL=preflight.js.map
|