@nerviq/cli 0.9.4 → 0.9.5

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/bin/cli.js CHANGED
@@ -21,7 +21,7 @@ const COMMAND_ALIASES = {
21
21
  gov: 'governance',
22
22
  outcome: 'feedback',
23
23
  };
24
- const KNOWN_COMMANDS = ['audit', 'setup', 'augment', 'suggest-only', 'plan', 'apply', 'governance', 'benchmark', 'deep-review', 'interactive', 'watch', 'badge', 'insights', 'history', 'compare', 'trend', 'scan', 'feedback', 'doctor', 'convert', 'migrate', 'help', 'version'];
24
+ const KNOWN_COMMANDS = ['audit', 'setup', 'augment', 'suggest-only', 'plan', 'apply', 'governance', 'benchmark', 'deep-review', 'interactive', 'watch', 'badge', 'insights', 'history', 'compare', 'trend', 'scan', 'feedback', 'doctor', 'convert', 'migrate', 'catalog', 'help', 'version'];
25
25
 
26
26
  function levenshtein(a, b) {
27
27
  const matrix = Array.from({ length: a.length + 1 }, () => Array(b.length + 1).fill(0));
@@ -237,6 +237,11 @@ const HELP = `
237
237
  npx nerviq badge Generate shields.io badge markdown
238
238
  npx nerviq feedback Record recommendation outcomes or show local outcome summary
239
239
 
240
+ Catalog:
241
+ npx nerviq catalog Show check catalog summary for all 8 platforms
242
+ npx nerviq catalog --json Full catalog as JSON
243
+ npx nerviq catalog --out catalog.json Write catalog to file
244
+
240
245
  Utilities:
241
246
  npx nerviq doctor Self-diagnostics: Node version, deps, freshness gates, platform detection
242
247
  npx nerviq convert --from claude --to codex Convert config between platforms
@@ -391,7 +396,7 @@ async function main() {
391
396
  const FULL_COMMAND_SET = new Set([
392
397
  'audit', 'scan', 'badge', 'augment', 'suggest-only', 'setup', 'plan', 'apply',
393
398
  'governance', 'benchmark', 'deep-review', 'interactive', 'watch', 'insights',
394
- 'history', 'compare', 'trend', 'feedback', 'help', 'version',
399
+ 'history', 'compare', 'trend', 'feedback', 'catalog', 'help', 'version',
395
400
  // Harmony + Synergy (cross-platform)
396
401
  'harmony-audit', 'harmony-sync', 'harmony-drift', 'harmony-advise',
397
402
  'harmony-watch', 'harmony-governance', 'synergy-report',
@@ -713,6 +718,39 @@ async function main() {
713
718
  } else if (normalizedCommand === 'watch') {
714
719
  const { watch } = require('../src/watch');
715
720
  await watch(options);
721
+ } else if (normalizedCommand === 'catalog') {
722
+ const { generateCatalog, writeCatalogJson } = require('../src/catalog');
723
+ if (options.out) {
724
+ const result = writeCatalogJson(options.out);
725
+ if (options.json) {
726
+ console.log(JSON.stringify({ path: result.path, count: result.count }));
727
+ } else {
728
+ console.log(`\n Catalog written to ${result.path} (${result.count} checks)\n`);
729
+ }
730
+ } else {
731
+ const catalog = generateCatalog();
732
+ if (options.json) {
733
+ console.log(JSON.stringify(catalog, null, 2));
734
+ } else {
735
+ // Print summary table
736
+ const platforms = {};
737
+ for (const entry of catalog) {
738
+ platforms[entry.platform] = (platforms[entry.platform] || 0) + 1;
739
+ }
740
+ console.log('');
741
+ console.log('\x1b[1m nerviq check catalog\x1b[0m');
742
+ console.log('\x1b[2m ═══════════════════════════════════════\x1b[0m');
743
+ console.log(` Total checks: \x1b[1m${catalog.length}\x1b[0m`);
744
+ console.log('');
745
+ for (const [plat, count] of Object.entries(platforms)) {
746
+ console.log(` ${plat.padEnd(12)} ${count} checks`);
747
+ }
748
+ console.log('');
749
+ console.log(' Use --json for full output or --out catalog.json to write file.');
750
+ console.log('');
751
+ }
752
+ }
753
+ process.exit(0);
716
754
  } else if (normalizedCommand === 'doctor') {
717
755
  const { runDoctor } = require('../src/doctor');
718
756
  const output = await runDoctor({ dir: options.dir, json: options.json, verbose: options.verbose });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nerviq/cli",
3
- "version": "0.9.4",
3
+ "version": "0.9.5",
4
4
  "description": "The intelligent nervous system for AI coding agents — audit, align, and amplify every platform on every project.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -20,7 +20,9 @@
20
20
  "test": "node test/run.js",
21
21
  "test:jest": "jest",
22
22
  "test:coverage": "jest --coverage",
23
- "test:all": "node test/run.js && node test/check-matrix.js && node test/codex-check-matrix.js && node test/golden-matrix.js && node test/codex-golden-matrix.js && node test/security-tests.js && jest"
23
+ "test:all": "node test/run.js && node test/check-matrix.js && node test/codex-check-matrix.js && node test/golden-matrix.js && node test/codex-golden-matrix.js && node test/security-tests.js && jest",
24
+ "benchmark:perf": "node tools/benchmark.js",
25
+ "catalog": "node -e \"const {generateCatalog}=require('./src/catalog');console.log(JSON.stringify(generateCatalog(),null,2))\""
24
26
  },
25
27
  "keywords": [
26
28
  "nerviq",
package/src/catalog.js ADDED
@@ -0,0 +1,87 @@
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
+
9
+ const { TECHNIQUES: CLAUDE_TECHNIQUES } = require('./techniques');
10
+ const { CODEX_TECHNIQUES } = require('./codex/techniques');
11
+ const { GEMINI_TECHNIQUES } = require('./gemini/techniques');
12
+ const { COPILOT_TECHNIQUES } = require('./copilot/techniques');
13
+ const { CURSOR_TECHNIQUES } = require('./cursor/techniques');
14
+ const { WINDSURF_TECHNIQUES } = require('./windsurf/techniques');
15
+ const { AIDER_TECHNIQUES } = require('./aider/techniques');
16
+ const { OPENCODE_TECHNIQUES } = require('./opencode/techniques');
17
+ const { attachSourceUrls } = require('./source-urls');
18
+
19
+ const PLATFORM_MAP = {
20
+ claude: CLAUDE_TECHNIQUES,
21
+ codex: CODEX_TECHNIQUES,
22
+ gemini: GEMINI_TECHNIQUES,
23
+ copilot: COPILOT_TECHNIQUES,
24
+ cursor: CURSOR_TECHNIQUES,
25
+ windsurf: WINDSURF_TECHNIQUES,
26
+ aider: AIDER_TECHNIQUES,
27
+ opencode: OPENCODE_TECHNIQUES,
28
+ };
29
+
30
+ /**
31
+ * Generate a unified catalog array from all platform technique files.
32
+ * Each entry contains:
33
+ * platform, id, key, name, category, impact, rating, fix, sourceUrl,
34
+ * confidence, template, deprecated
35
+ */
36
+ function generateCatalog() {
37
+ const catalog = [];
38
+
39
+ for (const [platform, techniques] of Object.entries(PLATFORM_MAP)) {
40
+ // Clone techniques so we don't mutate the originals
41
+ const cloned = {};
42
+ for (const [key, tech] of Object.entries(techniques)) {
43
+ cloned[key] = { ...tech };
44
+ }
45
+
46
+ // Attach source URLs
47
+ try {
48
+ attachSourceUrls(platform, cloned);
49
+ } catch (_) {
50
+ // If source URLs fail for a platform, continue without them
51
+ }
52
+
53
+ for (const [key, tech] of Object.entries(cloned)) {
54
+ catalog.push({
55
+ platform,
56
+ id: tech.id ?? null,
57
+ key,
58
+ name: tech.name ?? null,
59
+ category: tech.category ?? null,
60
+ impact: tech.impact ?? null,
61
+ rating: tech.rating ?? null,
62
+ fix: tech.fix ?? null,
63
+ sourceUrl: tech.sourceUrl ?? null,
64
+ confidence: tech.confidence ?? null,
65
+ template: tech.template ?? null,
66
+ deprecated: tech.deprecated ?? false,
67
+ });
68
+ }
69
+ }
70
+
71
+ return catalog;
72
+ }
73
+
74
+ /**
75
+ * Write the catalog as formatted JSON to the given output path.
76
+ * @param {string} outputPath - Absolute or relative path for the JSON file
77
+ * @returns {{ path: string, count: number }} Written path and entry count
78
+ */
79
+ function writeCatalogJson(outputPath) {
80
+ const catalog = generateCatalog();
81
+ const resolved = path.resolve(outputPath);
82
+ fs.mkdirSync(path.dirname(resolved), { recursive: true });
83
+ fs.writeFileSync(resolved, JSON.stringify(catalog, null, 2) + '\n', 'utf8');
84
+ return { path: resolved, count: catalog.length };
85
+ }
86
+
87
+ module.exports = { generateCatalog, writeCatalogJson };
@@ -1,10 +1,8 @@
1
1
  /**
2
- * Official source URL registry for platform technique catalogs.
2
+ * Official source URL + confidence registry for platform technique catalogs.
3
3
  *
4
- * These URLs intentionally point to the nearest authoritative official page for
5
- * a given category or check. Some advisory/internal heuristics do not have a
6
- * single line-item normative doc, so they fall back to the closest official
7
- * platform page that governs the surrounding feature area.
4
+ * We attach metadata at export time so the catalogs stay maintainable without
5
+ * hand-editing hundreds of technique literals.
8
6
  */
9
7
 
10
8
  const SOURCE_URLS = {
@@ -52,7 +50,7 @@ const SOURCE_URLS = {
52
50
  mcp: 'https://developers.openai.com/codex/mcp',
53
51
  skills: 'https://developers.openai.com/codex/skills',
54
52
  agents: 'https://developers.openai.com/codex/subagents',
55
- automation: 'https://developers.openai.com/codex/cli',
53
+ automation: 'https://developers.openai.com/codex/app/automations',
56
54
  review: 'https://developers.openai.com/codex/cli',
57
55
  local: 'https://developers.openai.com/codex/app/local-environments',
58
56
  'quality-deep': 'https://developers.openai.com/codex/feature-maturity',
@@ -66,6 +64,7 @@ const SOURCE_URLS = {
66
64
  codexAutomationAppPrereqAcknowledged: 'https://developers.openai.com/codex/app/automations',
67
65
  codexGitHubActionSafeStrategy: 'https://developers.openai.com/codex/github-action',
68
66
  codexGitHubActionPromptSourceExclusive: 'https://developers.openai.com/codex/github-action',
67
+ codexGitHubActionSinglePromptSource: 'https://developers.openai.com/codex/github-action',
69
68
  codexGitHubActionTriggerAllowlistsExplicit: 'https://developers.openai.com/codex/github-action',
70
69
  codexCiAuthUsesManagedKey: 'https://developers.openai.com/codex/github-action',
71
70
  codexPluginConfigValid: 'https://developers.openai.com/codex/skills',
@@ -84,9 +83,9 @@ const SOURCE_URLS = {
84
83
  sandbox: 'https://geminicli.com/docs/cli/sandbox/',
85
84
  agents: 'https://geminicli.com/docs/core/subagents/',
86
85
  skills: 'https://geminicli.com/docs/cli/skills/',
87
- automation: 'https://google-gemini.github.io/gemini-cli/docs/cli/headless.html',
86
+ automation: 'https://geminicli.com/docs/get-started/',
88
87
  extensions: 'https://geminicli.com/docs/extensions/',
89
- review: 'https://ai.google.dev/gemini-api/docs/coding-agents',
88
+ review: 'https://geminicli.com/docs/get-started/',
90
89
  'quality-deep': 'https://geminicli.com/docs/get-started/',
91
90
  commands: 'https://geminicli.com/docs/cli/custom-commands/',
92
91
  advisory: 'https://geminicli.com/docs/get-started/',
@@ -96,60 +95,60 @@ const SOURCE_URLS = {
96
95
  },
97
96
  },
98
97
  copilot: {
99
- defaultUrl: 'https://docs.github.com/copilot',
98
+ defaultUrl: 'https://docs.github.com/en/copilot',
100
99
  byCategory: {
101
- instructions: 'https://docs.github.com/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot',
102
- config: 'https://code.visualstudio.com/docs/copilot/customization/custom-instructions',
103
- trust: 'https://code.visualstudio.com/docs/copilot/security',
104
- mcp: 'https://code.visualstudio.com/docs/copilot/chat/mcp-servers',
100
+ instructions: 'https://docs.github.com/en/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot',
101
+ config: 'https://docs.github.com/en/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot',
102
+ trust: 'https://docs.github.com/en/copilot/responsible-use-of-github-copilot-features/github-copilot-data-handling',
103
+ mcp: 'https://docs.github.com/en/copilot/customizing-copilot/using-model-context-protocol/extending-copilot-chat-with-mcp',
105
104
  'cloud-agent': 'https://docs.github.com/en/copilot/concepts/agents/coding-agent/about-coding-agent',
106
105
  organization: 'https://docs.github.com/en/copilot/how-tos/administer-copilot/manage-for-organization/manage-policies',
107
- 'prompt-files': 'https://code.visualstudio.com/docs/copilot/customization/prompt-files',
108
- 'skills-agents': 'https://code.visualstudio.com/docs/copilot/agents/overview',
106
+ 'prompt-files': 'https://docs.github.com/en/copilot/customizing-copilot/adding-custom-instructions-for-github-copilot',
107
+ 'skills-agents': 'https://docs.github.com/en/copilot/concepts/agents/coding-agent/about-coding-agent',
109
108
  'ci-automation': 'https://docs.github.com/en/copilot/how-tos/use-copilot-agents/coding-agent/customize-the-agent-environment',
110
109
  enterprise: 'https://docs.github.com/en/copilot/how-tos/administer-copilot/manage-for-enterprise',
111
110
  extensions: 'https://docs.github.com/en/copilot/building-copilot-extensions/about-building-copilot-extensions',
112
- 'quality-deep': 'https://docs.github.com/copilot',
113
- advisory: 'https://docs.github.com/copilot',
114
- freshness: 'https://github.blog/changelog/',
111
+ 'quality-deep': 'https://docs.github.com/en/copilot',
112
+ advisory: 'https://docs.github.com/en/copilot',
113
+ freshness: 'https://docs.github.com/en/copilot',
115
114
  },
116
115
  },
117
116
  cursor: {
118
- defaultUrl: 'https://cursor.com/docs',
117
+ defaultUrl: 'https://docs.cursor.com/',
119
118
  byCategory: {
120
- rules: 'https://cursor.com/docs/context/rules',
121
- config: 'https://cursor.com/docs',
122
- trust: 'https://cursor.com/docs/enterprise/privacy-and-data-governance',
119
+ rules: 'https://docs.cursor.com/context/rules',
120
+ config: 'https://docs.cursor.com/',
121
+ trust: 'https://docs.cursor.com/enterprise/privacy-and-data-governance',
123
122
  'agent-mode': 'https://docs.cursor.com/en/chat/agent',
124
- mcp: 'https://cursor.com/docs/cli/mcp',
123
+ mcp: 'https://docs.cursor.com/cli/mcp',
125
124
  'instructions-quality': 'https://docs.cursor.com/guides/working-with-context',
126
125
  'background-agents': 'https://docs.cursor.com/en/background-agents',
127
- automations: 'https://cursor.com/docs/cloud-agent/automations',
128
- enterprise: 'https://cursor.com/docs/enterprise',
129
- bugbot: 'https://cursor.com/docs/bugbot',
130
- 'cross-surface': 'https://cursor.com/docs',
126
+ automations: 'https://docs.cursor.com/en/background-agents/automations',
127
+ enterprise: 'https://docs.cursor.com/enterprise',
128
+ bugbot: 'https://docs.cursor.com/bugbot',
129
+ 'cross-surface': 'https://docs.cursor.com/',
131
130
  'quality-deep': 'https://docs.cursor.com/guides/working-with-context',
132
- advisory: 'https://cursor.com/docs',
133
- freshness: 'https://cursor.com/changelog',
131
+ advisory: 'https://docs.cursor.com/',
132
+ freshness: 'https://docs.cursor.com/',
134
133
  },
135
134
  },
136
135
  windsurf: {
137
- defaultUrl: 'https://docs.windsurf.com/windsurf/cascade',
136
+ defaultUrl: 'https://docs.windsurf.com/windsurf/cascade/cascade',
138
137
  byCategory: {
139
- rules: 'https://windsurf.com/university/general-education/intro-rules-memories',
138
+ rules: 'https://docs.windsurf.com/windsurf/cascade/cascade',
140
139
  config: 'https://docs.windsurf.com/windsurf/cascade/cascade',
141
- trust: 'https://windsurf.com/security',
140
+ trust: 'https://docs.windsurf.com/windsurf/cascade/cascade',
142
141
  'cascade-agent': 'https://docs.windsurf.com/windsurf/cascade/agents-md',
143
142
  mcp: 'https://docs.windsurf.com/windsurf/cascade/mcp',
144
143
  'instructions-quality': 'https://docs.windsurf.com/windsurf/cascade/agents-md',
145
144
  workflows: 'https://docs.windsurf.com/windsurf/cascade/workflows',
146
145
  memories: 'https://docs.windsurf.com/windsurf/cascade/memories',
147
- enterprise: 'https://windsurf.com/security',
146
+ enterprise: 'https://docs.windsurf.com/windsurf/cascade/cascade',
148
147
  cascadeignore: 'https://docs.windsurf.com/windsurf/cascade/cascade',
149
148
  'cross-surface': 'https://docs.windsurf.com/windsurf/cascade/cascade',
150
149
  'quality-deep': 'https://docs.windsurf.com/windsurf/cascade/cascade',
151
150
  advisory: 'https://docs.windsurf.com/windsurf/cascade/cascade',
152
- freshness: 'https://windsurf.com/changelog',
151
+ freshness: 'https://docs.windsurf.com/windsurf/cascade/cascade',
153
152
  },
154
153
  },
155
154
  aider: {
@@ -162,36 +161,76 @@ const SOURCE_URLS = {
162
161
  conventions: 'https://aider.chat/docs/usage/conventions.html',
163
162
  architecture: 'https://aider.chat/docs/usage/modes.html',
164
163
  security: 'https://aider.chat/docs/config/dotenv.html',
165
- ci: 'https://aider.chat/docs/scripting.html',
166
- quality: 'https://aider.chat/docs/usage/lint-test.html',
164
+ ci: 'https://aider.chat/docs/usage/modes.html',
165
+ quality: 'https://aider.chat/docs/usage/modes.html',
167
166
  'workflow-patterns': 'https://aider.chat/docs/usage/modes.html',
168
- 'editor-integration': 'https://aider.chat/docs/config/editor.html',
167
+ 'editor-integration': 'https://aider.chat/docs/config.html',
169
168
  'release-readiness': 'https://aider.chat/docs/',
170
169
  },
171
170
  },
172
171
  opencode: {
173
- defaultUrl: 'https://opencode.ai/docs/',
172
+ defaultUrl: 'https://github.com/sst/opencode',
174
173
  byCategory: {
175
- instructions: 'https://opencode.ai/docs/rules/',
176
- config: 'https://opencode.ai/docs/config/',
177
- permissions: 'https://opencode.ai/docs/permissions',
178
- plugins: 'https://opencode.ai/docs/plugins/',
179
- security: 'https://opencode.ai/docs/tools/',
180
- mcp: 'https://opencode.ai/docs/mcp-servers/',
181
- ci: 'https://opencode.ai/docs/github/',
182
- 'quality-deep': 'https://opencode.ai/docs/',
183
- skills: 'https://opencode.ai/docs/skills/',
184
- agents: 'https://opencode.ai/docs/agents/',
185
- commands: 'https://opencode.ai/docs/commands/',
186
- tui: 'https://opencode.ai/docs/themes/',
187
- governance: 'https://opencode.ai/docs/github/',
188
- 'release-freshness': 'https://opencode.ai/docs/',
189
- 'mixed-agent': 'https://opencode.ai/docs/modes/',
190
- propagation: 'https://opencode.ai/docs/config/',
174
+ instructions: 'https://github.com/sst/opencode/blob/dev/AGENTS.md',
175
+ config: 'https://github.com/sst/opencode/tree/dev/.opencode',
176
+ permissions: 'https://github.com/sst/opencode/tree/dev/.opencode',
177
+ plugins: 'https://github.com/sst/opencode/tree/dev/.opencode',
178
+ security: 'https://github.com/sst/opencode/blob/dev/SECURITY.md',
179
+ mcp: 'https://github.com/sst/opencode/tree/dev/.opencode',
180
+ ci: 'https://github.com/sst/opencode/tree/dev/.github',
181
+ 'quality-deep': 'https://github.com/sst/opencode/blob/dev/README.md',
182
+ skills: 'https://github.com/sst/opencode/tree/dev/.opencode',
183
+ agents: 'https://github.com/sst/opencode/blob/dev/AGENTS.md',
184
+ commands: 'https://github.com/sst/opencode/tree/dev/.opencode',
185
+ tui: 'https://github.com/sst/opencode/blob/dev/README.md',
186
+ governance: 'https://github.com/sst/opencode/blob/dev/SECURITY.md',
187
+ 'release-freshness': 'https://github.com/sst/opencode/releases',
188
+ 'mixed-agent': 'https://github.com/sst/opencode/blob/dev/AGENTS.md',
189
+ propagation: 'https://github.com/sst/opencode/tree/dev/.opencode',
191
190
  },
192
191
  },
193
192
  };
194
193
 
194
+ const STALE_CONFIDENCE_IDS = new Set([
195
+ 'CX-B04',
196
+ 'CX-B09',
197
+ 'CX-C05',
198
+ 'CX-C06',
199
+ ]);
200
+
201
+ const RUNTIME_CONFIDENCE_IDS = {
202
+ codex: new Set([
203
+ 'CX-B01',
204
+ 'CX-C01',
205
+ 'CX-C02',
206
+ 'CX-C03',
207
+ 'CX-D01',
208
+ 'CX-E02',
209
+ 'CX-H02',
210
+ 'CX-H03',
211
+ 'CX-I01',
212
+ ]),
213
+ gemini: new Set(['GM-Q01', 'GM-Q02', 'GM-Q03', 'GM-Q04', 'GM-Q05']),
214
+ copilot: new Set(['CP-Q01', 'CP-Q02', 'CP-Q03', 'CP-Q04', 'CP-Q05']),
215
+ };
216
+
217
+ function hasRuntimeVerificationSignal(technique) {
218
+ const haystack = `${technique.name || ''}\n${technique.fix || ''}`;
219
+ return /experiment(?:ally)? confirmed|confirmed by (?:live )?experiment|current runtime|runtime evidence|runtime-verified|validated in current runtime|observed in current runtime|measured in live experiment|reproduced in runtime|confirmed by experiment/i.test(haystack);
220
+ }
221
+
222
+ function resolveConfidence(platform, technique) {
223
+ if (STALE_CONFIDENCE_IDS.has(technique.id)) {
224
+ return 0.3;
225
+ }
226
+
227
+ if (RUNTIME_CONFIDENCE_IDS[platform]?.has(technique.id) || hasRuntimeVerificationSignal(technique)) {
228
+ return 0.9;
229
+ }
230
+
231
+ return 0.7;
232
+ }
233
+
195
234
  function attachSourceUrls(platform, techniques) {
196
235
  const mapping = SOURCE_URLS[platform];
197
236
  if (!mapping) {
@@ -199,15 +238,17 @@ function attachSourceUrls(platform, techniques) {
199
238
  }
200
239
 
201
240
  for (const [key, technique] of Object.entries(techniques)) {
202
- if (technique.sourceUrl) continue;
203
241
  const resolved =
204
242
  mapping.byKey?.[key] ||
205
243
  mapping.byCategory?.[technique.category] ||
206
244
  mapping.defaultUrl;
245
+
207
246
  if (!resolved) {
208
247
  throw new Error(`No sourceUrl mapping found for ${platform}:${key}`);
209
248
  }
249
+
210
250
  technique.sourceUrl = resolved;
251
+ technique.confidence = resolveConfidence(platform, technique);
211
252
  }
212
253
 
213
254
  return techniques;