@neurcode-ai/cli 0.16.4 → 0.16.6

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 (76) hide show
  1. package/.telemetry-bundle/dist/index.js +0 -0
  2. package/LICENSE +201 -0
  3. package/dist/api-client.d.ts +75 -0
  4. package/dist/api-client.d.ts.map +1 -1
  5. package/dist/api-client.js +43 -0
  6. package/dist/api-client.js.map +1 -1
  7. package/dist/commands/brain.d.ts.map +1 -1
  8. package/dist/commands/brain.js +151 -0
  9. package/dist/commands/brain.js.map +1 -1
  10. package/dist/commands/cursor.d.ts.map +1 -1
  11. package/dist/commands/cursor.js +72 -0
  12. package/dist/commands/cursor.js.map +1 -1
  13. package/dist/commands/eval.d.ts +19 -0
  14. package/dist/commands/eval.d.ts.map +1 -0
  15. package/dist/commands/eval.js +246 -0
  16. package/dist/commands/eval.js.map +1 -0
  17. package/dist/commands/onboard.d.ts +29 -0
  18. package/dist/commands/onboard.d.ts.map +1 -0
  19. package/dist/commands/onboard.js +247 -0
  20. package/dist/commands/onboard.js.map +1 -0
  21. package/dist/commands/runtime-doctor.d.ts.map +1 -1
  22. package/dist/commands/runtime-doctor.js +80 -9
  23. package/dist/commands/runtime-doctor.js.map +1 -1
  24. package/dist/commands/runtime-sync.d.ts.map +1 -1
  25. package/dist/commands/runtime-sync.js +22 -0
  26. package/dist/commands/runtime-sync.js.map +1 -1
  27. package/dist/commands/runtime.d.ts +18 -0
  28. package/dist/commands/runtime.d.ts.map +1 -1
  29. package/dist/commands/runtime.js +321 -1
  30. package/dist/commands/runtime.js.map +1 -1
  31. package/dist/commands/session-hook.d.ts +10 -2
  32. package/dist/commands/session-hook.d.ts.map +1 -1
  33. package/dist/commands/session-hook.js +533 -122
  34. package/dist/commands/session-hook.js.map +1 -1
  35. package/dist/commands/session.d.ts +34 -0
  36. package/dist/commands/session.d.ts.map +1 -1
  37. package/dist/commands/session.js +243 -2
  38. package/dist/commands/session.js.map +1 -1
  39. package/dist/index.js +84 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/runtime-build.json +5 -5
  42. package/dist/utils/agent-guard-supervisor.d.ts.map +1 -1
  43. package/dist/utils/agent-guard-supervisor.js +0 -1
  44. package/dist/utils/agent-guard-supervisor.js.map +1 -1
  45. package/dist/utils/cursor-gate.d.ts +1 -0
  46. package/dist/utils/cursor-gate.d.ts.map +1 -1
  47. package/dist/utils/cursor-gate.js +34 -7
  48. package/dist/utils/cursor-gate.js.map +1 -1
  49. package/dist/utils/guided-eval.d.ts +251 -0
  50. package/dist/utils/guided-eval.d.ts.map +1 -0
  51. package/dist/utils/guided-eval.js +880 -0
  52. package/dist/utils/guided-eval.js.map +1 -0
  53. package/dist/utils/local-repo-brain.d.ts +158 -0
  54. package/dist/utils/local-repo-brain.d.ts.map +1 -0
  55. package/dist/utils/local-repo-brain.js +854 -0
  56. package/dist/utils/local-repo-brain.js.map +1 -0
  57. package/dist/utils/runtime-live.d.ts +25 -0
  58. package/dist/utils/runtime-live.d.ts.map +1 -1
  59. package/dist/utils/runtime-live.js +103 -4
  60. package/dist/utils/runtime-live.js.map +1 -1
  61. package/dist/utils/runtime-outbox.d.ts +2 -1
  62. package/dist/utils/runtime-outbox.d.ts.map +1 -1
  63. package/dist/utils/runtime-outbox.js +21 -16
  64. package/dist/utils/runtime-outbox.js.map +1 -1
  65. package/dist/utils/session-allowlist-rules.d.ts +12 -0
  66. package/dist/utils/session-allowlist-rules.d.ts.map +1 -1
  67. package/dist/utils/session-allowlist-rules.js +61 -1
  68. package/dist/utils/session-allowlist-rules.js.map +1 -1
  69. package/dist/utils/structural-understanding.d.ts +61 -1
  70. package/dist/utils/structural-understanding.d.ts.map +1 -1
  71. package/dist/utils/structural-understanding.js +534 -1
  72. package/dist/utils/structural-understanding.js.map +1 -1
  73. package/dist/utils/v0-governance.d.ts.map +1 -1
  74. package/dist/utils/v0-governance.js +10 -0
  75. package/dist/utils/v0-governance.js.map +1 -1
  76. package/package.json +7 -8
@@ -54,6 +54,8 @@ exports.waiveGovernanceObligationCommand = waiveGovernanceObligationCommand;
54
54
  exports.listRuntimeSessionsCommand = listRuntimeSessionsCommand;
55
55
  exports.showRuntimeSessionCommand = showRuntimeSessionCommand;
56
56
  exports.aiChangeRecordCommand = aiChangeRecordCommand;
57
+ exports.exportAIChangeRecordForCli = exportAIChangeRecordForCli;
58
+ exports.verifyAIChangeRecordForCli = verifyAIChangeRecordForCli;
57
59
  exports.structuralUnderstandingCommand = structuralUnderstandingCommand;
58
60
  exports.listSessionsCommand = listSessionsCommand;
59
61
  exports.endSessionCommand = endSessionCommand;
@@ -73,6 +75,7 @@ const session_continuity_1 = require("../utils/session-continuity");
73
75
  const runtime_evidence_1 = require("../utils/runtime-evidence");
74
76
  const v0_governance_1 = require("../utils/v0-governance");
75
77
  const runtime_connection_1 = require("../utils/runtime-connection");
78
+ const runtime_connection_2 = require("../utils/runtime-connection");
76
79
  const runtime_live_1 = require("../utils/runtime-live");
77
80
  const session_allowlist_rules_1 = require("../utils/session-allowlist-rules");
78
81
  const runtime_outbox_1 = require("../utils/runtime-outbox");
@@ -187,6 +190,15 @@ function resolveUnderstandingDiff(repoRoot, options) {
187
190
  function loadLocalGovernanceSession(repoRoot, sessionId) {
188
191
  return sessionId ? (0, governance_runtime_1.loadSession)(repoRoot, sessionId) : (0, governance_runtime_1.loadActiveSession)(repoRoot);
189
192
  }
193
+ function normalizeApprovalPathForCloudMatch(repoRoot, inputPath) {
194
+ const normalized = inputPath.trim().replace(/\\/g, '/');
195
+ if (!normalized)
196
+ return normalized;
197
+ if ((0, node_path_1.isAbsolute)(normalized)) {
198
+ return (0, node_path_1.relative)(repoRoot, normalized).replace(/\\/g, '/').replace(/^\.\//, '');
199
+ }
200
+ return normalized.replace(/^\.\//, '').replace(/^\//, '');
201
+ }
190
202
  function latestEventTimestamp(session) {
191
203
  return [...session.events]
192
204
  .reverse()
@@ -635,20 +647,64 @@ async function approveGovernanceSessionCommand(options = {}) {
635
647
  }
636
648
  const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
637
649
  try {
638
- const result = (0, governance_runtime_1.approveSession)(repoRoot, path, options.reason, options.sessionId);
650
+ const sessionId = options.sessionId || (0, governance_runtime_1.loadActiveSession)(repoRoot)?.sessionId;
651
+ const normalizedPath = normalizeApprovalPathForCloudMatch(repoRoot, path);
652
+ const discoveredCloudApproval = sessionId
653
+ ? await (0, runtime_live_1.findRuntimeLiveApprovalRequest)(repoRoot, sessionId, normalizedPath)
654
+ : null;
655
+ const explicitCloudApproval = options.requestId && sessionId
656
+ ? {
657
+ id: options.requestId,
658
+ sessionId,
659
+ path: normalizedPath,
660
+ reason: options.reason || 'Operator approved exact path',
661
+ status: 'requested',
662
+ requestedBy: 'local_operator',
663
+ expiresAt: undefined,
664
+ }
665
+ : null;
666
+ const matchingCloudApproval = discoveredCloudApproval || explicitCloudApproval;
667
+ const result = (0, governance_runtime_1.approveSession)(repoRoot, path, {
668
+ reason: options.reason,
669
+ sessionId: options.sessionId,
670
+ source: matchingCloudApproval ? 'dashboard' : 'local_cli',
671
+ approvedBy: matchingCloudApproval?.requestedBy || null,
672
+ requestId: matchingCloudApproval?.id || null,
673
+ expiresAt: matchingCloudApproval?.expiresAt || undefined,
674
+ });
675
+ if (matchingCloudApproval?.id) {
676
+ (0, runtime_live_1.queueRuntimeLiveApprovalAppliedAck)(repoRoot, result.sessionId, matchingCloudApproval, {
677
+ appliedPath: result.approvedPath,
678
+ expiresAt: result.expiresAt,
679
+ });
680
+ }
639
681
  const session = (0, governance_runtime_1.loadSession)(repoRoot, result.sessionId);
640
682
  if (session) {
641
683
  (0, session_allowlist_rules_1.refreshSessionScopeRules)({ dir: repoRoot, sessionId: session.sessionId });
642
684
  await (0, runtime_live_1.publishRuntimeLiveStatus)(repoRoot, session);
643
685
  }
644
686
  if (options.json) {
645
- console.log(JSON.stringify({ ok: true, repoRoot, ...result }, null, 2));
687
+ console.log(JSON.stringify({
688
+ ok: true,
689
+ repoRoot,
690
+ ...result,
691
+ runtimeApprovalRequest: matchingCloudApproval?.id
692
+ ? {
693
+ id: matchingCloudApproval.id,
694
+ source: discoveredCloudApproval ? 'matched' : 'explicit',
695
+ acknowledgementQueued: true,
696
+ }
697
+ : null,
698
+ }, null, 2));
646
699
  return;
647
700
  }
648
701
  console.log('');
649
702
  console.log(chalk.green(`Approved: ${result.approvedPath}`));
650
703
  console.log(chalk.dim(`Session: ${result.sessionId}`));
651
704
  console.log(chalk.dim(`Approved paths: ${compactList(result.approvedPaths, 12)}`));
705
+ if (matchingCloudApproval?.id) {
706
+ console.log(chalk.dim(`Runtime request: ${matchingCloudApproval.id} (${discoveredCloudApproval ? 'matched' : 'explicit'})`));
707
+ }
652
708
  console.log('');
653
709
  }
654
710
  catch (error) {
@@ -898,6 +954,7 @@ function renderAIChangeRecord(record, recordPath) {
898
954
  const pendingAmendments = record.plan.pendingAmendments.length;
899
955
  const consequenceImpacts = topConsequenceImpactsFromRecord(record.understanding.latest?.consequenceUnderstanding);
900
956
  const consequenceFindings = topConsequenceFindingsFromRecord(record.understanding.latest?.consequenceUnderstanding);
957
+ const reuseFindings = reuseFindingsFromRecord(record.understanding.latest);
901
958
  console.log('');
902
959
  console.log(chalk.bold(`AI Change Record ${record.session.sessionId}`));
903
960
  console.log(chalk.dim('-'.repeat(76)));
@@ -912,6 +969,22 @@ function renderAIChangeRecord(record, recordPath) {
912
969
  console.log(`Checks: ok=${counts.ok} warn=${counts.warn} block=${counts.block} approvals=${counts.approval}`);
913
970
  console.log(`Oblig: ${record.architecture.summary.satisfied}/${record.architecture.summary.total} satisfied${record.architecture.summary.blockingPending ? chalk.yellow(` · ${record.architecture.summary.blockingPending} blocking`) : ''}`);
914
971
  console.log(`Approvals: ${activeApprovals.length} active · ${record.approvals.length} lifecycle entr${record.approvals.length === 1 ? 'y' : 'ies'}`);
972
+ if (record.accountability) {
973
+ const facts = record.accountability.facts;
974
+ console.log('');
975
+ console.log(chalk.bold('Change accountability'));
976
+ console.log(`Asked: ${chalk.white(truncate(facts.agentGoal, 120))}`);
977
+ console.log(`Touched: ${chalk.dim(compactList(facts.touchedPaths, 8))}`);
978
+ console.log(`Allowed: ${chalk.dim(compactList(facts.allowedPaths, 6))}`);
979
+ console.log(`Blocked: ${facts.blockedBoundaries.length > 0 ? chalk.yellow(compactList(facts.blockedBoundaries, 6)) : chalk.dim('none')}`);
980
+ console.log(`Owners: ${facts.boundaryOwners.length > 0 ? chalk.white(compactList(facts.boundaryOwners, 6)) : chalk.dim('not recorded')}`);
981
+ console.log(`Approval: ${facts.approvalRequired ? chalk.yellow('required') : chalk.dim('not required')} · ${facts.exactPathApprovalOnly ? 'exact-path only' : 'no exact approval applied'}`);
982
+ console.log(`Neighbor: ${facts.neighboringSensitiveFilesBlocked ? chalk.green('contained') : chalk.dim('not observed')}`);
983
+ console.log(`Receipt: ${chalk.dim(facts.evidenceReceipt)} · source excluded=${facts.sourceExcluded ? 'yes' : 'no'}`);
984
+ if (record.accountability.assumptions.length > 0) {
985
+ console.log(`Assume: ${chalk.dim(compactList(record.accountability.assumptions, 2))}`);
986
+ }
987
+ }
915
988
  if (record.understanding.latest) {
916
989
  const understanding = record.understanding.latest;
917
990
  console.log(`Understand: ${understanding.changedSymbolCount} changed symbols · ` +
@@ -955,6 +1028,13 @@ function renderAIChangeRecord(record, recordPath) {
955
1028
  console.log(chalk.dim(` ${finding.rank}. ${truncate(finding.summary, 140)}${consumers}${reasons ? ` · ${reasons}` : ''}`));
956
1029
  }
957
1030
  }
1031
+ if (reuseFindings.length > 0) {
1032
+ console.log(`Reuse: ${reuseFindings.length} advisory finding${reuseFindings.length === 1 ? '' : 's'}`);
1033
+ for (const finding of reuseFindings.slice(0, 5)) {
1034
+ console.log(chalk.dim(` ${finding.changed.file}#${finding.changed.name} resembles ` +
1035
+ `${finding.existing.file}#${finding.existing.name} · ${finding.matchType} · ${finding.confidence}`));
1036
+ }
1037
+ }
958
1038
  }
959
1039
  if (blocked.length > 0) {
960
1040
  console.log('');
@@ -1039,6 +1119,17 @@ function topConsequenceFindingsFromRecord(value) {
1039
1119
  return [{ rank, summary, consumerCount, nonTestConsumerCount, testConsumerCount, reasonCodes }];
1040
1120
  }).sort((a, b) => a.rank - b.rank || a.summary.localeCompare(b.summary));
1041
1121
  }
1122
+ function reuseFindingsFromRecord(value) {
1123
+ if (!value || typeof value !== 'object' || Array.isArray(value))
1124
+ return [];
1125
+ const raw = value.reuseFindings;
1126
+ return Array.isArray(raw)
1127
+ ? raw.filter((item) => Boolean(item) &&
1128
+ typeof item === 'object' &&
1129
+ !Array.isArray(item) &&
1130
+ item.schemaVersion === 'neurcode.reuse-finding.v1')
1131
+ : [];
1132
+ }
1042
1133
  function aiChangeRecordCommand(options = {}) {
1043
1134
  const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
1044
1135
  const session = resolveAIChangeRecordSession(repoRoot, options);
@@ -1072,6 +1163,144 @@ function aiChangeRecordCommand(options = {}) {
1072
1163
  }
1073
1164
  renderAIChangeRecord(record, path.replace(`${repoRoot}/`, ''));
1074
1165
  }
1166
+ const PUBLIC_AI_CHANGE_RECORD_DIR = '.neurcode-ai-record';
1167
+ function publicAIChangeRecordPath(repoRoot, sessionId) {
1168
+ if (!/^[A-Za-z0-9._-]+$/.test(sessionId)) {
1169
+ throw new Error('AI Change Record session id is not safe for an artifact filename');
1170
+ }
1171
+ return (0, node_path_1.join)(repoRoot, PUBLIC_AI_CHANGE_RECORD_DIR, `${sessionId}.json`);
1172
+ }
1173
+ function writeJsonFile(path, value) {
1174
+ const dir = (0, node_path_1.dirname)(path);
1175
+ if (!(0, node_fs_1.existsSync)(dir))
1176
+ (0, node_fs_1.mkdirSync)(dir, { recursive: true });
1177
+ (0, node_fs_1.writeFileSync)(path, JSON.stringify(value, null, 2) + '\n', 'utf8');
1178
+ }
1179
+ function readJsonFile(path) {
1180
+ return JSON.parse((0, node_fs_1.readFileSync)(path, 'utf8'));
1181
+ }
1182
+ function extractRecordAndReceipt(value) {
1183
+ if (!value || typeof value !== 'object' || Array.isArray(value))
1184
+ return { record: null, receipt: null };
1185
+ const obj = value;
1186
+ const record = obj.record && typeof obj.record === 'object' && obj.record.recordType === 'ai-change-accountability-record'
1187
+ ? obj.record
1188
+ : obj.recordType === 'ai-change-accountability-record'
1189
+ ? obj
1190
+ : null;
1191
+ const receipt = obj.receipt && typeof obj.receipt === 'object'
1192
+ ? obj.receipt
1193
+ : obj.backendReceipt && typeof obj.backendReceipt === 'object'
1194
+ ? obj.backendReceipt
1195
+ : obj.schemaVersion === 'neurcode.ai-change-record-receipt.v1'
1196
+ ? obj
1197
+ : null;
1198
+ return { record, receipt };
1199
+ }
1200
+ async function exportAIChangeRecordForCli(options = {}) {
1201
+ const repoRoot = (0, v0_governance_1.resolveRepoRoot)(options.dir || process.cwd());
1202
+ const session = resolveAIChangeRecordSession(repoRoot, options);
1203
+ if (!session) {
1204
+ throw new Error(options.sessionId
1205
+ ? `No local governance session found for ${options.sessionId}`
1206
+ : 'No local governance sessions found');
1207
+ }
1208
+ const { record, path: localPath } = (0, governance_runtime_1.writeAIChangeRecord)(repoRoot, session);
1209
+ let receipt = null;
1210
+ let verification = null;
1211
+ const warnings = [];
1212
+ let trustLevel = record.integrity.trustLevel;
1213
+ if (options.signed) {
1214
+ try {
1215
+ const config = (0, config_1.loadConfig)();
1216
+ const connection = (0, runtime_connection_1.loadRuntimeConnection)(repoRoot);
1217
+ const metadata = (0, runtime_connection_2.collectRuntimeRepoMetadata)(repoRoot);
1218
+ const client = new api_client_1.ApiClient(config);
1219
+ const response = await client.signAIChangeRecord({
1220
+ repoId: connection?.repo.id ?? null,
1221
+ repoKey: connection?.repo.repoKey ?? metadata.remoteHash ?? metadata.rootHash,
1222
+ sessionId: record.session.sessionId,
1223
+ recordHash: record.integrity.recordHash,
1224
+ recordSchemaVersion: record.schemaVersion,
1225
+ recordGeneratedAt: record.generatedAt,
1226
+ });
1227
+ receipt = response.receipt || null;
1228
+ verification = response.verification || null;
1229
+ trustLevel = String(verification?.trustLevel || (response.ok ? 'backend_signed_verified' : 'backend_signed_invalid'));
1230
+ if (!response.ok) {
1231
+ warnings.push('Backend signing returned a non-valid verification result; exported as signed evidence needing review.');
1232
+ }
1233
+ }
1234
+ catch (error) {
1235
+ warnings.push(`Backend signing unavailable; exported self-attested record only: ${error instanceof Error ? error.message : String(error)}`);
1236
+ trustLevel = 'self_attested';
1237
+ }
1238
+ }
1239
+ const envelope = {
1240
+ schemaVersion: 'neurcode.ai-change-record-export.v1',
1241
+ generatedAt: new Date().toISOString(),
1242
+ trustLevel,
1243
+ record,
1244
+ ...(receipt ? { receipt } : {}),
1245
+ ...(verification ? { verification } : {}),
1246
+ warnings,
1247
+ privacy: {
1248
+ sourceUploaded: false,
1249
+ sourceFree: true,
1250
+ excludes: ['source code', 'diff hunks', 'patch bodies', 'raw prompts', 'secrets', 'raw file contents'],
1251
+ },
1252
+ };
1253
+ const publicPath = options.output ? (0, node_path_1.resolve)(repoRoot, options.output) : publicAIChangeRecordPath(repoRoot, record.session.sessionId);
1254
+ writeJsonFile(publicPath, envelope);
1255
+ return {
1256
+ ok: true,
1257
+ repoRoot,
1258
+ sessionId: record.session.sessionId,
1259
+ localPath,
1260
+ publicPath,
1261
+ publicRelativePath: (0, node_path_1.relative)(repoRoot, publicPath).replace(/\\/g, '/'),
1262
+ recordHash: record.integrity.recordHash,
1263
+ trustLevel,
1264
+ receipt: {
1265
+ present: Boolean(receipt),
1266
+ receiptId: typeof receipt?.receiptId === 'string' ? receipt.receiptId : null,
1267
+ keyId: typeof receipt?.signingKeyId === 'string' ? receipt.signingKeyId : null,
1268
+ verificationStatus: String(verification?.trustLevel || verification?.status || (receipt ? 'backend_signed_unverified' : 'self_attested')),
1269
+ },
1270
+ warnings,
1271
+ };
1272
+ }
1273
+ function verifyAIChangeRecordForCli(options = {}) {
1274
+ if (!options.record)
1275
+ throw new Error('--record is required');
1276
+ const recordPayload = readJsonFile((0, node_path_1.resolve)(options.record));
1277
+ const recordParts = extractRecordAndReceipt(recordPayload);
1278
+ const receiptPayload = options.receipt ? readJsonFile((0, node_path_1.resolve)(options.receipt)) : recordPayload;
1279
+ const receiptParts = extractRecordAndReceipt(receiptPayload);
1280
+ const record = recordParts.record;
1281
+ const receipt = receiptParts.receipt;
1282
+ if (!record)
1283
+ throw new Error('No AI Change Record found in --record JSON');
1284
+ if (!receipt)
1285
+ throw new Error('No AI Change Record receipt found; pass --receipt or provide an export envelope');
1286
+ const verification = (0, governance_runtime_1.verifyAIChangeRecordReceipt)({
1287
+ recordHash: record.integrity.recordHash,
1288
+ receipt,
1289
+ signingSecret: process.env.NEURCODE_AI_CHANGE_RECORD_SIGNING_SECRET || null,
1290
+ expectedSigningKeyId: process.env.NEURCODE_AI_CHANGE_RECORD_SIGNING_KEY_ID || null,
1291
+ });
1292
+ return {
1293
+ ok: verification.valid,
1294
+ recordHash: record.integrity.recordHash,
1295
+ receiptId: verification.receiptId,
1296
+ trustLevel: verification.trustLevel,
1297
+ verification,
1298
+ privacy: {
1299
+ sourceUploaded: false,
1300
+ sourceFree: true,
1301
+ },
1302
+ };
1303
+ }
1075
1304
  function referenceLabel(ref) {
1076
1305
  const owner = ref.referencingSymbol
1077
1306
  ? `${ref.referencingFile}#${ref.referencingSymbol}:${ref.line}`
@@ -1101,6 +1330,8 @@ function structuralEventDetail(artifact, artifactPath, repoRoot) {
1101
1330
  })),
1102
1331
  suppressedArtifacts: artifact.suppressedArtifacts,
1103
1332
  digest: artifact.digest,
1333
+ repoSymbolIndex: artifact.repoSymbolIndex,
1334
+ reuseFindings: artifact.reuseFindings,
1104
1335
  consequenceUnderstanding: artifact.consequenceUnderstanding,
1105
1336
  planAlignment: artifact.planAlignment,
1106
1337
  boundaryImpact: artifact.boundaryImpact,
@@ -1172,6 +1403,16 @@ function renderStructuralUnderstanding(artifact, artifactPath, repoRoot) {
1172
1403
  }
1173
1404
  }
1174
1405
  }
1406
+ if (artifact.reuseFindings.length > 0) {
1407
+ console.log('');
1408
+ console.log(chalk.bold('Reuse governance advisories'));
1409
+ console.log(chalk.dim(` ${artifact.reuseFindings.length} advisory finding${artifact.reuseFindings.length === 1 ? '' : 's'} from ` +
1410
+ `${artifact.repoSymbolIndex.indexedSymbolCount} indexed TS/JS symbol${artifact.repoSymbolIndex.indexedSymbolCount === 1 ? '' : 's'}.`));
1411
+ for (const finding of artifact.reuseFindings.slice(0, 8)) {
1412
+ console.log(chalk.dim(` ${finding.changed.file}#${finding.changed.name} -> ${finding.existing.file}#${finding.existing.name}` +
1413
+ ` · ${finding.matchType} · ${finding.confidence}`));
1414
+ }
1415
+ }
1175
1416
  if (artifact.digest.topReferences.length > 0 || artifact.digest.topSymbols.length > 0) {
1176
1417
  console.log('');
1177
1418
  console.log(chalk.bold('Structural digest'));