@nerviq/cli 1.2.0 → 1.2.2
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/LICENSE +19 -17
- package/package.json +2 -2
- package/src/aider/techniques.js +25 -24
- package/src/cursor/techniques.js +25 -24
- package/src/opencode/techniques.js +25 -24
- package/src/supplemental-checks.js +817 -767
- package/src/windsurf/techniques.js +25 -24
package/LICENSE
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
|
|
1
|
+
GNU AFFERO GENERAL PUBLIC LICENSE
|
|
2
|
+
Version 3, 19 November 2007
|
|
2
3
|
|
|
3
|
-
Copyright (c) 2026
|
|
4
|
+
Copyright (c) 2026 Nerviq (nerviq.net)
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
6
|
+
Everyone is permitted to copy and distribute verbatim copies of this
|
|
7
|
+
license document, but changing it is not allowed.
|
|
11
8
|
|
|
12
|
-
|
|
13
|
-
|
|
9
|
+
This program is free software: you can redistribute it and/or modify
|
|
10
|
+
it under the terms of the GNU Affero General Public License as
|
|
11
|
+
published by the Free Software Foundation, either version 3 of the
|
|
12
|
+
License, or (at your option) any later version.
|
|
14
13
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
This program is distributed in the hope that it will be useful,
|
|
15
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
16
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
17
|
+
GNU Affero General Public License for more details.
|
|
18
|
+
|
|
19
|
+
You should have received a copy of the GNU Affero General Public License
|
|
20
|
+
along with this program. If not, see <https://www.gnu.org/licenses/agpl-3.0.html>.
|
|
21
|
+
|
|
22
|
+
For the full AGPL-3.0 license text, see:
|
|
23
|
+
https://www.gnu.org/licenses/agpl-3.0.html
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nerviq/cli",
|
|
3
|
-
"version": "1.2.
|
|
4
|
-
"description": "The intelligent nervous system for AI coding agents —
|
|
3
|
+
"version": "1.2.2",
|
|
4
|
+
"description": "The intelligent nervous system for AI coding agents — 2,306 checks across 8 platforms and 10 languages. Audit, align, and amplify.",
|
|
5
5
|
"main": "src/index.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"nerviq": "bin/cli.js",
|
package/src/aider/techniques.js
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
const { containsEmbeddedSecret } = require('../secret-patterns');
|
|
23
23
|
const { attachSourceUrls } = require('../source-urls');
|
|
24
24
|
const { buildStackChecks } = require('../stack-checks');
|
|
25
|
+
const { isApiProject, isDatabaseProject, isAuthProject, isMonitoringRelevant } = require('../supplemental-checks');
|
|
25
26
|
|
|
26
27
|
const FILLER_PATTERNS = [
|
|
27
28
|
/\bbe helpful\b/i,
|
|
@@ -1480,168 +1481,168 @@ const AIDER_TECHNIQUES = {
|
|
|
1480
1481
|
},
|
|
1481
1482
|
aiderEndpointDocumentation: {
|
|
1482
1483
|
id: 'AD-T13', name: 'API endpoint documentation present',
|
|
1483
|
-
check: (ctx) => ctx.files.some(f => /openapi|swagger|api\.ya?ml|api\.json/i.test(f)),
|
|
1484
|
+
check: (ctx) => !isApiProject(ctx) ? null : ctx.files.some(f => /openapi|swagger|api\.ya?ml|api\.json/i.test(f)),
|
|
1484
1485
|
impact: 'medium', rating: 3, category: 'api-design',
|
|
1485
1486
|
fix: 'Add an OpenAPI/Swagger spec for Aider to understand the API surface.',
|
|
1486
1487
|
template: null, file: () => null, line: () => null,
|
|
1487
1488
|
},
|
|
1488
1489
|
aiderApiVersioningMentioned: {
|
|
1489
1490
|
id: 'AD-T14', name: 'API versioning strategy documented',
|
|
1490
|
-
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /api.{0,10}version|\/v\d|versioning/i.test(conv); },
|
|
1491
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /api.{0,10}version|\/v\d|versioning/i.test(conv); },
|
|
1491
1492
|
impact: 'medium', rating: 3, category: 'api-design',
|
|
1492
1493
|
fix: 'Document API versioning in CONVENTIONS.md.',
|
|
1493
1494
|
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1494
1495
|
},
|
|
1495
1496
|
aiderErrorHandlingPatterns: {
|
|
1496
1497
|
id: 'AD-T15', name: 'Error handling patterns in conventions',
|
|
1497
|
-
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /error.{0,15}handl|exception|try.?catch|Result\s*</i.test(conv); },
|
|
1498
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /error.{0,15}handl|exception|try.?catch|Result\s*</i.test(conv); },
|
|
1498
1499
|
impact: 'high', rating: 4, category: 'api-design',
|
|
1499
1500
|
fix: 'Add error handling patterns to CONVENTIONS.md for Aider.',
|
|
1500
1501
|
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1501
1502
|
},
|
|
1502
1503
|
aiderRateLimitingAwareness: {
|
|
1503
1504
|
id: 'AD-T16', name: 'Rate limiting awareness documented',
|
|
1504
|
-
check: (ctx) => { const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /rate.?limit|throttl|429/i.test(docs); },
|
|
1505
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /rate.?limit|throttl|429/i.test(docs); },
|
|
1505
1506
|
impact: 'medium', rating: 3, category: 'api-design',
|
|
1506
1507
|
fix: 'Document rate limiting in CONVENTIONS.md.',
|
|
1507
1508
|
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1508
1509
|
},
|
|
1509
1510
|
aiderRequestValidation: {
|
|
1510
1511
|
id: 'AD-T17', name: 'Request validation strategy documented',
|
|
1511
|
-
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /validat|zod|yup|joi\b/i.test(conv); },
|
|
1512
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /validat|zod|yup|joi\b/i.test(conv); },
|
|
1512
1513
|
impact: 'high', rating: 4, category: 'api-design',
|
|
1513
1514
|
fix: 'Document request validation (Zod, Yup) in CONVENTIONS.md.',
|
|
1514
1515
|
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1515
1516
|
},
|
|
1516
1517
|
aiderResponseFormatConsistent: {
|
|
1517
1518
|
id: 'AD-T18', name: 'Response format consistency documented',
|
|
1518
|
-
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /response.{0,20}format|json.{0,10}response|envelope/i.test(conv); },
|
|
1519
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /response.{0,20}format|json.{0,10}response|envelope/i.test(conv); },
|
|
1519
1520
|
impact: 'medium', rating: 3, category: 'api-design',
|
|
1520
1521
|
fix: 'Document standard response format in CONVENTIONS.md.',
|
|
1521
1522
|
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1522
1523
|
},
|
|
1523
1524
|
aiderMigrationStrategyDocumented: {
|
|
1524
1525
|
id: 'AD-T19', name: 'Database migration strategy documented',
|
|
1525
|
-
check: (ctx) => ctx.files.some(f => /migrations?\//i.test(f)) || /migration|alembic|flyway/i.test(conventionContent(ctx)),
|
|
1526
|
+
check: (ctx) => !isDatabaseProject(ctx) ? null : ctx.files.some(f => /migrations?\//i.test(f)) || /migration|alembic|flyway/i.test(conventionContent(ctx)),
|
|
1526
1527
|
impact: 'high', rating: 4, category: 'database',
|
|
1527
1528
|
fix: 'Document migration strategy in CONVENTIONS.md.',
|
|
1528
1529
|
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1529
1530
|
},
|
|
1530
1531
|
aiderQueryOptimizationMentioned: {
|
|
1531
1532
|
id: 'AD-T20', name: 'Query optimization guidance documented',
|
|
1532
|
-
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /n\+1|query.{0,15}optim|index|eager.{0,10}load/i.test(conv); },
|
|
1533
|
+
check: (ctx) => { if (!isDatabaseProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /n\+1|query.{0,15}optim|index|eager.{0,10}load/i.test(conv); },
|
|
1533
1534
|
impact: 'medium', rating: 3, category: 'database',
|
|
1534
1535
|
fix: 'Add N+1 prevention patterns to CONVENTIONS.md.',
|
|
1535
1536
|
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1536
1537
|
},
|
|
1537
1538
|
aiderConnectionPoolingConfigured: {
|
|
1538
1539
|
id: 'AD-T21', name: 'Connection pooling documented',
|
|
1539
|
-
check: (ctx) => { const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /connection.{0,15}pool|pool.{0,15}size/i.test(docs); },
|
|
1540
|
+
check: (ctx) => { if (!isDatabaseProject(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /connection.{0,15}pool|pool.{0,15}size/i.test(docs); },
|
|
1540
1541
|
impact: 'medium', rating: 3, category: 'database',
|
|
1541
1542
|
fix: 'Document connection pooling in CONVENTIONS.md.',
|
|
1542
1543
|
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1543
1544
|
},
|
|
1544
1545
|
aiderBackupStrategyDocumented: {
|
|
1545
1546
|
id: 'AD-T22', name: 'Database backup strategy documented',
|
|
1546
|
-
check: (ctx) => { const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /backup|restore|point.?in.?time/i.test(docs); },
|
|
1547
|
+
check: (ctx) => { if (!isDatabaseProject(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /backup|restore|point.?in.?time/i.test(docs); },
|
|
1547
1548
|
impact: 'medium', rating: 3, category: 'database',
|
|
1548
1549
|
fix: 'Document database backup strategy in README.md.',
|
|
1549
1550
|
template: null, file: () => 'README.md', line: () => null,
|
|
1550
1551
|
},
|
|
1551
1552
|
aiderSchemaDocumentation: {
|
|
1552
1553
|
id: 'AD-T23', name: 'Database schema documentation present',
|
|
1553
|
-
check: (ctx) => ctx.files.some(f => /schema\.(prisma|sql|graphql)|erd|dbml/i.test(f)),
|
|
1554
|
+
check: (ctx) => !isDatabaseProject(ctx) ? null : ctx.files.some(f => /schema\.(prisma|sql|graphql)|erd|dbml/i.test(f)),
|
|
1554
1555
|
impact: 'medium', rating: 3, category: 'database',
|
|
1555
1556
|
fix: 'Add schema documentation so Aider understands the data model.',
|
|
1556
1557
|
template: null, file: () => null, line: () => null,
|
|
1557
1558
|
},
|
|
1558
1559
|
aiderSeedDataMentioned: {
|
|
1559
1560
|
id: 'AD-T24', name: 'Seed data strategy documented',
|
|
1560
|
-
check: (ctx) => ctx.files.some(f => /seed\.(ts|js|sql|py)|fixtures\//i.test(f)) || /seed.{0,10}data/i.test(conventionContent(ctx)),
|
|
1561
|
+
check: (ctx) => !isDatabaseProject(ctx) ? null : ctx.files.some(f => /seed\.(ts|js|sql|py)|fixtures\//i.test(f)) || /seed.{0,10}data/i.test(conventionContent(ctx)),
|
|
1561
1562
|
impact: 'low', rating: 2, category: 'database',
|
|
1562
1563
|
fix: 'Add seed scripts and document local database setup.',
|
|
1563
1564
|
template: null, file: () => null, line: () => null,
|
|
1564
1565
|
},
|
|
1565
1566
|
aiderAuthFlowDocumented: {
|
|
1566
1567
|
id: 'AD-T25', name: 'Authentication flow documented in conventions',
|
|
1567
|
-
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /auth.{0,15}flow|login.{0,15}flow|authenticate/i.test(conv); },
|
|
1568
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /auth.{0,15}flow|login.{0,15}flow|authenticate/i.test(conv); },
|
|
1568
1569
|
impact: 'high', rating: 4, category: 'authentication',
|
|
1569
1570
|
fix: 'Document authentication flow in CONVENTIONS.md for Aider.',
|
|
1570
1571
|
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1571
1572
|
},
|
|
1572
1573
|
aiderTokenHandlingGuidance: {
|
|
1573
1574
|
id: 'AD-T26', name: 'Token handling guidance in conventions',
|
|
1574
|
-
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /jwt|token.{0,15}refresh|access.{0,10}token|bearer/i.test(conv); },
|
|
1575
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /jwt|token.{0,15}refresh|access.{0,10}token|bearer/i.test(conv); },
|
|
1575
1576
|
impact: 'high', rating: 4, category: 'authentication',
|
|
1576
1577
|
fix: 'Document JWT/token patterns in CONVENTIONS.md.',
|
|
1577
1578
|
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1578
1579
|
},
|
|
1579
1580
|
aiderSessionManagementDocumented: {
|
|
1580
1581
|
id: 'AD-T27', name: 'Session management documented',
|
|
1581
|
-
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /session.{0,15}manag|cookie|next.?auth/i.test(conv); },
|
|
1582
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /session.{0,15}manag|cookie|next.?auth/i.test(conv); },
|
|
1582
1583
|
impact: 'medium', rating: 3, category: 'authentication',
|
|
1583
1584
|
fix: 'Document session management approach in CONVENTIONS.md.',
|
|
1584
1585
|
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1585
1586
|
},
|
|
1586
1587
|
aiderRbacPermissionsReferenced: {
|
|
1587
1588
|
id: 'AD-T28', name: 'RBAC / permissions model referenced',
|
|
1588
|
-
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /rbac|role.?based|permission|authorization/i.test(conv); },
|
|
1589
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /rbac|role.?based|permission|authorization/i.test(conv); },
|
|
1589
1590
|
impact: 'high', rating: 4, category: 'authentication',
|
|
1590
1591
|
fix: 'Document RBAC/permissions model in CONVENTIONS.md.',
|
|
1591
1592
|
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1592
1593
|
},
|
|
1593
1594
|
aiderOauthSsoMentioned: {
|
|
1594
1595
|
id: 'AD-T29', name: 'OAuth/SSO configuration documented',
|
|
1595
|
-
check: (ctx) => { const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /oauth|sso|saml|oidc/i.test(docs); },
|
|
1596
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /oauth|sso|saml|oidc/i.test(docs); },
|
|
1596
1597
|
impact: 'medium', rating: 3, category: 'authentication',
|
|
1597
1598
|
fix: 'Document OAuth/SSO provider in README.md.',
|
|
1598
1599
|
template: null, file: () => 'README.md', line: () => null,
|
|
1599
1600
|
},
|
|
1600
1601
|
aiderCredentialRotationDocumented: {
|
|
1601
1602
|
id: 'AD-T30', name: 'Credential rotation policy documented',
|
|
1602
|
-
check: (ctx) => { const conv = conventionContent(ctx); if (!conv.trim()) return null; return /rotat.{0,10}secret|rotat.{0,10}key|credential.{0,10}rotat/i.test(conv); },
|
|
1603
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const conv = conventionContent(ctx); if (!conv.trim()) return null; return /rotat.{0,10}secret|rotat.{0,10}key|credential.{0,10}rotat/i.test(conv); },
|
|
1603
1604
|
impact: 'medium', rating: 3, category: 'authentication',
|
|
1604
1605
|
fix: 'Document credential rotation in CONVENTIONS.md.',
|
|
1605
1606
|
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1606
1607
|
},
|
|
1607
1608
|
aiderLoggingConfigured: {
|
|
1608
1609
|
id: 'AD-T31', name: 'Logging framework configured',
|
|
1609
|
-
check: (ctx) => /winston|pino|bunyan|morgan|loguru/i.test(ctx.fileContent('package.json') || '') || ctx.files.some(f => /log(ger|ging)\.config/i.test(f)),
|
|
1610
|
+
check: (ctx) => !isMonitoringRelevant(ctx) ? null : /winston|pino|bunyan|morgan|loguru/i.test(ctx.fileContent('package.json') || '') || ctx.files.some(f => /log(ger|ging)\.config/i.test(f)),
|
|
1610
1611
|
impact: 'high', rating: 4, category: 'monitoring',
|
|
1611
1612
|
fix: 'Add a logging framework and document log levels in CONVENTIONS.md.',
|
|
1612
1613
|
template: 'aider-conventions', file: () => 'CONVENTIONS.md', line: () => null,
|
|
1613
1614
|
},
|
|
1614
1615
|
aiderErrorTrackingSetup: {
|
|
1615
1616
|
id: 'AD-T32', name: 'Error tracking service configured',
|
|
1616
|
-
check: (ctx) => /sentry|bugsnag|rollbar/i.test(ctx.fileContent('package.json') || ''),
|
|
1617
|
+
check: (ctx) => !isMonitoringRelevant(ctx) ? null : /sentry|bugsnag|rollbar/i.test(ctx.fileContent('package.json') || ''),
|
|
1617
1618
|
impact: 'high', rating: 4, category: 'monitoring',
|
|
1618
1619
|
fix: 'Set up error tracking (Sentry) to catch production errors.',
|
|
1619
1620
|
template: null, file: () => null, line: () => null,
|
|
1620
1621
|
},
|
|
1621
1622
|
aiderApmMetricsMentioned: {
|
|
1622
1623
|
id: 'AD-T33', name: 'APM / metrics platform documented',
|
|
1623
|
-
check: (ctx) => { const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /datadog|newrelic|prometheus|grafana|apm|opentelemetry/i.test(docs); },
|
|
1624
|
+
check: (ctx) => { if (!isMonitoringRelevant(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /datadog|newrelic|prometheus|grafana|apm|opentelemetry/i.test(docs); },
|
|
1624
1625
|
impact: 'medium', rating: 3, category: 'monitoring',
|
|
1625
1626
|
fix: 'Document APM platform in README.md.',
|
|
1626
1627
|
template: null, file: () => 'README.md', line: () => null,
|
|
1627
1628
|
},
|
|
1628
1629
|
aiderHealthCheckEndpoint: {
|
|
1629
1630
|
id: 'AD-T34', name: 'Health check endpoint documented',
|
|
1630
|
-
check: (ctx) => { const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /health.?check|\/health|\/ping|\/status/i.test(docs); },
|
|
1631
|
+
check: (ctx) => { if (!isMonitoringRelevant(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /health.?check|\/health|\/ping|\/status/i.test(docs); },
|
|
1631
1632
|
impact: 'medium', rating: 3, category: 'monitoring',
|
|
1632
1633
|
fix: 'Document health check endpoint in README.md.',
|
|
1633
1634
|
template: null, file: () => 'README.md', line: () => null,
|
|
1634
1635
|
},
|
|
1635
1636
|
aiderAlertingReferenced: {
|
|
1636
1637
|
id: 'AD-T35', name: 'Alerting strategy referenced',
|
|
1637
|
-
check: (ctx) => { const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /alert|pagerduty|opsgenie|oncall|incident/i.test(docs); },
|
|
1638
|
+
check: (ctx) => { if (!isMonitoringRelevant(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /alert|pagerduty|opsgenie|oncall|incident/i.test(docs); },
|
|
1638
1639
|
impact: 'medium', rating: 3, category: 'monitoring',
|
|
1639
1640
|
fix: 'Document alerting strategy in README.md.',
|
|
1640
1641
|
template: null, file: () => 'README.md', line: () => null,
|
|
1641
1642
|
},
|
|
1642
1643
|
aiderLogRotationMentioned: {
|
|
1643
1644
|
id: 'AD-T36', name: 'Log rotation / retention documented',
|
|
1644
|
-
check: (ctx) => { const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /log.{0,15}rotat|log.{0,15}retent/i.test(docs); },
|
|
1645
|
+
check: (ctx) => { if (!isMonitoringRelevant(ctx)) return null; const docs = conventionContent(ctx) + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /log.{0,15}rotat|log.{0,15}retent/i.test(docs); },
|
|
1645
1646
|
impact: 'low', rating: 2, category: 'monitoring',
|
|
1646
1647
|
fix: 'Document log rotation policy in README.md.',
|
|
1647
1648
|
template: null, file: () => 'README.md', line: () => null,
|
package/src/cursor/techniques.js
CHANGED
|
@@ -17,6 +17,7 @@ const { CursorProjectContext } = require('./context');
|
|
|
17
17
|
const { EMBEDDED_SECRET_PATTERNS, containsEmbeddedSecret } = require('../secret-patterns');
|
|
18
18
|
const { attachSourceUrls } = require('../source-urls');
|
|
19
19
|
const { buildStackChecks } = require('../stack-checks');
|
|
20
|
+
const { isApiProject, isDatabaseProject, isAuthProject, isMonitoringRelevant } = require('../supplemental-checks');
|
|
20
21
|
const { validateMdcFrontmatter, validateMcpEnvVars } = require('./config-parser');
|
|
21
22
|
|
|
22
23
|
// ─── Shared helpers ─────────────────────────────────────────────────────────
|
|
@@ -1957,42 +1958,42 @@ const CURSOR_TECHNIQUES = {
|
|
|
1957
1958
|
// ── API Design (CU-T13..T18) ──
|
|
1958
1959
|
cursorEndpointDocumentation: {
|
|
1959
1960
|
id: 'CU-T13', name: 'API endpoint documentation present',
|
|
1960
|
-
check: (ctx) => ctx.files.some(f => /openapi|swagger|api\.ya?ml|api\.json/i.test(f)) || /openapi|swagger/i.test(ctx.fileContent('package.json') || ''),
|
|
1961
|
+
check: (ctx) => !isApiProject(ctx) ? null : ctx.files.some(f => /openapi|swagger|api\.ya?ml|api\.json/i.test(f)) || /openapi|swagger/i.test(ctx.fileContent('package.json') || ''),
|
|
1961
1962
|
impact: 'medium', rating: 3, category: 'api-design',
|
|
1962
1963
|
fix: 'Add an OpenAPI/Swagger spec so agents understand your API surface.',
|
|
1963
1964
|
template: null, file: () => null, line: () => null,
|
|
1964
1965
|
},
|
|
1965
1966
|
cursorApiVersioningMentioned: {
|
|
1966
1967
|
id: 'CU-T14', name: 'API versioning strategy documented',
|
|
1967
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /api.{0,10}version|\/v\d|versioning/i.test(docs); },
|
|
1968
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /api.{0,10}version|\/v\d|versioning/i.test(docs); },
|
|
1968
1969
|
impact: 'medium', rating: 3, category: 'api-design',
|
|
1969
1970
|
fix: 'Document API versioning strategy (URL versioning, header versioning) in CLAUDE.md.',
|
|
1970
1971
|
template: null, file: () => 'CLAUDE.md', line: () => null,
|
|
1971
1972
|
},
|
|
1972
1973
|
cursorErrorHandlingPatterns: {
|
|
1973
1974
|
id: 'CU-T15', name: 'Error handling patterns documented',
|
|
1974
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /error.{0,15}handl|exception|try.?catch|Result\s*<|error.{0,10}pattern/i.test(docs); },
|
|
1975
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /error.{0,15}handl|exception|try.?catch|Result\s*<|error.{0,10}pattern/i.test(docs); },
|
|
1975
1976
|
impact: 'high', rating: 4, category: 'api-design',
|
|
1976
1977
|
fix: 'Document error handling patterns in Cursor rules so agents use consistent error responses.',
|
|
1977
1978
|
template: null, file: () => '.cursor/rules/', line: () => null,
|
|
1978
1979
|
},
|
|
1979
1980
|
cursorRateLimitingAwareness: {
|
|
1980
1981
|
id: 'CU-T16', name: 'Rate limiting awareness documented',
|
|
1981
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /rate.?limit|throttl|429/i.test(docs); },
|
|
1982
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /rate.?limit|throttl|429/i.test(docs); },
|
|
1982
1983
|
impact: 'medium', rating: 3, category: 'api-design',
|
|
1983
1984
|
fix: 'Document rate limiting in CLAUDE.md so agents handle 429 responses and add retry logic.',
|
|
1984
1985
|
template: null, file: () => 'CLAUDE.md', line: () => null,
|
|
1985
1986
|
},
|
|
1986
1987
|
cursorRequestValidation: {
|
|
1987
1988
|
id: 'CU-T17', name: 'Request validation strategy documented',
|
|
1988
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /validat|zod|yup|joi\b|schema.{0,15}validat/i.test(docs); },
|
|
1989
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /validat|zod|yup|joi\b|schema.{0,15}validat/i.test(docs); },
|
|
1989
1990
|
impact: 'high', rating: 4, category: 'api-design',
|
|
1990
1991
|
fix: 'Document request validation library (Zod, Yup, Joi) in Cursor rules.',
|
|
1991
1992
|
template: null, file: () => '.cursor/rules/', line: () => null,
|
|
1992
1993
|
},
|
|
1993
1994
|
cursorResponseFormatConsistent: {
|
|
1994
1995
|
id: 'CU-T18', name: 'Response format consistency documented',
|
|
1995
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /response.{0,20}format|json.{0,10}response|api.{0,15}response|envelope/i.test(docs); },
|
|
1996
|
+
check: (ctx) => { if (!isApiProject(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /response.{0,20}format|json.{0,10}response|api.{0,15}response|envelope/i.test(docs); },
|
|
1996
1997
|
impact: 'medium', rating: 3, category: 'api-design',
|
|
1997
1998
|
fix: 'Document standard response format in Cursor rules so agents produce consistent API responses.',
|
|
1998
1999
|
template: null, file: () => '.cursor/rules/', line: () => null,
|
|
@@ -2001,42 +2002,42 @@ const CURSOR_TECHNIQUES = {
|
|
|
2001
2002
|
// ── Database (CU-T19..T24) ──
|
|
2002
2003
|
cursorMigrationStrategyDocumented: {
|
|
2003
2004
|
id: 'CU-T19', name: 'Database migration strategy documented',
|
|
2004
|
-
check: (ctx) => ctx.files.some(f => /migrations?\//i.test(f)) || /migration|prisma migrate|alembic|flyway|knex.{0,10}migrate/i.test(allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '')),
|
|
2005
|
+
check: (ctx) => !isDatabaseProject(ctx) ? null : ctx.files.some(f => /migrations?\//i.test(f)) || /migration|prisma migrate|alembic|flyway|knex.{0,10}migrate/i.test(allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '')),
|
|
2005
2006
|
impact: 'high', rating: 4, category: 'database',
|
|
2006
2007
|
fix: 'Document database migration strategy in CLAUDE.md (Prisma, Alembic, Flyway).',
|
|
2007
2008
|
template: null, file: () => 'CLAUDE.md', line: () => null,
|
|
2008
2009
|
},
|
|
2009
2010
|
cursorQueryOptimizationMentioned: {
|
|
2010
2011
|
id: 'CU-T20', name: 'Query optimization guidance documented',
|
|
2011
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /n\+1|query.{0,15}optim|index|explain.{0,10}query|eager.{0,10}load/i.test(docs); },
|
|
2012
|
+
check: (ctx) => { if (!isDatabaseProject(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /n\+1|query.{0,15}optim|index|explain.{0,10}query|eager.{0,10}load/i.test(docs); },
|
|
2012
2013
|
impact: 'medium', rating: 3, category: 'database',
|
|
2013
2014
|
fix: 'Document N+1 prevention and query optimization patterns in Cursor rules.',
|
|
2014
2015
|
template: null, file: () => '.cursor/rules/', line: () => null,
|
|
2015
2016
|
},
|
|
2016
2017
|
cursorConnectionPoolingConfigured: {
|
|
2017
2018
|
id: 'CU-T21', name: 'Connection pooling documented',
|
|
2018
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /connection.{0,15}pool|pool.{0,15}size|pgbouncer|prisma.*pool/i.test(docs); },
|
|
2019
|
+
check: (ctx) => { if (!isDatabaseProject(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /connection.{0,15}pool|pool.{0,15}size|pgbouncer|prisma.*pool/i.test(docs); },
|
|
2019
2020
|
impact: 'medium', rating: 3, category: 'database',
|
|
2020
2021
|
fix: 'Document connection pooling configuration in CLAUDE.md to prevent connection exhaustion.',
|
|
2021
2022
|
template: null, file: () => 'CLAUDE.md', line: () => null,
|
|
2022
2023
|
},
|
|
2023
2024
|
cursorBackupStrategyDocumented: {
|
|
2024
2025
|
id: 'CU-T22', name: 'Database backup strategy documented',
|
|
2025
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /backup|restore|point.?in.?time|snapshot.{0,15}db/i.test(docs); },
|
|
2026
|
+
check: (ctx) => { if (!isDatabaseProject(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /backup|restore|point.?in.?time|snapshot.{0,15}db/i.test(docs); },
|
|
2026
2027
|
impact: 'medium', rating: 3, category: 'database',
|
|
2027
2028
|
fix: 'Document database backup and restore procedures in README.md.',
|
|
2028
2029
|
template: null, file: () => 'README.md', line: () => null,
|
|
2029
2030
|
},
|
|
2030
2031
|
cursorSchemaDocumentation: {
|
|
2031
2032
|
id: 'CU-T23', name: 'Database schema documentation present',
|
|
2032
|
-
check: (ctx) => ctx.files.some(f => /schema\.(prisma|sql|graphql)|erd|dbml|entity.{0,15}diagram/i.test(f)) || ctx.files.some(f => /schema\.md/i.test(f)),
|
|
2033
|
+
check: (ctx) => !isDatabaseProject(ctx) ? null : ctx.files.some(f => /schema\.(prisma|sql|graphql)|erd|dbml|entity.{0,15}diagram/i.test(f)) || ctx.files.some(f => /schema\.md/i.test(f)),
|
|
2033
2034
|
impact: 'medium', rating: 3, category: 'database',
|
|
2034
2035
|
fix: 'Add schema documentation (schema.prisma, ERD, DBML) so agents understand the data model.',
|
|
2035
2036
|
template: null, file: () => null, line: () => null,
|
|
2036
2037
|
},
|
|
2037
2038
|
cursorSeedDataMentioned: {
|
|
2038
2039
|
id: 'CU-T24', name: 'Seed data strategy documented',
|
|
2039
|
-
check: (ctx) => ctx.files.some(f => /seed\.(ts|js|sql|py)|fixtures\//i.test(f)) || /seed.{0,10}data|seed.{0,10}script|prisma.*seed/i.test(allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '')),
|
|
2040
|
+
check: (ctx) => !isDatabaseProject(ctx) ? null : ctx.files.some(f => /seed\.(ts|js|sql|py)|fixtures\//i.test(f)) || /seed.{0,10}data|seed.{0,10}script|prisma.*seed/i.test(allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '')),
|
|
2040
2041
|
impact: 'low', rating: 2, category: 'database',
|
|
2041
2042
|
fix: 'Add seed scripts and document how to populate a local database for development.',
|
|
2042
2043
|
template: null, file: () => null, line: () => null,
|
|
@@ -2045,42 +2046,42 @@ const CURSOR_TECHNIQUES = {
|
|
|
2045
2046
|
// ── Authentication (CU-T25..T30) ──
|
|
2046
2047
|
cursorAuthFlowDocumented: {
|
|
2047
2048
|
id: 'CU-T25', name: 'Authentication flow documented',
|
|
2048
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /auth.{0,15}flow|login.{0,15}flow|sign.?in|authenticate/i.test(docs); },
|
|
2049
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /auth.{0,15}flow|login.{0,15}flow|sign.?in|authenticate/i.test(docs); },
|
|
2049
2050
|
impact: 'high', rating: 4, category: 'authentication',
|
|
2050
2051
|
fix: 'Document the authentication flow in CLAUDE.md so agents implement auth correctly.',
|
|
2051
2052
|
template: null, file: () => 'CLAUDE.md', line: () => null,
|
|
2052
2053
|
},
|
|
2053
2054
|
cursorTokenHandlingGuidance: {
|
|
2054
2055
|
id: 'CU-T26', name: 'Token handling guidance documented',
|
|
2055
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /jwt|token.{0,15}refresh|access.{0,10}token|bearer|token.{0,15}storage/i.test(docs); },
|
|
2056
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /jwt|token.{0,15}refresh|access.{0,10}token|bearer|token.{0,15}storage/i.test(docs); },
|
|
2056
2057
|
impact: 'high', rating: 4, category: 'authentication',
|
|
2057
2058
|
fix: 'Document JWT/token handling patterns in Cursor rules (storage, refresh, expiration).',
|
|
2058
2059
|
template: null, file: () => '.cursor/rules/', line: () => null,
|
|
2059
2060
|
},
|
|
2060
2061
|
cursorSessionManagementDocumented: {
|
|
2061
2062
|
id: 'CU-T27', name: 'Session management documented',
|
|
2062
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /session.{0,15}manag|cookie.{0,15}session|next.?auth|lucia|auth\.js/i.test(docs); },
|
|
2063
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /session.{0,15}manag|cookie.{0,15}session|next.?auth|lucia|auth\.js/i.test(docs); },
|
|
2063
2064
|
impact: 'medium', rating: 3, category: 'authentication',
|
|
2064
2065
|
fix: 'Document session management approach in CLAUDE.md (cookies, server-side sessions, JWT).',
|
|
2065
2066
|
template: null, file: () => 'CLAUDE.md', line: () => null,
|
|
2066
2067
|
},
|
|
2067
2068
|
cursorRbacPermissionsReferenced: {
|
|
2068
2069
|
id: 'CU-T28', name: 'RBAC / permissions model referenced',
|
|
2069
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /rbac|role.?based|permission|authorization|can\('|ability/i.test(docs); },
|
|
2070
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /rbac|role.?based|permission|authorization|can\('|ability/i.test(docs); },
|
|
2070
2071
|
impact: 'high', rating: 4, category: 'authentication',
|
|
2071
2072
|
fix: 'Document the permissions/RBAC model in Cursor rules so agents respect access controls.',
|
|
2072
2073
|
template: null, file: () => '.cursor/rules/', line: () => null,
|
|
2073
2074
|
},
|
|
2074
2075
|
cursorOauthSsoMentioned: {
|
|
2075
2076
|
id: 'CU-T29', name: 'OAuth/SSO configuration documented',
|
|
2076
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /oauth|sso|saml|oidc|google.{0,10}auth|github.{0,10}auth/i.test(docs); },
|
|
2077
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /oauth|sso|saml|oidc|google.{0,10}auth|github.{0,10}auth/i.test(docs); },
|
|
2077
2078
|
impact: 'medium', rating: 3, category: 'authentication',
|
|
2078
2079
|
fix: 'Document OAuth/SSO provider configuration in README.md.',
|
|
2079
2080
|
template: null, file: () => 'README.md', line: () => null,
|
|
2080
2081
|
},
|
|
2081
2082
|
cursorCredentialRotationDocumented: {
|
|
2082
2083
|
id: 'CU-T30', name: 'Credential rotation policy documented',
|
|
2083
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /rotat.{0,10}secret|rotat.{0,10}key|credential.{0,10}rotat|secret.{0,10}expir/i.test(docs); },
|
|
2084
|
+
check: (ctx) => { if (!isAuthProject(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || ''); if (!docs.trim()) return null; return /rotat.{0,10}secret|rotat.{0,10}key|credential.{0,10}rotat|secret.{0,10}expir/i.test(docs); },
|
|
2084
2085
|
impact: 'medium', rating: 3, category: 'authentication',
|
|
2085
2086
|
fix: 'Document credential rotation policy in Cursor rules (how often, automated or manual).',
|
|
2086
2087
|
template: null, file: () => '.cursor/rules/', line: () => null,
|
|
@@ -2089,42 +2090,42 @@ const CURSOR_TECHNIQUES = {
|
|
|
2089
2090
|
// ── Monitoring (CU-T31..T36) ──
|
|
2090
2091
|
cursorLoggingConfigured: {
|
|
2091
2092
|
id: 'CU-T31', name: 'Logging framework configured',
|
|
2092
|
-
check: (ctx) => { const pkg = ctx.fileContent('package.json') || ''; return /winston|pino|bunyan|log4js|morgan|loglevel|structlog|loguru/i.test(pkg) || ctx.files.some(f => /log(ger|ging)\.config/i.test(f)); },
|
|
2093
|
+
check: (ctx) => { if (!isMonitoringRelevant(ctx)) return null; const pkg = ctx.fileContent('package.json') || ''; return /winston|pino|bunyan|log4js|morgan|loglevel|structlog|loguru/i.test(pkg) || ctx.files.some(f => /log(ger|ging)\.config/i.test(f)); },
|
|
2093
2094
|
impact: 'high', rating: 4, category: 'monitoring',
|
|
2094
2095
|
fix: 'Add a structured logging framework (Pino, Winston) and document log levels in Cursor rules.',
|
|
2095
2096
|
template: null, file: () => 'package.json', line: () => null,
|
|
2096
2097
|
},
|
|
2097
2098
|
cursorErrorTrackingSetup: {
|
|
2098
2099
|
id: 'CU-T32', name: 'Error tracking service configured',
|
|
2099
|
-
check: (ctx) => { const pkg = ctx.fileContent('package.json') || ''; return /sentry|bugsnag|rollbar|honeybadger|raygun|airnav/i.test(pkg) || ctx.files.some(f => /sentry\.client|sentry\.server/i.test(f)); },
|
|
2100
|
+
check: (ctx) => { if (!isMonitoringRelevant(ctx)) return null; const pkg = ctx.fileContent('package.json') || ''; return /sentry|bugsnag|rollbar|honeybadger|raygun|airnav/i.test(pkg) || ctx.files.some(f => /sentry\.client|sentry\.server/i.test(f)); },
|
|
2100
2101
|
impact: 'high', rating: 4, category: 'monitoring',
|
|
2101
2102
|
fix: 'Set up error tracking (Sentry, Bugsnag) to catch runtime errors in production.',
|
|
2102
2103
|
template: null, file: () => null, line: () => null,
|
|
2103
2104
|
},
|
|
2104
2105
|
cursorApmMetricsMentioned: {
|
|
2105
2106
|
id: 'CU-T33', name: 'APM / metrics platform documented',
|
|
2106
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /datadog|newrelic|prometheus|grafana|opentelemetry|apm|metrics/i.test(docs); },
|
|
2107
|
+
check: (ctx) => { if (!isMonitoringRelevant(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /datadog|newrelic|prometheus|grafana|opentelemetry|apm|metrics/i.test(docs); },
|
|
2107
2108
|
impact: 'medium', rating: 3, category: 'monitoring',
|
|
2108
2109
|
fix: 'Document APM/metrics platform in README.md (Datadog, Prometheus, OpenTelemetry).',
|
|
2109
2110
|
template: null, file: () => 'README.md', line: () => null,
|
|
2110
2111
|
},
|
|
2111
2112
|
cursorHealthCheckEndpoint: {
|
|
2112
2113
|
id: 'CU-T34', name: 'Health check endpoint documented',
|
|
2113
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /health.?check|\/health|\/ping|\/status|liveness|readiness/i.test(docs); },
|
|
2114
|
+
check: (ctx) => { if (!isMonitoringRelevant(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /health.?check|\/health|\/ping|\/status|liveness|readiness/i.test(docs); },
|
|
2114
2115
|
impact: 'medium', rating: 3, category: 'monitoring',
|
|
2115
2116
|
fix: 'Document health check endpoint in README.md (/health, /ping) for load balancer integration.',
|
|
2116
2117
|
template: null, file: () => 'README.md', line: () => null,
|
|
2117
2118
|
},
|
|
2118
2119
|
cursorAlertingReferenced: {
|
|
2119
2120
|
id: 'CU-T35', name: 'Alerting strategy referenced',
|
|
2120
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /alert|pagerduty|opsgenie|oncall|on.?call|incident/i.test(docs); },
|
|
2121
|
+
check: (ctx) => { if (!isMonitoringRelevant(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /alert|pagerduty|opsgenie|oncall|on.?call|incident/i.test(docs); },
|
|
2121
2122
|
impact: 'medium', rating: 3, category: 'monitoring',
|
|
2122
2123
|
fix: 'Document alerting strategy in README.md (PagerDuty, OpsGenie, on-call rotation).',
|
|
2123
2124
|
template: null, file: () => 'README.md', line: () => null,
|
|
2124
2125
|
},
|
|
2125
2126
|
cursorLogRotationMentioned: {
|
|
2126
2127
|
id: 'CU-T36', name: 'Log rotation / retention documented',
|
|
2127
|
-
check: (ctx) => { const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /log.{0,15}rotat|log.{0,15}retent|logrotate|retention.{0,15}polic/i.test(docs); },
|
|
2128
|
+
check: (ctx) => { if (!isMonitoringRelevant(ctx)) return null; const docs = allRulesContent(ctx) + (ctx.fileContent('CLAUDE.md') || '') + (ctx.fileContent('README.md') || ''); if (!docs.trim()) return null; return /log.{0,15}rotat|log.{0,15}retent|logrotate|retention.{0,15}polic/i.test(docs); },
|
|
2128
2129
|
impact: 'low', rating: 2, category: 'monitoring',
|
|
2129
2130
|
fix: 'Document log rotation/retention policy in README.md.',
|
|
2130
2131
|
template: null, file: () => 'README.md', line: () => null,
|