@nerviq/cli 1.3.0 → 1.3.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/package.json +1 -1
- package/src/domain-pack-expansion.js +462 -0
- package/src/techniques.js +513 -0
package/package.json
CHANGED
|
@@ -191,6 +191,182 @@ const PACK_BLUEPRINTS = [
|
|
|
191
191
|
recommendedModules: ['Hardware integration guide', 'Device messaging review', 'Firmware safety checks'],
|
|
192
192
|
benchmarkFocus: ['device-surface safety', 'messaging reliability', 'firmware guardrails'],
|
|
193
193
|
},
|
|
194
|
+
{
|
|
195
|
+
key: 'healthcare',
|
|
196
|
+
label: 'Healthcare',
|
|
197
|
+
useWhen: 'Repos handling HIPAA compliance, HL7/FHIR integrations, PHI data, or medical records workflows.',
|
|
198
|
+
adoption: 'Recommended when patient data protection, regulatory compliance, and clinical data standards are central concerns.',
|
|
199
|
+
recommendedModules: ['HIPAA compliance guide', 'PHI data handling review', 'Clinical data safety checks'],
|
|
200
|
+
benchmarkFocus: ['HIPAA compliance posture', 'PHI protection', 'clinical data standards'],
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
key: 'fintech',
|
|
204
|
+
label: 'Fintech',
|
|
205
|
+
useWhen: 'Repos with PCI-DSS requirements, KYC/AML workflows, transaction processing, or financial regulation compliance.',
|
|
206
|
+
adoption: 'Recommended when financial data safety, regulatory compliance, and transaction integrity are core concerns.',
|
|
207
|
+
recommendedModules: ['PCI-DSS compliance guide', 'Transaction safety review', 'KYC/AML workflow checks'],
|
|
208
|
+
benchmarkFocus: ['PCI compliance posture', 'transaction integrity', 'regulatory readiness'],
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
key: 'gaming',
|
|
212
|
+
label: 'Gaming',
|
|
213
|
+
useWhen: 'Repos with ECS patterns, asset pipelines, multiplayer networking, or game loop architectures.',
|
|
214
|
+
adoption: 'Recommended when game loop performance, asset management, and multiplayer synchronization drive the workflow.',
|
|
215
|
+
recommendedModules: ['Game loop optimization guide', 'Asset pipeline review', 'Multiplayer safety checks'],
|
|
216
|
+
benchmarkFocus: ['game-loop performance', 'asset pipeline quality', 'multiplayer synchronization'],
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
key: 'iot',
|
|
220
|
+
label: 'IoT',
|
|
221
|
+
useWhen: 'Repos centered on MQTT messaging, edge computing, OTA firmware updates, or sensor data ingestion.',
|
|
222
|
+
adoption: 'Recommended when device communication protocols, edge processing, and sensor data pipelines are primary concerns.',
|
|
223
|
+
recommendedModules: ['MQTT integration guide', 'Edge computing review', 'OTA update safety checks'],
|
|
224
|
+
benchmarkFocus: ['device messaging reliability', 'edge processing safety', 'OTA update integrity'],
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
key: 'streaming',
|
|
228
|
+
label: 'Streaming',
|
|
229
|
+
useWhen: 'Repos handling video/audio codecs, CDN integration, adaptive bitrate streaming, or transcoding pipelines.',
|
|
230
|
+
adoption: 'Recommended when media delivery quality, codec management, and CDN configuration are central to the product.',
|
|
231
|
+
recommendedModules: ['Media delivery guide', 'Codec and transcoding review', 'CDN configuration checks'],
|
|
232
|
+
benchmarkFocus: ['media delivery quality', 'transcoding safety', 'CDN configuration'],
|
|
233
|
+
},
|
|
234
|
+
{
|
|
235
|
+
key: 'robotics',
|
|
236
|
+
label: 'Robotics',
|
|
237
|
+
useWhen: 'Repos with ROS integration, sensor fusion, real-time control constraints, or actuator management.',
|
|
238
|
+
adoption: 'Recommended when real-time safety, sensor data fusion, and hardware control are foundational to the repo.',
|
|
239
|
+
recommendedModules: ['Real-time control guide', 'Sensor fusion review', 'Actuator safety checks'],
|
|
240
|
+
benchmarkFocus: ['real-time constraint safety', 'sensor fusion quality', 'actuator control integrity'],
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
key: 'ar-vr',
|
|
244
|
+
label: 'AR / VR',
|
|
245
|
+
useWhen: 'Repos focused on 3D rendering, spatial computing, XR frameworks, or immersive experience development.',
|
|
246
|
+
adoption: 'Recommended when 3D rendering performance, spatial interaction, and XR platform integration drive the workflow.',
|
|
247
|
+
recommendedModules: ['3D rendering guide', 'Spatial computing review', 'XR framework checks'],
|
|
248
|
+
benchmarkFocus: ['rendering performance', 'spatial interaction quality', 'XR platform compatibility'],
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
key: 'climate-tech',
|
|
252
|
+
label: 'Climate Tech',
|
|
253
|
+
useWhen: 'Repos handling carbon tracking, energy modeling, ESG reporting, or environmental data workflows.',
|
|
254
|
+
adoption: 'Recommended when emissions data accuracy, energy modeling, and ESG compliance are central concerns.',
|
|
255
|
+
recommendedModules: ['Carbon tracking guide', 'Energy modeling review', 'ESG reporting checks'],
|
|
256
|
+
benchmarkFocus: ['emissions data accuracy', 'energy model quality', 'ESG compliance readiness'],
|
|
257
|
+
},
|
|
258
|
+
{
|
|
259
|
+
key: 'govtech',
|
|
260
|
+
label: 'GovTech',
|
|
261
|
+
useWhen: 'Repos requiring accessibility compliance, internationalization, audit trails, or government service standards.',
|
|
262
|
+
adoption: 'Recommended when accessibility, audit traceability, and regulatory compliance are mandatory product requirements.',
|
|
263
|
+
recommendedModules: ['Accessibility compliance guide', 'Audit trail review', 'Government standards checks'],
|
|
264
|
+
benchmarkFocus: ['accessibility compliance', 'audit trail completeness', 'regulatory standards'],
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
key: 'edtech',
|
|
268
|
+
label: 'EdTech',
|
|
269
|
+
useWhen: 'Repos with LMS integration, assessment engines, progress tracking, or educational content delivery.',
|
|
270
|
+
adoption: 'Recommended when learning management, student progress tracking, and assessment integrity are core product concerns.',
|
|
271
|
+
recommendedModules: ['LMS integration guide', 'Assessment engine review', 'Progress tracking checks'],
|
|
272
|
+
benchmarkFocus: ['LMS integration quality', 'assessment integrity', 'progress tracking accuracy'],
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
key: 'martech',
|
|
276
|
+
label: 'MarTech',
|
|
277
|
+
useWhen: 'Repos handling analytics pipelines, A/B testing frameworks, personalization engines, or marketing automation.',
|
|
278
|
+
adoption: 'Recommended when analytics accuracy, experiment integrity, and personalization workflows drive the product.',
|
|
279
|
+
recommendedModules: ['Analytics pipeline guide', 'A/B testing review', 'Personalization safety checks'],
|
|
280
|
+
benchmarkFocus: ['analytics accuracy', 'experiment integrity', 'personalization quality'],
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
key: 'proptech',
|
|
284
|
+
label: 'PropTech',
|
|
285
|
+
useWhen: 'Repos handling property data, geospatial analysis, building management systems, or real estate workflows.',
|
|
286
|
+
adoption: 'Recommended when property data accuracy, geospatial processing, and building system integration are central.',
|
|
287
|
+
recommendedModules: ['Property data guide', 'Geospatial processing review', 'Building systems checks'],
|
|
288
|
+
benchmarkFocus: ['property data accuracy', 'geospatial quality', 'building system integration'],
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
key: 'legaltech',
|
|
292
|
+
label: 'LegalTech',
|
|
293
|
+
useWhen: 'Repos focused on document automation, compliance management, e-discovery, or legal workflow tooling.',
|
|
294
|
+
adoption: 'Recommended when document accuracy, compliance tracking, and legal data handling are primary concerns.',
|
|
295
|
+
recommendedModules: ['Document automation guide', 'Compliance workflow review', 'E-discovery safety checks'],
|
|
296
|
+
benchmarkFocus: ['document accuracy', 'compliance tracking', 'legal data handling'],
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
key: 'agritech',
|
|
300
|
+
label: 'AgriTech',
|
|
301
|
+
useWhen: 'Repos handling agricultural sensor data, crop modeling, supply chain tracking, or farm management systems.',
|
|
302
|
+
adoption: 'Recommended when sensor data processing, crop analytics, and supply chain traceability are core concerns.',
|
|
303
|
+
recommendedModules: ['Sensor data pipeline guide', 'Crop modeling review', 'Supply chain tracking checks'],
|
|
304
|
+
benchmarkFocus: ['sensor data quality', 'crop model accuracy', 'supply chain traceability'],
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
key: 'biotech',
|
|
308
|
+
label: 'BioTech',
|
|
309
|
+
useWhen: 'Repos with bioinformatics pipelines, lab automation systems, or regulatory submission workflows.',
|
|
310
|
+
adoption: 'Recommended when biological data processing, lab workflow automation, and regulatory compliance drive the repo.',
|
|
311
|
+
recommendedModules: ['Bioinformatics pipeline guide', 'Lab automation review', 'Regulatory compliance checks'],
|
|
312
|
+
benchmarkFocus: ['bioinformatics accuracy', 'lab automation safety', 'regulatory compliance'],
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
key: 'cybersecurity',
|
|
316
|
+
label: 'Cybersecurity',
|
|
317
|
+
useWhen: 'Repos focused on threat detection, SIEM integration, incident response, or security operations tooling.',
|
|
318
|
+
adoption: 'Recommended when threat detection accuracy, incident response workflows, and security telemetry are central.',
|
|
319
|
+
recommendedModules: ['Threat detection guide', 'SIEM integration review', 'Incident response checks'],
|
|
320
|
+
benchmarkFocus: ['threat detection quality', 'SIEM integration', 'incident response readiness'],
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
key: 'logistics',
|
|
324
|
+
label: 'Logistics',
|
|
325
|
+
useWhen: 'Repos handling route optimization, fleet management, shipment tracking, or warehouse management systems.',
|
|
326
|
+
adoption: 'Recommended when routing accuracy, fleet coordination, and tracking reliability are core product concerns.',
|
|
327
|
+
recommendedModules: ['Route optimization guide', 'Fleet management review', 'Tracking system checks'],
|
|
328
|
+
benchmarkFocus: ['routing accuracy', 'fleet coordination', 'tracking reliability'],
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
key: 'media',
|
|
332
|
+
label: 'Media',
|
|
333
|
+
useWhen: 'Repos with CMS-driven publishing, digital asset management, content delivery pipelines, or editorial workflows.',
|
|
334
|
+
adoption: 'Recommended when content management, asset lifecycle, and delivery pipelines are central to the product.',
|
|
335
|
+
recommendedModules: ['Content management guide', 'Digital asset review', 'Content delivery checks'],
|
|
336
|
+
benchmarkFocus: ['content management quality', 'asset lifecycle safety', 'delivery pipeline reliability'],
|
|
337
|
+
},
|
|
338
|
+
{
|
|
339
|
+
key: 'social',
|
|
340
|
+
label: 'Social',
|
|
341
|
+
useWhen: 'Repos with feed algorithms, content moderation, real-time messaging, or social graph management.',
|
|
342
|
+
adoption: 'Recommended when feed quality, moderation safety, and messaging reliability are core product concerns.',
|
|
343
|
+
recommendedModules: ['Feed algorithm guide', 'Content moderation review', 'Messaging safety checks'],
|
|
344
|
+
benchmarkFocus: ['feed algorithm quality', 'moderation effectiveness', 'messaging reliability'],
|
|
345
|
+
},
|
|
346
|
+
{
|
|
347
|
+
key: 'travel',
|
|
348
|
+
label: 'Travel',
|
|
349
|
+
useWhen: 'Repos with booking engines, GDS integration, dynamic pricing, or travel inventory management.',
|
|
350
|
+
adoption: 'Recommended when booking reliability, pricing accuracy, and GDS integration are primary concerns.',
|
|
351
|
+
recommendedModules: ['Booking engine guide', 'GDS integration review', 'Pricing safety checks'],
|
|
352
|
+
benchmarkFocus: ['booking reliability', 'GDS integration quality', 'pricing accuracy'],
|
|
353
|
+
},
|
|
354
|
+
{
|
|
355
|
+
key: 'insurance',
|
|
356
|
+
label: 'Insurance',
|
|
357
|
+
useWhen: 'Repos handling claims processing, risk modeling, underwriting workflows, or policy management systems.',
|
|
358
|
+
adoption: 'Recommended when claims accuracy, risk model integrity, and underwriting workflow safety are core concerns.',
|
|
359
|
+
recommendedModules: ['Claims processing guide', 'Risk modeling review', 'Underwriting safety checks'],
|
|
360
|
+
benchmarkFocus: ['claims processing accuracy', 'risk model integrity', 'underwriting safety'],
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
key: 'energy',
|
|
364
|
+
label: 'Energy',
|
|
365
|
+
useWhen: 'Repos handling grid management, smart metering, renewable energy systems, or energy trading platforms.',
|
|
366
|
+
adoption: 'Recommended when grid reliability, metering accuracy, and energy system integration are central concerns.',
|
|
367
|
+
recommendedModules: ['Grid management guide', 'Smart metering review', 'Energy system checks'],
|
|
368
|
+
benchmarkFocus: ['grid reliability', 'metering accuracy', 'energy system integration'],
|
|
369
|
+
},
|
|
194
370
|
];
|
|
195
371
|
|
|
196
372
|
const PLATFORM_DEFAULTS = {
|
|
@@ -562,6 +738,292 @@ function detectAdditionalDomainPacks(options) {
|
|
|
562
738
|
hasDependency(depKeys, [/^johnny-five$/i]) ? 'Johnny-Five dependency detected.' : null,
|
|
563
739
|
]);
|
|
564
740
|
}
|
|
741
|
+
|
|
742
|
+
// Healthcare detection
|
|
743
|
+
if (
|
|
744
|
+
hasDependency(depKeys, [/^fhir/i, /^hl7/i, /^@medplum\//i, /^hapi-fhir/i]) ||
|
|
745
|
+
ctx.hasDir('hipaa') || ctx.hasDir('phi') || ctx.hasDir('medical') || ctx.hasDir('patients') ||
|
|
746
|
+
hasAnyFile(files, /(^|\/)(hipaa|fhir|hl7)/i)
|
|
747
|
+
) {
|
|
748
|
+
addMatch('healthcare', [
|
|
749
|
+
'Healthcare or clinical data signals detected.',
|
|
750
|
+
hasDependency(depKeys, [/^fhir/i, /^hl7/i]) ? 'FHIR/HL7 dependency detected.' : null,
|
|
751
|
+
ctx.hasDir('patients') ? 'Patients directory detected.' : null,
|
|
752
|
+
]);
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
// Fintech detection
|
|
756
|
+
if (
|
|
757
|
+
hasDependency(depKeys, [/^plaid$/i, /^@plaid\//i, /^dwolla$/i, /^alpaca/i, /^polygon\.io/i]) ||
|
|
758
|
+
ctx.hasDir('kyc') || ctx.hasDir('aml') || ctx.hasDir('transactions') ||
|
|
759
|
+
(pkg.keywords && pkg.keywords.some(k => ['fintech', 'pci-dss', 'kyc', 'aml'].includes(k)))
|
|
760
|
+
) {
|
|
761
|
+
addMatch('fintech', [
|
|
762
|
+
'Fintech or financial compliance signals detected.',
|
|
763
|
+
hasDependency(depKeys, [/^plaid$/i, /^@plaid\//i]) ? 'Plaid dependency detected.' : null,
|
|
764
|
+
ctx.hasDir('transactions') ? 'Transactions directory detected.' : null,
|
|
765
|
+
]);
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
// Gaming detection
|
|
769
|
+
if (
|
|
770
|
+
hasDependency(depKeys, [/^unity/i, /^godot/i, /^kaboom/i, /^excalibur/i, /^playcanvas/i, /^babylonjs/i, /^@babylonjs\//i]) ||
|
|
771
|
+
ctx.hasDir('sprites') || ctx.hasDir('assets/sprites') || ctx.hasDir('scenes') ||
|
|
772
|
+
hasAnyFile(files, /(^|\/)(game\.config|game\.json|\.gdproject|\.uproject)/i)
|
|
773
|
+
) {
|
|
774
|
+
addMatch('gaming', [
|
|
775
|
+
'Game development signals detected.',
|
|
776
|
+
ctx.hasDir('sprites') ? 'Sprites directory detected.' : null,
|
|
777
|
+
ctx.hasDir('scenes') ? 'Scenes directory detected.' : null,
|
|
778
|
+
]);
|
|
779
|
+
}
|
|
780
|
+
|
|
781
|
+
// IoT detection
|
|
782
|
+
if (
|
|
783
|
+
hasDependency(depKeys, [/^mqtt$/i, /^async-mqtt$/i, /^aws-iot-device-sdk/i, /^azure-iot/i, /^node-red/i]) ||
|
|
784
|
+
ctx.hasDir('sensors') || ctx.hasDir('devices') || ctx.hasDir('firmware') ||
|
|
785
|
+
hasAnyFile(files, /(^|\/)(mosquitto\.conf|iot|sensors)/i)
|
|
786
|
+
) {
|
|
787
|
+
addMatch('iot', [
|
|
788
|
+
'IoT or sensor data signals detected.',
|
|
789
|
+
hasDependency(depKeys, [/^mqtt$/i, /^async-mqtt$/i]) ? 'MQTT dependency detected.' : null,
|
|
790
|
+
ctx.hasDir('sensors') ? 'Sensors directory detected.' : null,
|
|
791
|
+
]);
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
// Streaming detection
|
|
795
|
+
if (
|
|
796
|
+
hasDependency(depKeys, [/^fluent-ffmpeg$/i, /^hls\.js$/i, /^dash\.js$/i, /^shaka-player$/i, /^video\.js$/i, /^mux/i, /^@mux\//i, /^cloudinary/i]) ||
|
|
797
|
+
ctx.hasDir('transcode') || ctx.hasDir('streams') ||
|
|
798
|
+
hasAnyFile(files, /(^|\/)(ffmpeg|transcode|hls|dash)/i)
|
|
799
|
+
) {
|
|
800
|
+
addMatch('streaming', [
|
|
801
|
+
'Media streaming signals detected.',
|
|
802
|
+
hasDependency(depKeys, [/^fluent-ffmpeg$/i]) ? 'FFmpeg dependency detected.' : null,
|
|
803
|
+
hasDependency(depKeys, [/^hls\.js$/i, /^dash\.js$/i]) ? 'Adaptive bitrate streaming dependency detected.' : null,
|
|
804
|
+
]);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
// Robotics detection
|
|
808
|
+
if (
|
|
809
|
+
hasDependency(depKeys, [/^rosnodejs$/i, /^rclnodejs$/i, /^roslib$/i, /^ros2/i]) ||
|
|
810
|
+
ctx.hasDir('ros') || ctx.hasDir('catkin_ws') || ctx.hasDir('robot') ||
|
|
811
|
+
hasAnyFile(files, /(^|\/)(CMakeLists\.txt|package\.xml|\.launch)/i) && ctx.hasDir('src')
|
|
812
|
+
) {
|
|
813
|
+
addMatch('robotics', [
|
|
814
|
+
'Robotics or ROS signals detected.',
|
|
815
|
+
hasDependency(depKeys, [/^rosnodejs$/i, /^rclnodejs$/i]) ? 'ROS dependency detected.' : null,
|
|
816
|
+
ctx.hasDir('catkin_ws') ? 'Catkin workspace detected.' : null,
|
|
817
|
+
]);
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
// AR/VR detection
|
|
821
|
+
if (
|
|
822
|
+
hasDependency(depKeys, [/^aframe$/i, /^@react-three\/xr$/i, /^webxr/i, /^@babylonjs\/core$/i, /^ar\.js$/i, /^mind-ar/i, /^8thwall/i]) ||
|
|
823
|
+
ctx.hasDir('xr') || ctx.hasDir('vr') || ctx.hasDir('ar') ||
|
|
824
|
+
hasAnyFile(files, /(^|\/)(.xr|spatial|immersive)/i)
|
|
825
|
+
) {
|
|
826
|
+
addMatch('ar-vr', [
|
|
827
|
+
'AR/VR or spatial computing signals detected.',
|
|
828
|
+
hasDependency(depKeys, [/^aframe$/i]) ? 'A-Frame dependency detected.' : null,
|
|
829
|
+
hasDependency(depKeys, [/^@react-three\/xr$/i]) ? 'React Three XR dependency detected.' : null,
|
|
830
|
+
]);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
// Climate Tech detection
|
|
834
|
+
if (
|
|
835
|
+
hasDependency(depKeys, [/^carbon/i, /^climatiq/i, /^patch-node$/i, /^@cloverly\//i]) ||
|
|
836
|
+
ctx.hasDir('emissions') || ctx.hasDir('carbon') || ctx.hasDir('esg') ||
|
|
837
|
+
(pkg.keywords && pkg.keywords.some(k => ['carbon', 'climate', 'esg', 'sustainability', 'emissions'].includes(k)))
|
|
838
|
+
) {
|
|
839
|
+
addMatch('climate-tech', [
|
|
840
|
+
'Climate tech or sustainability signals detected.',
|
|
841
|
+
ctx.hasDir('emissions') ? 'Emissions directory detected.' : null,
|
|
842
|
+
ctx.hasDir('esg') ? 'ESG directory detected.' : null,
|
|
843
|
+
]);
|
|
844
|
+
}
|
|
845
|
+
|
|
846
|
+
// GovTech detection
|
|
847
|
+
if (
|
|
848
|
+
hasDependency(depKeys, [/^pa11y$/i, /^axe-core$/i, /^@axe-core\//i, /^react-aria/i, /^@react-aria\//i]) ||
|
|
849
|
+
ctx.hasDir('audit-logs') || ctx.hasDir('accessibility') ||
|
|
850
|
+
(pkg.keywords && pkg.keywords.some(k => ['government', 'govtech', 'a11y', 'wcag', 'section508'].includes(k)))
|
|
851
|
+
) {
|
|
852
|
+
addMatch('govtech', [
|
|
853
|
+
'Government technology or accessibility compliance signals detected.',
|
|
854
|
+
hasDependency(depKeys, [/^axe-core$/i, /^@axe-core\//i]) ? 'Axe accessibility dependency detected.' : null,
|
|
855
|
+
ctx.hasDir('audit-logs') ? 'Audit logs directory detected.' : null,
|
|
856
|
+
]);
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
// EdTech detection
|
|
860
|
+
if (
|
|
861
|
+
hasDependency(depKeys, [/^scorm/i, /^xapi/i, /^lti/i, /^@canvas\//i]) ||
|
|
862
|
+
ctx.hasDir('courses') || ctx.hasDir('assessments') || ctx.hasDir('curriculum') || ctx.hasDir('lms') ||
|
|
863
|
+
(pkg.keywords && pkg.keywords.some(k => ['edtech', 'lms', 'elearning', 'assessment'].includes(k)))
|
|
864
|
+
) {
|
|
865
|
+
addMatch('edtech', [
|
|
866
|
+
'EdTech or learning management signals detected.',
|
|
867
|
+
ctx.hasDir('courses') ? 'Courses directory detected.' : null,
|
|
868
|
+
ctx.hasDir('assessments') ? 'Assessments directory detected.' : null,
|
|
869
|
+
]);
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
// MarTech detection
|
|
873
|
+
if (
|
|
874
|
+
hasDependency(depKeys, [/^@segment\//i, /^segment/i, /^mixpanel$/i, /^amplitude/i, /^optimizely/i, /^@optimizely\//i, /^launchdarkly/i, /^@launchdarkly\//i]) ||
|
|
875
|
+
ctx.hasDir('analytics') || ctx.hasDir('experiments') || ctx.hasDir('personalization') ||
|
|
876
|
+
hasAnyFile(files, /(^|\/)(segment|analytics|ab-test)/i)
|
|
877
|
+
) {
|
|
878
|
+
addMatch('martech', [
|
|
879
|
+
'Marketing technology or analytics signals detected.',
|
|
880
|
+
hasDependency(depKeys, [/^@segment\//i, /^segment/i]) ? 'Segment dependency detected.' : null,
|
|
881
|
+
hasDependency(depKeys, [/^optimizely/i, /^@optimizely\//i]) ? 'Optimizely dependency detected.' : null,
|
|
882
|
+
]);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// PropTech detection
|
|
886
|
+
if (
|
|
887
|
+
hasDependency(depKeys, [/^turf$/i, /^@turf\//i, /^mapbox/i, /^@mapbox\//i, /^leaflet$/i, /^openlayers/i]) ||
|
|
888
|
+
ctx.hasDir('properties') || ctx.hasDir('geospatial') || ctx.hasDir('buildings') ||
|
|
889
|
+
(pkg.keywords && pkg.keywords.some(k => ['proptech', 'real-estate', 'geospatial', 'property'].includes(k)))
|
|
890
|
+
) {
|
|
891
|
+
addMatch('proptech', [
|
|
892
|
+
'Property technology or geospatial signals detected.',
|
|
893
|
+
hasDependency(depKeys, [/^@turf\//i, /^turf$/i]) ? 'Turf.js geospatial dependency detected.' : null,
|
|
894
|
+
hasDependency(depKeys, [/^mapbox/i, /^@mapbox\//i]) ? 'Mapbox dependency detected.' : null,
|
|
895
|
+
]);
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
// LegalTech detection
|
|
899
|
+
if (
|
|
900
|
+
hasDependency(depKeys, [/^docusign/i, /^@docusign\//i, /^docassemble/i, /^clio/i]) ||
|
|
901
|
+
ctx.hasDir('contracts') || ctx.hasDir('legal') || ctx.hasDir('compliance') || ctx.hasDir('discovery') ||
|
|
902
|
+
(pkg.keywords && pkg.keywords.some(k => ['legaltech', 'legal', 'compliance', 'e-discovery', 'ediscovery'].includes(k)))
|
|
903
|
+
) {
|
|
904
|
+
addMatch('legaltech', [
|
|
905
|
+
'Legal technology or compliance signals detected.',
|
|
906
|
+
hasDependency(depKeys, [/^docusign/i, /^@docusign\//i]) ? 'DocuSign dependency detected.' : null,
|
|
907
|
+
ctx.hasDir('contracts') ? 'Contracts directory detected.' : null,
|
|
908
|
+
]);
|
|
909
|
+
}
|
|
910
|
+
|
|
911
|
+
// AgriTech detection
|
|
912
|
+
if (
|
|
913
|
+
hasDependency(depKeys, [/^agworld/i, /^farmos/i, /^cropsar/i]) ||
|
|
914
|
+
ctx.hasDir('crops') || ctx.hasDir('farm') || ctx.hasDir('agriculture') || ctx.hasDir('harvest') ||
|
|
915
|
+
(pkg.keywords && pkg.keywords.some(k => ['agritech', 'agriculture', 'farming', 'crop', 'precision-agriculture'].includes(k)))
|
|
916
|
+
) {
|
|
917
|
+
addMatch('agritech', [
|
|
918
|
+
'Agricultural technology signals detected.',
|
|
919
|
+
ctx.hasDir('crops') ? 'Crops directory detected.' : null,
|
|
920
|
+
ctx.hasDir('farm') ? 'Farm directory detected.' : null,
|
|
921
|
+
]);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// BioTech detection
|
|
925
|
+
if (
|
|
926
|
+
hasDependency(depKeys, [/^bionode/i, /^bioinformatics/i, /^@biom3\//i, /^openbabel/i]) ||
|
|
927
|
+
ctx.hasDir('bioinformatics') || ctx.hasDir('lab') || ctx.hasDir('sequences') || ctx.hasDir('genomics') ||
|
|
928
|
+
hasAnyFile(files, /(^|\/)(\.fasta|\.fastq|\.vcf|\.bam|\.sam)/i)
|
|
929
|
+
) {
|
|
930
|
+
addMatch('biotech', [
|
|
931
|
+
'Biotech or bioinformatics signals detected.',
|
|
932
|
+
ctx.hasDir('genomics') ? 'Genomics directory detected.' : null,
|
|
933
|
+
hasAnyFile(files, /(\.fasta|\.fastq|\.vcf)/i) ? 'Bioinformatics data files detected.' : null,
|
|
934
|
+
]);
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
// Cybersecurity detection
|
|
938
|
+
if (
|
|
939
|
+
hasDependency(depKeys, [/^snort/i, /^suricata/i, /^zeek/i, /^wazuh/i, /^elastic-siem/i, /^splunk/i, /^@elastic\/elasticsearch$/i]) ||
|
|
940
|
+
ctx.hasDir('threats') || ctx.hasDir('incidents') || ctx.hasDir('siem') || ctx.hasDir('detection-rules') ||
|
|
941
|
+
(pkg.keywords && pkg.keywords.some(k => ['cybersecurity', 'siem', 'threat-detection', 'incident-response'].includes(k)))
|
|
942
|
+
) {
|
|
943
|
+
addMatch('cybersecurity', [
|
|
944
|
+
'Cybersecurity or threat detection signals detected.',
|
|
945
|
+
ctx.hasDir('detection-rules') ? 'Detection rules directory detected.' : null,
|
|
946
|
+
ctx.hasDir('incidents') ? 'Incidents directory detected.' : null,
|
|
947
|
+
]);
|
|
948
|
+
}
|
|
949
|
+
|
|
950
|
+
// Logistics detection
|
|
951
|
+
if (
|
|
952
|
+
hasDependency(depKeys, [/^graphhopper/i, /^osrm/i, /^mapbox-directions/i, /^@googlemaps\//i]) ||
|
|
953
|
+
ctx.hasDir('routes') || ctx.hasDir('fleet') || ctx.hasDir('shipments') || ctx.hasDir('warehouse') ||
|
|
954
|
+
(pkg.keywords && pkg.keywords.some(k => ['logistics', 'fleet', 'routing', 'shipment', 'warehouse'].includes(k)))
|
|
955
|
+
) {
|
|
956
|
+
addMatch('logistics', [
|
|
957
|
+
'Logistics or fleet management signals detected.',
|
|
958
|
+
ctx.hasDir('fleet') ? 'Fleet directory detected.' : null,
|
|
959
|
+
ctx.hasDir('shipments') ? 'Shipments directory detected.' : null,
|
|
960
|
+
]);
|
|
961
|
+
}
|
|
962
|
+
|
|
963
|
+
// Media detection
|
|
964
|
+
if (
|
|
965
|
+
hasDependency(depKeys, [/^keystonejs/i, /^@keystonejs\//i, /^ghost/i, /^@tryghost\//i, /^wordpress/i, /^tinymce/i]) ||
|
|
966
|
+
ctx.hasDir('editorial') || ctx.hasDir('dam') || ctx.hasDir('assets/media') || ctx.hasDir('publications') ||
|
|
967
|
+
(pkg.keywords && pkg.keywords.some(k => ['media', 'publishing', 'editorial', 'digital-asset'].includes(k)))
|
|
968
|
+
) {
|
|
969
|
+
addMatch('media', [
|
|
970
|
+
'Media or digital publishing signals detected.',
|
|
971
|
+
hasDependency(depKeys, [/^keystonejs/i, /^@keystonejs\//i]) ? 'KeystoneJS CMS detected.' : null,
|
|
972
|
+
ctx.hasDir('editorial') ? 'Editorial directory detected.' : null,
|
|
973
|
+
]);
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
// Social detection
|
|
977
|
+
if (
|
|
978
|
+
hasDependency(depKeys, [/^stream-chat/i, /^@stream-io\//i, /^getstream/i, /^socket\.io$/i, /^perspective-api/i]) ||
|
|
979
|
+
ctx.hasDir('feed') || ctx.hasDir('moderation') || ctx.hasDir('messaging') || ctx.hasDir('social') ||
|
|
980
|
+
(pkg.keywords && pkg.keywords.some(k => ['social', 'feed', 'moderation', 'messaging', 'community'].includes(k)))
|
|
981
|
+
) {
|
|
982
|
+
addMatch('social', [
|
|
983
|
+
'Social platform signals detected.',
|
|
984
|
+
ctx.hasDir('feed') ? 'Feed directory detected.' : null,
|
|
985
|
+
ctx.hasDir('moderation') ? 'Moderation directory detected.' : null,
|
|
986
|
+
]);
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
// Travel detection
|
|
990
|
+
if (
|
|
991
|
+
hasDependency(depKeys, [/^amadeus/i, /^sabre/i, /^travelport/i, /^booking/i, /^skyscanner/i]) ||
|
|
992
|
+
ctx.hasDir('bookings') || ctx.hasDir('reservations') || ctx.hasDir('itineraries') || ctx.hasDir('pricing') ||
|
|
993
|
+
(pkg.keywords && pkg.keywords.some(k => ['travel', 'booking', 'gds', 'hospitality', 'reservation'].includes(k)))
|
|
994
|
+
) {
|
|
995
|
+
addMatch('travel', [
|
|
996
|
+
'Travel or booking platform signals detected.',
|
|
997
|
+
hasDependency(depKeys, [/^amadeus/i]) ? 'Amadeus GDS dependency detected.' : null,
|
|
998
|
+
ctx.hasDir('bookings') ? 'Bookings directory detected.' : null,
|
|
999
|
+
]);
|
|
1000
|
+
}
|
|
1001
|
+
|
|
1002
|
+
// Insurance detection
|
|
1003
|
+
if (
|
|
1004
|
+
hasDependency(depKeys, [/^guidewire/i, /^duck-creek/i, /^actuarial/i, /^socotra/i]) ||
|
|
1005
|
+
ctx.hasDir('claims') || ctx.hasDir('underwriting') || ctx.hasDir('policies') || ctx.hasDir('risk-models') ||
|
|
1006
|
+
(pkg.keywords && pkg.keywords.some(k => ['insurance', 'insurtech', 'claims', 'underwriting', 'actuarial'].includes(k)))
|
|
1007
|
+
) {
|
|
1008
|
+
addMatch('insurance', [
|
|
1009
|
+
'Insurance or risk management signals detected.',
|
|
1010
|
+
ctx.hasDir('claims') ? 'Claims directory detected.' : null,
|
|
1011
|
+
ctx.hasDir('underwriting') ? 'Underwriting directory detected.' : null,
|
|
1012
|
+
]);
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
// Energy detection
|
|
1016
|
+
if (
|
|
1017
|
+
hasDependency(depKeys, [/^openadr/i, /^green-button/i, /^iec61850/i, /^modbus/i, /^opc-ua/i]) ||
|
|
1018
|
+
ctx.hasDir('grid') || ctx.hasDir('metering') || ctx.hasDir('energy') || ctx.hasDir('renewables') ||
|
|
1019
|
+
(pkg.keywords && pkg.keywords.some(k => ['energy', 'smart-grid', 'metering', 'renewable', 'utility'].includes(k)))
|
|
1020
|
+
) {
|
|
1021
|
+
addMatch('energy', [
|
|
1022
|
+
'Energy or grid management signals detected.',
|
|
1023
|
+
ctx.hasDir('grid') ? 'Grid directory detected.' : null,
|
|
1024
|
+
ctx.hasDir('metering') ? 'Metering directory detected.' : null,
|
|
1025
|
+
]);
|
|
1026
|
+
}
|
|
565
1027
|
}
|
|
566
1028
|
|
|
567
1029
|
module.exports = {
|
package/src/techniques.js
CHANGED
|
@@ -129,6 +129,26 @@ function isJavaProject(ctx) {
|
|
|
129
129
|
return ctx.__nerviqIsJava;
|
|
130
130
|
}
|
|
131
131
|
|
|
132
|
+
function isFlutterProject(ctx) {
|
|
133
|
+
if (ctx.__nerviqIsFlutter !== undefined) return ctx.__nerviqIsFlutter;
|
|
134
|
+
ctx.__nerviqIsFlutter = hasProjectFile(ctx, /(^|\/)pubspec\.yaml$/i);
|
|
135
|
+
return ctx.__nerviqIsFlutter;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function isSwiftProject(ctx) {
|
|
139
|
+
if (ctx.__nerviqIsSwift !== undefined) return ctx.__nerviqIsSwift;
|
|
140
|
+
ctx.__nerviqIsSwift = hasProjectFile(ctx, /(^|\/)Package\.swift$/i) ||
|
|
141
|
+
hasProjectFile(ctx, /\.xcodeproj/i);
|
|
142
|
+
return ctx.__nerviqIsSwift;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function isKotlinProject(ctx) {
|
|
146
|
+
if (ctx.__nerviqIsKotlin !== undefined) return ctx.__nerviqIsKotlin;
|
|
147
|
+
const gradle = (ctx.fileContent('build.gradle.kts') || '') + (ctx.fileContent('build.gradle') || '');
|
|
148
|
+
ctx.__nerviqIsKotlin = /kotlin/i.test(gradle);
|
|
149
|
+
return ctx.__nerviqIsKotlin;
|
|
150
|
+
}
|
|
151
|
+
|
|
132
152
|
function getPythonFiles(ctx) {
|
|
133
153
|
if (ctx.__nerviqPythonFiles) return ctx.__nerviqPythonFiles;
|
|
134
154
|
ctx.__nerviqPythonFiles = findProjectFiles(ctx, /\.py$/i);
|
|
@@ -3602,6 +3622,499 @@ const TECHNIQUES = {
|
|
|
3602
3622
|
},
|
|
3603
3623
|
|
|
3604
3624
|
|
|
3625
|
+
// === FLUTTER STACK CHECKS (category: 'flutter') ===============
|
|
3626
|
+
// ============================================================
|
|
3627
|
+
|
|
3628
|
+
pubspecExists: {
|
|
3629
|
+
id: 120401,
|
|
3630
|
+
name: 'pubspec.yaml exists',
|
|
3631
|
+
check: (ctx) => {
|
|
3632
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3633
|
+
return true;
|
|
3634
|
+
},
|
|
3635
|
+
impact: 'high',
|
|
3636
|
+
category: 'flutter',
|
|
3637
|
+
fix: 'Add a `pubspec.yaml` manifest so Flutter/Dart dependencies and project metadata are tracked.',
|
|
3638
|
+
confidence: 0.7,
|
|
3639
|
+
},
|
|
3640
|
+
|
|
3641
|
+
flutterAnalysis: {
|
|
3642
|
+
id: 120402,
|
|
3643
|
+
name: 'Flutter analysis options configured',
|
|
3644
|
+
check: (ctx) => {
|
|
3645
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3646
|
+
return ctx.files.some(f => /analysis_options\.yaml$/i.test(f));
|
|
3647
|
+
},
|
|
3648
|
+
impact: 'medium',
|
|
3649
|
+
category: 'flutter',
|
|
3650
|
+
fix: 'Add analysis_options.yaml for Dart/Flutter linting rules.',
|
|
3651
|
+
confidence: 0.8,
|
|
3652
|
+
},
|
|
3653
|
+
|
|
3654
|
+
flutterTestDir: {
|
|
3655
|
+
id: 120403,
|
|
3656
|
+
name: 'Flutter tests exist',
|
|
3657
|
+
check: (ctx) => {
|
|
3658
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3659
|
+
return hasProjectFile(ctx, /(^|\/)test\/.*_test\.dart$/i);
|
|
3660
|
+
},
|
|
3661
|
+
impact: 'high',
|
|
3662
|
+
category: 'flutter',
|
|
3663
|
+
fix: 'Add Flutter widget or unit tests in a `test/` directory with `_test.dart` suffix.',
|
|
3664
|
+
confidence: 0.8,
|
|
3665
|
+
},
|
|
3666
|
+
|
|
3667
|
+
flutterLintRules: {
|
|
3668
|
+
id: 120404,
|
|
3669
|
+
name: 'Flutter lint package configured',
|
|
3670
|
+
check: (ctx) => {
|
|
3671
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3672
|
+
const pubspec = readProjectFiles(ctx, /(^|\/)pubspec\.yaml$/i);
|
|
3673
|
+
return /flutter_lints|very_good_analysis/i.test(pubspec);
|
|
3674
|
+
},
|
|
3675
|
+
impact: 'medium',
|
|
3676
|
+
category: 'flutter',
|
|
3677
|
+
fix: 'Add `flutter_lints` or `very_good_analysis` to pubspec.yaml dev_dependencies for consistent linting.',
|
|
3678
|
+
confidence: 0.8,
|
|
3679
|
+
},
|
|
3680
|
+
|
|
3681
|
+
flutterPlatformDirs: {
|
|
3682
|
+
id: 120405,
|
|
3683
|
+
name: 'Flutter platform directories present',
|
|
3684
|
+
check: (ctx) => {
|
|
3685
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3686
|
+
return hasProjectFile(ctx, /^android\//i) && hasProjectFile(ctx, /^ios\//i);
|
|
3687
|
+
},
|
|
3688
|
+
impact: 'medium',
|
|
3689
|
+
category: 'flutter',
|
|
3690
|
+
fix: 'Run `flutter create .` to generate `android/` and `ios/` platform directories.',
|
|
3691
|
+
confidence: 0.7,
|
|
3692
|
+
},
|
|
3693
|
+
|
|
3694
|
+
flutterWebSupport: {
|
|
3695
|
+
id: 120406,
|
|
3696
|
+
name: 'Flutter web support enabled',
|
|
3697
|
+
check: (ctx) => {
|
|
3698
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3699
|
+
return hasProjectFile(ctx, /^web\//i);
|
|
3700
|
+
},
|
|
3701
|
+
impact: 'low',
|
|
3702
|
+
category: 'flutter',
|
|
3703
|
+
fix: 'Run `flutter create --platforms=web .` to add web support.',
|
|
3704
|
+
confidence: 0.7,
|
|
3705
|
+
},
|
|
3706
|
+
|
|
3707
|
+
flutterL10n: {
|
|
3708
|
+
id: 120407,
|
|
3709
|
+
name: 'Flutter localization configured',
|
|
3710
|
+
check: (ctx) => {
|
|
3711
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3712
|
+
const pubspec = readProjectFiles(ctx, /(^|\/)pubspec\.yaml$/i);
|
|
3713
|
+
return hasProjectFile(ctx, /(^|\/)l10n\.yaml$/i) || /\bintl\b/i.test(pubspec);
|
|
3714
|
+
},
|
|
3715
|
+
impact: 'medium',
|
|
3716
|
+
category: 'flutter',
|
|
3717
|
+
fix: 'Add `l10n.yaml` or the `intl` package to support localization and internationalization.',
|
|
3718
|
+
confidence: 0.7,
|
|
3719
|
+
},
|
|
3720
|
+
|
|
3721
|
+
flutterStateManagement: {
|
|
3722
|
+
id: 120408,
|
|
3723
|
+
name: 'Flutter state management configured',
|
|
3724
|
+
check: (ctx) => {
|
|
3725
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3726
|
+
const pubspec = readProjectFiles(ctx, /(^|\/)pubspec\.yaml$/i);
|
|
3727
|
+
return /riverpod|flutter_bloc|\bbloc\b|\bprovider\b/i.test(pubspec);
|
|
3728
|
+
},
|
|
3729
|
+
impact: 'medium',
|
|
3730
|
+
category: 'flutter',
|
|
3731
|
+
fix: 'Add a state management solution such as `riverpod`, `bloc`, or `provider` to pubspec.yaml.',
|
|
3732
|
+
confidence: 0.7,
|
|
3733
|
+
},
|
|
3734
|
+
|
|
3735
|
+
flutterNavigation: {
|
|
3736
|
+
id: 120409,
|
|
3737
|
+
name: 'Flutter routing configured',
|
|
3738
|
+
check: (ctx) => {
|
|
3739
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3740
|
+
const pubspec = readProjectFiles(ctx, /(^|\/)pubspec\.yaml$/i);
|
|
3741
|
+
return /go_router|auto_route/i.test(pubspec);
|
|
3742
|
+
},
|
|
3743
|
+
impact: 'medium',
|
|
3744
|
+
category: 'flutter',
|
|
3745
|
+
fix: 'Add `go_router` or `auto_route` for declarative, type-safe Flutter routing.',
|
|
3746
|
+
confidence: 0.7,
|
|
3747
|
+
},
|
|
3748
|
+
|
|
3749
|
+
flutterCIConfigured: {
|
|
3750
|
+
id: 120410,
|
|
3751
|
+
name: 'CI runs flutter test',
|
|
3752
|
+
check: (ctx) => {
|
|
3753
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3754
|
+
return /flutter test(\s|$)/i.test(getWorkflowContent(ctx));
|
|
3755
|
+
},
|
|
3756
|
+
impact: 'high',
|
|
3757
|
+
category: 'flutter',
|
|
3758
|
+
fix: 'Add `flutter test` to your CI workflow so tests run automatically on every change.',
|
|
3759
|
+
confidence: 0.8,
|
|
3760
|
+
},
|
|
3761
|
+
|
|
3762
|
+
flutterCodeGen: {
|
|
3763
|
+
id: 120411,
|
|
3764
|
+
name: 'Flutter code generation configured',
|
|
3765
|
+
check: (ctx) => {
|
|
3766
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3767
|
+
const pubspec = readProjectFiles(ctx, /(^|\/)pubspec\.yaml$/i);
|
|
3768
|
+
return /build_runner|freezed/i.test(pubspec);
|
|
3769
|
+
},
|
|
3770
|
+
impact: 'medium',
|
|
3771
|
+
category: 'flutter',
|
|
3772
|
+
fix: 'Add `build_runner` and/or `freezed` to pubspec.yaml for code generation support.',
|
|
3773
|
+
confidence: 0.7,
|
|
3774
|
+
},
|
|
3775
|
+
|
|
3776
|
+
flutterFirebase: {
|
|
3777
|
+
id: 120412,
|
|
3778
|
+
name: 'Flutter Firebase integration',
|
|
3779
|
+
check: (ctx) => {
|
|
3780
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3781
|
+
const pubspec = readProjectFiles(ctx, /(^|\/)pubspec\.yaml$/i);
|
|
3782
|
+
return /firebase/i.test(pubspec) || hasProjectFile(ctx, /(^|\/)firebase_options\.dart$/i);
|
|
3783
|
+
},
|
|
3784
|
+
impact: 'medium',
|
|
3785
|
+
category: 'flutter',
|
|
3786
|
+
fix: 'Add Firebase packages to pubspec.yaml and run `flutterfire configure` to generate firebase_options.dart.',
|
|
3787
|
+
confidence: 0.7,
|
|
3788
|
+
},
|
|
3789
|
+
|
|
3790
|
+
flutterAssets: {
|
|
3791
|
+
id: 120413,
|
|
3792
|
+
name: 'Flutter assets configured',
|
|
3793
|
+
check: (ctx) => {
|
|
3794
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3795
|
+
const pubspec = readProjectFiles(ctx, /(^|\/)pubspec\.yaml$/i);
|
|
3796
|
+
return /\bassets\s*:/i.test(pubspec);
|
|
3797
|
+
},
|
|
3798
|
+
impact: 'low',
|
|
3799
|
+
category: 'flutter',
|
|
3800
|
+
fix: 'Add an `assets:` section in pubspec.yaml to declare images, fonts, and other bundled resources.',
|
|
3801
|
+
confidence: 0.7,
|
|
3802
|
+
},
|
|
3803
|
+
|
|
3804
|
+
flutterFlavors: {
|
|
3805
|
+
id: 120414,
|
|
3806
|
+
name: 'Flutter flavors configured',
|
|
3807
|
+
check: (ctx) => {
|
|
3808
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3809
|
+
const pubspec = readProjectFiles(ctx, /(^|\/)pubspec\.yaml$/i);
|
|
3810
|
+
return /\bflavors?\b/i.test(pubspec) || /--flavor/i.test(getWorkflowContent(ctx));
|
|
3811
|
+
},
|
|
3812
|
+
impact: 'low',
|
|
3813
|
+
category: 'flutter',
|
|
3814
|
+
fix: 'Configure Flutter flavors for environment-specific builds (dev, staging, production).',
|
|
3815
|
+
confidence: 0.7,
|
|
3816
|
+
},
|
|
3817
|
+
|
|
3818
|
+
flutterContainerized: {
|
|
3819
|
+
id: 120415,
|
|
3820
|
+
name: 'Flutter Dockerfile present',
|
|
3821
|
+
check: (ctx) => {
|
|
3822
|
+
if (!isFlutterProject(ctx)) return null;
|
|
3823
|
+
const dockerfiles = readProjectFiles(ctx, /(^|\/)Dockerfile/i);
|
|
3824
|
+
return /flutter|dart/i.test(dockerfiles);
|
|
3825
|
+
},
|
|
3826
|
+
impact: 'low',
|
|
3827
|
+
category: 'flutter',
|
|
3828
|
+
fix: 'Add a Dockerfile that includes the Flutter or Dart SDK for containerized builds.',
|
|
3829
|
+
confidence: 0.7,
|
|
3830
|
+
},
|
|
3831
|
+
|
|
3832
|
+
// === SWIFT STACK CHECKS (category: 'swift') ==================
|
|
3833
|
+
// ============================================================
|
|
3834
|
+
|
|
3835
|
+
swiftPackageExists: {
|
|
3836
|
+
id: 120501,
|
|
3837
|
+
name: 'Swift package or Xcode project exists',
|
|
3838
|
+
check: (ctx) => {
|
|
3839
|
+
if (!isSwiftProject(ctx)) return null;
|
|
3840
|
+
return true;
|
|
3841
|
+
},
|
|
3842
|
+
impact: 'high',
|
|
3843
|
+
category: 'swift',
|
|
3844
|
+
fix: 'Add a `Package.swift` or `.xcodeproj` to define your Swift project structure.',
|
|
3845
|
+
confidence: 0.7,
|
|
3846
|
+
},
|
|
3847
|
+
|
|
3848
|
+
swiftLinter: {
|
|
3849
|
+
id: 120502,
|
|
3850
|
+
name: 'SwiftLint configured',
|
|
3851
|
+
check: (ctx) => {
|
|
3852
|
+
if (!isSwiftProject(ctx)) return null;
|
|
3853
|
+
return hasProjectFile(ctx, /(^|\/)(\.swiftlint\.yml|\.swiftlint\.yaml)$/i);
|
|
3854
|
+
},
|
|
3855
|
+
impact: 'medium',
|
|
3856
|
+
category: 'swift',
|
|
3857
|
+
fix: 'Add `.swiftlint.yml` to enforce Swift coding conventions.',
|
|
3858
|
+
confidence: 0.8,
|
|
3859
|
+
},
|
|
3860
|
+
|
|
3861
|
+
swiftTests: {
|
|
3862
|
+
id: 120503,
|
|
3863
|
+
name: 'Swift tests exist',
|
|
3864
|
+
check: (ctx) => {
|
|
3865
|
+
if (!isSwiftProject(ctx)) return null;
|
|
3866
|
+
return hasProjectFile(ctx, /(^|\/)Tests\//i) ||
|
|
3867
|
+
findProjectFiles(ctx, /\.swift$/i).some(f => /XCTest/i.test(ctx.fileContent(f) || ''));
|
|
3868
|
+
},
|
|
3869
|
+
impact: 'high',
|
|
3870
|
+
category: 'swift',
|
|
3871
|
+
fix: 'Add Swift tests in a `Tests/` directory using XCTest.',
|
|
3872
|
+
confidence: 0.8,
|
|
3873
|
+
},
|
|
3874
|
+
|
|
3875
|
+
swiftFormatter: {
|
|
3876
|
+
id: 120504,
|
|
3877
|
+
name: 'SwiftFormat configured',
|
|
3878
|
+
check: (ctx) => {
|
|
3879
|
+
if (!isSwiftProject(ctx)) return null;
|
|
3880
|
+
return hasProjectFile(ctx, /(^|\/)\.swiftformat$/i);
|
|
3881
|
+
},
|
|
3882
|
+
impact: 'medium',
|
|
3883
|
+
category: 'swift',
|
|
3884
|
+
fix: 'Add `.swiftformat` to enforce consistent Swift formatting.',
|
|
3885
|
+
confidence: 0.7,
|
|
3886
|
+
},
|
|
3887
|
+
|
|
3888
|
+
swiftCIConfigured: {
|
|
3889
|
+
id: 120505,
|
|
3890
|
+
name: 'CI runs swift test or xcodebuild',
|
|
3891
|
+
check: (ctx) => {
|
|
3892
|
+
if (!isSwiftProject(ctx)) return null;
|
|
3893
|
+
return /swift test|xcodebuild(\s|$)/i.test(getWorkflowContent(ctx));
|
|
3894
|
+
},
|
|
3895
|
+
impact: 'high',
|
|
3896
|
+
category: 'swift',
|
|
3897
|
+
fix: 'Add `swift test` or `xcodebuild test` to your CI workflow.',
|
|
3898
|
+
confidence: 0.8,
|
|
3899
|
+
},
|
|
3900
|
+
|
|
3901
|
+
swiftDocComments: {
|
|
3902
|
+
id: 120506,
|
|
3903
|
+
name: 'Swift doc comments present',
|
|
3904
|
+
check: (ctx) => {
|
|
3905
|
+
if (!isSwiftProject(ctx)) return null;
|
|
3906
|
+
const swiftFiles = findProjectFiles(ctx, /\.swift$/i);
|
|
3907
|
+
if (swiftFiles.length === 0) return null;
|
|
3908
|
+
return swiftFiles.some(f => /\/\/\//.test(ctx.fileContent(f) || ''));
|
|
3909
|
+
},
|
|
3910
|
+
impact: 'low',
|
|
3911
|
+
category: 'swift',
|
|
3912
|
+
fix: 'Add `///` documentation comments to public Swift APIs.',
|
|
3913
|
+
confidence: 0.7,
|
|
3914
|
+
},
|
|
3915
|
+
|
|
3916
|
+
swiftSPM: {
|
|
3917
|
+
id: 120507,
|
|
3918
|
+
name: 'Swift Package Manager used',
|
|
3919
|
+
check: (ctx) => {
|
|
3920
|
+
if (!isSwiftProject(ctx)) return null;
|
|
3921
|
+
return hasProjectFile(ctx, /(^|\/)Package\.swift$/i);
|
|
3922
|
+
},
|
|
3923
|
+
impact: 'medium',
|
|
3924
|
+
category: 'swift',
|
|
3925
|
+
fix: 'Add `Package.swift` to use Swift Package Manager for dependency management.',
|
|
3926
|
+
confidence: 0.7,
|
|
3927
|
+
},
|
|
3928
|
+
|
|
3929
|
+
swiftMinVersion: {
|
|
3930
|
+
id: 120508,
|
|
3931
|
+
name: 'Swift tools version specified',
|
|
3932
|
+
check: (ctx) => {
|
|
3933
|
+
if (!isSwiftProject(ctx)) return null;
|
|
3934
|
+
const pkg = readProjectFiles(ctx, /(^|\/)Package\.swift$/i);
|
|
3935
|
+
return /swift-tools-version/i.test(pkg);
|
|
3936
|
+
},
|
|
3937
|
+
impact: 'medium',
|
|
3938
|
+
category: 'swift',
|
|
3939
|
+
fix: 'Add `// swift-tools-version:5.9` (or appropriate version) at the top of Package.swift.',
|
|
3940
|
+
confidence: 0.8,
|
|
3941
|
+
},
|
|
3942
|
+
|
|
3943
|
+
swiftAccessControl: {
|
|
3944
|
+
id: 120509,
|
|
3945
|
+
name: 'Swift access control used',
|
|
3946
|
+
check: (ctx) => {
|
|
3947
|
+
if (!isSwiftProject(ctx)) return null;
|
|
3948
|
+
const swiftFiles = findProjectFiles(ctx, /\.swift$/i);
|
|
3949
|
+
if (swiftFiles.length === 0) return null;
|
|
3950
|
+
return swiftFiles.some(f => /\b(public|internal)\b/.test(ctx.fileContent(f) || ''));
|
|
3951
|
+
},
|
|
3952
|
+
impact: 'low',
|
|
3953
|
+
category: 'swift',
|
|
3954
|
+
fix: 'Use `public`/`internal` access control in Swift files to define clear API boundaries.',
|
|
3955
|
+
confidence: 0.7,
|
|
3956
|
+
},
|
|
3957
|
+
|
|
3958
|
+
swiftConcurrency: {
|
|
3959
|
+
id: 120510,
|
|
3960
|
+
name: 'Swift concurrency used',
|
|
3961
|
+
check: (ctx) => {
|
|
3962
|
+
if (!isSwiftProject(ctx)) return null;
|
|
3963
|
+
const swiftFiles = findProjectFiles(ctx, /\.swift$/i);
|
|
3964
|
+
if (swiftFiles.length === 0) return null;
|
|
3965
|
+
return swiftFiles.some(f => /\basync\b.*\bawait\b|\bawait\b/s.test(ctx.fileContent(f) || ''));
|
|
3966
|
+
},
|
|
3967
|
+
impact: 'low',
|
|
3968
|
+
category: 'swift',
|
|
3969
|
+
fix: 'Adopt Swift structured concurrency with `async`/`await` for modern asynchronous code.',
|
|
3970
|
+
confidence: 0.7,
|
|
3971
|
+
},
|
|
3972
|
+
|
|
3973
|
+
// === KOTLIN STACK CHECKS (category: 'kotlin') ================
|
|
3974
|
+
// ============================================================
|
|
3975
|
+
|
|
3976
|
+
kotlinGradlePlugin: {
|
|
3977
|
+
id: 120601,
|
|
3978
|
+
name: 'Kotlin Gradle plugin configured',
|
|
3979
|
+
check: (ctx) => {
|
|
3980
|
+
if (!isKotlinProject(ctx)) return null;
|
|
3981
|
+
const gradle = readProjectFiles(ctx, /(^|\/)(build\.gradle\.kts|build\.gradle)$/i);
|
|
3982
|
+
return /kotlin\(|org\.jetbrains\.kotlin/i.test(gradle);
|
|
3983
|
+
},
|
|
3984
|
+
impact: 'high',
|
|
3985
|
+
category: 'kotlin',
|
|
3986
|
+
fix: 'Apply the Kotlin Gradle plugin in build.gradle.kts to enable Kotlin compilation.',
|
|
3987
|
+
confidence: 0.8,
|
|
3988
|
+
},
|
|
3989
|
+
|
|
3990
|
+
kotlinVersion: {
|
|
3991
|
+
id: 120602,
|
|
3992
|
+
name: 'Kotlin version specified',
|
|
3993
|
+
check: (ctx) => {
|
|
3994
|
+
if (!isKotlinProject(ctx)) return null;
|
|
3995
|
+
const gradle = readProjectFiles(ctx, /(^|\/)(build\.gradle\.kts|build\.gradle|gradle\.properties)$/i);
|
|
3996
|
+
return /kotlinVersion|kotlin_version|org\.jetbrains\.kotlin.*\d+\.\d+/i.test(gradle);
|
|
3997
|
+
},
|
|
3998
|
+
impact: 'high',
|
|
3999
|
+
category: 'kotlin',
|
|
4000
|
+
fix: 'Pin the Kotlin version in gradle.properties or build.gradle.kts for reproducible builds.',
|
|
4001
|
+
confidence: 0.8,
|
|
4002
|
+
},
|
|
4003
|
+
|
|
4004
|
+
kotlinLinter: {
|
|
4005
|
+
id: 120603,
|
|
4006
|
+
name: 'Kotlin linter configured',
|
|
4007
|
+
check: (ctx) => {
|
|
4008
|
+
if (!isKotlinProject(ctx)) return null;
|
|
4009
|
+
const gradle = readProjectFiles(ctx, /(^|\/)(build\.gradle\.kts|build\.gradle)$/i);
|
|
4010
|
+
return /ktlint|detekt/i.test(gradle) ||
|
|
4011
|
+
hasProjectFile(ctx, /(^|\/)(\.editorconfig|detekt\.yml|detekt\.yaml)$/i);
|
|
4012
|
+
},
|
|
4013
|
+
impact: 'medium',
|
|
4014
|
+
category: 'kotlin',
|
|
4015
|
+
fix: 'Add `ktlint` or `detekt` to enforce Kotlin code style and static analysis.',
|
|
4016
|
+
confidence: 0.8,
|
|
4017
|
+
},
|
|
4018
|
+
|
|
4019
|
+
kotlinTests: {
|
|
4020
|
+
id: 120604,
|
|
4021
|
+
name: 'Kotlin tests exist',
|
|
4022
|
+
check: (ctx) => {
|
|
4023
|
+
if (!isKotlinProject(ctx)) return null;
|
|
4024
|
+
return hasProjectFile(ctx, /(^|\/)src\/test\/.*\.kt$/i) ||
|
|
4025
|
+
hasProjectFile(ctx, /(^|\/)test\/.*\.kt$/i);
|
|
4026
|
+
},
|
|
4027
|
+
impact: 'high',
|
|
4028
|
+
category: 'kotlin',
|
|
4029
|
+
fix: 'Add Kotlin tests in `src/test/` using JUnit or KotlinTest.',
|
|
4030
|
+
confidence: 0.8,
|
|
4031
|
+
},
|
|
4032
|
+
|
|
4033
|
+
kotlinCoroutines: {
|
|
4034
|
+
id: 120605,
|
|
4035
|
+
name: 'Kotlin Coroutines in dependencies',
|
|
4036
|
+
check: (ctx) => {
|
|
4037
|
+
if (!isKotlinProject(ctx)) return null;
|
|
4038
|
+
const gradle = readProjectFiles(ctx, /(^|\/)(build\.gradle\.kts|build\.gradle)$/i);
|
|
4039
|
+
return /kotlinx[.-]coroutines/i.test(gradle);
|
|
4040
|
+
},
|
|
4041
|
+
impact: 'medium',
|
|
4042
|
+
category: 'kotlin',
|
|
4043
|
+
fix: 'Add `kotlinx-coroutines-core` to dependencies for structured concurrency.',
|
|
4044
|
+
confidence: 0.7,
|
|
4045
|
+
},
|
|
4046
|
+
|
|
4047
|
+
kotlinSerialization: {
|
|
4048
|
+
id: 120606,
|
|
4049
|
+
name: 'Kotlin serialization configured',
|
|
4050
|
+
check: (ctx) => {
|
|
4051
|
+
if (!isKotlinProject(ctx)) return null;
|
|
4052
|
+
const gradle = readProjectFiles(ctx, /(^|\/)(build\.gradle\.kts|build\.gradle)$/i);
|
|
4053
|
+
return /kotlinx[.-]serialization/i.test(gradle);
|
|
4054
|
+
},
|
|
4055
|
+
impact: 'medium',
|
|
4056
|
+
category: 'kotlin',
|
|
4057
|
+
fix: 'Add `kotlinx.serialization` for type-safe, multiplatform serialization.',
|
|
4058
|
+
confidence: 0.7,
|
|
4059
|
+
},
|
|
4060
|
+
|
|
4061
|
+
kotlinCompose: {
|
|
4062
|
+
id: 120607,
|
|
4063
|
+
name: 'Jetpack Compose configured',
|
|
4064
|
+
check: (ctx) => {
|
|
4065
|
+
if (!isKotlinProject(ctx)) return null;
|
|
4066
|
+
const gradle = readProjectFiles(ctx, /(^|\/)(build\.gradle\.kts|build\.gradle)$/i);
|
|
4067
|
+
return /compose/i.test(gradle);
|
|
4068
|
+
},
|
|
4069
|
+
impact: 'medium',
|
|
4070
|
+
category: 'kotlin',
|
|
4071
|
+
fix: 'Enable Jetpack Compose in build.gradle.kts for modern declarative Android UI.',
|
|
4072
|
+
confidence: 0.7,
|
|
4073
|
+
},
|
|
4074
|
+
|
|
4075
|
+
kotlinCIConfigured: {
|
|
4076
|
+
id: 120608,
|
|
4077
|
+
name: 'CI runs Kotlin tests',
|
|
4078
|
+
check: (ctx) => {
|
|
4079
|
+
if (!isKotlinProject(ctx)) return null;
|
|
4080
|
+
return /gradle.*test|gradlew.*test/i.test(getWorkflowContent(ctx));
|
|
4081
|
+
},
|
|
4082
|
+
impact: 'high',
|
|
4083
|
+
category: 'kotlin',
|
|
4084
|
+
fix: 'Add `./gradlew test` to your CI workflow so Kotlin tests run automatically.',
|
|
4085
|
+
confidence: 0.8,
|
|
4086
|
+
},
|
|
4087
|
+
|
|
4088
|
+
kotlinMultiplatform: {
|
|
4089
|
+
id: 120609,
|
|
4090
|
+
name: 'Kotlin Multiplatform configured',
|
|
4091
|
+
check: (ctx) => {
|
|
4092
|
+
if (!isKotlinProject(ctx)) return null;
|
|
4093
|
+
const gradle = readProjectFiles(ctx, /(^|\/)(build\.gradle\.kts|build\.gradle)$/i);
|
|
4094
|
+
return /multiplatform/i.test(gradle);
|
|
4095
|
+
},
|
|
4096
|
+
impact: 'medium',
|
|
4097
|
+
category: 'kotlin',
|
|
4098
|
+
fix: 'Apply the `kotlin-multiplatform` Gradle plugin to share code across JVM, iOS, JS, and Native targets.',
|
|
4099
|
+
confidence: 0.7,
|
|
4100
|
+
},
|
|
4101
|
+
|
|
4102
|
+
kotlinDocComments: {
|
|
4103
|
+
id: 120610,
|
|
4104
|
+
name: 'KDoc comments present',
|
|
4105
|
+
check: (ctx) => {
|
|
4106
|
+
if (!isKotlinProject(ctx)) return null;
|
|
4107
|
+
const ktFiles = findProjectFiles(ctx, /\.kt$/i);
|
|
4108
|
+
if (ktFiles.length === 0) return null;
|
|
4109
|
+
return ktFiles.some(f => /\/\*\*/.test(ctx.fileContent(f) || ''));
|
|
4110
|
+
},
|
|
4111
|
+
impact: 'low',
|
|
4112
|
+
category: 'kotlin',
|
|
4113
|
+
fix: 'Add KDoc comments (`/** ... */`) to public Kotlin APIs for documentation generation.',
|
|
4114
|
+
confidence: 0.7,
|
|
4115
|
+
},
|
|
4116
|
+
|
|
4117
|
+
|
|
3605
4118
|
};
|
|
3606
4119
|
|
|
3607
4120
|
Object.assign(TECHNIQUES, buildSupplementalChecks({
|