@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nerviq/cli",
3
- "version": "1.3.0",
3
+ "version": "1.3.1",
4
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": {
@@ -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({