@aikdna/kdna-cli 0.18.0 → 0.19.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.
package/src/agent.js CHANGED
@@ -308,7 +308,9 @@ function cmdMatch(taskText, args = []) {
308
308
  }
309
309
  }
310
310
  console.log('');
311
- console.log('To consider any of these, read its full data: kdna load <name|file.kdna> --as=json');
311
+ console.log(
312
+ 'To consider any of these, read its full data: kdna load <name|file.kdna> --as=json',
313
+ );
312
314
  }
313
315
  }
314
316
 
@@ -379,16 +381,24 @@ function cmdLoad(input, args = []) {
379
381
  const signature = manifest.signature;
380
382
  const isPlaceholder = !signature || signature === '' || signature.includes('placeholder');
381
383
  if (isPlaceholder) {
382
- loadWarnings.push('⚠ Domain is unsigned — no cryptographic proof of authorship. Trust depends on source.');
384
+ loadWarnings.push(
385
+ '⚠ Domain is unsigned — no cryptographic proof of authorship. Trust depends on source.',
386
+ );
383
387
  }
384
388
  if (manifest.status === 'deprecated') {
385
- loadWarnings.push(`⚠ Domain is deprecated${manifest.replaced_by ? ', replaced by ' + manifest.replaced_by : ''}.`);
389
+ loadWarnings.push(
390
+ `⚠ Domain is deprecated${manifest.replaced_by ? ', replaced by ' + manifest.replaced_by : ''}.`,
391
+ );
386
392
  }
387
393
  const riskLevel = manifest.risk_level || 'R1';
388
394
  if (riskLevel === 'R3' || riskLevel === 'R4') {
389
- loadWarnings.push(`⚠ High risk domain (${riskLevel}) — may influence agent behavior in safety-critical ways.`);
395
+ loadWarnings.push(
396
+ `⚠ High risk domain (${riskLevel}) — may influence agent behavior in safety-critical ways.`,
397
+ );
390
398
  if (manifest.quality_badge === 'untested' || !manifest.quality_badge) {
391
- loadWarnings.push('⚠ High risk + untested — load only if you trust the source and understand the risks.');
399
+ loadWarnings.push(
400
+ '⚠ High risk + untested — load only if you trust the source and understand the risks.',
401
+ );
392
402
  }
393
403
  }
394
404
  if (loadWarnings.length > 0) {
@@ -399,21 +409,27 @@ function cmdLoad(input, args = []) {
399
409
 
400
410
  // JSON format
401
411
  if (format === 'json') {
402
- process.stdout.write(JSON.stringify({
403
- manifest,
404
- core,
405
- patterns: pat,
406
- trust: {
407
- signature: isPlaceholder ? 'unsigned' : 'present',
408
- risk_level: riskLevel,
409
- deprecated: manifest.status === 'deprecated',
410
- yanked: false,
411
- warnings: loadWarnings,
412
- asset_digest: asset.asset_digest || null,
413
- content_digest: asset.content_digest || null,
414
- license_id: licenseActivation?.license_id || null,
415
- },
416
- }, null, 2) + '\n');
412
+ process.stdout.write(
413
+ JSON.stringify(
414
+ {
415
+ manifest,
416
+ core,
417
+ patterns: pat,
418
+ trust: {
419
+ signature: isPlaceholder ? 'unsigned' : 'present',
420
+ risk_level: riskLevel,
421
+ deprecated: manifest.status === 'deprecated',
422
+ yanked: false,
423
+ warnings: loadWarnings,
424
+ asset_digest: asset.asset_digest || null,
425
+ content_digest: asset.content_digest || null,
426
+ license_id: licenseActivation?.license_id || null,
427
+ },
428
+ },
429
+ null,
430
+ 2,
431
+ ) + '\n',
432
+ );
417
433
  recordTrace({
418
434
  timestamp: new Date().toISOString(),
419
435
  agent: detectAgent(),
@@ -429,7 +445,9 @@ function cmdLoad(input, args = []) {
429
445
  for (const f of ['KDNA_Core.json', 'KDNA_Patterns.json']) {
430
446
  const encrypted = encryptedEntries.includes(f);
431
447
  const buf = encrypted
432
- ? Buffer.from(JSON.stringify(container[f === 'KDNA_Core.json' ? 'core' : 'patterns'], null, 2))
448
+ ? Buffer.from(
449
+ JSON.stringify(container[f === 'KDNA_Core.json' ? 'core' : 'patterns'], null, 2),
450
+ )
433
451
  : readContainerEntry(asset.asset_path, f);
434
452
  if (buf) {
435
453
  process.stdout.write(`\n=== ${f} ===\n`);
@@ -1004,7 +1022,10 @@ function cmdRoute(taskText, args = []) {
1004
1022
 
1005
1023
  if (!taskText) {
1006
1024
  const err = { error: 'Usage: kdna route "<task description>" [--json] [--discover]' };
1007
- if (wantJson) { console.log(JSON.stringify(err)); process.exit(2); }
1025
+ if (wantJson) {
1026
+ console.log(JSON.stringify(err));
1027
+ process.exit(2);
1028
+ }
1008
1029
  console.error(err.error);
1009
1030
  process.exit(2);
1010
1031
  }
@@ -1031,19 +1052,53 @@ function cmdRoute(taskText, args = []) {
1031
1052
 
1032
1053
  // ═══ Gate 1: Intent — does this task need domain judgment? ═══
1033
1054
  const judgmentKeywords = [
1034
- 'review', 'diagnose', 'critique', 'evaluate', 'assess', 'judge',
1035
- 'should i', 'is this good', 'is this correct', 'how would you rate',
1036
- '分析', '诊断', '评估', '判断', '审查', '该怎么', '好不好',
1055
+ 'review',
1056
+ 'diagnose',
1057
+ 'critique',
1058
+ 'evaluate',
1059
+ 'assess',
1060
+ 'judge',
1061
+ 'should i',
1062
+ 'is this good',
1063
+ 'is this correct',
1064
+ 'how would you rate',
1065
+ '分析',
1066
+ '诊断',
1067
+ '评估',
1068
+ '判断',
1069
+ '审查',
1070
+ '该怎么',
1071
+ '好不好',
1037
1072
  ];
1038
1073
  const mechanicalKeywords = [
1039
- 'format', 'translate', 'convert', 'list', 'find', 'lookup', 'search',
1040
- 'run', 'execute', 'compile', 'build', 'fix syntax', 'fix the bug',
1041
- '格式化', '翻译', '转换', '列出', '查找', '搜索', '运行', '执行', '编译', '修复语法',
1074
+ 'format',
1075
+ 'translate',
1076
+ 'convert',
1077
+ 'list',
1078
+ 'find',
1079
+ 'lookup',
1080
+ 'search',
1081
+ 'run',
1082
+ 'execute',
1083
+ 'compile',
1084
+ 'build',
1085
+ 'fix syntax',
1086
+ 'fix the bug',
1087
+ '格式化',
1088
+ '翻译',
1089
+ '转换',
1090
+ '列出',
1091
+ '查找',
1092
+ '搜索',
1093
+ '运行',
1094
+ '执行',
1095
+ '编译',
1096
+ '修复语法',
1042
1097
  ];
1043
1098
 
1044
1099
  const taskLower = taskText.toLowerCase();
1045
- const hasJudgmentSignal = judgmentKeywords.some(k => taskLower.includes(k));
1046
- const hasMechanicalSignal = mechanicalKeywords.some(k => taskLower.includes(k));
1100
+ const hasJudgmentSignal = judgmentKeywords.some((k) => taskLower.includes(k));
1101
+ const hasMechanicalSignal = mechanicalKeywords.some((k) => taskLower.includes(k));
1047
1102
 
1048
1103
  result.needs_kdna = hasJudgmentSignal && !hasMechanicalSignal;
1049
1104
 
@@ -1053,7 +1108,10 @@ function cmdRoute(taskText, args = []) {
1053
1108
  result.reason = hasMechanicalSignal
1054
1109
  ? 'task is mechanical — no domain judgment required'
1055
1110
  : 'task does not appear to need domain judgment';
1056
- if (wantJson) { console.log(JSON.stringify(result, null, 2)); return; }
1111
+ if (wantJson) {
1112
+ console.log(JSON.stringify(result, null, 2));
1113
+ return;
1114
+ }
1057
1115
  console.log('SKIP (no judgment needed)');
1058
1116
  return;
1059
1117
  }
@@ -1062,7 +1120,10 @@ function cmdRoute(taskText, args = []) {
1062
1120
  result.status = 'SKIP_NO_LOCAL_DOMAIN';
1063
1121
  result.action = 'skip';
1064
1122
  result.reason = 'task may benefit from judgment, but no KDNA domains are installed';
1065
- if (wantJson) { console.log(JSON.stringify(result, null, 2)); return; }
1123
+ if (wantJson) {
1124
+ console.log(JSON.stringify(result, null, 2));
1125
+ return;
1126
+ }
1066
1127
  console.log('SKIP (no domains installed)');
1067
1128
  return;
1068
1129
  }
@@ -1137,8 +1198,8 @@ function cmdRoute(taskText, args = []) {
1137
1198
  candidates.sort((a, b) => b.score - a.score);
1138
1199
 
1139
1200
  // ═══ Gate 4: Decision ═══
1140
- const strongCandidates = candidates.filter(c => c.score >= 6);
1141
- const weakCandidates = candidates.filter(c => c.score > 0 && c.score < 6);
1201
+ const strongCandidates = candidates.filter((c) => c.score >= 6);
1202
+ const weakCandidates = candidates.filter((c) => c.score > 0 && c.score < 6);
1142
1203
 
1143
1204
  if (strongCandidates.length === 0 && weakCandidates.length === 0) {
1144
1205
  // No matches at all
@@ -1148,7 +1209,7 @@ function cmdRoute(taskText, args = []) {
1148
1209
  if (result.rejected_domains.length > 0) {
1149
1210
  result.reason += ` (${result.rejected_domains.length} domains explicitly excluded by does_not_apply_when)`;
1150
1211
  }
1151
- result.candidates = candidates.map(c => ({
1212
+ result.candidates = candidates.map((c) => ({
1152
1213
  domain: c.domain,
1153
1214
  decision: 'rejected',
1154
1215
  reason: 'insufficient match score',
@@ -1161,24 +1222,38 @@ function cmdRoute(taskText, args = []) {
1161
1222
  result.reason = `${strongCandidates.length} domains strongly match this task with different judgment frames`;
1162
1223
 
1163
1224
  result.ambiguity = {
1164
- domains: strongCandidates.slice(0, 3).map(c => ({
1225
+ domains: strongCandidates.slice(0, 3).map((c) => ({
1165
1226
  domain: c.domain,
1166
1227
  description: c.description,
1167
1228
  judgment_frame: c.reasons.length > 0 ? c.reasons[0].text : c.description,
1168
1229
  risk_if_wrong: `may misclassify the task as a ${c.domain.split('/').pop()} problem`,
1169
1230
  })),
1170
- recommendation: 'Choose the domain whose judgment frame best matches the task intent. Do not blend domains.',
1231
+ recommendation:
1232
+ 'Choose the domain whose judgment frame best matches the task intent. Do not blend domains.',
1171
1233
  };
1172
1234
 
1173
- result.candidates = strongCandidates.map(c => ({
1174
- domain: c.domain, decision: 'ambiguous', reason: `score ${c.score}`, confidence: c.confidence,
1235
+ result.candidates = strongCandidates.map((c) => ({
1236
+ domain: c.domain,
1237
+ decision: 'ambiguous',
1238
+ reason: `score ${c.score}`,
1239
+ confidence: c.confidence,
1175
1240
  }));
1176
1241
  } else if (strongCandidates.length === 1) {
1177
1242
  // One strong match + possible weak matches
1178
1243
  const selected = strongCandidates[0];
1179
1244
  result.candidates = [
1180
- { domain: selected.domain, decision: 'strong_match', reason: `score ${selected.score}`, confidence: selected.confidence },
1181
- ...weakCandidates.map(c => ({ domain: c.domain, decision: 'weak_match', reason: `score ${c.score}`, confidence: c.confidence })),
1245
+ {
1246
+ domain: selected.domain,
1247
+ decision: 'strong_match',
1248
+ reason: `score ${selected.score}`,
1249
+ confidence: selected.confidence,
1250
+ },
1251
+ ...weakCandidates.map((c) => ({
1252
+ domain: c.domain,
1253
+ decision: 'weak_match',
1254
+ reason: `score ${c.score}`,
1255
+ confidence: c.confidence,
1256
+ })),
1182
1257
  ];
1183
1258
 
1184
1259
  // ═══ Trust Gate ═══
@@ -1200,11 +1275,15 @@ function cmdRoute(taskText, args = []) {
1200
1275
  // Only weak matches — skip
1201
1276
  result.status = 'SKIP_WEAK_FIT';
1202
1277
  result.action = 'skip';
1203
- result.reason = weakCandidates.length > 0
1204
- ? `${weakCandidates.length} domain(s) have weak match only — skipping to avoid contamination`
1205
- : 'no installed domain matches this task';
1206
- result.candidates = weakCandidates.map(c => ({
1207
- domain: c.domain, decision: 'weak_match', reason: `score ${c.score}`, confidence: c.confidence,
1278
+ result.reason =
1279
+ weakCandidates.length > 0
1280
+ ? `${weakCandidates.length} domain(s) have weak match only — skipping to avoid contamination`
1281
+ : 'no installed domain matches this task';
1282
+ result.candidates = weakCandidates.map((c) => ({
1283
+ domain: c.domain,
1284
+ decision: 'weak_match',
1285
+ reason: `score ${c.score}`,
1286
+ confidence: c.confidence,
1208
1287
  }));
1209
1288
  }
1210
1289
 
@@ -1230,7 +1309,7 @@ function cmdRoute(taskText, args = []) {
1230
1309
  if (result.reason) console.log(`Reason: ${result.reason}`);
1231
1310
  if (result.selected_domain) console.log(`Domain: ${result.selected_domain}`);
1232
1311
  if (result.rejected_domains.length) {
1233
- console.log(`Rejected: ${result.rejected_domains.map(r => r.domain).join(', ')}`);
1312
+ console.log(`Rejected: ${result.rejected_domains.map((r) => r.domain).join(', ')}`);
1234
1313
  }
1235
1314
  }
1236
1315
 
@@ -1252,7 +1331,9 @@ function checkTrust(domainName) {
1252
1331
 
1253
1332
  // 2. Deprecation check
1254
1333
  if (manifest.status === 'deprecated') {
1255
- warnings.push(`domain is deprecated${manifest.replaced_by ? ', replaced by ' + manifest.replaced_by : ''}`);
1334
+ warnings.push(
1335
+ `domain is deprecated${manifest.replaced_by ? ', replaced by ' + manifest.replaced_by : ''}`,
1336
+ );
1256
1337
  }
1257
1338
 
1258
1339
  // 3. Signature check
@@ -1271,14 +1352,18 @@ function checkTrust(domainName) {
1271
1352
  const riskMap = { R0: 0, R1: 1, R2: 2, R3: 3, R4: 4 };
1272
1353
  const riskNum = riskMap[riskLevel] || 1;
1273
1354
  if (riskNum >= 3) {
1274
- warnings.push(`domain risk level is ${riskLevel} — high-risk judgment may influence agent behavior`);
1355
+ warnings.push(
1356
+ `domain risk level is ${riskLevel} — high-risk judgment may influence agent behavior`,
1357
+ );
1275
1358
  }
1276
1359
  if (riskNum >= 2 && (manifest.quality_badge === 'untested' || !manifest.quality_badge)) {
1277
- warnings.push(`risk level ${riskLevel} with quality_badge '${manifest.quality_badge || 'none'}' — consider requiring review`);
1360
+ warnings.push(
1361
+ `risk level ${riskLevel} with quality_badge '${manifest.quality_badge || 'none'}' — consider requiring review`,
1362
+ );
1278
1363
  }
1279
1364
 
1280
1365
  // 5. SPEC compatibility check
1281
- const specVersion = manifest.spec_version || manifest.kdna_spec || 'unknown';
1366
+ const specVersion = manifest.spec_version || 'unknown';
1282
1367
  const supportedSpecs = ['1.0-rc', '1.0', '0.7'];
1283
1368
  if (!supportedSpecs.includes(specVersion)) {
1284
1369
  warnings.push(`SPEC version '${specVersion}' may not be fully compatible with current loader`);
@@ -1290,8 +1375,8 @@ function checkTrust(domainName) {
1290
1375
  if (!licenseCheck.ok) {
1291
1376
  warnings.push(
1292
1377
  'commercial domain has no active entitlement — run: kdna license activate ' +
1293
- domainName +
1294
- ' --key <license-key> --server <url>'
1378
+ domainName +
1379
+ ' --key <license-key> --server <url>',
1295
1380
  );
1296
1381
  }
1297
1382
  }
@@ -1301,12 +1386,14 @@ function checkTrust(domainName) {
1301
1386
  const hasJudgmentCards = axioms.length > 0;
1302
1387
  if (hasJudgmentCards) {
1303
1388
  const humanLocks = evolution.human_locks || [];
1304
- const lockedAxioms = axioms.filter(a => {
1389
+ const lockedAxioms = axioms.filter((a) => {
1305
1390
  // Check if axiom has a human_lock field OR if an evolution lock covers it
1306
- return a.human_lock || humanLocks.some(hl => hl.lock_type === 'accept');
1391
+ return a.human_lock || humanLocks.some((hl) => hl.lock_type === 'accept');
1307
1392
  }).length;
1308
1393
  if (lockedAxioms === 0 && humanLocks.length === 0) {
1309
- warnings.push('domain has no Human Lock records — judgment-class content may not be human-verified');
1394
+ warnings.push(
1395
+ 'domain has no Human Lock records — judgment-class content may not be human-verified',
1396
+ );
1310
1397
  }
1311
1398
  }
1312
1399
 
@@ -1320,4 +1407,12 @@ function checkTrust(domainName) {
1320
1407
  };
1321
1408
  }
1322
1409
 
1323
- module.exports = { cmdAvailable, cmdMatch, cmdLoad, cmdSelect, cmdPostvalidate, cmdRoute, checkTrust };
1410
+ module.exports = {
1411
+ cmdAvailable,
1412
+ cmdMatch,
1413
+ cmdLoad,
1414
+ cmdSelect,
1415
+ cmdPostvalidate,
1416
+ cmdRoute,
1417
+ checkTrust,
1418
+ };
package/src/cli.js CHANGED
@@ -7,13 +7,7 @@
7
7
  */
8
8
 
9
9
  const { error, EXIT, setQuiet, setExitCodeOnly } = require('./cmds/_common');
10
- const {
11
- cmdValidate,
12
- cmdPack,
13
- cmdUnpack,
14
- cmdInspect,
15
- cmdCard,
16
- } = require('./cmds/domain');
10
+ const { cmdValidate, cmdPack, cmdUnpack, cmdInspect, cmdCard } = require('./cmds/domain');
17
11
  const { cmdList, cmdRegistry } = require('./cmds/registry');
18
12
  const {
19
13
  cmdCompare,
@@ -249,15 +243,24 @@ switch (cmd) {
249
243
  break;
250
244
  }
251
245
  case 'validate': {
252
- error('Directory validation is a dev-only operation. Use: kdna dev validate <source-dir>', EXIT.INPUT_ERROR);
246
+ error(
247
+ 'Directory validation is a dev-only operation. Use: kdna dev validate <source-dir>',
248
+ EXIT.INPUT_ERROR,
249
+ );
253
250
  break;
254
251
  }
255
252
  case 'pack': {
256
- error('Directory packaging is a dev-only operation. Use: kdna dev pack <source-dir>', EXIT.INPUT_ERROR);
253
+ error(
254
+ 'Directory packaging is a dev-only operation. Use: kdna dev pack <source-dir>',
255
+ EXIT.INPUT_ERROR,
256
+ );
257
257
  break;
258
258
  }
259
259
  case 'unpack': {
260
- error('Unpacking exposes internal files and is dev-only. Use: kdna dev unpack <file.kdna>', EXIT.INPUT_ERROR);
260
+ error(
261
+ 'Unpacking exposes internal files and is dev-only. Use: kdna dev unpack <file.kdna>',
262
+ EXIT.INPUT_ERROR,
263
+ );
261
264
  break;
262
265
  }
263
266
  case 'preview': {
@@ -463,7 +466,10 @@ switch (cmd) {
463
466
  break;
464
467
  }
465
468
  case 'package': {
466
- error('Directory packaging is a dev-only operation. Use: kdna dev pack <source-dir>', EXIT.INPUT_ERROR);
469
+ error(
470
+ 'Directory packaging is a dev-only operation. Use: kdna dev pack <source-dir>',
471
+ EXIT.INPUT_ERROR,
472
+ );
467
473
  break;
468
474
  }
469
475
  // Legacy (removed) commands
@@ -19,7 +19,9 @@ function setQuiet(val) {
19
19
  }
20
20
  }
21
21
 
22
- function isQuiet() { return _quiet; }
22
+ function isQuiet() {
23
+ return _quiet;
24
+ }
23
25
 
24
26
  function setExitCodeOnly(val) {
25
27
  _exitCodeOnly = val;
@@ -34,7 +36,9 @@ function setExitCodeOnly(val) {
34
36
  }
35
37
  }
36
38
 
37
- function isExitCodeOnly() { return _exitCodeOnly; }
39
+ function isExitCodeOnly() {
40
+ return _exitCodeOnly;
41
+ }
38
42
 
39
43
  function log(...args) {
40
44
  if (!_quiet && !_exitCodeOnly) _originalLog(...args);
@@ -154,7 +158,9 @@ function isYesNoSelfCheck(item) {
154
158
  raw.endsWith('?') ||
155
159
  raw.endsWith('吗') ||
156
160
  raw.includes('是否') ||
157
- /^(have|has|can|does|do|is|are|did|was|were|should|will|would|could|might|can not|cannot|能不能|会不会|有没有|要不要|是不是)/.test(lower)
161
+ /^(have|has|can|does|do|is|are|did|was|were|should|will|would|could|might|can not|cannot|能不能|会不会|有没有|要不要|是不是)/.test(
162
+ lower,
163
+ )
158
164
  );
159
165
  }
160
166
 
package/src/cmds/badge.js CHANGED
@@ -157,7 +157,8 @@ function cmdRegistryAudit(args = []) {
157
157
 
158
158
  if (yanked.length) audit.issues.push(`${yanked.length} yanked domain(s)`);
159
159
  if (deprecated.length) audit.issues.push(`${deprecated.length} deprecated domain(s)`);
160
- if (noPackage.length) audit.issues.push(`${noPackage.length} domain(s) without .kdna dev package`);
160
+ if (noPackage.length)
161
+ audit.issues.push(`${noPackage.length} domain(s) without .kdna dev package`);
161
162
  if (noSignature.length) audit.issues.push(`${noSignature.length} domain(s) without signature`);
162
163
 
163
164
  audit.healthy = audit.issues.length === 0;
@@ -184,7 +185,9 @@ function cmdRegistryAudit(args = []) {
184
185
  if (d.yanked) flags.push('yanked');
185
186
  if (d.deprecated) flags.push('deprecated');
186
187
  if (!d.has_asset_url) flags.push('no-package');
187
- console.log(` ${d.name.padEnd(36)} v${d.version || '?'} ${flags.length ? `[${flags.join(', ')}]` : '✓'}`);
188
+ console.log(
189
+ ` ${d.name.padEnd(36)} v${d.version || '?'} ${flags.length ? `[${flags.join(', ')}]` : '✓'}`,
190
+ );
188
191
  }
189
192
  }
190
193
 
@@ -230,7 +233,9 @@ function cmdPackage(domainPath, args = []) {
230
233
  scenarios: readJson(path.join(abs, 'KDNA_Scenarios.json'))?.scenes?.length || 0,
231
234
  cases: readJson(path.join(abs, 'KDNA_Cases.json'))?.cases?.length || 0,
232
235
  },
233
- files: fs.readdirSync(abs).filter((f) => f.endsWith('.json') || f === 'README.md' || f === 'LICENSE'),
236
+ files: fs
237
+ .readdirSync(abs)
238
+ .filter((f) => f.endsWith('.json') || f === 'README.md' || f === 'LICENSE'),
234
239
  };
235
240
 
236
241
  // Actually pack
@@ -90,25 +90,43 @@ function cmdChangelog(args = []) {
90
90
  // Diff maps
91
91
  const axioms = diffSummary(oldJ.axioms || {}, newJ.axioms || {}, 'one_sentence');
92
92
  const ontology = diffSummary(oldJ.ontology || {}, newJ.ontology || {}, 'concept');
93
- const misunderstandings = diffSummary(oldJ.misunderstandings || {}, newJ.misunderstandings || {}, 'wrong');
94
- const bannedTerms = diffList(Object.keys(oldJ.banned_terms || {}), Object.keys(newJ.banned_terms || {}));
93
+ const misunderstandings = diffSummary(
94
+ oldJ.misunderstandings || {},
95
+ newJ.misunderstandings || {},
96
+ 'wrong',
97
+ );
98
+ const bannedTerms = diffList(
99
+ Object.keys(oldJ.banned_terms || {}),
100
+ Object.keys(newJ.banned_terms || {}),
101
+ );
95
102
  const stances = diffList(oldJ.stances || [], newJ.stances || []);
96
103
 
97
104
  // Version bump suggestion
98
- const hasRemoved = Object.values(axioms).some((a) => a.status === 'removed') ||
99
- Object.values(misunderstandings).some((m) => m.status === 'removed');
100
- const hasAdded = Object.values(axioms).some((a) => a.status === 'added') ||
101
- Object.values(misunderstandings).some((m) => m.status === 'added');
102
- const hasChanged = Object.values(axioms).some((a) => a.status === 'changed') ||
103
- Object.values(misunderstandings).some((m) => m.status === 'changed');
105
+ const hasRemoved =
106
+ Object.values(axioms).some((a) => a.status === 'removed') ||
107
+ Object.values(misunderstandings).some((m) => m.status === 'removed');
108
+ const hasAdded =
109
+ Object.values(axioms).some((a) => a.status === 'added') ||
110
+ Object.values(misunderstandings).some((m) => m.status === 'added');
111
+ const hasChanged =
112
+ Object.values(axioms).some((a) => a.status === 'changed') ||
113
+ Object.values(misunderstandings).some((m) => m.status === 'changed');
104
114
  let recommendedBump = 'none';
105
115
  if (hasRemoved) recommendedBump = 'major';
106
116
  else if (hasAdded || hasChanged) recommendedBump = 'minor';
107
117
  else if (stances.added.length || stances.removed.length) recommendedBump = 'patch';
108
118
 
109
119
  // Cleanup
110
- try { fs.rmSync(tmpOld, { recursive: true, force: true }); } catch { /* cleanup */ }
111
- try { fs.rmSync(tmpNew, { recursive: true, force: true }); } catch { /* cleanup */ }
120
+ try {
121
+ fs.rmSync(tmpOld, { recursive: true, force: true });
122
+ } catch {
123
+ /* cleanup */
124
+ }
125
+ try {
126
+ fs.rmSync(tmpNew, { recursive: true, force: true });
127
+ } catch {
128
+ /* cleanup */
129
+ }
112
130
 
113
131
  // Output
114
132
  const changelog = {
@@ -139,7 +157,9 @@ function cmdChangelog(args = []) {
139
157
  console.log(`## ${fromVersion} → ${toVersion}`);
140
158
  console.log('');
141
159
  if (oldJ.judgment_version || newJ.judgment_version) {
142
- console.log(`Judgment version: ${oldJ.judgment_version || '(none)'} → ${newJ.judgment_version || '(none)'}`);
160
+ console.log(
161
+ `Judgment version: ${oldJ.judgment_version || '(none)'} → ${newJ.judgment_version || '(none)'}`,
162
+ );
143
163
  console.log('');
144
164
  }
145
165