@equilateral_ai/mindmeld 3.5.2 → 4.0.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.
Files changed (140) hide show
  1. package/hooks/session-end.js +25 -0
  2. package/hooks/session-start.js +363 -83
  3. package/hooks/session-watcher.js +585 -0
  4. package/package.json +19 -13
  5. package/scripts/init-project.js +9 -23
  6. package/src/client/dbShim.js +16 -0
  7. package/src/core/AuthManager.js +3 -2
  8. package/src/handlers/helpers/dbOperations.js +9 -46
  9. package/src/index.js +2 -217
  10. package/src/utils/piiMask.js +16 -0
  11. package/scripts/harvest.js +0 -601
  12. package/scripts/inject.js +0 -409
  13. package/scripts/mcp-bridge.js +0 -220
  14. package/scripts/repo-analyzer.js +0 -870
  15. package/src/collaboration/CollaborationPrompt.js +0 -460
  16. package/src/core/AlertEngine.js +0 -813
  17. package/src/core/AlertNotifier.js +0 -363
  18. package/src/core/CorrelationAnalyzer.js +0 -931
  19. package/src/core/CrossReferenceEngine.js +0 -624
  20. package/src/core/CurationEngine.js +0 -688
  21. package/src/core/DeprecationScheduler.js +0 -183
  22. package/src/core/LoadBearingDetector.js +0 -242
  23. package/src/core/NotificationService.js +0 -1032
  24. package/src/core/RapportOrchestrator.js +0 -632
  25. package/src/core/RelevanceDetector.js +0 -694
  26. package/src/core/StandardLifecycle.js +0 -244
  27. package/src/core/StandardsIngestion.js +0 -991
  28. package/src/core/TeamLoadBearingDetector.js +0 -431
  29. package/src/core/parsers/adrParser.js +0 -479
  30. package/src/core/parsers/cursorRulesParser.js +0 -564
  31. package/src/core/parsers/eslintParser.js +0 -439
  32. package/src/database/dbOperations.js +0 -105
  33. package/src/handlers/activity/activityGetMe.js +0 -98
  34. package/src/handlers/activity/activityGetTeam.js +0 -175
  35. package/src/handlers/admin/adminSetup.js +0 -216
  36. package/src/handlers/alerts/alertsAcknowledge.js +0 -92
  37. package/src/handlers/alerts/alertsGet.js +0 -250
  38. package/src/handlers/analytics/activitySummaryGet.js +0 -234
  39. package/src/handlers/analytics/coachingGet.js +0 -361
  40. package/src/handlers/analytics/convergenceGet.js +0 -236
  41. package/src/handlers/analytics/developerScoreGet.js +0 -137
  42. package/src/handlers/collaborators/collaboratorAdd.js +0 -200
  43. package/src/handlers/collaborators/collaboratorInvite.js +0 -219
  44. package/src/handlers/collaborators/collaboratorList.js +0 -82
  45. package/src/handlers/collaborators/collaboratorRemove.js +0 -128
  46. package/src/handlers/collaborators/inviteAccept.js +0 -122
  47. package/src/handlers/company/companyUsersDelete.js +0 -141
  48. package/src/handlers/company/companyUsersGet.js +0 -90
  49. package/src/handlers/company/companyUsersPost.js +0 -267
  50. package/src/handlers/company/companyUsersPut.js +0 -76
  51. package/src/handlers/context/contextGet.js +0 -57
  52. package/src/handlers/context/invariantsGet.js +0 -74
  53. package/src/handlers/context/loopsGet.js +0 -82
  54. package/src/handlers/context/notesCreate.js +0 -74
  55. package/src/handlers/context/purposeGet.js +0 -78
  56. package/src/handlers/correlations/correlationsDeveloperGet.js +0 -227
  57. package/src/handlers/correlations/correlationsGet.js +0 -93
  58. package/src/handlers/correlations/correlationsProjectGet.js +0 -153
  59. package/src/handlers/enterprise/controlTowerGet.js +0 -224
  60. package/src/handlers/enterprise/enterpriseAuditGet.js +0 -108
  61. package/src/handlers/enterprise/enterpriseContributorsGet.js +0 -85
  62. package/src/handlers/enterprise/enterpriseKnowledgeCategoriesGet.js +0 -53
  63. package/src/handlers/enterprise/enterpriseKnowledgeCreate.js +0 -77
  64. package/src/handlers/enterprise/enterpriseKnowledgeDelete.js +0 -71
  65. package/src/handlers/enterprise/enterpriseKnowledgeGet.js +0 -87
  66. package/src/handlers/enterprise/enterpriseKnowledgeUpdate.js +0 -122
  67. package/src/handlers/enterprise/enterpriseOnboardingComplete.js +0 -77
  68. package/src/handlers/enterprise/enterpriseOnboardingInvite.js +0 -138
  69. package/src/handlers/enterprise/enterpriseOnboardingSetup.js +0 -128
  70. package/src/handlers/enterprise/enterpriseOnboardingStatus.js +0 -88
  71. package/src/handlers/github/githubConnectionStatus.js +0 -49
  72. package/src/handlers/github/githubDiscoverPatterns.js +0 -621
  73. package/src/handlers/github/githubOAuthCallback.js +0 -178
  74. package/src/handlers/github/githubOAuthStart.js +0 -59
  75. package/src/handlers/github/githubPatternsReview.js +0 -76
  76. package/src/handlers/github/githubReposList.js +0 -105
  77. package/src/handlers/health/healthGet.js +0 -55
  78. package/src/handlers/helpers/auditLogger.js +0 -201
  79. package/src/handlers/helpers/checkSuperAdmin.js +0 -84
  80. package/src/handlers/helpers/decisionFrames.js +0 -29
  81. package/src/handlers/helpers/errorHandler.js +0 -49
  82. package/src/handlers/helpers/index.js +0 -138
  83. package/src/handlers/helpers/lambdaWrapper.js +0 -60
  84. package/src/handlers/helpers/mindmeldMcpCore.js +0 -1103
  85. package/src/handlers/helpers/predictiveCache.js +0 -51
  86. package/src/handlers/helpers/projectAccess.js +0 -88
  87. package/src/handlers/helpers/responseUtil.js +0 -55
  88. package/src/handlers/helpers/subscriptionTiers.js +0 -1168
  89. package/src/handlers/mcp/mcpHandler.js +0 -569
  90. package/src/handlers/mcp/mindmeldMcpHandler.js +0 -124
  91. package/src/handlers/mcp/mindmeldMcpStreamHandler.js +0 -342
  92. package/src/handlers/notifications/getPreferences.js +0 -84
  93. package/src/handlers/notifications/sendNotification.js +0 -170
  94. package/src/handlers/notifications/updatePreferences.js +0 -316
  95. package/src/handlers/patterns/patternEvaluatePromotionPost.js +0 -173
  96. package/src/handlers/patterns/patternUsagePost.js +0 -182
  97. package/src/handlers/patterns/patternViolationPost.js +0 -185
  98. package/src/handlers/projects/projectCreate.js +0 -248
  99. package/src/handlers/projects/projectDelete.js +0 -82
  100. package/src/handlers/projects/projectGet.js +0 -95
  101. package/src/handlers/projects/projectUpdate.js +0 -117
  102. package/src/handlers/reports/aiLeverage.js +0 -210
  103. package/src/handlers/reports/engineeringInvestment.js +0 -132
  104. package/src/handlers/reports/riskForecast.js +0 -206
  105. package/src/handlers/reports/standardsRoi.js +0 -254
  106. package/src/handlers/scheduled/analyzeCorrelations.js +0 -178
  107. package/src/handlers/scheduled/analyzeGitHistory.js +0 -510
  108. package/src/handlers/scheduled/generateAlerts.js +0 -135
  109. package/src/handlers/scheduled/maturityUpdateJob.js +0 -166
  110. package/src/handlers/scheduled/refreshActivity.js +0 -21
  111. package/src/handlers/scheduled/scanCompliance.js +0 -334
  112. package/src/handlers/sessions/sessionEndPost.js +0 -180
  113. package/src/handlers/sessions/sessionStandardsPost.js +0 -171
  114. package/src/handlers/standards/catalogGet.js +0 -185
  115. package/src/handlers/standards/catalogSync.js +0 -120
  116. package/src/handlers/standards/discoveriesGet.js +0 -89
  117. package/src/handlers/standards/projectStandardsGet.js +0 -129
  118. package/src/handlers/standards/projectStandardsPut.js +0 -151
  119. package/src/handlers/standards/standardsAuditGet.js +0 -65
  120. package/src/handlers/standards/standardsParseUpload.js +0 -149
  121. package/src/handlers/standards/standardsRelevantPost.js +0 -405
  122. package/src/handlers/standards/standardsTransition.js +0 -161
  123. package/src/handlers/stripe/addonManagePost.js +0 -240
  124. package/src/handlers/stripe/billingPortalPost.js +0 -93
  125. package/src/handlers/stripe/enterpriseCheckoutPost.js +0 -272
  126. package/src/handlers/stripe/seatsUpdatePost.js +0 -185
  127. package/src/handlers/stripe/subscriptionCancelDelete.js +0 -169
  128. package/src/handlers/stripe/subscriptionCreatePost.js +0 -221
  129. package/src/handlers/stripe/subscriptionUpdatePut.js +0 -163
  130. package/src/handlers/stripe/webhookPost.js +0 -482
  131. package/src/handlers/user/apiTokenCreate.js +0 -71
  132. package/src/handlers/user/apiTokenList.js +0 -64
  133. package/src/handlers/user/userSplashAck.js +0 -91
  134. package/src/handlers/user/userSplashGet.js +0 -211
  135. package/src/handlers/users/cognitoPostConfirmation.js +0 -186
  136. package/src/handlers/users/cognitoPreSignUp.js +0 -114
  137. package/src/handlers/users/userEntitlementsGet.js +0 -89
  138. package/src/handlers/users/userGet.js +0 -118
  139. package/src/handlers/users/userProfilePut.js +0 -77
  140. package/src/handlers/webhooks/githubWebhook.js +0 -215
@@ -1,564 +0,0 @@
1
- /**
2
- * Cursor Rules Parser - Parse AI coding assistant rule files into YAML standards format
3
- *
4
- * Supports:
5
- * - .cursorrules (Cursor IDE)
6
- * - CLAUDE.md (Claude Code)
7
- * - .windsurfrules (Windsurf IDE)
8
- * - Generic markdown rule files
9
- *
10
- * Extracts:
11
- * - Rules from sections with action headers (always, never, avoid, prefer, use)
12
- * - Banned patterns from "banned" or prohibition sections
13
- * - Implementation patterns from code blocks
14
- * - Trigger words and conventions
15
- *
16
- * Returns YAML-compatible object matching equilateral-standards schema
17
- *
18
- * @module parsers/cursorRulesParser
19
- */
20
-
21
- /**
22
- * Section header patterns that indicate rule types
23
- */
24
- const SECTION_PATTERNS = {
25
- ALWAYS: /\b(always|must|required|mandatory|enforce|critical)\b/i,
26
- NEVER: /\b(never|banned|prohibited|forbidden|do not|don't)\b/i,
27
- AVOID: /\b(avoid|discourage|anti-pattern|bad practice|don't use)\b/i,
28
- PREFER: /\b(prefer|should|recommend|favor|better|best practice)\b/i,
29
- USE: /\b(use|adopt|follow|implement|pattern|convention|standard)\b/i
30
- };
31
-
32
- /**
33
- * Parse AI coding assistant rules file into YAML-compatible standards object
34
- *
35
- * @param {string} content - Raw markdown/text content of the rules file
36
- * @param {Object} options - Parser options
37
- * @param {string} options.filename - Source filename (e.g., '.cursorrules', 'CLAUDE.md')
38
- * @param {string} options.category - Standards category override
39
- * @returns {Object} YAML-compatible standards object
40
- */
41
- function parseCursorRules(content, options = {}) {
42
- if (!content || typeof content !== 'string') {
43
- throw new Error('Rules file content is required and must be a string');
44
- }
45
-
46
- const filename = options.filename || 'rules';
47
- const sourceFormat = detectSourceFormat(filename);
48
-
49
- const sections = parseSections(content);
50
- const rules = extractRules(sections, content);
51
- const antiPatterns = extractAntiPatterns(sections, content);
52
- const examples = extractCodeExamples(content);
53
- const category = options.category || inferCategory(content);
54
-
55
- const id = generateId(filename);
56
-
57
- return {
58
- id,
59
- category,
60
- priority: determinePriority(rules),
61
- rules,
62
- anti_patterns: antiPatterns,
63
- context: {
64
- source_format: sourceFormat,
65
- source_file: filename,
66
- examples,
67
- description: extractDescription(content)
68
- },
69
- tags: extractTags(content, sourceFormat),
70
- updated: new Date().toISOString().split('T')[0]
71
- };
72
- }
73
-
74
- /**
75
- * Detect the source format from filename
76
- *
77
- * @param {string} filename - Source filename
78
- * @returns {string} Detected format identifier
79
- */
80
- function detectSourceFormat(filename) {
81
- const lower = filename.toLowerCase();
82
- if (lower.includes('.cursorrules') || lower.includes('cursorrules')) return 'cursorrules';
83
- if (lower.includes('claude.md') || lower.includes('claude')) return 'claude-md';
84
- if (lower.includes('.windsurfrules') || lower.includes('windsurfrules')) return 'windsurfrules';
85
- return 'markdown';
86
- }
87
-
88
- /**
89
- * Parse content into structured sections based on headers
90
- *
91
- * @param {string} content - Markdown content
92
- * @returns {Array<Object>} Array of section objects with heading, level, body, and action
93
- */
94
- function parseSections(content) {
95
- const sections = [];
96
- const lines = content.split('\n');
97
-
98
- let currentSection = null;
99
- let bodyLines = [];
100
-
101
- for (const line of lines) {
102
- const headerMatch = line.match(/^(#{1,4})\s+(.+)$/);
103
-
104
- if (headerMatch) {
105
- // Save previous section
106
- if (currentSection) {
107
- currentSection.body = bodyLines.join('\n').trim();
108
- sections.push(currentSection);
109
- }
110
-
111
- const heading = headerMatch[2].trim();
112
- const level = headerMatch[1].length;
113
- const action = classifySectionAction(heading);
114
-
115
- currentSection = { heading, level, action, body: '' };
116
- bodyLines = [];
117
- } else {
118
- bodyLines.push(line);
119
- }
120
- }
121
-
122
- // Save final section
123
- if (currentSection) {
124
- currentSection.body = bodyLines.join('\n').trim();
125
- sections.push(currentSection);
126
- }
127
-
128
- return sections;
129
- }
130
-
131
- /**
132
- * Classify the action type for a section based on its heading
133
- *
134
- * @param {string} heading - Section heading text
135
- * @returns {string|null} Action type or null if not a rule section
136
- */
137
- function classifySectionAction(heading) {
138
- // Check NEVER before ALWAYS since some headings might match both
139
- if (SECTION_PATTERNS.NEVER.test(heading)) return 'NEVER';
140
- if (SECTION_PATTERNS.AVOID.test(heading)) return 'AVOID';
141
- if (SECTION_PATTERNS.ALWAYS.test(heading)) return 'ALWAYS';
142
- if (SECTION_PATTERNS.PREFER.test(heading)) return 'PREFER';
143
- if (SECTION_PATTERNS.USE.test(heading)) return 'USE';
144
- return null;
145
- }
146
-
147
- /**
148
- * Extract rules from parsed sections and raw content
149
- *
150
- * @param {Array<Object>} sections - Parsed sections
151
- * @param {string} content - Raw content for fallback extraction
152
- * @returns {Array<Object>} Array of rule objects with action and rule text
153
- */
154
- function extractRules(sections, content) {
155
- const rules = [];
156
- const seenRules = new Set();
157
-
158
- // Extract rules from classified sections
159
- for (const section of sections) {
160
- if (!section.action) continue;
161
-
162
- const sectionRules = extractRulesFromBody(section.body, section.action);
163
- for (const rule of sectionRules) {
164
- const key = normalizeForDedup(rule.action, rule.rule);
165
- if (!seenRules.has(key)) {
166
- seenRules.add(key);
167
- rules.push(rule);
168
- }
169
- }
170
- }
171
-
172
- // Extract inline rules from content (lines with action keywords)
173
- const inlineRules = extractInlineRules(content);
174
- for (const rule of inlineRules) {
175
- const key = normalizeForDedup(rule.action, rule.rule);
176
- if (!seenRules.has(key)) {
177
- seenRules.add(key);
178
- rules.push(rule);
179
- }
180
- }
181
-
182
- return rules;
183
- }
184
-
185
- /**
186
- * Extract rules from a section body
187
- *
188
- * @param {string} body - Section body text
189
- * @param {string} defaultAction - Default action for rules in this section
190
- * @returns {Array<Object>} Array of rule objects
191
- */
192
- function extractRulesFromBody(body, defaultAction) {
193
- if (!body) return [];
194
-
195
- const rules = [];
196
- const lines = body.split('\n');
197
-
198
- for (const line of lines) {
199
- const trimmed = line.trim();
200
-
201
- // Skip empty lines, code block markers, and short lines
202
- if (!trimmed || trimmed.startsWith('```') || trimmed.length < 10) continue;
203
-
204
- // Skip lines that are inside code blocks
205
- // (handled at a higher level by code block extraction)
206
-
207
- // Extract list items
208
- const listMatch = trimmed.match(/^[-*+]\s+(.+)$/);
209
- if (listMatch) {
210
- const ruleText = cleanRuleText(listMatch[1]);
211
- if (ruleText.length >= 10) {
212
- const action = detectInlineAction(ruleText) || defaultAction;
213
- rules.push({
214
- action,
215
- rule: ruleText
216
- });
217
- }
218
- continue;
219
- }
220
-
221
- // Extract numbered list items
222
- const numberedMatch = trimmed.match(/^\d+\.?\s+(.+)$/);
223
- if (numberedMatch) {
224
- const ruleText = cleanRuleText(numberedMatch[1]);
225
- if (ruleText.length >= 10) {
226
- const action = detectInlineAction(ruleText) || defaultAction;
227
- rules.push({
228
- action,
229
- rule: ruleText
230
- });
231
- }
232
- continue;
233
- }
234
-
235
- // Extract bold statements as rules
236
- const boldMatch = trimmed.match(/^\*\*(.+?)\*\*[:\s]*(.*)/);
237
- if (boldMatch) {
238
- const ruleText = cleanRuleText(`${boldMatch[1]}${boldMatch[2] ? ': ' + boldMatch[2] : ''}`);
239
- if (ruleText.length >= 10) {
240
- const action = detectInlineAction(ruleText) || defaultAction;
241
- rules.push({
242
- action,
243
- rule: ruleText
244
- });
245
- }
246
- }
247
- }
248
-
249
- return rules;
250
- }
251
-
252
- /**
253
- * Extract rules that appear inline in content (not in classified sections)
254
- * Looks for lines starting with action keywords
255
- *
256
- * @param {string} content - Raw content
257
- * @returns {Array<Object>} Array of rule objects
258
- */
259
- function extractInlineRules(content) {
260
- const rules = [];
261
- const lines = content.split('\n');
262
-
263
- let inCodeBlock = false;
264
-
265
- for (const line of lines) {
266
- const trimmed = line.trim();
267
-
268
- // Track code blocks
269
- if (trimmed.startsWith('```')) {
270
- inCodeBlock = !inCodeBlock;
271
- continue;
272
- }
273
- if (inCodeBlock) continue;
274
-
275
- // Match lines that start with action keywords
276
- const actionMatch = trimmed.match(
277
- /^[-*]?\s*\*?\*?(ALWAYS|NEVER|AVOID|PREFER|USE|DO NOT|DON'T)\*?\*?[:\s]+(.+)/i
278
- );
279
-
280
- if (actionMatch) {
281
- const keyword = actionMatch[1].toUpperCase();
282
- const ruleText = cleanRuleText(actionMatch[2]);
283
-
284
- if (ruleText.length >= 10) {
285
- let action;
286
- if (keyword === 'ALWAYS') action = 'ALWAYS';
287
- else if (keyword === 'NEVER' || keyword === 'DO NOT' || keyword === "DON'T") action = 'NEVER';
288
- else if (keyword === 'AVOID') action = 'AVOID';
289
- else if (keyword === 'PREFER') action = 'PREFER';
290
- else action = 'USE';
291
-
292
- rules.push({ action, rule: ruleText });
293
- }
294
- }
295
- }
296
-
297
- return rules;
298
- }
299
-
300
- /**
301
- * Normalize a rule for deduplication by stripping action prefixes and lowercasing
302
- *
303
- * @param {string} action - Rule action type
304
- * @param {string} ruleText - Rule text
305
- * @returns {string} Normalized deduplication key
306
- */
307
- function normalizeForDedup(action, ruleText) {
308
- const normalized = ruleText
309
- .toLowerCase()
310
- .replace(/^(always|never|avoid|prefer|use|do not|don't|must not|shall not|must|shall|should)\s+/i, '')
311
- .replace(/^(use|using|adopt|follow|implement)\s+/i, '')
312
- .replace(/\s+/g, ' ')
313
- .trim();
314
- return `${action}:${normalized}`;
315
- }
316
-
317
- /**
318
- * Detect if a rule text contains an inline action keyword
319
- *
320
- * @param {string} text - Rule text
321
- * @returns {string|null} Detected action or null
322
- */
323
- function detectInlineAction(text) {
324
- if (/^(?:never|do not|don't|must not|shall not)\b/i.test(text)) return 'NEVER';
325
- if (/^(?:always|must|shall|required)\b/i.test(text)) return 'ALWAYS';
326
- if (/^(?:avoid|discourage)\b/i.test(text)) return 'AVOID';
327
- if (/^(?:prefer|should|recommend)\b/i.test(text)) return 'PREFER';
328
- if (/^(?:use|adopt|follow)\b/i.test(text)) return 'USE';
329
- return null;
330
- }
331
-
332
- /**
333
- * Extract anti-patterns from sections and content
334
- *
335
- * @param {Array<Object>} sections - Parsed sections
336
- * @param {string} content - Raw content
337
- * @returns {Array<string>} Array of anti-pattern descriptions
338
- */
339
- function extractAntiPatterns(sections, content) {
340
- const antiPatterns = [];
341
- const seen = new Set();
342
-
343
- // Extract from NEVER and AVOID sections
344
- for (const section of sections) {
345
- if (section.action !== 'NEVER' && section.action !== 'AVOID') continue;
346
-
347
- const lines = section.body.split('\n');
348
- for (const line of lines) {
349
- const trimmed = line.trim();
350
- const listMatch = trimmed.match(/^[-*+]\s+(.+)$/);
351
- if (listMatch && listMatch[1].length >= 10) {
352
- const text = cleanRuleText(listMatch[1]);
353
- if (!seen.has(text)) {
354
- seen.add(text);
355
- antiPatterns.push(text);
356
- }
357
- }
358
- }
359
- }
360
-
361
- // Extract banned patterns from content
362
- const bannedSection = content.match(/(?:^|\n)##?\s*(?:Banned|Prohibited|Forbidden)[^\n]*\n([\s\S]*?)(?=\n##?\s|\n*$)/i);
363
- if (bannedSection) {
364
- const lines = bannedSection[1].split('\n');
365
- for (const line of lines) {
366
- const listMatch = line.trim().match(/^[-*+]\s+(.+)$/);
367
- if (listMatch && listMatch[1].length >= 10) {
368
- const text = cleanRuleText(listMatch[1]);
369
- if (!seen.has(text)) {
370
- seen.add(text);
371
- antiPatterns.push(text);
372
- }
373
- }
374
- }
375
- }
376
-
377
- return antiPatterns;
378
- }
379
-
380
- /**
381
- * Extract code examples from markdown code blocks
382
- *
383
- * @param {string} content - Raw markdown content
384
- * @returns {Array<Object>} Array of example objects with language, code, and context
385
- */
386
- function extractCodeExamples(content) {
387
- const examples = [];
388
- const codeBlockRegex = /```(\w+)?\n([\s\S]*?)```/g;
389
-
390
- let match;
391
- while ((match = codeBlockRegex.exec(content)) !== null) {
392
- const language = match[1] || 'text';
393
- const code = match[2].trim();
394
-
395
- if (code.length < 5) continue;
396
-
397
- // Get surrounding context (text before the code block)
398
- const beforeBlock = content.substring(
399
- Math.max(0, match.index - 200),
400
- match.index
401
- );
402
- const contextMatch = beforeBlock.match(/([^\n]+)\n*$/);
403
- const description = contextMatch ? contextMatch[1].trim() : '';
404
-
405
- examples.push({
406
- language,
407
- code,
408
- description: cleanRuleText(description) || 'Code example'
409
- });
410
- }
411
-
412
- return examples;
413
- }
414
-
415
- /**
416
- * Extract a brief description from the top of the content
417
- *
418
- * @param {string} content - Raw content
419
- * @returns {string} Brief description
420
- */
421
- function extractDescription(content) {
422
- const lines = content.split('\n');
423
-
424
- for (const line of lines) {
425
- const trimmed = line.trim();
426
- // Skip headers, empty lines, and code markers
427
- if (!trimmed || trimmed.startsWith('#') || trimmed.startsWith('```')) continue;
428
- // Skip list items
429
- if (trimmed.match(/^[-*+\d]/)) continue;
430
-
431
- if (trimmed.length >= 20) {
432
- return trimmed.substring(0, 200);
433
- }
434
- }
435
-
436
- return '';
437
- }
438
-
439
- /**
440
- * Clean rule text by removing markdown formatting
441
- *
442
- * @param {string} text - Raw text
443
- * @returns {string} Cleaned text
444
- */
445
- function cleanRuleText(text) {
446
- return text
447
- .replace(/\*\*/g, '') // Remove bold
448
- .replace(/\*/g, '') // Remove italic
449
- .replace(/`([^`]+)`/g, '$1') // Remove inline code markers
450
- .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') // Replace links with text
451
- .replace(/\s+/g, ' ') // Normalize whitespace
452
- .replace(/\s*\.$/, '') // Remove trailing period
453
- .trim();
454
- }
455
-
456
- /**
457
- * Infer standards category from content
458
- *
459
- * @param {string} content - File content
460
- * @returns {string} Inferred category
461
- */
462
- function inferCategory(content) {
463
- const lower = content.toLowerCase();
464
-
465
- const categoryKeywords = {
466
- 'serverless-saas-aws': ['lambda', 'aws', 'serverless', 'sam', 'cloudformation', 'api gateway'],
467
- 'frontend-development': ['react', 'vue', 'angular', 'component', 'css', 'tsx', 'jsx', 'frontend', 'ui'],
468
- 'database': ['database', 'sql', 'postgresql', 'schema', 'migration', 'query'],
469
- 'backend': ['api', 'handler', 'endpoint', 'express', 'node.js', 'server'],
470
- 'multi-agent-orchestration': ['agent', 'orchestration', 'llm', 'prompt', 'ai assistant'],
471
- 'compliance-security': ['security', 'auth', 'encryption', 'compliance', 'vulnerability'],
472
- 'testing': ['test', 'jest', 'mocha', 'coverage', 'assertion'],
473
- 'deployment': ['deploy', 'ci/cd', 'pipeline', 'docker', 'kubernetes']
474
- };
475
-
476
- let bestCategory = 'general';
477
- let bestScore = 0;
478
-
479
- for (const [category, keywords] of Object.entries(categoryKeywords)) {
480
- const score = keywords.filter(kw => lower.includes(kw)).length;
481
- if (score > bestScore) {
482
- bestScore = score;
483
- bestCategory = category;
484
- }
485
- }
486
-
487
- return bestCategory;
488
- }
489
-
490
- /**
491
- * Generate standards ID from filename
492
- *
493
- * @param {string} filename - Source filename
494
- * @returns {string} Generated ID
495
- */
496
- function generateId(filename) {
497
- const slug = filename
498
- .replace(/^\./g, '')
499
- .replace(/\.(md|txt|json)$/i, '')
500
- .replace(/[^a-z0-9]+/gi, '-')
501
- .toLowerCase();
502
-
503
- return `rules-${slug}`;
504
- }
505
-
506
- /**
507
- * Determine priority based on rule distribution
508
- *
509
- * @param {Array<Object>} rules - Extracted rules
510
- * @returns {number} Priority (10, 20, or 30)
511
- */
512
- function determinePriority(rules) {
513
- if (rules.length === 0) return 30;
514
-
515
- const enforcedCount = rules.filter(r =>
516
- r.action === 'ALWAYS' || r.action === 'NEVER'
517
- ).length;
518
-
519
- if (enforcedCount / rules.length > 0.5) return 10;
520
- if (enforcedCount > 0) return 20;
521
- return 30;
522
- }
523
-
524
- /**
525
- * Extract tags from content and source format
526
- *
527
- * @param {string} content - File content
528
- * @param {string} sourceFormat - Detected source format
529
- * @returns {Array<string>} Tags
530
- */
531
- function extractTags(content, sourceFormat) {
532
- const tags = [sourceFormat, 'ai-rules'];
533
-
534
- const lower = content.toLowerCase();
535
-
536
- const tagKeywords = {
537
- 'code-style': /\b(style|formatting|naming|convention)\b/i,
538
- 'architecture': /\barchitectur/i,
539
- 'security': /\bsecurity\b/i,
540
- 'performance': /\bperformance\b/i,
541
- 'testing': /\btesting\b/i,
542
- 'documentation': /\bdocument/i,
543
- 'error-handling': /\berror.?handling\b/i,
544
- 'typescript': /\btypescript\b/i,
545
- 'react': /\breact\b/i,
546
- 'node': /\bnode\.?js\b/i
547
- };
548
-
549
- for (const [tag, pattern] of Object.entries(tagKeywords)) {
550
- if (pattern.test(lower)) {
551
- tags.push(tag);
552
- }
553
- }
554
-
555
- return [...new Set(tags)];
556
- }
557
-
558
- module.exports = {
559
- parseCursorRules,
560
- parseSections,
561
- extractRules,
562
- extractAntiPatterns,
563
- extractCodeExamples
564
- };