@nerviq/cli 1.20.1 → 1.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (181) hide show
  1. package/LICENSE +23 -23
  2. package/README.md +2 -2
  3. package/package.json +1 -1
  4. package/src/activity.js +1039 -1039
  5. package/src/adoption-advisor.js +299 -299
  6. package/src/aider/config-parser.js +166 -166
  7. package/src/aider/context.js +4 -1
  8. package/src/aider/deep-review.js +316 -316
  9. package/src/aider/domain-packs.js +303 -303
  10. package/src/aider/freshness.js +93 -93
  11. package/src/aider/governance.js +253 -253
  12. package/src/aider/interactive.js +334 -334
  13. package/src/aider/mcp-packs.js +329 -329
  14. package/src/aider/patch.js +214 -214
  15. package/src/aider/plans.js +186 -186
  16. package/src/aider/premium.js +360 -360
  17. package/src/aider/setup.js +404 -404
  18. package/src/aider/techniques.js +312 -67
  19. package/src/analyze.js +951 -951
  20. package/src/anti-patterns.js +485 -485
  21. package/src/audit/instruction-files.js +180 -180
  22. package/src/audit/recommendations.js +577 -577
  23. package/src/auto-suggest.js +154 -154
  24. package/src/badge.js +13 -13
  25. package/src/behavioral-drift.js +801 -801
  26. package/src/benchmark.js +67 -67
  27. package/src/catalog.js +103 -103
  28. package/src/certification.js +128 -128
  29. package/src/codex/config-parser.js +183 -183
  30. package/src/codex/context.js +223 -223
  31. package/src/codex/deep-review.js +493 -493
  32. package/src/codex/domain-packs.js +394 -394
  33. package/src/codex/freshness.js +84 -84
  34. package/src/codex/governance.js +192 -192
  35. package/src/codex/interactive.js +618 -618
  36. package/src/codex/mcp-packs.js +914 -914
  37. package/src/codex/patch.js +209 -209
  38. package/src/codex/plans.js +251 -251
  39. package/src/codex/premium.js +614 -614
  40. package/src/codex/setup.js +591 -591
  41. package/src/continuous-ops.js +681 -681
  42. package/src/copilot/activity.js +309 -309
  43. package/src/copilot/deep-review.js +346 -346
  44. package/src/copilot/domain-packs.js +372 -372
  45. package/src/copilot/freshness.js +57 -57
  46. package/src/copilot/governance.js +222 -222
  47. package/src/copilot/interactive.js +406 -406
  48. package/src/copilot/mcp-packs.js +826 -826
  49. package/src/copilot/plans.js +253 -253
  50. package/src/copilot/premium.js +451 -451
  51. package/src/copilot/setup.js +488 -488
  52. package/src/cost-tracking.js +61 -61
  53. package/src/cursor/activity.js +301 -301
  54. package/src/cursor/config-parser.js +265 -265
  55. package/src/cursor/context.js +256 -256
  56. package/src/cursor/deep-review.js +334 -334
  57. package/src/cursor/domain-packs.js +368 -368
  58. package/src/cursor/freshness.js +65 -65
  59. package/src/cursor/governance.js +229 -229
  60. package/src/cursor/interactive.js +391 -391
  61. package/src/cursor/mcp-packs.js +828 -828
  62. package/src/cursor/plans.js +254 -254
  63. package/src/cursor/premium.js +469 -469
  64. package/src/cursor/setup.js +488 -488
  65. package/src/dashboard.js +493 -493
  66. package/src/deep-review.js +428 -428
  67. package/src/deprecation.js +98 -98
  68. package/src/diff-only.js +280 -280
  69. package/src/doctor.js +119 -119
  70. package/src/domain-pack-expansion.js +1033 -1033
  71. package/src/domain-packs.js +387 -387
  72. package/src/feedback.js +178 -178
  73. package/src/fix-engine.js +783 -783
  74. package/src/fix-prompts.js +122 -122
  75. package/src/formatters/sarif.js +115 -115
  76. package/src/freshness.js +74 -74
  77. package/src/gemini/config-parser.js +275 -275
  78. package/src/gemini/deep-review.js +559 -559
  79. package/src/gemini/domain-packs.js +393 -393
  80. package/src/gemini/freshness.js +66 -66
  81. package/src/gemini/governance.js +201 -201
  82. package/src/gemini/interactive.js +860 -860
  83. package/src/gemini/mcp-packs.js +915 -915
  84. package/src/gemini/plans.js +269 -269
  85. package/src/gemini/premium.js +760 -760
  86. package/src/gemini/setup.js +692 -692
  87. package/src/governance.js +72 -72
  88. package/src/harmony/add.js +68 -68
  89. package/src/harmony/advisor.js +333 -333
  90. package/src/harmony/canon.js +565 -565
  91. package/src/harmony/cli.js +591 -591
  92. package/src/harmony/drift.js +401 -401
  93. package/src/harmony/governance.js +313 -313
  94. package/src/harmony/memory.js +239 -239
  95. package/src/harmony/sync.js +475 -475
  96. package/src/harmony/watch.js +370 -370
  97. package/src/hook-validation.js +342 -342
  98. package/src/index.js +271 -271
  99. package/src/init.js +184 -184
  100. package/src/instruction-surfaces.js +185 -185
  101. package/src/integrations.js +144 -144
  102. package/src/interactive.js +118 -118
  103. package/src/locales/en.json +1 -1
  104. package/src/locales/es.json +1 -1
  105. package/src/mcp-packs.js +830 -830
  106. package/src/mcp-server.js +726 -726
  107. package/src/mcp-validation.js +337 -337
  108. package/src/nerviq-sync.json +7 -7
  109. package/src/opencode/config-parser.js +109 -109
  110. package/src/opencode/context.js +247 -247
  111. package/src/opencode/deep-review.js +313 -313
  112. package/src/opencode/domain-packs.js +262 -262
  113. package/src/opencode/freshness.js +66 -66
  114. package/src/opencode/governance.js +159 -159
  115. package/src/opencode/interactive.js +392 -392
  116. package/src/opencode/mcp-packs.js +705 -705
  117. package/src/opencode/patch.js +184 -184
  118. package/src/opencode/plans.js +231 -231
  119. package/src/opencode/premium.js +413 -413
  120. package/src/opencode/setup.js +449 -449
  121. package/src/opencode/techniques.js +27 -27
  122. package/src/operating-profile.js +574 -574
  123. package/src/org.js +152 -152
  124. package/src/permission-rules.js +218 -218
  125. package/src/plans.js +839 -839
  126. package/src/platform-change-manifest.js +86 -86
  127. package/src/plugins.js +110 -110
  128. package/src/policy-layers.js +210 -210
  129. package/src/profiles.js +124 -124
  130. package/src/prompt-injection.js +74 -74
  131. package/src/public-api.js +173 -173
  132. package/src/recommendation-rules.js +84 -84
  133. package/src/repo-archetype.js +386 -386
  134. package/src/secret-patterns.js +39 -39
  135. package/src/server.js +527 -527
  136. package/src/setup/analysis.js +607 -607
  137. package/src/setup/runtime.js +172 -172
  138. package/src/setup.js +677 -677
  139. package/src/shared/capabilities.js +194 -194
  140. package/src/source-urls.js +132 -132
  141. package/src/stack-checks.js +565 -565
  142. package/src/supplemental-checks.js +13 -13
  143. package/src/synergy/adaptive.js +261 -261
  144. package/src/synergy/compensation.js +137 -137
  145. package/src/synergy/evidence.js +193 -193
  146. package/src/synergy/learning.js +199 -199
  147. package/src/synergy/patterns.js +227 -227
  148. package/src/synergy/ranking.js +83 -83
  149. package/src/synergy/report.js +165 -165
  150. package/src/synergy/routing.js +146 -146
  151. package/src/techniques/api.js +407 -407
  152. package/src/techniques/automation.js +316 -316
  153. package/src/techniques/compliance.js +257 -257
  154. package/src/techniques/hygiene.js +294 -294
  155. package/src/techniques/instructions.js +243 -243
  156. package/src/techniques/observability.js +226 -226
  157. package/src/techniques/optimization.js +142 -142
  158. package/src/techniques/quality.js +318 -318
  159. package/src/techniques/security.js +237 -237
  160. package/src/techniques/shared.js +443 -443
  161. package/src/techniques/stacks.js +2294 -2294
  162. package/src/techniques/tools.js +106 -106
  163. package/src/techniques/workflow.js +413 -413
  164. package/src/techniques.js +81 -81
  165. package/src/terminology.js +73 -73
  166. package/src/token-estimate.js +35 -35
  167. package/src/usage-patterns.js +99 -99
  168. package/src/verification-metadata.js +145 -145
  169. package/src/watch.js +247 -247
  170. package/src/windsurf/activity.js +302 -302
  171. package/src/windsurf/config-parser.js +267 -267
  172. package/src/windsurf/deep-review.js +337 -337
  173. package/src/windsurf/domain-packs.js +370 -370
  174. package/src/windsurf/freshness.js +36 -36
  175. package/src/windsurf/governance.js +231 -231
  176. package/src/windsurf/interactive.js +388 -388
  177. package/src/windsurf/mcp-packs.js +792 -792
  178. package/src/windsurf/plans.js +247 -247
  179. package/src/windsurf/premium.js +468 -468
  180. package/src/windsurf/setup.js +471 -471
  181. package/src/workspace.js +375 -375
package/src/benchmark.js CHANGED
@@ -2,12 +2,12 @@ const fs = require('fs');
2
2
  const os = require('os');
3
3
  const path = require('path');
4
4
 
5
- const { version } = require('../package.json');
6
- const { audit } = require('./audit');
7
- const { setup } = require('./setup');
8
- const { analyzeProject } = require('./analyze');
9
- const { getGovernanceSummary } = require('./governance');
10
- const { formatTerminologyLines } = require('./terminology');
5
+ const { version } = require('../package.json');
6
+ const { audit } = require('./audit');
7
+ const { setup } = require('./setup');
8
+ const { analyzeProject } = require('./analyze');
9
+ const { getGovernanceSummary } = require('./governance');
10
+ const { formatTerminologyLines } = require('./terminology');
11
11
 
12
12
  function copyProject(sourceDir, targetDir) {
13
13
  fs.mkdirSync(targetDir, { recursive: true });
@@ -202,36 +202,36 @@ function buildCaseStudy(before, after, applyResult) {
202
202
  };
203
203
  }
204
204
 
205
- function renderBenchmarkMarkdown(report) {
206
- return [
207
- '# NERVIQ CLI Benchmark Report',
208
- '',
209
- `- Generated by: ${report.generatedBy}`,
210
- `- Created at: ${report.createdAt}`,
211
- `- Source repo: ${report.directory}`,
212
- '',
213
- '## Score Semantics',
214
- `- Baseline live audit score: ${report.scoreSemantics.baseline}`,
215
- `- Projected benchmark score: ${report.scoreSemantics.projected}`,
216
- `- Organic score: ${report.scoreSemantics.organic}`,
217
- '',
218
- '## Methodology',
219
- ...report.methodology.map(item => `- ${item}`),
220
- '',
221
- '## Baseline (Live Repo)',
222
- `- Live audit score: ${report.before.score}/100`,
223
- `- Organic live score: ${report.before.organicScore}/100`,
224
- `- Passing checks: ${report.before.passed}/${report.before.checkCount}`,
225
- '',
226
- '## Projected (Isolated Benchmark Copy)',
227
- `- Projected benchmark score: ${report.after.score}/100`,
228
- `- Projected organic score: ${report.after.organicScore}/100`,
229
- `- Passing checks: ${report.after.passed}/${report.after.checkCount}`,
230
- '',
231
- '## Delta',
232
- `- Projected score delta: ${report.delta.score}`,
233
- `- Projected organic score delta: ${report.delta.organicScore}`,
234
- `- Passed checks delta: ${report.delta.passed}`,
205
+ function renderBenchmarkMarkdown(report) {
206
+ return [
207
+ '# NERVIQ CLI Benchmark Report',
208
+ '',
209
+ `- Generated by: ${report.generatedBy}`,
210
+ `- Created at: ${report.createdAt}`,
211
+ `- Source repo: ${report.directory}`,
212
+ '',
213
+ '## Score Semantics',
214
+ `- Baseline live audit score: ${report.scoreSemantics.baseline}`,
215
+ `- Projected benchmark score: ${report.scoreSemantics.projected}`,
216
+ `- Organic score: ${report.scoreSemantics.organic}`,
217
+ '',
218
+ '## Methodology',
219
+ ...report.methodology.map(item => `- ${item}`),
220
+ '',
221
+ '## Baseline (Live Repo)',
222
+ `- Live audit score: ${report.before.score}/100`,
223
+ `- Organic live score: ${report.before.organicScore}/100`,
224
+ `- Passing checks: ${report.before.passed}/${report.before.checkCount}`,
225
+ '',
226
+ '## Projected (Isolated Benchmark Copy)',
227
+ `- Projected benchmark score: ${report.after.score}/100`,
228
+ `- Projected organic score: ${report.after.organicScore}/100`,
229
+ `- Passing checks: ${report.after.passed}/${report.after.checkCount}`,
230
+ '',
231
+ '## Delta',
232
+ `- Projected score delta: ${report.delta.score}`,
233
+ `- Projected organic score delta: ${report.delta.organicScore}`,
234
+ `- Passed checks delta: ${report.delta.passed}`,
235
235
  '',
236
236
  '## Executive Summary',
237
237
  `- ${report.executiveSummary.headline}`,
@@ -291,14 +291,14 @@ async function runBenchmark(options) {
291
291
  schemaVersion: 1,
292
292
  generatedBy: `nerviq@${version}`,
293
293
  createdAt: new Date().toISOString(),
294
- directory: sourceDir,
295
- platform,
296
- scoreSemantics: {
297
- baseline: 'current repo state before benchmark runs',
298
- projected: 'starter-safe post-setup score measured on an isolated temp copy',
299
- organic: 'repo-owned config quality excluding starter-generated Nerviq assets',
300
- },
301
- methodology: [
294
+ directory: sourceDir,
295
+ platform,
296
+ scoreSemantics: {
297
+ baseline: 'current repo state before benchmark runs',
298
+ projected: 'starter-safe post-setup score measured on an isolated temp copy',
299
+ organic: 'repo-owned config quality excluding starter-generated Nerviq assets',
300
+ },
301
+ methodology: [
302
302
  'Run a baseline audit on the source repo.',
303
303
  'Copy the repo into a temporary isolated workspace.',
304
304
  `Apply starter-safe ${platform === 'codex' ? 'Codex' : 'Claude'} artifacts only on the isolated copy.`,
@@ -327,29 +327,29 @@ function printBenchmark(report, options = {}) {
327
327
  return;
328
328
  }
329
329
 
330
- console.log('');
331
- console.log(' nerviq benchmark');
332
- console.log(' ═══════════════════════════════════════');
333
- console.log(' Runs in an isolated temp copy. Your current repo is not modified.');
334
- console.log(' Score type: baseline = live repo audit, projected = isolated post-setup benchmark.');
335
- console.log('');
336
- const orgDeltaSign = report.delta.organicScore >= 0 ? '+' : '';
337
- const totalDeltaSign = report.delta.score >= 0 ? '+' : '';
338
- console.log(` Projected organic delta: \x1b[1m${orgDeltaSign}${report.delta.organicScore} points\x1b[0m (repo-owned config quality)`);
339
- console.log(` Projected total delta with nerviq setup: ${totalDeltaSign}${report.delta.score} points`);
340
- console.log('');
341
- console.log(` Baseline live audit: organic ${report.before.organicScore}/100, total ${report.before.score}/100`);
342
- console.log(` Projected after setup: organic ${report.after.organicScore}/100, total ${report.after.score}/100`);
343
- console.log('');
344
- console.log(` ${report.executiveSummary.headline}`);
345
- console.log(` Recommendation: ${report.executiveSummary.decisionGuidance}`);
346
- console.log(` Workflow evidence: ${report.workflowEvidence.summary.passed}/${report.workflowEvidence.summary.total} tasks (${report.workflowEvidence.summary.coverageScore}%)`);
347
- console.log('');
348
- for (const line of formatTerminologyLines(['governance', 'hooks', 'mcp'])) {
349
- console.log(line);
350
- }
351
- console.log('');
352
- }
330
+ console.log('');
331
+ console.log(' nerviq benchmark');
332
+ console.log(' ═══════════════════════════════════════');
333
+ console.log(' Runs in an isolated temp copy. Your current repo is not modified.');
334
+ console.log(' Score type: baseline = live repo audit, projected = isolated post-setup benchmark.');
335
+ console.log('');
336
+ const orgDeltaSign = report.delta.organicScore >= 0 ? '+' : '';
337
+ const totalDeltaSign = report.delta.score >= 0 ? '+' : '';
338
+ console.log(` Projected organic delta: \x1b[1m${orgDeltaSign}${report.delta.organicScore} points\x1b[0m (repo-owned config quality)`);
339
+ console.log(` Projected total delta with nerviq setup: ${totalDeltaSign}${report.delta.score} points`);
340
+ console.log('');
341
+ console.log(` Baseline live audit: organic ${report.before.organicScore}/100, total ${report.before.score}/100`);
342
+ console.log(` Projected after setup: organic ${report.after.organicScore}/100, total ${report.after.score}/100`);
343
+ console.log('');
344
+ console.log(` ${report.executiveSummary.headline}`);
345
+ console.log(` Recommendation: ${report.executiveSummary.decisionGuidance}`);
346
+ console.log(` Workflow evidence: ${report.workflowEvidence.summary.passed}/${report.workflowEvidence.summary.total} tasks (${report.workflowEvidence.summary.coverageScore}%)`);
347
+ console.log('');
348
+ for (const line of formatTerminologyLines(['governance', 'hooks', 'mcp'])) {
349
+ console.log(line);
350
+ }
351
+ console.log('');
352
+ }
353
353
 
354
354
  function writeBenchmarkReport(report, outFile) {
355
355
  fs.mkdirSync(path.dirname(outFile), { recursive: true });
package/src/catalog.js CHANGED
@@ -1,103 +1,103 @@
1
- /**
2
- * Public Check Catalog Generator
3
- * Reads ALL technique files from all 8 platforms and generates a unified JSON catalog.
4
- */
5
-
6
- const fs = require('fs');
7
- const path = require('path');
8
- const { version } = require('../package.json');
9
-
10
- const { TECHNIQUES: CLAUDE_TECHNIQUES } = require('./techniques');
11
- const { CODEX_TECHNIQUES } = require('./codex/techniques');
12
- const { GEMINI_TECHNIQUES } = require('./gemini/techniques');
13
- const { COPILOT_TECHNIQUES } = require('./copilot/techniques');
14
- const { CURSOR_TECHNIQUES } = require('./cursor/techniques');
15
- const { WINDSURF_TECHNIQUES } = require('./windsurf/techniques');
16
- const { AIDER_TECHNIQUES } = require('./aider/techniques');
17
- const { OPENCODE_TECHNIQUES } = require('./opencode/techniques');
18
- const { attachSourceUrls } = require('./source-urls');
19
-
20
- const PLATFORM_MAP = {
21
- claude: CLAUDE_TECHNIQUES,
22
- codex: CODEX_TECHNIQUES,
23
- gemini: GEMINI_TECHNIQUES,
24
- copilot: COPILOT_TECHNIQUES,
25
- cursor: CURSOR_TECHNIQUES,
26
- windsurf: WINDSURF_TECHNIQUES,
27
- aider: AIDER_TECHNIQUES,
28
- opencode: OPENCODE_TECHNIQUES,
29
- };
30
-
31
- /**
32
- * Generate a unified catalog array from all platform technique files.
33
- * Each entry contains:
34
- * platform, id, key, name, category, impact, rating, fix, sourceUrl,
35
- * confidence, lastVerified, template, deprecated
36
- */
37
- function generateCatalog() {
38
- const catalog = [];
39
-
40
- for (const [platform, techniques] of Object.entries(PLATFORM_MAP)) {
41
- // Clone techniques so we don't mutate the originals
42
- const cloned = {};
43
- for (const [key, tech] of Object.entries(techniques)) {
44
- cloned[key] = { ...tech };
45
- }
46
-
47
- // Attach source URLs
48
- try {
49
- attachSourceUrls(platform, cloned);
50
- } catch (_) {
51
- // If source URLs fail for a platform, continue without them
52
- }
53
-
54
- for (const [key, tech] of Object.entries(cloned)) {
55
- catalog.push({
56
- platform,
57
- id: tech.id ?? null,
58
- key,
59
- name: tech.name ?? null,
60
- category: tech.category ?? null,
61
- impact: tech.impact ?? null,
62
- rating: tech.rating ?? null,
63
- fix: tech.fix ?? null,
64
- sourceUrl: tech.sourceUrl ?? null,
65
- confidence: tech.confidence ?? null,
66
- lastVerified: tech.lastVerified ?? null,
67
- template: tech.template ?? null,
68
- deprecated: tech.deprecated ?? false,
69
- });
70
- }
71
- }
72
-
73
- return catalog;
74
- }
75
-
76
- /**
77
- * Generate a catalog envelope with version metadata.
78
- * @returns {{ catalogVersion: string, generatedAt: string, totalChecks: number, checks: Object[] }}
79
- */
80
- function generateCatalogWithVersion() {
81
- const checks = generateCatalog();
82
- return {
83
- catalogVersion: version,
84
- generatedAt: new Date().toISOString(),
85
- totalChecks: checks.length,
86
- checks,
87
- };
88
- }
89
-
90
- /**
91
- * Write the catalog as formatted JSON to the given output path.
92
- * @param {string} outputPath - Absolute or relative path for the JSON file
93
- * @returns {{ path: string, count: number }} Written path and entry count
94
- */
95
- function writeCatalogJson(outputPath) {
96
- const catalog = generateCatalog();
97
- const resolved = path.resolve(outputPath);
98
- fs.mkdirSync(path.dirname(resolved), { recursive: true });
99
- fs.writeFileSync(resolved, JSON.stringify(catalog, null, 2) + '\n', 'utf8');
100
- return { path: resolved, count: catalog.length };
101
- }
102
-
103
- module.exports = { generateCatalog, generateCatalogWithVersion, writeCatalogJson };
1
+ /**
2
+ * Public Check Catalog Generator
3
+ * Reads ALL technique files from all 8 platforms and generates a unified JSON catalog.
4
+ */
5
+
6
+ const fs = require('fs');
7
+ const path = require('path');
8
+ const { version } = require('../package.json');
9
+
10
+ const { TECHNIQUES: CLAUDE_TECHNIQUES } = require('./techniques');
11
+ const { CODEX_TECHNIQUES } = require('./codex/techniques');
12
+ const { GEMINI_TECHNIQUES } = require('./gemini/techniques');
13
+ const { COPILOT_TECHNIQUES } = require('./copilot/techniques');
14
+ const { CURSOR_TECHNIQUES } = require('./cursor/techniques');
15
+ const { WINDSURF_TECHNIQUES } = require('./windsurf/techniques');
16
+ const { AIDER_TECHNIQUES } = require('./aider/techniques');
17
+ const { OPENCODE_TECHNIQUES } = require('./opencode/techniques');
18
+ const { attachSourceUrls } = require('./source-urls');
19
+
20
+ const PLATFORM_MAP = {
21
+ claude: CLAUDE_TECHNIQUES,
22
+ codex: CODEX_TECHNIQUES,
23
+ gemini: GEMINI_TECHNIQUES,
24
+ copilot: COPILOT_TECHNIQUES,
25
+ cursor: CURSOR_TECHNIQUES,
26
+ windsurf: WINDSURF_TECHNIQUES,
27
+ aider: AIDER_TECHNIQUES,
28
+ opencode: OPENCODE_TECHNIQUES,
29
+ };
30
+
31
+ /**
32
+ * Generate a unified catalog array from all platform technique files.
33
+ * Each entry contains:
34
+ * platform, id, key, name, category, impact, rating, fix, sourceUrl,
35
+ * confidence, lastVerified, template, deprecated
36
+ */
37
+ function generateCatalog() {
38
+ const catalog = [];
39
+
40
+ for (const [platform, techniques] of Object.entries(PLATFORM_MAP)) {
41
+ // Clone techniques so we don't mutate the originals
42
+ const cloned = {};
43
+ for (const [key, tech] of Object.entries(techniques)) {
44
+ cloned[key] = { ...tech };
45
+ }
46
+
47
+ // Attach source URLs
48
+ try {
49
+ attachSourceUrls(platform, cloned);
50
+ } catch (_) {
51
+ // If source URLs fail for a platform, continue without them
52
+ }
53
+
54
+ for (const [key, tech] of Object.entries(cloned)) {
55
+ catalog.push({
56
+ platform,
57
+ id: tech.id ?? null,
58
+ key,
59
+ name: tech.name ?? null,
60
+ category: tech.category ?? null,
61
+ impact: tech.impact ?? null,
62
+ rating: tech.rating ?? null,
63
+ fix: tech.fix ?? null,
64
+ sourceUrl: tech.sourceUrl ?? null,
65
+ confidence: tech.confidence ?? null,
66
+ lastVerified: tech.lastVerified ?? null,
67
+ template: tech.template ?? null,
68
+ deprecated: tech.deprecated ?? false,
69
+ });
70
+ }
71
+ }
72
+
73
+ return catalog;
74
+ }
75
+
76
+ /**
77
+ * Generate a catalog envelope with version metadata.
78
+ * @returns {{ catalogVersion: string, generatedAt: string, totalChecks: number, checks: Object[] }}
79
+ */
80
+ function generateCatalogWithVersion() {
81
+ const checks = generateCatalog();
82
+ return {
83
+ catalogVersion: version,
84
+ generatedAt: new Date().toISOString(),
85
+ totalChecks: checks.length,
86
+ checks,
87
+ };
88
+ }
89
+
90
+ /**
91
+ * Write the catalog as formatted JSON to the given output path.
92
+ * @param {string} outputPath - Absolute or relative path for the JSON file
93
+ * @returns {{ path: string, count: number }} Written path and entry count
94
+ */
95
+ function writeCatalogJson(outputPath) {
96
+ const catalog = generateCatalog();
97
+ const resolved = path.resolve(outputPath);
98
+ fs.mkdirSync(path.dirname(resolved), { recursive: true });
99
+ fs.writeFileSync(resolved, JSON.stringify(catalog, null, 2) + '\n', 'utf8');
100
+ return { path: resolved, count: catalog.length };
101
+ }
102
+
103
+ module.exports = { generateCatalog, generateCatalogWithVersion, writeCatalogJson };
@@ -1,128 +1,128 @@
1
- /**
2
- * Certification system for Nerviq.
3
- * Evaluates a project against all active platforms and assigns a certification level.
4
- */
5
-
6
- const path = require('path');
7
- const { audit } = require('./audit');
8
- const { harmonyAudit } = require('./harmony/audit');
9
- const { detectPlatforms } = require('./public-api');
10
-
11
- const LEVELS = {
12
- GOLD: 'Nerviq Certified Gold',
13
- SILVER: 'Nerviq Certified Silver',
14
- BRONZE: 'Nerviq Certified Bronze',
15
- NONE: 'Not Certified',
16
- };
17
-
18
- const BADGE_COLORS = {
19
- [LEVELS.GOLD]: 'gold',
20
- [LEVELS.SILVER]: 'silver',
21
- [LEVELS.BRONZE]: 'cd7f32',
22
- [LEVELS.NONE]: 'lightgrey',
23
- };
24
-
25
- /**
26
- * Certify a project directory.
27
- * Runs harmony audit and per-platform audits, then determines certification level.
28
- *
29
- * @param {string} dir - Project directory path
30
- * @returns {Promise<{ level: string, harmonyScore: number, platformScores: Object, badge: string }>}
31
- */
32
- async function certifyProject(dir) {
33
- const resolvedDir = path.resolve(dir || '.');
34
-
35
- // Detect active platforms
36
- const platforms = detectPlatforms(resolvedDir);
37
-
38
- // Run per-platform audits
39
- const platformScores = {};
40
- const allAuditResults = [];
41
- for (const platform of platforms) {
42
- try {
43
- const result = await audit({ dir: resolvedDir, platform, silent: true });
44
- platformScores[platform] = result.score;
45
- if (Array.isArray(result.results)) {
46
- allAuditResults.push(...result.results);
47
- }
48
- } catch {
49
- platformScores[platform] = 0;
50
- }
51
- }
52
-
53
- // Run harmony audit
54
- let harmonyScore = 0;
55
- try {
56
- const harmonyResult = await harmonyAudit({ dir: resolvedDir, silent: true });
57
- harmonyScore = harmonyResult.harmonyScore || 0;
58
- } catch {
59
- harmonyScore = 0;
60
- }
61
-
62
- // Determine certification level with security gates
63
- const scores = Object.values(platformScores);
64
- const allAbove70 = scores.length > 0 && scores.every(s => s >= 70);
65
- const allAbove50 = scores.length > 0 && scores.every(s => s >= 50);
66
- const anyAbove40 = scores.some(s => s >= 40);
67
-
68
- // Security gate helpers — check whether specific audit checks passed
69
- const checkPassed = (key) => {
70
- const match = allAuditResults.find(r => r.key === key);
71
- return match ? match.passed === true : false;
72
- };
73
-
74
- const gitIgnoreOk = checkPassed('gitIgnoreEnv');
75
- const secretsOk = checkPassed('secretsProtection');
76
- const criticalAntiPatterns = allAuditResults.filter(
77
- r => r.passed === false && r.impact === 'critical'
78
- );
79
- const noCriticalAntiPatterns = criticalAntiPatterns.length === 0;
80
-
81
- // Bronze gate: score >= 40 AND basic security (gitignore + secrets protection)
82
- const bronzeSecurityGate = gitIgnoreOk && secretsOk;
83
- // Silver gate: Bronze requirements AND no critical anti-patterns
84
- const silverSecurityGate = bronzeSecurityGate && noCriticalAntiPatterns;
85
-
86
- let level;
87
- if (harmonyScore >= 80 && allAbove70 && silverSecurityGate) {
88
- level = LEVELS.GOLD;
89
- } else if (harmonyScore >= 60 && allAbove50 && silverSecurityGate) {
90
- level = LEVELS.SILVER;
91
- } else if (anyAbove40 && bronzeSecurityGate) {
92
- level = LEVELS.BRONZE;
93
- } else {
94
- level = LEVELS.NONE;
95
- }
96
-
97
- const badge = generateCertBadge(level);
98
-
99
- return {
100
- level,
101
- harmonyScore,
102
- platformScores,
103
- platforms,
104
- badge,
105
- securityGates: {
106
- gitIgnoreEnv: gitIgnoreOk,
107
- secretsProtection: secretsOk,
108
- noCriticalAntiPatterns,
109
- criticalAntiPatternCount: criticalAntiPatterns.length,
110
- },
111
- };
112
- }
113
-
114
- /**
115
- * Generate a shields.io badge markdown string for a certification level.
116
- *
117
- * @param {string} level - One of the LEVELS values
118
- * @returns {string} Markdown badge string
119
- */
120
- function generateCertBadge(level) {
121
- const color = BADGE_COLORS[level] || 'lightgrey';
122
- const label = encodeURIComponent('Nerviq');
123
- const message = encodeURIComponent(level);
124
- const url = `https://img.shields.io/badge/${label}-${message}-${color}`;
125
- return `[![${level}](${url})](https://nerviq.net)`;
126
- }
127
-
128
- module.exports = { certifyProject, generateCertBadge, LEVELS };
1
+ /**
2
+ * Certification system for Nerviq.
3
+ * Evaluates a project against all active platforms and assigns a certification level.
4
+ */
5
+
6
+ const path = require('path');
7
+ const { audit } = require('./audit');
8
+ const { harmonyAudit } = require('./harmony/audit');
9
+ const { detectPlatforms } = require('./public-api');
10
+
11
+ const LEVELS = {
12
+ GOLD: 'Nerviq Certified Gold',
13
+ SILVER: 'Nerviq Certified Silver',
14
+ BRONZE: 'Nerviq Certified Bronze',
15
+ NONE: 'Not Certified',
16
+ };
17
+
18
+ const BADGE_COLORS = {
19
+ [LEVELS.GOLD]: 'gold',
20
+ [LEVELS.SILVER]: 'silver',
21
+ [LEVELS.BRONZE]: 'cd7f32',
22
+ [LEVELS.NONE]: 'lightgrey',
23
+ };
24
+
25
+ /**
26
+ * Certify a project directory.
27
+ * Runs harmony audit and per-platform audits, then determines certification level.
28
+ *
29
+ * @param {string} dir - Project directory path
30
+ * @returns {Promise<{ level: string, harmonyScore: number, platformScores: Object, badge: string }>}
31
+ */
32
+ async function certifyProject(dir) {
33
+ const resolvedDir = path.resolve(dir || '.');
34
+
35
+ // Detect active platforms
36
+ const platforms = detectPlatforms(resolvedDir);
37
+
38
+ // Run per-platform audits
39
+ const platformScores = {};
40
+ const allAuditResults = [];
41
+ for (const platform of platforms) {
42
+ try {
43
+ const result = await audit({ dir: resolvedDir, platform, silent: true });
44
+ platformScores[platform] = result.score;
45
+ if (Array.isArray(result.results)) {
46
+ allAuditResults.push(...result.results);
47
+ }
48
+ } catch {
49
+ platformScores[platform] = 0;
50
+ }
51
+ }
52
+
53
+ // Run harmony audit
54
+ let harmonyScore = 0;
55
+ try {
56
+ const harmonyResult = await harmonyAudit({ dir: resolvedDir, silent: true });
57
+ harmonyScore = harmonyResult.harmonyScore || 0;
58
+ } catch {
59
+ harmonyScore = 0;
60
+ }
61
+
62
+ // Determine certification level with security gates
63
+ const scores = Object.values(platformScores);
64
+ const allAbove70 = scores.length > 0 && scores.every(s => s >= 70);
65
+ const allAbove50 = scores.length > 0 && scores.every(s => s >= 50);
66
+ const anyAbove40 = scores.some(s => s >= 40);
67
+
68
+ // Security gate helpers — check whether specific audit checks passed
69
+ const checkPassed = (key) => {
70
+ const match = allAuditResults.find(r => r.key === key);
71
+ return match ? match.passed === true : false;
72
+ };
73
+
74
+ const gitIgnoreOk = checkPassed('gitIgnoreEnv');
75
+ const secretsOk = checkPassed('secretsProtection');
76
+ const criticalAntiPatterns = allAuditResults.filter(
77
+ r => r.passed === false && r.impact === 'critical'
78
+ );
79
+ const noCriticalAntiPatterns = criticalAntiPatterns.length === 0;
80
+
81
+ // Bronze gate: score >= 40 AND basic security (gitignore + secrets protection)
82
+ const bronzeSecurityGate = gitIgnoreOk && secretsOk;
83
+ // Silver gate: Bronze requirements AND no critical anti-patterns
84
+ const silverSecurityGate = bronzeSecurityGate && noCriticalAntiPatterns;
85
+
86
+ let level;
87
+ if (harmonyScore >= 80 && allAbove70 && silverSecurityGate) {
88
+ level = LEVELS.GOLD;
89
+ } else if (harmonyScore >= 60 && allAbove50 && silverSecurityGate) {
90
+ level = LEVELS.SILVER;
91
+ } else if (anyAbove40 && bronzeSecurityGate) {
92
+ level = LEVELS.BRONZE;
93
+ } else {
94
+ level = LEVELS.NONE;
95
+ }
96
+
97
+ const badge = generateCertBadge(level);
98
+
99
+ return {
100
+ level,
101
+ harmonyScore,
102
+ platformScores,
103
+ platforms,
104
+ badge,
105
+ securityGates: {
106
+ gitIgnoreEnv: gitIgnoreOk,
107
+ secretsProtection: secretsOk,
108
+ noCriticalAntiPatterns,
109
+ criticalAntiPatternCount: criticalAntiPatterns.length,
110
+ },
111
+ };
112
+ }
113
+
114
+ /**
115
+ * Generate a shields.io badge markdown string for a certification level.
116
+ *
117
+ * @param {string} level - One of the LEVELS values
118
+ * @returns {string} Markdown badge string
119
+ */
120
+ function generateCertBadge(level) {
121
+ const color = BADGE_COLORS[level] || 'lightgrey';
122
+ const label = encodeURIComponent('Nerviq');
123
+ const message = encodeURIComponent(level);
124
+ const url = `https://img.shields.io/badge/${label}-${message}-${color}`;
125
+ return `[![${level}](${url})](https://nerviq.net)`;
126
+ }
127
+
128
+ module.exports = { certifyProject, generateCertBadge, LEVELS };